summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/common/blkdev.cc91
-rw-r--r--src/common/blkdev.h13
-rw-r--r--src/mgr/DaemonServer.cc14
-rw-r--r--src/mgr/DaemonState.cc28
-rw-r--r--src/mgr/DaemonState.h19
-rw-r--r--src/mon/Monitor.cc18
-rw-r--r--src/osd/OSD.cc20
-rw-r--r--src/pybind/mgr/cephadm/module.py4
-rw-r--r--src/pybind/mgr/cephadm/tests/test_cephadm.py2
-rw-r--r--src/pybind/mgr/orchestrator.py2
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.