def test_load_default(): """ Test load of default config """ config.load() assert config.SETTINGS.main_directory == "schema"
def test_load_custom(): """ Test load from configuration file """ # Load config file using fixture of config file config_file_name = FIXTURES_DIR + "/pyproject.toml" config.load(config_file_name=config_file_name) assert config.SETTINGS.main_directory == "schema1"
def test_load_data(): """ Test load from python data structure """ data = { "main_directory": "schema2", } config.load(config_data=data) assert config.SETTINGS.main_directory == "schema2"
def validate(show_pass, show_checks, strict): # noqa D205 """Validates instance files against defined schema. \f Args: show_pass (bool): show successful schema validations show_checks (bool): show schemas which will be validated against each instance file strict (bool): Forces a stricter schema check that warns about unexpected additional properties """ config.load() # --------------------------------------------------------------------- # Load Schema(s) from disk # --------------------------------------------------------------------- smgr = SchemaManager(config=config.SETTINGS) if not smgr.schemas: error("No schemas were loaded") sys.exit(1) # --------------------------------------------------------------------- # Load Instances # --------------------------------------------------------------------- ifm = InstanceFileManager(config=config.SETTINGS) if not ifm.instances: error("No instance files were found to validate") sys.exit(1) if show_checks: ifm.print_schema_mapping() sys.exit(0) error_exists = False for instance in ifm.instances: for result in instance.validate(smgr, strict): result.instance_type = "FILE" result.instance_name = instance.filename result.instance_location = instance.path if not result.passed(): error_exists = True result.print() elif result.passed() and show_pass: result.print() if not error_exists: print(colored("ALL SCHEMA VALIDATION CHECKS PASSED", "green")) else: sys.exit(1)
def test_load_mixed(): """ Test config load when config_file_name, data, and defaults are all used """ config_file_name = FIXTURES_DIR + "/pyproject2.toml" data = {"main_directory": "fake_dir"} config.load(config_file_name=config_file_name, config_data=data) # Assert main_directory inhered from data passed in assert config.SETTINGS.main_directory == "fake_dir" # Assert definitions_directory inhered from default, and not from file assert config.SETTINGS.definition_directory == "definitions"
def schema(check, generate_invalid, list_schemas, schema_id, dump_schemas): # noqa: D417,D301,D205 """Manage your schemas. \f Args: check (bool): Validates that all schemas are valid (spec and unit tests) generate_invalid (bool): Generates expected invalid data from a given schema list_schemas (bool): List all available schemas schema_id (str): Name of schema to evaluate dump_schemas (bool): Dump all schema data or a single schema if schema_id is provided """ if not check and not generate_invalid and not list_schemas and not schema_id and not dump_schemas: error( "The 'schema' command requires one or more arguments. You can run the command 'schema-enforcer schema --help' to see the arguments available." ) sys.exit(1) config.load() # --------------------------------------------------------------------- # Load Schema(s) from disk # --------------------------------------------------------------------- smgr = SchemaManager(config=config.SETTINGS) if not smgr.schemas: error("No schemas were loaded") sys.exit(1) if list_schemas: smgr.print_schemas_list() sys.exit(0) if dump_schemas: smgr.dump_schema(schema_id) sys.exit(0) if generate_invalid: if not schema_id: sys.exit( "Please indicate the schema you'd like to generate invalid data for using the --schema-id flag" ) smgr.generate_invalid_tests_expected(schema_id=schema_id) sys.exit(0) if check: smgr.test_schemas() sys.exit(0)
def ansible(inventory, limit, show_pass, show_checks): # pylint: disable=too-many-branches,too-many-locals,too-many-locals # noqa: D417,D301 """Validate the hostvars for all hosts within an Ansible inventory. The hostvars are dynamically rendered based on groups to which each host belongs. For each host, if a variable `schema_enforcer_schema_ids` is defined, it will be used to determine which schemas should be use to validate each key. If this variable is not defined, the hostvars top level keys will be automatically mapped to a schema definition's top level properties to automatically infer which schema should be used to validate which hostvar. \f Args: inventory (string): The name of the file used to construct an ansible inventory. limit (string, None): Name of a host to limit the execution to. show_pass (bool): Shows validation checks that pass. Defaults to False. show_checks (bool): Shows the schema ids each host will be evaluated against. Example: $ cd examples/ansible $ ls -ls total 8 drwxr-xr-x 5 damien staff 160B Jul 25 16:37 group_vars drwxr-xr-x 4 damien staff 128B Jul 25 16:37 host_vars -rw-r--r-- 1 damien staff 69B Jul 25 16:37 inventory.ini drwxr-xr-x 4 damien staff 128B Jul 25 16:37 schema $ schema-enforcer ansible -i inventory.ini Found 4 hosts in the inventory FAIL | [ERROR] False is not of type 'string' [HOST] spine1 [PROPERTY] dns_servers:0:address FAIL | [ERROR] False is not of type 'string' [HOST] spine2 [PROPERTY] dns_servers:0:address $ schema-enforcer ansible -i inventory.ini -h leaf1 Found 4 hosts in the inventory ALL SCHEMA VALIDATION CHECKS PASSED $ schema-enforcer ansible -i inventory.ini -h spine1 --show-pass Found 4 hosts in the inventory FAIL | [ERROR] False is not of type 'string' [HOST] spine1 [PROPERTY] dns_servers:0:address PASS | [HOST] spine1 [SCHEMA ID] schemas/interfaces """ # Ansible is currently always installed by schema-enforcer. This was added in the interest of making ansible an # optional dependency. We decided to make two separate packages installable via PyPi, one with ansible, one without. # This has been left in the code until such a time as we implement the change to two packages so code will not need # to be re-written/ try: from schema_enforcer.ansible_inventory import AnsibleInventory # pylint: disable=import-outside-toplevel except ModuleNotFoundError: error( "ansible package not found, you can run the command 'pip install schema-enforcer[ansible]' to install the latest schema-enforcer sanctioned version." ) sys.exit(1) if inventory: config.load(config_data={"ansible_inventory": inventory}) else: config.load() # --------------------------------------------------------------------- # Load Schema(s) from disk # --------------------------------------------------------------------- smgr = SchemaManager(config=config.SETTINGS) if not smgr.schemas: error("No schemas were loaded") sys.exit(1) # --------------------------------------------------------------------- # Load Ansible Inventory file # - generate hostvar for all devices in the inventory # - Validate Each key in the hostvar individually against the schemas defined in the var jsonschema_mapping # --------------------------------------------------------------------- inv = AnsibleInventory(inventory=config.SETTINGS.ansible_inventory) hosts = inv.get_hosts_containing() print(f"Found {len(hosts)} hosts in the inventory") if show_checks: inv.print_schema_mapping(hosts, limit, smgr) sys.exit(0) error_exists = False for host in hosts: if limit and host.name != limit: continue # Acquire Host Variables hostvars = inv.get_clean_host_vars(host) # Acquire validation settings for the given host schema_validation_settings = inv.get_schema_validation_settings(host) declared_schema_ids = schema_validation_settings["declared_schema_ids"] strict = schema_validation_settings["strict"] automap = schema_validation_settings["automap"] # Validate declared schemas exist smgr.validate_schemas_exist(declared_schema_ids) # Acquire schemas applicable to the given host applicable_schemas = inv.get_applicable_schemas( hostvars, smgr, declared_schema_ids, automap) # import pdb; pdb.set_trace() for schema_obj in applicable_schemas.values(): # Combine host attributes into a single data structure matching to properties defined at the top level of the schema definition if not strict: data = dict() for var in schema_obj.top_level_properties: data.update({var: hostvars.get(var)}) # If the schema_enforcer_strict bool is set, hostvars should match a single schema exactly. # Thus, we want to pass the entirety of the cleaned host vars into the validate method rather # than creating a data structure with only the top level vars defined by the schema. else: data = hostvars # Validate host vars against schema for result in schema_obj.validate(data=data, strict=strict): result.instance_type = "HOST" result.instance_hostname = host.name if not result.passed(): error_exists = True result.print() elif result.passed() and show_pass: result.print() if not error_exists: print(colored("ALL SCHEMA VALIDATION CHECKS PASSED", "green")) else: sys.exit(1)