From 0c0a5d681133b325da03d949c71fa04bb72a0c1f Mon Sep 17 00:00:00 2001 From: obaranov Date: Mon, 29 Feb 2016 16:53:03 +0200 Subject: [PATCH 1/8] Added IRApplication class (for provisioner) --- cli/conf.py | 32 +++++---- cli/provision.py | 184 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 145 insertions(+), 71 deletions(-) diff --git a/cli/conf.py b/cli/conf.py index 6cfc81d472..e76d67af37 100644 --- a/cli/conf.py +++ b/cli/conf.py @@ -43,38 +43,40 @@ class SpecManager(object): SPEC_EXTENSION = '.spec' - def __init__(self, config): - self.config = config + @classmethod + def parse_args(cls, module_name, config, args=None): + """ + Looks for all the specs for specified module + and parses the commandline input arguments accordingly. + + :param module_name: the module name: installer|provisioner|tester + """ + cmd = clg.CommandLine(cls._get_specs(module_name, config)) + return cmd.parse(args) - def get_specs(self, module_name): + @classmethod + def _get_specs(cls, module_name, config): """ Gets specs files as a dict from settings/ folder. :param module_name: the module name: installer|provisioner|tester """ res = {} - for spec_file in self.__get_all_specs(subfolder=module_name): + for spec_file in cls._get_all_specs(config, subfolder=module_name): spec = yaml.load(open(spec_file)) utils.dict_merge(res, spec) return res - def parse_args(self, module_name, args=None): - """ - Looks for all the specs for specified module - and parses the commandline input arguments accordingly. - """ - cmd = clg.CommandLine(self.get_specs(module_name)) - return cmd.parse(args) - - def __get_all_specs(self, subfolder=None): + @classmethod + def _get_all_specs(cls, config, subfolder=None): root_dir = utils.validate_settings_dir( - self.config.get('defaults', 'settings')) + config.get('defaults', 'settings')) if subfolder: root_dir = os.path.join(root_dir, subfolder) res = [] for dirpath, _, filenames in os.walk(root_dir): for filename in [f for f in filenames - if f.endswith(self.SPEC_EXTENSION)]: + if f.endswith(cls.SPEC_EXTENSION)]: res.append(os.path.join(dirpath, filename)) return res diff --git a/cli/provision.py b/cli/provision.py index 076fde61e2..eb3cc53b53 100755 --- a/cli/provision.py +++ b/cli/provision.py @@ -2,6 +2,9 @@ import os +from cli import logger # logger creation is first thing to be done +import cli.execute +import cli.yamls import yaml from cli import logger # logger creation is first thing to be done @@ -16,72 +19,141 @@ NON_SETTINGS_OPTIONS = ['command0', 'verbose', 'extra-vars', 'output-file', 'input-files', 'dry-run', 'cleanup', 'inventory'] - -def set_logger_verbosity(level): +class IRFactory(object): """ - Set the logger verbosity level - - :param level: verbosity level (int) + Creates and configures tha IR applications. """ from logging import WARNING, INFO, DEBUG LOG.setLevel((WARNING, INFO)[level] if level < 2 else DEBUG) - -def main(): - spec_manager = conf.SpecManager(CONF) - args = spec_manager.parse_args("provisioner") - - settings_files = [] - settings_dir = utils.validate_settings_dir( - CONF.get('defaults', 'settings')) - - set_logger_verbosity(args.verbose) - - provision_dir = os.path.join(settings_dir, 'provisioner', args['command0']) - - for input_file in args['input-files'] or []: - settings_files.append(utils.normalize_file(input_file)) - - settings_files.append(os.path.join(provision_dir, - args['command0'] + '.yml')) - - for key, val in vars(args).iteritems(): - if val is not None and key not in NON_SETTINGS_OPTIONS: - settings_file = os.path.join(provision_dir, key, val + '.yml') - LOG.debug('Searching settings file for the "%s" key...' % key) - if not os.path.isfile(settings_file): - settings_file = utils.normalize_file(val) - settings_files.append(settings_file) - LOG.debug('"%s" was added to settings files list as an argument ' - 'for "%s" key' % (settings_file, key)) - - LOG.debug("All settings files to be loaded:\n%s" % settings_files) - - cli.yamls.Lookup.settings = utils.generate_settings(settings_files, - args['extra-vars']) + NON_SETTINGS_OPTIONS = ['command0', 'verbose', 'extra-vars', 'output-file', + 'input-files', 'dry-run', 'cleanup', 'inventory'] + + @classmethod + def create(cls, app_name, config): + """ + Create the application object + by module name and provided configuration. + """ + if app_name in ["provisioner", ]: + args = conf.SpecManager.parse_args(app_name, config) + settings_files = cls.configure(app_name, config, args) + app_instance = IRApplication(app_name, args, settings_files) + + else: + raise Exception("Application is not supported: '{}'".format(app_name)) + return app_instance + + @classmethod + def configure(cls, app_name, config, args): + cls._configure_log(args) + settings_files = cls._collect_settings_files(app_name, config, args) + LOG.debug("All settings files to be loaded:\n%s" % settings_files) + return settings_files + + @classmethod + def _configure_log(cls, args): + """ + Perfroms the logging module configuration. + :return: + """ + from logging import WARNING, INFO, DEBUG + LOG.setLevel((WARNING, INFO)[args.verbose] + if args.verbose < 2 else DEBUG) + + @classmethod + def _collect_settings_files(cls, app_name, config, args): + """ + Collects all the settings files for given module and sub-command + """ + settings_files = [] + settings_dir = utils.validate_settings_dir( + config.get('defaults', 'settings')) + module_dir = os.path.join( + settings_dir, + app_name, + args.command0) + + # first take all the files from the input-files args + for input_file in args['input-files'] or []: + settings_files.append(utils.normalize_file(input_file)) + + # get the sub-command yml file + settings_files.append(os.path.join( + module_dir, + args.command0 + '.yml')) + + # load directly from args + for key, val in vars(args).iteritems(): + if val is not None and key not in cls.NON_SETTINGS_OPTIONS: + settings_file = os.path.join(module_dir, key, val + '.yml') + LOG.debug('Searching settings file for the "%s" key...' % key) + if not os.path.isfile(settings_file): + settings_file = utils.normalize_file(val) + settings_files.append(settings_file) + LOG.debug('"%s" was added to settings ' + 'files list as an argument ' + 'for "%s" key' % (settings_file, key)) + + return settings_files cli.yamls.Lookup.in_string_lookup() - LOG.debug("Dumping settings...") - - output = yaml.safe_dump(cli.yamls.Lookup.settings, - default_flow_style=False) - - if args['output-file']: - with open(args['output-file'], 'w') as output_file: - output_file.write(output) - else: - print output - - # playbook execution stage - if not args['dry-run']: - vars(args)['settings'] = yaml.load(yaml.safe_dump( - cli.yamls.Lookup.settings, default_flow_style=False)) - if not args['cleanup']: - vars(args)['provision'] = True +class IRApplication(object): + """ + Hold the default application workflow logic. + """ + def __init__(self, name, args, settings_files): + self.name = name + self.args = args + self.settings_files = settings_files + + def run(self): + """ + :return: Runs the application + """ + settings = self.lookup(self.settings_files) + self.dump_settings(settings) + self.execute(settings) + + def lookup(self, settings_files): + """ + Replaces a setting values with !lookup + in the setting files by values from + other settings files. + """ + cli.yamls.Lookup.settings = utils.generate_settings( + settings_files, + self.args['extra-vars']) + cli.yamls.Lookup.in_string_lookup() + + return cli.yamls.Lookup.settings + + def dump_settings(self, settings): + LOG.debug("Dumping settings...") + output = yaml.safe_dump(settings, + default_flow_style=False) + if self.args['output-file']: + with open(self.args['output-file'], 'w') as output_file: + output_file.write(output) + else: + print output + + def execute(self, settings): + """ + Executes a playbook. + """ + if not self.args['dry-run']: + vars(self.args)['settings'] = settings + if not self.args['cleanup']: + vars(self.args)['provision'] = True + + cli.execute.ansible_wrapper(self.args) - cli.execute.ansible_wrapper(args) +def main(): + app = IRFactory.create('provisioner', CONF) + app.run() if __name__ == '__main__': main() From d0566c1bb906cc242b1f5eb9ab8209fda6e975eb Mon Sep 17 00:00:00 2001 From: Yair Fried Date: Mon, 29 Feb 2016 17:14:36 +0200 Subject: [PATCH 2/8] [execute] Raise IRPlaybookFailedException --- cli/exceptions.py | 7 ++++--- cli/execute.py | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cli/exceptions.py b/cli/exceptions.py index 724006d389..03cf221041 100644 --- a/cli/exceptions.py +++ b/cli/exceptions.py @@ -35,9 +35,10 @@ def __init__(self, env_var): class IRPlaybookFailedException(IRException): - def __init__(self, playbook): - super(self.__class__, self).__init__( - 'Playbook "%s" failed!' % playbook) + def __init__(self, playbook, message=""): + msg = 'Playbook "%s" failed!' % playbook + msg = msg + message if message else msg + super(self.__class__, self).__init__(msg) class IRYAMLConstructorError(IRException): diff --git a/cli/execute.py b/cli/execute.py index 35c6ad7c51..2e083c1fce 100644 --- a/cli/execute.py +++ b/cli/execute.py @@ -136,9 +136,11 @@ def execute_ansible(playbook, args): print "" if len(failed_hosts) > 0: - raise Exception(2) + raise exceptions.IRPlaybookFailedException( + playbook, "Failed hosts: %s" % failed_hosts) if len(unreachable_hosts) > 0: - raise Exception(3) + raise exceptions.IRPlaybookFailedException( + playbook, "Unreachable hosts: %s" % unreachable_hosts) def ansible_wrapper(args): From 40691aa8decfb36dc917d540c6f476cfb61d5125 Mon Sep 17 00:00:00 2001 From: Yair Fried Date: Mon, 29 Feb 2016 17:58:57 +0200 Subject: [PATCH 3/8] Replace wget with ansible get_url module --- roles/ospd/overcloud/images/import/tasks/main.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/roles/ospd/overcloud/images/import/tasks/main.yml b/roles/ospd/overcloud/images/import/tasks/main.yml index fc5b2e35ce..6e5c16cd85 100644 --- a/roles/ospd/overcloud/images/import/tasks/main.yml +++ b/roles/ospd/overcloud/images/import/tasks/main.yml @@ -1,13 +1,8 @@ --- -- name: ensure wget is installed - yum: - name: wget - state: latest - - name: download the pre-built overcloud images - shell: > - wget --quiet -c -O ~/{{ item.value }} - "{{ installer.overcloud.images.server }}/{{ item.value }}" + get_url: + dest: "~/{{ item.value }}" + url: "{{ installer.overcloud.images.server }}/{{ item.value }}" with_dict: "{{ installer.overcloud.images.files }}" - name: untar the images From 2fd2f6419c275a40a2da4e068eb605925c8817a6 Mon Sep 17 00:00:00 2001 From: obaranov Date: Tue, 1 Mar 2016 12:56:33 +0200 Subject: [PATCH 4/8] Added IRApplication classes and dependant classes --- cli/install.py | 3 +- cli/provision.py | 96 +++++++++++++++++++++++++++++------------------- 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/cli/install.py b/cli/install.py index 94e2a10b0f..3723dec08b 100755 --- a/cli/install.py +++ b/cli/install.py @@ -44,8 +44,7 @@ def get_args(args=None): :return args: return loaded arguments from CLI """ - spec_manager = conf.SpecManager(CONF) - args = spec_manager.parse_args(ENTRY_POINT, args=args) + args = conf.SpecManager.parse_args(ENTRY_POINT, CONF, args=args) return args diff --git a/cli/provision.py b/cli/provision.py index eb3cc53b53..baf287f35b 100755 --- a/cli/provision.py +++ b/cli/provision.py @@ -7,7 +7,6 @@ import cli.yamls import yaml -from cli import logger # logger creation is first thing to be done from cli import conf from cli import utils import cli.yamls @@ -19,15 +18,11 @@ NON_SETTINGS_OPTIONS = ['command0', 'verbose', 'extra-vars', 'output-file', 'input-files', 'dry-run', 'cleanup', 'inventory'] + class IRFactory(object): """ - Creates and configures tha IR applications. + Creates and configures the IR applications. """ - from logging import WARNING, INFO, DEBUG - LOG.setLevel((WARNING, INFO)[level] if level < 2 else DEBUG) - - NON_SETTINGS_OPTIONS = ['command0', 'verbose', 'extra-vars', 'output-file', - 'input-files', 'dry-run', 'cleanup', 'inventory'] @classmethod def create(cls, app_name, config): @@ -35,58 +30,82 @@ def create(cls, app_name, config): Create the application object by module name and provided configuration. """ + args = conf.SpecManager.parse_args(app_name, config) + setting_dir = utils.validate_settings_dir( + CONF.get('defaults', 'settings')) + cls.configure(args) + if app_name in ["provisioner", ]: - args = conf.SpecManager.parse_args(app_name, config) - settings_files = cls.configure(app_name, config, args) - app_instance = IRApplication(app_name, args, settings_files) + app_instance = IRApplication(app_name, setting_dir, args) else: - raise Exception("Application is not supported: '{}'".format(app_name)) + raise Exception( + "Application is not supported: '{}'".format(app_name)) return app_instance @classmethod - def configure(cls, app_name, config, args): + def configure(cls, args): cls._configure_log(args) - settings_files = cls._collect_settings_files(app_name, config, args) - LOG.debug("All settings files to be loaded:\n%s" % settings_files) - return settings_files @classmethod def _configure_log(cls, args): """ - Perfroms the logging module configuration. + Performs the logging module configuration. :return: """ from logging import WARNING, INFO, DEBUG LOG.setLevel((WARNING, INFO)[args.verbose] if args.verbose < 2 else DEBUG) + +class IRSubCommand(object): + """ + Represents a command (virsh, ospd, etc) + for the application + """ + + def __init__(self, mame, args, settings_dir): + self.name = mame + self.args = args + self.settings_dir = settings_dir + @classmethod - def _collect_settings_files(cls, app_name, config, args): + def create(cls, app, settings_dir, args): """ - Collects all the settings files for given module and sub-command + Constructs the sub-command. + :param app: tha application name + :param settings_dir: the default application settings dir + :param args: the application args + :return: the IRSubCommand instance. + """ + if args: + settings_dir = os.path.join(settings_dir, + app) + if hasattr(args, "command0"): + settings_dir = os.path.join(settings_dir, + args['command0']) + return cls(args['command0'], args, settings_dir) + + def get_settings_files(self): + """ + Collects all the settings files for given application and sub-command """ settings_files = [] - settings_dir = utils.validate_settings_dir( - config.get('defaults', 'settings')) - module_dir = os.path.join( - settings_dir, - app_name, - args.command0) # first take all the files from the input-files args - for input_file in args['input-files'] or []: + for input_file in self.args['input-files'] or []: settings_files.append(utils.normalize_file(input_file)) # get the sub-command yml file settings_files.append(os.path.join( - module_dir, - args.command0 + '.yml')) + self.settings_dir, + self.name + '.yml')) # load directly from args - for key, val in vars(args).iteritems(): - if val is not None and key not in cls.NON_SETTINGS_OPTIONS: - settings_file = os.path.join(module_dir, key, val + '.yml') + for key, val in vars(self.args).iteritems(): + if val is not None and key not in NON_SETTINGS_OPTIONS: + settings_file = os.path.join( + self.settings_dir, key, val + '.yml') LOG.debug('Searching settings file for the "%s" key...' % key) if not os.path.isfile(settings_file): settings_file = utils.normalize_file(val) @@ -95,24 +114,25 @@ def _collect_settings_files(cls, app_name, config, args): 'files list as an argument ' 'for "%s" key' % (settings_file, key)) + LOG.debug("All settings files to be loaded:\n%s" % settings_files) return settings_files - cli.yamls.Lookup.in_string_lookup() class IRApplication(object): """ Hold the default application workflow logic. """ - def __init__(self, name, args, settings_files): + def __init__(self, name, settings_dir, args): self.name = name - self.args = args - self.settings_files = settings_files + self.args = args.args + self.settings_dir = settings_dir + self.sub_command = IRSubCommand.create(name, settings_dir, args) def run(self): """ :return: Runs the application """ - settings = self.lookup(self.settings_files) + settings = self.lookup(self.sub_command.get_settings_files()) self.dump_settings(settings) self.execute(settings) @@ -144,11 +164,13 @@ def execute(self, settings): Executes a playbook. """ if not self.args['dry-run']: - vars(self.args)['settings'] = settings + vars(self.args)['settings'] = yaml.load(yaml.safe_dump( + settings, + default_flow_style=False)) if not self.args['cleanup']: vars(self.args)['provision'] = True - cli.execute.ansible_wrapper(self.args) + cli.execute.ansible_wrapper(self.args) def main(): From 94d852adf12e40d82a4716fbecaaf24d2cd4fca1 Mon Sep 17 00:00:00 2001 From: obaranov Date: Tue, 1 Mar 2016 14:53:28 +0200 Subject: [PATCH 5/8] Addressed review comments --- cli/exceptions.py | 11 +++++++++++ cli/install.py | 2 +- cli/provision.py | 29 +++++++++++++---------------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/cli/exceptions.py b/cli/exceptions.py index 724006d389..c73a61ce05 100644 --- a/cli/exceptions.py +++ b/cli/exceptions.py @@ -57,3 +57,14 @@ def __init__(self, trace_message): class IRNotImplemented(IRException): pass + + +class IRUnknownApplicationException(IRException): + """ + This exceptions is raised when unknown application is + started by user. + """ + def __init__(self, app_name): + self.app_name = app_name + super(IRUnknownApplicationException, self).__init__( + "Application is unknown: '{}'".format(app_name)) diff --git a/cli/install.py b/cli/install.py index 3723dec08b..cf8be9539c 100755 --- a/cli/install.py +++ b/cli/install.py @@ -43,7 +43,7 @@ def get_args(args=None): """ :return args: return loaded arguments from CLI """ - + # todo(obaranov) remove one-line method. args = conf.SpecManager.parse_args(ENTRY_POINT, CONF, args=args) return args diff --git a/cli/provision.py b/cli/provision.py index baf287f35b..03dffd9d9d 100755 --- a/cli/provision.py +++ b/cli/provision.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import os +from logging import WARNING, INFO, DEBUG from cli import logger # logger creation is first thing to be done import cli.execute @@ -8,6 +9,7 @@ import yaml from cli import conf +from cli import exceptions from cli import utils import cli.yamls import cli.execute @@ -30,30 +32,25 @@ def create(cls, app_name, config): Create the application object by module name and provided configuration. """ - args = conf.SpecManager.parse_args(app_name, config) - setting_dir = utils.validate_settings_dir( - CONF.get('defaults', 'settings')) - cls.configure(args) - if app_name in ["provisioner", ]: + args = conf.SpecManager.parse_args(app_name, config) + cls.configure_environment(args) + setting_dir = utils.validate_settings_dir( + CONF.get('defaults', 'settings')) app_instance = IRApplication(app_name, setting_dir, args) else: - raise Exception( + raise exceptions.IRUnknownApplicationException( "Application is not supported: '{}'".format(app_name)) return app_instance @classmethod - def configure(cls, args): - cls._configure_log(args) - - @classmethod - def _configure_log(cls, args): + def configure_environment(cls, args): """ - Performs the logging module configuration. + Performs the environment configuration. + :param args: :return: """ - from logging import WARNING, INFO, DEBUG LOG.setLevel((WARNING, INFO)[args.verbose] if args.verbose < 2 else DEBUG) @@ -64,8 +61,8 @@ class IRSubCommand(object): for the application """ - def __init__(self, mame, args, settings_dir): - self.name = mame + def __init__(self, name, args, settings_dir): + self.name = name self.args = args self.settings_dir = settings_dir @@ -124,7 +121,7 @@ class IRApplication(object): """ def __init__(self, name, settings_dir, args): self.name = name - self.args = args.args + self.args = args self.settings_dir = settings_dir self.sub_command = IRSubCommand.create(name, settings_dir, args) From b90a4bd5edca6c960089d61948ed0e963426f81d Mon Sep 17 00:00:00 2001 From: obaranov Date: Tue, 1 Mar 2016 19:35:00 +0200 Subject: [PATCH 6/8] Fixed merge issues. --- cli/provision.py | 72 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/cli/provision.py b/cli/provision.py index 107c57fb08..3b6aa1bef4 100755 --- a/cli/provision.py +++ b/cli/provision.py @@ -1,12 +1,15 @@ #!/usr/bin/env python import os +from logging import WARNING, INFO, DEBUG +from cli.install import set_network_template as set_network import yaml from cli import logger # logger creation is first thing to be done from cli import conf from cli import utils +from cli import exceptions import cli.yamls import cli.execute @@ -94,7 +97,14 @@ def get_settings_files(self): self.settings_dir, self.name + '.yml')) + settings_files.extend(self._load_yaml_files()) + + LOG.debug("All settings files to be loaded:\n%s" % settings_files) + return settings_files + + def _load_yaml_files(self): # load directly from args + settings_files = [] for key, val in vars(self.args).iteritems(): if val is not None and key not in NON_SETTINGS_OPTIONS: settings_file = os.path.join( @@ -106,10 +116,45 @@ def get_settings_files(self): LOG.debug('"%s" was added to settings ' 'files list as an argument ' 'for "%s" key' % (settings_file, key)) - - LOG.debug("All settings files to be loaded:\n%s" % settings_files) return settings_files + def get_settings_dict(self): + return {} + + +class VirshCommand(IRSubCommand): + + def get_settings_dict(self): + + # todo(obaranov) this is virsh specific + # rework that and make this part of lookup or something. + image = dict( + name=self.args['image-file'], + base_url=self.args['image-server'] + ) + host = dict( + ssh_host=self.args['host'], + ssh_user=self.args['ssh-user'], + ssh_key_file=self.args['ssh-key'] + ) + + settings_dict = utils.dict_merge( + {'provisioner': {'image': image}}, + {'provisioner': {'hosts': {'host1': host}}}) + + # load network and image settings + for arg_dir in ('network', 'topology'): + with open(set_network(self.args[arg_dir], os.path.join( + self.settings_dir, arg_dir))) as settings_file: + settings = yaml.load(settings_file) + utils.dict_merge(settings_dict, settings) + + return settings_dict + + def _load_yaml_files(self): + # do not load additional yaml files. + return [] + class IRApplication(object): """ @@ -119,17 +164,25 @@ def __init__(self, name, settings_dir, args): self.name = name self.args = args self.settings_dir = settings_dir - self.sub_command = IRSubCommand.create(name, settings_dir, args) + + # todo(obaranov) replace with subcommand factory + self.sub_command = VirshCommand.create(name, settings_dir, args) def run(self): """ :return: Runs the application """ - settings = self.lookup(self.sub_command.get_settings_files()) + settings = self.collect_settings() self.dump_settings(settings) self.execute(settings) - def lookup(self, settings_files): + def collect_settings(self): + settings_files = self.sub_command.get_settings_files() + settings_dict = self.sub_command.get_settings_dict() + + return self.lookup(settings_files, settings_dict) + + def lookup(self, settings_files, settings_dict): """ Replaces a setting values with !lookup in the setting files by values from @@ -138,6 +191,8 @@ def lookup(self, settings_files): cli.yamls.Lookup.settings = utils.generate_settings( settings_files, self.args['extra-vars']) + + cli.yamls.Lookup.settings.merge(settings_dict) cli.yamls.Lookup.in_string_lookup() return cli.yamls.Lookup.settings @@ -146,8 +201,10 @@ def dump_settings(self, settings): LOG.debug("Dumping settings...") output = yaml.safe_dump(settings, default_flow_style=False) - if self.args['output-file']: - with open(self.args['output-file'], 'w') as output_file: + dump_file = self.args['output-file'] + if dump_file: + LOG.debug("Dump file: {}".format(dump_file)) + with open(dump_file, 'w') as output_file: output_file.write(output) else: print output @@ -165,7 +222,6 @@ def execute(self, settings): cli.execute.ansible_wrapper(self.args) - cli.execute.ansible_wrapper(args) def main(): app = IRFactory.create('provisioner', CONF) From b530f0a3bcc51783c2b677dd4d9b1e23439e0cde Mon Sep 17 00:00:00 2001 From: obaranov Date: Tue, 1 Mar 2016 19:40:18 +0200 Subject: [PATCH 7/8] Removed unused import --- cli/install.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/install.py b/cli/install.py index 666467ca96..a621f64de0 100755 --- a/cli/install.py +++ b/cli/install.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import logging import os import yaml From 72e33b0ea56be9c8e55b96bc68bb538e3981db0b Mon Sep 17 00:00:00 2001 From: Ariel Opincaru Date: Wed, 2 Mar 2016 09:45:43 +0200 Subject: [PATCH 8/8] Preserves params order in help screen as in spec Resolves #79 --- cli/conf.py | 8 +++++--- requirements.txt | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cli/conf.py b/cli/conf.py index 6427b4b89e..10844f70da 100644 --- a/cli/conf.py +++ b/cli/conf.py @@ -1,9 +1,10 @@ import ConfigParser - -import clg import os import yaml +import clg +import yamlordereddictloader + from cli import exceptions from cli import utils @@ -62,7 +63,8 @@ def _get_specs(cls, module_name, config): """ res = {} for spec_file in cls._get_all_specs(config, subfolder=module_name): - spec = yaml.load(open(spec_file)) + spec = yaml.load(open(spec_file), + Loader=yamlordereddictloader.Loader) utils.dict_merge(res, spec) return res diff --git a/requirements.txt b/requirements.txt index 6d9c751d64..02bbf3ee6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ PyYAML>=3.11 configure>=0.5 colorlog>=2.6.1 clg>=2.0.0 +yamlordereddictloader>=0.1.1