diff options
-rw-r--r-- | src/common/blkdev.cc | 91 | ||||
-rw-r--r-- | src/common/blkdev.h | 13 | ||||
-rw-r--r-- | src/mgr/DaemonServer.cc | 14 | ||||
-rw-r--r-- | src/mgr/DaemonState.cc | 28 | ||||
-rw-r--r-- | src/mgr/DaemonState.h | 19 | ||||
-rw-r--r-- | src/mon/Monitor.cc | 18 | ||||
-rw-r--r-- | src/osd/OSD.cc | 20 | ||||
-rw-r--r-- | src/pybind/mgr/cephadm/module.py | 4 | ||||
-rw-r--r-- | src/pybind/mgr/cephadm/tests/test_cephadm.py | 2 | ||||
-rw-r--r-- | src/pybind/mgr/orchestrator.py | 2 |
10 files changed, 160 insertions, 51 deletions
diff --git a/src/common/blkdev.cc b/src/common/blkdev.cc index 4241bccab8d..8ffdba8c630 100644 --- a/src/common/blkdev.cc +++ b/src/common/blkdev.cc @@ -659,6 +659,34 @@ static int block_device_run_vendor_nvme( return ret; } +std::string get_device_path(const std::string& devname, + std::string *err) +{ + std::set<std::string> links; + int r = easy_readdir("/dev/disk/by-path", &links); + if (r < 0) { + *err = "unable to list contents of /dev/disk/by-path: "s + + cpp_strerror(r); + return {}; + } + for (auto& i : links) { + char fn[PATH_MAX]; + char target[PATH_MAX+1]; + snprintf(fn, sizeof(fn), "/dev/disk/by-path/%s", i.c_str()); + int r = readlink(fn, target, sizeof(target)); + if (r < 0 || r >= (int)sizeof(target)) + continue; + target[r] = 0; + if ((unsigned)r > devname.size() + 1 && + strncmp(target + r - devname.size(), devname.c_str(), r) == 0 && + target[r - devname.size() - 1] == '/') { + return fn; + } + } + *err = "no symlink to "s + devname + " in /dev/disk/by-path"; + return {}; +} + static int block_device_run_smartctl(const string& devname, int timeout, std::string *result) { @@ -885,6 +913,16 @@ std::string get_device_id(const std::string& devname, return std::string(); } +std::string get_device_path(const std::string& devname, + std::string *err) +{ + // FIXME: implement me + if (err) { + *err = "not implemented"; + } + return std::string(); +} + #elif defined(__FreeBSD__) const char *BlkDev::sysfsdir() const { @@ -1045,6 +1083,16 @@ std::string get_device_id(const std::string& devname, return std::string(); } +std::string get_device_path(const std::string& devname, + std::string *err) +{ + // FIXME: implement me for freebsd + if (err) { + *err = "not implemented for FreeBSD"; + } + return std::string(); +} + int block_device_run_smartctl(const char *device, int timeout, std::string *result) { @@ -1189,6 +1237,16 @@ std::string get_device_id(const std::string& devname, return std::string(); } +std::string get_device_path(const std::string& devname, + std::string *err) +{ + // not implemented + if (err) { + *err = "not implemented"; + } + return std::string(); +} + int block_device_run_smartctl(const char *device, int timeout, std::string *result) { @@ -1208,3 +1266,36 @@ int block_device_run_nvme(const char *device, const char *vendor, int timeout, } #endif + + + +void get_device_metadata( + const std::set<std::string>& devnames, + std::map<std::string,std::string> *pm, + std::map<std::string,std::string> *errs) +{ + (*pm)["devices"] = stringify(devnames); + string &devids = (*pm)["device_ids"]; + string &devpaths = (*pm)["device_paths"]; + for (auto& dev : devnames) { + string err; + string id = get_device_id(dev, &err); + if (id.size()) { + if (!devids.empty()) { + devids += ","; + } + devids += dev + "=" + id; + } else { + (*errs)[dev] = " no unique device id for "s + dev + ": " + err; + } + string path = get_device_path(dev, &err); + if (path.size()) { + if (!devpaths.empty()) { + devpaths += ","; + } + devpaths += dev + "=" + path; + } else { + (*errs)[dev] + " no unique device path for "s + dev + ": " + err; + } + } +} diff --git a/src/common/blkdev.h b/src/common/blkdev.h index 1e5f9469f4c..488e4b787ab 100644 --- a/src/common/blkdev.h +++ b/src/common/blkdev.h @@ -5,6 +5,7 @@ #define __CEPH_COMMON_BLKDEV_H #include <set> +#include <map> #include <string> #include "json_spirit/json_spirit_value.h" @@ -24,8 +25,20 @@ extern int get_device_by_path(const char *path, char* partition, char* device, s extern std::string _decode_model_enc(const std::string& in); // helper, exported only so we can unit test +// get $vendor_$model_$serial style device id extern std::string get_device_id(const std::string& devname, std::string *err=0); + +// get /dev/disk/by-path/... style device id that is stable for a disk slot across reboots etc +extern std::string get_device_path(const std::string& devname, + std::string *err=0); + +// populate daemon metadata map with device info +extern void get_device_metadata( + const std::set<std::string>& devnames, + std::map<std::string,std::string> *pm, + std::map<std::string,std::string> *errs); + extern void get_dm_parents(const std::string& dev, std::set<std::string> *ls); extern int block_device_get_metrics(const std::string& devname, int timeout, json_spirit::mValue *result); diff --git a/src/mgr/DaemonServer.cc b/src/mgr/DaemonServer.cc index 8af4a1a76b6..f72ae5cfbc4 100644 --- a/src/mgr/DaemonServer.cc +++ b/src/mgr/DaemonServer.cc @@ -1985,11 +1985,11 @@ bool DaemonServer::_handle_command( auto now = ceph_clock_now(); daemon_state.with_devices([&tbl, now](const DeviceState& dev) { string h; - for (auto& i : dev.devnames) { + for (auto& i : dev.attachments) { if (h.size()) { h += " "; } - h += i.first + ":" + i.second; + h += std::get<0>(i) + ":" + std::get<1>(i); } string d; for (auto& i : dev.daemons) { @@ -2037,11 +2037,11 @@ bool DaemonServer::_handle_command( daemon_state.with_device( i.first, [&tbl, now] (const DeviceState& dev) { string h; - for (auto& i : dev.devnames) { + for (auto& i : dev.attachments) { if (h.size()) { h += " "; } - h += i.first + ":" + i.second; + h += std::get<0>(i) + ":" + std::get<1>(i); } tbl << dev.devid << h @@ -2083,12 +2083,12 @@ bool DaemonServer::_handle_command( daemon_state.with_device( devid, [&tbl, &host, now] (const DeviceState& dev) { string n; - for (auto& j : dev.devnames) { - if (j.first == host) { + for (auto& j : dev.attachments) { + if (std::get<0>(j) == host) { if (n.size()) { n += " "; } - n += j.second; + n += std::get<1>(j); } } string d; diff --git a/src/mgr/DaemonState.cc b/src/mgr/DaemonState.cc index 13e42ff1006..87f820bffb7 100644 --- a/src/mgr/DaemonState.cc +++ b/src/mgr/DaemonState.cc @@ -96,10 +96,11 @@ void DeviceState::dump(Formatter *f) const { f->dump_string("devid", devid); f->open_array_section("location"); - for (auto& i : devnames) { + for (auto& i : attachments) { f->open_object_section("attachment"); - f->dump_string("host", i.first); - f->dump_string("dev", i.second); + f->dump_string("host", std::get<0>(i)); + f->dump_string("dev", std::get<1>(i)); + f->dump_string("path", std::get<2>(i)); f->close_section(); } f->close_section(); @@ -119,8 +120,10 @@ void DeviceState::dump(Formatter *f) const void DeviceState::print(ostream& out) const { out << "device " << devid << "\n"; - for (auto& i : devnames) { - out << "attachment " << i.first << ":" << i.second << "\n"; + for (auto& i : attachments) { + out << "attachment " << std::get<0>(i) << " " << std::get<1>(i) << " " + << std::get<2>(i) << "\n"; + out << "\n"; } std::copy(std::begin(daemons), std::end(daemons), std::experimental::make_ostream_joiner(out, ",")); @@ -150,7 +153,13 @@ void DaemonStateIndex::_insert(DaemonStatePtr dm) for (auto& i : dm->devices) { auto d = _get_or_create_device(i.first); d->daemons.insert(dm->key); - d->devnames.insert(make_pair(dm->hostname, i.second)); + auto p = dm->devices_bypath.find(i.first); + if (p != dm->devices_bypath.end()) { + d->attachments.insert(std::make_tuple(dm->hostname, i.second, p->second)); + } else { + d->attachments.insert(std::make_tuple(dm->hostname, i.second, + std::string())); + } } } @@ -166,7 +175,12 @@ void DaemonStateIndex::_erase(const DaemonKey& dmk) auto d = _get_or_create_device(i.first); ceph_assert(d->daemons.count(dmk)); d->daemons.erase(dmk); - d->devnames.erase(make_pair(dm->hostname, i.second)); + auto p = dm->devices_bypath.find(i.first); + if (p != dm->devices_bypath.end()) { + d->attachments.erase(make_tuple(dm->hostname, i.second, p->second)); + } else { + d->attachments.erase(make_tuple(dm->hostname, i.second, std::string())); + } if (d->empty()) { _erase_device(d); } diff --git a/src/mgr/DaemonState.h b/src/mgr/DaemonState.h index 1d374b225ea..1b9ef543dc3 100644 --- a/src/mgr/DaemonState.h +++ b/src/mgr/DaemonState.h @@ -134,6 +134,9 @@ class DaemonState /// device ids -> devname, derived from metadata[device_ids] std::map<std::string,std::string> devices; + /// device ids -> by-path, derived from metadata[device_ids] + std::map<std::string,std::string> devices_bypath; + // TODO: this can be generalized to other daemons std::vector<DaemonHealthMetric> daemon_health_metrics; @@ -164,14 +167,23 @@ class DaemonState void set_metadata(const std::map<std::string,std::string>& m) { devices.clear(); + devices_bypath.clear(); metadata = m; auto p = m.find("device_ids"); if (p != m.end()) { - map<std::string,std::string> devs; + map<std::string,std::string> devs, paths; // devname -> id or path get_str_map(p->second, &devs, ",; "); + auto q = m.find("device_paths"); + if (q != m.end()) { + get_str_map(q->second, &paths, ",; "); + } for (auto& i : devs) { if (i.second.size()) { // skip blank ids - devices[i.second] = i.first; + devices[i.second] = i.first; // id -> devname + auto j = paths.find(i.first); + if (j != paths.end()) { + devices_bypath[i.second] = j->second; // id -> path + } } } } @@ -201,7 +213,8 @@ typedef std::map<DaemonKey, DaemonStatePtr> DaemonStateCollection; struct DeviceState : public RefCountedObject { std::string devid; - std::set<pair<std::string,std::string>> devnames; ///< (server,devname) + /// (server,devname,path) + std::set<std::tuple<std::string,std::string,std::string>> attachments; std::set<DaemonKey> daemons; std::map<string,string> metadata; ///< persistent metadata diff --git a/src/mon/Monitor.cc b/src/mon/Monitor.cc index 27ea54b0b32..24b4982a507 100644 --- a/src/mon/Monitor.cc +++ b/src/mon/Monitor.cc @@ -2336,21 +2336,11 @@ void Monitor::collect_metadata(Metadata *m) string devname = store->get_devname(); set<string> devnames; get_raw_devices(devname, &devnames); - (*m)["devices"] = stringify(devnames); - string devids; - for (auto& devname : devnames) { - string err; - string id = get_device_id(devname, &err); - if (id.size()) { - if (!devids.empty()) { - devids += ","; - } - devids += devname + "=" + id; - } else { - derr << "failed to get devid for " << devname << ": " << err << dendl; - } + map<string,string> errs; + get_device_metadata(devnames, m, &errs); + for (auto& i : errs) { + dout(1) << __func__ << " " << i.first << ": " << i.second << dendl; } - (*m)["device_ids"] = devids; } void Monitor::finish_election() diff --git a/src/osd/OSD.cc b/src/osd/OSD.cc index fcdc14fda0b..fdd04b860f8 100644 --- a/src/osd/OSD.cc +++ b/src/osd/OSD.cc @@ -6573,23 +6573,11 @@ void OSD::_collect_metadata(map<string,string> *pm) set<string> devnames; store->get_devices(&devnames); - (*pm)["devices"] = stringify(devnames); - string devids; - for (auto& dev : devnames) { - string err; - string id = get_device_id(dev, &err); - if (id.size()) { - if (!devids.empty()) { - devids += ","; - } - devids += dev + "=" + id; - } else { - dout(10) << __func__ << " no unique device id for " << dev << ": " - << err << dendl; - } + map<string,string> errs; + get_device_metadata(devnames, pm, &errs); + for (auto& i : errs) { + dout(1) << __func__ << " " << i.first << ": " << i.second << dendl; } - (*pm)["device_ids"] = devids; - dout(10) << __func__ << " " << *pm << dendl; } diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index 962c864dc26..f0f8ea95c32 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -931,13 +931,13 @@ class CephadmOrchestrator(MgrModule, orchestrator.Orchestrator): def blink_device_light(self, ident_fault, on, locs): @async_map_completion - def blink(host, dev): + def blink(host, dev, path): cmd = [ 'lsmcli', 'local-disk-%s-led-%s' % ( ident_fault, 'on' if on else 'off'), - '--path', dev, + '--path', path or dev, ] out, err, code = self._run_cephadm( host, 'osd', 'shell', ['--'] + cmd, diff --git a/src/pybind/mgr/cephadm/tests/test_cephadm.py b/src/pybind/mgr/cephadm/tests/test_cephadm.py index 04287da7e30..7ccdcaf0e86 100644 --- a/src/pybind/mgr/cephadm/tests/test_cephadm.py +++ b/src/pybind/mgr/cephadm/tests/test_cephadm.py @@ -230,6 +230,6 @@ class TestCephadm(object): @mock.patch("cephadm.module.CephadmOrchestrator._get_connection") def test_blink_device_light(self, _send_command, _get_connection, cephadm_module): with self._with_host(cephadm_module, 'test'): - c = cephadm_module.blink_device_light('ident', True, [('test', '')]) + c = cephadm_module.blink_device_light('ident', True, [('test', '', '')]) assert self._wait(cephadm_module, c) == ['Set ident light for test: on'] diff --git a/src/pybind/mgr/orchestrator.py b/src/pybind/mgr/orchestrator.py index 751c62517bc..c411dabcb5e 100644 --- a/src/pybind/mgr/orchestrator.py +++ b/src/pybind/mgr/orchestrator.py @@ -1413,7 +1413,7 @@ class InventoryNode(object): return self.name == other.name and self.devices == other.devices -class DeviceLightLoc(namedtuple('DeviceLightLoc', ['host', 'dev'])): +class DeviceLightLoc(namedtuple('DeviceLightLoc', ['host', 'dev', 'path'])): """ Describes a specific device on a specific host. Used for enabling or disabling LEDs on devices. |