summaryrefslogtreecommitdiffstats
path: root/src/spdk/scripts/spdkcli
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
commit483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch)
treee5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/spdk/scripts/spdkcli
parentInitial commit. (diff)
downloadceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz
ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/scripts/spdkcli')
-rw-r--r--src/spdk/scripts/spdkcli/__init__.py1
-rw-r--r--src/spdk/scripts/spdkcli/ui_node.py929
-rw-r--r--src/spdk/scripts/spdkcli/ui_node_iscsi.py635
-rw-r--r--src/spdk/scripts/spdkcli/ui_node_nvmf.py302
-rw-r--r--src/spdk/scripts/spdkcli/ui_root.py482
5 files changed, 2349 insertions, 0 deletions
diff --git a/src/spdk/scripts/spdkcli/__init__.py b/src/spdk/scripts/spdkcli/__init__.py
new file mode 100644
index 00000000..571d49a8
--- /dev/null
+++ b/src/spdk/scripts/spdkcli/__init__.py
@@ -0,0 +1 @@
+from .ui_root import UIRoot
diff --git a/src/spdk/scripts/spdkcli/ui_node.py b/src/spdk/scripts/spdkcli/ui_node.py
new file mode 100644
index 00000000..43f6bdfc
--- /dev/null
+++ b/src/spdk/scripts/spdkcli/ui_node.py
@@ -0,0 +1,929 @@
+from configshell_fb import ConfigNode, ExecutionError
+from uuid import UUID
+from rpc.client import JSONRPCException
+import json
+
+
+def convert_bytes_to_human(size):
+ if not size:
+ return ""
+ for x in ["bytes", "K", "M", "G", "T"]:
+ if size < 1024.0:
+ return "%3.1f%s" % (size, x)
+ size /= 1024.0
+
+
+class UINode(ConfigNode):
+ def __init__(self, name, parent=None, shell=None):
+ ConfigNode.__init__(self, name, parent, shell)
+
+ def refresh(self):
+ for child in self.children:
+ child.refresh()
+
+ def ui_command_refresh(self):
+ self.refresh()
+
+ def ui_command_ll(self, path=None, depth=None):
+ """
+ Alias for ls.
+ """
+ self.ui_command_ls(path, depth)
+
+ def execute_command(self, command, pparams=[], kparams={}):
+ try:
+ result = ConfigNode.execute_command(self, command,
+ pparams, kparams)
+ except Exception as msg:
+ self.shell.log.error(str(msg))
+ pass
+ else:
+ self.shell.log.debug("Command %s succeeded." % command)
+ return result
+
+
+class UIBdevs(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "bdevs", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ UIMallocBdev(self)
+ UIAIOBdev(self)
+ UILvolBdev(self)
+ UINvmeBdev(self)
+ UINullBdev(self)
+ UIErrorBdev(self)
+ UISplitBdev(self)
+ UIPmemBdev(self)
+ UIRbdBdev(self)
+ UIiSCSIBdev(self)
+ UIVirtioBlkBdev(self)
+ UIVirtioScsiBdev(self)
+
+
+class UILvolStores(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "lvol_stores", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for lvs in self.get_root().get_lvol_stores():
+ UILvsObj(lvs, self)
+
+ def ui_command_create(self, name, bdev_name, cluster_size=None):
+ """
+ Creates logical volume store on target bdev.
+
+ Arguments:
+ name - Friendly name to use alongside with UUID identifier.
+ bdev_name - On which bdev to create the lvol store.
+ cluster_size - Cluster size to use when creating lvol store, in bytes. Default: 4194304.
+ """
+
+ cluster_size = self.ui_eval_param(cluster_size, "number", None)
+
+ try:
+ self.get_root().create_lvol_store(lvs_name=name, bdev_name=bdev_name, cluster_sz=cluster_size)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name=None, uuid=None):
+ """
+ Deletes logical volume store from configuration.
+ This will also delete all logical volume bdevs created on this lvol store!
+
+ Arguments:
+ name - Friendly name of the logical volume store to be deleted.
+ uuid - UUID number of the logical volume store to be deleted.
+ """
+ if name is None and uuid is None:
+ self.shell.log.error("Please specify one of the identifiers: "
+ "lvol store name or UUID")
+ self.get_root().delete_lvol_store(lvs_name=name, uuid=uuid)
+ self.get_root().refresh()
+ self.refresh()
+
+ def summary(self):
+ return "Lvol stores: %s" % len(self.children), None
+
+
+class UIBdev(UINode):
+ def __init__(self, name, parent):
+ UINode.__init__(self, name, parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for bdev in self.get_root().get_bdevs(self.name):
+ UIBdevObj(bdev, self)
+
+ def ui_command_get_bdev_iostat(self, name=None):
+ try:
+ ret = self.get_root().get_bdevs_iostat(name=name)
+ self.shell.log.info(json.dumps(ret, indent=2))
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ def summary(self):
+ return "Bdevs: %d" % len(self.children), None
+
+
+class UIMallocBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "malloc", parent)
+
+ def ui_command_create(self, size, block_size, name=None, uuid=None):
+ """
+ Construct a Malloc bdev.
+
+ Arguments:
+ size - Size in megabytes.
+ block_size - Integer, block size to use when constructing bdev.
+ name - Optional argument. Custom name to use for bdev. If not provided
+ then name will be "MallocX" where X is next available ID.
+ uuid - Optional parameter. Custom UUID to use. If empty then random
+ will be generated.
+ """
+
+ size = self.ui_eval_param(size, "number", None)
+ block_size = self.ui_eval_param(block_size, "number", None)
+
+ try:
+ ret_name = self.get_root().create_malloc_bdev(num_blocks=size * 1024 * 1024 // block_size,
+ block_size=block_size,
+ name=name, uuid=uuid)
+ self.shell.log.info(ret_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes malloc bdev from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the malloc bdev to be deleted - UUID number or name alias.
+ """
+ try:
+ self.get_root().delete_malloc_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIAIOBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "aio", parent)
+
+ def ui_command_create(self, name, filename, block_size):
+ """
+ Construct an AIO bdev.
+ Backend file must exist before trying to create an AIO bdev.
+
+ Arguments:
+ name - Optional argument. Custom name to use for bdev. If not provided
+ then name will be "MallocX" where X is next available ID.
+ filename - Path to AIO backend.
+ block_size - Integer, block size to use when constructing bdev.
+ """
+
+ block_size = self.ui_eval_param(block_size, "number", None)
+
+ try:
+ ret_name = self.get_root().create_aio_bdev(name=name,
+ block_size=int(block_size),
+ filename=filename)
+ self.shell.log.info(ret_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes aio bdev from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the aio bdev to be deleted - UUID number or name alias.
+ """
+ try:
+ self.get_root().delete_aio_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UILvolBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "logical_volume", parent)
+
+ def ui_command_create(self, name, size, lvs, thin_provision=None):
+ """
+ Construct a Logical Volume bdev.
+
+ Arguments:
+ name - Friendly name to use for creating logical volume bdev.
+ size - Size in megabytes.
+ lvs - Identifier of logical volume store on which the bdev should be
+ created. Can be either a friendly name or UUID.
+ thin_provision - Whether the bdev should be thick or thin provisioned.
+ Default is False, and created bdevs are thick-provisioned.
+ """
+ uuid = None
+ lvs_name = None
+ try:
+ UUID(lvs)
+ uuid = lvs
+ except ValueError:
+ lvs_name = lvs
+
+ size = self.ui_eval_param(size, "number", None)
+ size *= (1024 * 1024)
+ thin_provision = self.ui_eval_param(thin_provision, "bool", False)
+
+ try:
+ ret_uuid = self.get_root().create_lvol_bdev(lvol_name=name, size=size,
+ lvs_name=lvs_name, uuid=uuid,
+ thin_provision=thin_provision)
+ self.shell.log.info(ret_uuid)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes lvol bdev from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the lvol bdev to be deleted - UUID number or name alias.
+ """
+ try:
+ self.get_root().destroy_lvol_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UINvmeBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "nvme", parent)
+
+ def ui_command_create(self, name, trtype, traddr,
+ adrfam=None, trsvcid=None, subnqn=None):
+
+ if "rdma" in trtype and None in [adrfam, trsvcid, subnqn]:
+ self.shell.log.error("Using RDMA transport type."
+ "Please provide arguments for adrfam, trsvcid and subnqn.")
+
+ try:
+ ret_name = self.get_root().create_nvme_bdev(name=name, trtype=trtype,
+ traddr=traddr, adrfam=adrfam,
+ trsvcid=trsvcid, subnqn=subnqn)
+ self.shell.log.info(ret_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes NVMe controller from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the NVMe controller to be deleted.
+ """
+ try:
+ self.get_root().delete_nvme_controller(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UINullBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "null", parent)
+
+ def ui_command_create(self, name, size, block_size, uuid=None):
+ """
+ Construct a Null bdev.
+
+ Arguments:
+ name - Name to use for bdev.
+ size - Size in megabytes.
+ block_size - Integer, block size to use when constructing bdev.
+ uuid - Optional parameter. Custom UUID to use. If empty then random
+ will be generated.
+ """
+
+ size = self.ui_eval_param(size, "number", None)
+ block_size = self.ui_eval_param(block_size, "number", None)
+ num_blocks = size * 1024 * 1024 // block_size
+
+ try:
+ ret_name = self.get_root().create_null_bdev(num_blocks=num_blocks,
+ block_size=block_size,
+ name=name, uuid=uuid)
+ self.shell.log.info(ret_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes null bdev from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the null bdev to be deleted - UUID number or name alias.
+ """
+ try:
+ self.get_root().delete_null_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIErrorBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "error", parent)
+
+ def ui_command_create(self, base_name):
+ """
+ Construct a error injection bdev.
+
+ Arguments:
+ base_name - base bdev name on top of which error bdev will be created.
+ """
+
+ try:
+ self.get_root().create_error_bdev(base_name=base_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes error bdev from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the error bdev to be deleted - UUID number or name alias.
+ """
+ try:
+ self.get_root().delete_error_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UISplitBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "split_disk", parent)
+
+ def ui_command_split_bdev(self, base_bdev, split_count, split_size_mb=None):
+ """
+ Construct split block devices from a base bdev.
+
+ Arguments:
+ base_bdev - Name of bdev to split
+ split_count - Number of split bdevs to create
+ split_size_mb- Size of each split volume in MiB (optional)
+ """
+
+ split_count = self.ui_eval_param(split_count, "number", None)
+ split_size_mb = self.ui_eval_param(split_size_mb, "number", None)
+
+ try:
+ ret_name = self.get_root().split_bdev(base_bdev=base_bdev,
+ split_count=split_count,
+ split_size_mb=split_size_mb)
+ self.shell.log.info(ret_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.parent.refresh()
+ self.refresh()
+
+ def ui_command_destruct_split_bdev(self, base_bdev):
+ """Destroy split block devices associated with base bdev.
+
+ Args:
+ base_bdev: name of previously split bdev
+ """
+
+ try:
+ self.get_root().destruct_split_bdev(base_bdev=base_bdev)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.parent.refresh()
+ self.refresh()
+
+
+class UIPmemBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "pmemblk", parent)
+
+ def ui_command_create_pmem_pool(self, pmem_file, total_size, block_size):
+ total_size = self.ui_eval_param(total_size, "number", None)
+ block_size = self.ui_eval_param(block_size, "number", None)
+ num_blocks = int((total_size * 1024 * 1024) / block_size)
+
+ try:
+ self.get_root().create_pmem_pool(pmem_file=pmem_file,
+ num_blocks=num_blocks,
+ block_size=block_size)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ def ui_command_delete_pmem_pool(self, pmem_file):
+ try:
+ self.get_root().delete_pmem_pool(pmem_file=pmem_file)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ def ui_command_info_pmem_pool(self, pmem_file):
+ try:
+ ret = self.get_root().delete_pmem_pool(pmem_file=pmem_file)
+ self.shell.log.info(ret)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ def ui_command_create(self, pmem_file, name):
+ try:
+ ret_name = self.get_root().create_pmem_bdev(pmem_file=pmem_file,
+ name=name)
+ self.shell.log.info(ret_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes pmem bdev from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the pmem bdev to be deleted - UUID number or name alias.
+ """
+ try:
+ self.get_root().delete_pmem_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIRbdBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "rbd", parent)
+
+ def ui_command_create(self, pool_name, rbd_name, block_size, name=None):
+ block_size = self.ui_eval_param(block_size, "number", None)
+
+ try:
+ ret_name = self.get_root().create_rbd_bdev(pool_name=pool_name,
+ rbd_name=rbd_name,
+ block_size=block_size,
+ name=name)
+ self.shell.log.info(ret_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes rbd bdev from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the rbd bdev to be deleted - UUID number or name alias.
+ """
+ try:
+ self.get_root().delete_rbd_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIiSCSIBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "iscsi", parent)
+
+ def ui_command_create(self, name, url, initiator_iqn):
+ """
+ Create iSCSI bdev in configuration by connecting to remote
+ iSCSI target.
+
+ Arguments:
+ name - name to be used as an ID for created iSCSI bdev.
+ url - iscsi url pointing to LUN on remote iSCSI target.
+ Example: iscsi://127.0.0.1:3260/iqn.2018-06.org.spdk/0.
+ initiator_iqn - IQN to use for initiating connection with the target.
+ """
+ try:
+ ret_name = self.get_root().create_iscsi_bdev(name=name,
+ url=url,
+ initiator_iqn=initiator_iqn)
+ self.shell.log.info(ret_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes iSCSI bdev from configuration.
+
+ Arguments:
+ name - name of the iscsi bdev to be deleted.
+ """
+ try:
+ self.get_root().delete_iscsi_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIVirtioBlkBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "virtioblk_disk", parent)
+
+ def ui_command_create(self, name, trtype, traddr,
+ vq_count=None, vq_size=None):
+
+ vq_count = self.ui_eval_param(vq_count, "number", None)
+ vq_size = self.ui_eval_param(vq_size, "number", None)
+
+ try:
+ ret = self.get_root().create_virtio_dev(name=name,
+ trtype=trtype,
+ traddr=traddr,
+ dev_type="blk",
+ vq_count=vq_count,
+ vq_size=vq_size)
+
+ self.shell.log.info(ret)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Deletes virtio scsi bdev from configuration.
+
+ Arguments:
+ name - Is a unique identifier of the virtio scsi bdev to be deleted - UUID number or name alias.
+ """
+ try:
+ self.get_root().remove_virtio_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIVirtioScsiBdev(UIBdev):
+ def __init__(self, parent):
+ UIBdev.__init__(self, "virtioscsi_disk", parent)
+
+ def refresh(self):
+ self._children = set([])
+ for bdev in self.get_root().get_virtio_scsi_devs():
+ UIVirtioScsiBdevObj(bdev, self)
+
+ def ui_command_create(self, name, trtype, traddr,
+ vq_count=None, vq_size=None):
+
+ vq_count = self.ui_eval_param(vq_count, "number", None)
+ vq_size = self.ui_eval_param(vq_size, "number", None)
+
+ try:
+ ret = self.get_root().create_virtio_dev(name=name,
+ trtype=trtype,
+ traddr=traddr,
+ dev_type="scsi",
+ vq_count=vq_count,
+ vq_size=vq_size)
+
+ self.shell.log.info(ret)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ try:
+ self.get_root().remove_virtio_bdev(name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIBdevObj(UINode):
+ def __init__(self, bdev, parent):
+ self.bdev = bdev
+ # Using bdev name also for lvol bdevs, which results in displying
+ # UUID instead of alias. This is because alias naming convention
+ # (lvol_store_name/lvol_bdev_name) conflicts with configshell paths
+ # ("/" as separator).
+ # Solution: show lvol alias in "summary field" for now.
+ # TODO: Possible next steps:
+ # - Either change default separator in tree for smth else
+ # - or add a UI command which would be able to autocomplete
+ # "cd" command based on objects alias and match is to the
+ # "main" bdev name.
+ UINode.__init__(self, self.bdev.name, parent)
+
+ def ui_command_show_details(self):
+ self.shell.log.info(json.dumps(vars(self.bdev), indent=2))
+
+ def summary(self):
+ size = convert_bytes_to_human(self.bdev.block_size * self.bdev.num_blocks)
+ size = "=".join(["Size", size])
+
+ in_use = "Not claimed"
+ if bool(self.bdev.claimed):
+ in_use = "Claimed"
+
+ alias = None
+ if self.bdev.aliases:
+ alias = self.bdev.aliases[0]
+
+ info = ", ".join([_f for _f in [alias, size, in_use] if _f])
+ return info, True
+
+
+class UIVirtioScsiBdevObj(UIBdevObj):
+ def __init__(self, bdev, parent):
+ UIBdevObj.__init__(self, bdev, parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for bdev in self.get_root().get_bdevs("virtio_scsi_disk"):
+ if self.bdev.name in bdev.name:
+ UIBdevObj(bdev, self)
+
+ def summary(self):
+ if "socket" in list(self.bdev.virtio.keys()):
+ info = self.bdev.virtio["socket"]
+ if "pci_address" in list(self.bdev.virtio.keys()):
+ info = self.bdev.virtio["pci_address"]
+ return info, True
+
+
+class UILvsObj(UINode):
+ def __init__(self, lvs, parent):
+ UINode.__init__(self, lvs.name, parent)
+ self.lvs = lvs
+
+ def ui_command_show_details(self):
+ self.shell.log.info(json.dumps(vars(self.lvs), indent=2))
+
+ def summary(self):
+ size = convert_bytes_to_human(self.lvs.total_data_clusters * self.lvs.cluster_size)
+ free = convert_bytes_to_human(self.lvs.free_clusters * self.lvs.cluster_size)
+ if not free:
+ free = "0"
+ size = "=".join(["Size", size])
+ free = "=".join(["Free", free])
+ info = ", ".join([str(size), str(free)])
+ return info, True
+
+
+class UIVhosts(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "vhost", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ self.get_root().list_vhost_ctrls()
+ UIVhostBlk(self)
+ UIVhostScsi(self)
+
+
+class UIVhost(UINode):
+ def __init__(self, name, parent):
+ UINode.__init__(self, name, parent)
+ self.refresh()
+
+ def ui_command_delete(self, name):
+ """
+ Delete a Vhost controller from configuration.
+
+ Arguments:
+ name - Controller name.
+ """
+ self.get_root().remove_vhost_controller(ctrlr=name)
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIVhostBlk(UIVhost):
+ def __init__(self, parent):
+ UIVhost.__init__(self, "block", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for ctrlr in self.get_root().get_vhost_ctrlrs(self.name):
+ UIVhostBlkCtrlObj(ctrlr, self)
+
+ def ui_command_create(self, name, bdev, cpumask=None, readonly=False):
+ """
+ Construct a Vhost BLK controller.
+
+ Arguments:
+ name - Controller name.
+ bdev - Which bdev to attach to the controller.
+ cpumask - Optional. Integer to specify mask of CPUs to use.
+ Default: 1.
+ readonly - Whether controller should be read only or not.
+ Default: False.
+ """
+ try:
+ self.get_root().create_vhost_blk_controller(ctrlr=name,
+ dev_name=bdev,
+ cpumask=cpumask,
+ readonly=bool(readonly))
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIVhostScsi(UIVhost):
+ def __init__(self, parent):
+ UIVhost.__init__(self, "scsi", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for ctrlr in self.get_root().get_vhost_ctrlrs(self.name):
+ UIVhostScsiCtrlObj(ctrlr, self)
+
+ def ui_command_create(self, name, cpumask=None):
+ """
+ Construct a Vhost SCSI controller.
+
+ Arguments:
+ name - Controller name.
+ cpumask - Optional. Integer to specify mask of CPUs to use.
+ Default: 1.
+ """
+ try:
+ self.get_root().create_vhost_scsi_controller(ctrlr=name,
+ cpumask=cpumask)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.get_root().refresh()
+ self.refresh()
+
+
+class UIVhostCtrl(UINode):
+ # Base class for SCSI and BLK controllers, do not instantiate
+ def __init__(self, ctrlr, parent):
+ self.ctrlr = ctrlr
+ UINode.__init__(self, self.ctrlr.ctrlr, parent)
+ self.refresh()
+
+ def ui_command_show_details(self):
+ self.shell.log.info(json.dumps(vars(self.ctrlr), indent=2))
+
+ def ui_command_set_coalescing(self, delay_base_us, iops_threshold):
+ delay_base_us = self.ui_eval_param(delay_base_us, "number", None)
+ iops_threshold = self.ui_eval_param(iops_threshold, "number", None)
+
+ try:
+ self.get_root().set_vhost_controller_coalescing(ctrlr=self.ctrlr.ctrlr,
+ delay_base_us=delay_base_us,
+ iops_threshold=iops_threshold)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+
+class UIVhostScsiCtrlObj(UIVhostCtrl):
+ def refresh(self):
+ self._children = set([])
+ for lun in self.ctrlr.backend_specific["scsi"]:
+ UIVhostTargetObj(lun, self)
+
+ def ui_command_remove_target(self, target_num):
+ """
+ Remove target node from SCSI controller.
+
+ Arguments:
+ target_num - Integer identifier of target node to delete.
+ """
+ try:
+ self.get_root().remove_vhost_scsi_target(ctrlr=self.ctrlr.ctrlr,
+ scsi_target_num=int(target_num))
+ for ctrlr in self.get_root().get_vhost_ctrlrs("scsi"):
+ if ctrlr.ctrlr == self.ctrlr.ctrlr:
+ self.ctrlr = ctrlr
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+ self.get_root().refresh()
+
+ def ui_command_add_lun(self, target_num, bdev_name):
+ """
+ Add LUN to SCSI target node.
+ Currently only one LUN (which is LUN ID 0) per target is supported.
+ Adding LUN to not existing target node will create that node.
+
+ Arguments:
+ target_num - Integer identifier of target node to modify.
+ bdev - Which bdev to add as LUN.
+ """
+ try:
+ self.get_root().add_vhost_scsi_lun(ctrlr=self.ctrlr.ctrlr,
+ scsi_target_num=int(target_num),
+ bdev_name=bdev_name)
+ for ctrlr in self.get_root().get_vhost_ctrlrs("scsi"):
+ if ctrlr.ctrlr == self.ctrlr.ctrlr:
+ self.ctrlr = ctrlr
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def summary(self):
+ info = self.ctrlr.socket
+ return info, True
+
+
+class UIVhostBlkCtrlObj(UIVhostCtrl):
+ def refresh(self):
+ self._children = set([])
+ UIVhostLunDevObj(self.ctrlr.backend_specific["block"]["bdev"], self)
+
+ def summary(self):
+ ro = None
+ if self.ctrlr.backend_specific["block"]["readonly"]:
+ ro = "Readonly"
+ info = ", ".join([_f for _f in [self.ctrlr.socket, ro] if _f])
+ return info, True
+
+
+class UIVhostTargetObj(UINode):
+ def __init__(self, target, parent):
+ self.target = target
+ # Next line: configshell does not allow paths with spaces.
+ UINode.__init__(self, target["target_name"].replace(" ", "_"), parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for target in self.target["luns"]:
+ UIVhostLunDevObj(target["bdev_name"], self)
+
+ def ui_command_show_details(self):
+ self.shell.log.info(json.dumps(self.target, indent=2))
+
+ def summary(self):
+ luns = "LUNs: %s" % len(self.target["luns"])
+ id = "TargetID: %s" % self.target["scsi_dev_num"]
+ info = ",".join([luns, id])
+ return info, True
+
+
+class UIVhostLunDevObj(UINode):
+ def __init__(self, name, parent):
+ UINode.__init__(self, name, parent)
diff --git a/src/spdk/scripts/spdkcli/ui_node_iscsi.py b/src/spdk/scripts/spdkcli/ui_node_iscsi.py
new file mode 100644
index 00000000..65592a75
--- /dev/null
+++ b/src/spdk/scripts/spdkcli/ui_node_iscsi.py
@@ -0,0 +1,635 @@
+from rpc.client import JSONRPCException
+from .ui_node import UINode
+
+
+class UIISCSI(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "iscsi", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ UIISCSIDevices(self)
+ UIPortalGroups(self)
+ UIInitiatorGroups(self)
+ UIISCSIConnections(self)
+ UIISCSIAuthGroups(self)
+ UIISCSIGlobalParams(self)
+
+
+class UIISCSIGlobalParams(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "global_params", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for param, val in self.get_root().get_iscsi_global_params().items():
+ UIISCSIGlobalParam("%s: %s" % (param, val), self)
+
+ def ui_command_set_auth(self, g=None, d=None, r=None, m=None):
+ """Set CHAP authentication for discovery service.
+
+ Optional arguments:
+ g = chap_group: Authentication group ID for discovery session
+ d = disable_chap: CHAP for discovery session should be disabled
+ r = require_chap: CHAP for discovery session should be required
+ m = mutual_chap: CHAP for discovery session should be mutual
+ """
+ chap_group = self.ui_eval_param(g, "number", None)
+ disable_chap = self.ui_eval_param(d, "bool", None)
+ require_chap = self.ui_eval_param(r, "bool", None)
+ mutual_chap = self.ui_eval_param(m, "bool", None)
+ try:
+ self.get_root().set_iscsi_discovery_auth(
+ chap_group=chap_group, disable_chap=disable_chap,
+ require_chap=require_chap, mutual_chap=mutual_chap)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.refresh()
+
+
+class UIISCSIGlobalParam(UINode):
+ def __init__(self, param, parent):
+ UINode.__init__(self, param, parent)
+
+
+class UIISCSIDevices(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "target_nodes", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ self.target_nodes = list(self.get_root().get_target_nodes())
+ self.scsi_devices = list(self.get_root().get_scsi_devices())
+ for device in self.scsi_devices:
+ for node in self.target_nodes:
+ if hasattr(device, "device_name") and node['name'] \
+ == device.device_name:
+ UIISCSIDevice(device, node, self)
+
+ def ui_command_create(self, name, alias_name, bdev_name_id_pairs,
+ pg_ig_mappings, queue_depth, g=None, d=None, r=None,
+ m=None, h=None, t=None):
+ """Create target node
+
+ Positional args:
+ name: Target node name (ASCII)
+ alias_name: Target node alias name (ASCII)
+ bdev_name_id_pairs: List of bdev_name_id_pairs
+ pg_ig_mappings: List of pg_ig_mappings
+ queue_depth: Desired target queue depth
+ Optional args:
+ g = chap_group: Authentication group ID for this target node
+ d = disable_chap: CHAP authentication should be disabled for this target node
+ r = require_chap: CHAP authentication should be required for this target node
+ m = mutual_chap: CHAP authentication should be mutual/bidirectional
+ h = header_digest: Header Digest should be required for this target node
+ t = data_digest: Data Digest should be required for this target node
+ """
+ luns = []
+ print("bdev_name_id_pairs: %s" % bdev_name_id_pairs)
+ print("pg_ig_mappings: %s" % pg_ig_mappings)
+ for u in bdev_name_id_pairs.strip().split(" "):
+ bdev_name, lun_id = u.split(":")
+ luns.append({"bdev_name": bdev_name, "lun_id": int(lun_id)})
+ pg_ig_maps = []
+ for u in pg_ig_mappings.strip().split(" "):
+ pg, ig = u.split(":")
+ pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
+ queue_depth = self.ui_eval_param(queue_depth, "number", None)
+ chap_group = self.ui_eval_param(g, "number", None)
+ disable_chap = self.ui_eval_param(d, "bool", None)
+ require_chap = self.ui_eval_param(r, "bool", None)
+ mutual_chap = self.ui_eval_param(m, "bool", None)
+ header_digest = self.ui_eval_param(h, "bool", None)
+ data_digest = self.ui_eval_param(t, "bool", None)
+ try:
+ self.get_root().construct_target_node(
+ name=name, alias_name=alias_name, luns=luns,
+ pg_ig_maps=pg_ig_maps, queue_depth=queue_depth,
+ chap_group=chap_group, disable_chap=disable_chap,
+ require_chap=require_chap, mutual_chap=mutual_chap,
+ header_digest=header_digest, data_digest=data_digest)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def ui_command_delete(self, name=None):
+ """Delete a target node. If name is not specified delete all target nodes.
+
+ Arguments:
+ name - Target node name.
+ """
+ if name is None:
+ for device in self.devices:
+ try:
+ self.get_root().delete_target_node(
+ target_node_name=device.device_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ else:
+ try:
+ self.get_root().delete_target_node(target_node_name=name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.refresh()
+
+ def ui_command_add_lun(self, name, bdev_name, lun_id=None):
+ """Add lun to the target node.
+
+ Required args:
+ name: Target node name (ASCII)
+ bdev_name: bdev name
+ Positional args:
+ lun_id: LUN ID (integer >= 0)
+ """
+ if lun_id:
+ lun_id = self.ui_eval_param(lun_id, "number", None)
+ try:
+ self.get_root().target_node_add_lun(
+ name=name, bdev_name=bdev_name, lun_id=lun_id)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.parent.refresh()
+
+ def summary(self):
+ count = 0
+ for device in self.scsi_devices:
+ for node in self.target_nodes:
+ if hasattr(device, "device_name") and node['name'] \
+ == device.device_name:
+ count = count + 1
+ return "Target nodes: %d" % count, None
+
+
+class UIISCSIDevice(UINode):
+ def __init__(self, device, target, parent):
+ UINode.__init__(self, device.device_name, parent)
+ self.device = device
+ self.target = target
+ self.refresh()
+
+ def ui_command_set_auth(self, g=None, d=None, r=None, m=None):
+ """Set CHAP authentication for the target node.
+
+ Optionals args:
+ g = chap_group: Authentication group ID for this target node
+ d = disable_chap: CHAP authentication should be disabled for this target node
+ r = require_chap: CHAP authentication should be required for this target node
+ m = mutual_chap: CHAP authentication should be mutual/bidirectional
+ """
+ chap_group = self.ui_eval_param(g, "number", None)
+ disable_chap = self.ui_eval_param(d, "bool", None)
+ require_chap = self.ui_eval_param(r, "bool", None)
+ mutual_chap = self.ui_eval_param(m, "bool", None)
+ try:
+ self.get_root().set_iscsi_target_node_auth(
+ name=self.device.device_name, chap_group=chap_group,
+ disable_chap=disable_chap,
+ require_chap=require_chap, mutual_chap=mutual_chap)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.parent.refresh()
+
+ def ui_command_add_pg_ig_maps(self, pg_ig_mappings):
+ """Add PG-IG maps to the target node.
+
+ Args:
+ pg_ig_maps: List of pg_ig_mappings, e.g. pg_tag:ig_tag pg_tag2:ig_tag2
+ """
+ pg_ig_maps = []
+ for u in pg_ig_mappings.strip().split(" "):
+ pg, ig = u.split(":")
+ pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
+ try:
+ self.get_root().add_pg_ig_maps(
+ pg_ig_maps=pg_ig_maps, name=self.device.device_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.parent.refresh()
+
+ def ui_command_delete_pg_ig_maps(self, pg_ig_mappings):
+ """Add PG-IG maps to the target node.
+
+ Args:
+ pg_ig_maps: List of pg_ig_mappings, e.g. pg_tag:ig_tag pg_tag2:ig_tag2
+ """
+ pg_ig_maps = []
+ for u in pg_ig_mappings.strip().split(" "):
+ pg, ig = u.split(":")
+ pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
+ try:
+ self.get_root().delete_pg_ig_maps(
+ pg_ig_maps=pg_ig_maps, name=self.device.device_name)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.parent.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ UIISCSILuns(self.target['luns'], self)
+ UIISCSIPgIgMaps(self.target['pg_ig_maps'], self)
+ auths = {"disable_chap": self.target["disable_chap"],
+ "require_chap": self.target["require_chap"],
+ "mutual_chap": self.target["mutual_chap"],
+ "chap_group": self.target["chap_group"],
+ "data_digest": self.target["data_digest"]}
+ UIISCSIAuth(auths, self)
+
+ def summary(self):
+ return "Id: %s, QueueDepth: %s" % (self.device.id,
+ self.target['queue_depth']), None
+
+
+class UIISCSIAuth(UINode):
+ def __init__(self, auths, parent):
+ UINode.__init__(self, "auths", parent)
+ self.auths = auths
+ self.refresh()
+
+ def summary(self):
+ return "disable_chap: %s, require_chap: %s, mutual_chap: %s, chap_group: %s" % (
+ self.auths['disable_chap'], self.auths['require_chap'],
+ self.auths['mutual_chap'], self.auths['chap_group']), None
+
+
+class UIISCSILuns(UINode):
+ def __init__(self, luns, parent):
+ UINode.__init__(self, "luns", parent)
+ self.luns = luns
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for lun in self.luns:
+ UIISCSILun(lun, self)
+
+ def summary(self):
+ return "Luns: %d" % len(self.luns), None
+
+
+class UIISCSILun(UINode):
+ def __init__(self, lun, parent):
+ UINode.__init__(self, "lun %s" % lun['lun_id'], parent)
+ self.lun = lun
+ self.refresh()
+
+ def summary(self):
+ return "%s" % self.lun['bdev_name'], None
+
+
+class UIISCSIPgIgMaps(UINode):
+ def __init__(self, pg_ig_maps, parent):
+ UINode.__init__(self, "pg_ig_maps", parent)
+ self.pg_ig_maps = pg_ig_maps
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for pg_ig in self.pg_ig_maps:
+ UIISCSIPgIg(pg_ig, self)
+
+ def summary(self):
+ return "Pg_ig_maps: %d" % len(self.pg_ig_maps), None
+
+
+class UIISCSIPgIg(UINode):
+ def __init__(self, pg_ig, parent):
+ UINode.__init__(self, "portal_group%s - initiator_group%s" %
+ (pg_ig['pg_tag'], pg_ig['ig_tag']), parent)
+ self.pg_ig = pg_ig
+ self.refresh()
+
+
+class UIPortalGroups(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "portal_groups", parent)
+ self.refresh()
+
+ def ui_command_create(self, tag, portal_list):
+ """Add a portal group.
+
+ Args:
+ portals: List of portals e.g. ip:port@cpumask ip2:port2
+ tag: Portal group tag (unique, integer > 0)
+ """
+ portals = []
+ print("portal_list: %s" % portal_list)
+ for portal in portal_list.strip().split(" "):
+ host = portal
+ cpumask = None
+ if "@" in portal:
+ host, cpumask = portal.split("@")
+ host, port = host.rsplit(":", -1)
+ portals.append({'host': host, 'port': port})
+ if cpumask:
+ portals[-1]['cpumask'] = cpumask
+ tag = self.ui_eval_param(tag, "number", None)
+ try:
+ self.get_root().construct_portal_group(tag=tag, portals=portals)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def ui_command_delete(self, tag):
+ """Delete a portal group with given tag (unique, integer > 0))"""
+ tag = self.ui_eval_param(tag, "number", None)
+ try:
+ self.get_root().delete_portal_group(tag=tag)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ self.pgs = list(self.get_root().get_portal_groups())
+ for pg in self.pgs:
+ UIPortalGroup(pg, self)
+
+ def summary(self):
+ return "Portal groups: %d" % len(self.pgs), None
+
+
+class UIPortalGroup(UINode):
+ def __init__(self, pg, parent):
+ UINode.__init__(self, "portal_group%s" % pg.tag, parent)
+ self.pg = pg
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for portal in self.pg.portals:
+ UIPortal(portal['host'], portal['port'], portal['cpumask'], self)
+
+ def summary(self):
+ return "Portals: %d" % len(self.pg.portals), None
+
+
+class UIPortal(UINode):
+ def __init__(self, host, port, cpumask, parent):
+ UINode.__init__(self, "host=%s, port=%s, cpumask=%s" % (
+ host, port, cpumask), parent)
+ self.refresh()
+
+
+class UIInitiatorGroups(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "initiator_groups", parent)
+ self.refresh()
+
+ def ui_command_create(self, tag, initiator_list, netmask_list):
+ """Add an initiator group.
+
+ Args:
+ tag: Initiator group tag (unique, integer > 0)
+ initiators: List of initiator hostnames or IP addresses
+ separated with whitespaces, e.g. 127.0.0.1 192.168.200.100
+ netmasks: List of initiator netmasks separated with whitespaces,
+ e.g. 255.255.0.0 255.248.0.0
+ """
+ tag = self.ui_eval_param(tag, "number", None)
+ try:
+ self.get_root().construct_initiator_group(
+ tag=tag, initiators=initiator_list.split(" "),
+ netmasks=netmask_list.split(" "))
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def ui_command_delete(self, tag):
+ """Delete an initiator group.
+
+ Args:
+ tag: Initiator group tag (unique, integer > 0)
+ """
+ tag = self.ui_eval_param(tag, "number", None)
+ try:
+ self.get_root().delete_initiator_group(tag=tag)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def ui_command_add_initiator(self, tag, initiators, netmasks):
+ """Add initiators to an existing initiator group.
+
+ Args:
+ tag: Initiator group tag (unique, integer > 0)
+ initiators: List of initiator hostnames or IP addresses,
+ e.g. 127.0.0.1 192.168.200.100
+ netmasks: List of initiator netmasks,
+ e.g. 255.255.0.0 255.248.0.0
+ """
+ tag = self.ui_eval_param(tag, "number", None)
+ try:
+ self.get_root().add_initiators_to_initiator_group(
+ tag=tag, initiators=initiators.split(" "),
+ netmasks=netmasks.split(" "))
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def ui_command_delete_initiator(self, tag, initiators=None, netmasks=None):
+ """Delete initiators from an existing initiator group.
+
+ Args:
+ tag: Initiator group tag (unique, integer > 0)
+ initiators: List of initiator hostnames or IP addresses, e.g. 127.0.0.1 192.168.200.100
+ netmasks: List of initiator netmasks, e.g. 255.255.0.0 255.248.0.0
+ """
+ tag = self.ui_eval_param(tag, "number", None)
+ if initiators:
+ initiators = initiators.split(" ")
+ if netmasks:
+ netmasks = netmasks.split(" ")
+ try:
+ self.get_root().delete_initiators_from_initiator_group(
+ tag=tag, initiators=initiators,
+ netmasks=netmasks)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ self.igs = list(self.get_root().get_initiator_groups())
+ for ig in self.igs:
+ UIInitiatorGroup(ig, self)
+
+ def summary(self):
+ return "Initiator groups: %d" % len(self.igs), None
+
+
+class UIInitiatorGroup(UINode):
+ def __init__(self, ig, parent):
+ UINode.__init__(self, "initiator_group%s" % ig.tag, parent)
+ self.ig = ig
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for initiator, netmask in zip(self.ig.initiators, self.ig.netmasks):
+ UIInitiator(initiator, netmask, self)
+
+ def summary(self):
+ return "Initiators: %d" % len(self.ig.initiators), None
+
+
+class UIInitiator(UINode):
+ def __init__(self, initiator, netmask, parent):
+ UINode.__init__(self, "hostname=%s, netmask=%s" % (initiator, netmask), parent)
+ self.refresh()
+
+
+class UIISCSIConnections(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "iscsi_connections", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ self.iscsicons = list(self.get_root().get_iscsi_connections())
+ for ic in self.iscsicons:
+ UIISCSIConnection(ic, self)
+
+ def summary(self):
+ return "Connections: %d" % len(self.iscsicons), None
+
+
+class UIISCSIConnection(UINode):
+ def __init__(self, ic, parent):
+ UINode.__init__(self, "%s" % ic['id'], parent)
+ self.ic = ic
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for key, val in self.ic.iteritems():
+ if key == "id":
+ continue
+ UIISCSIConnectionDetails("%s: %s" % (key, val), self)
+
+
+class UIISCSIConnectionDetails(UINode):
+ def __init__(self, info, parent):
+ UINode.__init__(self, "%s" % info, parent)
+ self.refresh()
+
+
+class UIISCSIAuthGroups(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "auth_groups", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ self.iscsi_auth_groups = list(self.get_root().get_iscsi_auth_groups())
+ if self.iscsi_auth_groups is None:
+ self.iscsi_auth_groups = []
+ for ag in self.iscsi_auth_groups:
+ UIISCSIAuthGroup(ag, self)
+
+ def ui_command_create(self, tag, secrets=None):
+ """Add authentication group for CHAP authentication.
+
+ Args:
+ tag: Authentication group tag (unique, integer > 0).
+ Optional args:
+ secrets: Array of secrets objects separated by comma sign,
+ e.g. user:test secret:test muser:mutual_test msecret:mutual_test
+ """
+ tag = self.ui_eval_param(tag, "number", None)
+ if secrets:
+ secrets = [dict(u.split(":") for u in a.split(" "))
+ for a in secrets.split(",")]
+ try:
+ self.get_root().add_iscsi_auth_group(tag=tag, secrets=secrets)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def ui_command_delete(self, tag):
+ """Delete an authentication group.
+
+ Args:
+ tag: Authentication group tag (unique, integer > 0)
+ """
+ tag = self.ui_eval_param(tag, "number", None)
+ try:
+ self.get_root().delete_iscsi_auth_group(tag=tag)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+
+ self.refresh()
+
+ def ui_command_add_secret(self, tag, user, secret,
+ muser=None, msecret=None):
+ """Add a secret to an authentication group.
+
+ Args:
+ tag: Authentication group tag (unique, integer > 0)
+ user: User name for one-way CHAP authentication
+ secret: Secret for one-way CHAP authentication
+ Optional args:
+ muser: User name for mutual CHAP authentication
+ msecret: Secret for mutual CHAP authentication
+ """
+ tag = self.ui_eval_param(tag, "number", None)
+ try:
+ self.get_root().add_secret_to_iscsi_auth_group(
+ tag=tag, user=user, secret=secret,
+ muser=muser, msecret=msecret)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.refresh()
+
+ def ui_command_delete_secret(self, tag, user):
+ """Delete a secret from an authentication group.
+
+ Args:
+ tag: Authentication group tag (unique, integer > 0)
+ user: User name for one-way CHAP authentication
+ """
+ tag = self.ui_eval_param(tag, "number", None)
+ try:
+ self.get_root().delete_secret_from_iscsi_auth_group(
+ tag=tag, user=user)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.refresh()
+
+ def summary(self):
+ return "Groups: %s" % len(self.iscsi_auth_groups), None
+
+
+class UIISCSIAuthGroup(UINode):
+ def __init__(self, ag, parent):
+ UINode.__init__(self, "group" + str(ag['tag']), parent)
+ self.ag = ag
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for secret in self.ag['secrets']:
+ UISCSIAuthSecret(secret, self)
+
+ def summary(self):
+ return "Secrets: %s" % len(self.ag['secrets']), None
+
+
+class UISCSIAuthSecret(UINode):
+ def __init__(self, secret, parent):
+ info = ", ".join("%s=%s" % (key, val)
+ for key, val in secret.items())
+ UINode.__init__(self, info, parent)
+ self.secret = secret
+ self.refresh()
diff --git a/src/spdk/scripts/spdkcli/ui_node_nvmf.py b/src/spdk/scripts/spdkcli/ui_node_nvmf.py
new file mode 100644
index 00000000..71b69367
--- /dev/null
+++ b/src/spdk/scripts/spdkcli/ui_node_nvmf.py
@@ -0,0 +1,302 @@
+from rpc.client import JSONRPCException
+from .ui_node import UINode
+
+
+class UINVMf(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "nvmf", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ UINVMfSubsystems(self)
+
+
+class UINVMfSubsystems(UINode):
+ def __init__(self, parent):
+ UINode.__init__(self, "subsystem", parent)
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for subsystem in self.get_root().get_nvmf_subsystems():
+ UINVMfSubsystem(subsystem, self)
+
+ def ui_command_create(self, nqn, serial_number=None,
+ max_namespaces=None, allow_any_host="false"):
+ """Create subsystem with given parameteres.
+
+ Arguments:
+ nqn - Target nqn(ASCII).
+ serial_number - Example: 'SPDK00000000000001'.
+ max_namespaces - Optional parameter. Maximum number of namespaces allowed to added during
+ active connection
+ allow_any_host - Optional parameter. Allow any host to connect (don't enforce host NQN
+ whitelist)
+ """
+ allow_any_host = self.ui_eval_param(allow_any_host, "bool", False)
+ max_namespaces = self.ui_eval_param(max_namespaces, "number", 0)
+ try:
+ self.get_root().create_nvmf_subsystem(nqn=nqn, serial_number=serial_number,
+ allow_any_host=allow_any_host,
+ max_namespaces=max_namespaces)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.refresh()
+
+ def ui_command_delete(self, subsystem_nqn):
+ """Delete subsystem with given nqn.
+
+ Arguments:
+ nqn_subsystem - Name of susbsytem to delete
+ """
+ try:
+ self.get_root().delete_nvmf_subsystem(nqn=subsystem_nqn)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.refresh()
+
+ def summary(self):
+ return "Subsystems: %s" % len(self.children), None
+
+
+class UINVMfSubsystem(UINode):
+ def __init__(self, subsystem, parent):
+ UINode.__init__(self, subsystem.nqn, parent)
+ self.subsystem = subsystem
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ UINVMfSubsystemListeners(self.subsystem.listen_addresses, self)
+ UINVMfSubsystemHosts(self.subsystem.hosts, self)
+ if hasattr(self.subsystem, 'namespaces'):
+ UINVMfSubsystemNamespaces(self.subsystem.namespaces, self)
+
+ def refresh_node(self):
+ for subsystem in self.get_root().get_nvmf_subsystems():
+ if subsystem.nqn == self.subsystem.nqn:
+ self.subsystem = subsystem
+ self.refresh()
+
+ def ui_command_show_details(self):
+ self.shell.log.info(json.dumps(vars(self.lvs), indent=2))
+
+ def ui_command_allow_any_host(self, disable="false"):
+ """Disable or or enable allow_any_host flag.
+
+ Arguments:
+ disable - Optional parameter. If false then enable, if true disable
+ """
+ disable = self.ui_eval_param(disable, "bool", None)
+ try:
+ self.get_root().nvmf_subsystem_allow_any_host(
+ nqn=self.subsystem.nqn, disable=disable)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh_node()
+
+ def summary(self):
+ sn = None
+ if hasattr(self.subsystem, 'serial_number'):
+ sn = "sn=%s" % self.subsystem.serial_number
+ st = None
+ if hasattr(self.subsystem, 'subtype'):
+ st = "st=%s" % self.subsystem.subtype
+ allow_any_host = None
+ if self.subsystem.allow_any_host:
+ allow_any_host = "Allow any host"
+ info = ", ".join(filter(None, [sn, st, allow_any_host]))
+ return info, None
+
+
+class UINVMfSubsystemListeners(UINode):
+ def __init__(self, listen_addresses, parent):
+ UINode.__init__(self, "listen_addresses", parent)
+ self.listen_addresses = listen_addresses
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for address in self.listen_addresses:
+ UINVMfSubsystemListener(address, self)
+
+ def refresh_node(self):
+ for subsystem in self.get_root().get_nvmf_subsystems():
+ if subsystem.nqn == self.parent.subsystem.nqn:
+ self.listen_addresses = subsystem.listen_addresses
+ self.refresh()
+
+ def ui_command_create(self, trtype, traddr, trsvcid, adrfam):
+ """Create address listener for subsystem.
+
+ Arguments:
+ trtype - NVMe-oF transport type: e.g., rdma.
+ traddr - NVMe-oF transport address: e.g., an ip address.
+ trsvcid - NVMe-oF transport service id: e.g., a port number.
+ adrfam - NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc.
+ """
+ try:
+ self.get_root().nvmf_subsystem_add_listener(
+ nqn=self.parent.subsystem.nqn, trtype=trtype, traddr=traddr,
+ trsvcid=trsvcid, adrfam=adrfam)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh_node()
+
+ def ui_command_delete(self, trtype, traddr, trsvcid, adrfam=None):
+ """Remove address listener for subsystem.
+
+ Arguments:
+ trtype - Transport type (RDMA)
+ traddr - NVMe-oF transport address: e.g., an ip address.
+ trsvcid - NVMe-oF transport service id: e.g., a port number.
+ adrfam - Optional argument. Address family ("IPv4", "IPv6", "IB" or "FC").
+ """
+ try:
+ self.get_root().nvmf_subsystem_remove_listener(
+ nqn=self.parent.subsystem.nqn, trtype=trtype,
+ traddr=traddr, trsvcid=trsvcid, adrfam=adrfam)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh_node()
+
+ def summary(self):
+ return "Addresses: %s" % len(self.listen_addresses), None
+
+
+class UINVMfSubsystemListener(UINode):
+ def __init__(self, address, parent):
+ UINode.__init__(self, "%s:%s" % (address['traddr'], address['trsvcid']),
+ parent)
+ self.address = address
+
+ def summary(self):
+ return "%s" % self.address['trtype'], True
+
+
+class UINVMfSubsystemHosts(UINode):
+ def __init__(self, hosts, parent):
+ UINode.__init__(self, "hosts", parent)
+ self.hosts = hosts
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for host in self.hosts:
+ UINVMfSubsystemHost(host, self)
+
+ def refresh_node(self):
+ for subsystem in self.get_root().get_nvmf_subsystems():
+ if subsystem.nqn == self.parent.subsystem.nqn:
+ self.hosts = subsystem.hosts
+ self.refresh()
+
+ def ui_command_create(self, host):
+ """Add a host NQN to the whitelist of allowed hosts.
+
+ Args:
+ host: Host NQN to add to the list of allowed host NQNs
+ """
+ try:
+ self.get_root().nvmf_subsystem_add_host(
+ nqn=self.parent.subsystem.nqn, host=host)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh_node()
+
+ def ui_command_delete(self, host):
+ """Delete host from subsystem.
+
+ Arguments:
+ host - NQN of host to remove.
+ """
+ try:
+ self.get_root().nvmf_subsystem_remove_host(
+ nqn=self.parent.subsystem.nqn, host=host)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh_node()
+
+ def summary(self):
+ return "Hosts: %s" % len(self.hosts), None
+
+
+class UINVMfSubsystemHost(UINode):
+ def __init__(self, host, parent):
+ UINode.__init__(self, "%s" % host['nqn'], parent)
+ self.host = host
+
+
+class UINVMfSubsystemNamespaces(UINode):
+ def __init__(self, namespaces, parent):
+ UINode.__init__(self, "namespaces", parent)
+ self.namespaces = namespaces
+ self.refresh()
+
+ def refresh(self):
+ self._children = set([])
+ for namespace in self.namespaces:
+ UINVMfSubsystemNamespace(namespace, self)
+
+ def refresh_node(self):
+ for subsystem in self.get_root().get_nvmf_subsystems():
+ if subsystem.nqn == self.parent.subsystem.nqn:
+ self.namespaces = subsystem.namespaces
+ self.refresh()
+
+ def ui_command_create(self, bdev_name, nsid=None,
+ nguid=None, eui64=None, uuid=None):
+ """Add a namespace to a subsystem.
+
+ Args:
+ bdev_name: Name of bdev to expose as a namespace.
+ Optional args:
+ nsid: Namespace ID.
+ nguid: 16-byte namespace globally unique identifier in hexadecimal.
+ eui64: 8-byte namespace EUI-64 in hexadecimal (e.g. "ABCDEF0123456789").
+ uuid: Namespace UUID.
+ """
+ nsid = self.ui_eval_param(nsid, "number", None)
+ try:
+ self.get_root().nvmf_subsystem_add_ns(
+ nqn=self.parent.subsystem.nqn, bdev_name=bdev_name,
+ nsid=nsid, nguid=nguid, eui64=eui64, uuid=uuid)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh_node()
+
+ def ui_command_delete(self, nsid):
+ """Delete namespace from subsystem.
+
+ Arguments:
+ nsid - Id of namespace to remove.
+ """
+ nsid = self.ui_eval_param(nsid, "number", None)
+ try:
+ self.get_root().nvmf_subsystem_remove_ns(
+ nqn=self.parent.subsystem.nqn, nsid=nsid)
+ except JSONRPCException as e:
+ self.shell.log.error(e.message)
+ self.get_root().refresh()
+ self.refresh_node()
+
+ def summary(self):
+ return "Namespaces: %s" % len(self.namespaces), None
+
+
+class UINVMfSubsystemNamespace(UINode):
+ def __init__(self, namespace, parent):
+ UINode.__init__(self, namespace['bdev_name'], parent)
+ self.namespace = namespace
+
+ def summary(self):
+ info = ", ".join([str(self.namespace['uuid']), self.namespace['name'],
+ str(self.namespace['nsid'])])
+ return info, None
diff --git a/src/spdk/scripts/spdkcli/ui_root.py b/src/spdk/scripts/spdkcli/ui_root.py
new file mode 100644
index 00000000..9854d373
--- /dev/null
+++ b/src/spdk/scripts/spdkcli/ui_root.py
@@ -0,0 +1,482 @@
+from .ui_node import UINode, UIBdevs, UILvolStores, UIVhosts
+from .ui_node_nvmf import UINVMf
+from .ui_node_iscsi import UIISCSI
+import rpc.client
+import rpc
+from functools import wraps
+
+
+class UIRoot(UINode):
+ """
+ Root node for CLI menu tree structure. Refreshes running config on startup.
+ """
+ def __init__(self, s, shell):
+ UINode.__init__(self, "/", shell=shell)
+ self.current_bdevs = []
+ self.current_lvol_stores = []
+ self.current_vhost_ctrls = []
+ self.current_nvmf_subsystems = []
+ self.set_rpc_target(s)
+ self.verbose = False
+ self.is_init = self.check_init()
+
+ def refresh(self):
+ if self.is_init is False:
+ methods = self.get_rpc_methods(current=True)
+ methods = "\n".join(methods)
+ self.shell.log.warning("SPDK Application is not yet initialized.\n"
+ "Please initialize subsystems with start_subsystem_init command.\n"
+ "List of available commands in current state:\n"
+ "%s" % methods)
+ else:
+ # Pass because we'd like to build main tree structure for "ls"
+ # even if state is uninitialized
+ pass
+
+ self._children = set([])
+ UIBdevs(self)
+ UILvolStores(self)
+ UIVhosts(self)
+ UINVMf(self)
+ UIISCSI(self)
+
+ def set_rpc_target(self, s):
+ self.client = rpc.client.JSONRPCClient(s)
+
+ def print_array(self, a):
+ return " ".join(a)
+
+ def verbose(f):
+ # For any configuration calls (create, delete, construct, etc.)
+ # Check if verbose option is to be used and set appropriately.
+ # Do not use for "get_*" methods so that output is not
+ # flooded.
+ def w(self, **kwargs):
+ self.client.verbose = self.verbose
+ r = f(self, **kwargs)
+ self.client.verbose = False
+ return r
+ return w
+
+ def ui_command_start_subsystem_init(self):
+ if rpc.start_subsystem_init(self.client):
+ self.is_init = True
+ self.refresh()
+
+ def ui_command_load_config(self, filename):
+ with open(filename, "r") as fd:
+ rpc.load_config(self.client, fd)
+
+ def ui_command_load_subsystem_config(self, filename):
+ with open(filename, "r") as fd:
+ rpc.load_subsystem_config(self.client, fd)
+
+ def ui_command_save_config(self, filename, indent=2):
+ with open(filename, "w") as fd:
+ rpc.save_config(self.client, fd, indent)
+
+ def ui_command_save_subsystem_config(self, filename, subsystem, indent=2):
+ with open(filename, "w") as fd:
+ rpc.save_subsystem_config(self.client, fd, indent, subsystem)
+
+ def get_rpc_methods(self, current=False):
+ return rpc.get_rpc_methods(self.client, current=current)
+
+ def check_init(self):
+ return "start_subsystem_init" not in self.get_rpc_methods(current=True)
+
+ def get_bdevs(self, bdev_type):
+ if self.is_init:
+ self.current_bdevs = rpc.bdev.get_bdevs(self.client)
+ # Following replace needs to be done in order for some of the bdev
+ # listings to work: logical volumes, split disk.
+ # For example logical volumes: listing in menu is "Logical_Volume"
+ # (cannot have space), but the product name in SPDK is "Logical Volume"
+ bdev_type = bdev_type.replace("_", " ")
+ for bdev in [x for x in self.current_bdevs if bdev_type in x["product_name"].lower()]:
+ test = Bdev(bdev)
+ yield test
+
+ def get_bdevs_iostat(self, **kwargs):
+ return rpc.bdev.get_bdevs_iostat(self.client, **kwargs)
+
+ @verbose
+ def split_bdev(self, **kwargs):
+ response = rpc.bdev.construct_split_vbdev(self.client, **kwargs)
+ return self.print_array(response)
+
+ @verbose
+ def destruct_split_bdev(self, **kwargs):
+ rpc.bdev.destruct_split_vbdev(self.client, **kwargs)
+
+ @verbose
+ def delete_bdev(self, name):
+ rpc.bdev.delete_bdev(self.client, bdev_name=name)
+
+ @verbose
+ def create_malloc_bdev(self, **kwargs):
+ response = rpc.bdev.construct_malloc_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_malloc_bdev(self, **kwargs):
+ rpc.bdev.delete_malloc_bdev(self.client, **kwargs)
+
+ @verbose
+ def create_iscsi_bdev(self, **kwargs):
+ response = rpc.bdev.construct_iscsi_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_iscsi_bdev(self, **kwargs):
+ rpc.bdev.delete_iscsi_bdev(self.client, **kwargs)
+
+ @verbose
+ def create_aio_bdev(self, **kwargs):
+ response = rpc.bdev.construct_aio_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_aio_bdev(self, **kwargs):
+ rpc.bdev.delete_aio_bdev(self.client, **kwargs)
+
+ @verbose
+ def create_lvol_bdev(self, **kwargs):
+ response = rpc.lvol.construct_lvol_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def destroy_lvol_bdev(self, **kwargs):
+ response = rpc.lvol.destroy_lvol_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def create_nvme_bdev(self, **kwargs):
+ response = rpc.bdev.construct_nvme_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_nvme_controller(self, **kwargs):
+ rpc.bdev.delete_nvme_controller(self.client, **kwargs)
+
+ @verbose
+ def create_null_bdev(self, **kwargs):
+ response = rpc.bdev.construct_null_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_null_bdev(self, **kwargs):
+ rpc.bdev.delete_null_bdev(self.client, **kwargs)
+
+ @verbose
+ def create_error_bdev(self, **kwargs):
+ response = rpc.bdev.construct_error_bdev(self.client, **kwargs)
+
+ @verbose
+ def delete_error_bdev(self, **kwargs):
+ rpc.bdev.delete_error_bdev(self.client, **kwargs)
+
+ def get_lvol_stores(self):
+ if self.is_init:
+ self.current_lvol_stores = rpc.lvol.get_lvol_stores(self.client)
+ for lvs in self.current_lvol_stores:
+ yield LvolStore(lvs)
+
+ @verbose
+ def create_lvol_store(self, **kwargs):
+ response = rpc.lvol.construct_lvol_store(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_lvol_store(self, **kwargs):
+ rpc.lvol.destroy_lvol_store(self.client, **kwargs)
+
+ @verbose
+ def create_pmem_pool(self, **kwargs):
+ response = rpc.pmem.create_pmem_pool(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_pmem_pool(self, **kwargs):
+ rpc.pmem.delete_pmem_pool(self.client, **kwargs)
+
+ @verbose
+ def create_pmem_bdev(self, **kwargs):
+ response = rpc.bdev.construct_pmem_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_pmem_bdev(self, **kwargs):
+ response = rpc.bdev.delete_pmem_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def create_rbd_bdev(self, **kwargs):
+ response = rpc.bdev.construct_rbd_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def delete_rbd_bdev(self, **kwargs):
+ response = rpc.bdev.delete_rbd_bdev(self.client, **kwargs)
+ return response
+
+ @verbose
+ def create_virtio_dev(self, **kwargs):
+ response = rpc.vhost.construct_virtio_dev(self.client, **kwargs)
+ return self.print_array(response)
+
+ @verbose
+ def remove_virtio_bdev(self, **kwargs):
+ response = rpc.vhost.remove_virtio_bdev(self.client, **kwargs)
+ return response
+
+ def get_virtio_scsi_devs(self):
+ if self.is_init:
+ for bdev in rpc.vhost.get_virtio_scsi_devs(self.client):
+ test = Bdev(bdev)
+ yield test
+
+ def list_vhost_ctrls(self):
+ if self.is_init:
+ self.current_vhost_ctrls = rpc.vhost.get_vhost_controllers(self.client)
+
+ def get_vhost_ctrlrs(self, ctrlr_type):
+ if self.is_init:
+ self.list_vhost_ctrls()
+ for ctrlr in [x for x in self.current_vhost_ctrls if ctrlr_type in list(x["backend_specific"].keys())]:
+ yield VhostCtrlr(ctrlr)
+
+ @verbose
+ def remove_vhost_controller(self, **kwargs):
+ rpc.vhost.remove_vhost_controller(self.client, **kwargs)
+
+ @verbose
+ def create_vhost_scsi_controller(self, **kwargs):
+ rpc.vhost.construct_vhost_scsi_controller(self.client, **kwargs)
+
+ @verbose
+ def create_vhost_blk_controller(self, **kwargs):
+ rpc.vhost.construct_vhost_blk_controller(self.client, **kwargs)
+
+ @verbose
+ def remove_vhost_scsi_target(self, **kwargs):
+ rpc.vhost.remove_vhost_scsi_target(self.client, **kwargs)
+
+ @verbose
+ def add_vhost_scsi_lun(self, **kwargs):
+ rpc.vhost.add_vhost_scsi_lun(self.client, **kwargs)
+
+ def set_vhost_controller_coalescing(self, **kwargs):
+ rpc.vhost.set_vhost_controller_coalescing(self.client, **kwargs)
+
+ def list_nvmf_subsystems(self):
+ if self.is_init:
+ self.current_nvmf_subsystems = rpc.nvmf.get_nvmf_subsystems(self.client)
+
+ def get_nvmf_subsystems(self):
+ if self.is_init:
+ self.list_nvmf_subsystems()
+ for subsystem in self.current_nvmf_subsystems:
+ yield NvmfSubsystem(subsystem)
+
+ @verbose
+ def create_nvmf_subsystem(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_create(self.client, **kwargs)
+
+ @verbose
+ def delete_nvmf_subsystem(self, **kwargs):
+ rpc.nvmf.delete_nvmf_subsystem(self.client, **kwargs)
+
+ @verbose
+ def nvmf_subsystem_add_listener(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_add_listener(self.client, **kwargs)
+
+ @verbose
+ def nvmf_subsystem_remove_listener(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_remove_listener(self.client, **kwargs)
+
+ @verbose
+ def nvmf_subsystem_add_host(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_add_host(self.client, **kwargs)
+
+ @verbose
+ def nvmf_subsystem_remove_host(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_remove_host(self.client, **kwargs)
+
+ @verbose
+ def nvmf_subsystem_allow_any_host(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_allow_any_host(self.client, **kwargs)
+
+ @verbose
+ def nvmf_subsystem_add_ns(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_add_ns(self.client, **kwargs)
+
+ @verbose
+ def nvmf_subsystem_remove_ns(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_remove_ns(self.client, **kwargs)
+
+ @verbose
+ def nvmf_subsystem_allow_any_host(self, **kwargs):
+ rpc.nvmf.nvmf_subsystem_allow_any_host(self.client, **kwargs)
+
+ def get_scsi_devices(self):
+ if self.is_init:
+ for device in rpc.iscsi.get_scsi_devices(self.client):
+ yield ScsiObj(device)
+
+ def get_target_nodes(self):
+ if self.is_init:
+ for tg in rpc.iscsi.get_target_nodes(self.client):
+ yield tg
+
+ @verbose
+ def construct_target_node(self, **kwargs):
+ rpc.iscsi.construct_target_node(self.client, **kwargs)
+
+ @verbose
+ def delete_target_node(self, **kwargs):
+ rpc.iscsi.delete_target_node(self.client, **kwargs)
+
+ def get_portal_groups(self):
+ if self.is_init:
+ for pg in rpc.iscsi.get_portal_groups(self.client):
+ yield ScsiObj(pg)
+
+ def get_initiator_groups(self):
+ if self.is_init:
+ for ig in rpc.iscsi.get_initiator_groups(self.client):
+ yield ScsiObj(ig)
+
+ @verbose
+ def construct_portal_group(self, **kwargs):
+ rpc.iscsi.add_portal_group(self.client, **kwargs)
+
+ @verbose
+ def delete_portal_group(self, **kwargs):
+ rpc.iscsi.delete_portal_group(self.client, **kwargs)
+
+ @verbose
+ def construct_initiator_group(self, **kwargs):
+ rpc.iscsi.add_initiator_group(self.client, **kwargs)
+
+ @verbose
+ def delete_initiator_group(self, **kwargs):
+ rpc.iscsi.delete_initiator_group(self.client, **kwargs)
+
+ @verbose
+ def get_iscsi_connections(self, **kwargs):
+ if self.is_init:
+ for ic in rpc.iscsi.get_iscsi_connections(self.client, **kwargs):
+ yield ic
+
+ @verbose
+ def add_initiators_to_initiator_group(self, **kwargs):
+ rpc.iscsi.add_initiators_to_initiator_group(self.client, **kwargs)
+
+ @verbose
+ def delete_initiators_from_initiator_group(self, **kwargs):
+ rpc.iscsi.delete_initiators_from_initiator_group(self.client, **kwargs)
+
+ @verbose
+ def add_pg_ig_maps(self, **kwargs):
+ rpc.iscsi.add_pg_ig_maps(self.client, **kwargs)
+
+ @verbose
+ def delete_pg_ig_maps(self, **kwargs):
+ rpc.iscsi.delete_pg_ig_maps(self.client, **kwargs)
+
+ @verbose
+ def add_secret_to_iscsi_auth_group(self, **kwargs):
+ rpc.iscsi.add_secret_to_iscsi_auth_group(self.client, **kwargs)
+
+ @verbose
+ def delete_secret_from_iscsi_auth_group(self, **kwargs):
+ rpc.iscsi.delete_secret_from_iscsi_auth_group(self.client, **kwargs)
+
+ @verbose
+ def get_iscsi_auth_groups(self, **kwargs):
+ return rpc.iscsi.get_iscsi_auth_groups(self.client, **kwargs)
+
+ @verbose
+ def add_iscsi_auth_group(self, **kwargs):
+ rpc.iscsi.add_iscsi_auth_group(self.client, **kwargs)
+
+ @verbose
+ def delete_iscsi_auth_group(self, **kwargs):
+ rpc.iscsi.delete_iscsi_auth_group(self.client, **kwargs)
+
+ @verbose
+ def set_iscsi_target_node_auth(self, **kwargs):
+ rpc.iscsi.set_iscsi_target_node_auth(self.client, **kwargs)
+
+ @verbose
+ def target_node_add_lun(self, **kwargs):
+ rpc.iscsi.target_node_add_lun(self.client, **kwargs)
+
+ @verbose
+ def set_iscsi_discovery_auth(self, **kwargs):
+ rpc.iscsi.set_iscsi_discovery_auth(self.client, **kwargs)
+
+ @verbose
+ def get_iscsi_global_params(self, **kwargs):
+ return rpc.iscsi.get_iscsi_global_params(self.client, **kwargs)
+
+
+class Bdev(object):
+ def __init__(self, bdev_info):
+ """
+ All class attributes are set based on what information is received
+ from get_bdevs RPC call.
+ # TODO: Document in docstring parameters which describe bdevs.
+ # TODO: Possible improvement: JSON schema might be used here in future
+ """
+ for i in list(bdev_info.keys()):
+ setattr(self, i, bdev_info[i])
+
+
+class LvolStore(object):
+ def __init__(self, lvs_info):
+ """
+ All class attributes are set based on what information is received
+ from get_bdevs RPC call.
+ # TODO: Document in docstring parameters which describe bdevs.
+ # TODO: Possible improvement: JSON schema might be used here in future
+ """
+ for i in list(lvs_info.keys()):
+ setattr(self, i, lvs_info[i])
+
+
+class VhostCtrlr(object):
+ def __init__(self, ctrlr_info):
+ """
+ All class attributes are set based on what information is received
+ from get_vhost_controllers RPC call.
+ # TODO: Document in docstring parameters which describe bdevs.
+ # TODO: Possible improvement: JSON schema might be used here in future
+ """
+ for i in list(ctrlr_info.keys()):
+ setattr(self, i, ctrlr_info[i])
+
+
+class NvmfSubsystem(object):
+ def __init__(self, subsystem_info):
+ """
+ All class attributes are set based on what information is received
+ from get_nvmf_subsystem RPC call.
+ # TODO: Document in docstring parameters which describe bdevs.
+ # TODO: Possible improvement: JSON schema might be used here in future
+ """
+ for i in subsystem_info.keys():
+ setattr(self, i, subsystem_info[i])
+
+
+class ScsiObj(object):
+ def __init__(self, device_info):
+ """
+ All class attributes are set based on what information is received
+ from iscsi related RPC calls.
+ # TODO: Document in docstring parameters which describe bdevs.
+ # TODO: Possible improvement: JSON schema might be used here in future
+ """
+ for i in device_info.keys():
+ setattr(self, i, device_info[i])