def _install_puppet_module(self, module_name): """ Installs a puppet module from the kit puppet_modules directory. :param module_name: The name of the puppet module to install. """ files = glob.glob( os.path.join(self.kit_installer.puppet_modules_path, '{}-*.tar.gz'.format(module_name))) if not files: logger.error('Unable to find Puppet module {}'.format(module_name)) raise ConfigurationError('Missing Puppet module for Base kit') logger.info('Installing Puppet module {}'.format(module_name)) module_path = files[0] if not os.path.exists(module_path): raise ConfigurationError( 'File does not exist: {}'.format(module_path)) cmd = ('/opt/puppetlabs/bin/puppet module install --color false' ' --force {}'.format(module_path)) tortugaSubprocess.executeCommand(cmd)
def run_script(self, action, software_profiles, nodes=None): script_path = self._get_host_action_hook_script() if script_path is None: return cmd = '{} --action {}'.format(script_path, action) if software_profiles: cmd += ' --software-profiles {}'.format( ','.join(software_profiles)) tmp_file_to_delete = None if nodes: fh, tmp_file_name = self._get_tmp_file() os.write(fh, '\n'.join(nodes)) os.close(fh) cmd += ' --nodes {}'.format(tmp_file_name) tmp_file_to_delete = tmp_file_name tortugaSubprocess.executeCommand(cmd) if tmp_file_to_delete: os.unlink(tmp_file_to_delete)
def _update_python_repo(self, pkg_dir: str): """ Updates the Tortuga Python repo with packages from the kit. :param pkg_dir: the source directory from which the packages will be copied """ # # Copy the files from the pkg_dir to the Tortuga repo # whl_path = os.path.join(pkg_dir, '*.whl') repo_path = os.path.join(self.config_manager.getTortugaIntWebRoot(), 'python-tortuga') cmd = 'rsync -a {} {}'.format(whl_path, repo_path) logger.debug(cmd) executeCommand(cmd) # # Re-build the package index # dir2pi = os.path.join(self.config_manager.getBinDir(), 'dir2pi') cmd = '{} {}'.format(dir2pi, repo_path) logger.debug(cmd) executeCommand(cmd)
def copy_clonezilla_files(self, dst_dir): tmp_dir = self.extract_clonezilla_files() if not os.path.exists(dst_dir): logger.info('Creating destination directory: {}'.format(dst_dir)) os.makedirs(dst_dir) logger.info('Copying Clonezilla files to {}'.format(dst_dir)) # Copy files into place file_pairs = [(os.path.join(tmp_dir, 'vmlinuz'), os.path.join(dst_dir, 'vmlinuz-cz')), (os.path.join(tmp_dir, 'initrd.img'), os.path.join(dst_dir, 'initrd-cz.img')), (os.path.join(tmp_dir, 'filesystem.squashfs'), os.path.join(dst_dir, 'filesystem.squashfs'))] clonezilla_files_path = os.path.join( self.kit_installer.config_manager.getRoot(), 'var/lib/clonezilla_files.txt') with open(clonezilla_files_path, 'w') as fp: for tmp_src_file, tmp_dst_file in file_pairs: shutil.copyfile(tmp_src_file, tmp_dst_file) # # Add entry into file catalog for clean uninstallation later # fp.write(tmp_dst_file + '\n') # # Cleanup the temporary directory # tortugaSubprocess.executeCommand('rm -rf {}'.format(tmp_dir))
def action_post_install(self, *args, **kwargs): self.configure() sshdir = '/root/.ssh' privkey = os.path.join(sshdir, 'id_rsa') pubkey = os.path.join(sshdir, 'id_rsa.pub') authkeys = os.path.join(sshdir, 'authorized_keys') if not os.path.exists(sshdir): os.makedirs(sshdir, 0o700) # # Create public key if key not found # if not os.path.exists(pubkey): # # RSA key, 2048 bits in size, /root/.ssh/id_rsa, no passphrase # tortugaSubprocess.executeCommand( 'ssh-keygen -t rsa -b 2048 -f {} -N ""'.format(privkey)) # # copy public key to authorized_keys # if not os.path.exists(authkeys): shutil.copy(pubkey, authkeys)
def action_post_uninstall(self, *args, **kwargs): super().action_post_install(*args, **kwargs) # # Remove symlink to modules # tortugaSubprocess.executeCommand( '/bin/rm -f {}/lib/tortuga/vmwareUtil'.format(self.getRoot())) # # Delete the hardware and software profiles we created in # post_install # hardware_profile_api = getHardwareProfileApi() software_profile_api = getSoftwareProfileApi() for hwp_name in DEFAULT_HARDWARE_PROFILE_LIST: try: hardware_profile_api.deleteHardwareProfile(hwp_name) except HardwareProfileNotFound: # # We can safely ignore this error # pass for swp_name in DEFAULT_SOFTWARE_PROFILE_LIST: try: software_profile_api.deleteSoftwareProfile(swp_name) except SoftwareProfileNotFound: # # We can safely ignore this error # pass
def extract_clonezilla_files(self): """ Extracts requisite files from Clonezilla Live zip archive Returns: <path to temporary directory containing files> Raises: InvalidActionRequest CommandFailed """ if not os.path.exists(SRC_CLONEZILLA_LIVE_ZIP): raise invalidActionRequest.InvalidActionRequest( 'Clonezilla Live zip archive not found: {}'.format( SRC_CLONEZILLA_LIVE_ZIP)) tmp_dir = tempfile.mkdtemp() logger.info('Extracting Clonezilla Live ZIP to {}'.format(tmp_dir)) reqd_files = [ 'live/vmlinuz', 'live/initrd.img', 'live/filesystem.squashfs' ] cmd = 'unzip -j -o {} -d {} {}'.format(SRC_CLONEZILLA_LIVE_ZIP, tmp_dir, ' '.join(reqd_files)) tortugaSubprocess.executeCommand(cmd) return tmp_dir
def _install_puppet_module(self, module_name): """ Installs a puppet module from the kit puppet_modules directory. :param module_name: The name of the puppet module to install. """ files = glob.glob( os.path.join( self.kit_installer.puppet_modules_path, '{}-*.tar.gz'.format(module_name) ) ) if not files: errmsg = f'Unable to find Puppet module {module_name}' logger.error(errmsg) raise ConfigurationError(errmsg) logger.info('Installing Puppet module {}'.format(module_name)) cmd = ('/opt/puppetlabs/bin/puppet module install --color false' ' {}'.format(files[0])) tortugaSubprocess.executeCommand(cmd)
def reload(cls, force=False): reload_file_path = os.path.join(cls.hostsdir, cls.reload_flag) if not force and not os.path.exists(reload_file_path): logger.debug('Reload flag not found, skipping dnsmasq reload') cmd = 'systemctl kill -s HUP dnsmasq.service' tortugaSubprocess.executeCommand(cmd) logger.debug('dnsmasq service reloaded') if os.path.exists(reload_file_path): os.remove(reload_file_path)
def _updateNetworkConfig(self, session: Session, dbNode: Node) -> bool: """ Returns True if configuration files were changed. """ bUpdated = False if self._componentEnabled(session, dbNode.softwareprofile, 'dhcpd'): print('Updating dhcpd configuration...') if dbNode.hardwareprofile.nics: print('Restarting dhcpd...') tortugaSubprocess.executeCommand('genconfig dhcpd') tortugaSubprocess.executeCommand('service dhcpd restart') else: print('Last provisioning NIC removed. Stopping dhcpd...') tortugaSubprocess.executeCommand('service dhcpd stop') bUpdated = True if self._componentEnabled(session, dbNode.softwareprofile, 'dns'): print('Updating DNS configuration...') tortugaSubprocess.executeCommand('genconfig dns') bUpdated = True return bUpdated
def _run_command(self, command): """ Runs an external command. :param command: string the command to run :raises KitBuildError: if the command failed """ logger.info(' {}'.format(command)) try: executeCommand(command) except CommandFailed as e: raise KitBuildError(str(e))
def execute(self, cmd, echo: bool = False): \ # pylint: disable=unused-argument """ Raises: TortugaException """ return tortugaSubprocess.executeCommand(cmd)
def create(self, relativePath, cacheDir=None): repoPath = self.__getRepoPath(relativePath) if not os.path.exists(repoPath): os.makedirs(repoPath) # Make sure nothing happens here for non-native os. if not osUtility.isNativeOsName(self.getOsInfo()): return # Build 'createrepo' command-line cmd = 'createrepo -q' if cacheDir is not None: cmd += ' --cachedir %s' % (cacheDir) cmd += ' %s' % (repoPath) tortugaSubprocess.executeCommand(cmd)
def pre_init_db(self): # If using 'mysql' as the database backend, we need to install the # puppetlabs-mysql Puppet module prior to bootstrapping. This used to # be done in 'install-tortuga.sh' if self._settings['database']['engine'] == 'mysql': logmsg = 'Installing \'puppetlabs-mysql\' module' self._logger.debug(logmsg) sys.stdout.write('\n' + logmsg + '... ') sys.stdout.flush() cmd = ('/opt/puppetlabs/bin/puppet module install' ' --color false puppetlabs-mysql') tortugaSubprocess.executeCommand(cmd) sys.stdout.write('done.\n')
def cleanup(self): files_to_delete = [] clonezilla_files_path = os.path.join( self.kit_installer.config_manager.getRoot(), 'var/lib/clonezilla_files.txt') if os.path.exists(clonezilla_files_path): with open( os.path.join(self.kit_installer.config_manager.getRoot(), 'lib/clonezilla_files.txt')) as fp: for line in fp.readlines(): files_to_delete.append(line.rstrip()) for tmp_file_name in files_to_delete: tortugaSubprocess.executeCommand('rm -f {}'.format(tmp_file_name)) tortugaSubprocess.executeCommand( 'rm -f %s'.format(clonezilla_files_path))
def execute(self, cmd, echo=False): """ Raises: TortugaException """ if echo: return tortugaSubprocess.executeCommandAndLogToStdOut(cmd) else: return tortugaSubprocess.executeCommand(cmd)
def installPuppetModule(self, modulePath): """ Install "standard" Puppet module using "puppet module install --force" Raises: ConfigurationError """ if not os.path.exists(modulePath): errmsg = ('Error: unable to install puppet module [%s].' ' Module does not exist' % (modulePath)) self.getLogger().error(errmsg) raise ConfigurationError(errmsg) cmd = ('/opt/puppetlabs/bin/puppet module install --color false' ' --force %s' % (modulePath)) tortugaSubprocess.executeCommand(cmd)
def pre_init_db(self): # If using 'mysql' as the database backend, we need to install the # puppetlabs-mysql Puppet module prior to bootstrapping. This used # to be done in 'install-tortuga.sh' if self._settings['database']['engine'] == 'mysql': print('\nUsing MySQL as backing database.') puppet_module = 'puppetlabs-mysql' logmsg = f'Installing \'{puppet_module}\' module' self._logger.debug(logmsg) print(f'\n{logmsg}...', end='') cmd = ('/opt/puppetlabs/bin/puppet module install' f' --color false {puppet_module}') tortugaSubprocess.executeCommand(cmd) print('done.')
def action_delete_host(self, hardware_profile_name, software_profile_name, nodes, *args, **kwargs): for node in nodes: short_host_name = node.getName().split('.')[0] # # Remove ssh keymapping for node # logger.debug('Removing ssh public key for node {}'.format(node)) # # Remove fullname # tortugaSubprocess.executeCommand( 'ssh-keygen -R {} >/dev/null 2>&1 ||:'.format(node)) # Remove short name tortugaSubprocess.executeCommand( 'ssh-keygen -R {} >/dev/null 2>&1 ||:'.format(short_host_name)) # # Remove nic entries # for nic in node.getNics(): tortugaSubprocess.executeCommand( 'ssh-keygen -R {} >/dev/null 2>&1 ||:'.format(nic.getIp())) self.configure()
def _get_navops_token(self) -> str: cmd = '{} token'.format(self.navops_cli) try: p = executeCommand(cmd) except Exception as ex: logger.info(str(ex)) raise ConfigException(str(ex)) if p.getExitStatus() != 0: raise ConfigException(p.getStdErr()) return p.getStdOut().decode()
def runCommand(self): self.parseArgs() self._loadDNSConfig() # Remove remnants oldDnsZone = self._getOldDnsZone() if not self.getArgs().zone: # Output current DNS zone and exit print(f'{oldDnsZone}') sys.exit(0) dnsZone = self.getArgs().zone.lower() if oldDnsZone == dnsZone and not self.getArgs().bForce: # Nothing changed. Nothing to do! sys.exit(0) # Update database self._updateDatabase(dnsZone) # Update dns-component.conf self._updateDnsComponentConf(dnsZone) # Update Puppet extdata file self._updatePuppetExtData(dnsZone) if 'type' in self.dns_conf and self.dns_conf['type'] == 'named': oldZoneFileName = '/var/named/%s.zone' % (oldDnsZone.lower()) if os.path.exists(oldZoneFileName): # Attempt to remove old named configuration os.unlink(oldZoneFileName) bDnsComponentEnabled = self.isDnsComponentEnabled() # TODO: update (genconfig dns) if bDnsComponentEnabled: tortugaSubprocess.executeCommand('genconfig dns') # TODO: schedule puppet update tortugaSubprocess.executeCommand( 'schedule-update "DNS zone changed from \"%s\" to \"%s\""' % (oldDnsZone, dnsZone)) if bDnsComponentEnabled and 'type' in self.dns_conf: if self.dns_conf['type'] == 'named': cmd = 'service named restart' else: cmd = 'service dnsmasq restart' tortugaSubprocess.executeCommand(cmd)
def getInitiatorName(self, target): '''Gets the iSCSI Initiator name of the specified target host/IP''' cmd = 'ssh root@%s cat /etc/iscsi/initiatorname.iscsi' % (target) stdout = executeCommand(cmd).getStdOut() pattern = re.compile(r'^\s*InitiatorName=["\']?([^"\' ]+)[^\'"]?', re.M) match = pattern.search(stdout) if not match: raise InternalError('Target [%s] unknown iSCSI initiator name' % (target)) initiator = match.group(1).strip() self.getLogger().debug('Target [%s] iSCSI initiator name: [%s]' % (target, initiator)) return initiator
def ping_all_nodes(self) -> List[NodePingReply]: p = executeCommand(self._command) replies: List[NodePingReply] = [] for line in p.getStdOut().decode().splitlines(): # # A typical ping response from the mco ping command looks like # this: # # execd-01-hioqz time=52.88 ms # parts = re.split(r"\s+", line.strip()) if len(parts) != 3: continue if not parts[1].startswith("time="): continue replies.append( NodePingReply(name=parts[0], reply=True, response_time=float(parts[1].replace( "time=", "")))) return replies
def _updateNetworkConfig(self, session, dbInstallerNode): """ Returns True if configuration files were changed. """ bUpdated = False bin_dir = os.path.dirname(sys.argv[0]) # Update dhcpd configuration if self._componentEnabled(session, dbInstallerNode.softwareprofile, 'dhcpd'): print('Updating dhcpd configuration...') tortugaSubprocess.executeCommand('{} {}'.format( os.path.join(bin_dir, 'genconfig'), 'dhcpd')) tortugaSubprocess.executeCommand('service dhcpd restart') bUpdated = True # Update DNS configuration after adding a provisioning NIC if self._componentEnabled(session, dbInstallerNode.softwareprofile, 'dns'): print('Updating DNS configuration...') tortugaSubprocess.executeCommand('{} {}'.format( os.path.join(bin_dir, 'genconfig'), 'dns')) # Because the entire configuration changes between before and # after there was a provisioning NIC installed, it is necessary # to restart the server. An 'rndc reload' will *NOT* suffice. tortugaSubprocess.executeCommandAndIgnoreFailure( 'service named restart') bUpdated = True return bUpdated
def cleanup(self): # If possible, remove any package sources we added self._removePackageSources() osUtility.removeFile(self._lockFilePath) osUtility.removeFile(self._cm.getProfileNiiFile()) # Turn off the webservice daemon self._disableTortugaws() # Restore resolv.conf if osUtility.haveBackupFile('/etc/resolv.conf'): osUtility.restoreFile('/etc/resolv.conf') # Drop database dbManager = self._osObjectFactory.getOsApplicationManager( self._settings['database']['engine']) try: dbSchema = self._cm.getDbSchema() self.out(' * Removing database [%s]\n' % (dbSchema)) dbManager.destroyDb(dbSchema) except Exception as ex: # pylint: disable=broad-except self._logger.exception( 'Could not destroy existing db: {}'.format(ex)) # Remove DB password file osUtility.removeFile(self._cm.getDbPasswordFile()) # Remove CFM secret cfmSecretFile = self._cm.getCfmSecretFile() if os.path.exists(cfmSecretFile): osUtility.removeFile(self._cm.getCfmSecretFile()) # Generic cleanup osUtility.removeLink('/etc/tortuga-release') # Cleanup or remove depot directory errmsg = 'Removing contents of [%s]' % (self._settings['depotpath']) self._logger.debug(errmsg) if self._depotCreated: self.out(' * %s\n' % (errmsg)) osUtility.removeDir(self._settings['depotpath']) else: if self._settings['depotpath']: self.out(' * %s\n' % (errmsg)) tortugaSubprocess.executeCommand( 'rm -rf %s/*' % (self._settings['depotpath'])) self.out('\n') if not self._forceCleaning: self.out('Consult log(s) for further details.\n') self._logger.error('Installation failed')
def prepDepot(self): depotpath = None if not self._settings['defaults']: self.out( _('Tortuga requires a directory for storage of OS' ' distribution media and other files required for' ' node provisioning.\n\n')) while not depotpath: if self._settings['defaults']: response = self._settings['depotpath'] else: try: response = input( 'Please enter a depot path (Ctrl-C to interrupt)' ' [%s]: ' % (self._settings['depotpath'])) except KeyboardInterrupt: raise InvalidArgument(_('Aborted by user.')) if not response: response = self._settings['depotpath'] if not response.startswith('/'): errmsg = 'Depot path must be fully-qualified' if not self._settings['defaults']: self.out('Error: %s\n' % (errmsg)) continue raise InvalidArgument(errmsg) if response == '/': errmsg = 'Depot path cannot be system root directory' if not self._settings['defaults']: self.out(_('Error: %s\n' % (errmsg))) continue raise InvalidArgument(errmsg) if os.path.exists(response): if not self._settings['force']: if not self._settings['defaults']: self.out( _('Directory [%s] already exists. Do you wish to' ' remove it [N/y]? ') % (response)) remove_response = input('') if not remove_response or \ remove_response[0].lower() == 'n': continue_response = input( 'Do you wish to continue [N/y]? ') if continue_response and \ continue_response[0].lower() == 'y': continue raise InvalidArgument(_('Aborted by user.')) else: raise InvalidArgument( _('Existing depot directory [%s] will not be' ' removed.') % (response)) else: self.out( _('\nRemoving existing depot directory [%s]... ') % ( response)) depotpath = response tortugaSubprocess.executeCommand( 'rm -rf %s/*' % (depotpath)) self.out(_('done.\n')) else: depotpath = response self._settings['depotpath'] = depotpath self._cm.setDepotDir(self._settings['depotpath'])
def __process(self): self._logger.debug('[%s] Begin processing timer' % (self.__class__.__name__)) while True: qSize = self._receiveQ.qsize() self._logger.debug('[%s] Current receive Q size: %s' % (self.__class__.__name__, qSize)) if qSize == 0: break applicationName, applicationData = self._receiveQ.get() self._logger.debug('[%s] Processing data for [%s]' % (self.__class__.__name__, applicationName)) monitorXmlDoc = self.__parseMonitorData(applicationData) for ruleId in self._receiveRuleDict.keys(): rule = self._receiveRuleDict.get(ruleId) # Rule might have been cancelled before we use it. if not rule: continue # Check if this is appropriate for the data. if rule.getApplicationName() != applicationName: continue self._logger.debug('[%s] Processing data using rule [%s]' % (self.__class__.__name__, ruleId)) rule.ruleInvoked() appMonitor = rule.getApplicationMonitor() actionCmd = appMonitor.getActionCommand() self._logger.debug('[%s] Action command: [%s]' % (self.__class__.__name__, actionCmd)) try: xPathReplacementDict = self.__evaluateXPathVariables( monitorXmlDoc, rule.getXPathVariableList()) invokeAction = self.__evaluateConditions( rule, monitorXmlDoc, xPathReplacementDict) if invokeAction: try: actionCmd = self.__replaceXPathVariables( actionCmd, xPathReplacementDict) self._logger.debug( '[%s] About to invoke: [%s]' % (self.__class__.__name__, actionCmd)) tortugaSubprocess.executeCommand( 'source %s/tortuga.sh && ' % (self._cm.getEtcDir()) + actionCmd) appMonitor.actionInvocationSucceeded() self._logger.debug( '[%s] Done with command: [%s]' % (self.__class__.__name__, actionCmd)) maxActionInvocations = \ appMonitor.getMaxActionInvocations() successfulActionInvocations = \ appMonitor.getSuccessfulActionInvocations() if maxActionInvocations: if int(maxActionInvocations) <= \ successfulActionInvocations: # Rule must be disabled. self._logger.debug( '[%s] Max. number of successful' ' invocations (%s) reached for' ' rule [%s]' % (self.__class__.__name__, maxActionInvocations, ruleId)) self.disableRule(rule.getApplicationName(), rule.getName()) except Exception as ex: appMonitor.actionInvocationFailed() else: self._logger.debug( '[%s] Will skip action: [%s]' % (self.__class__.__name__, actionCmd)) except TortugaException as ex: self._logger.error('[%s] %s' % (self.__class__.__name__, ex)) self._logger.debug('[%s] No more rules appropriate for [%s]' % (self.__class__.__name__, applicationName)) # No more data to process, exit timer. self._logger.debug('[%s] No more data to process' % (self.__class__.__name__)) self.__cancelProcessingTimer()
def __execute(self, rule): ruleId = self.__getRuleId(rule.getApplicationName(), rule.getName()) self._logger.debug('[%s] Begin execution for [%s]' % (self.__class__.__name__, ruleId)) rule.ruleInvoked() appMonitor = rule.getApplicationMonitor() queryCmd = appMonitor.getQueryCommand() self._logger.debug('[%s] Query command: [%s]' % (self.__class__.__name__, queryCmd)) actionCmd = appMonitor.getActionCommand() self._logger.debug('[%s] Action command: [%s]' % (self.__class__.__name__, actionCmd)) xPathReplacementDict = {} try: invokeAction = True queryStdOut = None if queryCmd: self._logger.debug('[%s] About to invoke: [%s]' % (self.__class__.__name__, queryCmd)) try: p = tortugaSubprocess.executeCommand( 'source %s/tortuga.sh && ' % (self._cm.getEtcDir()) + queryCmd) queryStdOut = p.getStdOut() appMonitor.queryInvocationSucceeded() except Exception as ex: appMonitor.queryInvocationFailed() raise monitorXmlDoc = self.__parseMonitorData(queryStdOut) xPathReplacementDict = self.__evaluateXPathVariables( monitorXmlDoc, rule.getXPathVariableList()) invokeAction = self.__evaluateConditions( rule, monitorXmlDoc, xPathReplacementDict) if invokeAction: try: actionCmd = self.__replaceXPathVariables( actionCmd, xPathReplacementDict) self._logger.debug('[%s] About to invoke: [%s]' % (self.__class__.__name__, actionCmd)) p = tortugaSubprocess.executeCommand( 'source %s/tortuga.sh && ' % (self._cm.getEtcDir()) + actionCmd) appMonitor.actionInvocationSucceeded() self._logger.debug('[%s] Done with command: [%s]' % (self.__class__.__name__, actionCmd)) except Exception as ex: appMonitor.actionInvocationFailed() raise else: self._logger.debug('[%s] Will skip action: [%s]' % (self.__class__.__name__, actionCmd)) except TortugaException as ex: self._logger.error('[%s] %s' % (self.__class__.__name__, ex)) if self.hasRule(ruleId): # Check if we need to stop invoking this rule. maxActionInvocations = appMonitor.getMaxActionInvocations() successfulActionInvocations = \ appMonitor.getSuccessfulActionInvocations() if maxActionInvocations: if int(maxActionInvocations) <= successfulActionInvocations: # Rule must be disabled. self._logger.debug( '[%s] Max. number of successful invocations (%s)' ' reached for rule [%s]' % (self.__class__.__name__, maxActionInvocations, ruleId)) self.disableRule(rule.getApplicationName(), rule.getName())
def action_post_uninstall(self, *args, **kwargs): tortugaSubprocess.executeCommand( 'rm -rf /var/cache/tortuga/pkgs/ganglia')
def __scheduleUpdate(self): tortugaSubprocess.executeCommand( os.path.join(self._cm.getRoot(), 'bin/schedule-update'))