Example #1
0
    def ui_command_restoreconfig(self,
                                 savefile=default_save_file,
                                 clear_existing=False):
        '''
        Restores configuration from a file.
        '''
        self.assert_root()

        savefile = os.path.expanduser(savefile)

        if not os.path.isfile(savefile):
            self.shell.log.info("Restore file %s not found" % savefile)
            return

        with open(savefile, "r") as f:
            try:
                errors = RTSRoot().restore(json.loads(f.read()),
                                           clear_existing)
            except ValueError:
                self.shell.log.error("Error parsing savefile: %s" % savefile)
                return

        if errors:
            self.shell.log.error(
                "Configuration restored, %d recoverable errors:" % len(errors))
            for error in errors:
                self.shell.log.error(error)
        else:
            self.shell.log.info("Configuration restored from %s" % savefile)

        self.refresh()
Example #2
0
    def next_hba_index(self):
        self.shell.log.debug("%r" % [(backstore.plugin, backstore.index)
                                     for backstore in RTSRoot().backstores])
        indexes = [
            backstore.index for backstore in RTSRoot().backstores
            if backstore.plugin == self.name
        ]
        self.shell.log.debug("Existing %s backstore indexes: %r" %
                             (self.name, indexes))
        for index in range(1048576):
            if index not in indexes:
                backstore_index = index
                break

        if backstore_index is None:
            raise ExecutionError("Cannot find an available backstore index.")
        else:
            self.shell.log.debug("First available %s backstore index is %d." %
                                 (self.name, backstore_index))
            return backstore_index
Example #3
0
    def refresh(self):
        '''
        Refreshes the tree of target fabric modules.
        '''
        self._children = set([])

        UIBackstores(self)

        # only show fabrics present in the system
        for fm in RTSRoot().fabric_modules:
            if fm.wwns == None or list(fm.wwns) != []:
                UIFabricModule(fm, self)
Example #4
0
 def refresh(self):
     self._children = set([])
     for backstore in RTSRoot().backstores:
         backstore_plugin = backstore.plugin
         if backstore_plugin == 'pscsi':
             UIPSCSIBackstoreLegacy(backstore, self)
         elif backstore_plugin == 'rd_mcp':
             UIRDMCPBackstoreLegacy(backstore, self)
         elif backstore_plugin == 'fileio':
             UIFileIOBackstoreLegacy(backstore, self)
         elif backstore_plugin == 'iblock':
             UIIBlockBackstoreLegacy(backstore, self)
Example #5
0
    def ui_command_clearconfig(self, confirm=None):
        '''
        Removes entire configuration of backstores and targets
        '''
        self.assert_root()

        confirm = self.ui_eval_param(confirm, 'bool', False)

        RTSRoot().clear_existing(confirm=confirm)

        self.shell.log.info("All configuration cleared")

        self.refresh()
Example #6
0
    def refresh(self):
        '''
        Refreshes the tree of target fabric modules.
        '''
        self._children = set([])
        if self.shell.prefs['legacy_hba_view']:
            UIBackstoresLegacy(self)
        else:
            UIBackstores(self)

        for fabric_module in RTSRoot().fabric_modules:
            self.shell.log.debug("Using fabric module %s." %
                                 fabric_module.name)
            UIFabricModule(fabric_module, self)
Example #7
0
def dedup_so_name(storage_object):
    '''
    Useful for migration from ui_backstore_legacy to new style with
    1:1 hba:so mapping. If name is a duplicate in a backstore, returns
    name_X where X is the HBA index.
    '''
    names = [
        so.name for so in RTSRoot().storage_objects
        if so.backstore.plugin == storage_object.backstore.plugin
    ]
    if names.count(storage_object.name) > 1:
        return "%s_%d" % (storage_object.name, storage_object.backstore.index)
    else:
        return storage_object.name
Example #8
0
    def ui_command_saveconfig(self, savefile=default_save_file):
        '''
        Saves the current configuration to a file so that it can be restored
        on next boot.
        '''
        self.assert_root()

        savefile = os.path.expanduser(savefile)

        with open(savefile + ".temp", "w+") as f:
            os.fchmod(f.fileno(), stat.S_IRUSR | stat.S_IWUSR)
            f.write(json.dumps(RTSRoot().dump(), sort_keys=True, indent=2))
            f.write("\n")
            os.fsync(f.fileno())

        # Only save backups if saving to default location
        if savefile == default_save_file:
            backup_dir = os.path.dirname(savefile) + "/backup"
            backup_name = "saveconfig-" + \
                datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json"
            backupfile = backup_dir + "/" + backup_name
            with ignored(IOError):
                shutil.move(savefile, backupfile)

            # Kill excess backups
            backups = sorted(
                glob(os.path.dirname(savefile) + "/backup/*.json"))
            files_to_unlink = list(reversed(backups))[kept_backups:]
            for f in files_to_unlink:
                os.unlink(f)

            self.shell.log.info("Last %d configs saved in %s." % \
                                    (kept_backups, backup_dir))

        os.rename(savefile + ".temp", savefile)
        self.shell.log.info("Configuration saved to %s" % savefile)
Example #9
0
    def ui_command_sessions(self, action="list", sid=None):
        '''
        Displays a detailed list of all open sessions.

        PARAMETERS
        ==========

        I{action}
        ---------
        The I{action} is one of:
            - B{list} gives a short session list
            - B{detail} gives a detailed list

        I{sid}
        ------
        You can specify an I{sid} to only list this one,
        with or without details.

        SEE ALSO
        ========
        status
        '''

        indent_step = 4
        base_steps = 0
        action_list = ("list", "detail")
        root = RTSRoot()

        if action not in action_list:
            raise ExecutionError("action must be one of: %s" %
                                 ", ".join(action_list))
        if sid is not None:
            try:
                int(sid)
            except ValueError:
                raise ExecutionError("sid must be a number, '%s' given" % sid)

        def indent_print(text, steps):
            console = self.shell.con
            console.display(console.indent(text, indent_step * steps),
                            no_lf=True)

        def print_session(session):
            acl = session['parent_nodeacl']
            indent_print("alias: %(alias)s\tsid: %(id)i type: " \
                             "%(type)s session-state: %(state)s" % session,
                         base_steps)

            if action == 'detail':
                if self.as_root:
                    if acl.authenticate_target:
                        auth = " (authenticated)"
                    else:
                        auth = " (NOT AUTHENTICATED)"
                else:
                    auth = ""

                indent_print("name: %s%s" % (acl.node_wwn, auth),
                             base_steps + 1)

                for mlun in acl.mapped_luns:
                    plugin = mlun.tpg_lun.storage_object.backstore.plugin
                    name = mlun.tpg_lun.storage_object.name
                    if mlun.write_protect:
                        mode = "r"
                    else:
                        mode = "rw"
                    indent_print(
                        "mapped-lun: %d backstore: %s/%s mode: %s" %
                        (mlun.mapped_lun, plugin, name, mode), base_steps + 1)

                for connection in session['connections']:
                    indent_print("address: %(address)s (%(transport)s)  cid: " \
                                     "%(cid)i connection-state: %(cstate)s" % \
                                     connection, base_steps + 1)

        if sid:
            printed_sessions = [
                x for x in root.sessions if x['id'] == int(sid)
            ]
        else:
            printed_sessions = list(root.sessions)

        if len(printed_sessions):
            for session in printed_sessions:
                print_session(session)
        else:
            if sid is None:
                indent_print("(no open sessions)", base_steps)
            else:
                raise ExecutionError("no session found with sid %i" % int(sid))
Example #10
0
def get_root():
    global _rtsroot
    if _rtsroot is None:
        _rtsroot = RTSRoot()
    return _rtsroot
Example #11
0
 def refresh(self):
     self._children = set([])
     for so in RTSRoot().storage_objects:
         if so.backstore.plugin == self.name:
             ui_so = UIStorageObject(so, self)
             ui_so.name = dedup_so_name(so)
Example #12
0
 def __init__(self, shell, as_root=False):
     UINode.__init__(self, '/', shell=shell)
     self.as_root = as_root
     self.rtsroot = RTSRoot()
Example #13
0
class UIRoot(UINode):
    '''
    The targetcli hierarchy root node.
    '''
    def __init__(self, shell, as_root=False):
        UINode.__init__(self, '/', shell=shell)
        self.as_root = as_root
        self.rtsroot = RTSRoot()

    def refresh(self):
        '''
        Refreshes the tree of target fabric modules.
        '''
        self._children = set([])

        UIBackstores(self)

        # only show fabrics present in the system
        for fm in self.rtsroot.fabric_modules:
            if fm.wwns == None or any(fm.wwns):
                UIFabricModule(fm, self)

    def ui_command_saveconfig(self, savefile=default_save_file):
        '''
        Saves the current configuration to a file so that it can be restored
        on next boot.
        '''
        self.assert_root()

        savefile = os.path.expanduser(savefile)

        # Only save backups if saving to default location
        if savefile == default_save_file:
            backup_dir = os.path.dirname(savefile) + "/backup"
            backup_name = "saveconfig-" + \
                datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json"
            backupfile = backup_dir + "/" + backup_name
            with ignored(IOError):
                shutil.copy(savefile, backupfile)

            # Kill excess backups
            backups = sorted(glob(os.path.dirname(savefile) + "/backup/*.json"))
            files_to_unlink = list(reversed(backups))[kept_backups:]
            for f in files_to_unlink:
                os.unlink(f)

            self.shell.log.info("Last %d configs saved in %s." % \
                                    (kept_backups, backup_dir))

        self.rtsroot.save_to_file(savefile)

        self.shell.log.info("Configuration saved to %s" % savefile)

    def ui_command_restoreconfig(self, savefile=default_save_file, clear_existing=False):
        '''
        Restores configuration from a file.
        '''
        self.assert_root()

        savefile = os.path.expanduser(savefile)

        if not os.path.isfile(savefile):
            self.shell.log.info("Restore file %s not found" % savefile)
            return

        errors = self.rtsroot.restore_from_file(savefile, clear_existing)

        self.refresh()

        if errors:
            raise ExecutionError("Configuration restored, %d recoverable errors:\n%s" % \
                                     (len(errors), "\n".join(errors)))

        self.shell.log.info("Configuration restored from %s" % savefile)

    def ui_command_clearconfig(self, confirm=None):
        '''
        Removes entire configuration of backstores and targets
        '''
        self.assert_root()

        confirm = self.ui_eval_param(confirm, 'bool', False)

        self.rtsroot.clear_existing(confirm=confirm)

        self.shell.log.info("All configuration cleared")

        self.refresh()

    def ui_command_version(self):
        '''
        Displays the targetcli and support libraries versions.
        '''
        from targetcli import __version__ as targetcli_version
        self.shell.log.info("targetcli version %s" % targetcli_version)

    def ui_command_sessions(self, action="list", sid=None):
        '''
        Displays a detailed list of all open sessions.

        PARAMETERS
        ==========

        I{action}
        ---------
        The I{action} is one of:
            - B{list} gives a short session list
            - B{detail} gives a detailed list

        I{sid}
        ------
        You can specify an I{sid} to only list this one,
        with or without details.

        SEE ALSO
        ========
        status
        '''

        indent_step = 4
        base_steps = 0
        action_list = ("list", "detail")

        if action not in action_list:
            raise ExecutionError("action must be one of: %s" %
                                                    ", ".join(action_list))
        if sid is not None:
            try:
                int(sid)
            except ValueError:
                raise ExecutionError("sid must be a number, '%s' given" % sid)

        def indent_print(text, steps):
            console = self.shell.con
            console.display(console.indent(text, indent_step * steps),
                            no_lf=True)

        def print_session(session):
            acl = session['parent_nodeacl']
            indent_print("alias: %(alias)s\tsid: %(id)i type: " \
                             "%(type)s session-state: %(state)s" % session,
                         base_steps)

            if action == 'detail':
                if self.as_root:
                    if acl.authenticate_target:
                        auth = " (authenticated)"
                    else:
                        auth = " (NOT AUTHENTICATED)"
                else:
                    auth = ""

                indent_print("name: %s%s" % (acl.node_wwn, auth),
                                 base_steps + 1)

                for mlun in acl.mapped_luns:
                    plugin = mlun.tpg_lun.storage_object.plugin
                    name = mlun.tpg_lun.storage_object.name
                    if mlun.write_protect:
                        mode = "r"
                    else:
                        mode = "rw"
                    indent_print("mapped-lun: %d backstore: %s/%s mode: %s" %
                                 (mlun.mapped_lun, plugin, name, mode),
                                 base_steps + 1)

                for connection in session['connections']:
                    indent_print("address: %(address)s (%(transport)s)  cid: " \
                                     "%(cid)i connection-state: %(cstate)s" % \
                                     connection, base_steps + 1)

        if sid:
            printed_sessions = [x for x in self.rtsroot.sessions if x['id'] == int(sid)]
        else:
            printed_sessions = list(self.rtsroot.sessions)

        if len(printed_sessions):
            for session in printed_sessions:
                print_session(session)
        else:
            if sid is None:
                indent_print("(no open sessions)", base_steps)
            else:
                raise ExecutionError("no session found with sid %i" % int(sid))
Example #14
0
    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_mcp}
        --------
        This I{backstore_plugin} uses a ramdisk with a separate
        mapping using memory copy. Typically used for bandwidth
        testing.

        EXAMPLE
        =======

        B{create iblock}
        ----------------
        Creates a new backstore, using the B{iblock} I{backstore_plugin}.
        '''
        self.assert_root()
        self.shell.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.shell.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.shell.log.error("Cannot find an available backstore index.")
            return
        else:
            self.shell.log.info("First available %s backstore index is %d." %
                                (backstore_plugin, backstore_index))

        if backstore_plugin == 'pscsi':
            backstore = PSCSIBackstore(backstore_index, mode='create')
            return self.new_node(UIPSCSIBackstoreLegacy(backstore, self))
        elif backstore_plugin == 'rd_mcp':
            backstore = RDMCPBackstore(backstore_index, mode='create')
            return self.new_node(UIRDMCPBackstoreLegacy(backstore, self))
        elif backstore_plugin == 'fileio':
            backstore = FileIOBackstore(backstore_index, mode='create')
            return self.new_node(UIFileIOBackstoreLegacy(backstore, self))
        elif backstore_plugin == 'iblock':
            backstore = IBlockBackstore(backstore_index, mode='create')
            return self.new_node(UIIBlockBackstoreLegacy(backstore, self))
        else:
            self.shell.log.error("Invalid backstore plugin %s" %
                                 backstore_plugin)
            return

        self.shell.log.info("Created new backstore %s" % backstore.name)
Example #15
0
    def ui_command_create(self, name, file_or_dev, size=None, write_back=None,
                          sparse=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
        I{write_back} parameter is a boolean controlling write
        caching. It is enabled by default. The I{sparse} parameter is
        only applicable when creating a new backing file. It is a
        boolean stating if the created file should be created as a
        sparse file (the default), or fully initialized.

        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)
        '''
        self.assert_root()

        sparse = self.ui_eval_param(sparse, 'bool', True)
        write_back = self.ui_eval_param(write_back, 'bool', True)

        self.shell.log.debug("Using params size=%s write_back=%s sparse=%s"
                             % (size, write_back, sparse))

        file_or_dev = os.path.expanduser(file_or_dev)
        # can't use is_dev_in_use() on files so just check against other
        # storage object paths
        if os.path.exists(file_or_dev):
            for so in RTSRoot().storage_objects:
                if so.udev_path and os.path.samefile(file_or_dev, so.udev_path):
                    raise ExecutionError("storage object for %s already exists: %s" % \
                                             (file_or_dev, so.name))

        if get_block_type(file_or_dev) is not None:
            if size:
                self.shell.log.info("Block device, size parameter ignored")
                size = None
            self.shell.log.info("Note: block backstore preferred for best results")
        else:
            # use given file size only if backing file does not exist
            if os.path.isfile(file_or_dev):
                new_size = os.path.getsize(file_or_dev)
                if size:
                    self.shell.log.info("%s exists, using its size (%s bytes) instead" 
                                        % (file_or_dev, new_size))
                size = new_size
            elif os.path.exists(file_or_dev):
                raise ExecutionError("Path %s exists but is not a file" % file_or_dev)
            else:
                # create file and extend to given file size
                if not size:
                    raise ExecutionError("Attempting to create file for new" +
                                         " fileio backstore, need a size")
                size = human_to_bytes(size)
                self._create_file(file_or_dev, size, sparse)

        so = FileIOStorageObject(name, file_or_dev, size, write_back=write_back)
        ui_so = UIFileioStorageObject(so, self)
        self.setup_model_alias(so)
        self.shell.log.info("Created fileio %s with size %s"
                            % (name, so.size))
        return self.new_node(ui_so)
Example #16
0
 def refresh(self):
     self._children = set([])
     for so in RTSRoot().storage_objects:
         if so.plugin == self.name:
             ui_so = self.so_cls(so, self)
             ui_so.name = so.name