diff options
| author | Nicholas Bellinger <nab@risingtidesystems.com> | 2011-05-04 21:14:18 +0000 |
|---|---|---|
| committer | Nicholas Bellinger <nab@risingtidesystems.com> | 2011-05-04 21:14:18 +0000 |
| commit | db0c7fd76abc5fcc929b039f2d49f1a38f947968 (patch) | |
| tree | e2d8b8535610d8e6d2c9d1b305230be376d15887 /rtsadmin/ui_backstore.py | |
| download | targetcli-db0c7fd76abc5fcc929b039f2d49f1a38f947968.tar.gz | |
initial rtsadmin commit1.99
Signed-off-by: Nicholas Bellinger <nab@risingtidesystems.com>
Diffstat (limited to 'rtsadmin/ui_backstore.py')
| -rw-r--r-- | rtsadmin/ui_backstore.py | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/rtsadmin/ui_backstore.py b/rtsadmin/ui_backstore.py new file mode 100644 index 0000000..d8595d8 --- /dev/null +++ b/rtsadmin/ui_backstore.py @@ -0,0 +1,482 @@ +''' +Implements the rtsadmin backstores related UI. + +This file is part of RTSAdmin Community Edition. +Copyright (c) 2011 by RisingTide Systems LLC + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, version 3 (AGPLv3). + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +''' + +from ui_node import UINode, UIAttributes, UIParameters +from rtslib import RTSRoot +from rtslib import FileIOBackstore, IBlockBackstore +from rtslib import PSCSIBackstore, RDDRBackstore, RDMCPBackstore +from rtslib import FileIOStorageObject, IBlockStorageObject +from rtslib import PSCSIStorageObject, RDDRStorageObject, RDMCPStorageObject +from rtslib.utils import get_block_type, is_disk_partition + +class UIBackstores(UINode): + ''' + The backstores container UI. + ''' + def __init__(self): + UINode.__init__(self) + self.name = 'backstores' + self.cfs_cwd = "%s/core" % self.cfs_cwd + self.refresh() + + def refresh(self): + self._children = set([]) + for backstore in RTSRoot().backstores: + backstore_plugin = backstore.plugin + if backstore_plugin == 'pscsi': + self.add_child(UIPSCSIBackstore(backstore)) + elif backstore_plugin == 'rd_dr': + self.add_child(UIRDDRBackstore(backstore)) + elif backstore_plugin == 'rd_mcp': + self.add_child(UIRDMCPBackstore(backstore)) + elif backstore_plugin == 'fileio': + self.add_child(UIFileIOBackstore(backstore)) + elif backstore_plugin == 'iblock': + self.add_child(UIIBlockBackstore(backstore)) + + def summary(self): + no_backstores = len(self._children) + if no_backstores > 1: + msg = "%d Backstores" % no_backstores + else: + msg = "%d Backstore" % no_backstores + return (msg, None) + + def ui_command_create(self, backstore_plugin): + ''' + Creates a new backstore, using the chosen I{backstore_plugin}. More + than one backstores using the same I{backstore_plugin} can co-exist. + They will be identified by incremental index numbers, starting from 0. + + AVAILABLE BACKSTORE PLUGINS + =========================== + + B{iblock} + --------- + This I{backstore_plugin} provides I{SPC-4}, along with I{ALUA} and + I{Persistent Reservations} emulation on top of Linux BLOCK devices: + B{any block device} that appears in /sys/block. + + B{pscsi} + -------- + Provides pass-through for Linux physical SCSI devices. It can be used + with any storage object that does B{direct pass-through} of SCSI + commands without SCSI emulation. This assumes an underlying SCSI + device that appears with lsscsi in /proc/scsi/scsi, such as a SAS hard + drive, such as any SCSI device. The Linux kernel code for device SCSI + drivers resides in linux/drivers/scsi. SCSI-3 and higher is supported + with this subsystem, but only for control CDBs capable by the device + firmware. + + B{fileio} + --------- + This I{backstore_plugin} provides I{SPC-4}, along with I{ALUA} and + I{Persistent Reservations} emulation on top of Linux VFS devices: + B{any file on a mounted filesystem}. It may be backed by a file or an + underlying real block device. FILEIO is using struct file to serve + block I/O with various methods (synchronous or asynchronous) and + (buffered or direct). + + B{rd_dr} + ------- + This I{backstore_plugin} provides the same level of SCSI emulation than + the I{fileio} and I{iblock} backstores, but uses a B{ramdisk}, based on + direct memory mapping. It is the fastest of all backstores, and is + typically used for bandwidth testing. + + B{rd_mcp} + -------- + This I{backstore_plugin} is a bit slower than B{rd_dr}, but more robust + with multiple initiators, with a separate memory mapping using memory + copy. Also typically used for bandwidth testing. + + EXAMPLE + ======= + + B{create iblock} + ---------------- + Creates a new backstore, using the B{iblock} I{backstore_plugin}. + ''' + self.log.debug("%r" % [(backstore.plugin, backstore.index) + for backstore in RTSRoot().backstores]) + indexes = [backstore.index for backstore in RTSRoot().backstores + if backstore.plugin == backstore_plugin] + self.log.debug("Existing %s backstore indexes: %r" + % (backstore_plugin, indexes)) + for index in range(1048576): + if index not in indexes: + backstore_index = index + break + + if backstore_index is None: + self.log.error("Cannot find an available backstore index.") + return + else: + self.log.info("First available %s backstore index is %d." + % (backstore_plugin, backstore_index)) + + if backstore_plugin == 'pscsi': + backstore = PSCSIBackstore(backstore_index, mode='create') + self.add_child(UIPSCSIBackstore(backstore)) + elif backstore_plugin == 'rd_dr': + backstore = RDDRBackstore(backstore_index, mode='create') + self.add_child(UIRDDRBackstore(backstore)) + elif backstore_plugin == 'rd_mcp': + backstore = RDMCPBackstore(backstore_index, mode='create') + self.add_child(UIRDMCPBackstore(backstore)) + elif backstore_plugin == 'fileio': + backstore = FileIOBackstore(backstore_index, mode='create') + self.add_child(UIFileIOBackstore(backstore)) + elif backstore_plugin == 'iblock': + backstore = IBlockBackstore(backstore_index, mode='create') + self.add_child(UIIBlockBackstore(backstore)) + else: + self.log.error("Invalid backstore plugin %s" % backstore_plugin) + return + + self.log.info("Created new backstore %s" % backstore.name) + + def ui_complete_create(self, parameters, text, current_param): + ''' + Parameter auto-completion method for user command create. + @param parameters: Parameters on the command line. + @type parameters: dict + @param text: Current text of parameter being typed by the user. + @type text: str + @param current_param: Name of parameter to complete. + @type current_param: str + @return: Possible completions + @rtype: list of str + ''' + if current_param == 'backstore_plugin': + plugins = ['pscsi', 'rd_dr', 'rd_mcp', 'fileio', 'iblock'] + completions = [plugin for plugin in plugins + if plugin.startswith(text)] + else: + completions = [] + + if len(completions) == 1: + return [completions[0] + ' '] + else: + return completions + + def ui_command_delete(self, backstore): + ''' + Deletes a I{backstore}, and recursively all defined storage objects + hanging under it. If there are existing LUNs making use of those + storage objects, they will be deleted too. + + EXAMPLE + ======= + B{delete iblock2} + ----------------- + That would recursively delete the B{iblock} backstore with index 2. + ''' + for child in self.children: + if child.name == backstore: + child.backstore.delete() + self.del_child(child) + self.log.info("Deleted backstore %s." % backstore) + self.parent.refresh() + break + else: + self.log.error("Cannot find backstore to delete: %s." + % backstore) + + def ui_complete_delete(self, parameters, text, current_param): + ''' + Parameter auto-completion method for user command delete. + @param parameters: Parameters on the command line. + @type parameters: dict + @param text: Current text of parameter being typed by the user. + @type text: str + @param current_param: Name of parameter to complete. + @type current_param: str + @return: Possible completions + @rtype: list of str + ''' + if current_param == 'backstore': + backstores = [child.name for child in self.children] + completions = [backstore for backstore in backstores + if backstore.startswith(text)] + else: + completions = [] + + if len(completions) == 1: + return [completions[0] + ' '] + else: + return completions + +class UIBackstore(UINode): + ''' + A backstore UI. + ''' + def __init__(self, backstore): + UINode.__init__(self) + self.backstore = backstore + self.name = backstore.name + self.cfs_cwd = backstore.path + self.refresh() + + def refresh(self): + self._children = set([]) + for storage_object in self.backstore.storage_objects: + self.add_child(UIStorageObject(storage_object)) + + def summary(self): + no_storage_objects = len(self._children) + if no_storage_objects > 1: + msg = "%d Storage Objects" % no_storage_objects + else: + msg = "%d Storage Object" % no_storage_objects + return (msg, None) + + def use_wwn(self, wwn): + if wwn is None: + wwn == True + if wwn: + self.log.info("Generating a wwn serial.") + else: + self.log.info("Not generating a wwn serial.") + return wwn + + def use_buffered(self, buffered): + if buffered is None: + buffered == True + if buffered: + self.log.info("Using buffered mode.") + else: + self.log.info("Not using buffered mode.") + return buffered + + def ui_command_version(self): + ''' + Displays the version of the current backstore's plugin. + ''' + self.con.display("Backstore plugin %s %s" + % (self.backstore.plugin, self.backstore.version)) + + def ui_command_delete(self, name): + ''' + Recursively deletes the storage object having the specified I{name}. If + there are LUNs using this storage object, they will be deleted too. + + EXAMPLE + ======= + B{delete mystorage} + ------------------- + Deletes the storage object named mystorage, and all associated LUNs. + ''' + for child in self.children: + if child.name == name: + child.storage_object.delete() + self.del_child(child) + self.log.info("Deleted storage object %s." % name) + self.parent.parent.refresh() + break + else: + self.log.error("Could not find storage object to delete: %s." + % name) + + def ui_complete_delete(self, parameters, text, current_param): + ''' + Parameter auto-completion method for user command delete. + @param parameters: Parameters on the command line. + @type parameters: dict + @param text: Current text of parameter being typed by the user. + @type text: str + @param current_param: Name of parameter to complete. + @type current_param: str + @return: Possible completions + @rtype: list of str + ''' + if current_param == 'name': + names = [child.name for child in self.children] + completions = [name for name in names + if name.startswith(text)] + else: + completions = [] + + if len(completions) == 1: + return [completions[0] + ' '] + else: + return completions + +class UIPSCSIBackstore(UIBackstore): + ''' + PSCSI backstore UI. + ''' + def ui_command_create(self, name, dev): + ''' + Creates a PSCSI storage object, with supplied name and SCSI device. The + SCSI device I{dev} can either be a path name to the device, in which + case it is recommended to use the /dev/disk/by-id hierarchy to have + consistent naming should your physical SCSI system be modified, or an + SCSI device ID in the H:C:T:L format, which is not recommended as SCSI + IDs may vary in time. + ''' + so = PSCSIStorageObject(self.backstore, name, dev) + self.add_child(UIStorageObject(so)) + self.log.info("Created pscsi storage object %s using %s" + % (name, dev)) + +class UIRDDRBackstore(UIBackstore): + ''' + RDDR backstore UI. + ''' + def ui_command_create(self, name, size, wwn=None): + ''' + Creates an RDDR storage object. I{size} is the size of the ramdisk, and + the optional I{wwn} parameter is a boolean specifying whether or not we + should generate a T10 wwn serial for the unit (by default, yes). + + SIZE SYNTAX + =========== + - If size is an int, it represents a number of bytes. + - If size is a string, the following units can be used: + - B{B} or no unit present for bytes + - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes) + - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes) + - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes) + - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes) + ''' + so = RDDRStorageObject(self.backstore, name, size, self.use_wwn(wwn)) + self.add_child(UIStorageObject(so)) + self.log.info("Created rd_dr ramdisk storage object %s with size %s." + % (name, size)) + +class UIRDMCPBackstore(UIBackstore): + ''' + RDMCP backstore UI. + ''' + def ui_command_create(self, name, size, wwn=None): + ''' + Creates an RDMCP storage object. I{size} is the size of the ramdisk, + and the optional I{wwn} parameter is a boolean specifying whether or + not we should generate a T10 wwn Serial for the unit (by default, yes). + + SIZE SYNTAX + =========== + - If size is an int, it represents a number of bytes. + - If size is a string, the following units can be used: + - B{B} or no unit present for bytes + - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes) + - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes) + - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes) + - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes) + ''' + so = RDMCPStorageObject(self.backstore, name, size, self.use_wwn(wwn)) + self.add_child(UIStorageObject(so)) + self.log.info("Created rd_mcp ramdisk storage object %s with size %s." + % (name, size)) + +class UIFileIOBackstore(UIBackstore): + ''' + FileIO backstore UI. + ''' + def ui_command_create(self, name, file_or_dev, size=None, wwn=None, + buffered=None): + ''' + Creates a FileIO storage object. If I{file_or_dev} is a path to a + regular file to be used as backend, then the I{size} parameter is + mandatory. Else, if I{file_or_dev} is a path to a block device, the + size parameter B{must} be ommited. If present, I{size} is the size of + the file to be used, I{file} the path to the file or I{dev} the path to + a block device. The optional I{wwn} parameter is a boolean specifying + whether or not we should generate a T10 wwn Serial for the unit (by + default, yes). The I{buffered} parameter is a boolean stating whether + or not to enable buffered mode. It is disabled by default (synchronous + mode). + + SIZE SYNTAX + =========== + - If size is an int, it represents a number of bytes. + - If size is a string, the following units can be used: + - B{B} or no unit present for bytes + - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes) + - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes) + - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes) + - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes) + ''' + is_dev = get_block_type(file_or_dev) is not None \ + or is_disk_partition(file_or_dev) + + if size is None and is_dev: + so = FileIOStorageObject(self.backstore, name, file_or_dev, + gen_wwn=self.use_wwn(wwn), + buffered_mode=self.use_buffered(buffered)) + self.log.info("Created fileio storage object %s with size %s." + % (name, size)) + self.add_child(UIStorageObject(so)) + elif size is not None and not is_dev: + so = FileIOStorageObject(self.backstore, name, file_or_dev, size, + gen_wwn=self.use_wwn(wwn), + buffered_mode=self.use_buffered(buffered)) + self.log.info("Created fileio storage object %s." % name) + self.add_child(UIStorageObject(so)) + else: + self.log.error("For fileio, you must either specify both a file " + + "and a size, or just a device path.") + +class UIIBlockBackstore(UIBackstore): + ''' + IBlock backstore UI. + ''' + def ui_command_create(self, name, dev, wwn=None): + ''' + Creates an IBlock Storage object. I{dev} is the path to the TYPE_DISK + block device to use and the optional I{wwn} parameter is a boolean + specifying whether or not we should generate a T10 wwn Serial for the + unit (by default, yes). + ''' + if wwn is None: + wwn == True + if wwn: + self.log.info("Generating a wwn serial.") + else: + self.log.info("Not generating a wwn serial.") + + so = IBlockStorageObject(self.backstore, name, dev, wwn) + self.add_child(UIStorageObject(so)) + self.log.info("Created iblock storage object %s using %s." + % (name, dev)) + +class UIStorageObject(UINode, UIAttributes): + ''' + A storage object UI. + ''' + def __init__(self, storage_object): + UINode.__init__(self) + UIAttributes.__init__(self, storage_object) + self.storage_object = storage_object + self.name = storage_object.name + self.cfs_cwd = storage_object.path + self.refresh() + + def summary(self): + so = self.storage_object + if so.backstore.plugin.startswith("rd"): + path = "ramdisk" + else: + path = so.udev_path + if not path: + return ("BROKEN STORAGE LINK", False) + else: + return ("%s %s" % (path, so.status), True) + |
