def _create_file(self, filename, size, sparse=True): try: f = open(filename, "w+") except (OSError, IOError): raise ExecutionError("Could not open %s" % filename) try: if sparse: try: os.posix_fallocate(f.fileno(), 0, size) except AttributeError: # Prior to version 3.3, Python does not provide fallocate os.ftruncate(f.fileno(), size) else: self.shell.log.info("Writing %d bytes" % size) while size > 0: write_size = min(size, 1024) f.write("\0" * write_size) size -= write_size except (OSError, IOError): os.remove(filename) raise ExecutionError("Could not expand file to %d bytes" % size) except OverflowError: raise ExecutionError("The file size is too large (%d bytes)" % size) finally: f.close()
def setup_model_alias(self, storageobject): if self.shell.prefs['export_backstore_name_as_model']: try: storageobject.set_attribute("emulate_model_alias", 1) except RTSLibError: raise ExecutionError("'export_backstore_name_as_model' is set but" " emulate_model_alias\n not supported by kernel.")
def assert_root(self): ''' For commands requiring root privileges, disable command if not the root node's as_root attribute is False. ''' root_node = self.get_root() if hasattr(root_node, 'as_root') and not root_node.as_root: raise ExecutionError("This privileged command is disabled: " + "you are not root.")
def _create_file(self, filename, size, sparse=True): f = open(filename, "w+") try: if sparse: os.ftruncate(f.fileno(), size) else: self.shell.log.info("Writing %s bytes" % size) while size > 0: write_size = min(size, 1024) f.write("\0" * write_size) size -= write_size except IOError: f.close() os.remove(filename) raise ExecutionError("Could not expand file to size") f.close()
def ui_command_enable(self): ''' Enables the TPG. SEE ALSO ======== B{disable status} ''' self.assert_root() if self.rtsnode.enable: self.shell.log.info("The TPGT is already enabled.") else: try: self.rtsnode.enable = True self.shell.log.info("The TPGT has been enabled.") except RTSLibError: raise ExecutionError("The TPGT could not be enabled.")
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
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. ''' self.assert_root() try: child = self.get_child(name) except ValueError: raise ExecutionError("No storage object named %s." % name) child.rtsnode.delete() self.remove_child(child) self.shell.log.info("Deleted storage object %s." % name)
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_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))
def assert_available_so_name(self, name): names = [child.name for child in self.children] if name in names: raise ExecutionError("Storage object %s/%s already exist." % (self.name, name))
except Exception, exception: backstore.delete() raise exception self.shell.log.info("Created fileio %s." % name) ui_so = UIStorageObject(so, self) return self.new_node(ui_so) else: # use given file size only if backing file does not exist if os.path.isfile(file_or_dev): new_size = str(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") self._create_file(file_or_dev, convert_human_to_bytes(size), sparse) class UIIBlockBackstore(UIBackstore): ''' IBlock backstore UI. ''' def __init__(self, parent): UIBackstore.__init__(self, 'iblock', parent)
def ui_command_create(self, storage_object, lun=None, add_mapped_luns=None): ''' Creates a new LUN in the Target Portal Group, attached to a storage object. If the I{lun} parameter is omitted, the first available LUN in the TPG will be used. If present, it must be a number greater than 0. Alternatively, the syntax I{lunX} where I{X} is a positive number is also accepted. The I{storage_object} must be the path of an existing storage object, i.e. B{/backstore/pscsi0/mydisk} to reference the B{mydisk} storage object of the virtual HBA B{pscsi0}. If I{add_mapped_luns} is omitted, the global parameter B{auto_add_mapped_luns} will be used, else B{true} or B{false} are accepted. If B{true}, then after creating the LUN, mapped LUNs will be automatically created for all existing node ACLs, mapping the new LUN. SEE ALSO ======== B{delete} ''' self.assert_root() add_mapped_luns = \ self.ui_eval_param(add_mapped_luns, 'bool', self.shell.prefs['auto_add_mapped_luns']) try: so = self.get_node(storage_object).rtsnode except ValueError: self.shell.log.error("Invalid storage object %s." % storage_object) return if so in (l.storage_object for l in self.parent.rtsnode.luns): raise ExecutionError("lun for storage object %s already exists" \ % storage_object) if lun and lun.lower().startswith('lun'): lun = lun[3:] lun_object = LUN(self.tpg, lun, so) self.shell.log.info("Created LUN %s." % lun_object.lun) ui_lun = UILUN(lun_object, self) if add_mapped_luns: for acl in self.tpg.node_acls: if lun: mapped_lun = lun else: mapped_lun = 0 existing_mluns = [mlun.mapped_lun for mlun in acl.mapped_luns] if mapped_lun in existing_mluns: mapped_lun = None for possible_mlun in xrange(LUN.MAX_LUN): if possible_mlun not in existing_mluns: mapped_lun = possible_mlun break if mapped_lun == None: self.shell.log.warning( "Cannot map new lun %s into ACL %s" % (lun_object.lun, acl.node_wwn)) else: mlun = MappedLUN(acl, mapped_lun, lun_object, write_protect=False) self.shell.log.info( "Created LUN %d->%d mapping in node ACL %s" % (mlun.tpg_lun.lun, mlun.mapped_lun, acl.node_wwn)) self.parent.refresh() return self.new_node(ui_lun)
def ui_command_create(self, mapped_lun, tpg_lun_or_backstore, write_protect=None): ''' Creates a mapping to one of the TPG LUNs for the initiator referenced by the ACL. The provided I{tpg_lun_or_backstore} will appear to that initiator as LUN I{mapped_lun}. If the I{write_protect} flag is set to B{1}, the initiator will not have write access to the Mapped LUN. A storage object may also be given for the I{tpg_lun_or_backstore} parameter, in which case the TPG LUN will be created for that backstore before mapping the LUN to the initiator. If a TPG LUN for the backstore already exists, the Mapped LUN will map to that TPG LUN. Finally, a path to an existing block device or file can be given. If so, a storage object of the appropriate type is created with default parameters, followed by the TPG LUN and the Mapped LUN. SEE ALSO ======== B{delete} ''' self.assert_root() try: mapped_lun = int(mapped_lun) except ValueError: raise ExecutionError("mapped_lun must be an integer") try: if tpg_lun_or_backstore.startswith("lun"): tpg_lun_or_backstore = tpg_lun_or_backstore[3:] tpg_lun = int(tpg_lun_or_backstore) except ValueError: try: so = self.get_node(tpg_lun_or_backstore).rtsnode except ValueError: try: so = StorageObjectFactory(tpg_lun_or_backstore) self.shell.log.info("Created storage object %s." % so.name) except RTSLibError: raise ExecutionError( "LUN, storage object, or path not valid") self.get_node("/backstores").refresh() ui_tpg = self.parent.parent for lun in ui_tpg.rtsnode.luns: if so == lun.storage_object: tpg_lun = lun.lun break else: lun_object = LUN(ui_tpg.rtsnode, storage_object=so) self.shell.log.info("Created LUN %s." % lun_object.lun) ui_lun = UILUN(lun_object, ui_tpg.get_node("luns")) tpg_lun = ui_lun.rtsnode.lun if tpg_lun in (ml.tpg_lun.lun for ml in self.rtsnodes[0].mapped_luns): self.shell.log.warning( "Warning: TPG LUN %d already mapped to this NodeACL" % tpg_lun) for na in self.rtsnodes: mlun = MappedLUN(na, mapped_lun, tpg_lun, write_protect) ui_mlun = UIMappedLUN(mlun, self) self.shell.log.info("Created Mapped LUN %s." % mlun.mapped_lun) return self.new_node(ui_mlun)
def ui_command_tag(self, wwn_or_tag, new_tag): ''' Tag a NodeACL. Usage: tag <wwn_or_tag> <new_tag> Tags help manage initiator WWNs. A tag can apply to one or more WWNs. This can give a more meaningful name to a single initiator's configuration, or allow multiple initiators with identical settings to be configured en masse. The WWNs described by <wwn_or_tag> will be given the new tag. If new_tag already exists, its new members will adopt the current tag's configuration. Within a tag, the 'info' command shows the WWNs the tag applies to. Use 'untag' to remove tags. NOTE: tags are only supported in kernel 3.8 and above. ''' if wwn_or_tag == new_tag: return # Since all WWNs have a '.' in them, let's avoid confusion. if '.' in new_tag: raise ExecutionError("'.' not permitted in tag names.") src = list(self.find_tagged(wwn_or_tag)) if not src: raise ExecutionError("wwn_or_tag %s not found." % wwn_or_tag) old_tag_members = list(self.find_tagged(new_tag)) # handle overlap src_wwns = [na.node_wwn for na in src] old_tag_members = [ old for old in old_tag_members if old.node_wwn not in src_wwns ] for na in src: na.tag = new_tag # if joining a tag, take its config if old_tag_members: model = old_tag_members[0] for mlun in na.mapped_luns: mlun.delete() for mlun in model.mapped_luns: MappedLUN(na, mlun.mapped_lun, mlun.tpg_lun, mlun.write_protect) if self.parent.rtsnode.has_feature("auth"): for param in auth_params: setattr(na, "chap_" + param, getattr(model, "chap_" + param)) for item in model.list_attributes(writable=True): na.set_attribute(item, model.get_attribute(item)) for item in model.list_parameters(writable=True): na.set_parameter(item, model.get_parameter(item)) self.refresh()
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)