Blame SOURCES/nvme-cli-add-netapp-ontapdev-command-patch

8b3e94
commit 70bfe077af40aeace6c9e2b628b20cf8a34c1def
8b3e94
Author: Martin George <marting@netapp.com>
8b3e94
Date:   Mon Apr 1 09:39:56 2019 -0700
8b3e94
8b3e94
    nvme-cli: add netapp ontapdevices command
8b3e94
    
8b3e94
    Add a new ontapdevices command to the NetApp plugin for
8b3e94
    NetApp ONTAP devices.
8b3e94
    
8b3e94
    Signed-off-by: Martin George <marting@netapp.com>
8b3e94
    Reviewed-by: Steve Schremmer <sschremm@netapp.com>
8b3e94
8b3e94
diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c
8b3e94
index 416e74e..2951311 100644
8b3e94
--- a/plugins/netapp/netapp-nvme.c
8b3e94
+++ b/plugins/netapp/netapp-nvme.c
8b3e94
@@ -21,6 +21,7 @@
8b3e94
 #include <unistd.h>
8b3e94
 #include <errno.h>
8b3e94
 #include <string.h>
8b3e94
+#include <sys/ioctl.h>
8b3e94
 
8b3e94
 #include "nvme.h"
8b3e94
 #include "nvme-ioctl.h"
8b3e94
@@ -31,12 +32,28 @@
8b3e94
 #define CREATE_CMD
8b3e94
 #include "netapp-nvme.h"
8b3e94
 
8b3e94
+#define ONTAP_C2_LOG_ID		0xC2
8b3e94
+#define ONTAP_C2_LOG_SIZE	4096
8b3e94
+#define ONTAP_LABEL_LEN		260
8b3e94
+#define ONTAP_NS_PATHLEN	520
8b3e94
+
8b3e94
 enum {
8b3e94
 	NNORMAL,
8b3e94
 	NJSON,
8b3e94
 	NCOLUMN,
8b3e94
 };
8b3e94
 
8b3e94
+enum {
8b3e94
+	ONTAP_C2_LOG_SUPPORTED_LSP	= 0x0,
8b3e94
+	ONTAP_C2_LOG_NSINFO_LSP		= 0x1,
8b3e94
+};
8b3e94
+
8b3e94
+enum {
8b3e94
+	ONTAP_VSERVER_TLV		= 0x11,
8b3e94
+	ONTAP_VOLUME_TLV		= 0x12,
8b3e94
+	ONTAP_NS_TLV			= 0x13,
8b3e94
+};
8b3e94
+
8b3e94
 static const char *dev_path = "/dev/";
8b3e94
 
8b3e94
 struct smdevice_info {
8b3e94
@@ -46,6 +63,15 @@ struct smdevice_info {
8b3e94
 	char			dev[265];
8b3e94
 };
8b3e94
 
8b3e94
+struct ontapdevice_info {
8b3e94
+	int			nsid;
8b3e94
+	struct nvme_id_ctrl	ctrl;
8b3e94
+	struct nvme_id_ns	ns;
8b3e94
+	char			nsdesc[4096];
8b3e94
+	unsigned char		log_data[ONTAP_C2_LOG_SIZE];
8b3e94
+	char			dev[265];
8b3e94
+};
8b3e94
+
8b3e94
 #define ARRAY_LABEL_LEN		60
8b3e94
 #define VOLUME_LABEL_LEN	60
8b3e94
 
8b3e94
@@ -77,6 +103,100 @@ static void netapp_nguid_to_str(char *str, __u8 *nguid)
8b3e94
 		str += sprintf(str, "%02x", nguid[i]);
8b3e94
 }
8b3e94
 
8b3e94
+static void netapp_get_ns_size(char *size, long long *lba,
8b3e94
+		struct nvme_id_ns *ns)
8b3e94
+{
8b3e94
+	*lba = 1 << ns->lbaf[(ns->flbas & 0x0F)].ds;
8b3e94
+	double nsze = le64_to_cpu(ns->nsze) * (*lba);
8b3e94
+	const char *s_suffix = suffix_si_get(&nsze);
8b3e94
+
8b3e94
+	sprintf(size, "%.2f%sB", nsze, s_suffix);
8b3e94
+}
8b3e94
+
8b3e94
+static void netapp_uuid_to_str(char *str, void *data)
8b3e94
+{
8b3e94
+#ifdef LIBUUID
8b3e94
+	uuid_t uuid;
8b3e94
+	struct nvme_ns_id_desc *desc = data;
8b3e94
+
8b3e94
+	memcpy(uuid, data + sizeof(*desc), 16);
8b3e94
+	uuid_unparse_lower(uuid, str);
8b3e94
+#endif
8b3e94
+}
8b3e94
+
8b3e94
+static void ontap_labels_to_str(char *dst, char *src, int count)
8b3e94
+{
8b3e94
+	int i;
8b3e94
+
8b3e94
+	memset(dst, 0, ONTAP_LABEL_LEN);
8b3e94
+	for (i = 0; i < count; i++) {
8b3e94
+		if (src[i] >= '!' && src[i] <= '~')
8b3e94
+			dst[i] = src[i];
8b3e94
+		else
8b3e94
+			break;
8b3e94
+	}
8b3e94
+	dst[i] = '\0';
8b3e94
+}
8b3e94
+
8b3e94
+static void netapp_get_ontap_labels(char *vsname, char *nspath,
8b3e94
+		unsigned char *log_data)
8b3e94
+{
8b3e94
+	int lsp, tlv, label_len;
8b3e94
+	char *vserver_name, *volume_name, *namespace_name;
8b3e94
+	char vol_name[ONTAP_LABEL_LEN], ns_name[ONTAP_LABEL_LEN];
8b3e94
+	const char *ontap_vol = "/vol/";
8b3e94
+	int i, j;
8b3e94
+
8b3e94
+	/* get the lsp */
8b3e94
+	lsp = (*(__u8 *)&log_data[16]) & 0x0F;
8b3e94
+	if (lsp != ONTAP_C2_LOG_NSINFO_LSP)
8b3e94
+		/* lsp not related to nsinfo */
8b3e94
+		return;
8b3e94
+
8b3e94
+	/* get the vserver tlv and name */
8b3e94
+	tlv = *(__u8 *)&log_data[32];
8b3e94
+	if (tlv == ONTAP_VSERVER_TLV) {
8b3e94
+		label_len = (*(__u16 *)&log_data[34]) * 4;
8b3e94
+		vserver_name = (char *)&log_data[36];
8b3e94
+		ontap_labels_to_str(vsname, vserver_name, label_len);
8b3e94
+	} else {
8b3e94
+		/* not the expected vserver tlv */
8b3e94
+		fprintf(stderr, "Unable to fetch ONTAP vserver name\n");
8b3e94
+		return;
8b3e94
+	}
8b3e94
+
8b3e94
+	i = 36 + label_len;
8b3e94
+	j = i + 2;
8b3e94
+	/* get the volume tlv and name */
8b3e94
+	tlv = *(__u8 *)&log_data[i];
8b3e94
+	if (tlv == ONTAP_VOLUME_TLV) {
8b3e94
+		label_len = (*(__u16 *)&log_data[j]) * 4;
8b3e94
+		volume_name = (char *)&log_data[j + 2];
8b3e94
+		ontap_labels_to_str(vol_name, volume_name, label_len);
8b3e94
+	} else {
8b3e94
+		/* not the expected volume tlv */
8b3e94
+		fprintf(stderr, "Unable to fetch ONTAP volume name\n");
8b3e94
+		return;
8b3e94
+	}
8b3e94
+
8b3e94
+	i += 4 + label_len;
8b3e94
+	j += 4 + label_len;
8b3e94
+	/* get the namespace tlv and name */
8b3e94
+	tlv = *(__u8 *)&log_data[i];
8b3e94
+	if (tlv == ONTAP_NS_TLV) {
8b3e94
+		label_len = (*(__u16 *)&log_data[j]) * 4;
8b3e94
+		namespace_name = (char *)&log_data[j + 2];
8b3e94
+		ontap_labels_to_str(ns_name, namespace_name, label_len);
8b3e94
+	} else {
8b3e94
+		/* not the expected namespace tlv */
8b3e94
+		fprintf(stderr, "Unable to fetch ONTAP namespace name\n");
8b3e94
+		return;
8b3e94
+	}
8b3e94
+
8b3e94
+	snprintf(nspath, ONTAP_NS_PATHLEN, "%s%s%s%s", ontap_vol,
8b3e94
+			vol_name, "/", ns_name);
8b3e94
+}
8b3e94
+
8b3e94
 static void netapp_smdevice_json(struct json_array *devices, char *devname,
8b3e94
 		char *arrayname, char *volname, int nsid, char *nguid,
8b3e94
 		char *ctrl, char *astate, char *size, long long lba,
8b3e94
@@ -99,6 +219,25 @@ static void netapp_smdevice_json(struct json_array *devices, char *devname,
8b3e94
 	json_array_add_value_object(devices, device_attrs);
8b3e94
 }
8b3e94
 
8b3e94
+static void netapp_ontapdevice_json(struct json_array *devices, char *devname,
8b3e94
+		char *vsname, char *nspath, int nsid, char *uuid,
8b3e94
+		char *size, long long lba, long long nsze)
8b3e94
+{
8b3e94
+	struct json_object *device_attrs;
8b3e94
+
8b3e94
+	device_attrs = json_create_object();
8b3e94
+	json_object_add_value_string(device_attrs, "Device", devname);
8b3e94
+	json_object_add_value_string(device_attrs, "Vserver", vsname);
8b3e94
+	json_object_add_value_string(device_attrs, "Namespace_Path", nspath);
8b3e94
+	json_object_add_value_int(device_attrs, "NSID", nsid);
8b3e94
+	json_object_add_value_string(device_attrs, "UUID", uuid);
8b3e94
+	json_object_add_value_string(device_attrs, "Size", size);
8b3e94
+	json_object_add_value_int(device_attrs, "LBA_Data_Size", lba);
8b3e94
+	json_object_add_value_int(device_attrs, "Namespace_Size", nsze);
8b3e94
+
8b3e94
+	json_array_add_value_object(devices, device_attrs);
8b3e94
+}
8b3e94
+
8b3e94
 static void netapp_smdevices_print(struct smdevice_info *devices, int count, int format)
8b3e94
 {
8b3e94
 	struct json_object *root = NULL;
8b3e94
@@ -161,6 +300,94 @@ static void netapp_smdevices_print(struct smdevice_info *devices, int count, int
8b3e94
 	}
8b3e94
 }
8b3e94
 
8b3e94
+static void netapp_ontapdevices_print(struct ontapdevice_info *devices,
8b3e94
+		int count, int format)
8b3e94
+{
8b3e94
+	struct json_object *root = NULL;
8b3e94
+	struct json_array *json_devices = NULL;
8b3e94
+	char vsname[ONTAP_LABEL_LEN] = " ";
8b3e94
+	char nspath[ONTAP_NS_PATHLEN] = " ";
8b3e94
+	long long lba;
8b3e94
+	char size[128];
8b3e94
+	char uuid_str[37] = " ";
8b3e94
+	int i;
8b3e94
+
8b3e94
+	char basestr[] = "%s, Vserver %s, Namespace Path %s, NSID %d, UUID %s, %s\n";
8b3e94
+	char columnstr[] = "%-16s %-25s %-50s %-4d %-38s %-9s\n";
8b3e94
+
8b3e94
+	/* default to 'normal' output format */
8b3e94
+	char *formatstr = basestr;
8b3e94
+
8b3e94
+	if (format == NCOLUMN) {
8b3e94
+		/* change output string and print column headers */
8b3e94
+		formatstr = columnstr;
8b3e94
+		printf("%-16s %-25s %-50s %-4s %-38s %-9s\n",
8b3e94
+				"Device", "Vserver", "Namespace Path",
8b3e94
+				"NSID", "UUID", "Size");
8b3e94
+		printf("%-16s %-25s %-50s %-4s %-38s %-9s\n",
8b3e94
+				"----------------", "-------------------------",
8b3e94
+				"--------------------------------------------------",
8b3e94
+				"----", "--------------------------------------",
8b3e94
+				"---------");
8b3e94
+	} else if (format == NJSON) {
8b3e94
+		/* prepare for json output */
8b3e94
+		root = json_create_object();
8b3e94
+		json_devices = json_create_array();
8b3e94
+	}
8b3e94
+
8b3e94
+	for (i = 0; i < count; i++) {
8b3e94
+
8b3e94
+		netapp_get_ns_size(size, &lba, &devices[i].ns);
8b3e94
+		netapp_uuid_to_str(uuid_str, devices[i].nsdesc);
8b3e94
+		netapp_get_ontap_labels(vsname, nspath, devices[i].log_data);
8b3e94
+
8b3e94
+		if (format == NJSON) {
8b3e94
+			netapp_ontapdevice_json(json_devices, devices[i].dev,
8b3e94
+					vsname, nspath, devices[i].nsid,
8b3e94
+					uuid_str, size, lba,
8b3e94
+					le64_to_cpu(devices[i].ns.nsze));
8b3e94
+		} else
8b3e94
+			printf(formatstr, devices[i].dev, vsname, nspath,
8b3e94
+					devices[i].nsid, uuid_str, size);
8b3e94
+	}
8b3e94
+
8b3e94
+	if (format == NJSON) {
8b3e94
+		/* complete the json output */
8b3e94
+		json_object_add_value_array(root, "ONTAPdevices", json_devices);
8b3e94
+		json_print_object(root, NULL);
8b3e94
+	}
8b3e94
+}
8b3e94
+
8b3e94
+static int nvme_get_ontap_c2_log(int fd, __u32 nsid, void *buf, __u32 buflen)
8b3e94
+{
8b3e94
+	struct nvme_admin_cmd get_log;
8b3e94
+	int err;
8b3e94
+
8b3e94
+	memset(buf, 0, buflen);
8b3e94
+	memset(&get_log, 0, sizeof(struct nvme_admin_cmd));
8b3e94
+
8b3e94
+	get_log.opcode = nvme_admin_get_log_page;
8b3e94
+	get_log.nsid = nsid;
8b3e94
+	get_log.addr = (__u64)(uintptr_t)buf;
8b3e94
+	get_log.data_len = buflen;
8b3e94
+
8b3e94
+	__u32 numd = (get_log.data_len >> 2) - 1;
8b3e94
+	__u32 numdu = numd >> 16;
8b3e94
+	__u32 numdl = numd & 0xFFFF;
8b3e94
+
8b3e94
+	get_log.cdw10 = ONTAP_C2_LOG_ID | (numdl << 16);
8b3e94
+	get_log.cdw10 |= ONTAP_C2_LOG_NSINFO_LSP << 8;
8b3e94
+	get_log.cdw11 = numdu;
8b3e94
+
8b3e94
+	err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &get_log);
8b3e94
+	if (err) {
8b3e94
+		fprintf(stderr, "ioctl error %0x\n", err);
8b3e94
+		return 1;
8b3e94
+	}
8b3e94
+
8b3e94
+	return 0;
8b3e94
+}
8b3e94
+
8b3e94
 static int netapp_smdevices_get_info(int fd, struct smdevice_info *item,
8b3e94
 				     const char *dev)
8b3e94
 {
8b3e94
@@ -188,6 +415,50 @@ static int netapp_smdevices_get_info(int fd, struct smdevice_info *item,
8b3e94
 	return 1;
8b3e94
 }
8b3e94
 
8b3e94
+static int netapp_ontapdevices_get_info(int fd, struct ontapdevice_info *item,
8b3e94
+		const char *dev)
8b3e94
+{
8b3e94
+	int err;
8b3e94
+
8b3e94
+	err = nvme_identify_ctrl(fd, &item->ctrl);
8b3e94
+	if (err) {
8b3e94
+		fprintf(stderr, "Identify Controller failed to %s (%s)\n",
8b3e94
+				dev, strerror(err));
8b3e94
+		return 0;
8b3e94
+	}
8b3e94
+
8b3e94
+	if (strncmp("NetApp ONTAP Controller", item->ctrl.mn, 23) != 0)
8b3e94
+		/* not the right controller model */
8b3e94
+		return 0;
8b3e94
+
8b3e94
+	item->nsid = nvme_get_nsid(fd);
8b3e94
+
8b3e94
+	err = nvme_identify_ns(fd, item->nsid, 0, &item->ns);
8b3e94
+	if (err) {
8b3e94
+		fprintf(stderr, "Unable to identify namespace for %s (%s)\n",
8b3e94
+				dev, strerror(err));
8b3e94
+		return 0;
8b3e94
+	}
8b3e94
+
8b3e94
+	err = nvme_identify_ns_descs(fd, item->nsid, item->nsdesc);
8b3e94
+	if (err) {
8b3e94
+		fprintf(stderr, "Unable to identify namespace descriptor for %s (%s)\n",
8b3e94
+				dev, strerror(err));
8b3e94
+		return 0;
8b3e94
+	}
8b3e94
+
8b3e94
+	err = nvme_get_ontap_c2_log(fd, item->nsid, item->log_data, ONTAP_C2_LOG_SIZE);
8b3e94
+	if (err) {
8b3e94
+		fprintf(stderr, "Unable to get log page data for %s (%s)\n",
8b3e94
+				dev, strerror(err));
8b3e94
+		return 0;
8b3e94
+	}
8b3e94
+
8b3e94
+	strncpy(item->dev, dev, sizeof(item->dev));
8b3e94
+
8b3e94
+	return 1;
8b3e94
+}
8b3e94
+
8b3e94
 static int netapp_nvme_filter(const struct dirent *d)
8b3e94
 {
8b3e94
 	char path[264];
8b3e94
@@ -294,3 +565,74 @@ static int netapp_smdevices(int argc, char **argv, struct command *command,
8b3e94
 	free(smdevices);
8b3e94
 	return 0;
8b3e94
 }
8b3e94
+
8b3e94
+/* handler for 'nvme netapp ontapdevices' */
8b3e94
+static int netapp_ontapdevices(int argc, char **argv, struct command *command,
8b3e94
+		struct plugin *plugin)
8b3e94
+{
8b3e94
+	const char *desc = "Display information about ONTAP devices.";
8b3e94
+	struct config {
8b3e94
+		char *output_format;
8b3e94
+	};
8b3e94
+	struct config cfg = {
8b3e94
+		.output_format = "normal",
8b3e94
+	};
8b3e94
+	struct dirent **devices;
8b3e94
+	int num, i, fd, ret, fmt;
8b3e94
+	struct ontapdevice_info *ontapdevices;
8b3e94
+	char path[264];
8b3e94
+	int num_ontapdevices = 0;
8b3e94
+
8b3e94
+	const struct argconfig_commandline_options opts[] = {
8b3e94
+		{"output-format", 'o', "FMT", CFG_STRING, &cfg.output_format,
8b3e94
+			required_argument, "Output Format: normal|json|column"},
8b3e94
+		{NULL}
8b3e94
+	};
8b3e94
+
8b3e94
+	ret = argconfig_parse(argc, argv, desc, opts, &cfg, sizeof(cfg));
8b3e94
+	if (ret < 0)
8b3e94
+		return ret;
8b3e94
+
8b3e94
+	fmt = netapp_output_format(cfg.output_format);
8b3e94
+	if (fmt != NNORMAL && fmt != NCOLUMN && fmt != NJSON) {
8b3e94
+		fprintf(stderr, "Unrecognized output format: %s\n", cfg.output_format);
8b3e94
+		return -EINVAL;
8b3e94
+	}
8b3e94
+
8b3e94
+	num = scandir(dev_path, &devices, netapp_nvme_filter, alphasort);
8b3e94
+	if (num <= 0) {
8b3e94
+		fprintf(stderr, "No NVMe devices detected.\n");
8b3e94
+		return num;
8b3e94
+	}
8b3e94
+
8b3e94
+	ontapdevices = calloc(num, sizeof(*ontapdevices));
8b3e94
+	if (!ontapdevices) {
8b3e94
+		fprintf(stderr, "Unable to allocate memory for devices.\n");
8b3e94
+		return -ENOMEM;
8b3e94
+	}
8b3e94
+
8b3e94
+	for (i = 0; i < num; i++) {
8b3e94
+		snprintf(path, sizeof(path), "%s%s", dev_path,
8b3e94
+				devices[i]->d_name);
8b3e94
+		fd = open(path, O_RDONLY);
8b3e94
+		if (fd < 0) {
8b3e94
+			fprintf(stderr, "Unable to open %s: %s\n", path,
8b3e94
+					strerror(errno));
8b3e94
+			continue;
8b3e94
+		}
8b3e94
+
8b3e94
+		num_ontapdevices += netapp_ontapdevices_get_info(fd,
8b3e94
+				&ontapdevices[num_ontapdevices], path);
8b3e94
+
8b3e94
+		close(fd);
8b3e94
+	}
8b3e94
+
8b3e94
+	if (num_ontapdevices)
8b3e94
+		netapp_ontapdevices_print(ontapdevices, num_ontapdevices, fmt);
8b3e94
+
8b3e94
+	for (i = 0; i < num; i++)
8b3e94
+		free(devices[i]);
8b3e94
+	free(devices);
8b3e94
+	free(ontapdevices);
8b3e94
+	return 0;
8b3e94
+}
8b3e94
diff --git a/plugins/netapp/netapp-nvme.h b/plugins/netapp/netapp-nvme.h
8b3e94
index 3dd019e..d4eebd6 100644
8b3e94
--- a/plugins/netapp/netapp-nvme.h
8b3e94
+++ b/plugins/netapp/netapp-nvme.h
8b3e94
@@ -9,6 +9,7 @@
8b3e94
 PLUGIN(NAME("netapp", "NetApp vendor specific extensions"),
8b3e94
 	COMMAND_LIST(
8b3e94
 		ENTRY("smdevices", "NetApp SMdevices", netapp_smdevices)
8b3e94
+		ENTRY("ontapdevices", "NetApp ONTAPdevices", netapp_ontapdevices)
8b3e94
 	)
8b3e94
 );
8b3e94