def _validate_add_count(self, sess: Session, swp_name: str, count: int): """ See validate_add_count. """ swp_api = SoftwareProfilesDbHandler() swp = swp_api.getSoftwareProfile(sess, swp_name) if swp.maxNodes <= 0: return current_count = len(swp.nodes) request_count = self._count_current_node_requests(sess, swp) if current_count + request_count + count > swp.maxNodes: raise OperationFailed( 'Request to add {} node(s) exceeds software profile' ' limit of {} nodes'.format(count, swp.maxNodes))
class SoftwareProfileDbApi(TagsDbApiMixin, TortugaDbApi): """ SoftwareProfile DB API class. """ tag_model = SoftwareProfileTag def __init__(self): super().__init__() self._softwareProfilesDbHandler = SoftwareProfilesDbHandler() self._globalParametersDbHandler = GlobalParametersDbHandler() self._adminsDbHandler = AdminsDbHandler() self._osDbHandler = OperatingSystemsDbHandler() def getSoftwareProfile( self, session, name: str, optionDict: Optional[Dict[str, bool]] = None) -> SoftwareProfile: """ Get softwareProfile from the db. Returns: softwareProfile Throws: SoftwareProfileNotFound DbError """ try: dbSoftwareProfile = \ self._softwareProfilesDbHandler.getSoftwareProfile( session, name) return self.__get_software_profile_obj(dbSoftwareProfile, options=optionDict) except TortugaException: raise except Exception as ex: self._logger.exception(str(ex)) raise def __get_software_profile_obj( self, software_profile: SoftwareProfileModel, options: Optional[dict] = None) -> SoftwareProfile: """ Deserialize SQLAlchemy object to TortugaObject """ self.loadRelations(software_profile, options) self.loadRelations(software_profile, dict(tags=True)) # if 'components' is specified, ensure 'kit' relationship is # also serialized if options and 'components' in options and options['components']: for component in software_profile.components: self.loadRelation(component, 'kit') software_profile_obj = SoftwareProfile.getFromDbDict( software_profile.__dict__) return software_profile_obj def getSoftwareProfileById( self, session: Session, softwareProfileId: int, optionDict: Optional[Dict[str, bool]] = None) -> SoftwareProfile: """ Get softwareProfile from the db. Returns: softwareProfile Throws: SoftwareProfileNotFound DbError """ try: dbSoftwareProfile = \ self._softwareProfilesDbHandler.getSoftwareProfileById( session, softwareProfileId) return self.__get_software_profile_obj(dbSoftwareProfile, options=optionDict) except TortugaException: raise except Exception as ex: self._logger.exception(str(ex)) raise def getSoftwareProfileList(self, session: Session, tags=None): """ Get list of all available softwareProfiles from the db. Returns: [softwareProfile] Throws: DbError """ try: dbSoftwareProfileList = \ self._softwareProfilesDbHandler.getSoftwareProfileList( session, tags=tags) softwareProfileList = TortugaObjectList() for dbSoftwareProfile in dbSoftwareProfileList: softwareProfileList.append( self.__get_software_profile_obj(dbSoftwareProfile, options={ 'components': True, 'partitions': True, 'hardwareprofiles': True, 'tags': True, })) return softwareProfileList except TortugaException: raise except Exception as ex: self._logger.exception(str(ex)) raise def addSoftwareProfile( self, session: Session, softwareProfile: SoftwareProfile) -> SoftwareProfileModel: """ Insert software profile into the db. Raises: SoftwareProfileAlreadyExists DbError """ try: try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile( session, softwareProfile.getName()) raise SoftwareProfileAlreadyExists( 'Software profile [%s] already exists' % (softwareProfile)) except SoftwareProfileNotFound as ex: pass dbSoftwareProfile = self.__populateSoftwareProfile( session, softwareProfile) session.query(func.max(SoftwareProfileModel.id)).one() softwareProfile.setId(dbSoftwareProfile.id) self._logger.info('Added software profile [%s]' % (dbSoftwareProfile.name)) return dbSoftwareProfile except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def deleteSoftwareProfile(self, session: Session, name: str) -> None: """ Delete softwareProfile from the db. Returns: None Throws: SoftwareProfileNotFound DbError """ try: # This role of this lookup is twofold. One, it validates # the existence of the software profile to be deleted and # second, gets the id for looking up any associated nodes dbSwProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, name) if dbSwProfile.nodes: # The software profile cannot be removed while # associated # nodes exist raise TortugaException('Unable to delete software profile with' ' associated nodes') # Proceed with software profile deletion self._logger.debug('Marking software profile [%s] for deletion' % (name)) session.delete(dbSwProfile) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def getAllEnabledComponentList(self, session: Session) -> TortugaObjectList: """ Get a list of all enabled components in the system Returns: [ components ] Throws: DbError """ self._logger.debug('Retrieving enabled component list') try: dbComponents = \ ComponentsDbHandler().getEnabledComponentList(session) return self.getTortugaObjectList(Component, dbComponents) except TortugaException: raise except Exception as ex: self._logger.exception(str(ex)) raise def getEnabledComponentList(self, session: Session, name: str) -> TortugaObjectList: """ Get a list of enabled components from the db. Returns: node Throws: DbError """ try: componentList = TortugaObjectList() for c in self._softwareProfilesDbHandler.getSoftwareProfile( session, name).components: self.loadRelation(c, 'kit') componentList.append(Component.getFromDbDict(c.__dict__)) return componentList except TortugaException: raise except Exception as ex: self._logger.exception(str(ex)) raise def getNodeList(self, session: Session, softwareProfile: SoftwareProfile) -> TortugaObjectList: """ Get list of nodes in 'softwareProfile' Returns: [node] Throws: DbError """ try: dbSoftwareProfile = \ self._softwareProfilesDbHandler.getSoftwareProfile( session, softwareProfile) nodeList = TortugaObjectList() for dbNode in dbSoftwareProfile.nodes: self.loadRelation(dbNode, 'hardwareprofile') nodeList.append(Node.getFromDbDict(dbNode.__dict__)) return nodeList except TortugaException: raise except Exception as ex: self._logger.exception(str(ex)) raise def getPartitionList( self, session: Session, softwareProfileName: str) \ -> TortugaObjectList: """ Get a list of software profile partitions from the db. Returns: [partition] Throws: SoftwareProfileNotFound DbError """ try: dbSoftwareProfile = \ self._softwareProfilesDbHandler.getSoftwareProfile( session, softwareProfileName) return self.getTortugaObjectList(Partition, dbSoftwareProfile.partitions) except TortugaException: raise except Exception as ex: self._logger.exception(str(ex)) raise def addPartition(self, session: Session, partitionName: str, softwareProfileName: str) -> None: """ Add software profile partition. Returns: partitionId Throws: PartitionAlreadyExists SoftwareProfileNotFound """ try: self._softwareProfilesDbHandler.addPartitionToSoftwareProfile( session, partitionName, softwareProfileName) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def deletePartition(self, session: Session, partitionName: str, softwareProfileName: str) -> None: """ Delete node from the db. Returns: None Throws: PartitionNotFound SoftwareProfileNotFound DbError """ try: self._softwareProfilesDbHandler.deletePartitionFromSoftwareProfile( session, partitionName, softwareProfileName) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def addUsableHardwareProfileToSoftwareProfile( self, session: Session, hardwareProfileName: str, softwareProfileName: str) -> None: """ Add hardwareProfile to softwareProfile Returns: SoftwareUsesHardwareId Throws: HardwareProfileNotFound SoftwareProfileNotFound SoftwareUsesHardwareAlreadyExists DbError """ try: self._softwareProfilesDbHandler.addUsableHardwareProfileToSoftwareProfile( session, hardwareProfileName, softwareProfileName) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def deleteUsableHardwareProfileFromSoftwareProfile( self, session: Session, hardwareProfileName: str, softwareProfileName: str) -> None: """ Delete hardwareProfile from softwareProfile Returns: None Throws: HardwareProfileNotFound SoftwareProfileNotFound SoftwareUsesHardwareNotFound DbError """ try: self._softwareProfilesDbHandler.\ deleteUsableHardwareProfileFromSoftwareProfile( session, hardwareProfileName, softwareProfileName) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def addAdmin(self, session: Session, softwareProfileName: str, adminUsername: str) -> None: """ Add an admin to this software profile Raises: AdminAlreadyExists """ try: dbAdmin = self._adminsDbHandler.getAdmin(session, adminUsername) dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, softwareProfileName) if dbAdmin not in dbSoftwareProfile.admins: dbSoftwareProfile.admins.append(dbAdmin) else: raise AdminAlreadyExists( 'Admin [%s] already associated with %s' % (adminUsername, softwareProfileName)) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def deleteAdmin(self, session: Session, softwareProfileName: str, adminUsername: str) -> None: """ Delete an admin from a software profile """ try: dbAdmin = self._adminsDbHandler.getAdmin(session, adminUsername) dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, softwareProfileName) if dbAdmin in dbSoftwareProfile.admins: dbSoftwareProfile.admins.remove(dbAdmin) else: raise AdminNotFound( 'Admin [%s] not associated with software profile' ' [%s]' % (adminUsername, softwareProfileName)) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def updateSoftwareProfile(self, session: Session, softwareProfileObject: SoftwareProfile) -> None: """ Update a software profile """ try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfileById( session, softwareProfileObject.getId()) self.__populateSoftwareProfile(session, softwareProfileObject, dbSoftwareProfile) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self._logger.exception(str(ex)) raise def __populateSoftwareProfile( self, session: Session, softwareProfile: SoftwareProfile, dbSoftwareProfile: Optional[SoftwareProfileModel] = None) \ -> SoftwareProfileModel: """ Helper function for creating/updating dbSoftwareProfile Object """ # Validate object if softwareProfile.getName() is None: raise UpdateSoftwareProfileFailed('Software profile name required') if softwareProfile.getType() is None: raise UpdateSoftwareProfileFailed( 'Software profile must have valid type') osInfo = softwareProfile.getOsInfo() if osInfo is None or osInfo.getName() is None: raise UpdateSoftwareProfileFailed( 'Software profile must have valid operating system') if dbSoftwareProfile is None: dbSoftwareProfile = SoftwareProfileModel() dbOs = self._osDbHandler.addOsIfNotFound(session, osInfo) dbSoftwareProfile.name = softwareProfile.getName() dbSoftwareProfile.description = softwareProfile.getDescription() dbSoftwareProfile.kernel = softwareProfile.getKernel() dbSoftwareProfile.kernelParams = softwareProfile.getKernelParams() dbSoftwareProfile.initrd = softwareProfile.getInitrd() dbSoftwareProfile.os = dbOs dbSoftwareProfile.type = softwareProfile.getType() dbSoftwareProfile.minNodes = softwareProfile.getMinNodes() dbSoftwareProfile.maxNodes = softwareProfile.getMaxNodes() dbSoftwareProfile.lockedState = softwareProfile.getLockedState() dbSoftwareProfile.dataRoot = softwareProfile.getDataRoot() dbSoftwareProfile.dataRsync = softwareProfile.getDataRsync() # Add partitions partitions: Dict[Tuple[str, str], Partition] = {} for partition in softwareProfile.getPartitions(): # This is a new partition dbPartition = PartitionModel() dbPartition.name = partition.getName() dbPartition.device = partition.getDevice() dbPartition.mountPoint = partition.getMountPoint() dbPartition.fsType = partition.getFsType() dbPartition.size = partition.getSize() dbPartition.options = partition.getOptions() dbPartition.preserve = partition.getPreserve() dbPartition.bootLoader = partition.getBootLoader() dbPartition.diskSize = partition.getDiskSize() dbPartition.directAttachment = partition.getDirectAttachment() dbPartition.indirectAttachment = partition.\ getIndirectAttachment() dbPartition.sanVolume = partition.getSanVolume() if not dbPartition.name: raise InvalidPartitionScheme( 'Invalid partition in software profile:' ' missing or empty name') if not dbPartition.device: raise InvalidPartitionScheme( 'Invalid partition in software profile:' ' missing or empty device') if not dbPartition.fsType: raise InvalidPartitionScheme( 'Invalid partition [%s/%s] in software profile:' ' missing or empty fsType' % (dbPartition.name, dbPartition.device)) if dbPartition.size is None: raise InvalidPartitionScheme( 'Invalid partition [%s/%s] in software profile:' ' missing size' % (dbPartition.name, dbPartition.device)) if partitions.get( (dbPartition.name, dbPartition.device)) is not None: # Duplicate partition ...error raise UpdateSoftwareProfileFailed( 'Duplicate partition [%s/%s] found' % (dbPartition.name, dbPartition.device)) try: int(dbPartition.size) except ValueError: raise InvalidPartitionScheme( 'Invalid partition [%s/%s] in software profile:' ' non-integer size' % (dbPartition.name, dbPartition.device)) try: if dbPartition.diskSize is not None: int(dbPartition.diskSize) except ValueError: raise InvalidPartitionScheme( 'Invalid partition [%s/%s] in software profile:' ' non-integer disk size' % (dbPartition.name, dbPartition.device)) bGrow = partition.getGrow() if bGrow is not None: dbPartition.grow = bGrow maxSize = partition.getMaxSize() if maxSize is not None: dbPartition.maxSize = maxSize partitions[(dbPartition.name, dbPartition.device)] = \ dbPartition # Delete out the old ones dbSoftwareProfile.partitions = [] session.flush() dbSoftwareProfile.partitions = list(partitions.values()) self._set_tags(dbSoftwareProfile, softwareProfile.getTags()) session.add(dbSoftwareProfile) session.flush() return dbSoftwareProfile def copySoftwareProfile(self, session: Session, srcSoftwareProfileName: str, dstSoftwareProfileName: str) -> None: src_swprofile = self._softwareProfilesDbHandler.getSoftwareProfile( session, srcSoftwareProfileName) dst_swprofile = SoftwareProfileModel( name=dstSoftwareProfileName, description='Copy of %s' % (src_swprofile.description), kernel=src_swprofile.kernel, kernelParams=src_swprofile.kernelParams, initrd=src_swprofile.initrd, type=src_swprofile.type, minNodes=src_swprofile.minNodes, maxNodes=src_swprofile.maxNodes, lockedState=src_swprofile.lockedState, dataRoot=src_swprofile.dataRoot, dataRsync=src_swprofile.dataRsync) # os dst_swprofile.os = src_swprofile.os # admins for admin in src_swprofile.admins: dst_swprofile.admins.append(admin) # partitions for partition in src_swprofile.partitions: dst_swprofile.partitions.append(partition) # components for component in src_swprofile.components: dst_swprofile.components.append(component) # tags for tag in src_swprofile.tags: dst_swprofile.tags.append(tag) # kitsources for kitsource in src_swprofile.kitsources: dst_swprofile.kitsources.append(kitsource) # hardwareprofiles for hwprofile in src_swprofile.hardwareprofiles: dst_swprofile.hardwareprofiles.append(hwprofile) session.add(dst_swprofile) session.commit() def getUsableNodes(self, session: Session, name: str) \ -> TortugaObjectList: """ Return list of nodes with same software/hardware profile mapping as the specified software profile. """ try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, name) nodes = [ dbNode for dbHardwareProfile in dbSoftwareProfile.hardwareprofiles for dbNode in dbHardwareProfile.nodes ] return self.getTortugaObjectList(Node, nodes) except TortugaException: raise except Exception as ex: self._logger.exception(str(ex)) raise
class NodesDbHandler(TortugaDbObjectHandler): """ This class handles nodes table. """ def __init__(self): super().__init__() self._softwareProfilesDbHandler = SoftwareProfilesDbHandler() def getNode(self, session: Session, name: str) -> Node: """ Return node. Raises: NodeNotFound """ try: if '.' in name: # Attempt exact match on fully-qualfied name return session.query(Node).filter( func.lower(Node.name) == name.lower()).one() # 'name' is short host name; attempt to match on either short # host name or any host starting with same host name return session.query(Node).filter( or_( func.lower(Node.name) == name.lower(), func.lower(Node.name).like(name.lower() + '.%'))).one() except NoResultFound: raise NodeNotFound("Node [%s] not found" % (name)) def get_installer_node(self, session: Session) -> Node: """ Return installer node derived from searching for all software profiles with type 'installer'. Raises: NodeNotFound """ installer_swprofiles = \ SoftwareProfilesDbHandler().getSoftwareProfileList( session, profile_type='installer' ) if not installer_swprofiles: raise NodeNotFound('No installer software profiles found') installer_nodes = installer_swprofiles[0].nodes if not installer_nodes: raise NodeNotFound( 'No installer node found in software profile {}'.format( installer_swprofiles[0].name)) return installer_nodes[0] def getNodesByTags(self, session: Session, tags: Optional[Tags] = None): """ Gets nodes by tag(s). Tags is a dictionary of key/value pairs to match against. :param session: a SQLAlchemy database session :param tags: a dictionary of key/value tag pairs :return: a list of nodes """ searchspec = [] # iterate over list of tag tuples making SQLAlchemy search # specification if tags: for name, value in tags.items(): if value: # # Match both name and value # searchspec.append( and_(Node.tags.any(name=name), Node.tags.any(value=value))) else: # # Match name only # searchspec.append(Node.tags.any(name=name)) return session.query(Node).filter(or_(*searchspec)).all() def getNodesByAddHostSession(self, session: Session, ahSession: str) \ -> List[Node]: """ Get nodes by add host session Returns a list of nodes """ self._logger.debug('getNodesByAddHostSession(): ahSession [%s]' % (ahSession)) return session.query(Node).filter( Node.addHostSession == ahSession).order_by(Node.name).all() def getNodesByNameFilter( self, session: Session, filter_spec: Union[str, list], include_installer: Optional[bool] = True) -> List[Node]: """ Filter follows SQL "LIKE" semantics (ie. "something%") Exclude installer node from node list by setting 'include_installer' to False. Returns a list of Node """ filter_spec_list = [filter_spec] \ if not isinstance(filter_spec, list) else filter_spec node_filter = [] for filter_spec_item in filter_spec_list: if '.' not in filter_spec_item: # Match exactly (ie. "hostname-01") node_filter.append(Node.name.like(filter_spec_item)) # Match host name only (ie. "hostname-01.%") node_filter.append(Node.name.like(filter_spec_item + '.%')) continue # Match fully-qualified node names exactly # (ie. "hostname-01.domain") node_filter.append(Node.name.like(filter_spec_item)) if not include_installer: installer_fqdn = getfqdn() return session.query(Node).filter( and_(Node.name != installer_fqdn, or_(*node_filter))).all() return session.query(Node).filter(or_(*node_filter)).all() def getNodeById(self, session: Session, _id: int) -> Node: """ Return node. Raises: NodeNotFound """ self._logger.debug('Retrieving node by ID [%s]' % (_id)) dbNode = session.query(Node).get(_id) if not dbNode: raise NodeNotFound('Node ID [%s] not found.' % (_id)) return dbNode def getNodeByIp(self, session: Session, ip: str) -> Node: """ Raises: NodeNotFound """ self._logger.debug('Retrieving node by IP [%s]' % (ip)) try: return session.query(Node).join(Nic).filter(Nic.ip == ip).one() except NoResultFound: raise NodeNotFound('Node with IP address [%s] not found.' % (ip)) def getNodeList(self, session: Session, softwareProfile: Optional[str] = None, tags: Optional[Tags] = None) -> List[Node]: """ Get sorted list of nodes from the db. Raises: SoftwareProfileNotFound """ self._logger.debug('getNodeList()') if softwareProfile: dbSoftwareProfile = \ self._softwareProfilesDbHandler.getSoftwareProfile( session, softwareProfile) return dbSoftwareProfile.nodes searchspec = [] if tags: for name, value in dict(tags).items(): if value: # # Match both name and value # searchspec.append( and_(Node.tags.any(name=name), Node.tags.any(value=value))) else: # # Match name only # searchspec.append(Node.tags.any(name=name)) return session.query(Node).filter(or_(*searchspec)).order_by( Node.name).all() def getNodeListByNodeStateAndSoftwareProfileName( self, session: Session, nodeState: str, softwareProfileName: str) -> List[Node]: """ Get list of nodes from the db. """ self._logger.debug('Retrieving nodes with state [%s] from software' ' profile [%s]' % (nodeState, softwareProfileName)) return session.query(Node).join(SoftwareProfile).filter( and_(SoftwareProfile.name == softwareProfileName, Node.state == nodeState)).all() def getNodesByNodeState(self, session: Session, state: str) -> List[Node]: return session.query(Node).filter(Node.state == state).all() def getNodesByMac(self, session: Session, usedMacList: List[str]) \ -> List[Node]: if not usedMacList: return [] return session.query(Node).join(Nic).filter( Nic.mac.in_(usedMacList)).all() def build_node_filterspec(self, nodespec: str) -> List[str]: filter_spec = [] for nodespec_token in nodespec.split(','): # Convert shell-style wildcards into SQL wildcards if '*' in nodespec_token or '?' in nodespec_token: filter_spec.append( nodespec_token.replace('*', '%').replace('?', '_')) continue if '.' not in nodespec_token: filter_spec.append(nodespec_token) filter_spec.append(nodespec_token + '.%') continue # Add nodespec "AS IS" filter_spec.append(nodespec_token) return filter_spec def expand_nodespec(self, session: Session, nodespec: str, include_installer: Optional[bool] = True) \ -> List[Node]: """ Expand command-line nodespec (ie. "compute*") to list of nodes """ return self.getNodesByNameFilter(session, self.build_node_filterspec(nodespec), include_installer=include_installer)
class ComponentDbApi(TortugaDbApi): """ Component DB API class. """ def __init__(self): TortugaDbApi.__init__(self) self._softwareProfilesDbHandler = SoftwareProfilesDbHandler() self._componentsDbHandler = ComponentsDbHandler() def getComponent(self, session: Session, name: str, version: str, osInfo: OsInfo, optionDict: Optional[Union[dict, None]] = None) \ -> Component: """ Get component from the db. Returns: component Throws: ComponentNotFound DbError """ try: dbComponent = self._componentsDbHandler.getComponentByOsInfo( session, name, version, osInfo) self.loadRelations(dbComponent, optionDict) return Component.getFromDbDict(dbComponent.__dict__) except TortugaException: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise def getBestMatchComponent(self, session: Session, name, version, osInfo, kitId): """ Get component from the db. Returns: component Throws: ComponentNotFound DbError """ try: dbComponent = self._componentsDbHandler.getBestMatchComponent( session, name, version, osInfo, kitId) self.loadRelations( dbComponent, { 'os': True, 'family': True, 'kit': True, 'os_components': True, 'osfamily_components': True, }) return Component.getFromDbDict(dbComponent.__dict__) except TortugaException: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise def addComponentToSoftwareProfile(self, session: Session, componentId, softwareProfileId): """ Add component to softwareProfile. Returns: None Throws: SoftwareProfileNotFound ComponentNotFound SoftwareProfileComponentAlreadyExists DbError """ try: self._softwareProfilesDbHandler.addComponentToSoftwareProfile( session, componentId, softwareProfileId) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise def deleteComponentFromSoftwareProfile(self, session: Session, componentId, softwareProfileId): """ Delete component to software profile. Returns: None Throws: SoftwareProfileNotFound ComponentNotFound SoftwareProfileComponentNotFound DbError """ try: self._softwareProfilesDbHandler.\ deleteComponentFromSoftwareProfile( session, componentId, softwareProfileId) session.commit() except TortugaException: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise def getComponentList(self, session: Session, softwareProfile=None): try: if softwareProfile: return self._softwareProfilesDbHandler.getSoftwareProfile( session, softwareProfile).components # List all components self.getLogger().debug('Retrieving component list') dbComps = self._componentsDbHandler.getComponentList(session) return self.getTortugaObjectList(Component, dbComps) except Exception as ex: self.getLogger().exception('%s' % ex) raise
def validate_addnodes_request(addNodesRequest): """ Raises: HardwareProfileNotFound SoftwareProfileNotFound ProfileMappingNotAllowed InvalidArgument """ if 'hardwareProfile' not in addNodesRequest and \ 'softwareProfile' not in addNodesRequest: raise InvalidArgument( 'Hardware and/or software profile must be specified') hpapi = HardwareProfilesDbHandler() spapi = SoftwareProfilesDbHandler() hardwareProfileName = addNodesRequest['hardwareProfile'] \ if 'hardwareProfile' in addNodesRequest else None nodeDetails = addNodesRequest.get('nodeDetails', []) softwareProfileName = addNodesRequest['softwareProfile'] \ if 'softwareProfile' in addNodesRequest else None nodeCount = int(addNodesRequest.get('count', 0)) rackNumber = addNodesRequest.get('rack') session = DbManager().openSession() try: # Look up hardware profile hp = hpapi.getHardwareProfile(session, hardwareProfileName) \ if hardwareProfileName else None # Look up software profile sp = spapi.getSoftwareProfile( session, softwareProfileName) if softwareProfileName else None if sp and not sp.isIdle and 'isIdle' in addNodesRequest and \ addNodesRequest['isIdle']: raise InvalidArgument( 'Software profile [%s] is not idle software profile' % (softwareProfileName)) # Make sure that if a software profile is given that it is allowed # to be used with the given hardware profile if sp is not None and hp is not None: checkProfilesMapped(sp, hp) elif sp is not None and hp is None: if not sp.hardwareprofiles: raise InvalidArgument( 'Software profile [{0}] is not mapped to any hardware' ' profiles'.format(softwareProfileName)) if len(sp.hardwareprofiles) > 1: raise InvalidArgument( 'Ambiguous request: multiple hardware profiles are' ' mapped to software profile [{0}]'.format( softwareProfileName)) hp = sp.hardwareprofiles[0] elif hp is not None and sp is None and not addNodesRequest['isIdle']: if not hp.mappedsoftwareprofiles: raise InvalidArgument( 'Hardware profile [{0}] is not mapped to any software' ' profiles'.format(hardwareProfileName)) if len(hp.mappedsoftwareprofiles) > 1: raise InvalidArgument( 'Ambiguous request: multiple software profiles are' ' mapped to hardware profile [{0}]'.format( hardwareProfileName)) sp = hp.mappedsoftwareprofiles[0] # Ensure user does not make a request for DHCP discovery mode. # Currently, this is determined by the presence of the item # 'nodeDetails' in addNodesRequest. Ultimately, this should be # shared code between here and the default resource adapter. if hp.resourceadapter and \ hp.resourceadapter.name == 'default' and not nodeDetails: raise InvalidArgument( 'DHCP discovery is not available through WS API.') if sp and 'softwareProfile' not in addNodesRequest: addNodesRequest['softwareProfile'] = sp.name if 'hardwareProfile' not in addNodesRequest: addNodesRequest['hardwareProfile'] = hp.name # Validate 'nodeDetails' if nodeDetails: # Reconcile nodeDetails that contain hostnames with hwp name # format bWildcardNameFormat = hp.nameFormat == '*' hostname = nodeDetails[0]['name'] \ if 'name' in nodeDetails[0] else None if hostname and not bWildcardNameFormat: # Host name specified, but hardware profile does not # allow setting the host name raise InvalidArgument('Hardware profile does not allow setting' ' host names of imported nodes') elif not hostname and bWildcardNameFormat: # Host name not specified but hardware profile expects it raise InvalidArgument('Hardware profile requires imported node' ' name to be set') if nodeCount > 0 and nodeCount != len(nodeDetails): raise InvalidArgument('Node count must be equal to number' ' of MAC/IP/node names provided') if hostname: # Ensure host does not already exist existing_node = session.query(Nodes).filter( Nodes.name == hostname).first() if existing_node: raise NodeAlreadyExists('Node [%s] already exists' % (hostname)) # Prohibit running add-host against installer validate_hwprofile(hp) # If the given hardwareProfile's nameFormat contains "#R', # then the rackNumber is required. nameFormat = hp.nameFormat if nameFormat.find('#R') != -1 and rackNumber is None: raise InvalidArgument( 'Missing "rackNumber" for name format [%s] of' ' hardware profile [%s]' % (nameFormat, hp)) adapter = resourceAdapterFactory.getApi(hp.resourceadapter.name) adapter.validate_start_arguments(addNodesRequest, hp, dbSoftwareProfile=sp) finally: DbManager().closeSession()
class SoftwareProfileDbApi(TortugaDbApi): """ SoftwareProfile DB API class. """ def __init__(self): TortugaDbApi.__init__(self) self._softwareProfilesDbHandler = SoftwareProfilesDbHandler() self._nodesDbHandler = NodesDbHandler() self._globalParametersDbHandler = GlobalParametersDbHandler() self._adminsDbHandler = AdminsDbHandler() self._osDbHandler = OperatingSystemsDbHandler() def getSoftwareProfile(self, name, optionDict=None): """ Get softwareProfile from the db. Returns: softwareProfile Throws: SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, name) self.loadRelations(dbSoftwareProfile, optionDict or {}) self.loadRelations(dbSoftwareProfile, dict(tags=True)) return SoftwareProfile.getFromDbDict(dbSoftwareProfile.__dict__) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getSoftwareProfileById(self, softwareProfileId, optionDict=None): """ Get softwareProfile from the db. Returns: softwareProfile Throws: SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfileById(session, softwareProfileId) self.loadRelations(dbSoftwareProfile, optionDict or {}) return SoftwareProfile.getFromDbDict(dbSoftwareProfile.__dict__) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getSoftwareProfileList(self, tags=None): """ Get list of all available softwareProfiles from the db. Returns: [softwareProfile] Throws: DbError """ session = DbManager().openSession() try: dbSoftwareProfileList = self._softwareProfilesDbHandler.\ getSoftwareProfileList(session, tags=tags) softwareProfileList = TortugaObjectList() for dbSoftwareProfile in dbSoftwareProfileList: self.loadRelations( dbSoftwareProfile, { 'components': True, 'partitions': True, 'hardwareprofiles': True, 'tags': True, }) softwareProfileList.append( SoftwareProfile.getFromDbDict(dbSoftwareProfile.__dict__)) return softwareProfileList except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getIdleSoftwareProfileList(self): """ Get list of all available idle softwareProfiles from the db. Returns: [idle softwareProfile] Throws: DbError """ session = DbManager().openSession() try: dbSoftwareProfileList = self._softwareProfilesDbHandler.\ getIdleSoftwareProfileList(session) return self.getTortugaObjectList(SoftwareProfile, dbSoftwareProfileList) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def setIdleState(self, softwareProfileName, state): """ Set idle state of a software profile Returns: -none- Throws: DbError """ session = DbManager().openSession() try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, softwareProfileName) self.getLogger().debug( 'Setting idle state [%s] on software profile [%s]' % (state, dbSoftwareProfile.name)) dbSoftwareProfile.isIdle = state session.commit() except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def addSoftwareProfile(self, softwareProfile, session=None): """ Insert software profile into the db. Raises: SoftwareProfileAlreadyExists DbError """ # Keep local 'session' instance. If 'session' is None, # ensure transaction is committed before returning to the # caller, otherwise the caller is responsible. On exceptions, # the rollback is performed regardless. _session = session if _session is None: _session = DbManager().openSession() try: try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile( _session, softwareProfile.getName()) raise SoftwareProfileAlreadyExists( 'Software profile [%s] already exists' % (softwareProfile)) except SoftwareProfileNotFound as ex: pass dbSoftwareProfile = self.__populateSoftwareProfile( _session, softwareProfile) _session.query(func.max(SoftwareProfiles.id)).one() softwareProfile.setId(dbSoftwareProfile.id) if session is None: _session.commit() self.getLogger().info('Added software profile [%s]' % (dbSoftwareProfile.name)) return dbSoftwareProfile except TortugaException as ex: _session.rollback() raise except Exception as ex: _session.rollback() self.getLogger().exception('%s' % ex) raise finally: if session is None: DbManager().closeSession() def deleteSoftwareProfile(self, name): """ Delete softwareProfile from the db. Returns: None Throws: SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: # This role of this lookup is twofold. One, it validates # the existence of the software profile to be deleted and # second, gets the id for looking up any associated nodes dbSwProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, name) if dbSwProfile.nodes: # The software profile cannot be removed while # associated # nodes exist raise TortugaException('Unable to delete software profile with' ' associated nodes') if dbSwProfile.isIdle: # Ensure this software profile is not associated with # a hardware profile if dbSwProfile.hwprofileswithidle: # This software profile is associated with one # or more hardware profiles raise TortugaException( 'Unable to delete software profile.' ' This software profile is associated' ' with one or more hardware profiles: [%s]' % (' '.join([ hwprofile.name for hwprofile in dbSwProfile.hwprofileswithidle ]))) # Proceed with software profile deletion self.getLogger().debug( 'Marking software profile [%s] for deletion' % (name)) session.delete(dbSwProfile) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getNode(self, name): """ Get node from the db. Returns: node Throws: NodeNotFound DbError """ session = DbManager().openSession() try: return self._nodesDbHandler.getNode(session, name) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getAllEnabledComponentList(self): """ Get a list of all enabled components in the system Returns: [ components ] Throws: DbError """ session = DbManager().openSession() try: handler = ComponentsDbHandler() dbComponents = handler.getEnabledComponentList(session) return self.getTortugaObjectList(Component, dbComponents) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getEnabledComponentList(self, name): """ Get a list of enabled components from the db. Returns: node Throws: DbError """ session = DbManager().openSession() try: componentList = TortugaObjectList() for c in self._softwareProfilesDbHandler.getSoftwareProfile( session, name).components: self.loadRelation(c, 'kit') componentList.append(Component.getFromDbDict(c.__dict__)) return componentList except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getNodeList(self, softwareProfile): """ Get list of nodes in 'softwareProfile' Returns: [node] Throws: DbError """ session = DbManager().openSession() try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, softwareProfile) nodeList = TortugaObjectList() for dbNode in dbSoftwareProfile.nodes: self.loadRelation(dbNode, 'hardwareprofile') nodeList.append(Node.getFromDbDict(dbNode.__dict__)) return nodeList except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getNodeListByNodeStateAndSoftwareProfileName(self, nodeState, softwareProfileName): """ Return a list of Node objects based on the specified node state and software profile """ session = DbManager().openSession() try: dbNodes = self._nodesDbHandler.\ getNodeListByNodeStateAndSoftwareProfileName( session, nodeState, softwareProfileName) return self.getTortugaObjectList(Node, dbNodes) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def deleteNode(self, name): """ Delete node from the db. Returns: None Throws: NodeNotFound DbError """ session = DbManager().openSession() try: self._nodesDbHandler.deleteNode(session, name) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getPackageList(self, softwareProfileName): """ Get a list of packages from the db. Returns: [package] Throws: SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: dbPackages = self._softwareProfilesDbHandler.\ getSoftwareProfile( session, softwareProfileName).packages return self.getTortugaObjectList(Package, dbPackages) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def getPartitionList(self, softwareProfileName): """ Get a list of software profile partitions from the db. Returns: [partition] Throws: SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, softwareProfileName) return self.getTortugaObjectList(Partition, dbSoftwareProfile.partitions) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def addPackage(self, packageName, softwareProfileName): """ Add software profile package. Returns: packageId Throws: PackageAlreadyExists SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: self._softwareProfilesDbHandler.addPackageToSoftwareProfile( session, packageName, softwareProfileName) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def deletePackage(self, packageName, softwareProfileName): """ Delete node from the db. Returns: None Throws: PackageNotFound SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: self._softwareProfilesDbHandler.\ deletePackageFromSoftwareProfile( session, packageName, softwareProfileName) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def addPartition(self, partitionName, softwareProfileName): """ Add software profile partition. Returns: partitionId Throws: PartitionAlreadyExists SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: self._softwareProfilesDbHandler.\ addPartitionToSoftwareProfile( session, partitionName, softwareProfileName) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def deletePartition(self, partitionName, softwareProfileName): """ Delete node from the db. Returns: None Throws: PartitionNotFound SoftwareProfileNotFound DbError """ session = DbManager().openSession() try: self._softwareProfilesDbHandler.\ deletePartitionFromSoftwareProfile( session, partitionName, softwareProfileName) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def addUsableHardwareProfileToSoftwareProfile(self, hardwareProfileName, softwareProfileName): """ Add hardwareProfile to softwareProfile Returns: SoftwareUsesHardwareId Throws: HardwareProfileNotFound SoftwareProfileNotFound SoftwareUsesHardwareAlreadyExists DbError """ session = DbManager().openSession() try: swUsesHwId = self._softwareProfilesDbHandler.\ addUsableHardwareProfileToSoftwareProfile( session, hardwareProfileName, softwareProfileName) session.commit() return swUsesHwId except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def deleteUsableHardwareProfileFromSoftwareProfile(self, hardwareProfileName, softwareProfileName): """ Delete hardwareProfile from softwareProfile Returns: None Throws: HardwareProfileNotFound SoftwareProfileNotFound SoftwareUsesHardwareNotFound DbError """ session = DbManager().openSession() try: self._softwareProfilesDbHandler.\ deleteUsableHardwareProfileFromSoftwareProfile( session, hardwareProfileName, softwareProfileName) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def addAdmin(self, softwareProfileName, adminUsername): """ Add an admin to this software profile """ session = DbManager().openSession() try: dbAdmin = self._adminsDbHandler.getAdmin(session, adminUsername) dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, softwareProfileName) if dbAdmin not in dbSoftwareProfile.admins: dbSoftwareProfile.admins.append(dbAdmin) else: raise AdminAlreadyExists( 'Admin [%s] already associated with %s' % (adminUsername, softwareProfileName)) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def deleteAdmin(self, softwareProfileName, adminUsername): """ Delete an admin from a software profile """ session = DbManager().openSession() try: dbAdmin = self._adminsDbHandler.getAdmin(session, adminUsername) dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, softwareProfileName) if dbAdmin in dbSoftwareProfile.admins: dbSoftwareProfile.admins.remove(dbAdmin) else: raise AdminNotFound( 'Admin [%s] not associated with software profile' ' [%s]' % (adminUsername, softwareProfileName)) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def updateSoftwareProfile(self, softwareProfileObject): """ Update a software profile """ session = DbManager().openSession() try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfileById( session, softwareProfileObject.getId()) self.__populateSoftwareProfile(session, softwareProfileObject, dbSoftwareProfile) session.commit() except TortugaException as ex: session.rollback() raise except Exception as ex: session.rollback() self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession() def __populateSoftwareProfile(self, session, softwareProfile, dbSoftwareProfile=None): """ Helper function for creating/updating dbSoftwareProfile Object """ # Validate object if softwareProfile.getName() is None: raise UpdateSoftwareProfileFailed('Software profile name required') if softwareProfile.getType() is None: raise UpdateSoftwareProfileFailed( 'Software profile must have valid type') osInfo = softwareProfile.getOsInfo() if osInfo is None or osInfo.getName() is None: raise UpdateSoftwareProfileFailed( 'Software profile must have valid operating system') if dbSoftwareProfile is None: dbSoftwareProfile = SoftwareProfiles() dbOs = self._osDbHandler.addOsIfNotFound(session, osInfo) dbSoftwareProfile.name = softwareProfile.getName() dbSoftwareProfile.description = softwareProfile.getDescription() dbSoftwareProfile.kernel = softwareProfile.getKernel() dbSoftwareProfile.kernelParams = softwareProfile.getKernelParams() dbSoftwareProfile.initrd = softwareProfile.getInitrd() dbSoftwareProfile.os = dbOs dbSoftwareProfile.type = softwareProfile.getType() dbSoftwareProfile.minNodes = softwareProfile.getMinNodes() dbSoftwareProfile.isIdle = softwareProfile.getIsIdle() # Add packages packages = {} for package in softwareProfile.getPackages(): # This is a new package dbPackage = Packages() dbPackage.name = package.getName() if packages.get(dbPackage.name) is not None: # Duplicate package ...error raise UpdateSoftwareProfileFailed( 'Duplicate package [%s] found' % (dbPackage.name)) packages[dbPackage.name] = dbPackage # Add partitions partitions = {} for partition in softwareProfile.getPartitions(): # This is a new partition dbPartition = Partitions() dbPartition.name = partition.getName() dbPartition.device = partition.getDevice() dbPartition.mountPoint = partition.getMountPoint() dbPartition.fsType = partition.getFsType() dbPartition.size = partition.getSize() dbPartition.options = partition.getOptions() dbPartition.preserve = partition.getPreserve() dbPartition.bootLoader = partition.getBootLoader() dbPartition.diskSize = partition.getDiskSize() dbPartition.directAttachment = partition.getDirectAttachment() dbPartition.indirectAttachment = partition.\ getIndirectAttachment() dbPartition.sanVolume = partition.getSanVolume() if not dbPartition.name: raise InvalidPartitionScheme( 'Invalid partition in software profile:' ' missing or empty name') if not dbPartition.device: raise InvalidPartitionScheme( 'Invalid partition in software profile:' ' missing or empty device') if not dbPartition.fsType: raise InvalidPartitionScheme( 'Invalid partition [%s/%s] in software profile:' ' missing or empty fsType' % (dbPartition.name, dbPartition.device)) if dbPartition.size is None: raise InvalidPartitionScheme( 'Invalid partition [%s/%s] in software profile:' ' missing size' % (dbPartition.name, dbPartition.device)) if partitions.get( (dbPartition.name, dbPartition.device)) is not None: # Duplicate partition ...error raise UpdateSoftwareProfileFailed( 'Duplicate partition [%s/%s] found' % (dbPartition.name, dbPartition.device)) try: int(dbPartition.size) except ValueError: raise InvalidPartitionScheme( 'Invalid partition [%s/%s] in software profile:' ' non-integer size' % (dbPartition.name, dbPartition.device)) try: if dbPartition.diskSize is not None: int(dbPartition.diskSize) except ValueError: raise InvalidPartitionScheme( 'Invalid partition [%s/%s] in software profile:' ' non-integer disk size' % (dbPartition.name, dbPartition.device)) bGrow = partition.getGrow() if bGrow is not None: dbPartition.grow = bGrow maxSize = partition.getMaxSize() if maxSize is not None: dbPartition.maxSize = maxSize partitions[(dbPartition.name, dbPartition.device)] = \ dbPartition # Delete out the old ones dbSoftwareProfile.partitions = [] dbSoftwareProfile.packages = [] session.flush() dbSoftwareProfile.partitions = list(partitions.values()) dbSoftwareProfile.packages = list(packages.values()) session.add(dbSoftwareProfile) session.flush() return dbSoftwareProfile def copySoftwareProfile(self, srcSoftwareProfileName, dstSoftwareProfileName): session = DbManager().openSession() srcSoftwareProfile = self.getSoftwareProfile(srcSoftwareProfileName, { 'partitions': True, 'packages': True, 'components': True, }) dstSoftwareProfile = self.getSoftwareProfile(srcSoftwareProfileName) dstSoftwareProfile.setName(dstSoftwareProfileName) newDescription = 'Copy of %s' % (dstSoftwareProfile.getDescription()) dstSoftwareProfile.setDescription(newDescription) # partitions dstSoftwareProfile.setPartitions(srcSoftwareProfile.getPartitions()) # packages dstSoftwareProfile.setPackages(srcSoftwareProfile.getPackages()) # Finally add the software profile dstSoftwareProfile = self.addSoftwareProfile(dstSoftwareProfile, session) # Enable components separately srcCompList = self.getEnabledComponentList(srcSoftwareProfileName) for srcComp in srcCompList: if srcComp.getKit().getIsOs() or srcComp.getName() == 'core': self._softwareProfilesDbHandler.\ addComponentToSoftwareProfileEx( session, srcComp.getId(), dstSoftwareProfile) session.commit() def getUsableNodes(self, name): """ Return list of nodes with same software/hardware profile mapping as the specified software profile. """ session = DbManager().openSession() try: dbSoftwareProfile = self._softwareProfilesDbHandler.\ getSoftwareProfile(session, name) nodes = [ dbNode for dbHardwareProfile in dbSoftwareProfile.hardwareprofiles for dbNode in dbHardwareProfile.nodes ] return self.getTortugaObjectList(Node, nodes) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise finally: DbManager().closeSession()
def validate_addnodes_request(session: Session, addNodesRequest: Dict[str, Any]): """ Raises: HardwareProfileNotFound SoftwareProfileNotFound ProfileMappingNotAllowed InvalidArgument OperationFailed """ if 'hardwareProfile' not in addNodesRequest and \ 'softwareProfile' not in addNodesRequest: raise InvalidArgument( 'Hardware and/or software profile must be specified') hpapi = HardwareProfilesDbHandler() spapi = SoftwareProfilesDbHandler() hardwareProfileName = addNodesRequest['hardwareProfile'] \ if 'hardwareProfile' in addNodesRequest else None nodeDetails = addNodesRequest.get('nodeDetails', []) softwareProfileName = addNodesRequest['softwareProfile'] \ if 'softwareProfile' in addNodesRequest else None nodeCount = int(addNodesRequest.get('count', 0)) rackNumber = addNodesRequest.get('rack') # Look up hardware profile hp = hpapi.getHardwareProfile(session, hardwareProfileName) \ if hardwareProfileName else None # Look up software profile sp = spapi.getSoftwareProfile( session, softwareProfileName) if softwareProfileName else None # Make sure that if a software profile is given that it is allowed # to be used with the given hardware profile if sp is not None and hp is not None: checkProfilesMapped(sp, hp) elif sp is not None and hp is None: if not sp.hardwareprofiles: raise InvalidArgument( 'Software profile [{0}] is not mapped to any hardware' ' profiles'.format(softwareProfileName)) if len(sp.hardwareprofiles) > 1: raise InvalidArgument( 'Ambiguous request: multiple hardware profiles are' ' mapped to software profile [{0}]'.format( softwareProfileName)) hp = sp.hardwareprofiles[0] elif hp is not None and sp is None: if not hp.mappedsoftwareprofiles: raise InvalidArgument( 'Hardware profile [{0}] is not mapped to any software' ' profiles'.format(hardwareProfileName)) if len(hp.mappedsoftwareprofiles) > 1: raise InvalidArgument( 'Ambiguous request: multiple software profiles are' ' mapped to hardware profile [{0}]'.format( hardwareProfileName)) sp = hp.mappedsoftwareprofiles[0] if sp and 'softwareProfile' not in addNodesRequest: addNodesRequest['softwareProfile'] = sp.name if 'hardwareProfile' not in addNodesRequest: addNodesRequest['hardwareProfile'] = hp.name swprofile_node_count = len(sp.nodes) # Validate 'nodeDetails' if nodeDetails: # Reconcile nodeDetails that contain hostnames with hwp name # format bWildcardNameFormat = hp.nameFormat == '*' hostname = nodeDetails[0]['name'] \ if 'name' in nodeDetails[0] else None if hostname and not bWildcardNameFormat: # Host name specified, but hardware profile does not # allow setting the host name raise InvalidArgument('Hardware profile does not allow setting' ' host names of imported nodes') if nodeCount > 0 and nodeCount != len(nodeDetails): raise InvalidArgument('Node count must be equal to number' ' of MAC/IP/node names provided') # check if software profile is locked if sp.lockedState: if sp.lockedState == 'HardLocked': raise OperationFailed( 'Nodes cannot be added to hard locked software' ' profile [{}]'.format(sp.name)) elif sp.lockedState == 'SoftLocked': if 'force' not in addNodesRequest or \ not addNodesRequest['force']: raise OperationFailed( 'Use --force argument to add nodes to soft locked' f' software profile [{sp.name}]') # ensure adding nodes does not exceed imposed limits if sp.maxNodes > 0 and \ (swprofile_node_count + nodeCount) > sp.maxNodes: raise OperationFailed( 'Request to add {} node(s) exceeds software profile' ' limit of {} nodes'.format(nodeCount, sp.maxNodes)) # Prohibit running add-host against installer validate_hwprofile(hp) # If the given hardwareProfile's nameFormat contains "#R', # then the rackNumber is required. nameFormat = hp.nameFormat if nameFormat.find('#R') != -1 and rackNumber is None: raise InvalidArgument('Missing "rackNumber" for name format [%s] of' ' hardware profile [%s]' % (nameFormat, hp)) adapter = resourceAdapterFactory.get_api(hp.resourceadapter.name) adapter.session = session adapter.validate_start_arguments(addNodesRequest, hp, dbSoftwareProfile=sp)