def parse_configuration(config_filename, schema_filename): ''' Given the path to a config filename in YAML format and the path to a schema filename in pykwalify YAML schema format, return the parsed configuration as a data structure of nested dicts and lists corresponding to the schema. Example return value: {'location': {'source_directories': ['/home', '/etc'], 'repository': 'hostname.borg'}, 'retention': {'keep_daily': 7}, 'consistency': {'checks': ['repository', 'archives']}} Raise FileNotFoundError if the file does not exist, PermissionError if the user does not have permissions to read the file, or Validation_error if the config does not match the schema. ''' logging.getLogger('pykwalify').setLevel(logging.ERROR) try: config = load.load_configuration(config_filename) schema = load.load_configuration(schema_filename) except (ruamel.yaml.error.YAMLError, RecursionError) as error: raise Validation_error(config_filename, (str(error),)) # pykwalify gets angry if the example field is not a string. So rather than bend to its will, # remove all examples before passing the schema to pykwalify. for section_name, section_schema in schema['map'].items(): for field_name, field_schema in section_schema['map'].items(): field_schema.pop('example', None) validator = pykwalify.core.Core(source_data=config, schema_data=schema) parsed_result = validator.validate(raise_exception=False) if validator.validation_errors: raise Validation_error(config_filename, validator.validation_errors) apply_logical_validation(config_filename, parsed_result) return parsed_result
def parse_configuration(config_filename, schema_filename, overrides=None): ''' Given the path to a config filename in YAML format, the path to a schema filename in pykwalify YAML schema format, a sequence of configuration file override strings in the form of "section.option=value", return the parsed configuration as a data structure of nested dicts and lists corresponding to the schema. Example return value: {'location': {'source_directories': ['/home', '/etc'], 'repository': 'hostname.borg'}, 'retention': {'keep_daily': 7}, 'consistency': {'checks': ['repository', 'archives']}} Raise FileNotFoundError if the file does not exist, PermissionError if the user does not have permissions to read the file, or Validation_error if the config does not match the schema. ''' logging.getLogger('pykwalify').setLevel(logging.ERROR) try: config = load.load_configuration(config_filename) schema = load.load_configuration(schema_filename) except (ruamel.yaml.error.YAMLError, RecursionError) as error: raise Validation_error(config_filename, (str(error), )) override.apply_overrides(config, overrides) normalize.normalize(config) validator = pykwalify.core.Core(source_data=config, schema_data=remove_examples(schema)) parsed_result = validator.validate(raise_exception=False) if validator.validation_errors: raise Validation_error(config_filename, validator.validation_errors) apply_logical_validation(config_filename, parsed_result) return parsed_result
def test_load_configuration_inlines_include(): builtins = flexmock(sys.modules['builtins']) builtins.should_receive('open').with_args('include.yaml').and_return('value') builtins.should_receive('open').with_args('config.yaml').and_return( 'key: !include include.yaml' ) assert module.load_configuration('config.yaml') == {'key': 'value'}
def test_load_configuration_does_not_merge_include_list(): builtins = flexmock(sys.modules['builtins']) builtins.should_receive('open').with_args('include.yaml').and_return(''' - one - two ''') builtins.should_receive('open').with_args('config.yaml').and_return(''' foo: bar repositories: <<: !include include.yaml ''') with pytest.raises(ruamel.yaml.error.YAMLError): assert module.load_configuration('config.yaml')
def test_load_configuration_merges_include(): builtins = flexmock(sys.modules['builtins']) builtins.should_receive('open').with_args('include.yaml').and_return(''' foo: bar baz: quux ''') builtins.should_receive('open').with_args('config.yaml').and_return(''' foo: override <<: !include include.yaml ''') assert module.load_configuration('config.yaml') == { 'foo': 'override', 'baz': 'quux' }
def test_load_configuration_merges_include(): builtins = flexmock(sys.modules['builtins']) builtins.should_receive('open').with_args('include.yaml').and_return( ''' foo: bar baz: quux ''' ) builtins.should_receive('open').with_args('config.yaml').and_return( ''' foo: override <<: !include include.yaml ''' ) assert module.load_configuration('config.yaml') == {'foo': 'override', 'baz': 'quux'}
def generate_sample_configuration(source_filename, destination_filename, schema_filename): ''' Given an optional source configuration filename, and a required destination configuration filename, and the path to a schema filename in pykwalify YAML schema format, write out a sample configuration file based on that schema. If a source filename is provided, merge the parsed contents of that configuration into the generated configuration. ''' schema = yaml.round_trip_load(open(schema_filename)) source_config = None if source_filename: source_config = load.load_configuration(source_filename) destination_config = merge_source_configuration_into_destination( _schema_to_sample_configuration(schema), source_config ) write_configuration( destination_filename, _comment_out_optional_configuration(render_configuration(destination_config)), )
def test_load_configuration_parses_contents(): builtins = flexmock(sys.modules['builtins']) builtins.should_receive('open').with_args('config.yaml').and_return('key: value') assert module.load_configuration('config.yaml') == {'key': 'value'}