validations-libs/validations_libs/validation.py

371 lines
11 KiB
Python

# 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.
#
import logging
import os
import yaml
from collections import OrderedDict
LOG = logging.getLogger(__name__ + ".validation")
class Validation(object):
"""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