return device_id;
}
-int block_device_get_metrics(const char *device, int timeout,
- json_spirit::mValue *result)
+static std::string get_device_vendor(const std::string& devname)
{
- std::string s;
- if (int r = block_device_run_smartctl(device, timeout, &s); r != 0) {
- s = "{\"error\": \"smartctl failed\", \"dev\": \"";
- s += device;
- s += "\", \"smartctl_error_code\": " + stringify(r);
- s += "\", \"smartctl_output\": \"" + s;
- s += + "\"}";
+ struct udev_device *dev;
+ static struct udev *udev;
+ const char *data;
+
+ udev = udev_new();
+ if (!udev) {
+ return {};
}
- if (json_spirit::read(s, *result)) {
- return 0;
+ dev = udev_device_new_from_subsystem_sysname(udev, "block", devname.c_str());
+ if (!dev) {
+ udev_unref(udev);
+ return {};
}
- s = "{\"error\": \"smartctl returned invalid JSON\", \"dev\": \"";
- s += device;
- s += "\"}";
- if (json_spirit::read(s, *result)) {
- return 0;
+
+ std::string id_vendor, id_model;
+ data = udev_device_get_property_value(dev, "ID_VENDOR");
+ if (data) {
+ id_vendor = data;
+ }
+ data = udev_device_get_property_value(dev, "ID_MODEL");
+ if (data) {
+ id_model = data;
+ }
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ std::transform(id_vendor.begin(), id_vendor.end(), id_vendor.begin(),
+ ::tolower);
+ std::transform(id_model.begin(), id_model.end(), id_model.begin(),
+ ::tolower);
+
+ if (id_vendor.size()) {
+ return id_vendor;
+ }
+ if (id_model.size()) {
+ int pos = id_model.find(" ");
+ if (pos > 0) {
+ return id_model.substr(0, pos);
+ } else {
+ return id_model;
+ }
+ }
+
+ std::string vendor, model;
+ char buf[1024] = {0};
+ BlkDev blkdev(devname);
+ if (!blkdev.vendor(buf, sizeof(buf))) {
+ vendor = buf;
}
- return -EINVAL;
+ if (!blkdev.model(buf, sizeof(buf))) {
+ model = buf;
+ }
+ if (vendor.size()) {
+ return vendor;
+ }
+ if (model.size()) {
+ int pos = model.find(" ");
+ if (pos > 0) {
+ return model.substr(0, pos);
+ } else {
+ return model;
+ }
+ }
+
+ return {};
}
-int block_device_run_smartctl(const char *device, int timeout,
- std::string *result)
+static int block_device_run_vendor_nvme(
+ const string& devname, const string& vendor, int timeout,
+ std::string *result)
+{
+ string device = "/dev/" + devname;
+
+ SubProcessTimed nvmecli(
+ "sudo", SubProcess::CLOSE, SubProcess::PIPE, SubProcess::CLOSE,
+ timeout);
+ nvmecli.add_cmd_args(
+ "nvme",
+ vendor.c_str(),
+ "smart-log-add",
+ "--json",
+ device.c_str(),
+ NULL);
+ int ret = nvmecli.spawn();
+ if (ret != 0) {
+ *result = std::string("error spawning nvme command: ") + nvmecli.err();
+ return ret;
+ }
+
+ bufferlist output;
+ ret = output.read_fd(nvmecli.get_stdout(), 100*1024);
+ if (ret < 0) {
+ bufferlist err;
+ err.read_fd(nvmecli.get_stderr(), 100 * 1024);
+ *result = std::string("failed to execute nvme: ") + err.to_str();
+ } else {
+ ret = 0;
+ *result = output.to_str();
+ }
+
+ if (nvmecli.join() != 0) {
+ *result = std::string("nvme returned an error: ") + nvmecli.err();
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int block_device_run_smartctl(const string& devname, int timeout,
+ std::string *result)
{
+ string device = "/dev/" + devname;
+
// when using --json, smartctl will report its errors in JSON format to stdout
SubProcessTimed smartctl(
"sudo", SubProcess::CLOSE, SubProcess::PIPE, SubProcess::CLOSE,
"-a",
//"-x",
"--json",
- device,
+ device.c_str(),
NULL);
int ret = smartctl.spawn();
return ret;
}
+int block_device_get_metrics(const string& devname, int timeout,
+ json_spirit::mValue *result)
+{
+ std::string s;
+
+ // smartctl
+ if (int r = block_device_run_smartctl(devname, timeout, &s);
+ r != 0) {
+ s = "{\"error\": \"smartctl failed\", \"dev\": \"/dev/";
+ s += devname;
+ s += "\", \"smartctl_error_code\": " + stringify(r);
+ s += "\", \"smartctl_output\": \"" + s;
+ s += + "\"}";
+ }
+ if (!json_spirit::read(s, *result)) {
+ s = "{\"error\": \"smartctl returned invalid JSON\", \"dev\": \"/dev/";
+ s += devname;
+ s += "\"}";
+ }
+ if (!json_spirit::read(s, *result)) {
+ return -EINVAL;
+ }
+
+ json_spirit::mObject& base = result->get_obj();
+ string vendor = get_device_vendor(devname);
+ if (vendor.size()) {
+ base["nvme_vendor"] = vendor;
+ s.clear();
+ json_spirit::mValue nvme_json;
+ if (int r = block_device_run_vendor_nvme(devname, vendor, timeout, &s);
+ r == 0) {
+ if (json_spirit::read(s, nvme_json) != 0) {
+ base["nvme_smart_health_information_add_log"] = nvme_json;
+ } else {
+ base["nvme_smart_health_information_add_log_error"] = "bad json output: "
+ + s;
+ }
+ } else {
+ base["nvme_smart_health_information_add_log_error_code"] = r;
+ base["nvme_smart_health_information_add_log_error"] = s;
+ }
+ } else {
+ base["nvme_vendor"] = "unknown";
+ }
+
+ return 0;
+}
#elif defined(__APPLE__)
#include <sys/disk.h>
return -EOPNOTSUPP;
}
+int block_device_run_nvme(const char *device, const char *vendor, int timeout,
+ std::string *result)
+{
+ return -EOPNOTSUPP;
+}
+
static int block_device_devname(int fd, char *devname, size_t max)
{
struct fiodgname_arg arg;
return -EOPNOTSUPP;
}
+int block_device_run_nvme(const char *device, const char *vendor, int timeout,
+ std::string *result)
+{
+ return -EOPNOTSUPP;
+}
+
#endif