def runCommand(self): self.parseArgs(_('Create software profile')) 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 # load template if specified with '--template', otherwise build # template tmpl_dict = {} if not self.getArgs().jsonTemplatePath else \ self.load_software_profile_template( self.getArgs().jsonTemplatePath) if name: tmpl_dict['name'] = name if self.getArgs().description: tmpl_dict['description'] = self.getArgs().description if self.getArgs().profileType: tmpl_dict['type'] = self.getArgs().profileType elif 'type' not in tmpl_dict: tmpl_dict['type'] = 'compute' if self.getArgs().tags: tmpl_dict['tags'] = parse_tags(self.getArgs().tags) 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().dataRoot: tmpl_dict['dataRoot'] = self.getArgs().dataRoot if self.getArgs().dataRsync: tmpl_dict['dataRsync'] = self.getArgs().dataRsync sw_profile_spec = SoftwareProfile.getFromDict(tmpl_dict) api = SoftwareProfileWsApi(username=self.getUsername(), password=self.getPassword(), baseurl=self.getUrl(), verify=self._verify) # Populate 'settings_dict' from command-line arguments settings_dict = { 'bOsMediaRequired': self.getArgs().bOsMediaRequired, 'unmanagedProfile': self.getArgs().unmanaged, } api.createSoftwareProfile(sw_profile_spec, settings_dict)
def add_tag(self, session: Session, args): if not args.nodespec and not args.software_profile and \ not args.hardware_profile: sys.stderr.write('Error: must specify --nodes' '/--software-profile/--hardware-profile\n') sys.stderr.flush() sys.exit(1) tags = parse_tags(args.tags) if args.nodespec: nodes = self._node_api.getNodesByNameFilter(session, args.nodespec) for node in nodes: node_tags = node.getTags() node_tags.update(tags) self._node_api.set_tags(session, node_id=node.getId(), tags=node_tags) print(node.getName(), node.getTags()) if args.software_profile: for name in args.software_profile.split(','): swp = self._swp_api.getSoftwareProfile(session, name) swp_tags = swp.getTags() swp_tags.update(tags) swp.setTags(swp_tags) self._swp_api.updateSoftwareProfile(session, swp) if args.hardware_profile: for name in args.hardware_profile.split(','): hwp = self._hwp_api.getHardwareProfile(session, name) hwp_tags = hwp.getTags() hwp_tags.update(tags) hwp.setTags(hwp_tags) self._hwp_api.updateHardwareProfile(session, hwp) session.commit()
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 runCommand(self): self.parseArgs() # Validate options if self.getArgs().macImportFile: if self.getArgs().nodeCount: sys.stderr.write('Ignoring \'--count\' option when importing' ' from MAC file\n') addHostWsApi = self.configureClient(AddHostWsApi) addNodesRequest = { 'force': self.getArgs().force, } if self.getArgs().hardwareProfileName: addNodesRequest['hardwareProfile'] = \ self.getArgs().hardwareProfileName # Parse extra arguments extra_args = dict() for extra_arg in self.getArgs().extra_args or []: key, value = extra_arg.split('=', 1) \ if '=' in extra_arg else (extra_arg, None) extra_args[key] = value if self.getArgs().extra_args and extra_args: addNodesRequest['extra_args'] = extra_args if not self.getArgs().macImportFile and \ self.getArgs().nodeCount is not None: # Only set the node count if not importing from a file addNodesRequest['count'] = self.getArgs().nodeCount if self.getArgs().rackNumber is not None: addNodesRequest['rack'] = self.getArgs().rackNumber if self.getArgs().softwareProfileName: addNodesRequest['softwareProfile'] = \ self.getArgs().softwareProfileName if not self.getArgs().hardwareProfileName and \ not self.getArgs().softwareProfileName: self.getParser().error( '--software-profile or --hardware-profile and ' ' --software-profile must be specified') if self.getArgs().tags: addNodesRequest['tags'] = parse_tags(self.getArgs().tags) if self.getArgs().data: addNodesRequest['data'] = self.getArgs().data nodeDetails = [] if self.getArgs().macAddr or self.getArgs().ipAddress or \ self.getArgs().hostName: if self.getArgs().nodeCount is not None: addNodesRequest['count'] = self.getArgs().nodeCount nics = self.__extract_nics( self.getArgs().ipAddress, self.getArgs().macAddr.lower() if self.getArgs().macAddr else None) nodeDetail = {} if nics: nodeDetail['nics'] = nics addNodesRequest['count'] = 1 if self.getArgs().hostName: nodeDetail['name'] = self.getArgs().hostName addNodesRequest['count'] = 1 nodeDetails.append(nodeDetail) else: if self.getArgs().macImportFile: if not os.path.exists(self.getArgs().macImportFile): sys.stderr.write( _('Error: file [%s] not found') % (self.getArgs().macImportFile) + '\n') raise SystemExit(1) # Parse input file with open(self.getArgs().macImportFile) as fd: for line in fd.readlines(): if not line == '' and not line[0] == '#': itemList = line.rstrip().split() mac = ipAddr = hostname = None for item in itemList: # Definatly MAC if not item.find(':') < 0: mac = item.lower() # IP or hostname elif not item.find('.') < 0: try: ipAddr = str( ipaddress.IPv4Address(item)) except ipaddress.AddressValueError: # Unable to convert to dotted quad; # must be a host name hostname = item else: hostname = item nodeDetail = {} if mac: nodeDetail['mac'] = mac if ipAddr: nodeDetail['ip'] = ipAddr if hostname: nodeDetail['name'] = hostname nodeDetails.append({'nics': [nodeDetail]}) if nodeDetails: addNodesRequest['nodeDetails'] = nodeDetails if 'count' not in addNodesRequest: addNodesRequest['count'] = len(nodeDetails) if self.getArgs().resource_adapter_configuration: addNodesRequest['resource_adapter_configuration'] = \ self.getArgs().resource_adapter_configuration try: addHostSession = addHostWsApi.addNodes(addNodesRequest) if self.getArgs().quiet: sys.stdout.write('{0}\n'.format(addHostSession)) else: # Async (default) invocation; show user output sys.stdout.write( 'Add host session [{0}] created successfully.\n' ' Use \'get-node-requests -r {0}\' to query request' ' status\n'.format(addHostSession)) sys.stdout.flush() except (UrlErrorException, HttpErrorException): sys.stderr.write( 'Error: unable to communicate with Tortuga webservice.\n' '\nEnsure webservice is running. Check log(s)' ' for additional information.\n') raise SystemExit(1)
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 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)