Source code for validations_libs.validation

#   Copyright 2020 Red Hat, Inc.
#
#   Licensed under the Apache License, Version 2.0 (the "License"); you may
#   not use this file except in compliance with the License. You may obtain
#   a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#   License for the specific language governing permissions and limitations
#   under the License.
#

from validations_libs.logger import getLogger
import os
import yaml
from collections import OrderedDict

LOG = getLogger(__name__ + ".validation")


[docs]class Validation: """An object for encapsulating a validation Each validation is an `Ansible` playbook. Each playbook have some ``metadata``. Here is what a minimal validation would look like: .. code-block:: yaml - hosts: webserver vars: metadata: name: Hello World description: This validation prints Hello World! roles: - hello_world As shown here, the validation playbook requires three top-level directives: ``hosts``, ``vars -> metadata`` and ``roles`` ``hosts`` specify which nodes to run the validation on. The ``vars`` section serves for storing variables that are going to be available to the `Ansible` playbook. The validations API uses the ``metadata`` section to read validation's name and description. These values are then reported by the API. The validations can be grouped together by specifying a ``groups``, a ``categories`` and a ``products`` metadata. ``groups`` are the deployment stage the validations should run on, ``categories`` are the technical classification for the validations and ``products`` are the specific validations which should be executed against a specific product. Groups, Categories and Products function similar to tags and a validation can thus be part of many groups and many categories. Here is an example: .. code-block:: yaml - hosts: webserver vars: metadata: name: Hello World description: This validation prints Hello World! groups: - pre-deployment - hardware categories: - os - networking - storage - security products: - product1 - product2 roles: - hello_world """ _col_keys = ['ID', 'Name', 'Description', 'Groups', 'Categories', 'Products'] def __init__(self, validation_path): self.dict = self._get_content(validation_path) self.id = os.path.splitext(os.path.basename(validation_path))[0] self.path = os.path.dirname(validation_path) def _get_content(self, val_path): try: with open(val_path, 'r') as val_playbook: return yaml.safe_load(val_playbook)[0] except IOError: raise IOError("Validation playbook not found") @property def has_vars_dict(self): """Check the presence of the vars dictionary .. code-block:: yaml - hosts: webserver vars: <==== metadata: name: hello world description: this validation prints hello world! groups: - pre-deployment - hardware categories: - os - networking - storage - security products: - product1 - product2 roles: - hello_world :return: `true` if `vars` is found, `false` if not. :rtype: `boolean` """ return 'vars' in self.dict.keys() @property def has_metadata_dict(self): """Check the presence of the metadata dictionary .. code-block:: yaml - hosts: webserver vars: metadata: <==== name: hello world description: this validation prints hello world! groups: - pre-deployment - hardware categories: - os - networking - storage - security products: - product1 - product2 roles: - hello_world :return: `true` if `vars` and metadata are found, `false` if not. :rtype: `boolean` """ return self.has_vars_dict and 'metadata' in self.dict['vars'].keys() @property def get_metadata(self): """Get the metadata of a validation :return: The validation metadata :rtype: `dict` or `None` if no metadata has been found :raise: A `NameError` exception if no metadata has been found in the playbook :Example: >>> pl = '/foo/bar/val1.yaml' >>> val = Validation(pl) >>> print(val.get_metadata) {'description': 'Val1 desc.', 'groups': ['group1', 'group2'], 'categories': ['category1', 'category2'], 'products': ['product1', 'product2'], 'id': 'val1', 'name': 'The validation val1\'s name', 'path': '/tmp/foo/'} """ if self.has_metadata_dict: self.metadata = {'id': self.id, 'path': self.path} self.metadata.update(self.dict['vars'].get('metadata')) return self.metadata else: raise NameError( "No metadata found in validation {}".format(self.id) ) @property def get_vars(self): """Get only the variables of a validation :return: All the variables belonging to a validation :rtype: `dict` or `None` if no metadata has been found :raise: A `NameError` exception if no metadata has been found in the playbook :Example: >>> pl = '/foo/bar/val.yaml' >>> val = Validation(pl) >>> print(val.get_vars) {'var_name1': 'value1', 'var_name2': 'value2'} """ if self.has_metadata_dict: validation_vars = self.dict['vars'].copy() validation_vars.pop('metadata') return validation_vars else: raise NameError( "No metadata found in validation {}".format(self.id) ) @property def get_data(self): """Get the full contents of a validation playbook :return: The full content of the playbook :rtype: `dict` :Example: >>> pl = '/foo/bar/val.yaml' >>> val = Validation(pl) >>> print(val.get_data) {'gather_facts': True, 'hosts': 'all', 'roles': ['val_role'], 'vars': {'metadata': {'description': 'description of val ', 'groups': ['group1', 'group2'], 'categories': ['category1', 'category2'], 'products': ['product1', 'product2'], 'name': 'validation one'}, 'var_name1': 'value1'}} """ return self.dict @property def groups(self): """Get the validation list of groups :return: A list of groups for the validation :rtype: `list` or `None` if no metadata has been found :raise: A `NameError` exception if no metadata has been found in the playbook :Example: >>> pl = '/foo/bar/val.yaml' >>> val = Validation(pl) >>> print(val.groups) ['group1', 'group2'] """ if self.has_metadata_dict: return self.dict['vars']['metadata'].get('groups', []) else: raise NameError( "No metadata found in validation {}".format(self.id) ) @property def categories(self): """Get the validation list of categories :return: A list of categories for the validation :rtype: `list` or `None` if no metadata has been found :raise: A `NameError` exception if no metadata has been found in the playbook :Example: >>> pl = '/foo/bar/val.yaml' >>> val = Validation(pl) >>> print(val.categories) ['category1', 'category2'] """ if self.has_metadata_dict: return self.dict['vars']['metadata'].get('categories', []) else: raise NameError( "No metadata found in validation {}".format(self.id) ) @property def products(self): """Get the validation list of products :return: A list of products for the validation :rtype: `list` or `None` if no metadata has been found :raise: A `NameError` exception if no metadata has been found in the playbook :Example: >>> pl = '/foo/bar/val.yaml' >>> val = Validation(pl) >>> print(val.products) ['product1', 'product2'] """ if self.has_metadata_dict: return self.dict['vars']['metadata'].get('products', []) else: raise NameError( "No metadata found in validation {}".format(self.id) ) @property def get_id(self): """Get the validation id :return: The validation id :rtype: `string` :Example: >>> pl = '/foo/bar/check-cpu.yaml' >>> val = Validation(pl) >>> print(val.id) 'check-cpu' """ return self.id @property def get_ordered_dict(self): """Get the full ordered content of a validation :return: An `OrderedDict` with the full data of a validation :rtype: `OrderedDict` """ data = OrderedDict(self.dict) return data @property def get_formated_data(self): """Get basic information from a validation for output display :return: Basic information of a validation including the `Description`, the list of 'Categories', the list of `Groups`, the `ID` and the `Name`. :rtype: `dict` :raise: A `NameError` exception if no metadata has been found in the playbook :Example: >>> pl = '/foo/bar/val.yaml' >>> val = Validation(pl) >>> print(val.get_formated_data) {'Categories': ['category1', 'category2'], 'Products': ['product1', 'product2'], 'Description': 'description of val', 'Groups': ['group1', 'group2'], 'ID': 'val', 'Name': 'validation one', 'path': '/tmp/foo/'} """ data = {} metadata = self.get_metadata for key in metadata: if key == 'id': data[key.upper()] = metadata.get(key) else: data[key.capitalize()] = metadata.get(key) return data