def do_ssm_add(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_ssm_add() return 1 systems = self.expand_systems(args) if not systems: logging.warning(_N('No systems found')) return 1 for system in systems: if system in self.ssm: logging.warning(_N('%s is already in the list') % system) continue else: self.ssm[system] = self.get_system_id(system) logging.debug('Added %s' % system) if self.ssm: logging.debug('Systems Selected: %i' % len(self.ssm)) # save the SSM for use between sessions save_cache(self.ssm_cache_file, self.ssm) return 0
def do_errata_delete(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_errata_delete() return 1 # allow globbing and searching via arguments errata = self.expand_errata(args) if not errata: logging.warning(_N('No patches to delete')) return 1 print(_('Erratum Channels')) print('------- --------') # tell the user how many channels each erratum affects for erratum in sorted(errata): channels = self.client.errata.applicableToChannels(self.session, erratum) print('%s %s' % (erratum.ljust(20), str(len(channels)).rjust(3))) if not self.options.yes and not self.user_confirm(_('Delete these patches [y/N]:')): return 1 for erratum in errata: self.client.errata.delete(self.session, erratum) logging.info(_N('Deleted %i patches') % len(errata)) self.generate_errata_cache(True) return 0
def do_org_removetrust(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if len(args) != 2: self.help_org_removetrust() return 1 your_org, trust_org = args your_org_id = self.get_org_id(your_org) trusted_org_id = self.get_org_id(trust_org) if your_org_id is None: logging.warning(_N("No organisation found for the name %s"), your_org) print(_("Organisation '{}' was not found").format(your_org)) return 1 elif trusted_org_id is None: logging.warning(_N("No trust organisation found for the name %s"), trust_org) print(_("Organisation '{}' to trust for, was not found").format(trust_org)) return 1 else: systems = self.client.org.trusts.listSystemsAffected(self.session, your_org_id, trusted_org_id) print(_('Affected Systems')) print('----------------') if systems: print('\n'.join(sorted([s.get('systemName') for s in systems]))) else: print(_('None')) if self.user_confirm(_('Remove this trust [y/N]:')): self.client.org.trusts.removeTrust(self.session, your_org_id, trusted_org_id) return 0
def do_package_removeorphans(self, args): packages = \ self.client.channel.software.listPackagesWithoutChannel(self.session) if not packages: print(_("No orphaned packages has been found")) logging.warning(_N('No orphaned packages')) return 1 if not self.user_confirm(_('Remove these packages [y/N]:')): print(_("No packages were removed")) return 1 print(_('Packages')) print('--------') print('\n'.join(sorted(build_package_names(packages)))) for package in packages: try: self.client.packages.removePackage(self.session, package.get('id')) except xmlrpclib.Fault: logging.error( _N('Failed to remove package ID %i') % package.get('id')) return 1 return 0
def do_org_listtrusts(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_org_listtrusts() return 1 org_id = self.get_org_id(args[0]) if org_id is None: logging.warning(_N("No organisation found for the name %s"), args[0]) print(_("Organisation '{}' was not found").format(args[0])) return 1 else: trusts = self.client.org.trusts.listTrusts(self.session, org_id) if not trusts: print(_("No trust organisation has been found")) logging.warning(_N("No trust organisation has been found")) return 1 else: for trust in sorted(trusts, key=itemgetter('orgName')): if trust.get('trustEnabled'): print(trust.get('orgName')) return 0
def do_group_listconfigchannels(self, args): if not self.check_api_version('25.0'): logging.warning(_N("This version of the API doesn't support this method")) return 1 arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_group_listconfigchannels() return 1 add_separator = False for group in args: try: channels = self.client.systemgroup.listAssignedConfigChannels(self.session, group) except xmlrpclib.Fault: logging.warning(_N('The group "%s" is invalid') % group) return 1 for details in channels: if add_separator: print(self.SEPARATOR) add_separator = True print(_('Label: %s') % details.get('label')) print(_('Name: %s') % details.get('name')) print(_('Description: %s') % details.get('description')) print(_('Type: %s') % details.get('configChannelType', {}).get('label')) return 0
def load_cache(cachefile): data = {} expire = datetime.now() logging.debug('Loading cache from %s', cachefile) if os.path.isfile(cachefile): try: inputfile = open(cachefile, 'rb') data = pickle.load(inputfile) inputfile.close() except (EOFError, pickle.UnpicklingError) as exc: # If cache generation is interrupted (e.g by ctrl-c) you can end up # with an EOFError exception due to the partial picked file # So we catch this error and remove the corrupt partial file # If you don't do this then spacecmd will fail with an unhandled # exception until the partial file is manually removed logging.warning(_N("Loading cache file %s failed"), cachefile) logging.warning( _N("Cache generation was probably interrupted," + "removing corrupt %s"), cachefile) logging.debug(str(exc)) os.remove(cachefile) except IOError: logging.error(_N("Couldn't load cache from %s"), cachefile) if isinstance(data, (list, dict)): if 'expire' in data: expire = data['expire'] del data['expire'] else: logging.debug('%s does not exist', cachefile) return data, expire
def do_org_addtrust(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if len(args) != 2: self.help_org_addtrust() return 1 your_org, trust_org = args your_org_id = self.get_org_id(your_org) org_to_trust_id = self.get_org_id(trust_org) if your_org_id is None: logging.warning(_N("No organisation found for the name %s"), your_org) print(_("Organisation '{}' was not found").format(your_org)) return 1 elif org_to_trust_id is None: logging.warning(_N("No trust organisation found for the name %s"), trust_org) print( _("Organisation '{}' to trust for, was not found").format( trust_org)) return 1 else: self.client.org.trusts.addTrust(self.session, your_org_id, org_to_trust_id) return 0
def config_channel_order(all_channels=None, new_channels=None): all_channels = all_channels or [] new_channels = new_channels or [] while True: print(_('Current Selections')) print('------------------') for i, new_channel in enumerate(new_channels, 1): print('%i. %s' % (i, new_channel)) print('') action = prompt_user('a[dd], r[emove], c[lear], d[one]:') if re.match('a', action, re.I): print('') print(_('Available Configuration Channels')) print('--------------------------------') for c in sorted(all_channels): print(c) print('') channel = prompt_user(_('Channel:')) if channel not in all_channels: logging.warning(_N('Invalid channel')) continue try: rank = int(prompt_user(_('New Rank:'))) if channel in new_channels: new_channels.remove(channel) new_channels.insert(rank - 1, channel) except IndexError: logging.warning(_N('Invalid rank')) continue except ValueError: logging.warning(_N('Invalid rank')) continue elif re.match('r', action, re.I): channel = prompt_user(_('Channel:')) if channel not in all_channels: logging.warning(_N('Invalid channel')) continue new_channels.remove(channel) elif re.match('c', action, re.I): print(_('Clearing current selections')) new_channels = [] continue elif re.match('d', action, re.I): break print('') return new_channels
def do_schedule_details(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_schedule_details() return 1 else: action_id = args[0] try: action_id = int(action_id) except ValueError: logging.warning(_N('The ID "%s" is invalid') % action_id) return 1 completed = self.client.schedule.listCompletedSystems(self.session, action_id) failed = self.client.schedule.listFailedSystems(self.session, action_id) pending = self.client.schedule.listInProgressSystems(self.session, action_id) action = dict(map(lambda e: [e.get("id"), e], self.client.schedule.listAllActions(self.session))).get(action_id) if action is not None: print(_('ID: %i') % action.get('id')) print(_('Action: %s') % action.get('name')) print(_('User: %s') % action.get('scheduler')) print(_('Date: %s') % action.get('earliest')) print('') print(_('Completed: %s') % str(len(completed)).rjust(3)) print(_('Failed: %s') % str(len(failed)).rjust(3)) print(_('Pending: %s') % str(len(pending)).rjust(3)) if completed: print('') print(_('Completed Systems')) print('-----------------') for s in completed: print(s.get('server_name')) if failed: print('') print(_('Failed Systems')) print('--------------') for s in failed: print(s.get('server_name')) if pending: print('') print(_('Pending Systems')) print('---------------') for s in pending: print(s.get('server_name')) else: logging.error(_N('No action found with the ID "%s"') % action_id) return 1 return 0
def do_cryptokey_create(self, args): arg_parser = get_argument_parser() arg_parser.add_argument('-t', '--type') arg_parser.add_argument('-d', '--description') arg_parser.add_argument('-f', '--file') (args, options) = parse_command_arguments(args, arg_parser) options.contents = None if is_interactive(options): options.type = prompt_user(_('GPG or SSL [G/S]:')) options.description = '' while options.description == '': options.description = prompt_user(_('Description:')) if self.user_confirm(_('Read an existing file [y/N]:'), nospacer=True, ignore_yes=True): options.file = prompt_user('File:') else: options.contents = editor(delete=True) else: if not options.type: logging.error(_N('The key type is required')) return 1 if not options.description: logging.error(_N('A description is required')) return 1 if not options.file: logging.error(_N('A file containing the key is required')) return 1 # read the file the user specified if options.file: options.contents = read_file(options.file) if not options.contents: logging.error(_N('No contents of the file')) return 1 # translate the key type to what the server expects if re.match('G', options.type, re.I): options.type = 'GPG' elif re.match('S', options.type, re.I): options.type = 'SSL' else: logging.error(_N('Invalid key type')) return 1 self.client.kickstart.keys.create(self.session, options.description, options.type, options.contents) return 0
def do_api(self, args): arg_parser = get_argument_parser() arg_parser.add_argument('-A', '--args', default='') arg_parser.add_argument('-F', '--format', default='') arg_parser.add_argument('-o', '--output', default='') # set glob = False otherwise repo filters cannot set wildcard characters args, options = parse_command_arguments(args, arg_parser, glob=False) if not args: self.help_api() return api_name = args[0] api_args = parse_api_args(options.args) if options.output: try: output = open(options.output, "w") except IOError: logging.warning(_N("Could not open to write: %s"), options.output) logging.info(_N("Fallback output to stdout")) output = sys.stdout else: output = sys.stdout api = getattr(self.client, api_name, None) if not callable(api): logging.warning(_N("No such API: %s"), api_name) return try: if api_name in ['api.getVersion', 'api.systemVersion']: res = api(*api_args) else: res = api(self.session, *api_args) if not isinstance(res, list): res = [res] if options.format: for r in res: output.write(options.format % r + "\n") else: json_dump(res, output, indent=2, cls=CustomJsonEncoder) if (output != sys.stdout): output.close() except xmlrpclib.Fault as e: logging.error(e) if (output != sys.stdout): output.close()
def do_snippet_create(self, args, update_name=''): arg_parser = get_argument_parser() arg_parser.add_argument('-n', '--name') arg_parser.add_argument('-f', '--file') (args, options) = parse_command_arguments(args, arg_parser) contents = '' if is_interactive(options): # if update_name was passed, we're trying to update an existing snippet if update_name: options.name = update_name snippets = self.client.kickstart.snippet.listCustom(self.session) for s in snippets: if s.get('name') == update_name: contents = s.get('contents') break if not options.name: options.name = prompt_user('Name:', noblank=True) if self.user_confirm(_('Read an existing file [y/N]:'), nospacer=True, ignore_yes=True): options.file = prompt_user('File:') else: (contents, _ignore) = editor(template=contents, delete=True) else: if not options.name: logging.error(_N('A name is required for the snippet')) return 1 if not options.file: logging.error(_N('A file is required')) return 1 if options.file: contents = read_file(options.file) print('') print(_('Snippet: %s') % options.name) print(_('Contents')) print('--------') print(contents) if self.user_confirm(): self.client.kickstart.snippet.createOrUpdate(self.session, options.name, contents) return 0
def json_read_from_file(filename): data = None try: with open(filename) as fhd: data = json.loads(fhd.read()) except IOError as exc: logging.error(_N("Could not open file %s for reading: %s"), filename, str(exc)) except ValueError as exc: logging.error(_N("Could not parse JSON data from %s: %s"), filename, str(exc)) except Exception as exc: logging.error(_N("Error processing file %s: %s"), filename, str(exc)) return data
def get_string_diff_dicts(string1, string2, sep="-"): """ compares two strings and determine, if one string can be transformed into the other by simple string replacements. If these strings are closly related, it returns two dictonaries of regular expressions. The first dictionary can be used to transform type 1 strings into type 2 strings. The second dictionary vice versa. These replacements blocks must be separated by "-". Example: string1: rhel6-x86_64-dev-application1 string2: rhel6-x86_64-qas-application1 Result: dict1: {'(^|-)dev(-|$)': '\\1DIFF(dev|qas)\\2'} dict2: {'(^|-)qas(-|$)': '\\1DIFF(dev|qas)\\2'} """ replace1 = {} replace2 = {} if string1 == string2: logging.info( _N("Skipping usage of common strings: both strings are equal")) return [None, None] substrings1 = deque(string1.split(sep)) substrings2 = deque(string2.split(sep)) while substrings1 and substrings2: sub1 = substrings1.popleft() sub2 = substrings2.popleft() if sub1 == sub2: # equal, nothing to do pass else: # TODO: replace only if len(sub1) == len(sub2) ? replace1[ '(^|-)' + sub1 + '(-|$)'] = r'\1' + "DIFF(" + sub1 + "|" + sub2 + ")" + r'\2' replace2[ '(^|-)' + sub2 + '(-|$)'] = r'\1' + "DIFF(" + sub1 + "|" + sub2 + ")" + r'\2' if substrings1 or substrings2: logging.info( _N("Skipping usage of common strings: number of substrings differ") ) return [None, None] return [replace1, replace2]
def json_dump_to_file(obj, filename): json_data = json.dumps(obj, indent=4, sort_keys=True) out = False if json_data is None: logging.error(_N("Could not generate json data object!")) else: try: with open(filename, 'w') as fdh: fdh.write(json_data) out = True except IOError as exc: logging.error(_N("Could not open file %s for writing: %s"), filename, str(exc)) return out
def do_get_session(self, args): if self.session: print(self.session) return 0 else: logging.error(_N('No session found')) return 1
def do_package_listinstalledsystems(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args.replace('\\', '\\\\'), arg_parser) if not args: self.help_package_listinstalledsystems() return 1 packages = self.do_package_search(' '.join(args), True) if not packages: logging.warning(_N('No packages found')) return 1 add_separator = False for package in packages: if add_separator: print(self.SEPARATOR) add_separator = True systems = [] for package_id in self.get_package_id(package): systems += self.client.system.listSystemsWithPackage(self.session, package_id) print(package) print('-' * len(package)) if systems: print('\n'.join(sorted(['%s : %s' % (s.get('name'), s.get('id')) for s in systems]))) return 0
def do_package_remove(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args.replace('\\', '\\\\'), arg_parser) if not args: self.help_package_remove() return 1 to_remove = filter_results(self.get_package_names(True), args) if not to_remove: logging.debug("Failed to find packages for criteria %s", str(args)) print(_("No packages found to remove")) return 1 if not self.user_confirm(_('Remove these packages [y/N]:')): print(_("No packages has been removed")) return 1 print(_('Packages')) print('--------') print('\n'.join(sorted(to_remove))) for package in to_remove: for package_id in self.get_package_id(package): try: self.client.packages.removePackage(self.session, package_id) except xmlrpclib.Fault: logging.error(_N('Failed to remove package ID %i') % package_id) # regenerate the package cache after removing these packages self.generate_package_cache(True) return 0
def do_repo_updatessl(self, args): arg_parser = get_argument_parser() arg_parser.add_argument('-n', '--name') arg_parser.add_argument('--ca', default='') arg_parser.add_argument('--cert', default='') arg_parser.add_argument('--key', default='') (args, options) = parse_command_arguments(args, arg_parser) if is_interactive(options): options.name = prompt_user(_('Name:'), noblank=True) options.ca = prompt_user(_('SSL CA cert:')) options.cert = prompt_user(_('SSL Client cert:')) options.key = prompt_user(_('SSL Client key:')) else: if not options.name: logging.error(_N('A name is required')) return 1 self.client.channel.software.updateRepoSsl(self.session, options.name, options.ca, options.cert, options.key) return 0
def do_group_details(self, args, short=False): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_group_details() return 1 add_separator = False for group in args: try: details = self.client.systemgroup.getDetails(self.session, group) systems = [stm.get('profile_name') for stm in self.client.systemgroup.listSystems(self.session, group)] except xmlrpclib.Fault: logging.warning(_N('The group "%s" is invalid') % group) return 1 if add_separator: print(self.SEPARATOR) add_separator = True print(_('ID: %i') % details.get('id')) print(_('Name: %s') % details.get('name')) print(_('Description: %s') % details.get('description')) print(_('Number of Systems: %i') % details.get('system_count')) if not short: print('') print(_('Members')) print('-------') print('\n'.join(sorted(systems))) return 0
def do_ssm_intersect(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_ssm_intersect() return 1 systems = self.expand_systems(args) if not systems: logging.warning(_N('No systems found')) return 1 # tmp_ssm placeholder to gather systems that are both in original ssm # selection and newly selected group tmp_ssm = {} for system in systems: if system in self.ssm: logging.debug('%s is in both groups: leaving in SSM' % system) tmp_ssm[system] = self.ssm[system] # set self.ssm to tmp_ssm, which now holds the intersection self.ssm = tmp_ssm # save the SSM for use between sessions save_cache(self.ssm_cache_file, self.ssm) if self.ssm: logging.debug('Systems Selected: %i' % len(self.ssm)) return 0
def do_package_listerrata(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args.replace('\\', '\\\\'), arg_parser) if not args: self.help_package_listerrata() return 1 packages = self.do_package_search(' '.join(args), True) if not packages: logging.warning(_N('No packages found')) return 1 add_separator = False for package in packages: if add_separator: print(self.SEPARATOR) add_separator = True for package_id in self.get_package_id(package): errata = self.client.packages.listProvidingErrata( self.session, package_id) print(package) print('-' * len(package)) if errata: print('\n'.join(sorted([e.get('advisory') for e in errata]))) return 0
def do_ssm_remove(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_ssm_remove() return 1 systems = self.expand_systems(args) if not systems: logging.warning(_N('No systems found')) return 1 for system in systems: # double-check for existance in case of duplicate names if system in self.ssm: logging.debug('Removed %s' % system) del self.ssm[system] logging.debug('Systems Selected: %i' % len(self.ssm)) # save the SSM for use between sessions save_cache(self.ssm_cache_file, self.ssm) return 0
def do_cryptokey_delete(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if not args: self.help_cryptokey_delete() return 1 # allow globbing of cryptokey names keys = filter_results(self.do_cryptokey_list('', True), args) logging.debug("cryptokey_delete called with args %s, keys=%s" % (args, keys)) if not keys: logging.error(_N("No keys matched argument %s") % args) return 1 # Print the keys prior to the confirmation print('\n'.join(sorted(keys))) if self.user_confirm(_('Delete key(s) [y/N]:')): for key in keys: self.client.kickstart.keys.delete(self.session, key) return 0 else: return 1
def do_errata_publish(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if len(args) < 2: self.help_errata_publish() return 1 # allow globbing and searching via arguments errata = self.expand_errata(args[0]) channels = args[1:] if not errata: logging.warning(_N('No patches to publish')) return 1 print('\n'.join(sorted(errata))) if not self.options.yes and not self.user_confirm( _('Publish these patches [y/N]:')): return 1 for erratum in errata: self.client.errata.publish(self.session, erratum, channels) return 0
def do_scap_schedulexccdfscan(self, args): arg_parser = get_argument_parser() (args, _options) = parse_command_arguments(args, arg_parser) if len(args) < 3: self.help_scap_schedulexccdfscan() return 1 path = args[0] param = "--" param += args[1] # use the systems listed in the SSM if re.match('ssm', args[0], re.I): systems = self.ssm.keys() else: systems = self.expand_systems(args[2:]) if not systems: logging.warning(_N('No systems selected')) return 1 for system in systems: system_id = self.get_system_id(system) if not system_id: continue self.client.system.scap.scheduleXccdfScan(self.session, system_id, path, param) return 0
def do_whoami(self, args): if self.current_user: print(self.current_user) return 0 else: logging.warning(_N("You are not logged in")) return 1
def do_whoamitalkingto(self, args): if self.server: print(self.server) return 0 else: logging.warning(_N('Yourself')) return 1
def do_repo_setfilters(self, args): # arguments can start with -, so don't parse arguments in the normal way args = shlex.split(args) if len(args) < 2: self.help_repo_setfilters() return 1 repo = args[0] filters = [] for arg in args[1:]: flag = arg[0] repofilter = arg[1:] if not (flag == '+' or flag == '-'): logging.error(_N('Each filter must start with + or -')) return 1 filters.append({'filter': repofilter, 'flag': flag}) self.client.channel.software.setRepoFilters(self.session, repo, filters) return 0