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.
|
|
#
|
|
|
|
import logging
|
|
import os
|
|
import yaml
|
|
from collections import OrderedDict
|
|
|
|
LOG = logging.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
|