validations-libs/validations_libs/validation.py
Jiri Podivin 5a23115811 Expanding logging facilities to rest of the framework
Logger was introduced in If8ed6510dad16dc8495717789bb132b957828e0d
and so far has been performing admirably.

We can now expand it to the rest of the library and remove leftovers
from the original setup. This way we can establish proper monitoring
of code execution across our tool and provide operators with
more actionable information.

Signed-off-by: Jiri Podivin <jpodivin@redhat.com>
Change-Id: I3dd296c8b8b9a33f87a451dd7bef68b38ba60af7
2022-11-23 09:16:18 +00:00

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.
#
from validations_libs.logger import getLogger
import os
import yaml
from collections import OrderedDict
LOG = getLogger(__name__ + ".validation")
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