5a23115811
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
371 lines
11 KiB
Python
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
|