blob: 56116fcb68549a470f1c93411f2432e664874d48 [file] [log] [blame]
# Copyright 2016 Mirantis, 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 tcp_tests import logger
from tcp_tests.managers.execute_commands import ExecuteCommandsMixin
LOG = logger.logger
class ReclassManager(ExecuteCommandsMixin):
"""docstring for ReclassManager"""
__config = None
__underlay = None
reclass_tools_cmd = ". venv-reclass-tools/bin/activate; reclass-tools "
tgt = "cfg01" # place where the reclass-tools installed
def __init__(self, config, underlay):
self.__config = config
self.__underlay = underlay
reclass_node = [node_name
for node_name in self.__underlay.node_names()
if self.tgt in node_name]
self.ssh = self.__underlay.remote(node_name=reclass_node[0])
super(ReclassManager, self).__init__(config=config, underlay=underlay)
def check_existence(self, key):
"""
Returns True if reclass contains that key.
:param key: string
:return: boolean
"""
if key in self.ssh.check_call(
"{reclass_tools} get-key {key} /srv/salt/reclass/classes".
format(reclass_tools=self.reclass_tools_cmd,
key=key)):
LOG.warning("({}) key already exists in reclass".format(key))
return True
return False
def add_key(self, key, value, short_path):
"""
Shows alert if key exists
:param key: string, parameters which will be added or updated
:param value: value of key
:param short_path: path to reclass yaml file.
It takes into account default path where the reclass locates.
May look like cluster/*/cicd/control/leader.yml
:return: None
"""
value = str(value).replace('"', "'")
# let's escape $ symbol for bash-like command
value = str(value).replace("$", r"\$")
# value can contain a space symbol. So value should be in quotes
cmd = "{reclass_tools} add-key {key} \"{value}\" \
/srv/salt/reclass/classes/{path}".format(
reclass_tools=self.reclass_tools_cmd,
key=key,
value=value,
path=short_path)
LOG.info("Add key to reclass: \n {cmd} ".format(cmd=cmd))
self.ssh.check_call(cmd)
def get_key(self, key, file_name):
"""Find a key in a YAML
:param key: string, parameter to add
:param file_name: name of YAML file to find a key
:return: str, key if found
"""
LOG.info("Try to get '{key}' key from '{file}' file".format(
file=file_name,
key=key
))
request_key = self.ssh.check_call(
"{reclass_tools} get-key {key} /srv/salt/reclass/*/{file_name}".
format(reclass_tools=self.reclass_tools_cmd,
key=key,
file_name=file_name))['stdout']
# Reclass-tools returns result to stdout, so we get it as
# ['\n',
# '---\n',
# '# Found parameters._param.jenkins_pipelines_branch in \
# /srv/salt/reclass/classes/cluster/../infra/init.yml\n',
# 'release/proposed/2019.2.0\n',
# '...\n',
# '\n']
# So we have no chance to get value without dirty code like `stdout[3]`
LOG.info("Raw output from reclass.get_key {}".format(request_key))
if len(request_key) < 4:
print("Can't find {key} in {file_name}. Got stdout {stdout}".
format(key=key,
file_name=file_name,
stdout=request_key))
return None
value = request_key[3].strip('\n')
LOG.info("From reclass.get_key {}".format(value))
return value
def add_bool_key(self, key, value, short_path):
"""
Shows alert if key exists
:param key: string, parameters which will be added or updated
:param value: value of key
:param short_path: path to reclass yaml file.
It takes into account default path where the reclass locates.
May look like cluster/*/cicd/control/leader.yml
:return: None
"""
self.check_existence(key)
self.ssh.check_call(
"{reclass_tools} add-bool-key {key} {value} \
/srv/salt/reclass/classes/{path}".format(
reclass_tools=self.reclass_tools_cmd,
key=key,
value=value,
path=short_path
), raise_on_err=False)
def add_class(self, value, short_path):
"""
Shows warning if class exists
:param value: role to add to 'classes' parameter in the reclass
:param short_path: path to reclass yaml file.
It takes into account default path where the reclass locates.
May look like cluster/*/cicd/control/leader.yml
:return: None
"""
if value in self.ssh.check_call(
"{reclass_tools} get-key classes \
/srv/salt/reclass/classes/{path}".format(
reclass_tools=self.reclass_tools_cmd,
value=value,
path=short_path
)):
LOG.warning("Class {} already exists in {}".format(
value,
short_path
))
self.ssh.check_call(
"{reclass_tools} add-key classes {value} \
/srv/salt/reclass/classes/{path} --merge".format(
reclass_tools=self.reclass_tools_cmd,
value=value,
path=short_path
))
def delete_key(self, key, short_path):
"""
Remove key from the provided file
:param value: string, parameter which will be deleted
:param short_path: string,, path to reclass yaml file.
It takes into account default path where the reclass locates.
May look like cluster/*/cicd/control/leader.yml
:return: None
"""
self.ssh.check_call(
"{reclass_tools} del-key {key} \
/srv/salt/reclass/classes/{path}".format(
reclass_tools=self.reclass_tools_cmd,
key=key,
path=short_path
))
def merge_context(self, yaml_context, short_path):
"""
Merge
:param yaml_context: string, yaml with extra context
:param short_path: string, path to reclass yaml file.
It takes into account default path where the reclass locates.
May look like cluster/*/cicd/control/leader.yml
:return: None
"""
tmp_file = "/tmp/extra_context.yaml"
with open(tmp_file, "w") as f:
f.write(yaml_context)
self.ssh.upload(tmp_file, tmp_file)
self.ssh.check_call(
"{reclass_tools} merge-context {yaml} \
/srv/salt/reclass/classes/{path}".format(
reclass_tools=self.reclass_tools_cmd,
yaml=tmp_file,
path=short_path
))
def commit(self, text_commit):
self.ssh.check_call(
"cd /srv/salt/reclass; git add -u && git commit --allow-empty "
"-m '{text}'".format(text=text_commit))