def ui_command_create(self, name, size, cfgstring, wwn=None, hw_max_sectors=None): ''' Creates a User-backed storage object. 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) ''' size = human_to_bytes(size) wwn = self.ui_eval_param(wwn, 'string', None) config = self.handler + "/" + cfgstring ok, errmsg = self.iface.CheckConfig('(s)', config) if not ok: raise ExecutionError("cfgstring invalid: %s" % errmsg) try: so = UserBackedStorageObject(name, size=size, config=config, wwn=wwn, hw_max_sectors=hw_max_sectors) except: raise ExecutionError("UserBackedStorageObject creation failed.") ui_so = UIUserBackedStorageObject(so, self) self.shell.log.info("Created user-backed storage object %s size %d." % (name, size)) return self.new_node(ui_so)
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 ui_command_changemedium(self, name, size, cfgstring): size = human_to_bytes(size) config = self.handler + "/" + cfgstring try: rc, errmsg = self.iface.ChangeMedium('(sts)', name, size, config) except Exception as e: raise ExecutionError("ChangeMedium failed: %s" % e) else: if rc == 0: self.shell.log.info("Medium Changed.") else: raise ExecutionError("ChangeMedium failed: %s" % errmsg)
def ui_command_restoreconfig(self, savefile=default_save_file, clear_existing=False, target=None, storage_object=None): ''' 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 target = self.ui_eval_param(target, 'string', None) storage_object = self.ui_eval_param(storage_object, 'string', None) errors = self.rtsroot.restore_from_file(savefile, clear_existing, target, storage_object) 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_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 backup_error = None if not os.path.exists(backup_dir): try: os.makedirs(backup_dir); except OSError as exe: raise ExecutionError("Cannot create backup directory [%s] %s." % (backup_dir, exc.strerror)) # Only save backups if savefile exits if os.path.exists(savefile): backed_files_list = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) # Save backup if 1. backup dir is empty, or 2. savefile is differnt from recent backup copy if not backed_files_list or not filecmp.cmp(backed_files_list[-1], savefile): try: shutil.copy(savefile, backupfile) except IOError as ioe: backup_error = ioe.strerror or "Unknown error" if backup_error == None: # Kill excess backups max_backup_files = int(self.shell.default_prefs['max_backup_files']) try: with open(universal_prefs_file) as prefs: backups = [line for line in prefs.read().splitlines() if re.match('^max_backup_files\s*=', line)] if max_backup_files < int(backups[0].split('=')[1].strip()): max_backup_files = int(backups[0].split('=')[1].strip()) except: self.shell.log.debug("No universal prefs file '%s'." % universal_prefs_file) files_to_unlink = list(reversed(backed_files_list))[max_backup_files:] for f in files_to_unlink: with ignored(IOError): os.unlink(f) self.shell.log.info("Last %d configs saved in %s." % \ (max_backup_files, backup_dir)) else: self.shell.log.warning("Could not create backup file %s: %s." % \ (backupfile, backup_error)) self.rtsroot.save_to_file(savefile) self.shell.log.info("Configuration saved to %s" % savefile)
def ui_command_create(self, tag, portal_list): """Add a portal group. Args: portals: List of portals e.g. ip:port ip2:port2 tag: Portal group tag (unique, integer > 0) """ portals = [] for portal in portal_list.strip().split(" "): host = portal cpumask = None if "@" in portal: host, cpumask = portal.split("@") if ":" not in host: raise ExecutionError( "Incorrect format of portal group. Port is missing." "Use 'help create' to see the command syntax.") host, port = host.rsplit(":", -1) portals.append({'host': host, 'port': port}) if cpumask: print( "WARNING: Specifying a CPU mask for portal groups is no longer supported. Ignoring." ) tag = self.ui_eval_param(tag, "number", None) self.get_root().construct_portal_group(tag=tag, portals=portals, private=None)
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 ui_command_delete(self, name, save=None): ''' 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) save = self.ui_eval_param(save, 'bool', False) if save: rn = self.get_root() rn._save_backups(default_save_file) child.rtsnode.delete(save=save) self.remove_child(child) self.shell.log.info("Deleted storage object %s." % name)
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 self.get_root().as_root: raise ExecutionError("This privileged command is disabled: " + "you are not root.")
def _ui_block_ro_check(self, dev): BLKROGET = 0x0000125E try: f = os.open(dev, os.O_RDONLY) except (OSError, IOError): raise ExecutionError("Could not open %s" % dev) # ioctl returns an int. Provision a buffer for it buf = array.array('b', [0] * 4) try: fcntl.ioctl(f, BLKROGET, buf) except (OSError, IOError): os.close(f) return False os.close(f) if struct.unpack('I', buf)[0] == 0: return False return True
def _create_dir(self, dirname): ''' create directory with permissions 0o600 set if directory already exists, set right perms ''' mode = stat.S_IRUSR | stat.S_IWUSR # 0o600 if not os.path.exists(dirname): umask = 0o777 ^ mode # Prevents always downgrading umask to 0 umask_original = os.umask(umask) try: os.makedirs(dirname, mode) except OSError as exe: raise ExecutionError("Cannot create directory [%s] %s." % (dirname, exe.strerror)) finally: os.umask(umask_original) else: if (os.stat(dirname).st_mode & 0o777) != mode: os.chmod(dirname, mode)
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 _save_backups(self, savefile): ''' Take backup of config-file if needed. ''' # Only save backups if saving to default location if savefile != default_save_file: return backup_dir = os.path.dirname(savefile) + "/backup/" backup_name = "saveconfig-" + \ datetime.now().strftime("%Y%m%d-%H:%M:%S") + "-json.gz" backupfile = backup_dir + backup_name backup_error = None if not os.path.exists(backup_dir): try: os.makedirs(backup_dir) except OSError as exe: raise ExecutionError( "Cannot create backup directory [%s] %s." % (backup_dir, exe.strerror)) # Only save backups if savefile exits if not os.path.exists(savefile): return backed_files_list = sorted(glob(os.path.dirname(savefile) + \ "/backup/saveconfig-*json*")) # Save backup if backup dir is empty, or savefile is differnt from recent backup copy if not backed_files_list or not self._compare_files( backed_files_list[-1], savefile): try: with open(savefile, 'rb') as f_in, gzip.open(backupfile, 'wb') as f_out: shutil.copyfileobj(f_in, f_out) f_out.flush() except IOError as ioe: backup_error = ioe.strerror or "Unknown error" if backup_error == None: # remove excess backups max_backup_files = int(self.shell.prefs['max_backup_files']) try: with open(universal_prefs_file) as prefs: backups = [ line for line in prefs.read().splitlines() if re.match('^max_backup_files\s*=', line) ] if max_backup_files < int( backups[0].split('=')[1].strip()): max_backup_files = int( backups[0].split('=')[1].strip()) except: self.shell.log.debug("No universal prefs file '%s'." % universal_prefs_file) files_to_unlink = list( reversed(backed_files_list))[max_backup_files - 1:] for f in files_to_unlink: with ignored(IOError): os.unlink(f) self.shell.log.info("Last %d configs saved in %s." % (max_backup_files, backup_dir)) else: self.shell.log.warning("Could not create backup file %s: %s." % (backupfile, backup_error))
def ui_command_sessions(self, action="list", sid=None): ''' Displays a detailed list of all open sessions. PARAMETERS ========== action ------ The action is one of: - `list`` gives a short session list - `detail` gives a detailed list sid --- You can specify an "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))
def ui_command_create(self, name, file_or_dev, size=None, write_back=None, sparse=None, wwn=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) wwn = self.ui_eval_param(wwn, 'string', None) 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, wwn=wwn) 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)