numam-spdk/scripts/spdkcli/ui_node.py
Karol Latecki 7df80205b4 spdkcli: add error injection bdev
Change-Id: Id86b4f464b89b0010129fccd32a1138a31fb62a1
Signed-off-by: Karol Latecki <karol.latecki@intel.com>
Reviewed-on: https://review.gerrithub.io/414954
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
2018-06-14 18:13:44 +00:00

388 lines
13 KiB
Python

from configshell_fb import ConfigNode, ExecutionError
from uuid import UUID
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)
def ui_command_delete(self, name):
"""
Deletes bdev from configuration.
Arguments:
name - Is a unique identifier of the bdev to be deleted - UUID number or name alias.
"""
self.get_root().delete_bdev(name=name)
self.refresh()
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)
self.get_root().create_lvol_store(lvs_name=name, bdev_name=bdev_name, cluster_sz=cluster_size)
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_delete(self, name):
"""
Deletes bdev from configuration.
Arguments:
name - Is a unique identifier of the bdev to be deleted - UUID number or name alias.
"""
self.get_root().delete_bdev(name=name)
self.get_root().refresh()
self.refresh()
def ui_command_get_bdev_iostat(self, name=None):
if name is None:
ret = self.get_root().get_bdevs_iostat()
else:
ret = self.get_root().get_bdevs_iostat(name=name)
self.shell.log.info(json.dumps(ret, indent=2))
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)
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)
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
"""
self.get_root().destruct_split_bdev(base_bdev=base_bdev)
self.parent.refresh()
self.refresh()
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)
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)
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)
ret_name = self.get_root().create_aio_bdev(name=name,
block_size=int(block_size),
filename=filename)
self.shell.log.info(ret_name)
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)
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)
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.")
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)
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
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)
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.
"""
self.get_root().create_error_bdev(base_name=base_name)
self.get_root().refresh()
self.refresh()
class UISplitBdev(UIBdev):
def __init__(self, parent):
UIBdev.__init__(self, "Split_Disk", parent)
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(filter(None, [alias, size, in_use]))
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