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
230 lines
7.4 KiB
Python
230 lines
7.4 KiB
Python
#!/usr/bin/env python
|
|
|
|
# Copyright 2021 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 re
|
|
import os
|
|
# @matbu backward compatibility for stable/train
|
|
try:
|
|
from pathlib import Path
|
|
except ImportError:
|
|
from pathlib2 import Path
|
|
|
|
from validations_libs import constants, utils
|
|
|
|
LOG = getLogger(__name__)
|
|
|
|
|
|
class CommunityValidation:
|
|
"""Init Community Validation Role and Playbook Command Class
|
|
|
|
Initialize a new community role using ansible-galaxy and create a playboook
|
|
from a template.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
validation_name,
|
|
validation_dir=constants.ANSIBLE_VALIDATION_DIR,
|
|
ansible_base_dir=constants.DEFAULT_VALIDATIONS_BASEDIR):
|
|
"""Construct Role and Playbook."""
|
|
|
|
self._validation_name = validation_name
|
|
self.validation_dir = validation_dir
|
|
self.ansible_base_dir = ansible_base_dir
|
|
|
|
def execute(self):
|
|
"""Execute the actions necessary to create a new community validation
|
|
|
|
Check if the role name is compliant with Ansible specification
|
|
Initializing the new role using ansible-galaxy
|
|
Creating the validation playbook from a template on disk
|
|
|
|
:rtype: ``NoneType``
|
|
"""
|
|
if not self.is_role_name_compliant:
|
|
raise RuntimeError(
|
|
"Role Name are limited to contain only lowercase "
|
|
"alphanumeric characters, plus '_', '-' and start with an "
|
|
"alpha character."
|
|
)
|
|
|
|
cmd = ['ansible-galaxy', 'init', '-v',
|
|
'--offline', self.role_name,
|
|
'--init-path', self.role_basedir]
|
|
|
|
result = utils.run_command_and_log(LOG, cmd)
|
|
|
|
if result != 0:
|
|
raise RuntimeError(
|
|
(
|
|
"Ansible Galaxy failed to create the role "
|
|
"{}, returned {}."
|
|
.format(self.role_name, result)
|
|
)
|
|
)
|
|
|
|
LOG.info("New role created successfully in {}"
|
|
.format(self.role_dir_path))
|
|
|
|
try:
|
|
self.create_playbook()
|
|
except (PermissionError, OSError) as error:
|
|
raise RuntimeError(
|
|
(
|
|
"Exception {} encountered while trying to write "
|
|
"the community validation playbook file {}."
|
|
.format(error, self.playbook_path)
|
|
)
|
|
)
|
|
|
|
LOG.info("New playbook created successfully in {}"
|
|
.format(self.playbook_path))
|
|
|
|
def create_playbook(self, content=constants.COMMUNITY_PLAYBOOK_TEMPLATE):
|
|
"""Create the playbook for the new community validation"""
|
|
playbook = content.format(self.role_name)
|
|
with open(self.playbook_path, 'w') as playbook_file:
|
|
playbook_file.write(playbook)
|
|
|
|
def is_role_exists(self):
|
|
"""New role existence check
|
|
|
|
This class method checks if the new role name is already existing
|
|
in the official validations catalog and in the current community
|
|
validations directory.
|
|
|
|
First, it gets the list of the role names available in
|
|
``constants.ANSIBLE_ROLES_DIR``. If there is a match in at least one
|
|
of the directories, it returns ``True``, otherwise ``False``.
|
|
|
|
:rtype: ``Boolean``
|
|
"""
|
|
roles_dir = os.path.join(self.ansible_base_dir, "roles/")
|
|
non_community_roles = []
|
|
if Path(roles_dir).exists():
|
|
non_community_roles = [
|
|
Path(x).name
|
|
for x in Path(roles_dir).iterdir()
|
|
if x.is_dir()
|
|
]
|
|
|
|
return Path(self.role_dir_path).exists() or \
|
|
self.role_name in non_community_roles
|
|
|
|
def is_playbook_exists(self):
|
|
"""New playbook existence check
|
|
|
|
This class method checks if the new playbook file is already existing
|
|
in the official validations catalog and in the current community
|
|
validations directory.
|
|
|
|
First, it gets the list of the playbooks yaml file available in
|
|
``constants.ANSIBLE_VALIDATIONS_DIR``. If there is a match in at least
|
|
one of the directories, it returns ``True``, otherwise ``False``.
|
|
|
|
:rtype: ``Boolean``
|
|
"""
|
|
non_community_playbooks = []
|
|
if Path(self.validation_dir).exists():
|
|
non_community_playbooks = [
|
|
Path(x).name
|
|
for x in Path(self.validation_dir).iterdir()
|
|
if x.is_file()
|
|
]
|
|
|
|
return Path(self.playbook_path).exists() or \
|
|
self.playbook_name in non_community_playbooks
|
|
|
|
def is_community_validations_enabled(self, base_config):
|
|
"""Checks if the community validations are enabled in the config file
|
|
|
|
:param base_config: Contents of the configuration file
|
|
:type base_config: ``Dict``
|
|
|
|
:rtype: ``Boolean``
|
|
"""
|
|
config = base_config
|
|
default_conf = (config.get('default', {})
|
|
if isinstance(config, dict) else {})
|
|
return default_conf.get('enable_community_validations', True)
|
|
|
|
@property
|
|
def role_name(self):
|
|
"""Returns the community validation role name
|
|
|
|
:rtype: ``str``
|
|
"""
|
|
if re.match(r'^[a-z][a-z0-9_-]+$', self._validation_name) and \
|
|
'-' in self._validation_name:
|
|
return self._validation_name.replace('-', '_')
|
|
return self._validation_name
|
|
|
|
@property
|
|
def role_basedir(self):
|
|
"""Returns the absolute path of the community validations roles
|
|
|
|
:rtype: ``pathlib.PosixPath``
|
|
"""
|
|
return constants.COMMUNITY_ROLES_DIR
|
|
|
|
@property
|
|
def role_dir_path(self):
|
|
"""Returns the community validation role directory name
|
|
|
|
:rtype: ``pathlib.PosixPath``
|
|
"""
|
|
return Path.joinpath(self.role_basedir, self.role_name)
|
|
|
|
@property
|
|
def is_role_name_compliant(self):
|
|
"""Check if the role name is compliant with Ansible Rules
|
|
|
|
Roles Name are limited to contain only lowercase
|
|
alphanumeric characters, plus '_' and start with an
|
|
alpha character.
|
|
|
|
:rtype: ``Boolean``
|
|
"""
|
|
if not re.match(r'^[a-z][a-z0-9_]+$', self.role_name):
|
|
return False
|
|
return True
|
|
|
|
@property
|
|
def playbook_name(self):
|
|
"""Return the new playbook name with the yaml extension
|
|
|
|
:rtype: ``str``
|
|
"""
|
|
return self._validation_name.replace('_', '-') + ".yaml"
|
|
|
|
@property
|
|
def playbook_basedir(self):
|
|
"""Returns the absolute path of the community playbooks directory
|
|
|
|
:rtype: ``pathlib.PosixPath``
|
|
"""
|
|
return constants.COMMUNITY_PLAYBOOKS_DIR
|
|
|
|
@property
|
|
def playbook_path(self):
|
|
"""Returns the absolute path of the new community playbook yaml file
|
|
|
|
:rtype: ``pathlib.PosixPath``
|
|
"""
|
|
return Path.joinpath(self.playbook_basedir, self.playbook_name)
|