def runCommand(self): self.parseArgs( _(""" Associates an existing adminstrative user with a hardware or software profile. """)) swprofile = self.getArgs().swprofile hwprofile = self.getArgs().hwprofile if swprofile and hwprofile: raise InvalidCliRequest( _('Only one of --software-profile and --hardware-profile' ' can be specified.')) if not swprofile and not hwprofile: raise InvalidCliRequest( _('Either --software-profile or --hardware-profile must' ' be specified.')) admin_username = self.getArgs().adminUsername if admin_username is None: raise InvalidCliRequest(_('Missing Admin Username')) if swprofile: profile = swprofile api = self.configureClient(SoftwareProfileWsApi) else: profile = hwprofile api = self.configureClient(HardwareProfileWsApi) api.addAdmin(profile, admin_username)
def runCommand(self): self.parseArgs(_(""" post-application-data --app-name=APPLICATIONNAME --data-file=DATAFILE Description: The post-application-data tool posts an XML file to the Tortuga Rule Engine web service as input for configured rules. """)) application_name = self.getArgs().applicationName if not application_name: raise InvalidCliRequest(_('Missing application name.')) data_file = self.getArgs().dataFile if not data_file: raise InvalidCliRequest(_('Missing application data file.')) if not os.path.exists(data_file): raise FileNotFound(_('Invalid application data file: %s.') % data_file) f = open(data_file, 'r') application_data = f.read() f.close() if not len(application_data): raise InvalidCliRequest(_('Empty application data file.')) self.get_rule_api().postApplicationData(application_name, application_data)
def getAdminUsernameAndPassword(self): adminUsername = self.getArgs().adminUsername adminPassword = self.getArgs().adminPassword if not adminUsername: raise InvalidCliRequest(_('Missing Admin User Name.')) if not adminPassword: raise InvalidCliRequest(_('Missing Admin Password.')) return adminUsername, adminPassword
def getApplicationNameAndRuleName(self): applicationName = self._options.applicationName ruleName = self._options.ruleName if not applicationName: raise InvalidCliRequest(_('Missing application name.')) if not ruleName: raise InvalidCliRequest(_('Missing rule name.')) return applicationName, ruleName
def runCommand(self): self.parseArgs(''' Marks active node idle which will cause varying actions based on the resource adapter associated with the given node. ''') # The "--node nodeName" option was implemented first # and we maintain backwards compatability for it. # The "nodeName..." arguments were added later. node_api = NodeWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) try: results = node_api.idleNode(self.getArgs().nodeName) if results['NodeAlreadyIdle']: print(_('The following node(s) are already in idle state:')) print('\n'.join(results['NodeAlreadyIdle'])) if results['NodeSoftwareProfileLocked']: print( _('The following node(s) are locked and cannot be idled:')) print('\n'.join(results['NodeSoftwareProfileLocked'])) except Exception as msg: raise InvalidCliRequest(_("Can't idle node(s): {}").format(msg))
def runCommand(self): self.parseArgs( _(""" execute-rule --app-name=APPNAME --rule-name=RULENAME [--data-file=DATAFILE] Description: The execute-rule tool forces execution of a given rule in the Tortuga Rule Engine. """)) application_name, rule_name = self.getApplicationNameAndRuleName() data_file = self.getOptions().dataFile application_data = '' if data_file: if not os.path.exists(data_file): raise FileNotFound( _('Invalid application data file: {}').format(data_file)) else: f = open(data_file, 'r') application_data = f.read() f.close() if not len(application_data): raise InvalidCliRequest(_('Empty application data file.')) self.get_rule_api().executeRule(application_name, rule_name, application_data) print(_('Executed rule {}/{}').format(application_name, rule_name))
def getNetworkFromXml(self): """ If the xmlFile option is present attempt to create a Network object from the xml. Otherwise return None """ network = None if self.getArgs().xmlFile: # An XML file was provided as input...start with that... f = open(self.getArgs().xmlFile, 'r') try: xmlString = f.read() finally: f.close() try: from tortuga.objects.network import Network network = Network.getFromXml(xmlString) except Exception as ex: # pylint: disable=W0703 self.getLogger().debug('Error parsing xml %s' % ex) if network is None: raise InvalidCliRequest( _('File [%s] does not contain a valid network.') % (self.getArgs().xmlFile)) return network
def runCommand(self): self.parseArgs(_('Remove network from the system')) # Parse --network argument if it exists networkAddress, networkSubnet = self.parseNetworkParameter( self.getArgs().network) # If we don't have a network and a netmask its an error if networkAddress is not None and networkSubnet is not None: network = self.getNetworkApi().getNetwork( networkAddress, networkSubnet) else: raise InvalidCliRequest( _('--network argument must be specified.')) # Delete the network try: self.getNetworkApi().deleteNetwork(network.getId()) except DeleteNetworkFailed as ex: raise NetworkInUse( _('Unable to delete network [{0}].\nReason: {1}').format( network, ex)) except NetworkInUse: raise NetworkInUse( _('Network [{0}] contains nodes. It cannot be' ' deleted.').format(network))
def runCommand(self): self.parseArgs() options = self.getArgs() if options.state and (options.bNotInstalled or options.bInstalled): raise InvalidCliRequest( '--state and --installed/--not-installed arguments are' ' mutually exclusive') api = NodeWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) nodes: List[Dict[str, Any]] = [ dict(x) for x in api.getNodeList(nodespec=options.nodeName, tags=options.tags) ] if not nodes: if options.nodeName: print('No nodes matching nodespec [{}]\n'.format( options.nodeName)) sys.exit(1) if options.bInstalled: nodes = self.__filter_nodes(nodes, 'state', 'Installed') if options.bNotInstalled: nodes = self.__filter_nodes(nodes, 'state', 'Installed', True) if options.state: nodes = self.__filter_nodes(nodes, 'state', options.state) if options.softwareProfile: nodes = self.__filter_nodes(nodes, ['softwareprofile', 'name'], options.softwareProfile) if options.hardwareProfile: nodes = self.__filter_nodes(nodes, ['hardwareprofile', 'name'], options.hardwareProfile) if not options.showAll: nodes = self.__filter_nodes(nodes, 'state', 'Deleted', True) grouped: Dict[str, List[Dict[str, Any]]] = self.__group_nodes( nodes, options.bByHardwareProfile) if options.bShortOutput: output: Optional[str] = self.__make_short_output(grouped) elif options.bListOutput: output: Optional[str] = self.__make_list_output(nodes) else: output: Optional[str] = self.__make_full_output(grouped) if output: print(output)
def runCommand(self): self.parseArgs(_('Shuts down the given node')) try: # Perform a "soft" shutdown NodeWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify).shutdownNode( self.getArgs().nodeName, True) except Exception as msg: raise InvalidCliRequest(_("Can't shutdown node(s) - %s") % (msg))
def runCommand(self): self.parseArgs(_('Shuts down the given node')) api = self.configureClient(NodeWsApi) try: # Perform a "soft" shutdown api.shutdownNode(self.getArgs().nodeName, True) except Exception as msg: raise InvalidCliRequest( _("Can't shutdown node(s) - %s") % (msg))
def validateNetwork(self, network): # pylint: disable=no-self-use """ Verify a network object has the minimum populated fields needed to add it to the database """ if not network.getAddress(): raise InvalidCliRequest(_('Network address must be specified.')) if not network.getNetmask(): raise InvalidCliRequest(_('Subnet mask must be specified.')) if not network.getType(): raise InvalidCliRequest(_('Network type must be specified.')) if network.getUsingDhcp() is None: raise InvalidCliRequest( _('Address allocation must be specified as DHCP or' ' static.')) if network.getIncrement(): increment = network.getIncrement() try: value = int(increment) if value < 1: raise InvalidCliRequest(_('Increment must be positive.')) except ValueError: raise InvalidCliRequest( _('Increment must be a positive integer.'))
def __call__(self, parser, namespace, values, option_string=None): osValues = values.split('-', 3) if len(osValues) != 3: raise InvalidCliRequest( _('Error: Incorrect operating system specification.' '\n\n--os argument should be in' ' OSNAME-OSVERSION-OSARCH format')) name = osValues[0] version = osValues[1] arch = osValues[2] setattr(namespace, 'osInfo', OsInfo(name, version, arch))
def runCommand(self): self.parseArgs( _(""" Associates an existing adminstrative user with a hardware or software profile. """)) swprofile = self.getArgs().swprofile hwprofile = self.getArgs().hwprofile if swprofile and hwprofile: raise InvalidCliRequest( _('Only one of --software-profile and --hardware-profile' ' can be specified.')) if not swprofile and not hwprofile: raise InvalidCliRequest( _('Either --software-profile or --hardware-profile must' ' be specified.')) admin_username = self.getArgs().adminUsername if admin_username is None: raise InvalidCliRequest(_('Missing Admin Username')) if swprofile: profile = swprofile api = SoftwareProfileWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) else: profile = hwprofile api = HardwareProfileWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) api.addAdmin(profile, admin_username)
def runCommand(self): self.parseArgs( _(""" add-rule --desc-file=DESCRIPTIONFILE Description: The add-rule tool adds a rule to the Tortuga Simple Policy Engine. """)) if not self.getOptions().descriptionFile: raise InvalidCliRequest(_('Missing required --desc-file argument')) parser = RuleObjectFactory().getParser() rule = parser.parse(self.getOptions().descriptionFile) self.get_rule_api().addRule(rule)
def assertIp(self, ip, parameterName, errorMsg=None): \ # pylint: disable=no-self-use """ Convienience function for testing IPs and raising a configurable exception if the IP is invalid. """ if errorMsg is None: errorMsg = _('The %s parameter must be a valid IP address.') % ( parameterName) try: ipaddress.IPv4Address(str(ip)) except ipaddress.AddressValueError: raise InvalidCliRequest(errorMsg)
def parseNetworkParameter(self, network): \ # pylint: disable=no-self-use """ Validator for the --network parameter. """ try: result = ipaddress.IPv4Network(str(network)) except ipaddress.AddressValueError: # Invalid argument to --network specified raise InvalidCliRequest( _('--network argument must be formatted as ' ' XXX.XXX.XXX.XXX/YY or XXX.XXX.XXX.XXX/YYY.YYY.YYY.YYY')) return result.network_address.exploded, result.netmask.exploded
def runCommand(self): self.parseArgs() # Turn user input into a list destinationList = [ node.strip() for node in self.getArgs().destinationString.split(',') ] if self.getArgs().destinationString else [] api = self.configureClient(NodeWsApi) try: api.startupNode(self.getArgs().nodeName, destinationList, self.getArgs().bootMethod) except Exception as msg: raise InvalidCliRequest(_("Unable to start node(s) - %s") % (msg))
def runCommand(self): self.parseArgs(_('Get details of a specific network.')) # Parse --network parameter if it exists networkAddress, networkMask = self.parseNetworkParameter( self.getArgs().network) # If we don't have a network and a netmask its an error if networkAddress is not None and networkMask is not None: network = self.getNetworkApi().getNetwork( networkAddress, networkMask) else: raise InvalidCliRequest( _('The --network parameter must be specified.')) # TODO: do not output XML print(network.getXmlRep())
def runCommand(self): self._logger.info('=' * 75) self._logger.info('Installation started') self._logger.info('=' * 75) self.parseArgs(_(""" tortuga-setup is run after the installation of the Tortuga software in order to configure the base cluster environment. """)) self._logger.info('command-line args: %s' % (' '.join(sys.argv[1:]))) if self.getArgs().responseFile: if not os.path.exists(self.getArgs().responseFile): raise InvalidCliRequest( 'Response file [%s] does not exist' % ( self.getArgs().responseFile)) tortuga_cfg = {} if self.getArgs().responseFile: tortuga_cfg['inifile'] = self.getArgs().responseFile tortuga_cfg['acceptEula'] = self.getArgs().acceptEula tortuga_cfg['force'] = self.getArgs().force tortuga_cfg['defaults'] = self.getArgs().defaults tortuga_cfg['consoleLogLevel'] = self.getArgs().consoleLogLevel if self.getArgs().skip_kits: tortuga_cfg['skip_kits'] = self.getArgs().skip_kits try: tortugaDeployer.TortugaDeployer( self._logger, cmdline_options=tortuga_cfg).runSetup() self._logger.info('=' * 75) self._logger.info('Installation completed successfully') self._logger.info('=' * 75) except TortugaException: self._logger.info('=' * 75) self._logger.info('Installation failed') self._logger.info('=' * 75) raise
def runCommand(self): self.parseArgs() # Turn user input into a list destinationList = [ node.strip() for node in self.getArgs().destinationString.split(',') ] if self.getArgs().destinationString else [] try: NodeWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify).startupNode( self.getArgs().nodeName, destinationList, self.getArgs().bootMethod) except Exception as msg: raise InvalidCliRequest(_("Unable to start node(s) - %s") % (msg))
def runCommand(self): self.parseArgs( _(""" Reboots specified node(s). Mark nodes for reinstallation if --reinstall flag is specified. """)) nodeApi = self.configureClient(NodeWsApi) # If the node is being reinstalled as a result of the reboot, # do not use a soft shutdown. bSoftReboot = not self.getArgs().bReinstall try: nodeApi.rebootNode(self.getArgs().nodeSpec, bSoftReboot, bReinstall=self.getArgs().bReinstall) except Exception as msg: raise InvalidCliRequest(_("Can't reboot node(s) - %s") % (msg))
def runCommand(self): self.parseArgs( _(""" Reboots specified node(s). Mark nodes for reinstallation if --reinstall flag is specified. """)) nodeApi = NodeWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) # If the node is being reinstalled as a result of the reboot, # do not use a soft shutdown. bSoftReboot = not self.getArgs().bReinstall try: nodeApi.rebootNode(self.getArgs().nodeSpec, bSoftReboot, bReinstall=self.getArgs().bReinstall) except Exception as msg: raise InvalidCliRequest(_("Can't reboot node(s) - %s") % (msg))
def load_software_profile_template(self, tmplpath: str) -> dict: """ Raises: InvalidProfileCreationTemplate InvalidCliRequest """ # load from template if tmplpath and not os.path.exists(tmplpath): raise InvalidCliRequest( _('Cannot read software profile template [%s]') % (tmplpath)) try: with open(tmplpath) as fp: result = json.load(fp) if 'softwareprofile' not in result: raise InvalidProfileCreationTemplate( 'Missing \"softwareprofile\" envelope') return result['softwareprofile'] except Exception as exc: raise InvalidProfileCreationTemplate( 'Invalid software profile template: {}'.format(exc))
def runCommand(self): self.parseArgs(usage=_(""" Updates software profile in the Tortuga system. """)) if not self.getArgs().name and \ not self.getArgs().softwareProfileName: self.getParser().error( 'the following arguments are required: NAME' ) if self.getArgs().name and self.getArgs().softwareProfileName: self.getParser().error( 'argument name: not allowed with argument --name' ) name = self.getArgs().name \ if self.getArgs().name else self.getArgs().softwareProfileName api = SoftwareProfileWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) sp = api.getSoftwareProfile(name, UpdateSoftwareProfileCli.optionDict) if self.getArgs().new_name is not None: sp.setName(self.getArgs().new_name) if self.getArgs().description is not None: sp.setDescription(self.getArgs().description) if self.getArgs().kernel is not None: sp.setKernel(self.getArgs().kernel) if self.getArgs().kernelParameters is not None: sp.setKernelParams(self.getArgs().kernelParameters) if self.getArgs().initrd is not None: sp.setInitrd(self.getArgs().initrd) if self.getArgs().unlock: if self.getArgs().soft_locked is not None: raise InvalidCliRequest( '--soft-locked/--hard-locked arguments and --unlock' ' argument are mutually exclusive' ) sp.setLockedState('Unlocked') if self.getArgs().soft_locked is not None: sp.setLockedState( 'SoftLocked' if self.getArgs().soft_locked else 'HardLocked') if self.getArgs().min_nodes is not None: # update min_nodes value try: if self.getArgs().min_nodes.lower() == 'none': min_nodes = -1 else: min_nodes = int(self.getArgs().min_nodes) except ValueError: raise InvalidCliRequest( 'Invalid argument value for --min-nodes') sp.setMinNodes(min_nodes) else: min_nodes = sp.getMinNodes() if self.getArgs().max_nodes: try: max_nodes = -1 \ if self.getArgs().max_nodes.lower() == 'none' else \ int(self.getArgs().max_nodes) except ValueError: raise InvalidCliRequest( 'Invalid argument value for --max-nodes' ) # update maxNodes value if max_nodes < min_nodes: # do not allow max nodes to be less than min nodes raise InvalidCliRequest( 'Maximum number of allowed nodes must be greater or equal' ' to the mininum number of required nodes' ) sp.setMaxNodes(max_nodes) if self.getArgs().deletePartition is not None: out = TortugaObjectList() for p in sp.getPartitions(): for dp in self.getArgs().deletePartition: if dp == p.getName(): # Skip over this item..its getting deleted break else: # Not a partition we are deleting out.append(p) sp.setPartitions(out) partitionObject = None if self.getArgs().updatePartition: if self.getArgs().addPartition: raise InvalidCliRequest( _('Must provide only one of --update-partition and' ' --add-partition')) for p in sp.getPartitions(): if p.getName() == self.getArgs().updatePartition: partitionObject = p break else: raise InvalidCliRequest( _('Cannot update non-existent partition "%s"') % ( self.getArgs().updatePartition)) if self.getArgs().addPartition: from tortuga.objects.partition import Partition partitionObject = Partition() partitionObject.setName(self.getArgs().addPartition) sp.getPartitions().append(partitionObject) if self.getArgs().device is None or \ self.getArgs().fileSystem is None or \ self.getArgs().size is None: raise InvalidCliRequest( _('--device, --file-system, and --size options required' ' with --add-partition')) if self.getArgs().grow: if not partitionObject: raise InvalidCliRequest( _('The --grow/--no-grow options is only allowed with' ' --update-partition or --add-partition')) partitionObject.setGrow(self.getArgs().grow) if self.getArgs().maxsize: if not partitionObject: raise InvalidCliRequest( _('The --max-size option is only allowed with' ' --update-partition or --add-partition')) partitionObject.setMaxSize(self.getArgs().maxsize) if self.getArgs().device is not None: if partitionObject is None: raise InvalidCliRequest( _('The --device option is only allowed with' ' --update-partition or --add-partition')) partitionObject.setDevice(self.getArgs().device) if self.getArgs().mountPoint is not None: if partitionObject is None: raise InvalidCliRequest( _('--mount-point option only allowed with' ' --update-partition or --add-partition')) partitionObject.setMountPoint(self.getArgs().mountPoint) if self.getArgs().fileSystem is not None: if partitionObject is None: raise InvalidCliRequest( _('The --file-system option only allowed with' ' --update-partition or --add-partition')) partitionObject.setFsType(self.getArgs().fileSystem) if self.getArgs().size is not None: if partitionObject is None: raise InvalidCliRequest( _('--size option only allowed with --update-partition or' ' --add-partition')) partitionObject.setSize(self._parseDiskSize(self.getArgs().size)) if self.getArgs().options is not None: if partitionObject is None: raise InvalidCliRequest( _('--options option only allowed with --update-partition' ' or --add-partition')) partitionObject.setOptions(self.getArgs().options) if self.getArgs().directAttachment is not None: if partitionObject is None: raise InvalidCliRequest( _('--direct-attachment option only allowed with' ' --update-partition or --add-partition')) partitionObject.setDirectAttachment( self.getArgs().directAttachment) if self.getArgs().indirectAttachment is not None: if partitionObject is None: raise InvalidCliRequest( _('--indirect-attachment option only allowed with' ' --update-partition or --add-partition')) partitionObject.setIndirectAttachment( self.getArgs().indirectAttachment) if self.getArgs().diskSize is not None: if partitionObject is None: raise InvalidCliRequest( _('--disk-size option only allowed with' ' --update-partition or --add-partition')) try: partitionObject.setDiskSize( self._parseDiskSize(self.getArgs().diskSize)) except ValueError: raise InvalidCliRequest(_('Invalid --disk-size argument')) if self.getArgs().sanVolume is not None: if partitionObject is None: raise InvalidCliRequest( _('--san-volume option only allowed with' ' --update-partition or --add-partition')) partitionObject.setSanVolume(self.getArgs().sanVolume) if self.getArgs().preserve is None: if self.getArgs().addPartition is not None: raise InvalidCliRequest( _('--preserve or --no-preserve must be specified when' ' adding a new partition.')) else: if partitionObject is None: raise InvalidCliRequest( _('--preserve and --no-preserve options are only allowed' ' with --update-partition or --add-partition')) partitionObject.setPreserve(self.getArgs().preserve) if self.getArgs().bootLoader is None: if self.getArgs().addPartition is not None: raise InvalidCliRequest( _('--boot-loader or --no-boot-loader must be specified' ' when adding a new partition.')) else: if partitionObject is None: raise InvalidCliRequest( _('--boot-loader and --no-boot-loader options only' ' allowed with --update-partition or --add-partition')) partitionObject.setBootLoader(self.getArgs().bootLoader) if self.getArgs().tags: tags = sp.getTags() tags.update(parse_tags(self.getArgs().tags)) sp.setTags(tags) print(tags) # DEBUG if self.getArgs().remove_tags: tags = sp.getTags() for string in self.getArgs().remove_tags: for tag_name in string.split(','): if tag_name in tags.keys(): tags.pop(tag_name) sp.setTags(tags) print(tags) # DEBUG if self.getArgs().dataRoot is not None: sp.setDataRoot(self.getArgs().dataRoot) if self.getArgs().dataRsync is not None: sp.setDataRsync(self.getArgs().dataRsync) api.updateSoftwareProfile(sp)
def _addNic(self, session, nicName): # Get IP address and netmask using facter facterNicName = nicName.replace(':', '_').replace('.', '_') entries = [ 'ipaddress_%s' % (facterNicName), 'netmask_%s' % (facterNicName), 'macaddress_%s' % (facterNicName), 'network_%s' % (facterNicName) ] d = self._getMultipleFacterEntries(entries) if not 'ipaddress_%s' % (facterNicName) in d or \ not d['ipaddress_%s' % (facterNicName)]: if not self.getArgs().ipaddress: raise InvalidCliRequest( 'Unable to determine IP address, use command-line' ' override') ipaddress = self.getArgs().ipaddress else: ipaddress = d['ipaddress_%s' % (facterNicName)] if not 'netmask_%s' % (facterNicName) in d or \ not d['netmask_%s' % (facterNicName)]: if not self.getArgs().netmask: raise InvalidCliRequest( 'Unable to determine netmask, use command-line' ' override') netmask = self.getArgs().netmask else: netmask = d['netmask_%s' % (facterNicName)] if not 'network_%s' % (facterNicName) in d or \ not d['network_%s' % (facterNicName)]: if not self.getArgs().network: raise InvalidCliRequest( 'Unable to determine network, use command-line' ' override') network = self.getArgs().network else: network = d['network_%s' % (facterNicName)] # Check if nic is the default gateway as well... self._check_default_gateway_nic(nicName) dbNetwork = None # Attempt to find matching network try: dbNetwork = session.query(Network).filter( and_(Network.address == network, Network.netmask == netmask)).one() print('Found network [%s/%s]' % (dbNetwork.address, dbNetwork.netmask)) except NoResultFound: # Network is not known to Tortuga, add it pass if dbNetwork is None: print('Adding network [%s/%s]' % (network, netmask)) dbNetwork = self._addNetwork(nicName, network, netmask, session) # Attempt to find entry in NetworkDevices dbNetworkDevice = self._getNetworkDevice(nicName, session) if not dbNetworkDevice: # Create network device print('Adding network device [%s] as provisioning NIC' % (nicName)) dbNetworkDevice = self._addNetworkDevice(nicName, session) else: print('Found existing network device [%s]' % (nicName)) dbNode = NodesDbHandler().getNode(session, self._cm.getInstaller()) # Attempt to find Nics entry for dbNic in dbNode.nics: if dbNic.networkdevice.name == nicName.lower(): print('Found existing NIC entry for [%s]' % (dbNic.networkdevice.name)) break else: print('Creating NIC entry for [%s]' % (dbNetworkDevice.name)) dbNic = Nic() dbNic.networkdevice = dbNetworkDevice dbNic.ip = ipaddress dbNic.boot = True dbNic.network = dbNetwork dbNode.nics.append(dbNic) # Attempt to find NIC association with hardware profile (commonly # known as hardware profile provisioning NIC) for dbHwProfileNic in dbNode.hardwareprofile.nics: if dbHwProfileNic == dbNic: break else: print('Adding NIC [%s] to hardware profile [%s]' % (dbNic.networkdevice.name, dbNode.hardwareprofile.name)) dbNode.hardwareprofile.nics.append(dbNic) # Attempt to find 'HardwareProfileNetworks' entry for dbHardwareProfileNetwork in \ dbNode.hardwareprofile.hardwareprofilenetworks: if dbHardwareProfileNetwork.network == dbNetwork and \ dbHardwareProfileNetwork.networkdevice == dbNetworkDevice: print('Found existing hardware profile/network association') break else: dbHardwareProfileNetwork = HardwareProfileNetwork() dbHardwareProfileNetwork.network = dbNetwork dbHardwareProfileNetwork.networkdevice = dbNetworkDevice dbNode.hardwareprofile.hardwareprofilenetworks.append( dbHardwareProfileNetwork) session.commit() bUpdated = self._updateNetworkConfig(session, dbNode) if bUpdated and self.getArgs().bSync: print('Applying changes to Tortuga...') cmd = ('/opt/puppetlabs/bin/puppet agent --onetime' ' --no-daemonize >/dev/null 2>&1') tortugaSubprocess.executeCommandAndIgnoreFailure(cmd)
def runCommand(self): self.parseArgs() if self.getArgs().name and self.getArgs().deprecated_name: self.getParser().error( 'argument name: not allowed with argument --name') name = self.getArgs().name \ if self.getArgs().name else self.getArgs().deprecated_name if self.getArgs().jsonTemplatePath: # load from template if self.getArgs().jsonTemplatePath and \ not os.path.exists(self.getArgs().jsonTemplatePath): raise InvalidCliRequest( _('Cannot read template from %s') % (self.getArgs().jsonTemplatePath)) try: with open(self.getArgs().jsonTemplatePath) as fp: buf = json.load(fp) tmpl_dict = buf['hardwareProfile'] except Exception as exc: raise InvalidProfileCreationTemplate( 'Invalid profile creation template: {}'.format(exc)) else: tmpl_dict = {} # build up dict from scratch if name: tmpl_dict['name'] = name if self.getArgs().description: tmpl_dict['description'] = self.getArgs().description if hasattr(self.getArgs(), 'osInfo'): tmpl_dict['os'] = { 'name': getattr(self.getArgs(), 'osInfo').getName(), 'version': getattr(self.getArgs(), 'osInfo').getVersion(), 'arch': getattr(self.getArgs(), 'osInfo').getArch(), } if self.getArgs().nameFormat: tmpl_dict['nameFormat'] = self.getArgs().nameFormat elif 'nameFormat' not in tmpl_dict: tmpl_dict['nameFormat'] = 'compute-#NN' if self.getArgs().tags: tmpl_dict['tags'] = parse_tags(self.getArgs().tags) settings_dict = { 'defaults': self.getArgs().bUseDefaults, 'osInfo': getattr(self.getArgs(), 'osInfo') if hasattr( self.getArgs(), 'osInfo') else None, } hw_profile_spec = HardwareProfile.getFromDict(tmpl_dict) api = HardwareProfileWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) api.createHardwareProfile(hw_profile_spec, settings_dict)
def runCommand(self): self.parseArgs() if not self.getArgs().name and \ not self.getArgs().hardwareProfileName: self.getParser().error( 'the following arguments are required: NAME') if self.getArgs().name and self.getArgs().hardwareProfileName: self.getParser().error( 'argument name: not allowed with argument --name') name = self.getArgs().name \ if self.getArgs().name else self.getArgs().hardwareProfileName api = HardwareProfileWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) spApi = SoftwareProfileWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) nodeApi = NodeWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) hp = api.getHardwareProfile(name, UpdateHardwareProfileCli.optionDict) if self.getArgs().newName is not None: hp.setName(self.getArgs().newName) if self.getArgs().description is not None: hp.setDescription(self.getArgs().description) if self.getArgs().nameFormat is not None: hp.setNameFormat(self.getArgs().nameFormat) if self.getArgs().kernel is not None: hp.setKernel(self.getArgs().kernel) if self.getArgs().kernelParameters is not None: hp.setKernelParams(self.getArgs().kernelParameters) if self.getArgs().initrd is not None: hp.setInitrd(self.getArgs().initrd) if self.getArgs().soAllowed is not None: if self.getArgs().soAllowed.lower() == _('true'): hp.setSoftwareOverrideAllowed(True) elif self.getArgs().soAllowed.lower() == _('false'): hp.setSoftwareOverrideAllowed(False) else: raise InvalidCliRequest( _('--software-override-allowed must be either "true" or' ' "false".')) if self.getArgs().idleProfile is not None and \ self.getArgs().bUnsetIdleProfile: raise InvalidCliRequest( _('Conflicting options --idle-software-profile and' ' --unset-idle-software-profile')) if self.getArgs().idleProfile is not None: sp = spApi.getSoftwareProfile(self.getArgs().idleProfile) hp.setIdleSoftwareProfileId(sp.getId()) if self.getArgs().bUnsetIdleProfile: hp.setIdleSoftwareProfileId(None) if self.getArgs().location is not None: hp.setLocation(self.getArgs().location) if self.getArgs().localBootParameters is not None: hp.setLocalBootParams(self.getArgs().localBootParameters) if self.getArgs().cost is not None: hp.setCost(self.getArgs().cost) if self.getArgs().resourceAdapter: resourceAdapter = ResourceAdapter( name=self.getArgs().resourceAdapter) hp.setResourceAdapter(resourceAdapter) if self.getArgs().default_adapter_config: hp.setDefaultResourceAdapterConfig( self.getArgs().default_adapter_config) if self.getArgs().deletePNic is not None: out = TortugaObjectList() for nic in hp.getProvisioningNics(): for dnic in self.getArgs().deletePNic: if dnic == nic.getIp(): # Skip over this item..its getting deleted break else: # Not a NIC we are deleting out.append(nic) hp.setProvisioningNics(out) if self.getArgs().addPNic is not None: for nicIp in self.getArgs().addPNic: nicsNode = nodeApi.getNodeByIp(nicIp) if nicsNode is not None: for nic in nicsNode.getNics(): if nic.getIp() == nicIp: hp.getProvisioningNics().append(nic) break if self.getArgs().deleteNetwork is not None: # Make sure we actually delete a network out = TortugaObjectList() out.extend(hp.getNetworks()) for netstring in self.getArgs().deleteNetwork: try: dnet, dmask, ddev = netstring.split('/') except ValueError: raise InvalidCliRequest( _('Incorrect input format for --delete-network' ' ("address/mask/device")')) for network in hp.getNetworks(): if dnet == network.getAddress() and \ dmask == network.getNetmask() and \ ddev == network.getNetworkDevice().getName(): # Skip over this item..its getting deleted for n in out: if n.getId() == network.getId(): out.remove(n) break break else: # Not a NIC we are deleting print('Ignoring deletion of non-existent network:' ' %s/%s/%s' % (dnet, dmask, ddev)) hp.setNetworks(out) if self.getArgs().addNetwork: for netstring in self.getArgs().addNetwork: try: anet, amask, adev = netstring.split('/') except ValueError: raise InvalidCliRequest( _('Incorrect input format for --add-network' ' ("address/mask/device")')) network = Network() networkDevice = NetworkDevice() networkDevice.setName(adev) network.setAddress(anet) network.setNetmask(amask) network.setNetworkDevice(networkDevice) hp.getNetworks().append(network) if self.getArgs().tags: tags = hp.getTags() tags.update(parse_tags(self.getArgs().tags)) hp.setTags(tags) if self.getArgs().remove_tags: tags = hp.getTags() for string in self.getArgs().remove_tags: for tag_name in string.split(','): if tag_name in tags.keys(): tags.pop(tag_name) hp.setTags(tags) api.updateHardwareProfile(hp)
def updateNetwork(self, network): """ Update a passed in network tortuga object with the values passed in on the command line. """ # Check for conflicting command-line options if (self.getArgs().netmask or self.getArgs().address) and \ self.getArgs().network: self.getParser().error( 'Specify network using --network/--netmask or --network') if self.getArgs().network: # Use 'ipaddr' module to validate network spec parsed_network, parsed_netmask = \ self.parseNetworkParameter(self.getArgs().network) network.setAddress(parsed_network) network.setNetmask(parsed_netmask) else: if self.getArgs().address is not None: self.assertIp(self.getArgs().address, '--address') network.setAddress(self.getArgs().address) if self.getArgs().netmask is not None: self.assertIp(self.getArgs().netmask, '--netmask') network.setNetmask(self.getArgs().netmask) if self.getArgs().suffix is not None: network.setSuffix(self.getArgs().suffix) if self.getArgs().gateway is not None: self.assertIp(self.getArgs().gateway, '--gateway') network.setGateway(self.getArgs().gateway) if self.getArgs().name is not None: network.setName(self.getArgs().name) if self.getArgs().startIp is not None: self.assertIp(self.getArgs().startIp, '--start-ip') network.setStartIp(self.getArgs().startIp) if self.getArgs().type is not None: network.setType(self.getArgs().type) if self.getArgs().increment is not None: network.setIncrement(self.getArgs().increment) optionsString = network.getOptions() optionsDict = {} if optionsString: # VLAN info may already exist for this network optionsList = optionsString.split(';') for originalOption in optionsList: key, value = originalOption.split('=') optionsDict[key] = value vlanIdFound = self.getArgs().vlanId is not None or \ 'vlan' in optionsDict vlanParentNetworkFound = \ self.getArgs().vlanParentNetwork is not None or \ 'vlanparent' in optionsDict if (vlanIdFound and not vlanParentNetworkFound) or \ (not vlanIdFound and vlanParentNetworkFound): raise InvalidCliRequest( _('--vlan-id and --vlan-parent-network must be used' ' together.')) if self.getArgs().vlanId: optionsDict['vlan'] = self.getArgs().vlanId if self.getArgs().vlanParentNetwork: # Match the given parent network to a network in the DB networkAddr, subnetMask = self.parseNetworkParameter( self.getArgs().vlanParentNetwork) existingNetworkList = self.getNetworkApi().getNetworkList() matchingNetworkId = None for existingNetwork in existingNetworkList: if existingNetwork.getAddress() == networkAddr and \ existingNetwork.getNetmask() == subnetMask: matchingNetworkId = existingNetwork.getId() if not matchingNetworkId: raise InvalidCliRequest( _('Network [%s] not found') % (self.getArgs().vlanParentNetwork)) optionsDict['vlanparent'] = matchingNetworkId newOptions = '' if self.getArgs().vlanId or self.getArgs().vlanParentNetwork: for entry in list(optionsDict.items()): optionKey, optionValue = entry newOptions += '%s=%s;' % (optionKey, optionValue) # Take off the last semicolon newOptions = newOptions[:-1] if self.getArgs().options: if newOptions: newOptions = '%s;%s' % (newOptions, self.getArgs().options) else: newOptions = self.getArgs().options if self.getArgs().options or self.getArgs().vlanId or \ self.getArgs().vlanParentNetwork: network.setOptions(newOptions) if self.getArgs().usingDhcp is not None: network.setUsingDhcp(self.getArgs().usingDhcp)