diff --git a/scripts/spdkcli/ui_node.py b/scripts/spdkcli/ui_node.py index d26d10809..01b6aaa1d 100644 --- a/scripts/spdkcli/ui_node.py +++ b/scripts/spdkcli/ui_node.py @@ -477,3 +477,191 @@ class UILvsObj(UINode): 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. + """ + ret_name = self.get_root().create_vhost_blk_controller(ctrlr=name, + dev_name=bdev, + cpumask=cpumask, + readonly=bool(readonly)) + 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. + """ + ret_name = self.get_root().create_vhost_scsi_controller(ctrlr=name, + cpumask=cpumask) + 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) + self.get_root().set_vhost_controller_coalescing(ctrlr=self.ctrlr.ctrlr, + delay_base_us=delay_base_us, + iops_threshold=iops_threshold) + + +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. + """ + 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 + + 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. + """ + + 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 + 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(filter(None, [self.ctrlr.socket, ro])) + 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/scripts/spdkcli/ui_root.py b/scripts/spdkcli/ui_root.py index 3a9b0e52a..b9abfd96d 100644 --- a/scripts/spdkcli/ui_root.py +++ b/scripts/spdkcli/ui_root.py @@ -1,4 +1,4 @@ -from .ui_node import UINode, UIBdevs, UILvolStores +from .ui_node import UINode, UIBdevs, UILvolStores, UIVhosts import rpc.client import rpc @@ -11,12 +11,14 @@ class UIRoot(UINode): UINode.__init__(self, "/", shell=shell) self.current_bdevs = [] self.current_lvol_stores = [] + self.current_vhost_ctrls = [] self.set_rpc_target(s) def refresh(self): self._children = set([]) UIBdevs(self) UILvolStores(self) + UIVhosts(self) def set_rpc_target(self, s): self.client = rpc.client.JSONRPCClient(s) @@ -121,6 +123,37 @@ class UIRoot(UINode): response = rpc.bdev.construct_rbd_bdev(self.client, **kwargs) return response + def list_vhost_ctrls(self): + self.current_vhost_ctrls = rpc.vhost.get_vhost_controllers(self.client) + + def get_vhost_ctrlrs(self, ctrlr_type): + for ctrlr in filter(lambda x: ctrlr_type in x["backend_specific"].keys(), + self.current_vhost_ctrls): + yield VhostCtrlr(ctrlr) + + def remove_vhost_controller(self, **kwargs): + rpc.vhost.remove_vhost_controller(self.client, **kwargs) + self.current_vhost_ctrls = rpc.vhost.get_vhost_controllers(self.client) + + def create_vhost_scsi_controller(self, **kwargs): + rpc.vhost.construct_vhost_scsi_controller(self.client, **kwargs) + self.current_vhost_ctrls = rpc.vhost.get_vhost_controllers(self.client) + + def create_vhost_blk_controller(self, **kwargs): + rpc.vhost.construct_vhost_blk_controller(self.client, **kwargs) + self.current_vhost_ctrls = rpc.vhost.get_vhost_controllers(self.client) + + def remove_vhost_scsi_target(self, **kwargs): + rpc.vhost.remove_vhost_scsi_target(self.client, **kwargs) + self.current_vhost_ctrls = rpc.vhost.get_vhost_controllers(self.client) + + def add_vhost_scsi_lun(self, **kwargs): + rpc.vhost.add_vhost_scsi_lun(self.client, **kwargs) + self.current_vhost_ctrls = rpc.vhost.get_vhost_controllers(self.client) + + def set_vhost_controller_coalescing(self, **kwargs): + rpc.vhost.set_vhost_controller_coalescing(self.client, **kwargs) + class Bdev(object): def __init__(self, bdev_info): @@ -144,3 +177,15 @@ class LvolStore(object): """ for i in 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 ctrlr_info.keys(): + setattr(self, i, ctrlr_info[i])