def __init__(self, parent_tpg, lun=None, storage_object=None, alias=None): ''' A LUN object can be instanciated in two ways: - B{Creation mode}: If I{storage_object} is specified, the underlying configFS object will be created with that parameter. No LUN with the same I{lun} index can pre-exist in the parent TPG in that mode, or instanciation will fail. - B{Lookup mode}: If I{storage_object} is not set, then the LUN will be bound to the existing configFS LUN object of the parent TPG having the specified I{lun} index. The underlying configFS object must already exist in that mode. @param parent_tpg: The parent TPG object. @type parent_tpg: TPG @param lun: The LUN index. @type lun: 0-255 @param storage_object: The storage object to be exported as a LUN. @type storage_object: StorageObject subclass @param alias: An optional parameter to manually specify the LUN alias. You probably do not need this. @type alias: string @return: A LUN object. ''' super(LUN, self).__init__() if isinstance(parent_tpg, TPG): self._parent_tpg = parent_tpg else: raise RTSLibError("Invalid parent TPG.") if lun is None: luns = [l.lun for l in self.parent_tpg.luns] for index in xrange(self.MAX_LUN + 1): if index not in luns: lun = index break if lun is None: raise RTSLibError("All LUNs 0-%d in use" % self.MAX_LUN) else: lun = int(lun) if lun < 0 or lun > self.MAX_LUN: raise RTSLibError("LUN must be 0 to %d" % self.MAX_LUN) self._lun = lun self._path = "%s/lun/lun_%d" % (self.parent_tpg.path, self.lun) if storage_object is None and alias is not None: raise RTSLibError("The alias parameter has no meaning " \ + "without the storage_object parameter.") if storage_object is not None: self._create_in_cfs_ine('create') try: self._configure(storage_object, alias) except: self.delete() raise else: self._create_in_cfs_ine('lookup')
def __init__(self, name, config=None, level=None, size=None, wwn=None): ''' @param name: The name of the UserBackedStorageObject. @type name: string @param dev: The path to the backend block device to be used. - Example: I{dev="/dev/sda"}. - The only device type that is accepted I{TYPE_DISK}. For other device types, use pscsi. @type dev: string @param size: The size of the device to create, in bytes. @type size: int @param config: user-handler-specific config string. - e.g. "rbd/machine1@snap4" @type config: string @param level: TCMU emulation level, 0 or 1. Level 0 will pass all SCSI commands, 1 will just pass I/O commands, READ, WRITE, etc. @type level: int @return: A UserBackedStorageObject object. ''' if size is not None: if level is None or config is None: raise RTSLibError("'size', 'level', and 'config' must be set when " "creating a new UserBackedStorageObject") if '/' not in config: raise RTSLibError("'config' must contain a '/' separating subtype " "from its configuration string") super(UserBackedStorageObject, self).__init__(name, 'create') try: self._configure(config, level, size, wwn) except: self.delete() raise else: super(UserBackedStorageObject, self).__init__(name, 'lookup')
def _configure(self, dev, size, wwn, write_back): self._check_self() block_type = get_blockdev_type(dev) if block_type is None: # a file if os.path.exists(os.path.realpath(dev)) and not os.path.isfile(dev): raise RTSLibError("Path not to a file or block device") if size is None: raise RTSLibError("Path is to a file, size needed") self._control("fd_dev_name=%s,fd_dev_size=%d" % (dev, size)) else: # a block device # size is ignored but we can't raise an exception because # dump() saves it and thus restore() will call us with it. if block_type != 0: raise RTSLibError("Device is not a TYPE_DISK block device") if is_dev_in_use(dev): raise RTSLibError("Device %s is already in use" % dev) self._control("fd_dev_name=%s" % dev) if write_back: self.set_attribute("emulate_write_cache", 1) self._control("fd_buffered_io=%d" % write_back) self._set_udev_path(dev) self._enable() super(FileIOStorageObject, self)._configure(wwn)
def _set_nexus(self, nexus_wwn=None): ''' Sets the nexus initiator WWN. Raises an exception if the nexus is already set or if the TPG does not use a nexus. ''' self._check_self() if not self.has_feature('nexus'): raise RTSLibError("The TPG does not use a nexus.") if self._get_nexus(): raise RTSLibError("The TPG's nexus initiator WWN is already set.") # Nexus wwn type should match parent target wwn_type = self.parent_target.wwn_type if nexus_wwn: # Not using fabric-specific version of normalize_wwn, since we # want to make sure wwn conforms to regexp, but don't check # against target wwn_list, since we're setting the "initiator" here. nexus_wwn = normalize_wwn((wwn_type, ), nexus_wwn)[0] else: nexus_wwn = generate_wwn(wwn_type) fm = self.parent_target.fabric_module fwrite("%s/nexus" % self.path, fm.to_fabric_wwn(nexus_wwn))
def _configure(self, tpg_lun, write_protect): self._check_self() if isinstance(tpg_lun, LUN): tpg_lun = tpg_lun.lun else: try: tpg_lun = int(tpg_lun) except ValueError: raise RTSLibError("The tpg_lun must be either an " + "integer or a LUN object.") # Check that the tpg_lun exists in the TPG for lun in self.parent_nodeacl.parent_tpg.luns: if lun.lun == tpg_lun: tpg_lun = lun break if not (isinstance(tpg_lun, LUN) and tpg_lun): raise RTSLibError("LUN %s does not exist in this TPG." % str(tpg_lun)) os.symlink(tpg_lun.path, "%s/%s" % (self.path, str(uuid.uuid4())[-10:])) try: self.write_protect = int(write_protect) > 0 except: self.write_protect = False
def _create_in_cfs_ine(self, mode): ''' Creates the configFS node if it does not already exist depending on the mode. any -> makes sure it exists, also works if the node already does exist lookup -> make sure it does NOT exist create -> create the node which must not exist beforehand Upon success (no exception raised), self._fresh is True if a node was created, else self._fresh is False. ''' if mode not in ['any', 'lookup', 'create']: raise RTSLibError("Invalid mode: %s" % mode) if self and mode == 'create': raise RTSLibError("This %s already exists in configFS." % self.__class__.__name__) elif not self and mode == 'lookup': raise RTSLibNotInCFS("No such %s in configfs: %s." % (self.__class__.__name__, self.path)) if self: self._fresh = False return try: os.mkdir(self.path) self._fresh = True except Exception as e: raise RTSLibError("Could not create %s: %s" % (self.path, e))
def __init__(self, parent_nodeacl, mapped_lun, tpg_lun=None, write_protect=None): ''' A MappedLUN object can be instantiated in two ways: - B{Creation mode}: If I{tpg_lun} is specified, the underlying configFS object will be created with that parameter. No MappedLUN with the same I{mapped_lun} index can pre-exist in the parent NodeACL in that mode, or instantiation will fail. - B{Lookup mode}: If I{tpg_lun} is not set, then the MappedLUN will be bound to the existing configFS MappedLUN object of the parent NodeACL having the specified I{mapped_lun} index. The underlying configFS object must already exist in that mode. @param mapped_lun: The mapped LUN index. @type mapped_lun: int @param tpg_lun: The TPG LUN index to map, or directly a LUN object that belong to the same TPG as the parent NodeACL. @type tpg_lun: int or LUN @param write_protect: The write-protect flag value, defaults to False (write-protection disabled). @type write_protect: bool ''' super(MappedLUN, self).__init__() if not isinstance(parent_nodeacl, NodeACL): raise RTSLibError("The parent_nodeacl parameter must be " \ + "a NodeACL object.") else: self._parent_nodeacl = parent_nodeacl if not parent_nodeacl.exists: raise RTSLibError("The parent_nodeacl does not exist.") try: self._mapped_lun = int(mapped_lun) except ValueError: raise RTSLibError("The mapped_lun parameter must be an " \ + "integer value.") self._path = "%s/lun_%d" % (self.parent_nodeacl.path, self.mapped_lun) if tpg_lun is None and write_protect is not None: raise RTSLibError("The write_protect parameter has no " \ + "meaning without the tpg_lun parameter.") if tpg_lun is not None: self._create_in_cfs_ine('create') try: self._configure(tpg_lun, write_protect) except: self.delete() raise else: self._create_in_cfs_ine('lookup')
def _configure(self, config, level, size, wwn): self._check_self() if ':' in config: raise RTSLibError("':' not allowed in config string") if level not in (0, 1): raise RTSLibError("Current allowable levels are 0 or 1") self._control("dev_config=%s" % config) self._control("pass_level=%d" % level) self._control("dev_size=%d" % size) self._enable() super(UserBackedStorageObject, self)._configure(wwn)
def _configure(self, dev, wwn, readonly): self._check_self() if get_blockdev_type(dev) != 0: raise RTSLibError("Device %s is not a TYPE_DISK block device" % dev) if is_dev_in_use(dev): raise RTSLibError("Cannot configure StorageObject because " + "device %s is already in use" % dev) self._set_udev_path(dev) self._control("udev_path=%s" % dev) self._control("readonly=%d" % readonly) self._enable() super(BlockStorageObject, self)._configure(wwn)
def __init__(self, backstore, backstore_class, name, mode): if not isinstance(backstore, backstore_class): raise RTSLibError("The parent backstore must be of " + "type %s" % backstore_class.__name__) super(StorageObject, self).__init__() self._backstore = backstore if "/" in name or " " in name or "\t" in name or "\n" in name: raise RTSLibError("A storage object's name cannot contain " " /, newline or spaces/tabs.") else: self._name = name self._path = "%s/%s" % (self.backstore.path, self.name) self._create_in_cfs_ine(mode)
def __init__(self, plugin, storage_class, index, mode): super(Backstore, self).__init__() if issubclass(storage_class, StorageObject): self._storage_object_class = storage_class self._plugin = plugin else: raise RTSLibError("StorageClass must derive from StorageObject.") try: self._index = int(index) except ValueError: raise RTSLibError("Invalid backstore index: %s" % index) self._path = "%s/core/%s_%d" % (self.configfs_dir, self._plugin, self._index) self._create_in_cfs_ine(mode)
def __init__(self, parent_target, tag=None, mode='any'): ''' @param parent_target: The parent Target object of the TPG. @type parent_target: Target @param tag: The TPG Tag (TPGT). @type tag: int > 0 @param mode:An optionnal string containing the object creation mode: - I{'any'} means the configFS object will be either looked up or created. - I{'lookup'} means the object MUST already exist configFS. - I{'create'} means the object must NOT already exist in configFS. @type mode:string @return: A TPG object. ''' super(TPG, self).__init__() if tag is None: tags = [tpg.tag for tpg in parent_target.tpgs] for index in xrange(1048576): if index not in tags and index > 0: tag = index break if tag is None: raise RTSLibError("Cannot find an available TPG Tag.") else: tag = int(tag) if not tag > 0: raise RTSLibError("The TPG Tag must be >0.") self._tag = tag if isinstance(parent_target, Target): self._parent_target = parent_target else: raise RTSLibError("Invalid parent Target.") self._path = "%s/tpgt_%d" % (self.parent_target.path, self.tag) target_path = self.parent_target.path if not self.has_feature('tpgts') and not os.path.isdir(self._path): for filename in os.listdir(target_path): if filename.startswith("tpgt_") \ and os.path.isdir("%s/%s" % (target_path, filename)) \ and filename != "tpgt_%d" % self.tag: raise RTSLibError("Target cannot have multiple TPGs.") self._create_in_cfs_ine(mode) if self.has_feature('nexus') and not self._get_nexus(): self._set_nexus()
def __init__(self, parent_tpg, node_wwn, mode='any'): ''' @param parent_tpg: The parent TPG object. @type parent_tpg: TPG @param node_wwn: The wwn of the initiator node for which the ACL is created. @type node_wwn: string @param mode: An optionnal string containing the object creation mode: - I{'any'} means the configFS object will be either looked up or created. - I{'lookup'} means the object MUST already exist configFS. - I{'create'} means the object must NOT already exist in configFS. @type mode: string @return: A NodeACL object. ''' super(NodeACL, self).__init__() if isinstance(parent_tpg, TPG): self._parent_tpg = parent_tpg else: raise RTSLibError("Invalid parent TPG.") self._node_wwn = str(node_wwn).lower() self._path = "%s/acls/%s" % (self.parent_tpg.path, self.node_wwn) self._create_in_cfs_ine(mode)
def _configure(self, dev, wwn, readonly, write_back): self._check_self() if get_blockdev_type(dev) != 0: raise RTSLibError("Device is not a TYPE_DISK block device.") if is_dev_in_use(dev): raise RTSLibError("Cannot configure StorageObject because " + "device %s is already in use." % dev) self._set_udev_path(dev) self._control("udev_path=%s" % dev) self._control("readonly=%d" % readonly) self._enable() if write_back: self.set_attribute("emulate_write_cache", 1) super(BlockStorageObject, self)._configure(wwn)
def _set_nexus(self, nexus_wwn=None): ''' Sets the nexus initiator WWN. Raises an exception if the nexus is already set or if the TPG does not use a nexus. ''' self._check_self() if not self.has_feature('nexus'): raise RTSLibError("The TPG does not use a nexus.") elif self._get_nexus(): raise RTSLibError("The TPG's nexus initiator WWN is already set.") else: if nexus_wwn is None: nexus_wwn = generate_wwn(self.parent_target.wwn_type) elif not is_valid_wwn(self.parent_target.wwn_type, nexus_wwn): raise RTSLibError("WWN '%s' is not of type '%s'." % (nexus_wwn, self.parent_target.wwn_type)) fwrite("%s/nexus" % self.path, nexus_wwn)
def _get_wwn(self): self._check_self() if self.is_configured(): path = "%s/wwn/vpd_unit_serial" % self.path return fread(path).partition(":")[2].strip() else: raise RTSLibError("Cannot read a T10 WWN Unit Serial from " + "an unconfigured StorageObject")
def _set_wwn(self, wwn): self._check_self() if self.is_configured(): path = "%s/wwn/vpd_unit_serial" % self.path fwrite(path, "%s\n" % wwn) else: raise RTSLibError("Cannot write a T10 WWN Unit Serial to " + "an unconfigured StorageObject")
def __init__(self, parent_target, tag, mode='any', nexus_wwn=None): ''' @param parent_target: The parent Target object of the TPG. @type parent_target: Target @param tag: The TPG Tag (TPGT). @type tag: int > 0 @param mode: An optionnal string containing the object creation mode: - I{'any'} means the configFS object will be either looked up or created. - I{'lookup'} means the object MUST already exist configFS. - I{'create'} means the object must NOT already exist in configFS. @type mode: string @param nexus: An optionnal naa WWN that makes sense only for fabrics supporting that feature, like the loopback fabric. @type nexus: string @return: A TPG object. ''' super(TPG, self).__init__() try: self._tag = int(tag) except ValueError: raise RTSLibError("Invalid Tag.") if tag < 0: raise RTSLibError("Invalig Tag, it must be 0 or more.") if isinstance(parent_target, Target): self._parent_target = parent_target else: raise RTSLibError("Invalid parent Target.") self._path = "%s/tpgt_%d" % (self.parent_target.path, self.tag) target_path = self.parent_target.path if not self.has_feature('tpgts') and not os.path.isdir(self._path): for filename in os.listdir(target_path): if filename.startswith("tpgt_") \ and os.path.isdir("%s/%s" % (target_path, filename)) \ and filename != "tpgt_%d" % self.tag: raise RTSLibError("Target cannot have multiple TPGs.") self._create_in_cfs_ine(mode) if self.has_feature('nexus') and not self._get_nexus(): self._set_nexus(nexus_wwn)
def _set_iser(self, boolean): path = "%s/iser" % self.path try: fwrite(path, str(int(boolean))) except IOError: # b/w compat: don't complain if iser entry is missing if os.path.isfile(path): raise RTSLibError("Cannot change iser")
def _configure(self, storage_object, alias): self._check_self() if alias is None: alias = str(uuid.uuid4())[-10:] else: alias = str(alias).strip() if '/' in alias: raise RTSLibError("Invalid alias: %s", alias) destination = "%s/%s" % (self.path, alias) if storage_object.exists: source = storage_object.path else: raise RTSLibError("storage_object does not exist in configFS.") os.symlink(source, destination)
def _set_tcq_depth(self, depth): self._check_self() path = "%s/cmdsn_depth" % self.path try: fwrite(path, "%s" % depth) except IOError, msg: msg = msg[1] raise RTSLibError("Cannot set tcq_depth: %s" % str(msg))
def __init__(self, name, storage_object_cls, mode): super(_Backstore, self).__init__() self._so_cls = storage_object_cls self._plugin = bs_params[self._so_cls]['name'] dirp = bs_params[self._so_cls].get("alt_dirprefix", self._plugin) global bs_cache if not bs_cache: for dir in glob.iglob("%s/core/*_*/*/" % self.configfs_dir): parts = dir.split("/") bs_name = parts[-2] bs_dirp, bs_index = parts[-3].rsplit("_", 1) current_key = "%s/%s" % (bs_dirp, bs_name) bs_cache[current_key] = int(bs_index) # mapping in cache? self._lookup_key = "%s/%s" % (dirp, name) self._index = bs_cache.get(self._lookup_key, None) if self._index != None and mode == 'create': raise RTSLibError("Storage object %s/%s exists" % (self._plugin, name)) elif self._index == None: if mode == 'lookup': raise RTSLibError("Storage object %s/%s not found" % (self._plugin, name)) else: # Allocate new index value for index in xrange(1048576): if index not in bs_cache.values(): self._index = index bs_cache[self._lookup_key] = self._index break else: raise RTSLibError("No available backstore index") self._path = "%s/core/%s_%d" % (self.configfs_dir, dirp, self._index) try: self._create_in_cfs_ine(mode) except: del bs_cache[self._lookup_key] raise
def _set_iser_attr(self, iser_attr): path = "%s/iser" % self.path if os.path.isfile(path): if iser_attr: fwrite(path, "1") else: fwrite(path, "0") else: raise RTSLibError("iser network portal attribute does not exist.")
def _configure(self, dev): self._check_self() # Use H:C:T:L format or preserve the path given by the user. try: (hostid, channelid, targetid, lunid) = \ convert_scsi_path_to_hctl(dev) except TypeError: try: (hostid, channelid, targetid, lunid) = dev.split(':') hostid = int(hostid) channelid = int(channelid) targetid = int(targetid) lunid = int(lunid) except ValueError: raise RTSLibError("Cannot find SCSI device by " + "path, and dev " + "parameter not in H:C:T:L " + "format: %s" % dev) else: udev_path = convert_scsi_hctl_to_path(hostid, channelid, targetid, lunid) if not udev_path: raise RTSLibError("SCSI device does not exist") else: udev_path = dev.strip() if is_dev_in_use(udev_path): raise RTSLibError("Cannot configure StorageObject because " + "device %s (SCSI %d:%d:%d:%d) " % (udev_path, hostid, channelid, targetid, lunid) + "is already in use") self._control("scsi_host_id=%d," % hostid \ + "scsi_channel_id=%d," % channelid \ + "scsi_target_id=%d," % targetid \ + "scsi_lun_id=%d" % lunid) self._set_udev_path(udev_path) self._enable() super(PSCSIStorageObject, self)._configure()
def set_parameter(self, parameter, value): ''' Sets the value of a named RFC-3720 parameter. The parameter must exist in configFS. @param parameter: The RFC-3720 parameter's name. It is case-sensitive. @type parameter: string @param value: The parameter's value. @type value: string ''' self._check_self() path = "%s/param/%s" % (self.path, str(parameter)) if not os.path.isfile(path): raise RTSLibError("Cannot find parameter: %s." % str(parameter)) else: try: fwrite(path, "%s\n" % str(value)) except Exception as e: raise RTSLibError("Cannot set parameter %s: %s" % (parameter, e))
def set_attribute(self, attribute, value): ''' Sets the value of a named attribute. The attribute must exist in configFS. @param attribute: The attribute's name. It is case-sensitive. @type attribute: string @param value: The attribute's value. @type value: string ''' self._check_self() path = "%s/attrib/%s" % (self.path, str(attribute)) if not os.path.isfile(path): raise RTSLibError("Cannot find attribute: %s." % str(attribute)) else: try: fwrite(path, "%s" % str(value)) except Exception as e: raise RTSLibError("Cannot set attribute %s: %s" % (attribute, e))
def __init__(self, parent_tpg, ip_address, port=3260, mode='any'): ''' @param parent_tpg: The parent TPG object. @type parent_tpg: TPG @param ip_address: The ipv4 IP address of the NetworkPortal. @type ip_address: string @param port: The optional (defaults to 3260) NetworkPortal TCP/IP port. @type port: int @param mode: An optionnal string containing the object creation mode: - I{'any'} means the configFS object will be either looked up or created. - I{'lookup'} means the object MUST already exist configFS. - I{'create'} means the object must NOT already exist in configFS. @type mode:string @return: A NetworkPortal object. ''' super(NetworkPortal, self).__init__() if not (is_ipv4_address(ip_address) or is_ipv6_address(ip_address)): raise RTSLibError("Invalid IP address: %s" % ip_address) else: self._ip_address = str(ip_address) try: self._port = int(port) except ValueError: raise RTSLibError("Invalid port.") if isinstance(parent_tpg, TPG): self._parent_tpg = parent_tpg else: raise RTSLibError("Invalid parent TPG.") if is_ipv4_address(ip_address): self._path = "%s/np/%s:%d" \ % (self.parent_tpg.path, self.ip_address, self.port) else: self._path = "%s/np/[%s]:%d" \ % (self.parent_tpg.path, self.ip_address, self.port) try: self._create_in_cfs_ine(mode) except OSError, msg: raise RTSLibError(msg[1])
def _get_version(self): if self.exists: for attr in version_attributes: path = "%s/%s" % (self.path, attr) if os.path.isfile(path): return fread(path) else: raise RTSLibError("Can't find version for fabric module %s" % self.name) else: return None
def get_attribute(self, attribute): ''' @param attribute: The attribute's name. It is case-sensitive. @return: The named attribute's value, as a string. ''' self._check_self() path = "%s/attrib/%s" % (self.path, str(attribute)) if not os.path.isfile(path): raise RTSLibError("Cannot find attribute: %s." % str(attribute)) else: return fread(path)
def _configure(self, storage_object, alias): self._check_self() if alias is None: alias = str(uuid.uuid4())[-10:] else: alias = str(alias).strip() if '/' in alias: raise RTSLibError("Invalid alias: %s", alias) destination = "%s/%s" % (self.path, alias) from tcm import StorageObject if isinstance(storage_object, StorageObject): if storage_object.exists: source = storage_object.path else: raise RTSLibError("The storage_object does not exist " \ + "in configFS.") else: raise RTSLibError("Invalid storage object.") os.symlink(source, destination)