def ui_command_create(self, mapped_lun, tpg_lun, write_protect=None): ''' Creates a mapping to one of the TPG LUNs for the initiator referenced by the ACL. The provided I{tpg_lun} 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. SEE ALSO ======== B{delete} ''' self.assert_root() try: tpg_lun = int(tpg_lun) mapped_lun = int(mapped_lun) except ValueError: self.shell.log.error("Incorrect LUN value.") return if tpg_lun in (ml.tpg_lun.lun for ml in self.rtsnode.mapped_luns): self.shell.log.warning( "Warning: TPG LUN %d already mapped to this NodeACL" % tpg_lun) mlun = MappedLUN(self.rtsnode, 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_create(self, wwn, add_mapped_luns=None): ''' Creates a Node ACL for the initiator node with the specified I{wwn}. The node's I{wwn} must match the expected WWN Type of the target's fabric module. 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 ACL, mapped LUNs will be automatically created for all existing LUNs. 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']) node_acl = NodeACL(self.tpg, wwn, mode="create") ui_node_acl = UINodeACL(node_acl.node_wwn, self) self.shell.log.info("Created Node ACL for %s" % node_acl.node_wwn) if add_mapped_luns: for lun in self.tpg.luns: MappedLUN(node_acl, lun.lun, lun.lun, write_protect=False) self.shell.log.info("Created mapped LUN %d." % lun.lun) self.refresh() return self.new_node(ui_node_acl)
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. SEE ALSO ======== B{delete} ''' self.assert_root() try: mapped_lun = int(mapped_lun) except ValueError: self.shell.log.error("mapped_lun must be an integer") return 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: self.shell.log.error("LUN or storage object not found") return 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_delete(self, mapped_lun): ''' Deletes the specified I{mapped_lun}. SEE ALSO ======== B{create} ''' self.assert_root() mlun = MappedLUN(self.rtsnode, mapped_lun) mlun.delete() self.shell.log.info("Deleted Mapped LUN %s." % mapped_lun) self.refresh()
def apply_delete_obj(obj): ''' Deletes an object from the live system. ''' # TODO Factorize this when stable # TODO storage fabric cannot be deleted from the system, find a way to # handle this when i.e. path 'storage fileio' is in current config, but # no objects are hanging under it. root = get_root() log.debug("apply_delete(%s)" % obj.data) if obj.key[0] == 'mapped_lun': acl = obj.parent if acl.parent.key[0] == 'tpgt': tpg = acl.parent target = tpg.parent else: tpg = None target = acl.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') if tpg is None: tpgt = 1 else: tpgt = int(tpg.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') node_wwn = acl.key[1] lio_acl = NodeACL(lio_tpg, node_wwn, mode='lookup') mlun = int(obj.key[1]) lio_mlun = MappedLUN(lio_acl, mlun) lio_mlun.delete() elif obj.key[0] == 'acl': if obj.parent.key[0] == 'tpgt': tpg = obj.parent target = tpg.parent else: tpg = None target = obj.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') if tpg is None: tpgt = 1 else: tpgt = int(tpg.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') node_wwn = obj.key[1] lio_acl = NodeACL(lio_tpg, node_wwn, mode='lookup') lio_acl.delete() elif obj.key[0] == 'portal': if obj.parent.key[0] == 'tpgt': tpg = obj.parent target = tpg.parent else: tpg = None target = obj.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') if tpg is None: tpgt = 1 else: tpgt = int(tpg.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') (address, _, port) = obj.key[1].partition(':') port = int(port) lio_portal = NetworkPortal(lio_tpg, address, port, mode='lookup') lio_portal.delete() elif obj.key[0] == 'lun': if obj.parent.key[0] == 'tpgt': tpg = obj.parent target = tpg.parent else: tpg = None target = obj.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') if tpg is None: tpgt = 1 else: tpgt = int(tpg.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') lun = int(obj.key[1]) lio_lun = LUN(lio_tpg, lun) lio_lun.delete() elif obj.key[0] == 'tpgt': target = obj.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') tpgt = int(obj.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') # FIXME IS this really needed ? lio_tpg.enable = True lio_tpg.delete() elif obj.key[0] == 'target': fabric = obj.parent wwn = obj.key[1] lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=wwn, mode='lookup') lio_target.delete() elif obj.key[0] == 'disk': plugin = obj.parent.key[1] name = obj.key[1] matching_lio_so = [ so for so in root.storage_objects if so.backstore.plugin == plugin and so.name == name ] log.debug("Looking for storage object %s in %s" % (obj.path_str, str([ "%s/%s" % (so.backstore.plugin, so.name) for so in root.storage_objects ]))) if len(matching_lio_so) > 1: raise ConfigError("Detected unsupported configfs storage objects " "allocation schema for storage object '%s'" % obj.path_str) elif len(matching_lio_so) == 0: raise ConfigError("Could not find storage object '%s'" % obj.path_str) else: lio_so = matching_lio_so[0] lio_so.delete()
def apply_create_obj(obj): ''' Creates an object on the live system. ''' # TODO Factorize this when stable, merging it with update and delete, # leveraging rtslib 'any' mode (create if not exist) # TODO storage root = get_root() log.debug("apply_create(%s)" % obj.data) if obj.key[0] == 'mapped_lun': acl = obj.parent if acl.parent.key[0] == 'tpgt': tpg = acl.parent target = tpg.parent else: tpg = None target = acl.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') if tpg is None: tpgt = 1 else: tpgt = int(tpg.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') node_wwn = acl.key[1] lio_acl = NodeACL(lio_tpg, node_wwn, mode='lookup') mlun = int(obj.key[1]) write_protect = obj_attr(obj, "write_protect") tpg_lun = int(obj_attr(obj, "target_lun").rpartition(' ')[2]) lio_mlun = MappedLUN(lio_acl, mlun, tpg_lun, write_protect) apply_group_attrs(obj, lio_mlun) elif obj.key[0] == 'acl': if obj.parent.key[0] == 'tpgt': tpg = obj.parent target = tpg.parent else: tpg = None target = obj.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') if tpg is None: tpgt = 1 else: tpgt = int(tpg.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') node_wwn = obj.key[1] lio_acl = NodeACL(lio_tpg, node_wwn) apply_group_attrs(obj, lio_acl) elif obj.key[0] == 'portal': if obj.parent.key[0] == 'tpgt': tpg = obj.parent target = tpg.parent else: tpg = None target = obj.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') if tpg is None: tpgt = 1 else: tpgt = int(tpg.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') (address, _, port) = obj.key[1].partition(':') port = int(port) lio_portal = NetworkPortal(lio_tpg, address, port) apply_group_attrs(obj, lio_portal) elif obj.key[0] == 'lun': if obj.parent.key[0] == 'tpgt': tpg = obj.parent target = tpg.parent else: tpg = None target = obj.parent fabric = target.parent lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') if tpg is None: tpgt = 1 else: tpgt = int(tpg.key[1]) lio_tpg = TPG(lio_target, tpgt, mode='lookup') lun = int(obj.key[1]) (plugin, name) = obj_attr(obj, "backend") # TODO move that to a separate function, use for disk too matching_lio_so = [ so for so in root.storage_objects if so.backstore.plugin == plugin and so.name == name ] if len(matching_lio_so) > 1: raise ConfigError("Detected unsupported configfs storage objects " "allocation schema for storage object '%s'" % obj.path_str) elif len(matching_lio_so) == 0: raise ConfigError( "Could not find storage object '%s %s' for '%s'" % (plugin, name, obj.path_str)) else: lio_so = matching_lio_so[0] lio_lun = LUN(lio_tpg, lun, lio_so) apply_group_attrs(obj, lio_lun) elif obj.key[0] == 'tpgt': target = obj.parent fabric = target.parent has_enable = len(obj.search([("enable", ".*")])) != 0 if has_enable: enable = obj_attr(obj, "enable") lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=target.key[1], mode='lookup') tpgt = int(obj.key[1]) try: nexus_wwn = obj_attr(obj, "nexus_wwn") lio_tpg = TPG(lio_target, tpgt, nexus_wwn=nexus_wwn) except ConfigError: lio_tpg = TPG(lio_target, tpgt) if has_enable: lio_tpg.enable = enable apply_group_attrs(obj, lio_tpg) elif obj.key[0] == 'target': fabric = obj.parent wwn = obj.key[1] lio_fabric = FabricModule(fabric.key[1]) lio_target = Target(lio_fabric, wwn=wwn) apply_group_attrs(obj, lio_target) if not lio_target.has_feature("tpgts"): try: nexus_wwn = obj_attr(obj, "nexus_wwn") lio_tpg = TPG(lio_target, 1, nexus_wwn=nexus_wwn) except ConfigError: lio_tpg = TPG(lio_target, 1) if len(obj.search([("enable", ".*")])) != 0: lio_tpg.enable = True elif obj.key[0] == 'fabric': lio_fabric = FabricModule(obj.key[1]) apply_group_attrs(obj, lio_fabric) elif obj.key[0] == 'disk': plugin = obj.parent.key[1] name = obj.key[1] idx = max([0] + [b.index for b in root.backstores if b.plugin == plugin]) + 1 if plugin == 'fileio': dev = obj_attr(obj, "path") size = obj_attr(obj, "size") try: wwn = obj_attr(obj, "wwn") except ConfigError: wwn = None buffered = obj_attr(obj, "buffered") lio_bs = FileIOBackstore(idx) lio_so = lio_bs.storage_object(name, dev, size, wwn, buffered) apply_group_attrs(obj, lio_so) elif plugin == 'iblock': # TODO Add policy for iblock lio_bs = IBlockBackstore(idx) dev = obj_attr(obj, "path") wwn = obj_attr(obj, "wwn") lio_so = lio_bs.storage_object(name, dev, wwn) apply_group_attrs(obj, lio_so) elif plugin == 'pscsi': # TODO Add policy for pscsi lio_bs = PSCSIBackstore(idx) dev = obj_attr(obj, "path") lio_so = lio_bs.storage_object(name, dev) apply_group_attrs(obj, lio_so) elif plugin == 'rd_mcp': # TODO Add policy for rd_mcp lio_bs = RDMCPBackstore(idx) size = obj_attr(obj, "size") wwn = obj_attr(obj, "wwn") nullio = obj_attr(obj, "nullio") lio_so = lio_bs.storage_object(name, size, wwn, nullio) apply_group_attrs(obj, lio_so) else: raise ConfigError("Unknown backend '%s' for backstore '%s'" % (plugin, obj)) matching_lio_so = [ so for so in root.storage_objects if so.backstore.plugin == plugin and so.name == name ] if len(matching_lio_so) > 1: raise ConfigError("Detected unsupported configfs storage objects " "allocation schema for '%s'" % obj.path_str) elif len(matching_lio_so) == 0: raise ConfigError("Could not find backstore '%s'" % obj.path_str) else: lio_so = matching_lio_so[0]
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: self.shell.log.error("'.' not permitted in tag names.") return src = list(self.find_tagged(wwn_or_tag)) if not src: self.shell.log.error("wwn_or_tag %s not found." % wwn_or_tag) return 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, 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_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() if lun is None: luns = [lun.lun for lun in self.tpg.luns] for index in range(1048576): if index not in luns: lun = index break if lun is None: self.shell.log.error("Cannot find an available LUN.") return else: self.shell.log.info("Selected LUN %d." % lun) else: try: if lun.startswith('lun'): lun = lun[3:] lun = int(lun) except ValueError: self.shell.log.error("The LUN must be an integer value.") return else: if lun < 0: self.shell.log.error("The LUN cannot be negative.") return add_mapped_luns = \ self.ui_eval_param(add_mapped_luns, 'bool', self.shell.prefs['auto_add_mapped_luns']) try: storage_object = self.get_node(storage_object).rtsnode except ValueError: self.shell.log.error("Invalid storage object %s." % storage_object) return lun_object = LUN(self.tpg, lun, storage_object) 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: mapped_lun = lun existing_mluns = [mlun.mapped_lun for mlun in acl.mapped_luns] if mapped_lun in existing_mluns: tentative_mlun = 0 while mapped_lun == lun: if tentative_mlun not in existing_mluns: mapped_lun = tentative_mlun self.shell.log.warning( "Mapped LUN %d already " % lun + "exists in ACL %s, using %d instead." % (acl.node_wwn, mapped_lun)) else: tentative_mlun += 1 mlun = MappedLUN(acl, mapped_lun, lun, write_protect=False) self.shell.log.info("Created mapped LUN %d in node ACL %s" % (mapped_lun, acl.node_wwn)) self.parent.refresh() return self.new_node(ui_lun)
add_mapped_luns = \ self.ui_eval_param(add_mapped_luns, 'bool', self.shell.prefs['auto_add_mapped_luns']) try: node_acl = NodeACL(self.tpg, wwn, mode="create") except RTSLibError, msg: self.shell.log.error(str(msg)) return else: self.shell.log.info("Created Node ACL for %s" % node_acl.node_wwn) ui_node_acl = UINodeACL(node_acl, self) if add_mapped_luns: for lun in self.tpg.luns: MappedLUN(node_acl, lun.lun, lun.lun, write_protect=False) self.shell.log.info("Created mapped LUN %d." % lun.lun) self.refresh() return self.new_node(ui_node_acl) def ui_command_delete(self, wwn): ''' Deletes the Node ACL with the specified I{wwn}. SEE ALSO ======== B{create} ''' self.assert_root() node_acl = NodeACL(self.tpg, wwn, mode='lookup')