def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) try: # expand includes if args.includes is not None: args.includes = args.includes.split(',') except: pass # eval public try: if args.public is not None: args.public = (args.public.lower() in ['1', 'true', 'yes', 'y']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help
def configure(self, config, args, args_extra, parsers): if args.action == None: parsers.template.print_help() sys.exit(1) # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) try: # expand includes if args.includes is not None: args.includes = args.includes.split(',') except: pass # eval public try: if args.public is not None: args.public = (args.public.lower() in ['1', 'true', 'yes', 'y']) except: pass # store args for additional processing self.args = args
def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help
def configure(self, config, args, args_extra, parsers): if args.action == None: parsers.machine.print_help() sys.exit(1) # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args
def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # eval enabled try: if args.enabled is not None: args.enabled = (args.enabled.lower() in ['1', 'true']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help
def configure(self, config, args, args_extra, parsers): if args.action == None: parsers.repo.print_help() sys.exit(1) # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # eval enabled try: if args.enabled is not None: args.enabled = (args.enabled.lower() in ['1', 'true']) except: pass # store args for additional processing self.args = args
class MachineCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] machine [<args>]\n" "{0} machine add [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine update [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine list [user] [--filter-name] [--filter-description]\n" "{0} machine rm [user:]name\n" "{0} machine diff [user:]name [--output=path]\n" "{0} machine connect [user:]name\n" "{0} machine cmd [user:]name command arg1 arg2 ... argN\n" "{0} machine sync [user:]name [--pull [[user:]template]] | --push [user:]template]\n" "{0} machine disconnect [user:]name\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} machine add [user:]machine [user:]template [--title] [--description]\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: print('command: not implemented') return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): m = Machine(self.args.machine, user=self.args.username) t = Template(self.args.template, user=self.args.username) # grab the template we're associating to the machine try: t = self.cs.template_get(t, auth=True) except ServiceException as e: print(e) return 1 # add template uuid to machine m.template = t.uuid # add machine bits that are specified if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_create(m) except ServiceException as e: print(e) return 1 print(res) # update config with our newly added (registered) machine self.config.set('machine', 'uuid', res['uuid']) self.config.set('machine', 'key', res['key']) self.config.save() print('info: machine added.') return 0 def run_cmd(self): print('MACHINE CMD') def run_diff(self): print('MACHINE DIFF') def run_list(self): # fetch all accessible/available templates try: machines = self.cs.machine_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description ) except ServiceException as e: print(e) return 1 if len(machines): l = prettytable.PrettyTable(["user:name", "title"]) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 # add table items and print for m in machines: l.add_row(["{0}:{1}".format(m['username'], m['stub']), m['name']]) print(l) # print summary print('\n{0} machine(s) found.'.format(len(machines))) else: print('0 machines found.') def run_rm(self): m = Machine(self.args.machine, user=self.args.username) try: res = self.cs.machine_delete(m) except ServiceException as e: print(e) return 1 self.config.unset('machine', 'uuid') self.config.unset('machine', 'key') self.config.save() print('info: machine removed.') return 0 def run_sync(self): uuid = self.config.get('machine', 'uuid') key = self.config.get('machine', 'key') try: res = self.cs.machine_sync(uuid, key) except ServiceException as e: print(e) return 1 print(res) print('info: machine synced.') return 0 def run_update(self): m = Machine(self.args.machine, user=self.args.username) try: m = self.cs.machine_get(m) except ServiceException as e: print(e) return 1 # add machine bits that are specified for update if self.args.template: t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 m.template = t.uuid if self.args.name is not None: m.name = self.args.name if self.args.title is not None: m.title = self.args.title if self.args.title is not None: m.title = self.args.title if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_update(m) except ServiceException as e: print(e) return 1 print('info: machine updated.') return 0
class MachineCommand(Command): def configure(self, config, args, args_extra, parsers): if args.action == None: parsers.machine.print_help() sys.exit(1) # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print( "General usage: {0} [--version] [--help] [--verbose] machine [<args>]\n" "{0} machine add [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine update [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine list [user] [--filter-name] [--filter-description]\n" "{0} machine rm [user:]name\n" "{0} machine diff [user:]name [--output=path]\n" "{0} machine connect [user:]name\n" "{0} machine cmd [user:]name command arg1 arg2 ... argN\n" "{0} machine sync [user:]name [--pull [[user:]template]] | --push [user:]template]\n" "{0} machine disconnect [user:]name\n" "\n".format(self.prog_name)) def help_add(self): print( "Usage: {0} machine add [user:]machine [user:]template [--title] [--description]\n" "\n".format(self.prog_name)) def help_cmd(self): pass def help_connect(self): pass def help_disconnect(self): pass def help_diff(self): pass def help_list(self): pass def help_rm(self): pass def help_sync(self): pass def help_update(self): pass def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): m = Machine(self.args.machine, user=self.args.username) t = Template(self.args.template, user=self.args.username) # grab the template we're associating to the machine try: t = self.cs.template_get(t, auth=True, resolve_includes=False) except ServiceException as e: print(e) return 1 # add template uuid to machine m.template = t.uuid # add machine bits that are specified if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_create(m) except ServiceException as e: print(e) return 1 print(res) # update config with our newly added (registered) machine self.config.set('machine', 'uuid', res['uuid']) self.config.set('machine', 'key', res['key']) self.config.save() print('info: machine added.') return 0 def run_cmd(self): print('MACHINE CMD') def run_diff(self): uuid = self.config.get('machine', 'uuid') key = self.config.get('machine', 'key') try: res = self.cs.machine_sync(uuid, key, template=True) except ServiceException as e: print(e) return 1 m = Machine(res['template']) t = Template(res['template']) ts = Template.from_system() (l_r, r_l) = t.package_diff(ts.packages_all) print("In template not in system:") for p in l_r: print(" - {0}".format(p.name)) print() print("On system not in template:") for p in r_l: print(" + {0}".format(p.name)) print() def run_list(self): # fetch all accessible/available templates try: machines = self.cs.machine_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description) except ServiceException as e: print(e) return 1 if len(machines): l = TextTable(['[USER:]NAME', 'TITLE']) # add table items and print for m in machines: l.add_row( ["{0}:{1}".format(m['username'], m['stub']), m['name']]) print(l) # print summary print('\n{0} machine(s) found.'.format(len(machines))) else: print('0 machines found.') def run_rm(self): m = Machine(self.args.machine, user=self.args.username) try: res = self.cs.machine_delete(m) except ServiceException as e: print(e) return 1 self.config.unset('machine', 'uuid') self.config.unset('machine', 'key') self.config.save() print('info: machine removed.') return 0 def run_sync(self): uuid = self.config.get('machine', 'uuid') key = self.config.get('machine', 'key') try: res = self.cs.machine_sync(uuid, key, template=True) except ServiceException as e: print(e) return 1 m = Machine(res['template']) t = Template(res['template']) t.system_prepare() # describe process for dry runs if self.args.dry_run: tx = t.system_transaction() packages_install = list(tx.install_set) packages_install.sort(key=lambda x: x.name) packages_remove = list(tx.remove_set) packages_remove.sort(key=lambda x: x.name) if len(packages_install) or len(packages_remove): print( 'The following would be installed to (+) and removed from (-) the system:' ) for p in packages_install: print(' + ' + str(p)) for p in packages_remove: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages_install) + len(packages_remove))) print() else: print('No system changes required.') print('No action peformed during this dry-run.') return 0 # TODO: progress for download, install and removal t.to_system_apply(clean=False) print('info: machine synced.') return 0 def run_update(self): m = Machine(self.args.machine, user=self.args.username) try: m = self.cs.machine_get(m) except ServiceException as e: print(e) return 1 # add machine bits that are specified for update if self.args.template: t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t, resolve_includes=False) except ServiceException as e: print(e) return 1 m.template = t.uuid if self.args.name is not None: m.name = self.args.name if self.args.title is not None: m.title = self.args.title if self.args.title is not None: m.title = self.args.title if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_update(m) except ServiceException as e: print(e) return 1 print('info: machine updated.') return 0
class ObjectCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] object [<args>]\n" "\n" "Specific usage:\n" "{0} object add [user:]template[@version] [store:]object_name --data=|--data-file=|--source= --action= [--action= ...]\n" "{0} object list [user:]template[@version] [--filter-store=...] [--filter-name=...]\n" "{0} object rm [user:]template[@version] [store1:]object_name1 [store2:]object_name2 ... [storeN:]object_nameN\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} object add [user:]template[@version]\n" " [store:]object_name --data=|--data-file=|--source= --action= [--action= ...]\n" "\n" "Availablle actions are: \n" " copy DST_PATH\n" " extract DST_PATH\n" " execute\n" " execute-command COMMAND\n" " ks-pre\n" " ks-pre-install\n" " ks-post\n" "\n".format(self.prog_name)) def help_list(self): print("Usage: {0} object list [user:]template[@version] [--filter-store=...] [--filter-name=...]\n" "\n".format(self.prog_name)) def help_rm(self): print("Usage: {0} object rm [user:]template[@version] [store:]object_name [store2:]object_name2 ... [storeN:]object_nameN\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 try: obj = Object(name=self.args.object, data=self.args.data, data_file=self.args.data_file, source=self.args.source, xsum=self.args.xsum, actions=self.args.actions) except ErrorInvalidObject as e: print (e) return 1 t.add_object(obj) # describe process for dry runs if self.args.dry_run: print('The following object would be added to the template: {0}'.format(t.name)) print(' - ' + str(obj)) print() return 1 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 return 0 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 objects = t.objects if objects: print('Template {0} has the following objects:'.format(t.name)) for o in t.objects: print(' - ' + str(o)) else: print('Template {0} has no objects.'.format(t.name)) def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 for o in self.args.objects: try: obj = Object(name=o) except ErrorInvalidObject as e: print (e) return 1 t.remove_object(obj) if self.args.dry_run: # TODO: print changes return 1 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 return 0
class RepoCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # eval enabled try: if args.enabled is not None: args.enabled = (args.enabled.lower() in ['1', 'true']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: print('command: not implemented') return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # default enabled if self.args.enabled is None: self.args.enabled = True r = Repository(stub=self.args.repo, name=self.args.name, baseurl=self.args.baseurl, mirrorlist=self.args.mirrorlist, metalink=self.args.metalink, enabled=self.args.enabled, cost=self.args.cost, priority=self.args.priority, gpgkey=self.args.gpgkey, gpgcheck=self.args.gpgcheck, exclude=self.args.exclude) t.add_repo(r) # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: repo added.') return 0 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 r = t.find_repo(self.args.repo) if len(r) != 1: print('error: repo is not defined in template.') return 1 r = r[0] o = { 'n': self.args.name, 'bu': self.args.baseurl, 'ml': self.args.mirrorlist, 'ma': self.args.metalink, 'e': self.args.enabled, 'c': self.args.cost, 'p': self.args.priority, 'gc': self.args.gpgcheck, 'gk': self.args.gpgkey, 'sk': self.args.skip, 'x': self.args.exclude } o = {k: v for k, v in o.items() if v != None} r.parse(o) if not t.update_repo(r): print('error: no changes detected.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: repo updated.') return 0 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 repos = list(t.repos_all) repos.sort(key=lambda x: x.stub) if len(repos): l = prettytable.PrettyTable( ['repo', 'name', 'priority', 'cost', 'enabled']) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 for r in repos: if r.cost is None: r.cost = '-' if r.priority is None: r.priority = '-' if r.enabled: r.enabled = 'Y' else: r.enabled = 'N' l.add_row([r.stub, r.name, r.priority, r.cost, r.enabled]) print(l) print() else: print('0 repos defined.') def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 repos = [] for r in self.args.repo: r = Repository(r) if t.remove_repo(r): repos.append(r) repos.sort(key=lambda x: x.stub) # describe process for dry runs if self.args.dry_run: if len(repos): print('The following would be removed from the template: {0}'. format(t.name)) for r in repos: print(' - ' + str(r)) print() print('Summary:') print(' - Repo(s): %d' % (len(repos))) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(repos): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1
class PackageCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] package [<args>]\n" "\n" "Specific usage:\n" "{0} package add [user:]template [--nodeps] package1 packagelist1 package2 ... packageN\n" "{0} package list [user:]template [--filter-name] [--filter-summary] [--filter-description] [--filter-arch] [--filter-repo] [--output=path]\n" "{0} package rm [user:]template [--nodeps] package1 package2 ... packageN\n" "{0} template list\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} template add [user:]template [--name] [--title] [--description]\n" " [--includes] [--public]\n" "\n" "Options:\n" " --name NAME Define the pretty NAME of template\n" " --title TITLE Define the pretty TITLE of template\n" " --description TEXT Define descriptive TEXT of the template\n" " --includes TEMPLATE Define descriptive TEXT of the template\n" "\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: print('command: not implemented') return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 for p in self.args.package: t.add_package(Package(p)) packages = list(t.packages_delta) packages.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages): print('The following would be added to the template: {0}'.format(t.name)) for p in packages: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % ( len(packages) )) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(packages): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 packages = list(t.packages_all) packages.sort(key=lambda x: x.name) if len(packages): l = prettytable.PrettyTable(['package', 'epoch', 'version', 'release', 'arch', 'action']) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 for p in packages: if p.epoch is None: p.epoch = '-' if p.version is None: p.version = '-' if p.release is None: p.release = '-' if p.arch is None: p.arch = '-' if p.included(): p.action = '+' else: p.action = '-' l.add_row([p.name, p.epoch, p.version, p.release, p.arch, p.action]) print(l) print() else: print('0 packages defined.') def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 packages = [] for p in self.args.package: p = Package(p) if t.remove_package(p): packages.append(p) packages.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages): print('The following would be removed from the template: {0}'.format(t.name)) for p in packages: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % ( len(packages) )) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(packages): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # track updates to determine server update updated = False for p in self.args.package: # parse new and find old pn = Package(p) print(pn) if pn not in t.packages: print('warn: package is not defined in template.') continue # update with new and track if t.update_package(pn): updated = True if not updated: print('info: no changes detected.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: package(s) updated.') return 0
class RepoCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # eval enabled try: if args.enabled is not None: args.enabled = (args.enabled.lower() in ['1', 'true']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print( "General usage: {0} [--version] [--help] [--verbose] repo [<args>]\n" "\n" "Specific usage:\n" "{0} repo add [user:]template[@version] repo_name [--filepath] [--baseurl] [--metalink] [--mirrorlist] [--cost] [--enabled] [--gpgkey] [--name] [--priority]\n" "{0} repo update [user:]template[@version] repo_name [--baseurl] [--metalink] [--mirrorlist] [--cost] [--enabled] [--gpgkey] [--name] [--priority]\n" "{0} repo list [user:] template[@version]\n" "{0} repo rm [user:]template[@version] repo_name\n" "\n".format(self.prog_name)) def help_add(self): pass def help_list(self): pass def help_rm(self): pass def help_update(self): print( "Usage: {0} repo update [user:]template[@version] repo_name [--baseurl] [--metalink] [--mirrorlist] [--cost] [--enabled] [--gpgkey] [--name] [--priority]\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # default enabled if self.args.enabled is None: self.args.enabled = True r = Repository({ 'stub': self.args.repo, 'name': self.args.name, 'baseurl': self.args.baseurl, 'mirrorlist': self.args.mirrorlist, 'metalink': self.args.metalink, 'enabled': self.args.enabled, 'cost': self.args.cost, 'priority': self.args.priority, 'gpgkey': self.args.gpgkey, 'gpgcheck': self.args.gpgcheck, 'exclude': self.args.exclude }) t.add_repo(r) # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: repo added.') return 0 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 r = t.find_repo(self.args.repo) if len(r) != 1: print('error: repo is not defined in template.') return 1 r = r[0] # reset baseurl, metalink and mirrorlist when any are specified if self.args.baseurl is not None or self.args.metalink is not None or self.args.mirrorlist is not None: r.baseurl = None r.mirrorlist = None r.metalink = None if self.args.baseurl is not None: r.baseurl = self.args.baseurl if self.args.cost is not None: r.cost = self.args.cost if self.args.enabled is not None: r.enabled = self.args.enabled if self.args.gpgcheck is not None: r.gpgcheck = self.args.gpgcheck if self.args.gpgkey is not None: r.gpgkey = self.args.gpgkey if self.args.metalink is not None: r.metalink = self.args.metalink if self.args.mirrorlist is not None: r.mirrorlist = self.args.mirrorlist if self.args.name is not None: r.name = self.args.name if self.args.priority is not None: r.priority = self.args.priority if not t.update_repo(r): print('info: no changes detected.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: repo updated.') return 0 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 repos = list(t.repos_all) repos.sort(key=lambda x: x.stub) if len(repos): l = TextTable( header=['REPO', 'NAME', 'PRIORITY', 'COST', 'ENABLED']) for r in repos: cost = r.cost if cost is None: cost = '-' priority = '-' if priority is None: priority = '-' enabled = r.enabled if enabled: enabled = 'Y' l.add_row([r.stub, r.name, priority, cost, enabled]) print(l) print() else: print('0 repos defined.') def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 repos = [] for r in self.args.repo: r = Repository(r) if t.remove_repo(r): repos.append(r) repos.sort(key=lambda x: x.stub) # describe process for dry runs if self.args.dry_run: if len(repos): print('The following would be removed from the template: {0}'. format(t.name)) for r in repos: print(' - ' + str(r)) print() print('Summary:') print(' - Repo(s): %d' % (len(repos))) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(repos): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1
class TemplateCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) try: # expand includes if args.includes is not None: args.includes = args.includes.split(',') except: pass # eval public try: if args.public is not None: args.public = (args.public.lower() in ['1', 'true']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] template [<args>]\n" "\n" "Specific usage:\n" "{0} template add [user:]template [--name] [--description] [--includes] [--public]\n" "{0} template update [user:]template [--name] [--description] [--includes] [--public]\n" "{0} template rm [user:]template\n" "{0} template push [user:]template [--all]\n" "{0} template pull [user:]template [--clean]\n" "{0} template diff [user:]template\n" "{0} template copy [user_from:]template_from [[user_to:]template_to]\n" "{0} template list\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} template add [user:]template [--name] [--title] [--description]\n" " [--includes] [--public]\n" "\n" "Options:\n" " --name NAME Define the pretty NAME of template\n" " --title TITLE Define the pretty TITLE of template\n" " --description TEXT Define descriptive TEXT of the template\n" " --includes TEMPLATE Define descriptive TEXT of the template\n" "\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: print('command: not implemented') return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) if self.args.username: if not self.cs.authenticate(self.args.username, getpass.getpass('Password ({0}): '.format(self.args.username))): print('error: unable to authenticate with canvas service.') return 1 # add template bits that are specified if self.args.title is not None: t.title = self.args.title if self.args.description is not None: t.description = self.args.description if self.args.includes is not None: t.includes = self.args.includes if self.args.public is not None: t.public = self.args.public try: res = self.cs.template_create(t) except ServiceException as e: print(e) return 1 print('info: template added.') return 0 def run_copy(self): t = Template(self.args.template_from, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # reparse for template destination t.parse(self.args.template_to) try: res = self.cs.template_create(t) except ServiceException as e: print(e) return 1 print('info: template copied.') return 0 def run_diff(self): t = Template(self.args.template_from, user=self.args.username) # grab the template we're pushing to try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # fetch template to compare to if self.args.template_to is not None: ts = Template(self.args.template_to, user=self.args.username) try: ts = self.cs.template_get(ts) except ServiceException as e: print(e) return 1 # otherwise build from system else: ts = Template('system') ts.from_system() (l_r, r_l) = t.package_diff(ts.packages_all) print("In template not in system:") for p in l_r: print(" - {0}".format(p.name)) print() print("On system not in template:") for p in r_l: print(" + {0}".format(p.name)) print() def run_dump(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 if self.args.yaml: print(yaml.dump(t.to_object(), indent=4)) return 0 elif self.args.json: print(json.dumps(t.to_object(), indent=4)) return 0 # pretty general information print('Name: {0} ({1})'.format(t.name, t.user)) print('Description:\n{0}\n'.format(t.description)) # pretty print includes if len(t.includes): print('Includes:') for i in t.includes: print(' - {0}'.format(i)) # pretty print packages repos = list(t.repos_all) repos.sort(key=lambda x: x.stub) if len(repos): l = prettytable.PrettyTable(['repo', 'name', 'priority', 'cost', 'enabled']) l.min_table_width=120 l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 for r in repos: if r.cost is None: r.cost = '-' if r.priority is None: r.priority = '-' if r.enabled: r.enabled = 'Y' else: r.enabled = 'N' l.add_row([r.stub, r.name, r.priority, r.cost, r.enabled]) print(l) print() # pretty print packages packages = list(t.packages_all) packages.sort(key=lambda x: x.name) if len(packages): l = prettytable.PrettyTable(['package', 'epoch', 'version', 'release', 'arch', 'action']) l.min_table_width=120 l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 for p in packages: if p.epoch is None: p.epoch = '-' if p.version is None: p.version = '-' if p.release is None: p.release = '-' if p.arch is None: p.arch = '-' if p.included(): p.action = '+' else: p.action = '-' l.add_row([p.name, p.epoch, p.version, p.release, p.arch, p.action]) print(l) print() return 0 def run_list(self): # don't auth if looking for public only if not self.args.public_only: try: self.cs.authenticate() except ServiceException as e: print(e) return 1 # fetch all accessible/available templates try: templates = self.cs.template_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description ) except ServiceException as e: print(e) return 1 if len(templates): l = prettytable.PrettyTable(["user:name", "title"]) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 # add table items and print for t in templates: l.add_row(["{0}:{1}".format(t['username'], t['stub']), t['name']]) print(l) # print summary print('\n{0} template(s) found.'.format(len(templates))) else: print('0 templates found.') def run_pull(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # prepare dnf print('info: analysing system ...') db = dnf.Base() # install repos from template for r in t.repos_all: dr = r.to_repo() dr.load() db.repos.add(dr) db.read_comps() try: db.fill_sack() except OSError as e: pass multilib_policy = db.conf.multilib_policy clean_deps = db.conf.clean_requirements_on_remove # process all packages in template for p in t.packages_all: if p.included(): # # stripped from dnf.base install() in full and optimesd # for canvas usage subj = dnf.subject.Subject(p.to_pkg_spec()) if multilib_policy == "all" or subj.is_arch_specified(db.sack): q = subj.get_best_query(db.sack) if not q: continue already_inst, available = db._query_matches_installed(q) for a in available: db._goal.install(a, optional=False) elif multilib_policy == "best": sltrs = subj.get_best_selectors(db.sack) match = reduce(lambda x, y: y.matches() or x, sltrs, []) if match: for sltr in sltrs: if sltr.matches(): db._goal.install(select=sltr, optional=False) else: # # stripped from dnf.base remove() in full and optimesd # for canvas usage matches = dnf.subject.Subject(p.to_pkg_spec()).get_best_query(db.sack) for pkg in matches.installed(): db._goal.erase(pkg, clean_deps=clean_deps) db.resolve() # describe process for dry runs if self.args.dry_run: packages_install = list(db.transaction.install_set) packages_install.sort(key=lambda x: x.name) packages_remove = list(db.transaction.remove_set) packages_remove.sort(key=lambda x: x.name) if len(packages_install) or len(packages_remove): print('The following would be installed to (+) and removed from (-) the system:') for p in packages_install: print(' + ' + str(p)) for p in packages_remove: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages_install)+len(packages_remove))) print() else: print('No system changes required.') print('No action peformed during this dry-run.') return 0 # TODO: progress for download, install and removal db.download_packages(list(db.transaction.install_set)) return db.do_transaction() def run_push(self): t = Template(self.args.template, user=self.args.username) # grab the template we're pushing to try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # prepare dnf print('info: analysing system ...') db = dnf.Base() db.read_all_repos() db.read_comps() try: db.fill_sack() except OSError as e: pass db_list = db.iter_userinstalled() if self.args.push_all: db_list = db.sack.query().installed() # add our user installed packages for p in db_list: # no need to store versions t.add_package(Package(p, evr=False)) # add only enabled repos for r in db.repos.enabled(): t.add_repo(Repository(r)) packages = list(t.packages_delta) packages.sort(key=lambda x: x.name) repos = list(t.repos_delta) repos.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages) or len(repos): print('The following would be added to the template: {0}'.format(t.name)) for p in packages: print(' - ' + str(p)) for r in repos: print(' - ' + str(r)) print() print('Summary:') print(' - Package(s): %d' % ( len(packages) )) print(' - Repo(s): %d' % ( len(repos) )) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(packages) and not len(repos): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: template pushed.') return 0 def run_rm(self): t = Template(self.args.template, user=self.args.username) try: res = self.cs.template_delete(t) except ServiceException as e: print(e) return 1 print('info: template removed.') return 0 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # add template bits that are specified for update if self.args.title is not None: t.title = self.args.title if self.args.description is not None: t.description = self.args.description if self.args.includes is not None: t.includes = self.args.includes if self.args.public is not None: t.public = self.args.public try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: template updated.') return 0
class MachineCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] machine [<args>]\n" "{0} machine add [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine update [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine list [user] [--filter-name] [--filter-description]\n" "{0} machine rm [user:]name\n" "{0} machine diff [user:]name [--output=path]\n" "{0} machine connect [user:]name\n" "{0} machine cmd [user:]name command arg1 arg2 ... argN\n" "{0} machine sync [user:]name [--pull [[user:]template]] | --push [user:]template]\n" "{0} machine disconnect [user:]name\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} machine add [user:]machine [user:]template [--title] [--description]\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): m = Machine(self.args.machine, user=self.args.username) t = Template(self.args.template, user=self.args.username) # grab the template we're associating to the machine try: t = self.cs.template_get(t, auth=True) except ServiceException as e: print(e) return 1 # add template uuid to machine m.template = t.uuid # add machine bits that are specified if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_create(m) except ServiceException as e: print(e) return 1 print(res) # update config with our newly added (registered) machine self.config.set('machine', 'uuid', res['uuid']) self.config.set('machine', 'key', res['key']) self.config.save() print('info: machine added.') return 0 def run_cmd(self): print('MACHINE CMD') def run_diff(self): uuid = self.config.get('machine', 'uuid') key = self.config.get('machine', 'key') try: res = self.cs.machine_sync(uuid, key, template=True) except ServiceException as e: print(e) return 1 m = Machine(res['template']) t = Template(res['template']) ts = Template('system') ts.from_system() (l_r, r_l) = t.package_diff(ts.packages_all) print("In template not in system:") for p in l_r: print(" - {0}".format(p.name)) print() print("On system not in template:") for p in r_l: print(" + {0}".format(p.name)) print() def run_list(self): # fetch all accessible/available templates try: machines = self.cs.machine_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description ) except ServiceException as e: print(e) return 1 if len(machines): l = prettytable.PrettyTable(["user:name", "title"]) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 # add table items and print for m in machines: l.add_row(["{0}:{1}".format(m['username'], m['stub']), m['name']]) print(l) # print summary print('\n{0} machine(s) found.'.format(len(machines))) else: print('0 machines found.') def run_rm(self): m = Machine(self.args.machine, user=self.args.username) try: res = self.cs.machine_delete(m) except ServiceException as e: print(e) return 1 self.config.unset('machine', 'uuid') self.config.unset('machine', 'key') self.config.save() print('info: machine removed.') return 0 def run_sync(self): uuid = self.config.get('machine', 'uuid') key = self.config.get('machine', 'key') try: res = self.cs.machine_sync(uuid, key, template=True) except ServiceException as e: print(e) return 1 m = Machine(res['template']) t = Template(res['template']) # prepare dnf print('info: analysing system ...') db = dnf.Base() # install repos from template for r in t.repos_all: dr = r.to_repo() try: dr.load() db.repos.add(dr) except dnf.exceptions.RepoError as e: print(e) return 1 db.read_comps() try: db.fill_sack() except OSError as e: pass multilib_policy = db.conf.multilib_policy clean_deps = db.conf.clean_requirements_on_remove # process all packages in template for p in t.packages_all: if p.included(): # # stripped from dnf.base install() in full and optimesd # for canvas usage subj = dnf.subject.Subject(p.to_pkg_spec()) if multilib_policy == "all" or subj.is_arch_specified(db.sack): q = subj.get_best_query(db.sack) if not q: continue already_inst, available = db._query_matches_installed(q) for a in available: db._goal.install(a, optional=False) elif multilib_policy == "best": sltrs = subj.get_best_selectors(db.sack) match = reduce(lambda x, y: y.matches() or x, sltrs, []) if match: for sltr in sltrs: if sltr.matches(): db._goal.install(select=sltr, optional=False) else: # # stripped from dnf.base remove() in full and optimesd # for canvas usage matches = dnf.subject.Subject(p.to_pkg_spec()).get_best_query(db.sack) for pkg in matches.installed(): db._goal.erase(pkg, clean_deps=clean_deps) db.resolve() # describe process for dry runs if self.args.dry_run: packages_install = list(db.transaction.install_set) packages_install.sort(key=lambda x: x.name) packages_remove = list(db.transaction.remove_set) packages_remove.sort(key=lambda x: x.name) if len(packages_install) or len(packages_remove): print('The following would be installed to (+) and removed from (-) the system:') for p in packages_install: print(' + ' + str(p)) for p in packages_remove: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages_install)+len(packages_remove))) print() else: print('No system changes required.') print('No action peformed during this dry-run.') return 0 # TODO: progress for download, install and removal db.download_packages(list(db.transaction.install_set)) return db.do_transaction() print('info: machine synced.') return 0 def run_update(self): m = Machine(self.args.machine, user=self.args.username) try: m = self.cs.machine_get(m) except ServiceException as e: print(e) return 1 # add machine bits that are specified for update if self.args.template: t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 m.template = t.uuid if self.args.name is not None: m.name = self.args.name if self.args.title is not None: m.title = self.args.title if self.args.title is not None: m.title = self.args.title if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_update(m) except ServiceException as e: print(e) return 1 print('info: machine updated.') return 0
class RepoCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # eval enabled try: if args.enabled is not None: args.enabled = (args.enabled.lower() in ['1', 'true']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] repo [<args>]\n" "\n" "Specific usage:\n" "{0} repo add [user:]template[@version] repo_name [--filepath] [--baseurl] [--metalink] [--mirrorlist] [--cost] [--enabled] [--gpgkey] [--name] [--priority]\n" "{0} repo update [user:]template[@version] repo_name [--baseurl] [--metalink] [--mirrorlist] [--cost] [--enabled] [--gpgkey] [--name] [--priority]\n" "{0} repo list [user:] template[@version]\n" "{0} repo rm [user:]template[@version] repo_name\n" "\n".format(self.prog_name)) def help_add(self): pass def help_list(self): pass def help_rm(self): pass def help_update(self): print("Usage: {0} repo update [user:]template[@version] repo_name [--baseurl] [--metalink] [--mirrorlist] [--cost] [--enabled] [--gpgkey] [--name] [--priority]\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # default enabled if self.args.enabled is None: self.args.enabled = True r = Repository({ 'stub' : self.args.repo, 'name' : self.args.name, 'baseurl' : self.args.baseurl, 'mirrorlist' : self.args.mirrorlist, 'metalink' : self.args.metalink, 'enabled' : self.args.enabled, 'cost' : self.args.cost, 'priority' : self.args.priority, 'gpgkey' : self.args.gpgkey, 'gpgcheck' : self.args.gpgcheck, 'exclude' : self.args.exclude }) t.add_repo(r) # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: repo added.') return 0 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 r = t.find_repo(self.args.repo) if len(r) != 1: print('error: repo is not defined in template.') return 1 r = r[0] # reset baseurl, metalink and mirrorlist when any are specified if self.args.baseurl is not None or self.args.metalink is not None or self.args.mirrorlist is not None: r.baseurl = None r.mirrorlist = None r.metalink = None if self.args.baseurl is not None: r.baseurl = self.args.baseurl if self.args.cost is not None: r.cost = self.args.cost if self.args.enabled is not None: r.enabled = self.args.enabled if self.args.gpgcheck is not None: r.gpgcheck = self.args.gpgcheck if self.args.gpgkey is not None: r.gpgkey = self.args.gpgkey if self.args.metalink is not None: r.metalink = self.args.metalink if self.args.mirrorlist is not None: r.mirrorlist = self.args.mirrorlist if self.args.name is not None: r.name = self.args.name if self.args.priority is not None: r.priority= self.args.priority if not t.update_repo(r): print('info: no changes detected.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: repo updated.') return 0 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 repos = list(t.repos_all) repos.sort(key=lambda x: x.stub) if len(repos): l = TextTable(header=['REPO', 'NAME', 'PRIORITY', 'COST', 'ENABLED']) for r in repos: cost = r.cost if cost is None: cost = '-' priority = '-' if priority is None: priority = '-' enabled = r.enabled if enabled: enabled = 'Y' l.add_row([r.stub, r.name, priority, cost, enabled]) print(l) print() else: print('0 repos defined.') def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 repos = [] for r in self.args.repo: r = Repository(r) if t.remove_repo(r): repos.append(r) repos.sort(key=lambda x: x.stub) # describe process for dry runs if self.args.dry_run: if len(repos): print('The following would be removed from the template: {0}'.format(t.name)) for r in repos: print(' - ' + str(r)) print() print('Summary:') print(' - Repo(s): %d' % (len(repos))) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(repos): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1
class TemplateCommand(Command): def configure(self, config, args, args_extra, parsers): if args.action == None: parsers.template.print_help() sys.exit(1) # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) try: # expand includes if args.includes is not None: args.includes = args.includes.split(',') except: pass # eval public try: if args.public is not None: args.public = (args.public.lower() in ['1', 'true', 'yes', 'y']) except: pass # store args for additional processing self.args = args def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: logging.error('Action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) # add template bits that are specified if self.args.title is not None: t.title = self.args.title if self.args.description is not None: t.description = self.args.description if self.args.includes is not None: t.includes = self.args.includes if self.args.public is not None: t.public = self.args.public try: res = self.cs.template_create(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template added.') return 0 def run_copy(self): t = Template(self.args.template_from, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 # reparse for template destination t.parse(self.args.template_to) try: res = self.cs.template_create(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template copied.') return 0 def run_diff(self): t = Template(self.args.template_from, user=self.args.username) # grab the template we're pushing to try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 # fetch template to compare to if self.args.template_to is not None: ts = Template(self.args.template_to, user=self.args.username) try: ts = self.cs.template_get(ts) except ServiceException as e: logging.exception(e) return 1 # otherwise build from system else: ts = Template.from_system() (l_r, r_l) = t.package_diff(ts.packages_all) if len(l_r): print('In template and not marked for install in system:') for p in l_r: print(" * {0}".format(p.name)) print() if len(r_l): print('Marked for install on system and not in template:') for p in r_l: print(" * {0}".format(p.name)) print() def run_dump(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get( t, resolve_includes=not self.args.no_resolve_includes) except ServiceException as e: logging.exception(e) return 1 if self.args.kickstart: print(t.to_kickstart(resolved=not self.args.no_resolve_includes)) return 0 elif self.args.yaml: print( yaml.dump( t.to_object(resolved=not self.args.no_resolve_includes), indent=4)) return 0 elif self.args.json: print( json.dumps( t.to_object(resolved=not self.args.no_resolve_includes), indent=4, sort_keys=True)) return 0 # pretty general information print('TEMPLATE: {0} ({1})\n'.format(t.name, t.user)) if t.description is not None and len(t.description): print('Description:\n{0}\n'.format(t.description)) # pretty print includes if len(t.includes): l = TextTable(header=['INCLUDE']) for i in t.includes: l.add_row([i]) print(l) print() # pretty print packages repos = list(t.repos_all) repos.sort(key=lambda x: x.stub) if len(repos): l = TextTable(header=["REPO", "NAME", "ENABLED", "TEMPLATE"]) for r in repos: cost = r.cost if cost is None: cost = '-' priority = r.priority if priority is None: priority = '-' enabled = 'N' if r.enabled: enabled = 'Y' template = r.template if template == t.unv: template = '' #l.add_row([r.stub, r.name, priority, cost, enabled]) l.add_row([r.stub, r.name, enabled, template]) print(l) print() # pretty print packages packages = list(t.packages_all) packages.sort(key=lambda x: x.name) if len(packages): l = TextTable(header=["PACKAGE", "ACTION", "TEMPLATE"]) for p in packages: if p.included: p.action = '+' elif p.excluded: p.action = '-' elif p.ignored: p.action = '!' else: p.action = '?' # no template specified indicates it's part of this template if p.template == t.unv: p.template = '' l.add_row([p.name, p.action, p.template]) print(l) print() return 0 def run_iso(self): t = Template(self.args.template, user=self.args.username) if self.args.use_livecd_creator: iso_creator = 'livecd-creator' install_package = 'livecd-tools' else: iso_creator = 'livemedia-creator' install_package = 'lorax' # check for required software try: subprocess.run([iso_creator, "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) except FileNotFoundError: logging.error( 'You need to install the "{0}" package to create ISOs using {1}.' .format(install_package, iso_creator)) return 1 try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 # calculate name and title for use if not specified arch = os.uname()[4] if arch in ['i386', 'i686']: arch_pretty = '32 bit' arch_pretty_long = '32 bit (%s)' % (arch) elif arch in ['x86_64']: arch_pretty = '64 bit' arch_pretty_long = '64 bit (%s)' % (arch) # we'll use the release version in our names if self.args.releasever is None: if t.version is None or t.version is '': # default to release ver of installed system at / self.args.releasever = dnf.rpm.detect_releasever('/') else: self.args.releasever = t.version name = "{0}".format(t.name) name_long = "{0}-{1}-{2}".format(t.name, self.args.releasever, arch) name_pretty = "{0}-{1}-{2}".format(t.name, self.args.releasever, arch_pretty) title = "{0}".format(t.title) title_long = "{0} - {1} - {2}".format(t.title, self.args.releasever, arch_pretty) title_pretty_long = "{0} - {1} - {2}".format(t.title, self.args.releasever, arch_pretty_long) # build missing strings if self.args.build_dir is None: self.args.build_dir = "/var/tmp/canvas/{0}-{1}".format( name_long.lower(), t.uuid) tmp_dir = os.path.join(self.args.build_dir, 'tmp') if self.args.result_dir is None: self.args.result_dir = os.path.join(self.args.build_dir, 'iso') if self.args.cache_dir is None: self.args.cache_dir = os.path.join(self.args.build_dir, 'cache') if self.args.iso_name is None: self.args.iso_name = "{0}.iso".format(name_long.lower()) if self.args.project is None: self.args.project = title_pretty_long if self.args.volid is None: self.args.volid = name_long.lower() if self.args.title is None: self.args.title = title if self.args.logfile is None: self.args.logfile = os.path.join( self.args.build_dir, "{0}.log".format(name_long.lower())) # build kickstart file ks_file = "canvas-{0}.ks".format(t.uuid) ks_path = os.path.join(self.args.build_dir, "ks", ks_file) try: # ensure our result_dir exists if not os.path.exists(os.path.dirname(ks_path)): os.makedirs(os.path.dirname(ks_path)) with open(ks_path, 'w') as f: f.write(t.to_kickstart(resolved=True)) except IOError as e: logging.error( 'You need root privileges to build iso at this location.') return 1 env = os.environ.copy() logging.info('Build directory: {0}'.format(self.args.build_dir)) logging.info('Cache directory: {0}'.format(self.args.cache_dir)) logging.info('Result directory: {0}'.format(self.args.result_dir)) logging.info('Log file: {0}'.format(self.args.logfile)) logging.info('ISO name: {0}'.format(self.args.iso_name)) logging.info('Project: {0}'.format(self.args.project)) logging.info('Title: {0}'.format(self.args.title)) logging.info('Volumne ID: {0}'.format(self.args.volid)) working_dir = None # livecd-creator if self.args.use_livecd_creator: logging.info('Building via livecd-creator ...') working_dir = self.args.result_dir args = [ iso_creator, '--verbose', '--config', ks_path, '--fslabel', name_long.lower(), '--title', self.args.title, '--releasever', self.args.releasever, '--product', self.args.project, '--cache', self.args.cache_dir, '--tmpdir', tmp_dir, '--logfile', self.args.logfile ] env["setarch"] = arch # livemedia-creator else: logging.info('Building via livemedia-creator ...') args = [ iso_creator, '--no-virt', '--make-iso', '--iso-only', '--macboot', '--ks', ks_path, '--resultdir', self.args.result_dir, '--project', self.args.project, '--volid', self.args.volid, '--iso-name', self.args.iso_name, '--releasever', self.args.releasever, '--title', self.args.title, '--logfile', self.args.logfile ] logging.debug('Build args:', args) # ensure working directory exists if set if working_dir is not None: os.makedirs(working_dir, exist_ok=True) subprocess.run(args, cwd=working_dir, env=env) return 0 def run_list(self): # fetch all accessible/available templates try: templates = self.cs.template_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description, public=self.args.public_only) except ServiceException as e: logging.exception(e) return 1 if len(templates): l = TextTable(header=["[USER:]NAME", "TITLE", "VERSIONS"]) # compress versions template_versions = {} for t in templates: un = "{0}:{1}".format(t['username'], t['stub']) a = template_versions.get(un, { 'name': t['username'], 'versions': [] }) a['versions'].append(t['version']) template_versions[un] = a # add table items and print for k, v in template_versions.items(): l.add_row([ k, v['name'], ', '.join([ x if x is not '' else '*' for x in sorted(v['versions']) ]) ]) print(l) # print summary print('\n{0} template(s) found.'.format(len(templates))) else: print('0 templates found.') def run_pull(self): # am i effectively root if not self.args.dry_run and os.geteuid() != 0: logging.error( 'You need to have root privileges to modify the system.') return 0 t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 t.system_prepare(clean=self.args.pull_clean) # describe process for dry runs if self.args.dry_run: tx = t.system_transaction() packages_install = list(tx.install_set) packages_install.sort(key=lambda x: x.name) packages_remove = list(tx.remove_set) packages_remove.sort(key=lambda x: x.name) if len(packages_install) or len(packages_remove): print( 'The following would be installed to (+) and removed from (-) the system:' ) for p in packages_install: print(' + ' + str(p)) for p in packages_remove: print(' - ' + str(p)) print() print('Summary:') if len(packages_install): print(' - %d package(s) installed' % (len(packages_install))) if len(packages_remove): print(' - %d package(s) removed' % (len(packages_remove))) print() else: print('No system changes required.') logging.info('No action peformed during this dry-run.') return 0 t.system_apply() def run_push(self): t = Template(self.args.template, user=self.args.username) # grab the template we're pushing to try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 if self.args.push_clean: t.clear() if self.args.kickstart is not None: logging.info('Parsing kickstart ...') t.from_kickstart(self.args.kickstart) else: # prepare dnf logging.info('Analysing system ...') db = dnf.Base() db.read_all_repos() db.read_comps() try: db.fill_sack() except OSError as e: pass db_list = db.iter_userinstalled() if self.args.push_all: db_list = db.sack.query().installed() # add our user installed packages for p in db_list: # no need to store versions t.add_package(Package(p, evr=False)) # add only enabled repos for r in db.repos.enabled(): t.add_repo(Repository(r)) objects = list(t.objects_delta) objects.sort(key=lambda x: x.name) packages = list(t.packages_delta) packages.sort(key=lambda x: x.name) repos = list(t.repos_delta) repos.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages) or len(repos): print( 'The following would be added to the template: {0}'.format( t.name)) for r in repos: print(' - ' + str(r)) for p in packages: print(' - ' + str(p)) for o in objects: print(' - ' + str(o)) print() print('Summary:') print(' - Repo(s): %d' % (len(repos))) print(' - Package(s): %d' % (len(packages))) print(' - Object(s): %d' % (len(objects))) print() else: print('No template changes required.') logging.info('No action peformed during this dry-run.') return 0 if self.args.kickstart is None and not len(packages) and not len( repos): logging.info('No changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template pushed.') return 0 def run_rm(self): t = Template(self.args.template, user=self.args.username) try: res = self.cs.template_delete(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template removed.') return 0 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t, resolve_includes=False) except ServiceException as e: logging.exception(e) return 1 # add template bits that are specified for update if self.args.title is not None: t.title = self.args.title if self.args.description is not None: t.description = self.args.description if self.args.includes is not None: t.includes = self.args.includes if self.args.public is not None: t.public = self.args.public try: res = self.cs.template_update(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template updated.') return 0
class PackageCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, "help_{0}".format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print( "General usage: {0} [--version] [--help] [--verbose] package [<args>]\n" "\n" "Specific usage:\n" "{0} package add [user:]template[@version] [--nodeps] package1 packagelist1 package2 ... packageN\n" "{0} package list [user:]template[@version] [--filter-name] [--filter-summary] [--filter-description] [--filter-arch] [--filter-repo] [--output=path]\n" "{0} package rm [user:]template[@version] [--nodeps] package1 package2 ... packageN\n" "{0} package update [user:]template[@version] [--nodeps] package1 packagelist1 package2 ... packageN\n" "\n".format(self.prog_name) ) def help_add(self): print( "Usage: {0} package add [user:]template[@version] [--nodeps]\n" " package1 packagelist1 package2 ... packageN\n" "\n" "Options:\n" " --nodeps Do not automatically remove dependencies\n" "\n".format(self.prog_name) ) def help_list(self): pass def help_rm(self): pass def help_update(self): pass def run(self): command = None # search for our function based on the specified action try: command = getattr(self, "run_{0}".format(self.args.action)) except: self.help() return 1 if not command: print("error: action is not reachable.") return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 for p in self.args.package: try: pkg = Package(p) except TypeError as e: print(e) return 1 t.add_package(pkg) packages = list(t.packages_delta) packages.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages): print("The following would be added to the template: {0}".format(t.name)) for p in packages: print(" - " + str(p)) print() print("Summary:") print(" - Package(s): %d" % (len(packages))) print() else: print("No template changes required.") print("No action peformed during this dry-run.") return 0 if not len(packages): print("info: no changes detected, template up to date.") return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 packages = list(t.packages_all) packages.sort(key=lambda x: x.name) if len(packages): l = TextTable(header=["PACKAGE", "EPOCH", "VERSION", "RELEASE", "ARCH", "ACTION"]) for p in packages: if p.epoch is None: p.epoch = "-" if p.version is None: p.version = "-" if p.release is None: p.release = "-" if p.arch is None: p.arch = "-" if p.included: p.action = "+" else: p.action = "-" l.add_row([p.name, p.epoch, p.version, p.release, p.arch, p.action]) print(l) print() else: print("0 packages defined.") def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 packages = [] for p in self.args.package: p = Package(p) if t.remove_package(p): packages.append(p) packages.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages): print("The following would be removed from the template: {0}".format(t.name)) for p in packages: print(" - " + str(p)) print() print("Summary:") print(" - Package(s): %d" % (len(packages))) print() else: print("No template changes required.") print("No action peformed during this dry-run.") return 0 if not len(packages): print("info: no changes detected, template up to date.") return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # track updates to determine server update updated = False for p in self.args.package: # parse new and find old pn = Package(p) print(pn) if pn not in t.packages: print("warn: package is not defined in template.") continue # update with new and track if t.update_package(pn): updated = True if not updated: print("info: no changes detected.") return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print("info: package(s) updated.") return 0
class MachineCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] machine [<args>]\n" "{0} machine add [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine update [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine list [user] [--filter-name] [--filter-description]\n" "{0} machine rm [user:]name\n" "{0} machine diff [user:]name [--output=path]\n" "{0} machine connect [user:]name\n" "{0} machine cmd [user:]name command arg1 arg2 ... argN\n" "{0} machine sync [user:]name [--pull [[user:]template]] | --push [user:]template]\n" "{0} machine disconnect [user:]name\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} machine add [user:]machine [user:]template [--title] [--description]\n" "\n".format(self.prog_name)) def help_cmd(self): pass def help_connect(self): pass def help_disconnect(self): pass def help_diff(self): pass def help_list(self): pass def help_rm(self): pass def help_sync(self): pass def help_update(self): pass def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): m = Machine(self.args.machine, user=self.args.username) t = Template(self.args.template, user=self.args.username) # grab the template we're associating to the machine try: t = self.cs.template_get(t, auth=True, resolve_includes=False) except ServiceException as e: print(e) return 1 # add template uuid to machine m.template = t.uuid # add machine bits that are specified if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_create(m) except ServiceException as e: print(e) return 1 print(res) # update config with our newly added (registered) machine self.config.set('machine', 'uuid', res['uuid']) self.config.set('machine', 'key', res['key']) self.config.save() print('info: machine added.') return 0 def run_cmd(self): print('MACHINE CMD') def run_diff(self): uuid = self.config.get('machine', 'uuid') key = self.config.get('machine', 'key') try: res = self.cs.machine_sync(uuid, key, template=True) except ServiceException as e: print(e) return 1 m = Machine(res['template']) t = Template(res['template']) ts = Template.from_system() (l_r, r_l) = t.package_diff(ts.packages_all) print("In template not in system:") for p in l_r: print(" - {0}".format(p.name)) print() print("On system not in template:") for p in r_l: print(" + {0}".format(p.name)) print() def run_list(self): # fetch all accessible/available templates try: machines = self.cs.machine_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description ) except ServiceException as e: print(e) return 1 if len(machines): l = TextTable(['[USER:]NAME', 'TITLE']) # add table items and print for m in machines: l.add_row(["{0}:{1}".format(m['username'], m['stub']), m['name']]) print(l) # print summary print('\n{0} machine(s) found.'.format(len(machines))) else: print('0 machines found.') def run_rm(self): m = Machine(self.args.machine, user=self.args.username) try: res = self.cs.machine_delete(m) except ServiceException as e: print(e) return 1 self.config.unset('machine', 'uuid') self.config.unset('machine', 'key') self.config.save() print('info: machine removed.') return 0 def run_sync(self): uuid = self.config.get('machine', 'uuid') key = self.config.get('machine', 'key') try: res = self.cs.machine_sync(uuid, key, template=True) except ServiceException as e: print(e) return 1 m = Machine(res['template']) t = Template(res['template']) t.system_prepare() # describe process for dry runs if self.args.dry_run: tx = t.system_transaction() packages_install = list(tx.install_set) packages_install.sort(key=lambda x: x.name) packages_remove = list(tx.remove_set) packages_remove.sort(key=lambda x: x.name) if len(packages_install) or len(packages_remove): print('The following would be installed to (+) and removed from (-) the system:') for p in packages_install: print(' + ' + str(p)) for p in packages_remove: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages_install)+len(packages_remove))) print() else: print('No system changes required.') print('No action peformed during this dry-run.') return 0 # TODO: progress for download, install and removal t.to_system_apply(clean=False) print('info: machine synced.') return 0 def run_update(self): m = Machine(self.args.machine, user=self.args.username) try: m = self.cs.machine_get(m) except ServiceException as e: print(e) return 1 # add machine bits that are specified for update if self.args.template: t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t, resolve_includes=False) except ServiceException as e: print(e) return 1 m.template = t.uuid if self.args.name is not None: m.name = self.args.name if self.args.title is not None: m.title = self.args.title if self.args.title is not None: m.title = self.args.title if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_update(m) except ServiceException as e: print(e) return 1 print('info: machine updated.') return 0
class RepoCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # eval enabled try: if args.enabled is not None: args.enabled = (args.enabled.lower() in ['1', 'true']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: print('command: not implemented') return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # default enabled if self.args.enabled is None: self.args.enabled = True r = Repository( stub = self.args.repo, name = self.args.name, baseurl = self.args.baseurl, mirrorlist = self.args.mirrorlist, metalink = self.args.metalink, enabled = self.args.enabled, cost = self.args.cost, priority = self.args.priority, gpgkey = self.args.gpgkey, gpgcheck = self.args.gpgcheck, exclude = self.args.exclude ) t.add_repo(r) # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: repo added.') return 0 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 r = t.find_repo(self.args.repo) if len(r) != 1: print('error: repo is not defined in template.') return 1 r = r[0] o = { 'n': self.args.name, 'bu': self.args.baseurl, 'ml': self.args.mirrorlist, 'ma': self.args.metalink, 'e': self.args.enabled, 'c': self.args.cost, 'p': self.args.priority, 'gc': self.args.gpgcheck, 'gk': self.args.gpgkey, 'sk': self.args.skip, 'x': self.args.exclude } o = {k: v for k, v in o.items() if v != None} r.parse(o) if not t.update_repo(r): print('error: no changes detected.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: repo updated.') return 0 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 repos = list(t.repos_all) repos.sort(key=lambda x: x.stub) if len(repos): l = prettytable.PrettyTable(['repo', 'name', 'priority', 'cost', 'enabled']) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 for r in repos: if r.cost is None: r.cost = '-' if r.priority is None: r.priority = '-' if r.enabled: r.enabled = 'Y' else: r.enabled = 'N' l.add_row([r.stub, r.name, r.priority, r.cost, r.enabled]) print(l) print() else: print('0 repos defined.') def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 repos = [] for r in self.args.repo: r = Repository(r) if t.remove_repo(r): repos.append(r) repos.sort(key=lambda x: x.stub) # describe process for dry runs if self.args.dry_run: if len(repos): print('The following would be removed from the template: {0}'.format(t.name)) for r in repos: print(' - ' + str(r)) print() print('Summary:') print(' - Repo(s): %d' % ( len(repos) )) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(repos): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1
class PackageCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print( "General usage: {0} [--version] [--help] [--verbose] package [<args>]\n" "\n" "Specific usage:\n" "{0} package add [user:]template [--nodeps] package1 packagelist1 package2 ... packageN\n" "{0} package list [user:]template [--filter-name] [--filter-summary] [--filter-description] [--filter-arch] [--filter-repo] [--output=path]\n" "{0} package rm [user:]template [--nodeps] package1 package2 ... packageN\n" "{0} template list\n" "\n".format(self.prog_name)) def help_add(self): print( "Usage: {0} template add [user:]template [--name] [--title] [--description]\n" " [--includes] [--public]\n" "\n" "Options:\n" " --name NAME Define the pretty NAME of template\n" " --title TITLE Define the pretty TITLE of template\n" " --description TEXT Define descriptive TEXT of the template\n" " --includes TEMPLATE Define descriptive TEXT of the template\n" "\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: print('command: not implemented') return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 for p in self.args.package: t.add_package(Package(p)) packages = list(t.packages_delta) packages.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages): print( 'The following would be added to the template: {0}'.format( t.name)) for p in packages: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages))) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(packages): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 packages = list(t.packages_all) packages.sort(key=lambda x: x.name) if len(packages): l = prettytable.PrettyTable( ['package', 'epoch', 'version', 'release', 'arch', 'action']) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 for p in packages: if p.epoch is None: p.epoch = '-' if p.version is None: p.version = '-' if p.release is None: p.release = '-' if p.arch is None: p.arch = '-' if p.included(): p.action = '+' else: p.action = '-' l.add_row( [p.name, p.epoch, p.version, p.release, p.arch, p.action]) print(l) print() else: print('0 packages defined.') def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 packages = [] for p in self.args.package: p = Package(p) if t.remove_package(p): packages.append(p) packages.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages): print('The following would be removed from the template: {0}'. format(t.name)) for p in packages: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages))) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(packages): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # track updates to determine server update updated = False for p in self.args.package: # parse new and find old pn = Package(p) print(pn) if pn not in t.packages: print('warn: package is not defined in template.') continue # update with new and track if t.update_package(pn): updated = True if not updated: print('info: no changes detected.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: package(s) updated.') return 0
class TemplateCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) try: # expand includes if args.includes is not None: args.includes = args.includes.split(',') except: pass # eval public try: if args.public is not None: args.public = (args.public.lower() in ['1', 'true', 'yes', 'y']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] template [<args>]\n" "\n" "Specific usage:\n" "{0} template add [user:]template[@version] [--title] [--description] [--includes] [--public]\n" "{0} template update [user:]template[@version] [--title] [--description] [--includes] [--public]\n" "{0} template rm [user:]template[@version]\n" "{0} template push [user:]template[@version] [--all]\n" "{0} template pull [user:]template[@version] [--clean]\n" "{0} template diff [user:]template[@version]\n" "{0} template copy [user_from:]template_from[@version] [[user_to:]template_to[@version]]\n" "{0} template list [--public-only]\n" "{0} template dump [user:]template[@version] [--json|--kickstart|--yaml] [--no-resolve-includes]\n" "{0} template iso [user:]template[@version] [--releasever] [--livecd-creator]\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} template add [user:]template[@version] [--title] [--description]\n" " [--includes] [--public]\n" "\n" "Options:\n" " --title TITLE Define the pretty TITLE of template\n" " --description TEXT Define descriptive TEXT of the template\n" " --includes TEMPLATE Comma separated list of TEMPLATEs to include\n" "\n" "\n".format(self.prog_name)) def help_copy(self): print("Usage: {0} template copy [user_from:]template_from[@version] [user_to:]template_to[@version]\n" "\n" "\n".format(self.prog_name)) def help_diff(self): print("Usage: {0} template diff [user:]template[@version]\n" "\n" "\n".format(self.prog_name)) def help_dump(self): print("Usage: {0} template dump [user:]template[@version] [--json|--kickstart|--yaml] [--no-resolve-includes]\n" "\n" "Options:\n" " --json Output the template in JSON format\n" " --kickstart Output the template in kickstart format\n" " --yaml Output the template in YAML format\n" " --no-resolve-includes Do not resolve any template includes\n" "\n".format(self.prog_name)) def help_iso(self): print("Usage: {0} template iso [user:]template[@version] [--releasever] [--livecd-creator]\n" "\n" "\n".format(self.prog_name)) def help_list(self): print("Usage: {0} template list [--public-only]\n" "\n" "\n".format(self.prog_name)) def help_pull(self): print("Usage: {0} template pull [user:]template[@version] [--clean]\n" "\n" "\n".format(self.prog_name)) def help_push(self): print("Usage: {0} template push [user:]template[@version] [--all]\n" "\n" "\n".format(self.prog_name)) def help_update(self): print("Usage: {0} template update [user:]template[@version] [--title] [--description]\n" " [--includes] [--public]\n" "\n" "Options:\n" " --title TITLE Define the pretty TITLE of template\n" " --description TEXT Define descriptive TEXT of the template\n" " --includes TEMPLATE Comma separated list of TEMPLATEs to include\n" "\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: logging.error('Action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) # add template bits that are specified if self.args.title is not None: t.title = self.args.title if self.args.description is not None: t.description = self.args.description if self.args.includes is not None: t.includes = self.args.includes if self.args.public is not None: t.public = self.args.public try: res = self.cs.template_create(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template added.') return 0 def run_copy(self): t = Template(self.args.template_from, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 # reparse for template destination t.parse(self.args.template_to) try: res = self.cs.template_create(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template copied.') return 0 def run_diff(self): t = Template(self.args.template_from, user=self.args.username) # grab the template we're pushing to try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 # fetch template to compare to if self.args.template_to is not None: ts = Template(self.args.template_to, user=self.args.username) try: ts = self.cs.template_get(ts) except ServiceException as e: logging.exception(e) return 1 # otherwise build from system else: ts = Template.from_system() (l_r, r_l) = t.package_diff(ts.packages_all) if len(l_r): print('In template and not marked for install in system:') for p in l_r: print(" * {0}".format(p.name)) print() if len(r_l): print('Marked for install on system and not in template:') for p in r_l: print(" * {0}".format(p.name)) print() def run_dump(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t, resolve_includes=not self.args.no_resolve_includes) except ServiceException as e: logging.exception(e) return 1 if self.args.kickstart: print(t.to_kickstart(resolved=not self.args.no_resolve_includes)) return 0 elif self.args.yaml: print(yaml.dump(t.to_object(resolved=not self.args.no_resolve_includes), indent=4)) return 0 elif self.args.json: print(json.dumps(t.to_object(resolved=not self.args.no_resolve_includes), indent=4, sort_keys=True)) return 0 # pretty general information print('TEMPLATE: {0} ({1})\n'.format(t.name, t.user)) if t.description is not None and len(t.description): print('Description:\n{0}\n'.format(t.description)) # pretty print includes if len(t.includes): l = TextTable(header=['INCLUDE']) for i in t.includes: l.add_row([i]) print(l) print() # pretty print packages repos = list(t.repos_all) repos.sort(key=lambda x: x.stub) if len(repos): l = TextTable(header=["REPO", "NAME", "ENABLED"]) for r in repos: cost = r.cost if cost is None: cost = '-' priority = r.priority if priority is None: priority = '-' enabled = 'N' if r.enabled: enabled = 'Y' #l.add_row([r.stub, r.name, priority, cost, enabled]) l.add_row([r.stub, r.name, enabled]) print(l) print() # pretty print packages packages = list(t.packages_all) packages.sort(key=lambda x: x.name) if len(packages): l = TextTable(header=["PACKAGE", "ACTION"]) for p in packages: if p.included: p.action = '+' else: p.action = '-' l.add_row([p.name, p.action]) print(l) print() return 0 def run_iso(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 # calculate name and title for use if not specified arch = os.uname()[4] if arch in ['i386', 'i686']: arch_pretty = '32 bit' arch_pretty_long = '32 bit (%s)' % (arch) elif arch in ['x86_64']: arch_pretty = '64 bit' arch_pretty_long = '64 bit (%s)' % (arch) # we'll use the release version in our names if self.args.releasever is None: if t.version is None or t.version is '': # default to release ver of installed system at / self.args.releasever = dnf.rpm.detect_releasever('/') else: self.args.releasever = t.version name = "{0}".format(t.name) name_long = "{0}-{1}-{2}".format(t.name, self.args.releasever, arch) name_pretty = "{0}-{1}-{2}".format(t.name, self.args.releasever, arch_pretty) title = "{0}".format(t.title) title_long = "{0} - {1} - {2}".format(t.title, self.args.releasever, arch_pretty) title_pretty_long = "{0} - {1} - {2}".format(t.title, self.args.releasever, arch_pretty_long) # build missing strings if self.args.resultdir is None: self.args.resultdir = "/var/tmp/canvas/isos/{0}-{1}".format(name_long.lower(), ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(8))) if self.args.iso_name is None: self.args.iso_name = "{0}.iso".format(name_long.lower()) if self.args.project is None: self.args.project = title if self.args.volid is None: self.args.volid = name_long.lower() if self.args.title is None: self.args.title = title_pretty_long if self.args.logfile is None: self.args.logfile = "/var/tmp/canvas/{0}.log".format(name_long.lower()) # build kickstart file ks_file = "canvas-{0}.ks".format(t.uuid) ks_path = os.path.join("/var/tmp/canvas/ks", ks_file) try: # ensure our resultdir exists if not os.path.exists(os.path.dirname(ks_path)): os.makedirs(os.path.dirname(ks_path)) with open(ks_path, 'w') as f: f.write(t.to_kickstart(resolved=True)) except IOError as e: logging.error('You need root privileges to build iso at this location.') return 1 env = os.environ.copy() # livecd-creator if self.args.use_livecd_creator: args = [ 'livecd-creator', '--verbose', '--config', ks_path, '--fslabel', name_long.lower(), '--title', self.args.title, '--releasever', self.args.releasever, '--product', self.args.project, '--cache', self.args.resultdir, '--logfile', self.args.logfile ] env["setarch"] = arch # livemedia-creator else: args = [ 'livemedia-creator', '--no-virt', '--make-iso', '--iso-only', '--macboot', '--ks', ks_path, '--resultdir', self.args.resultdir, '--project', self.args.project, '--volid', self.args.volid, '--iso-name', self.args.iso_name, '--releasever', self.args.releasever, '--title', self.args.title, '--logfile', self.args.logfile ] subprocess.run(args, env=env) return 0 def run_list(self): # fetch all accessible/available templates try: templates = self.cs.template_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description, public=self.args.public_only ) except ServiceException as e: logging.exception(e) return 1 if len(templates): l = TextTable(header=["[USER:]NAME", "TITLE"]) # add table items and print for t in templates: l.add_row(["{0}:{1}".format(t['username'], t['stub']), t['name']]) print(l) # print summary print('\n{0} template(s) found.'.format(len(templates))) else: print('0 templates found.') def run_pull(self): # am i effectively root if os.geteuid() != 0: logging.error('You need to have root privileges to modify the system.') return 0 t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 t.system_prepare(clean=self.args.pull_clean) # describe process for dry runs if self.args.dry_run: tx = t.system_transaction() packages_install = list(tx.install_set) packages_install.sort(key=lambda x: x.name) packages_remove = list(tx.remove_set) packages_remove.sort(key=lambda x: x.name) if len(packages_install) or len(packages_remove): print('The following would be installed to (+) and removed from (-) the system:') for p in packages_install: print(' + ' + str(p)) for p in packages_remove: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages_install)+len(packages_remove))) print() else: print('No system changes required.') logging.info('No action peformed during this dry-run.') return 0 t.system_apply() def run_push(self): t = Template(self.args.template, user=self.args.username) # grab the template we're pushing to try: t = self.cs.template_get(t) except ServiceException as e: logging.exception(e) return 1 if self.args.push_clean: t.clear() if self.args.kickstart is not None: logging.info('Parsing kickstart ...') t.from_kickstart(self.args.kickstart) else: # prepare dnf logging.info('Analysing system ...') db = dnf.Base() db.read_all_repos() db.read_comps() try: db.fill_sack() except OSError as e: pass db_list = db.iter_userinstalled() if self.args.push_all: db_list = db.sack.query().installed() # add our user installed packages for p in db_list: # no need to store versions t.add_package(Package(p, evr=False)) # add only enabled repos for r in db.repos.enabled(): t.add_repo(Repository(r)) objects = list(t.objects_delta) objects.sort(key=lambda x: x.name) packages = list(t.packages_delta) packages.sort(key=lambda x: x.name) repos = list(t.repos_delta) repos.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages) or len(repos): print('The following would be added to the template: {0}'.format(t.name)) for r in repos: print(' - ' + str(r)) for p in packages: print(' - ' + str(p)) for o in objects: print(' - ' + str(o)) print() print('Summary:') print(' - Repo(s): %d' % (len(repos))) print(' - Package(s): %d' % (len(packages))) print(' - Object(s): %d' % (len(objects))) print() else: print('No template changes required.') logging.info('No action peformed during this dry-run.') return 0 if self.args.kickstart is None and not len(packages) and not len(repos): logging.info('No changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template pushed.') return 0 def run_rm(self): t = Template(self.args.template, user=self.args.username) try: res = self.cs.template_delete(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template removed.') return 0 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t, resolve_includes=False) except ServiceException as e: logging.exception(e) return 1 # add template bits that are specified for update if self.args.title is not None: t.title = self.args.title if self.args.description is not None: t.description = self.args.description if self.args.includes is not None: t.includes = self.args.includes if self.args.public is not None: t.public = self.args.public try: res = self.cs.template_update(t) except ServiceException as e: logging.exception(e) return 1 logging.info('Template updated.') return 0
class MachineCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print( "General usage: {0} [--version] [--help] [--verbose] machine [<args>]\n" "{0} machine add [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine update [user:]name [--description=] [--location=] [--name=] [--template=]\n" "{0} machine list [user] [--filter-name] [--filter-description]\n" "{0} machine rm [user:]name\n" "{0} machine diff [user:]name [--output=path]\n" "{0} machine connect [user:]name\n" "{0} machine cmd [user:]name command arg1 arg2 ... argN\n" "{0} machine sync [user:]name [--pull [[user:]template]] | --push [user:]template]\n" "{0} machine disconnect [user:]name\n" "\n".format(self.prog_name)) def help_add(self): print( "Usage: {0} machine add [user:]machine [user:]template [--title] [--description]\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: print('command: not implemented') return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): m = Machine(self.args.machine, user=self.args.username) t = Template(self.args.template, user=self.args.username) # grab the template we're associating to the machine try: t = self.cs.template_get(t, auth=True) except ServiceException as e: print(e) return 1 # add template uuid to machine m.template = t.uuid # add machine bits that are specified if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_create(m) except ServiceException as e: print(e) return 1 print(res) # update config with our newly added (registered) machine self.config.set('machine', 'uuid', res['uuid']) self.config.set('machine', 'key', res['key']) self.config.save() print('info: machine added.') return 0 def run_cmd(self): print('MACHINE CMD') def run_diff(self): print('MACHINE DIFF') def run_list(self): # fetch all accessible/available templates try: machines = self.cs.machine_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description) except ServiceException as e: print(e) return 1 if len(machines): l = prettytable.PrettyTable(["user:name", "title"]) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 # add table items and print for m in machines: l.add_row( ["{0}:{1}".format(m['username'], m['stub']), m['name']]) print(l) # print summary print('\n{0} machine(s) found.'.format(len(machines))) else: print('0 machines found.') def run_rm(self): m = Machine(self.args.machine, user=self.args.username) try: res = self.cs.machine_delete(m) except ServiceException as e: print(e) return 1 self.config.unset('machine', 'uuid') self.config.unset('machine', 'key') self.config.save() print('info: machine removed.') return 0 def run_sync(self): uuid = self.config.get('machine', 'uuid') key = self.config.get('machine', 'key') try: res = self.cs.machine_sync(uuid, key) except ServiceException as e: print(e) return 1 print(res) print('info: machine synced.') return 0 def run_update(self): m = Machine(self.args.machine, user=self.args.username) try: m = self.cs.machine_get(m) except ServiceException as e: print(e) return 1 # add machine bits that are specified for update if self.args.template: t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 m.template = t.uuid if self.args.name is not None: m.name = self.args.name if self.args.title is not None: m.title = self.args.title if self.args.title is not None: m.title = self.args.title if self.args.description is not None: m.description = self.args.description try: res = self.cs.machine_update(m) except ServiceException as e: print(e) return 1 print('info: machine updated.') return 0
class PackageCommand(Command): def configure(self, config, args, args_extra, parsers): if args.action == None: parsers.package.print_help() sys.exit(1) # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print( "General usage: {0} [--version] [--help] [--verbose] package [<args>]\n" "\n" "Specific usage:\n" "{0} package add [user:]template[@version] [--nodeps] package1 packagelist1 package2 ... packageN\n" "{0} package list [user:]template[@version] [--filter-name] [--filter-summary] [--filter-description] [--filter-arch] [--filter-repo] [--output=path]\n" "{0} package rm [user:]template[@version] [--nodeps] package1 package2 ... packageN\n" "{0} package update [user:]template[@version] [--nodeps] package1 packagelist1 package2 ... packageN\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} package add [user:]template[@version] [--nodeps]\n" " package1 packagelist1 package2 ... packageN\n" "\n" "Options:\n" " --nodeps Do not automatically remove dependencies\n" "\n".format(self.prog_name)) def help_list(self): pass def help_rm(self): pass def help_update(self): pass def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 for p in self.args.package: try: pkg = Package(p) except TypeError as e: print(e) return 1 t.add_package(pkg) packages = list(t.packages_delta) packages.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages): print( 'The following would be added to the template: {0}'.format( t.name)) for p in packages: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages))) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(packages): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 packages = list(t.packages_all) packages.sort(key=lambda x: x.name) if len(packages): l = TextTable(header=[ 'PACKAGE', 'EPOCH', 'VERSION', 'RELEASE', 'ARCH', 'ACTION' ]) for p in packages: if p.epoch is None: p.epoch = '-' if p.version is None: p.version = '-' if p.release is None: p.release = '-' if p.arch is None: p.arch = '-' if p.included: p.action = '+' elif p.excluded: p.action = '-' elif p.ignored: p.action = '!' else: p.action = '?' l.add_row( [p.name, p.epoch, p.version, p.release, p.arch, p.action]) print(l) print() else: print('0 packages defined.') def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 packages = [] for p in self.args.package: p = Package(p) if t.remove_package(p): packages.append(p) packages.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages): print('The following would be removed from the template: {0}'. format(t.name)) for p in packages: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages))) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if not len(packages): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # track updates to determine server update updated = False for p in self.args.package: # parse new and find old pn = Package(p) print(pn) if pn not in t.packages: print('warn: package is not defined in template.') continue # update with new and track if t.update_package(pn): updated = True if not updated: print('info: no changes detected.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: package(s) updated.') return 0
class ObjectCommand(Command): def configure(self, config, args, args_extra, parsers): if args.action == None: parsers.object.print_help() sys.exit(1) # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) # store args for additional processing self.args = args def help_add(self): print("Usage: {0} object add [user:]template[@version]\n" " [store:]object_name --data=|--data-file=|--source= --action= [--action= ...]\n" "\n" "Availablle actions are: \n" " copy DST_PATH\n" " extract DST_PATH\n" " execute\n" " execute-command COMMAND\n" " ks-pre\n" " ks-pre-install\n" " ks-post\n" "\n".format(self.prog_name)) def help_list(self): print("Usage: {0} object list [user:]template[@version] [--filter-store=...] [--filter-name=...]\n" "\n".format(self.prog_name)) def help_rm(self): print("Usage: {0} object rm [user:]template[@version] [store:]object_name [store2:]object_name2 ... [storeN:]object_nameN\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 try: obj = Object(name=self.args.object, data=self.args.data, data_file=self.args.data_file, source=self.args.source, xsum=self.args.xsum, actions=self.args.actions) except ErrorInvalidObject as e: print (e) return 1 t.add_object(obj) # describe process for dry runs if self.args.dry_run: print('The following object would be added to the template: {0}'.format(t.name)) print(' - ' + str(obj)) print() return 1 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 return 0 def run_list(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 objects = t.objects if objects: print('Template {0} has the following objects:'.format(t.name)) for o in t.objects: print(' - ' + str(o)) else: print('Template {0} has no objects.'.format(t.name)) def run_rm(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 for o in self.args.objects: try: obj = Object(name=o) except ErrorInvalidObject as e: print (e) return 1 t.remove_object(obj) if self.args.dry_run: # TODO: print changes return 1 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 return 0
class TemplateCommand(Command): def configure(self, config, args, args_extra): # store loaded config self.config = config # create our canvas service object self.cs = Service(host=args.host, username=args.username) try: # expand includes if args.includes is not None: args.includes = args.includes.split(',') except: pass # eval public try: if args.public is not None: args.public = (args.public.lower() in ['1', 'true']) except: pass # store args for additional processing self.args = args # return false if any error, help, or usage needs to be shown return not args.help def help(self): # check for action specific help first if self.args.action is not None: try: command = getattr(self, 'help_{0}'.format(self.args.action)) # show action specific if available if command: return command() except: pass # fall back to general usage print("General usage: {0} [--version] [--help] [--verbose] template [<args>]\n" "\n" "Specific usage:\n" "{0} template add [user:]template [--title] [--description] [--includes] [--public]\n" "{0} template update [user:]template [--title] [--description] [--includes] [--public]\n" "{0} template rm [user:]template\n" "{0} template push [user:]template [--all]\n" "{0} template pull [user:]template [--clean]\n" "{0} template diff [user:]template\n" "{0} template copy [user_from:]template_from [[user_to:]template_to]\n" "{0} template list [--public]\n" "\n".format(self.prog_name)) def help_add(self): print("Usage: {0} template add [user:]template [--title] [--description]\n" " [--includes] [--public]\n" "\n" "Options:\n" " --title TITLE Define the pretty TITLE of template\n" " --description TEXT Define descriptive TEXT of the template\n" " --includes TEMPLATE Define descriptive TEXT of the template\n" "\n" "\n".format(self.prog_name)) def help_update(self): print("Usage: {0} template update [user:]template [--title] [--description]\n" " [--includes] [--public]\n" "\n" "Options:\n" " --title TITLE Define the pretty TITLE of template\n" " --description TEXT Define descriptive TEXT of the template\n" " --includes TEMPLATE Define descriptive TEXT of the template\n" "\n" "\n".format(self.prog_name)) def run(self): command = None # search for our function based on the specified action try: command = getattr(self, 'run_{0}'.format(self.args.action)) except: self.help() return 1 if not command: print('error: action is not reachable.') return return command() def run_add(self): t = Template(self.args.template, user=self.args.username) # add template bits that are specified if self.args.title is not None: t.title = self.args.title if self.args.description is not None: t.description = self.args.description if self.args.includes is not None: t.includes = self.args.includes if self.args.public is not None: t.public = self.args.public try: res = self.cs.template_create(t) except ServiceException as e: print(e) return 1 print('info: template added.') return 0 def run_copy(self): t = Template(self.args.template_from, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # reparse for template destination t.parse(self.args.template_to) try: res = self.cs.template_create(t) except ServiceException as e: print(e) return 1 print('info: template copied.') return 0 def run_diff(self): t = Template(self.args.template_from, user=self.args.username) # grab the template we're pushing to try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # fetch template to compare to if self.args.template_to is not None: ts = Template(self.args.template_to, user=self.args.username) try: ts = self.cs.template_get(ts) except ServiceException as e: print(e) return 1 # otherwise build from system else: ts = Template('system') ts.from_system() (l_r, r_l) = t.package_diff(ts.packages_all) if len(l_r): print('In template and not marked for install in system:') for p in l_r: print(" * {0}".format(p.name)) print() if len(r_l): print('Marked for install on system and not in template:') for p in r_l: print(" * {0}".format(p.name)) print() def run_dump(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 if self.args.kickstart: print(t.to_kickstart()) return 0 elif self.args.yaml: print(yaml.dump(t.to_object(), indent=4)) return 0 elif self.args.json: print(json.dumps(t.to_object(), indent=4)) return 0 # pretty general information print('Name: {0} ({1})'.format(t.name, t.user)) if t.description is not None and len(t.description): print('Description:\n{0}\n'.format(t.description)) # pretty print includes if len(t.includes): print('Includes:') for i in t.includes: print(' - {0}'.format(i)) print() # pretty print packages repos = list(t.repos_all) repos.sort(key=lambda x: x.stub) if len(repos): l = prettytable.PrettyTable(['repo', 'name', 'priority', 'cost', 'enabled']) l.min_table_width = 120 l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 for r in repos: if r.cost is None: r.cost = '-' if r.priority is None: r.priority = '-' if r.enabled: r.enabled = 'Y' else: r.enabled = 'N' l.add_row([r.stub, r.name, r.priority, r.cost, r.enabled]) print(l) print() # pretty print packages packages = list(t.packages_all) packages.sort(key=lambda x: x.name) if len(packages): l = prettytable.PrettyTable(['package', 'epoch', 'version', 'release', 'arch', 'action']) l.min_table_width = 120 l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 for p in packages: if p.epoch is None: p.epoch = '-' if p.version is None: p.version = '-' if p.release is None: p.release = '-' if p.arch is None: p.arch = '-' if p.included(): p.action = '+' else: p.action = '-' l.add_row([p.name, p.epoch, p.version, p.release, p.arch, p.action]) print(l) print() return 0 def run_list(self): # fetch all accessible/available templates try: templates = self.cs.template_list( user=self.args.filter_user, name=self.args.filter_name, description=self.args.filter_description, public=self.args.public_only ) except ServiceException as e: print(e) return 1 if len(templates): l = prettytable.PrettyTable(["user:name", "title"]) l.hrules = prettytable.HEADER l.vrules = prettytable.NONE l.align = 'l' l.padding_witdth = 1 # add table items and print for t in templates: l.add_row(["{0}:{1}".format(t['username'], t['stub']), t['name']]) print(l) # print summary print('\n{0} template(s) found.'.format(len(templates))) else: print('0 templates found.') def run_pull(self): # am i effectively root if os.geteuid() != 0: print('You need to have root privileges to modify the system.') return 0 t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # prepare dnf print('info: analysing system ...') db = dnf.Base() # install repos from template if len(t.repos_all): for r in t.repos_all: dr = r.to_repo() dr.set_progress_bar(dnf.cli.progress.MultiFileProgressMeter()) dr.load() db.repos.add(dr) elif True: print('No template repos specified, using available system repos.') db.read_all_repos() else: print('No repos defined.') return 0 db.read_comps() try: db.fill_sack() except OSError as e: pass multilib_policy = db.conf.multilib_policy clean_deps = db.conf.clean_requirements_on_remove print('info: preparing transaction ...') # process all packages in template for p in t.packages_all: if p.included(): try: db.install(p.to_pkg_spec()) except: print ("error: Package does not exist " + str(p)) pass else: db.remove(p.to_pkg_spec()) print('info: resolving actions ...') db.resolve(allow_erasing=True) # describe process for dry runs if self.args.dry_run: packages_install = list(db.transaction.install_set) packages_install.sort(key=lambda x: x.name) packages_remove = list(db.transaction.remove_set) packages_remove.sort(key=lambda x: x.name) if len(packages_install) or len(packages_remove): print('The following would be installed to (+) and removed from (-) the system:') for p in packages_install: print(' + ' + str(p)) for p in packages_remove: print(' - ' + str(p)) print() print('Summary:') print(' - Package(s): %d' % (len(packages_install)+len(packages_remove))) print() else: print('No system changes required.') print('No action peformed during this dry-run.') return 0 if len(db.transaction.install_set) or len(db.transaction.remove_set): print('info: downloading ...') db.download_packages(list(db.transaction.install_set), progress=MultiFileProgressMeter()) print('info: completing ...') db.do_transaction() print('info: syncing history ...') for p in t.packages_all: if p.included(): pkg = p.to_pkg(); if pkg is not None: db.yumdb.get_package(pkg).reason = 'user' def run_push(self): t = Template(self.args.template, user=self.args.username) # grab the template we're pushing to try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 if self.args.kickstart is not None: print('info: parsing kickstart ...') t.from_kickstart(self.args.kickstart) else: # prepare dnf print('info: analysing system ...') db = dnf.Base() db.read_all_repos() db.read_comps() try: db.fill_sack() except OSError as e: pass db_list = db.iter_userinstalled() if self.args.push_all: db_list = db.sack.query().installed() # add our user installed packages for p in db_list: # no need to store versions t.add_package(Package(p, evr=False)) # add only enabled repos for r in db.repos.enabled(): t.add_repo(Repository(r)) packages = list(t.packages_delta) packages.sort(key=lambda x: x.name) repos = list(t.repos_delta) repos.sort(key=lambda x: x.name) # describe process for dry runs if self.args.dry_run: if len(packages) or len(repos): print('The following would be added to the template: {0}'.format(t.name)) for p in packages: print(' - ' + str(p)) for r in repos: print(' - ' + str(r)) print() print('Summary:') print(' - Package(s): %d' % (len(packages))) print(' - Repo(s): %d' % (len(repos))) print() else: print('No template changes required.') print('No action peformed during this dry-run.') return 0 if self.args.kickstart is None and not len(packages) and not len(repos): print('info: no changes detected, template up to date.') return 0 # push our updated template try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: template pushed.') return 0 def run_rm(self): t = Template(self.args.template, user=self.args.username) try: res = self.cs.template_delete(t) except ServiceException as e: print(e) return 1 print('info: template removed.') return 0 def run_update(self): t = Template(self.args.template, user=self.args.username) try: t = self.cs.template_get(t) except ServiceException as e: print(e) return 1 # add template bits that are specified for update if self.args.title is not None: t.title = self.args.title if self.args.description is not None: t.description = self.args.description if self.args.includes is not None: t.includes = self.args.includes if self.args.public is not None: t.public = self.args.public try: res = self.cs.template_update(t) except ServiceException as e: print(e) return 1 print('info: template updated.') return 0