Add a command to create a new Kayobe environment

Change-Id: I2f33bda5f2f84bbbb0f40240d2575a0b69ddceea
Story: 2002009
Task: 40038
This commit is contained in:
Pierre Riteau 2021-03-09 16:06:36 +01:00
parent 1419636930
commit 2bc568debb
5 changed files with 194 additions and 0 deletions

View File

@ -21,6 +21,7 @@ from cliff.command import Command
from cliff.hooks import CommandHook
from kayobe import ansible
from kayobe import environment
from kayobe import kolla_ansible
from kayobe import utils
from kayobe import vault
@ -1687,3 +1688,34 @@ class BaremetalComputeUpdateDeploymentImage(KayobeAnsibleMixin, VaultMixin,
)
self.run_kayobe_playbooks(parsed_args, playbooks,
extra_vars=extra_vars)
class EnvironmentCreate(KayobeAnsibleMixin, VaultMixin, Command):
"""Create a new Kayobe environment."""
def get_parser(self, prog_name):
parser = super(EnvironmentCreate, self).get_parser(prog_name)
group = parser.add_argument_group("Kayobe Environments")
environment.add_args(group)
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Creating new Kayobe environment")
if not parsed_args.environment:
self.app.LOG.error("An environment must be specified")
sys.exit(1)
source_config_path = parsed_args.source_config_path
if source_config_path:
result = utils.is_readable_dir(source_config_path)
if not result["result"]:
self.app.LOG.error("Kayobe configuration %s is invalid: %s",
source_config_path, result["message"])
sys.exit(1)
try:
environment.create_kayobe_environment(parsed_args)
except Exception as e:
self.app.LOG.error("Failed to create environment %s: %s",
parsed_args.environment, repr(e))
sys.exit(1)

64
kayobe/environment.py Normal file
View File

@ -0,0 +1,64 @@
# Copyright (c) 2021 StackHPC Ltd.
#
# 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 os.path
import sys
from kayobe import utils
LOG = logging.getLogger(__name__)
def add_args(parser):
"""Add arguments required for managing Kayobe environments to a parser."""
parser.add_argument("--source-config-path",
help="Kayobe configuration to import")
def create_kayobe_environment(parsed_args):
"""Create a new Kayobe environment."""
if not parsed_args.environment:
LOG.error("You must specify an environment to create")
sys.exit(1)
# Ensure environments directory exists and is readable inside config path
kc_environments = os.path.join(parsed_args.config_path, "environments")
result = utils.is_readable_dir(kc_environments)
if not result["result"]:
if result["message"] == "Path does not exist":
os.mkdir(kc_environments)
else:
LOG.error("Kayobe global environments directory %s is invalid: %s",
kc_environments, result["message"])
sys.exit(1)
env_path = os.path.join(kc_environments, parsed_args.environment)
result = utils.is_readable_dir(env_path)
if result["result"]:
LOG.error("Kayobe environment directory %s already exists", env_path)
sys.exit(1)
else:
if result["message"] == "Path does not exist":
os.mkdir(env_path)
else:
LOG.error("Kayobe environment directory %s is invalid: %s",
env_path, result["message"])
sys.exit(1)
source_config_path = parsed_args.source_config_path
if source_config_path:
utils.copy_dir(source_config_path, env_path)

View File

@ -0,0 +1,83 @@
# Copyright (c) 2021 StackHPC Ltd.
#
# 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 argparse
import os
import unittest
from unittest import mock
from kayobe import ansible
from kayobe import environment
from kayobe import utils
class TestCase(unittest.TestCase):
@mock.patch.object(utils, "is_readable_dir")
def test_unreadable_environments_directory(self, mock_readable_dir):
mock_readable_dir.return_value = {
"result": False,
"message": "Directory is not readable"
}
parser = argparse.ArgumentParser()
args = [
"--config-path", "/path/to/config",
"--environment", "foo",
]
ansible.add_args(parser)
environment.add_args(parser)
parsed_args = parser.parse_args(args)
self.assertRaises(SystemExit,
environment.create_kayobe_environment, parsed_args)
@mock.patch.object(utils, "is_readable_dir")
def test_environment_exists(self, mock_readable_dir):
mock_readable_dir.side_effect = [{"result": True}, {"result": True}]
parser = argparse.ArgumentParser()
args = [
"--config-path", "/path/to/config",
"--environment", "foo",
]
ansible.add_args(parser)
environment.add_args(parser)
parsed_args = parser.parse_args(args)
self.assertRaises(SystemExit,
environment.create_kayobe_environment, parsed_args)
@mock.patch.object(utils, "copy_dir")
@mock.patch.object(os, "mkdir")
@mock.patch.object(utils, "is_readable_dir")
def test_create_kayobe_environment(self, mock_readable_dir, mock_mkdir,
mock_copy_dir):
mock_readable_dir.return_value = {
"result": False,
"message": "Path does not exist"
}
parser = argparse.ArgumentParser()
args = [
"--config-path", "/path/to/config",
"--source-config-path", "/path/to/foo",
"--environment", "foo",
]
ansible.add_args(parser)
environment.add_args(parser)
parsed_args = parser.parse_args(args)
environment.create_kayobe_environment(parsed_args)
expected_calls = [
mock.call("/path/to/config/environments"),
mock.call("/path/to/config/environments/foo"),
]
self.assertEqual(expected_calls, mock_mkdir.call_args_list)
mock_copy_dir.assert_called_once_with(
"/path/to/foo", "/path/to/config/environments/foo")

View File

@ -17,6 +17,7 @@ import glob
import itertools
import logging
import os
import shutil
import subprocess
import sys
@ -222,3 +223,16 @@ def intersect_limits(args_limit, cli_limit):
separator = ':&'
limits = [l for l in [args_limit, cli_limit] if l]
return separator.join(limits)
def copy_dir(src, dest):
if not os.path.isdir(dest):
shutil.copytree(src, dest)
else:
for file in os.listdir(src):
src_path = os.path.join(src, file)
dest_path = os.path.join(dest, file)
if os.path.isdir(src_path):
copy_dir(src_path, dest_path)
else:
shutil.copy2(src_path, dest_path)

View File

@ -48,6 +48,7 @@ kayobe.cli=
control_host_bootstrap = kayobe.cli.commands:ControlHostBootstrap
control_host_upgrade = kayobe.cli.commands:ControlHostUpgrade
configuration_dump = kayobe.cli.commands:ConfigurationDump
environment_create = kayobe.cli.commands:EnvironmentCreate
kolla_ansible_run = kayobe.cli.commands:KollaAnsibleRun
network_connectivity_check = kayobe.cli.commands:NetworkConnectivityCheck
overcloud_bios_raid_configure = kayobe.cli.commands:OvercloudBIOSRAIDConfigure