
The validation init did not respect the paths set on the config file. Now it does. Resolves: rhbz#2031069 Change-Id: Ia492b53f170d65f712a182a545ef09217449648c
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.
|
|
#
|
|
|
|
import logging
|
|
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 = logging.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)
|