def __init__(self): super(SoftwareProfileManager, self).__init__() self._sp_db_api = SoftwareProfileDbApi() self._node_db_api = NodeDbApi() self._component_db_api = ComponentDbApi() self._global_param_db_api = GlobalParameterDbApi() self._kit_db_api = KitDbApi() self._config_manager = ConfigManager()
def __init__(self): super(SoftwareProfileManager, self).__init__() self._sp_db_api = SoftwareProfileDbApi() self._node_db_api = NodeDbApi() self._component_db_api = ComponentDbApi() self._global_param_db_api = GlobalParameterDbApi() self._kit_db_api = KitDbApi() self._config_manager = ConfigManager() self._logger = logging.getLogger(SOFTWARE_PROFILE_NAMESPACE)
def __init__(self, eula_validator=None): super(KitManager, self).__init__() self._eula_validator = eula_validator if not self._eula_validator: self._eula_validator = BaseEulaValidator() self._kit_db_api = KitDbApi() self._config_manager = ConfigManager() self._kits_root = self._config_manager.getKitDir() self._component_db_api = componentDbApi.ComponentDbApi()
def _get_kit_id(self): for kit in KitDbApi().getKitList(): if kit.getName() == self.name: break else: raise InvalidActionRequest( 'Unable to find kit: {}'.format(self.name)) return kit.getId()
def _get_base_kit(self): """ Gets the base kit from the database. :return: a Kit instance """ kit = None for k in KitDbApi().getKitList(): if k.getName() == 'base': kit = k return kit
class KitManager(TortugaObjectManager): def __init__(self, eula_validator=None): super(KitManager, self).__init__() self._eula_validator = eula_validator if not self._eula_validator: self._eula_validator = BaseEulaValidator() self._kit_db_api = KitDbApi() self._config_manager = ConfigManager() self._kits_root = self._config_manager.getKitDir() self._component_db_api = componentDbApi.ComponentDbApi() self._logger = logging.getLogger(KIT_NAMESPACE) def getKitList(self, session: Session): """ Get all installed kits. """ return self._kit_db_api.getKitList(session) def getKit(self, session: Session, name, version=None, iteration=None): """ Get a single kit by name, and optionally version and/or iteration. :param name: the kit name :param version: the kit version :param iteration: the kit iteration :return: the kit instance """ return self._kit_db_api.getKit(session, name, version, iteration) def getKitById(self,session: Session, id_): """ Get a single kit by id. :param id_: the kit id :return: the kit instance """ return self._kit_db_api.getKitById(session, id_) def get_kit_url(self, name, version, iteration): kit = Kit(name, version, iteration) native_repo = repoManager.getRepo() return os.path.join( native_repo.getRemoteUrl(), kit.getTarBz2FileName()) def installKit(self, db_manager, name, version, iteration): """ Install kit using kit name/version/iteration. The kit package must be located in the remote repository for the native OS. Kit will be installed for all operating systems that: 1) have repo configured on the local machine 2) are specified in the kit.xml file Raises: KitAlreadyExists """ kit = Kit(name, version, iteration) # nativeRepo = repoManager.getRepo() kitPkgUrl = self.get_kit_url(name, version, iteration) # Check for kit existence. with db_manager.session() as session: self._check_if_kit_exists(session, kit) self._logger.debug( '[{0}] Installing kit [{1}]'.format( self.__class__.__name__, kit)) return self.installKitPackage(db_manager, kitPkgUrl) def _check_if_kit_exists(self, session: Session, kit): """ Check if a kit exists, if it does then raise an exception. :raisesKitAlreadyExists: """ try: self._kit_db_api.getKit( session, kit.getName(), kit.getVersion(), kit.getIteration()) raise KitAlreadyExists( 'Kit already exists: ({}, {}, {})'.format( kit.getName(), kit.getVersion(), kit.getIteration() ) ) except KitNotFound: pass def installKitPackage(self, db_manager, kit_pkg_url): """ Install kit from the given kit url (url might be a local file). :param db_manager: a database manager instance :param kit_pkg_url: the URL to the kit package archive :raises KitAlreadyExists: :raises EulaAcceptanceRequired: """ self._logger.debug( 'Installing kit package: {}'.format(kit_pkg_url)) with db_manager.session() as session: installer = self._prepare_installer(kit_pkg_url) installer.session = session kit = installer.get_kit() try: self._run_installer(db_manager, installer) except Exception as ex: self._delete_kit(session, kit, force=False) raise ex return kit def _prepare_installer(self, kit_pkg_url): """ Extracts a kit archive and prepares the kit installer for the installation process. :param kit_pkg_url: the URL to the kit package archive :return: the KitInstaller instance """ # # Download/copy kit archive. # kit_src_path = os.path.basename(kit_pkg_url) kit_pkg_path = utils.retrieve( kit_src_path, kit_pkg_url, self._kits_root) # # Make sure the kit version is compatible # kit_meta = utils.get_metadata_from_archive(kit_pkg_path) kit_spec = (kit_meta['name'], kit_meta['version'], kit_meta['iteration']) requires_core = kit_meta.get('requires_core', VERSION) if not version_is_compatible(requires_core): errmsg = 'The {} kit requires tortuga core >= {}'.format( kit_meta['name'], requires_core) raise OperationFailed(errmsg) # # Unpack the archive # kit_dir = utils.unpack_kit_archive(kit_pkg_path, self._kits_root) # # Load and initialize kit installer # load_kits() try: installer = get_kit_installer(kit_spec)() assert installer.is_installable() except Exception as ex: if os.path.exists(kit_dir): self._logger.debug( 'Removing kit installation directory: {}'.format(kit_dir)) osUtility.removeDir(kit_dir) self._logger.warning( 'Kit is not installable: {}'.format(kit_spec)) return return installer def _run_installer(self, db_manager: DbManager, installer): """ Runs the installation process for a kit installer. :param db_manager: the DbManager instance :param installer: the KitInstaller instance to run the install process for """ kit = installer.get_kit() # # This method will throw KitAlreadyExists, if it does... # self._check_if_kit_exists(installer.session, kit) # # Validate eula # eula = installer.get_eula() if not eula: self._logger.debug('No EULA acceptance required') else: if not self._eula_validator.validate_eula(eula): raise EulaAcceptanceRequired( 'You must accept the EULA to install this kit') # # Runs the kit pre install method # installer.run_action('pre_install') # # Get list of operating systems supported by this installer # os_info_list = [ repo.getOsInfo() for repo in repoManager.getRepoList() ] # # Install operating system specific packages # self._install_os_packages(kit, os_info_list) # # Initialize any DB tables provided by the kit # db_manager.init_database() # # Add the kit to the database # self._kit_db_api.addKit(installer.session, kit) # # Clean up the kit archive directory # self._clean_kit_achiv_dir(kit, installer.install_path) # # Install puppet modules # installer.run_action('install_puppet_modules') # # Run post install # installer.run_action('post_install') if eula: ActionManager().logAction( 'Kit [{}] installed and EULA accepted at [{}]' ' local machine time.'.format( installer.spec, time.ctime()) ) else: ActionManager().logAction( 'Kit [{}] installed at [{}] local machine time.'.format( installer.spec, time.ctime()) ) def _install_os_packages(self, kit, os_info_list): """ Installs OS specific packages from the kit into the repo. :param kit: the kit instance :param os_info_list: a list of osInfo instances to install for """ all_component_list = kit.getComponentList() for os_info in os_info_list: self._logger.debug( 'Preparing to install ({}, {}, {}) for {}'.format( kit.getName(), kit.getVersion(), kit.getIteration(), os_info ) ) # # Get list of compatible components # os_object_factory = getOsObjectFactory( mapOsName(os_info.getName()) ) component_manager = os_object_factory.getComponentManager() component_list = component_manager.getCompatibleComponentList( os_info, all_component_list) if not component_list: continue # # Create the package directory in the repo # repo = repoManager.getRepo(os_info) full_dir = os.path.join(repo.getLocalPath(), kit.getKitRepoDir()) osUtility.createDir(full_dir) # # Install the packages into the repo package directory # for component in component_list: self._logger.debug( '[{0}] Found component [{1}]'.format( self.__class__.__name__, component)) for package in component.getPackageList(): package_file = os.path.join( kit.install_path, package.getRelativePath()) self._logger.debug( '[{0}] Found package [{1}]'.format( self.__class__.__name__, package_file)) repo.addPackage(package_file, kit.getKitRepoDir()) repo.create(kit.getKitRepoDir()) def get_kit_eula(self, name, version, iteration=None): return self.get_kit_package_eula( self.get_kit_url(name, version, iteration) ) def get_kit_package_eula(self, kit_pkg_url): # # Download/copy and unpack kit archive. # kit_src_path = os.path.basename(kit_pkg_url) kit_pkg_path = utils.retrieve( kit_src_path, kit_pkg_url, self._kits_root) kit_spec = utils.unpack_archive(kit_pkg_path, self._kits_root) # # Get the EULA from the installer # installer = get_kit_installer(kit_spec)() eula = installer.get_eula() return eula def _retrieveOSMedia(self, url: str) -> str: """ Download the OS media. :param url: String :return: String file path to download """ return urllib.request.urlretrieve(url)[0] def _processMediaspec(self, os_media_urls: List[str]) -> List[dict]: """ :param os_media_urls: List String :return: List Dictionary """ media_list: List[dict] = [] for url in os_media_urls: m = urllib.parse.urlparse(url) media_item_dict = { 'urlparse': m, } if m.scheme.lower() in ('http', 'https') and \ os.path.splitext(m.path)[1].lower() == '.iso': file_name = self._retrieveOSMedia(m.geturl()) media_item_dict['localFilePath'] = file_name media_list.append(media_item_dict) return media_list @staticmethod def _getKitOpsClass(os_family_info) -> Any: """ Import the KitOps class for the specified OS family name Raises: OsNotSupported """ try: _temp = __import__( 'tortuga.kit.%sOsKitOps' % os_family_info.getName(), globals(), locals(), ['KitOps'], 0 ) return getattr(_temp, 'KitOps') except ImportError: raise OsNotSupported('Currently unsupported distribution') def _checkExistingKit(self, session: Session, kitname: str, kitversion: str, kitarch: str): """ Raises: KitAlreadyExists """ try: kit = self._kit_db_api.getKit(session, kitname, kitversion) longname = format_kit_descriptor(kitname, kitversion, kitarch) # Attempt to get matching OS component for c in kit.getComponentList(session): if c.getName() != longname: continue for cOs in c.getOsInfoList(): if cOs == OsInfo(kitname, kitversion, kitarch): raise KitAlreadyExists( "OS kit [%s] already installed" % longname) # Kit exists, but doesn't have a matching os component except KitNotFound: pass def _create_kit_db_entry(self, session: Session, kit) -> Kit: """ Creates a database entry for a kit. :param kit: :return: a Kit instance (TortugaObject) """ try: return self._kit_db_api.getKit(session, kit['name'], kit['ver']) except KitNotFound: pass # Add the database entries for the kit kitObj = Kit(name=kit['name'], version=kit['ver'], iteration='0') kitObj.setDescription(kit['sum']) kitObj.setIsOs(True) kitObj.setIsRemovable(True) kit_descr = format_kit_descriptor(kit['name'], kit['ver'], kit['arch']) newComp = Component(name=kit_descr, version=kit['ver']) newComp.setDescription('%s mock component' % (kit_descr)) newComp.addOsInfo( osHelper.getOsInfo(kit['name'], kit['ver'], kit['arch'])) kitObj.addComponent(newComp) # Kit does not previously exist, perform 'normal' add kit operation self._kit_db_api.addKit(session, kitObj) return kitObj def installOsKit(self, session: Session, os_media_urls: List[str], **kwargs) -> Kit: """ :param os_media_urls: :param kwargs: :return: """ media_list: List[dict] = self._processMediaspec(os_media_urls) os_distro = None kit_ops = None enable_proxy = False mount_manager = None is_interactive = kwargs['bInteractive'] \ if 'bInteractive' in kwargs else False use_symlinks = kwargs['bUseSymlinks'] \ if 'bUseSymlinks' in kwargs else False # If 'mirror' is True, treat 'mediaspec' as a mirror, instead of # specific OS version. This affects the stored OS version. is_mirror = kwargs['mirror'] if 'mirror' in kwargs else False media: dict = media_list[0] # For now, remove support for multiple ISOs / mirrors. source_path = None mount_manager_source_path = None try: if use_symlinks: source_path = media['urlparse'].path elif 'localFilePath' in media: # Remote ISO file has been transferred locally and # filename is stored in 'localFilePath' mount_manager_source_path = media['localFilePath'] else: pr = media['urlparse'] if pr.scheme.lower() in ('http', 'https'): # This is a proxy URL source_path = pr.geturl() enable_proxy = True elif not pr.scheme or pr.scheme.lower() == 'file': if os.path.ismount(pr.path): # Mount point specified source_path = pr.path else: mount_manager_source_path = pr.path else: raise UnrecognizedKitMedia( 'Unhandled URL scheme [%s]' % pr.scheme) # Mount source media, as necessary if mount_manager_source_path: mount_manager = MountManager(mount_manager_source_path) mount_manager.mountMedia() source_path = mount_manager.mountpoint if os_distro is None: # Determine the OS we're working with... os_distro = DistributionFactory(source_path)() if os_distro is None: raise OsNotSupported('Could not match media') os_info = os_distro.get_os_info() # Check if OS is already installed before attempting to # perform copy operation... try: self._checkExistingKit( session, os_info.getName(), os_info.getVersion(), os_info.getArch()) except KitAlreadyExists: if mount_manager_source_path: mount_manager.unmountMedia() if 'localFilePath' in media: if os.path.exists( media['localFilePath']): os.unlink(media['localFilePath']) raise kit_ops_class = self._getKitOpsClass( os_info.getOsFamilyInfo()) kit_ops = kit_ops_class( os_distro, bUseSymlinks=use_symlinks, mirror=is_mirror) kit = kit_ops.prepareOSKit() # Copy files here if enable_proxy: kit_ops.addProxy(source_path) else: descr = None if is_interactive: descr = "Installing..." kit_ops.copyOsMedia(descr=descr) finally: if mount_manager_source_path: # MountManager instance exists. Unmount any mounted # path mount_manager.unmountMedia() if 'localFilePath' in media: if os.path.exists(media['localFilePath']): os.unlink(media['localFilePath']) kit_object = self._create_kit_db_entry(session, kit) self._postInstallOsKit(session, kit_object) return kit_object def _postInstallOsKit(self, session: Session, kit): """ Enable the OS component that may already be associated with an existing software profile. This is possible when OS media is not available during installation/creation of software profiles """ osComponents = kit.getComponentList() kitOsInfo = osComponents[0].getOsComponentList()[0].getOsInfo() # Load the newly added kit component from the database c = self._component_db_api.getComponent( session, osComponents[0].getName(), osComponents[0].getVersion(), kitOsInfo ) # Iterate over all software profiles looking for matching OS for swProfile in \ SoftwareProfileApi().getSoftwareProfileList(session): if swProfile.getOsInfo() != kitOsInfo: continue # Ensure OS component is enabled on this software profile try: self._component_db_api.addComponentToSoftwareProfile( session, c.getId(), swProfile.getId()) except SoftwareProfileComponentAlreadyExists: # Not an error... pass def _clean_kit_achiv_dir(self, kit, kit_dir): """ Remove packages from the kit archive directory """ component_list = kit.getComponentList() if not component_list: self._logger.debug('No components found') return for component in component_list: self._logger.debug( 'Found component: {}'.format(component)) for package in component.getPackageList(): package_path = os.path.join(kit_dir, package.getRelativePath()) if os.path.exists(package_path): self._logger.debug( 'Deleting package: {}'.format(package_path)) os.remove(package_path) else: self._logger.debug( 'Skipping non-existent package: {}'.format( package_path)) def deleteKit(self, session: Session, name, version=None, iteration=None, force=False): """ Delete a kit. :param session: a database session :param name: the kit name :param version: the kit version :param iteration: the kit iteration :param force: whether or not to force the deletion """ kit = self.getKit(session, name, version, iteration) if kit.getIsOs(): self._delete_os_kit(session, kit, force) else: self._delete_kit(session, kit, force) self._logger.info('Deleted kit: {}'.format(kit)) def _delete_os_kit(self, session: Session, kit, force): """ Deletes an OS kit. :param kit: the Kit instance :param force: whether or not to force the deletion """ self._cleanup_kit(session, kit, force) def _delete_kit(self, session: Session, kit: Kit, force: bool): """ Deletes a regular kit. :param session: a database instance :param kit: the Kit instance :param force: whether or not to force the deletion """ kit_spec = (kit.getName(), kit.getVersion(), kit.getIteration()) # # If the kit does not exist in the DB, then we want to skip # the step of removing it from the DB # skip_db = False try: self.getKit(session, *kit_spec) except KitNotFound: skip_db = True kit_install_path = os.path.join(self._kits_root, kit.getDirName()) if os.path.exists(kit_install_path): # # Attempt to get the kit installer # installer = None try: installer = get_kit_installer(kit_spec)() installer.session = session except KitNotFound: pass # # Attempt to run pre-uninstall action # if installer: try: installer.run_action('pre_uninstall') except Exception as ex: self._logger.warning( 'Error running pre_uninstall: {}'.format( str(ex) ) ) # # Remove db record and files # self._cleanup_kit(session, kit, force, skip_db) # # Attempt to uninstall puppet modules, and perform post-install # if installer: try: installer.run_action('uninstall_puppet_modules') except Exception as ex: self._logger.warning( 'Error uninstalling puppet modules: {}'.format( str(ex) ) ) try: installer.run_action('post_uninstall') except Exception as ex: self._logger.warning( 'Error running post-install: {}'.format( str(ex) ) ) def _cleanup_kit(self, session: Session, kit: Kit, force: bool, skip_db: bool = False): """ Uninstalls the kit and it's file repos. :param session: a database session :param kit: the Kit instance :param force: whether or not to force the deletion """ repo_dir = kit.getKitRepoDir() # # Remove the kit from the DB # if not skip_db: self._kit_db_api.deleteKit(session, kit.getName(), kit.getVersion(), kit.getIteration(), force=force) # # Remove the files and repo # for repo in repoManager.getRepoList(): # # Delete the repo # repo.delete(repo_dir) # # Remove repo files # full_repo_dir = os.path.join(repo.getLocalPath(), repo_dir) self._logger.debug( 'Removing repo dir: {}'.format(full_repo_dir)) # # When LINKOSKITMEDIA is used, the kit directory is a symlink # to the real media, delete the link instead of attempting # to delete the directory. # if os.path.islink(full_repo_dir): os.unlink(full_repo_dir) else: osUtility.removeDir(full_repo_dir) # # Check and clean up proxy # self.remove_proxy(repo_dir) # # Remove the kit installation dir # kit_dir = os.path.join(self._kits_root, kit.getDirName()) if os.path.exists(kit_dir): self._logger.debug( 'Removing kit installation directory: {}'.format(kit_dir)) osUtility.removeDir(kit_dir) def remove_proxy(self, repoDir): # Check for this repo as an actual entry in the tortuga apache.conf # file to remove config = configparser.ConfigParser() cfgfile = os.path.join( self._config_manager.getKitConfigBase(), 'base/apache-component.conf' ) config.read(cfgfile) paths = config.get('cache', 'cache_path_list').split() newPaths = paths[:] for p in paths: if p.endswith(repoDir): # Remove this dir from the cache newPaths.remove(p) if config.has_option('proxy', 'proxy_list'): proxies = config.get('proxy', 'proxy_list').split() if repoDir in ' '.join(proxies): # Remove this repository from the proxy for entry in proxies[:]: if repoDir in entry: config.remove_option('proxy', entry) proxies.remove(entry) else: proxies = [] # Update apache configuration config.set('cache', 'cache_path_list', '\n'.join(newPaths)) config.set('proxy', 'proxy_list', ' '.join(proxies)) with open(cfgfile, 'w') as fp: config.write(fp) def configureProxy(self, medialoc, repoDir): config = configparser.ConfigParser() cfgfile = os.path.join( self._config_manager.getKitConfigBase(), 'base/apache-component.conf') config.read(cfgfile) proxies = [] paths = [] if config.has_section('cache') and \ config.has_option('cache', 'cache_path_list'): paths = config.get('cache', 'cache_path_list').split() if repoDir not in paths: paths.append(repoDir) if not config.has_section('cache'): config.add_section('cache') config.set('cache', 'cache_path_list', '\n'.join(paths)) if config.has_section('proxy') and \ config.has_option('proxy', 'proxy_list'): proxies = config.get('proxy', 'proxy_list').split() if repoDir not in proxies: proxies.append(repoDir) if not config.has_section('proxy'): config.add_section('proxy') config.set('proxy', 'proxy_list', ' '.join(proxies)) config.set('proxy', repoDir, medialoc) with open(cfgfile, 'w') as fp: config.write(fp)
class SoftwareProfileManager(TortugaObjectManager, Singleton): BASE_KIT_NAME = 'base' def __init__(self): super(SoftwareProfileManager, self).__init__() self._sp_db_api = SoftwareProfileDbApi() self._node_db_api = NodeDbApi() self._component_db_api = ComponentDbApi() self._global_param_db_api = GlobalParameterDbApi() self._kit_db_api = KitDbApi() self._config_manager = ConfigManager() def getSoftwareProfileList(self, tags=None): """Return all of the softwareprofiles with referenced components in this softwareprofile """ return self._sp_db_api.getSoftwareProfileList(tags=tags) def getIdleSoftwareProfileList(self): """ Return all of the idle softwareprofiles """ return self._sp_db_api.getIdleSoftwareProfileList() def setIdleState(self, softwareProfileName, state): """ Sets the idle state of a softwareprofile """ return self._sp_db_api.setIdleState(softwareProfileName, state) def addAdmin(self, softwareProfileName, adminUsername): """ Add an admin as an authorized user. Returns: None Throws: TortugaException AdminNotFound SoftwareProfileNotFound """ return self._sp_db_api.addAdmin(softwareProfileName, adminUsername) def deleteAdmin(self, softwareProfileName, adminUsername): """ Remove an admin as an authorized user. Returns: None Throws: TortugaException AdminNotFound SoftwareProfileNotFound """ return self._sp_db_api.deleteAdmin(softwareProfileName, adminUsername) def updateSoftwareProfile(self, softwareProfileObject): try: self.getLogger().debug('Updating software profile: %s' % (softwareProfileObject.getName())) # First get the object from the db we are updating... existingProfile = self.\ getSoftwareProfileById(softwareProfileObject.getId()) # Set parameters that we will not allow updating softwareProfileObject.setOsInfo(existingProfile.getOsInfo()) softwareProfileObject.setOsId(existingProfile.getOsId()) softwareProfileObject.setIsIdle(existingProfile.getIsIdle()) softwareProfileObject.setType(existingProfile.getType()) self._sp_db_api.updateSoftwareProfile(softwareProfileObject) except TortugaException as ex: raise except Exception as ex: self.getLogger().exception('%s' % ex) raise TortugaException(exception=ex) def getSoftwareProfile(self, name, optionDict=None): return self._sp_db_api.getSoftwareProfile(name, optionDict or {}) def getSoftwareProfileById(self, id_, optionDict=None): return self._sp_db_api.getSoftwareProfileById(id_, optionDict=optionDict or {}) def _getCoreComponentForOsInfo(self, osInfo): # Find core component # Find the version of the 'core' component import tortuga.kit.kitApi _kitApi = tortuga.kit.kitApi.KitApi() baseKit = None for baseKit in _kitApi.getKitList(): if not baseKit.getName() == self.BASE_KIT_NAME: continue break else: raise KitNotFound('Kit [%s] not found.' % (self.BASE_KIT_NAME)) baseComp = None for baseComp in baseKit.getComponentList(): if baseComp.getName() != 'core': continue break else: raise ComponentNotFound('Component [%s] not found in kit [%s]' % ('core', baseKit.getName())) comp = osUtility.getOsObjectFactory().getComponentManager().\ getBestMatchComponent( baseComp.getName(), baseComp.getVersion(), osInfo, baseKit.getId()) comp.setKit(baseKit) return comp def _getOsInfo(self, bOsMediaRequired): if not bOsMediaRequired: # As a placeholder, use the same OS as the installer # Find installer node entry node = self._node_db_api.getNode(ConfigManager().getInstaller(), {'softwareprofile': True}) return node.getSoftwareProfile().getOsInfo() # Use available operating system kit; raise exception if # multiple available os_kits = _get_os_kits() if not os_kits: raise KitNotFound('No operating system kit installed') if len(os_kits) > 1: raise KitNotFound( 'Multiple OS kits defined; use --os option to specify' ' operating system') kit = kitApiFactory.getKitApi().getKit(os_kits[0].getName(), os_kits[0].getVersion(), '0') components = kit.getComponentList() if not components: raise ComponentNotFound('Malformed operating system kit [%s]' % (os_kits)) osinfo_list = components[0].getOsInfoList() if len(osinfo_list) > 1: raise ComponentNotFound( 'Multiple operating system components for kit [%s];' ' use --os argument to specify operating system' % (os_kits[0])) return osinfo_list[0] def createSoftwareProfile(self, swProfileSpec, settingsDict=None): """ Exceptions: ConfigurationError NetworkNotFound ComponentNotFound KitNotFound OSError """ # Parse 'settingsDict' if settingsDict: # ... bOsMediaRequired; default is True bOsMediaRequired = settingsDict['bOsMediaRequired'] \ if 'bOsMediaRequired' in settingsDict else True # ... unmanagedProfile; default is False unmanagedProfile = settingsDict['unmanagedProfile'] \ if 'unmanagedProfile' in settingsDict else False # Validate software profile name validation.validateProfileName(swProfileSpec.getName()) # Insert default description for software profile if not swProfileSpec.getDescription() or \ swProfileSpec.getDescription() == '**DEFAULT**': swProfileSpec.setDescription('%s Nodes' % (swProfileSpec.getName())) self.getLogger().debug('Creating software profile [%s]' % (swProfileSpec)) osInfo = swProfileSpec.getOsInfo() \ if swProfileSpec.getOsInfo() else self._getOsInfo(bOsMediaRequired) # If we're creating an unmanaged software profile (no # DHCP/PXE/kickstart/OS) just create it now and we're done if unmanagedProfile: self._sp_db_api.addSoftwareProfile(swProfileSpec) else: if bOsMediaRequired and swProfileSpec.getOsInfo(): try: kitApiFactory.getKitApi().getKit( swProfileSpec.getOsInfo().getName(), swProfileSpec.getOsInfo().getVersion(), '0') except KitNotFound: self._logger.error('OS kit for [%s] not found' % (swProfileSpec.getOsInfo())) raise else: swProfileSpec.setOsInfo(osInfo) # Get component manager for appropriate OS family osConfig = osHelper.getOsInfo(osInfo.getName(), osInfo.getVersion(), osInfo.getArch()) osObjFactory = osUtility.getOsObjectFactory( osConfig.getOsFamilyInfo().getName()) compManager = osObjFactory.getComponentManager() # Need to be fancy with components spComponents = swProfileSpec.getComponents() swProfileSpec.setComponents(TortugaObjectList()) bFoundOsComponent = False bFoundCoreComponent = False components = [] # Iterate over components, adding them to the software profile for c in spComponents: cobj = compManager.getBestMatchComponent( c.getName(), c.getVersion(), osInfo, c.getKit().getId()) k = cobj.getKit() if k.getIsOs(): # This component is a member of the OS kit, set the flag bFoundOsComponent = True else: if c.getName() == 'core': # Found the 'core' component, set the flag bFoundCoreComponent = True components.append(cobj) # If the operating system is undefined for this software # profile, use the same OS as the installer. if bOsMediaRequired and not bFoundOsComponent: # Find OS component osCompName = '%s-%s-%s' % ( osInfo.getName(), osInfo.getVersion(), osInfo.getArch()) self.getLogger().debug('Automatically adding OS component [%s]' ' (not specified in template)' % (osCompName)) try: osComponent = self._component_db_api.getComponent( osCompName, osInfo.getVersion(), osInfo, {'kit': True}) components.append(osComponent) except ComponentNotFound: # Cannot find OS component, don't freak out pass # Ensure 'core' component is enabled if not bFoundCoreComponent: # Attempt to automatically add the core component, only # if one exists for this OS try: comp = self._getCoreComponentForOsInfo(osInfo) self.getLogger().debug( 'Automatically adding [core] component' ' (not specified in template)') components.append(comp) except ComponentNotFound: pass # Initialize values for kernel, kernelParams, and initrd if not swProfileSpec.getKernel(): swProfileSpec.setKernel( osObjFactory.getOsSysManager().getKernel(osInfo)) if not swProfileSpec.getInitrd(): swProfileSpec.setInitrd( osObjFactory.getOsSysManager().getInitrd(osInfo)) # Add the software profile self._sp_db_api.addSoftwareProfile(swProfileSpec) # Enable components in one fell swoop for comp in components: self.getLogger().debug('Enabling component [%s]' % (comp.getName())) if comp.getKit().getIsOs(): # Don't use enableComponent() on OS kit self._component_db_api.\ addComponentToSoftwareProfile( comp.getId(), swProfileSpec.getId()) continue self.enableComponent(swProfileSpec.getName(), comp.getKit().getName(), comp.getKit().getVersion(), comp.getKit().getIteration(), comp.getName(), comp.getVersion()) self.getLogger().debug( 'Software profile [%s] created successfully' % (swProfileSpec.getName())) def _getComponent(self, kit, compName, compVersion): \ # pylint: disable=no-self-use # Iterate over component list, looking for a match comp = None for comp in kit.getComponentList(): if comp.getName() == compName and \ comp.getVersion() == compVersion: break else: raise ComponentNotFound("Component [%s-%s] not found in kit [%s]" % (compName, compVersion, kit)) return comp def _get_kit_by_component(self, comp_name, comp_version=None): """ Gets a kit by compoent name/version. :param comp_name: the name of the component :param comp_version: the version of the component :raises KitNotFound: :raises ComponentNotFound: """ kit_list = self._kit_db_api.getKitList() kits = [ kit for kit in kit_list for component in kit.getComponentList() if component.getName() == comp_name and ( comp_version is None or component.getVersion() == comp_version) ] if not kits: raise KitNotFound('Kit containing component [%s] not found' % (comp_name)) if len(kits) > 1: raise ComponentNotFound( 'Kit name must be specified, multiple kits contain ' 'component: {}'.format(comp_name)) return kits[0] def enableComponent(self, software_profile_name, kit_name, kit_version, kit_iteration, comp_name, comp_version=None): """ Enable a component on a software profile. :param software_profile_name: the name of the software profile :param kit_name: the name of the kit :param kit_version: the version of the kit :param kit_iteration: the iteration of the kit :param comp_name: the name of the component :param comp_version: the version of the component :raises KitNotFound: :raises SoftwareProfileNotFound: :raises ComponentNotFound: """ kit, comp_version = self._get_kit_and_component_version( kit_name, kit_version, kit_iteration, comp_name, comp_version) software_profile = self.getSoftwareProfile(software_profile_name, {'os': True}) if kit.getIsOs(): best_match_component = self._enable_os_kit_component( kit, comp_name, comp_version, software_profile) else: best_match_component = self._enable_kit_component( kit, comp_name, comp_version, software_profile) if not best_match_component: self.getLogger().info( 'Component not enabled: {}'.format(comp_name)) else: self.getLogger().info( 'Enabled component on software profile: {} -> {}'.format( best_match_component, software_profile)) def _get_kit_and_component_version(self, kit_name, kit_version, kit_iteration, comp_name, comp_version=None): """ Gets a Kit instance and component version. :param kit_name: the name of the kit :param kit_version: the version of the kit :param kit_iteration: the iteration of the kit :param comp_name: the component name :param comp_version: the component version (optional) :return: a tuple, consisting of (Kit, component_version) """ kit = None if kit_name is None: kit = self._get_kit_by_component(comp_name, comp_version=comp_version) # # Get component version if required # if comp_version is None: for component in kit.getComponentList(): if component.getName() == comp_name: comp_version = component.getVersion() break elif kit_version is None or kit_iteration is None: kits_found = 0 for k in self._kit_db_api.getKitList(): if k.getName() == kit_name and \ (kit_version is None or k.getVersion() == kit_version) and \ (kit_iteration is None or k.getIteration() == kit_iteration): kit = k kits_found += 1 if kits_found > 1: if kit_version is not None: raise KitNotFound('Multiple kits found: {}-{}'.format( kit_name, kit_version)) else: raise KitNotFound( 'Multiple kits found {}'.format(kit_name)) else: kit = self._kit_db_api.getKit(kit_name, kit_version, kit_iteration) return kit, comp_version def _enable_kit_component(self, kit, comp_name, comp_version, software_profile): """ Enables a regular kit component on a specific software profile. :param kit: the Kit instance, whose component is being enabled :param comp_name: the name of the component to enable :param comp_version: the version of the component to enable :param software_profile: the software profile on which the component will be enabled :return: the Component instance that was enabled """ kit_spec = (kit.getName(), kit.getVersion(), kit.getIteration()) load_kits() installer = get_kit_installer(kit_spec)() comp_installer = installer.get_component_installer(comp_name) if not comp_installer.is_enableable(software_profile): self.getLogger().warning('Component cannot be enabled: {}'.format( comp_installer.spec)) return None comp_installer.run_action('pre_enable', software_profile.getName()) best_match_component = self._add_component_to_software_profile( kit, comp_name, comp_version, software_profile) comp_installer.run_action('enable', software_profile.getName()) comp_installer.run_action('post_enable', software_profile.getName()) return best_match_component def _enable_os_kit_component(self, kit, comp_name, comp_version, software_profile): """ Enables an OS kit component on a specific software profile. :param kit: the OS Kit instance, whose component is being enabled :param comp_name: the name of the component to enable :param comp_version: the version of the component to enable :param software_profile: the software profile on which the component will be enabled :return: the Component instance that was enabled """ return self._add_component_to_software_profile(kit, comp_name, comp_version, software_profile) def _add_component_to_software_profile(self, kit, comp_name, comp_version, software_profile): """ Adds a kit to a software profile. This is a data-only operation, as no pre/post enable actions are called. :param kit: the OS Kit instance, whose component is being added :param comp_name: the name of the component to add :param comp_version: the version of the component to add :param software_profile: the software profile to which the component will be added :return: the Component instance that was added """ os_obj_factory = osUtility.getOsObjectFactory( software_profile.getOsInfo().getOsFamilyInfo().getName()) comp_manager = os_obj_factory.getComponentManager() best_match_component = comp_manager.getBestMatchComponent( comp_name, comp_version, software_profile.getOsInfo(), kit.getId()) self._component_db_api.addComponentToSoftwareProfile( best_match_component.getId(), software_profile.getId()) return best_match_component def disableComponent(self, software_profile_name, kit_name, kit_version, kit_iteration, comp_name, comp_version=None): """ Disables a component on a software profile. :param software_profile_name: the name of the software profile :param kit_name: the name of the kit :param kit_version: the version of the kit :param kit_iteration: the iteration of the kit :param comp_name: the name of the component :param comp_version: the version of the component :raises KitNotFound: :raises SoftwareProfileNotFound: :raises ComponentNotFound: """ kit, comp_version = self._get_kit_and_component_version( kit_name, kit_version, kit_iteration, comp_name) software_profile = self.getSoftwareProfile(software_profile_name, {'os': True}) if kit.getIsOs(): best_match_component = self._disable_os_kit_component( kit, comp_name, comp_version, software_profile) else: best_match_component = self._disable_kit_component( kit, comp_name, comp_version, software_profile) self.getLogger().info( 'Disabled component on software profile: {} -> {}'.format( best_match_component, software_profile)) def _disable_kit_component(self, kit, comp_name, comp_version, software_profile): """ Disables a regular kit component on a specific software profile. :param kit: the Kit instance, whose component is being disabled :param comp_name: the name of the component to disable :param comp_version: the version of the component to disable :param software_profile: the software profile on which the component will be disable :return: the Component instance that was disabled """ kit_spec = (kit.getName(), kit.getVersion(), kit.getIteration()) load_kits() installer = get_kit_installer(kit_spec)() comp_installer = installer.get_component_installer(comp_name) comp_installer.run_action('pre_disable', software_profile.getName()) comp_installer.run_action('disable', software_profile.getName()) best_match_component = \ self._remove_component_from_software_profile( kit, comp_name, comp_version, software_profile) comp_installer.run_action('post_disable', software_profile.getName()) return best_match_component def _disable_os_kit_component(self, kit, comp_name, comp_version, software_profile): """ Enables an OS kit component on a specific software profile. :param kit: the OS Kit instance, whose component is being disabled :param comp_name: the name of the component to disable :param comp_version: the version of the component to disable :param software_profile: the software profile on which the component will be disabled :return: the Component instance that was disabled """ return self._remove_component_from_software_profile( kit, comp_name, comp_version, software_profile) def _remove_component_from_software_profile(self, kit, comp_name, comp_version, software_profile): """ Removes a kit to a software profile. This is a data-only operation, as no pre/post disable actions are called. :param kit: the OS Kit instance, whose component is being removed :param comp_name: the name of the component to remove :param comp_version: the version of the component to remove :param software_profile: the software profile to which the component will be removed :return: the Component instance that was removed """ os_obj_factory = osUtility.getOsObjectFactory( software_profile.getOsInfo().getOsFamilyInfo().getName()) comp_manager = os_obj_factory.getComponentManager() best_match_component = comp_manager.getBestMatchComponent( comp_name, comp_version, software_profile.getOsInfo(), kit.getId()) self._component_db_api.deleteComponentFromSoftwareProfile( best_match_component.getId(), software_profile.getId()) return best_match_component def deleteSoftwareProfile(self, name): """ Delete software profile by name Raises: SoftwareProfileNotFound """ self._sp_db_api.deleteSoftwareProfile(name) # Remove all flags for software profile swProfileFlagPath = os.path.join(self._config_manager.getRoot(), 'var/run/actions/%s' % (name)) if os.path.exists(swProfileFlagPath): shutil.rmtree(swProfileFlagPath) self.getLogger().info('Deleted software profile [%s]' % (name)) def getNodeList(self, softwareProfileName): return self._sp_db_api.getNodeList(softwareProfileName) def getEnabledComponentList(self, name): """ Get the list of enabled components """ return self._sp_db_api.getEnabledComponentList(name) def getPackageList(self, softwareProfileName): """ Get list of packages. """ return self._sp_db_api.getPackageList(softwareProfileName) def getPartitionList(self, softwareProfileName): """ Get list of partitions. """ return self._sp_db_api.getPartitionList(softwareProfileName) def getProvisioningInfo(self, nodeName): return self._sp_db_api.getProvisioningInfo(nodeName) def addUsableHardwareProfileToSoftwareProfile(self, hardwareProfileName, softwareProfileName): self._logger.info( 'Mapping hardware profile [%s] to software profile [%s]' % (hardwareProfileName, softwareProfileName)) return self._sp_db_api.\ addUsableHardwareProfileToSoftwareProfile(hardwareProfileName, softwareProfileName) def deleteUsableHardwareProfileFromSoftwareProfile(self, hardwareProfileName, softwareProfileName): return self._sp_db_api.\ deleteUsableHardwareProfileFromSoftwareProfile( hardwareProfileName, softwareProfileName) def copySoftwareProfile(self, srcSoftwareProfileName, dstSoftwareProfileName): # Validate software profile name validation.validateProfileName(dstSoftwareProfileName) self._logger.info('Copying software profile [%s] to [%s]' % (srcSoftwareProfileName, dstSoftwareProfileName)) softwareProfile = self._sp_db_api.\ copySoftwareProfile(srcSoftwareProfileName, dstSoftwareProfileName) return softwareProfile def getUsableNodes(self, softwareProfileName): return self._sp_db_api.getUsableNodes(softwareProfileName)
class SoftwareProfileManager(TortugaObjectManager): \ # pylint: disable=too-many-public-methods BASE_KIT_NAME = 'base' def __init__(self): super(SoftwareProfileManager, self).__init__() self._sp_db_api = SoftwareProfileDbApi() self._node_db_api = NodeDbApi() self._component_db_api = ComponentDbApi() self._global_param_db_api = GlobalParameterDbApi() self._kit_db_api = KitDbApi() self._config_manager = ConfigManager() self._logger = logging.getLogger(SOFTWARE_PROFILE_NAMESPACE) def getSoftwareProfileList(self, session: Session, tags=None): """Return all of the softwareprofiles with referenced components in this softwareprofile """ results = self._sp_db_api.getSoftwareProfileList(session, tags=tags) for software_profile_obj in results: # load any available software profile metadata software_profile_obj.setMetadata( self.get_software_profile_metadata( session, software_profile_obj.getName())) return results def addAdmin(self, session: Session, softwareProfileName, adminUsername): """ Add an admin as an authorized user. Returns: None Throws: TortugaException AdminNotFound SoftwareProfileNotFound """ return self._sp_db_api.addAdmin(session, softwareProfileName, adminUsername) def deleteAdmin(self, session: Session, softwareProfileName, adminUsername): """ Remove an admin as an authorized user. Returns: None Throws: TortugaException AdminNotFound SoftwareProfileNotFound """ return self._sp_db_api.deleteAdmin(session, softwareProfileName, adminUsername) def updateSoftwareProfile(self, session: Session, softwareProfileObject): self._logger.debug('Updating software profile: %s' % (softwareProfileObject.getName())) # # First get the object from the db we are updating # existing_swp = self.getSoftwareProfileById( session, softwareProfileObject.getId()) # # Set parameters that we will not allow updating # softwareProfileObject.setOsInfo(existing_swp.getOsInfo()) softwareProfileObject.setOsId(existing_swp.getOsId()) softwareProfileObject.setType(existing_swp.getType()) # # Do the DB update # self._sp_db_api.updateSoftwareProfile(session, softwareProfileObject) # # Get the new version # new_swp = self.getSoftwareProfileById(session, softwareProfileObject.getId()) # # If the tags have changed, fire the tags changed event # if existing_swp.getTags() != new_swp.getTags(): SoftwareProfileTagsChanged.fire( softwareprofile_id=str(new_swp.getId()), softwareprofile_name=new_swp.getName(), tags=new_swp.getTags(), previous_tags=existing_swp.getTags()) def getSoftwareProfile( self, session: Session, name: str, optionDict: Optional[Dict[str, bool]] = None) -> SoftwareProfile: """ Retrieve software profile by name """ software_profile_obj: SoftwareProfile = \ self._sp_db_api.getSoftwareProfile( session, name, optionDict=optionDict) # load any available software profile metadata software_profile_obj.setMetadata( self.get_software_profile_metadata(session, name)) return software_profile_obj def getSoftwareProfileById( self, session: Session, id_: int, optionDict: Optional[Dict[str, bool]] = None) -> SoftwareProfile: """ Retrieve software profile by id """ software_profile_obj: SoftwareProfile = \ self._sp_db_api.getSoftwareProfileById( session, id_, optionDict=optionDict) # load any available software profile metadata software_profile_obj.setMetadata( self.get_software_profile_metadata(session, software_profile_obj.getName())) return software_profile_obj def _getCoreComponentForOsInfo(self, session: Session, osInfo): # Find core component baseKit = None for baseKit in self._kit_db_api.getKitList(session): if not baseKit.getName() == self.BASE_KIT_NAME: continue break else: raise KitNotFound('Kit [%s] not found.' % (self.BASE_KIT_NAME)) baseComp = None for baseComp in baseKit.getComponentList(): if baseComp.getName() != 'core': continue break else: raise ComponentNotFound('Component [%s] not found in kit [%s]' % ('core', baseKit.getName())) comp = self._component_db_api.getBestMatchComponent( session, baseComp.getName(), baseComp.getVersion(), osInfo, baseKit.getId()) comp.setKit(baseKit) return comp def _getOsInfo(self, session: Session, bOsMediaRequired: bool): if not bOsMediaRequired: # As a placeholder, use the same OS as the installer # Find installer node entry node = self._node_db_api.getNode(session, ConfigManager().getInstaller(), {'softwareprofile': True}) return node.getSoftwareProfile().getOsInfo() # Use available operating system kit; raise exception if # multiple available os_kits = self._kit_db_api.getKitList(session, os_kits_only=True) if not os_kits: raise KitNotFound('No operating system kit installed') if len(os_kits) > 1: raise KitNotFound( 'Multiple OS kits defined; use --os option to specify' ' operating system') kit = self._kit_db_api.getKit(session, os_kits[0].getName(), os_kits[0].getVersion(), '0') components = kit.getComponentList() if not components: raise ComponentNotFound('Malformed operating system kit [%s]' % (os_kits)) osinfo_list = components[0].getOsInfoList() if len(osinfo_list) > 1: raise ComponentNotFound( 'Multiple operating system components for kit [%s];' ' use --os argument to specify operating system' % (os_kits[0])) return osinfo_list[0] def createSoftwareProfile(self, session: Session, swProfileSpec, settingsDict=None): """ Exceptions: ConfigurationError NetworkNotFound ComponentNotFound KitNotFound OSError """ if settingsDict == None: settingsDict = {} bOsMediaRequired = settingsDict.get('bOsMediaRequired', True) unmanagedProfile = settingsDict.get('unmanagedProfile', False) # Validate software profile name validation.validateProfileName(swProfileSpec.getName()) # Insert default description for software profile if swProfileSpec.getDescription() is None: swProfileSpec.setDescription('%s Nodes' % (swProfileSpec.getName())) self._logger.debug('Creating software profile [%s]' % (swProfileSpec)) osInfo = swProfileSpec.getOsInfo() \ if swProfileSpec.getOsInfo() else self._getOsInfo( session, bOsMediaRequired) # If we're creating an unmanaged software profile (no # DHCP/PXE/kickstart/OS) just create it now and we're done if unmanagedProfile: self._sp_db_api.addSoftwareProfile(session, swProfileSpec) else: if bOsMediaRequired and swProfileSpec.getOsInfo(): try: self._kit_db_api.getKit( session, swProfileSpec.getOsInfo().getName(), swProfileSpec.getOsInfo().getVersion(), '0') except KitNotFound: self._logger.error('OS kit for [%s] not found' % (swProfileSpec.getOsInfo())) raise else: swProfileSpec.setOsInfo(osInfo) # Get component manager for appropriate OS family osConfig = osHelper.getOsInfo(osInfo.getName(), osInfo.getVersion(), osInfo.getArch()) osObjFactory = osUtility.getOsObjectFactory( osConfig.getOsFamilyInfo().getName()) # Need to be fancy with components spComponents = swProfileSpec.getComponents() swProfileSpec.setComponents(TortugaObjectList()) bFoundOsComponent = False bFoundCoreComponent = False components = [] # Iterate over components, adding them to the software profile for c in spComponents: cobj = self._component_db_api.getBestMatchComponent( session, c.getName(), c.getVersion(), osInfo, c.getKit().getId()) k = cobj.getKit() if k.getIsOs(): # This component is a member of the OS kit, set the flag bFoundOsComponent = True elif k.getName() == 'base' and c.getName() == 'core': # Found the 'core' component in 'base' kit bFoundCoreComponent = True components.append(cobj) # If the operating system is undefined for this software # profile, use the same OS as the installer. if bOsMediaRequired and not bFoundOsComponent: # Find OS component osCompName = '%s-%s-%s' % ( osInfo.getName(), osInfo.getVersion(), osInfo.getArch()) self._logger.debug('Automatically adding OS component [%s]' ' (not specified in template)' % (osCompName)) try: osComponent = self._component_db_api.getComponent( session, osCompName, osInfo.getVersion(), osInfo, {'kit': True}) components.append(osComponent) except ComponentNotFound: # Cannot find OS component, don't freak out pass # Ensure 'core' component is enabled if not bFoundCoreComponent: # Attempt to automatically add the core component, only # if one exists for this OS try: comp = self._getCoreComponentForOsInfo(session, osInfo) self._logger.debug('Automatically adding [core] component' ' (not specified in template)') components.append(comp) except ComponentNotFound: self._logger.warning( 'OS [{}] does not have a compatible \'core\'' ' component'.format(osInfo)) # Initialize values for kernel, kernelParams, and initrd if not swProfileSpec.getKernel(): swProfileSpec.setKernel( osObjFactory.getOsSysManager().getKernel(osInfo)) if not swProfileSpec.getInitrd(): swProfileSpec.setInitrd( osObjFactory.getOsSysManager().getInitrd(osInfo)) # Add the software profile self._sp_db_api.addSoftwareProfile(session, swProfileSpec) # Enable components in one fell swoop for comp in components: self._logger.debug('Enabling component [%s]' % (comp.getName())) if comp.getKit().getIsOs(): # Don't use enableComponent() on OS kit self._component_db_api.addComponentToSoftwareProfile( session, comp.getId(), swProfileSpec.getId()) continue self.enableComponent(session, swProfileSpec.getName(), comp.getKit().getName(), comp.getKit().getVersion(), comp.getKit().getIteration(), comp.getName(), comp.getVersion()) # # Fire the tags changed event for all creates that have tags # # Get the latest version from the db in case the create method # added some embellishments # swp = self.getSoftwareProfile(session, swProfileSpec.getName()) if swp.getTags(): SoftwareProfileTagsChanged.fire(softwareprofile_id=str( swp.getId()), softwareprofile_name=swp.getName(), tags=swp.getTags(), previous_tags={}) def _getComponent(self, kit, compName, compVersion): \ # pylint: disable=no-self-use # Iterate over component list, looking for a match comp = None for comp in kit.getComponentList(): if comp.getName() == compName and \ comp.getVersion() == compVersion: break else: raise ComponentNotFound("Component [%s-%s] not found in kit [%s]" % (compName, compVersion, kit)) return comp def _get_kit_by_component(self, session: Session, comp_name, comp_version=None): """ Gets a kit by compoent name/version. :param comp_name: the name of the component :param comp_version: the version of the component :raises KitNotFound: :raises ComponentNotFound: """ kit_list = self._kit_db_api.getKitList(session) kits = [ kit for kit in kit_list for component in kit.getComponentList() if component.getName() == comp_name and ( comp_version is None or component.getVersion() == comp_version) ] if not kits: raise KitNotFound('Kit containing component [%s] not found' % (comp_name)) if len(kits) > 1: raise ComponentNotFound( 'Kit name must be specified, multiple kits contain ' 'component: {}'.format(comp_name)) return kits[0] def enableComponent(self, session: Session, software_profile_name: str, kit_name: str, kit_version: str, kit_iteration: str, comp_name: str, comp_version: Optional[str] = None): """ Enable a component on a software profile. :param software_profile_name: the name of the software profile :param kit_name: the name of the kit :param kit_version: the version of the kit :param kit_iteration: the iteration of the kit :param comp_name: the name of the component :param comp_version: the version of the component :raises KitNotFound: :raises SoftwareProfileNotFound: :raises ComponentNotFound: """ kit, comp_version = self._get_kit_and_component_version( session, kit_name, kit_version, kit_iteration, comp_name, comp_version) software_profile = self.getSoftwareProfile(session, software_profile_name, {'os': True}) if kit.getIsOs(): best_match_component = self._enable_os_kit_component( session, kit, comp_name, comp_version, software_profile) else: best_match_component = self._enable_kit_component( session, kit, comp_name, comp_version, software_profile) if not best_match_component: self._logger.info('Component not enabled: {}'.format(comp_name)) else: self._logger.info( 'Enabled component on software profile: {} -> {}'.format( best_match_component, software_profile)) def _get_kit_and_component_version(self, session: Session, kit_name, kit_version, kit_iteration, comp_name, comp_version=None): """ Gets a Kit instance and component version. :param kit_name: the name of the kit :param kit_version: the version of the kit :param kit_iteration: the iteration of the kit :param comp_name: the component name :param comp_version: the component version (optional) :return: a tuple, consisting of (Kit, component_version) """ kit = None if kit_name is None: kit = self._get_kit_by_component(session, comp_name, comp_version=comp_version) # # Get component version if required # if comp_version is None: for component in kit.getComponentList(): if component.getName() == comp_name: comp_version = component.getVersion() break elif kit_version is None or kit_iteration is None: kits_found = 0 for k in self._kit_db_api.getKitList(session): if k.getName() == kit_name and \ (kit_version is None or k.getVersion() == kit_version) and \ (kit_iteration is None or k.getIteration() == kit_iteration): kit = k kits_found += 1 if kits_found > 1: if kit_version is not None: raise KitNotFound('Multiple kits found: {}-{}'.format( kit_name, kit_version)) else: raise KitNotFound( 'Multiple kits found {}'.format(kit_name)) else: kit = self._kit_db_api.getKit(session, kit_name, kit_version, kit_iteration) if kit is None: raise KitNotFound('Kit [%s] not found' % (Kit(kit_name, kit_version, kit_iteration))) return kit, comp_version def _enable_kit_component(self, session: Session, kit, comp_name, comp_version, software_profile): """ Enables a regular kit component on a specific software profile. :param kit: the Kit instance, whose component is being enabled :param comp_name: the name of the component to enable :param comp_version: the version of the component to enable :param software_profile: the software profile on which the component will be enabled :return: the Component instance that was enabled """ kit_spec = (kit.getName(), kit.getVersion(), kit.getIteration()) installer = get_kit_installer(kit_spec)() installer.session = session comp_installer = installer.get_component_installer(comp_name) if comp_installer is None: raise ComponentNotFound('Component [%s] not found in kit [%s]' % (comp_name, kit)) if not comp_installer.is_enableable(software_profile): self._logger.warning('Component cannot be enabled: {}'.format( comp_installer.spec)) return None comp_installer.run_action('pre_enable', software_profile.getName()) best_match_component = self._add_component_to_software_profile( session, kit, comp_name, comp_version, software_profile) comp_installer.run_action('enable', software_profile.getName()) comp_installer.run_action('post_enable', software_profile.getName()) return best_match_component def _enable_os_kit_component(self, session: Session, kit, comp_name, comp_version, software_profile): """ Enables an OS kit component on a specific software profile. :param kit: the OS Kit instance, whose component is being enabled :param comp_name: the name of the component to enable :param comp_version: the version of the component to enable :param software_profile: the software profile on which the component will be enabled :return: the Component instance that was enabled """ return self._add_component_to_software_profile(session, kit, comp_name, comp_version, software_profile) def _add_component_to_software_profile(self, session: Session, kit, comp_name, comp_version, software_profile): """ Adds a kit to a software profile. This is a data-only operation, as no pre/post enable actions are called. :param kit: the OS Kit instance, whose component is being added :param comp_name: the name of the component to add :param comp_version: the version of the component to add :param software_profile: the software profile to which the component will be added :return: the Component instance that was added """ best_match_component = \ self._component_db_api.getBestMatchComponent( session, comp_name, comp_version, software_profile.getOsInfo(), kit.getId()) self._component_db_api.addComponentToSoftwareProfile( session, best_match_component.getId(), software_profile.getId()) return best_match_component def disableComponent(self, session: Session, software_profile_name, kit_name, kit_version, kit_iteration, comp_name, comp_version=None): \ # pylint: disable=unused-argument """ Disables a component on a software profile. :param software_profile_name: the name of the software profile :param kit_name: the name of the kit :param kit_version: the version of the kit :param kit_iteration: the iteration of the kit :param comp_name: the name of the component :param comp_version: the version of the component :raises KitNotFound: :raises SoftwareProfileNotFound: :raises ComponentNotFound: """ kit, comp_version = self._get_kit_and_component_version( session, kit_name, kit_version, kit_iteration, comp_name) software_profile = self.getSoftwareProfile(session, software_profile_name, {'os': True}) if kit.getIsOs(): best_match_component = self._disable_os_kit_component( session, kit, comp_name, comp_version, software_profile) else: best_match_component = self._disable_kit_component( session, kit, comp_name, comp_version, software_profile) self._logger.info( 'Disabled component on software profile: {} -> {}'.format( best_match_component, software_profile)) def _disable_kit_component(self, session, kit, comp_name, comp_version, software_profile): """ Disables a regular kit component on a specific software profile. :param kit: the Kit instance, whose component is being disabled :param comp_name: the name of the component to disable :param comp_version: the version of the component to disable :param software_profile: the software profile on which the component will be disable :return: the Component instance that was disabled """ kit_spec = (kit.getName(), kit.getVersion(), kit.getIteration()) installer = get_kit_installer(kit_spec)() installer.session = session comp_installer = installer.get_component_installer(comp_name) if comp_installer is None: raise ComponentNotFound('Component [%s] not found in kit [%s]' % (comp_name, kit)) comp_installer.run_action('pre_disable', software_profile.getName()) comp_installer.run_action('disable', software_profile.getName()) best_match_component = \ self._remove_component_from_software_profile( session, kit, comp_name, comp_version, software_profile) comp_installer.run_action('post_disable', software_profile.getName()) return best_match_component def _disable_os_kit_component(self, session, kit, comp_name, comp_version, software_profile): """ Enables an OS kit component on a specific software profile. :param kit: the OS Kit instance, whose component is being disabled :param comp_name: the name of the component to disable :param comp_version: the version of the component to disable :param software_profile: the software profile on which the component will be disabled :return: the Component instance that was disabled """ return self._remove_component_from_software_profile( session, kit, comp_name, comp_version, software_profile) def _remove_component_from_software_profile(self, session: Session, kit, comp_name, comp_version, software_profile): """ Removes a kit to a software profile. This is a data-only operation, as no pre/post disable actions are called. :param kit: the OS Kit instance, whose component is being removed :param comp_name: the name of the component to remove :param comp_version: the version of the component to remove :param software_profile: the software profile to which the component will be removed :return: the Component instance that was removed """ best_match_component = self._component_db_api.getBestMatchComponent( session, comp_name, comp_version, software_profile.getOsInfo(), kit.getId()) self._component_db_api.deleteComponentFromSoftwareProfile( session, best_match_component.getId(), software_profile.getId()) return best_match_component def deleteSoftwareProfile(self, session: Session, name): """ Delete software profile by name Raises: SoftwareProfileNotFound """ self._sp_db_api.deleteSoftwareProfile(session, name) # Remove all flags for software profile swProfileFlagPath = os.path.join(self._config_manager.getRoot(), 'var/run/actions/%s' % (name)) if os.path.exists(swProfileFlagPath): shutil.rmtree(swProfileFlagPath) self._logger.info('Deleted software profile [%s]' % (name)) def getNodeList(self, session: Session, softwareProfileName): return self._sp_db_api.getNodeList(session, softwareProfileName) def getEnabledComponentList(self, session: Session, name): """ Get the list of enabled components """ return self._sp_db_api.getEnabledComponentList(session, name) def getPartitionList(self, session: Session, softwareProfileName): """ Get list of partitions. """ return self._sp_db_api.getPartitionList(session, softwareProfileName) def addUsableHardwareProfileToSoftwareProfile( self, session: Session, hardwareProfileName: str, softwareProfileName: str) -> None: """ Map software profile to hardware profile """ self._logger.info( 'Mapping hardware profile [%s] to software profile [%s]', hardwareProfileName, softwareProfileName) self._sp_db_api.addUsableHardwareProfileToSoftwareProfile( session, hardwareProfileName, softwareProfileName) def deleteUsableHardwareProfileFromSoftwareProfile(self, session: Session, hardwareProfileName, softwareProfileName): return self._sp_db_api.deleteUsableHardwareProfileFromSoftwareProfile( session, hardwareProfileName, softwareProfileName) def copySoftwareProfile(self, session: Session, srcSoftwareProfileName, dstSoftwareProfileName): validation.validateProfileName(dstSoftwareProfileName) self._logger.info('Copying software profile [%s] to [%s]', srcSoftwareProfileName, dstSoftwareProfileName) self._sp_db_api.copySoftwareProfile(session, srcSoftwareProfileName, dstSoftwareProfileName) # # Fire the tags changed event for all copies that have tags # swp = self.getSoftwareProfile(session, dstSoftwareProfileName) if swp.getTags(): SoftwareProfileTagsChanged.fire(softwareprofile_id=str( swp.getId()), softwareprofile_name=swp.getName(), tags=swp.getTags(), previous_tags={}) def getUsableNodes(self, session: Session, softwareProfileName): return self._sp_db_api.getUsableNodes(session, softwareProfileName) def get_software_profile_metadata(self, session: Session, name: str) -> Dict[str, str]: """ Call action_get_metadata() method for all kits """ self._logger.debug('Retrieving metadata for software profile [%s]', name) metadata: Dict[str, str] = {} for kit in self._kit_db_api.getKitList(session): if kit.getIsOs(): # ignore OS kits continue kit_installer = get_kit_installer( (kit.getName(), kit.getVersion(), kit.getIteration()))() kit_installer.session = session # we are only interested in software profile metadata item = kit_installer.action_get_metadata( software_profile_name=name) if item: metadata.update(item) return metadata
def test_getKitList(dbm): with dbm.session() as session: result = KitDbApi().getKitList(session) assert isinstance(result, TortugaObjectList)