]> git.proxmox.com Git - ceph.git/blame - ceph/src/mds/FSMap.cc
bump version to 16.2.6-pve2
[ceph.git] / ceph / src / mds / FSMap.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
f67539c2 15#include <ostream>
7c673cae
FG
16
17#include "FSMap.h"
18
11fdf7f2 19#include "common/StackStringStream.h"
7c673cae 20
11fdf7f2
TL
21#ifdef WITH_SEASTAR
22#include "crimson/common/config_proxy.h"
23#else
24#include "common/config_proxy.h"
25#endif
26#include "global/global_context.h"
224ce89b
WB
27#include "mon/health_check.h"
28
f67539c2
TL
29using std::list;
30using std::pair;
31using std::ostream;
32using std::string;
33
34using ceph::bufferlist;
35using ceph::Formatter;
36
37void ClusterInfo::encode(ceph::buffer::list &bl) const {
38 ENCODE_START(1, 1, bl);
39 encode(client_name, bl);
40 encode(cluster_name, bl);
41 encode(fs_name, bl);
42 ENCODE_FINISH(bl);
43}
44
45void ClusterInfo::decode(ceph::buffer::list::const_iterator &iter) {
46 DECODE_START(1, iter);
47 decode(client_name, iter);
48 decode(cluster_name, iter);
49 decode(fs_name, iter);
50 DECODE_FINISH(iter);
51}
52
53void ClusterInfo::dump(ceph::Formatter *f) const {
54 f->dump_string("client_name", client_name);
55 f->dump_string("cluster_name", cluster_name);
56 f->dump_string("fs_name", fs_name);
57}
58
59void ClusterInfo::print(std::ostream& out) const {
60 out << "[client_name=" << client_name << ", cluster_name=" << cluster_name
61 << ", fs_name=" << fs_name << "]" << std::endl;
62}
63
64void Peer::encode(ceph::buffer::list &bl) const {
65 ENCODE_START(1, 1, bl);
66 encode(uuid, bl);
67 encode(remote, bl);
68 ENCODE_FINISH(bl);
69}
70
71void Peer::decode(ceph::buffer::list::const_iterator &iter) {
72 DECODE_START(1, iter);
73 decode(uuid, iter);
74 decode(remote, iter);
75 DECODE_FINISH(iter);
76}
77
78void Peer::dump(ceph::Formatter *f) const {
79 f->open_object_section(uuid);
80 f->dump_object("remote", remote);
81 f->close_section();
82}
83
84void Peer::print(std::ostream& out) const {
85 out << "[uuid=" << uuid << ", remote=" << remote << "]" << std::endl;
86}
87
88void MirrorInfo::encode(ceph::buffer::list &bl) const {
89 ENCODE_START(1, 1, bl);
90 encode(mirrored, bl);
91 encode(peers, bl);
92 ENCODE_FINISH(bl);
93}
94
95void MirrorInfo::decode(ceph::buffer::list::const_iterator &iter) {
96 DECODE_START(1, iter);
97 decode(mirrored, iter);
98 decode(peers, iter);
99 DECODE_FINISH(iter);
100}
101
102void MirrorInfo::dump(ceph::Formatter *f) const {
103 f->open_object_section("peers");
104 for (auto &peer : peers) {
105 peer.dump(f);
106 }
107 f->close_section(); // peers
108}
109
110void MirrorInfo::print(std::ostream& out) const {
111 out << "[peers=" << peers << "]" << std::endl;
112}
7c673cae
FG
113
114void Filesystem::dump(Formatter *f) const
115{
116 f->open_object_section("mdsmap");
117 mds_map.dump(f);
118 f->close_section();
119 f->dump_int("id", fscid);
f67539c2
TL
120 if (mirror_info.is_mirrored()) {
121 f->open_object_section("mirror_info");
122 mirror_info.dump(f);
123 f->close_section(); // mirror_info
124 }
7c673cae
FG
125}
126
127void FSMap::dump(Formatter *f) const
128{
129 f->dump_int("epoch", epoch);
11fdf7f2
TL
130 // Use 'default' naming to match 'set-default' CLI
131 f->dump_int("default_fscid", legacy_client_fscid);
7c673cae
FG
132
133 f->open_object_section("compat");
522d829b 134 default_compat.dump(f);
7c673cae
FG
135 f->close_section();
136
137 f->open_object_section("feature_flags");
138 f->dump_bool("enable_multiple", enable_multiple);
139 f->dump_bool("ever_enabled_multiple", ever_enabled_multiple);
140 f->close_section();
141
142 f->open_array_section("standbys");
9f95a23c 143 for (const auto& [gid, info] : standby_daemons) {
7c673cae 144 f->open_object_section("info");
9f95a23c
TL
145 info.dump(f);
146 f->dump_int("epoch", standby_epochs.at(gid));
7c673cae
FG
147 f->close_section();
148 }
149 f->close_section();
150
151 f->open_array_section("filesystems");
152 for (const auto &fs : filesystems) {
153 f->open_object_section("filesystem");
154 fs.second->dump(f);
155 f->close_section();
156 }
157 f->close_section();
158}
159
9f95a23c
TL
160FSMap &FSMap::operator=(const FSMap &rhs)
161{
162 epoch = rhs.epoch;
163 next_filesystem_id = rhs.next_filesystem_id;
164 legacy_client_fscid = rhs.legacy_client_fscid;
522d829b 165 default_compat = rhs.default_compat;
9f95a23c
TL
166 enable_multiple = rhs.enable_multiple;
167 mds_roles = rhs.mds_roles;
168 standby_daemons = rhs.standby_daemons;
169 standby_epochs = rhs.standby_epochs;
170
171 filesystems.clear();
172 for (const auto &i : rhs.filesystems) {
173 const auto &fs = i.second;
174 filesystems[fs->fscid] = std::make_shared<Filesystem>(*fs);
175 }
176
177 return *this;
178}
179
180void FSMap::generate_test_instances(std::list<FSMap*>& ls)
7c673cae
FG
181{
182 FSMap *m = new FSMap();
183
184 std::list<MDSMap*> mds_map_instances;
185 MDSMap::generate_test_instances(mds_map_instances);
186
187 int k = 20;
188 for (auto i : mds_map_instances) {
11fdf7f2 189 auto fs = Filesystem::create();
7c673cae
FG
190 fs->fscid = k++;
191 fs->mds_map = *i;
192 delete i;
193 m->filesystems[fs->fscid] = fs;
194 }
195 mds_map_instances.clear();
196
197 ls.push_back(m);
198}
199
200void FSMap::print(ostream& out) const
201{
202 out << "e" << epoch << std::endl;
203 out << "enable_multiple, ever_enabled_multiple: " << enable_multiple << ","
204 << ever_enabled_multiple << std::endl;
522d829b 205 out << "default compat: " << default_compat << std::endl;
7c673cae
FG
206 out << "legacy client fscid: " << legacy_client_fscid << std::endl;
207 out << " " << std::endl;
208
209 if (filesystems.empty()) {
210 out << "No filesystems configured" << std::endl;
7c673cae
FG
211 }
212
11fdf7f2
TL
213 for (const auto& p : filesystems) {
214 p.second->print(out);
7c673cae
FG
215 out << " " << std::endl << " " << std::endl; // Space out a bit
216 }
217
218 if (!standby_daemons.empty()) {
219 out << "Standby daemons:" << std::endl << " " << std::endl;
220 }
221
9f95a23c
TL
222 for (const auto& p : standby_daemons) {
223 out << p.second << std::endl;
7c673cae
FG
224 }
225}
226
f67539c2
TL
227void FSMap::print_daemon_summary(ostream& out) const
228{
229 // this appears in the "services:" section of "ceph status"
230 int num_up = 0, num_in = 0, num_failed = 0;
231 int num_standby_replay = 0;
232 for (auto& [fscid, fs] : filesystems) {
233 num_up += fs->mds_map.get_num_up_mds();
234 num_in += fs->mds_map.get_num_in_mds();
235 num_failed += fs->mds_map.get_num_failed_mds();
236 num_standby_replay += fs->mds_map.get_num_standby_replay_mds();
237 }
238 int num_standby = standby_daemons.size();
239 out << num_up << "/" << num_in << " daemons up";
240 if (num_failed) {
241 out << " (" << num_failed << " failed)";
242 }
243 if (num_standby) {
244 out << ", " << num_standby << " standby";
245 }
246 if (num_standby_replay) {
247 out << ", " << num_standby_replay << " hot standby";
248 }
249}
250
251void FSMap::print_fs_summary(ostream& out) const
252{
253 // this appears in the "data:" section of "ceph status"
254 if (!filesystems.empty()) {
255 int num_failed = 0, num_recovering = 0, num_stopped = 0, num_healthy = 0;
256 int num_damaged = 0;
257 for (auto& [fscid, fs] : filesystems) {
258 if (fs->mds_map.is_any_damaged()) {
259 ++num_damaged;
260 }
261 if (fs->mds_map.is_any_failed()) {
262 ++num_failed;
263 } else if (fs->mds_map.is_degraded()) {
264 ++num_recovering;
265 } else if (fs->mds_map.get_max_mds() == 0) {
266 ++num_stopped;
267 } else {
268 ++num_healthy;
269 }
270 }
271 out << " volumes: "
272 << num_healthy << "/" << filesystems.size() << " healthy";
273 if (num_recovering) {
274 out << ", " << num_recovering << " recovering";
275 }
276 if (num_failed) {
277 out << ", " << num_failed << " failed";
278 }
279 if (num_stopped) {
280 out << ", " << num_stopped << " stopped";
281 }
282 if (num_damaged) {
283 out << "; " << num_damaged << " damaged";
284 }
285 out << "\n";
286 }
287}
288
7c673cae
FG
289void FSMap::print_summary(Formatter *f, ostream *out) const
290{
7c673cae
FG
291 if (f) {
292 f->dump_unsigned("epoch", get_epoch());
11fdf7f2
TL
293 for (const auto &p : filesystems) {
294 auto& fs = p.second;
7c673cae
FG
295 f->dump_unsigned("id", fs->fscid);
296 f->dump_unsigned("up", fs->mds_map.up.size());
297 f->dump_unsigned("in", fs->mds_map.in.size());
298 f->dump_unsigned("max", fs->mds_map.max_mds);
299 }
300 } else {
11fdf7f2
TL
301 auto count = filesystems.size();
302 if (count <= 3) {
303 bool first = true;
304 for (const auto& p : filesystems) {
305 const auto& fs = p.second;
306 if (!first) {
307 *out << " ";
308 }
309 if (fs->mds_map.is_degraded()) {
310 *out << fs->mds_map.fs_name << ":" << fs->mds_map.up.size() << "/" << fs->mds_map.in.size();
311 } else {
312 *out << fs->mds_map.fs_name << ":" << fs->mds_map.in.size();
313 }
314 first = false;
315 }
316 } else {
317 *out << count << " fs";
318 unsigned degraded = 0;
319 CachedStackStringStream css;
320 *css << " (degraded: ";
321 for (const auto& p : filesystems) {
322 const auto& fs = p.second;
323 if (fs->mds_map.is_degraded()) {
324 degraded++;
325 if (degraded <= 3) {
326 *css << fs->mds_map.fs_name << ":" << fs->mds_map.up.size() << "/" << fs->mds_map.in.size();
327 }
328 }
329 }
330 if (degraded > 0) {
331 if (degraded <= 3) {
332 *css << ")";
333 *out << css->strv();
334 } else {
335 *out << " (degraded: " << degraded << " fs)";
336 }
337 }
7c673cae
FG
338 }
339 }
340
341 if (f) {
342 f->open_array_section("by_rank");
343 }
344
11fdf7f2
TL
345 std::map<MDSMap::DaemonState,unsigned> by_state;
346 std::map<mds_role_t, std::pair<MDSMap::DaemonState, std::string>> by_rank;
347 by_state[MDSMap::DaemonState::STATE_STANDBY] = standby_daemons.size();
348 for (const auto& [gid, fscid] : mds_roles) {
349 if (fscid == FS_CLUSTER_ID_NONE)
350 continue;
351
352 const auto& info = filesystems.at(fscid)->mds_map.get_info_gid(gid);
353 auto s = std::string(ceph_mds_state_name(info.state));
7c673cae
FG
354 if (info.laggy()) {
355 s += "(laggy or crashed)";
356 }
357
11fdf7f2
TL
358 if (f) {
359 f->open_object_section("mds");
360 f->dump_unsigned("filesystem_id", fscid);
361 f->dump_unsigned("rank", info.rank);
362 f->dump_string("name", info.name);
363 f->dump_string("status", s);
364 f->dump_unsigned("gid", gid);
365 f->close_section();
366 } else if (info.state != MDSMap::DaemonState::STATE_STANDBY_REPLAY) {
367 by_rank[mds_role_t(fscid, info.rank)] = std::make_pair(info.state, info.name + "=" + s);
7c673cae 368 }
11fdf7f2 369 by_state[info.state]++;
7c673cae
FG
370 }
371
372 if (f) {
373 f->close_section();
374 } else {
11fdf7f2 375 if (0 < by_rank.size() && by_rank.size() < 5) {
7c673cae
FG
376 if (filesystems.size() > 1) {
377 // Disambiguate filesystems
378 std::map<std::string, std::string> pretty;
11fdf7f2
TL
379 for (const auto& [role,status] : by_rank) {
380 const auto &fs_name = filesystems.at(role.fscid)->mds_map.fs_name;
381 CachedStackStringStream css;
382 *css << fs_name << ":" << role.rank;
383 pretty.emplace(std::piecewise_construct, std::forward_as_tuple(css->strv()), std::forward_as_tuple(status.second));
384 --by_state[status.first]; /* already printed! */
7c673cae
FG
385 }
386 *out << " " << pretty;
387 } else {
388 // Omit FSCID in output when only one filesystem exists
389 std::map<mds_rank_t, std::string> shortened;
11fdf7f2
TL
390 for (const auto& [role,status] : by_rank) {
391 shortened[role.rank] = status.second;
392 --by_state[status.first]; /* already printed! */
7c673cae
FG
393 }
394 *out << " " << shortened;
395 }
396 }
11fdf7f2
TL
397 for (const auto& [state, count] : by_state) {
398 if (count > 0) {
399 auto s = std::string_view(ceph_mds_state_name(state));
400 *out << " " << count << " " << s;
401 }
402 }
7c673cae
FG
403 }
404
11fdf7f2
TL
405 if (f) {
406 const auto state = MDSMap::DaemonState::STATE_STANDBY;
407 auto&& name = ceph_mds_state_name(state);
408 auto count = standby_daemons.size();
409 f->dump_unsigned(name, count);
7c673cae
FG
410 }
411
412 size_t failed = 0;
413 size_t damaged = 0;
11fdf7f2
TL
414 for (const auto& p : filesystems) {
415 auto& fs = p.second;
7c673cae
FG
416 failed += fs->mds_map.failed.size();
417 damaged += fs->mds_map.damaged.size();
418 }
419
420 if (failed > 0) {
421 if (f) {
422 f->dump_unsigned("failed", failed);
423 } else {
424 *out << ", " << failed << " failed";
425 }
426 }
427
428 if (damaged > 0) {
429 if (f) {
430 f->dump_unsigned("damaged", damaged);
431 } else {
432 *out << ", " << damaged << " damaged";
433 }
434 }
435 //if (stopped.size())
436 //out << ", " << stopped.size() << " stopped";
437}
438
9f95a23c
TL
439mds_gid_t Filesystem::get_standby_replay(mds_gid_t who) const
440{
441 for (const auto &i : mds_map.mds_info) {
442 const auto &info = i.second;
443 if (info.state == MDSMap::STATE_STANDBY_REPLAY
444 && info.rank == mds_map.mds_info.at(who).rank) {
445 return info.global_id;
446 }
447 }
448 return MDS_GID_NONE;
449}
7c673cae 450
11fdf7f2 451Filesystem::ref FSMap::create_filesystem(std::string_view name,
522d829b
TL
452 int64_t metadata_pool, int64_t data_pool, uint64_t features,
453 fs_cluster_id_t fscid)
7c673cae 454{
11fdf7f2 455 auto fs = Filesystem::create();
28e407b8 456 fs->mds_map.epoch = epoch;
11fdf7f2 457 fs->mds_map.fs_name = name;
31f18b77 458 fs->mds_map.data_pools.push_back(data_pool);
7c673cae
FG
459 fs->mds_map.metadata_pool = metadata_pool;
460 fs->mds_map.cas_pool = -1;
522d829b 461 fs->mds_map.compat = default_compat;
7c673cae
FG
462 fs->mds_map.created = ceph_clock_now();
463 fs->mds_map.modified = ceph_clock_now();
7c673cae 464 fs->mds_map.enabled = true;
522d829b
TL
465 if (fscid == FS_CLUSTER_ID_NONE) {
466 fs->fscid = next_filesystem_id++;
467 } else {
468 fs->fscid = fscid;
469 next_filesystem_id = std::max(fscid, (fs_cluster_id_t)next_filesystem_id) + 1;
470 }
471
472 // File system's ID can be FS_CLUSTER_ID_ANONYMOUS if we're recovering
473 // a legacy file system by passing FS_CLUSTER_ID_ANONYMOUS as the desired
474 // file system ID
475 if (fscid != FS_CLUSTER_ID_ANONYMOUS) {
476 // ANONYMOUS is only for upgrades from legacy mdsmaps, we should
477 // have initialized next_filesystem_id such that it's never used here.
478 ceph_assert(fs->fscid != FS_CLUSTER_ID_ANONYMOUS);
479 }
7c673cae
FG
480 filesystems[fs->fscid] = fs;
481
482 // Created first filesystem? Set it as the one
483 // for legacy clients to use
484 if (filesystems.size() == 1) {
485 legacy_client_fscid = fs->fscid;
486 }
11fdf7f2
TL
487
488 return fs;
7c673cae
FG
489}
490
9f95a23c
TL
491Filesystem::const_ref FSMap::get_filesystem(std::string_view name) const
492{
493 for (const auto& p : filesystems) {
494 if (p.second->mds_map.fs_name == name) {
495 return p.second;
496 }
497 }
498 return nullptr;
499}
500
501std::vector<Filesystem::const_ref> FSMap::get_filesystems(void) const
502{
503 std::vector<Filesystem::const_ref> ret;
504 for (const auto& p : filesystems) {
505 ret.push_back(p.second);
506 }
507 return ret;
508}
509
7c673cae
FG
510void FSMap::reset_filesystem(fs_cluster_id_t fscid)
511{
512 auto fs = get_filesystem(fscid);
11fdf7f2 513 auto new_fs = Filesystem::create();
7c673cae
FG
514
515 // Populate rank 0 as existing (so don't go into CREATING)
516 // but failed (so that next available MDS is assigned the rank)
517 new_fs->mds_map.in.insert(mds_rank_t(0));
518 new_fs->mds_map.failed.insert(mds_rank_t(0));
519
520 // Carry forward what makes sense
521 new_fs->fscid = fs->fscid;
522 new_fs->mds_map.inline_data_enabled = fs->mds_map.inline_data_enabled;
7c673cae
FG
523 new_fs->mds_map.data_pools = fs->mds_map.data_pools;
524 new_fs->mds_map.metadata_pool = fs->mds_map.metadata_pool;
525 new_fs->mds_map.cas_pool = fs->mds_map.cas_pool;
526 new_fs->mds_map.fs_name = fs->mds_map.fs_name;
522d829b 527 new_fs->mds_map.compat = default_compat;
7c673cae
FG
528 new_fs->mds_map.created = ceph_clock_now();
529 new_fs->mds_map.modified = ceph_clock_now();
7c673cae
FG
530 new_fs->mds_map.standby_count_wanted = fs->mds_map.standby_count_wanted;
531 new_fs->mds_map.enabled = true;
532
c07f9fc5
FG
533 // Remember mds ranks that have ever started. (They should load old inotable
534 // instead of creating new one if they start again.)
535 new_fs->mds_map.stopped.insert(fs->mds_map.in.begin(), fs->mds_map.in.end());
536 new_fs->mds_map.stopped.insert(fs->mds_map.stopped.begin(), fs->mds_map.stopped.end());
537 new_fs->mds_map.stopped.erase(mds_rank_t(0));
538
7c673cae
FG
539 // Persist the new FSMap
540 filesystems[new_fs->fscid] = new_fs;
541}
542
543void FSMap::get_health(list<pair<health_status_t,string> >& summary,
544 list<pair<health_status_t,string> > *detail) const
545{
546 mds_rank_t standby_count_wanted = 0;
547 for (const auto &i : filesystems) {
548 const auto &fs = i.second;
549
550 // TODO: move get_health up into here so that we can qualify
551 // all the messages with what filesystem they're talking about
552 fs->mds_map.get_health(summary, detail);
553
554 standby_count_wanted = std::max(standby_count_wanted, fs->mds_map.get_standby_count_wanted((mds_rank_t)standby_daemons.size()));
555 }
556
557 if (standby_count_wanted) {
f67539c2
TL
558 CachedStackStringStream css;
559 *css << "insufficient standby daemons available: have " << standby_daemons.size() << "; want " << standby_count_wanted << " more";
560 summary.push_back(make_pair(HEALTH_WARN, css->str()));
7c673cae
FG
561 }
562}
563
564bool FSMap::check_health(void)
565{
566 bool changed = false;
567 for (auto &i : filesystems) {
568 changed |= i.second->mds_map.check_health((mds_rank_t)standby_daemons.size());
569 }
570 return changed;
571}
572
224ce89b
WB
573void FSMap::get_health_checks(health_check_map_t *checks) const
574{
575 mds_rank_t standby_count_wanted = 0;
576 for (const auto &i : filesystems) {
577 const auto &fs = i.second;
578 health_check_map_t fschecks;
d2e6a577 579
224ce89b 580 fs->mds_map.get_health_checks(&fschecks);
d2e6a577
FG
581
582 // Some of the failed ranks might be transient (i.e. there are standbys
583 // ready to replace them). We will report only on "stuck" failed, i.e.
584 // ranks which are failed and have no standby replacement available.
585 std::set<mds_rank_t> stuck_failed;
586
587 for (const auto &rank : fs->mds_map.failed) {
9f95a23c
TL
588 auto rep_info = find_replacement_for({fs->fscid, rank});
589 if (!rep_info) {
d2e6a577
FG
590 stuck_failed.insert(rank);
591 }
592 }
593
594 // FS_WITH_FAILED_MDS
595 if (!stuck_failed.empty()) {
596 health_check_t& fscheck = checks->get_or_add(
597 "FS_WITH_FAILED_MDS", HEALTH_WARN,
9f95a23c 598 "%num% filesystem%plurals% %hasorhave% a failed mds daemon", 1);
f67539c2
TL
599 CachedStackStringStream css;
600 *css << "fs " << fs->mds_map.fs_name << " has " << stuck_failed.size()
d2e6a577 601 << " failed mds" << (stuck_failed.size() > 1 ? "s" : "");
f67539c2 602 fscheck.detail.push_back(css->str()); }
d2e6a577 603
224ce89b
WB
604 checks->merge(fschecks);
605 standby_count_wanted = std::max(
606 standby_count_wanted,
607 fs->mds_map.get_standby_count_wanted((mds_rank_t)standby_daemons.size()));
608 }
609
610 // MDS_INSUFFICIENT_STANDBY
611 if (standby_count_wanted) {
f67539c2
TL
612 CachedStackStringStream css1, css2;
613 *css1 << "insufficient standby MDS daemons available";
614 auto& d = checks->get_or_add("MDS_INSUFFICIENT_STANDBY", HEALTH_WARN, css1->str(), 1);
615 *css2 << "have " << standby_daemons.size() << "; want " << standby_count_wanted
616 << " more";
617 d.detail.push_back(css2->str());
224ce89b
WB
618 }
619}
620
9f95a23c
TL
621void FSMap::encode(bufferlist& bl, uint64_t features) const
622{
522d829b 623 ENCODE_START(STRUCT_VERSION, 6, bl);
9f95a23c
TL
624 encode(epoch, bl);
625 encode(next_filesystem_id, bl);
626 encode(legacy_client_fscid, bl);
522d829b 627 encode(default_compat, bl);
9f95a23c
TL
628 encode(enable_multiple, bl);
629 {
630 std::vector<Filesystem::ref> v;
631 v.reserve(filesystems.size());
632 for (auto& p : filesystems) v.emplace_back(p.second);
633 encode(v, bl, features);
7c673cae 634 }
9f95a23c
TL
635 encode(mds_roles, bl);
636 encode(standby_daemons, bl, features);
637 encode(standby_epochs, bl);
638 encode(ever_enabled_multiple, bl);
639 ENCODE_FINISH(bl);
7c673cae
FG
640}
641
11fdf7f2 642void FSMap::decode(bufferlist::const_iterator& p)
7c673cae 643{
522d829b
TL
644 DECODE_START(STRUCT_VERSION, p);
645 DECODE_OLDEST(7);
646 struct_version = struct_v;
f67539c2
TL
647 decode(epoch, p);
648 decode(next_filesystem_id, p);
649 decode(legacy_client_fscid, p);
522d829b 650 decode(default_compat, p);
f67539c2
TL
651 decode(enable_multiple, p);
652 {
653 std::vector<Filesystem::ref> v;
654 decode(v, p);
7c673cae 655 filesystems.clear();
f67539c2
TL
656 for (auto& ref : v) {
657 auto em = filesystems.emplace(std::piecewise_construct, std::forward_as_tuple(ref->fscid), std::forward_as_tuple(std::move(ref)));
658 ceph_assert(em.second);
7c673cae
FG
659 }
660 }
f67539c2
TL
661 decode(mds_roles, p);
662 decode(standby_daemons, p);
663 decode(standby_epochs, p);
664 if (struct_v >= 7) {
665 decode(ever_enabled_multiple, p);
666 }
7c673cae
FG
667 DECODE_FINISH(p);
668}
669
11fdf7f2 670void FSMap::sanitize(const std::function<bool(int64_t pool)>& pool_exists)
3efd9988
FG
671{
672 for (auto &fs : filesystems) {
673 fs.second->mds_map.sanitize(pool_exists);
674 }
675}
7c673cae
FG
676
677void Filesystem::encode(bufferlist& bl, uint64_t features) const
678{
f67539c2 679 ENCODE_START(2, 1, bl);
11fdf7f2 680 encode(fscid, bl);
7c673cae
FG
681 bufferlist mdsmap_bl;
682 mds_map.encode(mdsmap_bl, features);
11fdf7f2 683 encode(mdsmap_bl, bl);
f67539c2 684 encode(mirror_info, bl);
7c673cae
FG
685 ENCODE_FINISH(bl);
686}
687
11fdf7f2 688void Filesystem::decode(bufferlist::const_iterator& p)
7c673cae 689{
f67539c2 690 DECODE_START(2, p);
11fdf7f2 691 decode(fscid, p);
7c673cae 692 bufferlist mdsmap_bl;
11fdf7f2
TL
693 decode(mdsmap_bl, p);
694 auto mdsmap_bl_iter = mdsmap_bl.cbegin();
7c673cae 695 mds_map.decode(mdsmap_bl_iter);
f67539c2
TL
696 if (struct_v >= 2) {
697 decode(mirror_info, p);
698 }
7c673cae
FG
699 DECODE_FINISH(p);
700}
701
702int FSMap::parse_filesystem(
11fdf7f2
TL
703 std::string_view ns_str,
704 Filesystem::const_ref* result
7c673cae
FG
705 ) const
706{
707 std::string ns_err;
94b18763
FG
708 std::string s(ns_str);
709 fs_cluster_id_t fscid = strict_strtol(s.c_str(), 10, &ns_err);
7c673cae
FG
710 if (!ns_err.empty() || filesystems.count(fscid) == 0) {
711 for (auto &fs : filesystems) {
94b18763 712 if (fs.second->mds_map.fs_name == s) {
7c673cae
FG
713 *result = std::const_pointer_cast<const Filesystem>(fs.second);
714 return 0;
715 }
716 }
f67539c2 717 return -CEPHFS_ENOENT;
7c673cae
FG
718 } else {
719 *result = get_filesystem(fscid);
720 return 0;
721 }
722}
723
724void Filesystem::print(std::ostream &out) const
725{
726 out << "Filesystem '" << mds_map.fs_name
727 << "' (" << fscid << ")" << std::endl;
728 mds_map.print(out);
f67539c2
TL
729 if (mirror_info.is_mirrored()) {
730 mirror_info.print(out);
731 }
7c673cae
FG
732}
733
9f95a23c 734bool FSMap::is_any_degraded() const
7c673cae 735{
9f95a23c
TL
736 for (auto& i : filesystems) {
737 if (i.second->mds_map.is_degraded()) {
738 return true;
739 }
740 }
741 return false;
742}
743
744std::map<mds_gid_t, MDSMap::mds_info_t> FSMap::get_mds_info() const
745{
746 std::map<mds_gid_t, mds_info_t> result;
747 for (const auto &i : standby_daemons) {
748 result[i.first] = i.second;
749 }
750
751 for (const auto &i : filesystems) {
752 const auto &fs_info = i.second->mds_map.get_mds_info();
753 for (const auto &j : fs_info) {
754 result[j.first] = j.second;
755 }
756 }
757
758 return result;
759}
760
522d829b 761const MDSMap::mds_info_t* FSMap::get_available_standby(const Filesystem& fs) const
9f95a23c 762{
522d829b 763 const bool upgradeable = fs.is_upgradeable();
9f95a23c 764 const mds_info_t* who = nullptr;
11fdf7f2
TL
765 for (const auto& [gid, info] : standby_daemons) {
766 ceph_assert(info.rank == MDS_RANK_NONE);
767 ceph_assert(info.state == MDSMap::STATE_STANDBY);
7c673cae 768
11fdf7f2 769 if (info.laggy() || info.is_frozen()) {
7c673cae 770 continue;
522d829b
TL
771 } else if (!info.compat.writeable(fs.mds_map.compat)) {
772 /* standby is not compatible with this fs */
773 continue;
774 } else if (!upgradeable && !fs.mds_map.compat.writeable(info.compat)) {
775 /* promotion would change fs.mds_map.compat and we're not upgradeable */
776 continue;
7c673cae
FG
777 }
778
522d829b 779 if (info.join_fscid == fs.fscid) {
9f95a23c
TL
780 who = &info;
781 break;
782 } else if (info.join_fscid == FS_CLUSTER_ID_NONE) {
783 who = &info; /* vanilla standby */
784 } else if (who == nullptr) {
785 who = &info; /* standby for another fs, last resort */
786 }
787 }
788 return who;
789}
790
791mds_gid_t FSMap::find_mds_gid_by_name(std::string_view s) const
792{
793 const auto info = get_mds_info();
794 for (const auto &p : info) {
795 if (p.second.name == s) {
796 return p.first;
797 }
7c673cae 798 }
11fdf7f2 799 return MDS_GID_NONE;
7c673cae
FG
800}
801
9f95a23c
TL
802const MDSMap::mds_info_t* FSMap::find_by_name(std::string_view name) const
803{
804 std::map<mds_gid_t, mds_info_t> result;
805 for (const auto &i : standby_daemons) {
806 if (i.second.name == name) {
807 return &(i.second);
808 }
809 }
810
811 for (const auto &i : filesystems) {
812 const auto &fs_info = i.second->mds_map.get_mds_info();
813 for (const auto &j : fs_info) {
814 if (j.second.name == name) {
815 return &(j.second);
816 }
817 }
818 }
819
820 return nullptr;
821}
822
823const MDSMap::mds_info_t* FSMap::find_replacement_for(mds_role_t role) const
11fdf7f2
TL
824{
825 auto&& fs = get_filesystem(role.fscid);
7c673cae 826
11fdf7f2
TL
827 // First see if we have a STANDBY_REPLAY
828 for (const auto& [gid, info] : fs->mds_map.mds_info) {
829 if (info.rank == role.rank && info.state == MDSMap::STATE_STANDBY_REPLAY) {
830 if (info.is_frozen()) {
831 /* the standby-replay is frozen, do nothing! */
9f95a23c 832 return nullptr;
11fdf7f2 833 } else {
522d829b 834 ceph_assert(info.compat.writeable(fs->mds_map.compat));
9f95a23c 835 return &info;
11fdf7f2 836 }
7c673cae
FG
837 }
838 }
7c673cae 839
522d829b 840 return get_available_standby(*fs);
7c673cae
FG
841}
842
843void FSMap::sanity() const
844{
845 if (legacy_client_fscid != FS_CLUSTER_ID_NONE) {
11fdf7f2 846 ceph_assert(filesystems.count(legacy_client_fscid) == 1);
7c673cae
FG
847 }
848
522d829b
TL
849 for (const auto& [fscid, fs] : filesystems) {
850 ceph_assert(fscid == fs->fscid);
851 for (const auto& [gid, info] : fs->mds_map.mds_info) {
852 ceph_assert(info.rank != MDS_RANK_NONE);
853 ceph_assert(mds_roles.at(gid) == fscid);
854 ceph_assert(standby_daemons.count(gid) == 0);
855 ceph_assert(standby_epochs.count(gid) == 0);
856 if (info.state != MDSMap::STATE_STANDBY_REPLAY) {
857 ceph_assert(fs->mds_map.up.at(info.rank) == gid);
858 ceph_assert(fs->mds_map.failed.count(info.rank) == 0);
859 ceph_assert(fs->mds_map.damaged.count(info.rank) == 0);
860 } else {
861 ceph_assert(fs->mds_map.allows_standby_replay());
7c673cae 862 }
522d829b 863 ceph_assert(info.compat.writeable(fs->mds_map.compat));
7c673cae
FG
864 }
865
866 for (const auto &j : fs->mds_map.up) {
867 mds_rank_t rank = j.first;
11fdf7f2 868 ceph_assert(fs->mds_map.in.count(rank) == 1);
7c673cae 869 mds_gid_t gid = j.second;
11fdf7f2 870 ceph_assert(fs->mds_map.mds_info.count(gid) == 1);
7c673cae
FG
871 }
872 }
873
874 for (const auto &i : standby_daemons) {
11fdf7f2
TL
875 ceph_assert(i.second.state == MDSMap::STATE_STANDBY);
876 ceph_assert(i.second.rank == MDS_RANK_NONE);
877 ceph_assert(i.second.global_id == i.first);
878 ceph_assert(standby_epochs.count(i.first) == 1);
879 ceph_assert(mds_roles.count(i.first) == 1);
880 ceph_assert(mds_roles.at(i.first) == FS_CLUSTER_ID_NONE);
7c673cae
FG
881 }
882
883 for (const auto &i : standby_epochs) {
11fdf7f2 884 ceph_assert(standby_daemons.count(i.first) == 1);
7c673cae
FG
885 }
886
887 for (const auto &i : mds_roles) {
888 if (i.second == FS_CLUSTER_ID_NONE) {
11fdf7f2 889 ceph_assert(standby_daemons.count(i.first) == 1);
7c673cae 890 } else {
11fdf7f2
TL
891 ceph_assert(filesystems.count(i.second) == 1);
892 ceph_assert(filesystems.at(i.second)->mds_map.mds_info.count(i.first) == 1);
7c673cae
FG
893 }
894 }
895}
896
897void FSMap::promote(
898 mds_gid_t standby_gid,
11fdf7f2 899 Filesystem& filesystem,
7c673cae
FG
900 mds_rank_t assigned_rank)
901{
11fdf7f2 902 ceph_assert(gid_exists(standby_gid));
7c673cae
FG
903 bool is_standby_replay = mds_roles.at(standby_gid) != FS_CLUSTER_ID_NONE;
904 if (!is_standby_replay) {
11fdf7f2
TL
905 ceph_assert(standby_daemons.count(standby_gid));
906 ceph_assert(standby_daemons.at(standby_gid).state == MDSMap::STATE_STANDBY);
7c673cae
FG
907 }
908
11fdf7f2 909 MDSMap &mds_map = filesystem.mds_map;
7c673cae
FG
910
911 // Insert daemon state to Filesystem
912 if (!is_standby_replay) {
913 mds_map.mds_info[standby_gid] = standby_daemons.at(standby_gid);
914 } else {
11fdf7f2
TL
915 ceph_assert(mds_map.mds_info.count(standby_gid));
916 ceph_assert(mds_map.mds_info.at(standby_gid).state == MDSMap::STATE_STANDBY_REPLAY);
917 ceph_assert(mds_map.mds_info.at(standby_gid).rank == assigned_rank);
7c673cae 918 }
522d829b 919 auto& info = mds_map.mds_info.at(standby_gid);
7c673cae
FG
920
921 if (mds_map.stopped.erase(assigned_rank)) {
922 // The cluster is being expanded with a stopped rank
923 info.state = MDSMap::STATE_STARTING;
924 } else if (!mds_map.is_in(assigned_rank)) {
925 // The cluster is being expanded with a new rank
926 info.state = MDSMap::STATE_CREATING;
927 } else {
928 // An existing rank is being assigned to a replacement
929 info.state = MDSMap::STATE_REPLAY;
930 mds_map.failed.erase(assigned_rank);
931 }
932 info.rank = assigned_rank;
933 info.inc = epoch;
522d829b 934 mds_roles.at(standby_gid) = filesystem.fscid;
7c673cae
FG
935
936 // Update the rank state in Filesystem
937 mds_map.in.insert(assigned_rank);
938 mds_map.up[assigned_rank] = standby_gid;
939
940 // Remove from the list of standbys
941 if (!is_standby_replay) {
942 standby_daemons.erase(standby_gid);
943 standby_epochs.erase(standby_gid);
944 }
945
522d829b
TL
946 if (!filesystem.mds_map.compat.writeable(info.compat)) {
947 ceph_assert(filesystem.is_upgradeable());
948 filesystem.mds_map.compat.merge(info.compat);
949 }
950
7c673cae
FG
951 // Indicate that Filesystem has been modified
952 mds_map.epoch = epoch;
953}
954
955void FSMap::assign_standby_replay(
956 const mds_gid_t standby_gid,
957 const fs_cluster_id_t leader_ns,
958 const mds_rank_t leader_rank)
959{
11fdf7f2
TL
960 ceph_assert(mds_roles.at(standby_gid) == FS_CLUSTER_ID_NONE);
961 ceph_assert(gid_exists(standby_gid));
962 ceph_assert(!gid_has_rank(standby_gid));
963 ceph_assert(standby_daemons.count(standby_gid));
7c673cae
FG
964
965 // Insert to the filesystem
966 auto fs = filesystems.at(leader_ns);
967 fs->mds_map.mds_info[standby_gid] = standby_daemons.at(standby_gid);
968 fs->mds_map.mds_info[standby_gid].rank = leader_rank;
969 fs->mds_map.mds_info[standby_gid].state = MDSMap::STATE_STANDBY_REPLAY;
970 mds_roles[standby_gid] = leader_ns;
971
972 // Remove from the list of standbys
973 standby_daemons.erase(standby_gid);
974 standby_epochs.erase(standby_gid);
975
976 // Indicate that Filesystem has been modified
977 fs->mds_map.epoch = epoch;
978}
979
f67539c2 980void FSMap::erase(mds_gid_t who, epoch_t blocklist_epoch)
7c673cae
FG
981{
982 if (mds_roles.at(who) == FS_CLUSTER_ID_NONE) {
983 standby_daemons.erase(who);
984 standby_epochs.erase(who);
985 } else {
986 auto &fs = filesystems.at(mds_roles.at(who));
987 const auto &info = fs->mds_map.mds_info.at(who);
988 if (info.state != MDSMap::STATE_STANDBY_REPLAY) {
989 if (info.state == MDSMap::STATE_CREATING) {
990 // If this gid didn't make it past CREATING, then forget
991 // the rank ever existed so that next time it's handed out
992 // to a gid it'll go back into CREATING.
993 fs->mds_map.in.erase(info.rank);
994 } else {
995 // Put this rank into the failed list so that the next available
996 // STANDBY will pick it up.
997 fs->mds_map.failed.insert(info.rank);
998 }
11fdf7f2 999 ceph_assert(fs->mds_map.up.at(info.rank) == info.global_id);
7c673cae
FG
1000 fs->mds_map.up.erase(info.rank);
1001 }
1002 fs->mds_map.mds_info.erase(who);
f67539c2 1003 fs->mds_map.last_failure_osd_epoch = blocklist_epoch;
7c673cae
FG
1004 fs->mds_map.epoch = epoch;
1005 }
1006
1007 mds_roles.erase(who);
1008}
1009
f67539c2 1010void FSMap::damaged(mds_gid_t who, epoch_t blocklist_epoch)
7c673cae 1011{
11fdf7f2 1012 ceph_assert(mds_roles.at(who) != FS_CLUSTER_ID_NONE);
7c673cae
FG
1013 auto fs = filesystems.at(mds_roles.at(who));
1014 mds_rank_t rank = fs->mds_map.mds_info[who].rank;
1015
f67539c2 1016 erase(who, blocklist_epoch);
7c673cae
FG
1017 fs->mds_map.failed.erase(rank);
1018 fs->mds_map.damaged.insert(rank);
1019
11fdf7f2 1020 ceph_assert(fs->mds_map.epoch == epoch);
7c673cae
FG
1021}
1022
1023/**
1024 * Update to indicate that the rank `rank` is to be removed
1025 * from the damaged list of the filesystem `fscid`
1026 */
1027bool FSMap::undamaged(const fs_cluster_id_t fscid, const mds_rank_t rank)
1028{
1029 auto fs = filesystems.at(fscid);
1030
1031 if (fs->mds_map.damaged.erase(rank)) {
1032 fs->mds_map.failed.insert(rank);
1033 fs->mds_map.epoch = epoch;
1034 return true;
1035 } else {
1036 return false;
1037 }
1038}
1039
1040void FSMap::insert(const MDSMap::mds_info_t &new_info)
1041{
11fdf7f2
TL
1042 ceph_assert(new_info.state == MDSMap::STATE_STANDBY);
1043 ceph_assert(new_info.rank == MDS_RANK_NONE);
7c673cae
FG
1044 mds_roles[new_info.global_id] = FS_CLUSTER_ID_NONE;
1045 standby_daemons[new_info.global_id] = new_info;
1046 standby_epochs[new_info.global_id] = epoch;
1047}
1048
9f95a23c 1049std::vector<mds_gid_t> FSMap::stop(mds_gid_t who)
7c673cae 1050{
11fdf7f2 1051 ceph_assert(mds_roles.at(who) != FS_CLUSTER_ID_NONE);
7c673cae
FG
1052 auto fs = filesystems.at(mds_roles.at(who));
1053 const auto &info = fs->mds_map.mds_info.at(who);
1054 fs->mds_map.up.erase(info.rank);
1055 fs->mds_map.in.erase(info.rank);
1056 fs->mds_map.stopped.insert(info.rank);
1057
1058 // Also drop any standby replays that were following this rank
9f95a23c 1059 std::vector<mds_gid_t> standbys;
7c673cae
FG
1060 for (const auto &i : fs->mds_map.mds_info) {
1061 const auto &other_gid = i.first;
1062 const auto &other_info = i.second;
1063 if (other_info.rank == info.rank
1064 && other_info.state == MDSMap::STATE_STANDBY_REPLAY) {
1065 standbys.push_back(other_gid);
1066 erase(other_gid, 0);
1067 }
1068 }
1069
1070 fs->mds_map.mds_info.erase(who);
1071 mds_roles.erase(who);
1072
1073 fs->mds_map.epoch = epoch;
1074
1075 return standbys;
1076}
1077
1078
1079/**
1080 * Given one of the following forms:
1081 * <fs name>:<rank>
1082 * <fs id>:<rank>
1083 * <rank>
1084 *
1085 * Parse into a mds_role_t. The rank-only form is only valid
1086 * if legacy_client_ns is set.
1087 */
f67539c2
TL
1088
1089int FSMap::parse_role(
1090 std::string_view role_str,
1091 mds_role_t *role,
1092 std::ostream &ss,
1093 const std::vector<string> &filter) const
1094{
1095 int r = parse_role(role_str, role, ss);
1096 if (r < 0) return r;
1097
1098 string_view fs_name = get_filesystem(role->fscid)->mds_map.get_fs_name();
1099
1100 if (!filter.empty() &&
1101 std::find(filter.begin(), filter.end(), fs_name) == filter.end()) {
1102 if (r >= 0) {
1103 ss << "Invalid file system";
1104 }
1105 return -CEPHFS_ENOENT;
1106 }
1107
1108 return r;
1109}
1110
7c673cae 1111int FSMap::parse_role(
11fdf7f2 1112 std::string_view role_str,
7c673cae
FG
1113 mds_role_t *role,
1114 std::ostream &ss) const
1115{
1116 size_t colon_pos = role_str.find(":");
1117 size_t rank_pos;
11fdf7f2 1118 Filesystem::const_ref fs;
7c673cae
FG
1119 if (colon_pos == std::string::npos) {
1120 if (legacy_client_fscid == FS_CLUSTER_ID_NONE) {
1121 ss << "No filesystem selected";
f67539c2 1122 return -CEPHFS_ENOENT;
7c673cae
FG
1123 }
1124 fs = get_filesystem(legacy_client_fscid);
1125 rank_pos = 0;
1126 } else {
1127 if (parse_filesystem(role_str.substr(0, colon_pos), &fs) < 0) {
1128 ss << "Invalid filesystem";
f67539c2 1129 return -CEPHFS_ENOENT;
7c673cae
FG
1130 }
1131 rank_pos = colon_pos+1;
1132 }
1133
1134 mds_rank_t rank;
1135 std::string err;
94b18763 1136 std::string rank_str(role_str.substr(rank_pos));
7c673cae
FG
1137 long rank_i = strict_strtol(rank_str.c_str(), 10, &err);
1138 if (rank_i < 0 || !err.empty()) {
1139 ss << "Invalid rank '" << rank_str << "'";
f67539c2 1140 return -CEPHFS_EINVAL;
7c673cae
FG
1141 } else {
1142 rank = rank_i;
1143 }
1144
1145 if (fs->mds_map.in.count(rank) == 0) {
1146 ss << "Rank '" << rank << "' not found";
f67539c2 1147 return -CEPHFS_ENOENT;
7c673cae
FG
1148 }
1149
1150 *role = {fs->fscid, rank};
1151
1152 return 0;
1153}
9f95a23c
TL
1154
1155bool FSMap::pool_in_use(int64_t poolid) const
1156{
1157 for (auto const &i : filesystems) {
1158 if (i.second->mds_map.is_data_pool(poolid)
1159 || i.second->mds_map.metadata_pool == poolid) {
1160 return true;
1161 }
1162 }
1163 return false;
1164}
1165
1166void FSMap::erase_filesystem(fs_cluster_id_t fscid)
1167{
1168 filesystems.erase(fscid);
1169 for (auto& [gid, info] : standby_daemons) {
1170 if (info.join_fscid == fscid) {
1171 modify_daemon(gid, [](auto& info) {
1172 info.join_fscid = FS_CLUSTER_ID_NONE;
1173 });
1174 }
1175 }
1176 for (auto& p : filesystems) {
1177 for (auto& [gid, info] : p.second->mds_map.get_mds_info()) {
1178 if (info.join_fscid == fscid) {
1179 modify_daemon(gid, [](auto& info) {
1180 info.join_fscid = FS_CLUSTER_ID_NONE;
1181 });
1182 }
1183 }
1184 }
1185}