Merge "Remove plan/plan-role management commands"

This commit is contained in:
Zuul 2021-02-04 12:47:48 +00:00 committed by Gerrit Code Review
commit 28ce651e85
15 changed files with 20 additions and 2167 deletions

View File

@ -0,0 +1,8 @@
---
upgrade:
- |
``openstack overcloud plan *`` commands have been removed.
These commands are irrelevant as overcloud deploy/update/upgrade
does not create/update swift plan anymore. Also, some of the
``openstack overcloud role`` commands that use swift plan have
been removed.

View File

@ -67,19 +67,12 @@ openstack.tripleoclient.v2 =
overcloud_node_unprovision = tripleoclient.v2.overcloud_node:UnprovisionNode
overcloud_node_extract_provisioned = tripleoclient.v1.overcloud_node:ExtractProvisionedNode
overcloud_parameters_set = tripleoclient.v1.overcloud_parameters:SetParameters
overcloud_plan_create = tripleoclient.v1.overcloud_plan:CreatePlan
overcloud_plan_delete = tripleoclient.v1.overcloud_plan:DeletePlan
overcloud_plan_deploy = tripleoclient.v1.overcloud_plan:DeployPlan
overcloud_plan_list = tripleoclient.v1.overcloud_plan:ListPlans
overcloud_plan_export = tripleoclient.v1.overcloud_plan:ExportPlan
overcloud_profiles_match = tripleoclient.v1.overcloud_profiles:MatchProfiles
overcloud_profiles_list = tripleoclient.v1.overcloud_profiles:ListProfiles
overcloud_raid_create = tripleoclient.v1.overcloud_raid:CreateRAID
overcloud_role_show= tripleoclient.v1.overcloud_roles:RoleShow
overcloud_role_list = tripleoclient.v1.overcloud_roles:RoleList
overcloud_roles_generate = tripleoclient.v1.overcloud_roles:RolesGenerate
overcloud_roles_list = tripleoclient.v1.overcloud_plan_roles:ListRoles
overcloud_roles_show = tripleoclient.v1.overcloud_plan_roles:ShowRole
overcloud_support_report_collect = tripleoclient.v2.overcloud_support:ReportExecute
overcloud_update_prepare= tripleoclient.v1.overcloud_update:UpdatePrepare
overcloud_update_run = tripleoclient.v1.overcloud_update:UpdateRun

View File

@ -205,16 +205,6 @@ try:
except (configparser.NoOptionError, FileNotFoundError):
UNDERCLOUD_OUTPUT_DIR = CLOUD_HOME_DIR
# regex patterns to exclude when looking for unused params
# - exclude *Image params as they may be unused because the service is not
# enabled
# - exclude SwiftFetchDir*Tempurl because it's used by ceph and generated by us
# - exclude PythonInterpreter because it's generated by us and only used
# in some custom scripts
UNUSED_PARAMETER_EXCLUDES_RE = ['^(Docker|Container).*Image$',
'^SwiftFetchDir(Get|Put)Tempurl$',
'^PythonInterpreter$']
EXPORT_PASSWORD_EXCLUDE_PATTERNS = [
'ceph.*'
]

View File

@ -27,8 +27,6 @@ from osc_lib.tests import utils
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import plugin
from tripleoclient.tests import fakes as ooofakes
from tripleoclient.tests.fixture_data import deployment
from tripleoclient.tests.v1.overcloud_deploy import fakes
from tripleoclient.v1 import overcloud_deploy
@ -91,53 +89,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_run_command.start()
self.addCleanup(mock_run_command.stop)
plan_list = mock.patch(
"tripleoclient.workflows.plan_management.list_deployment_plans",
autospec=True
)
plan_list.start()
plan_list.return_value = ([
"test-plan-1",
"test-plan-2",
])
self.addCleanup(plan_list.stop)
roles = mock.patch(
'tripleoclient.workflows.roles.list_available_roles',
autospec=True,
return_value=[
{
'TestRole1': {
'TestParameter1': {}
}
}
]
)
roles.start()
self.addCleanup(roles.stop)
flatten = mock.patch(
'tripleo_common.utils.stack_parameters.get_flattened_parameters',
autospec=True,
return_value={
'environment_parameters': {
'TestParameter1': {},
'TestRole1': 'TestParameter2'
},
'heat_resource_tree': {
'parameters': {
'TestParameter2': {
'name': 'TestParameter2',
'tags': [
'role_specific'
]
}
},
'resources': {}
}
}
)
flatten.start()
self.addCleanup(flatten.stop)
# Mock playbook runner
playbook_runner = mock.patch(
'tripleoclient.utils.run_ansible_playbook',
@ -404,8 +355,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@mock.patch('tripleoclient.utils.check_stack_network_matches_env_files')
@mock.patch('tripleoclient.utils.check_ceph_fsid_matches_env_files')
@mock.patch('tripleoclient.workflows.parameters.'
'check_deprecated_parameters', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_postconfig', autospec=True)
@mock.patch('tripleo_common.update.add_breakpoints_cleanup_into_env',
@ -423,7 +372,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_get_template_contents,
mock_create_parameters_env, mock_validate_args,
mock_breakpoints_cleanup,
mock_postconfig, mock_deprecated_params, mock_stack_network_check,
mock_postconfig, mock_stack_network_check,
mock_ceph_fsid, mock_get_undercloud_host_entry, mock_copy,
mock_chdir, mock_overcloudrc,
mock_process_env, mock_roles_data,
@ -1274,10 +1223,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
@mock.patch('tripleoclient.utils.check_stack_network_matches_env_files')
@mock.patch('tripleoclient.utils.check_ceph_fsid_matches_env_files')
@mock.patch('heatclient.common.template_utils.deep_update', autospec=True)
@mock.patch('tripleoclient.workflows.plan_management.'
'create_plan_from_templates', autospec=True)
def test_config_download_timeout(
self, mock_plan_man, mock_hc, mock_stack_network_check,
self, mock_hc, mock_stack_network_check,
mock_ceph_fsid, mock_hd,
mock_get_undercloud_host_entry, mock_copy,
mock_get_ctlplane_attrs, mock_nic_ansible,
@ -1633,14 +1580,6 @@ class TestGetDeploymentStatus(utils.TestCommand):
super(TestGetDeploymentStatus, self).setUp()
self.cmd = overcloud_deploy.GetDeploymentStatus(self.app, None)
self.app.client_manager = mock.Mock()
clients = self.clients = self.app.client_manager
tc = clients.tripleoclient = ooofakes.FakeClientWrapper()
tc.create_mistral_context = plugin.ClientWrapper(
instance=ooofakes.FakeInstanceData
).create_mistral_context
obj = tc.object_store = mock.Mock()
obj.put_object = mock.Mock()
obj.put_container = mock.Mock()
@mock.patch("tripleoclient.workflows.deployment.get_deployment_status")
def test_get_deployment_status(self, mock_get_deployment_status):
@ -1652,10 +1591,10 @@ class TestGetDeploymentStatus(utils.TestCommand):
self.cmd.take_action(parsed_args)
expected = (
'+-----------+-------------------+\n'
'| Plan Name | Deployment Status |\n'
'+-----------+-------------------+\n'
'+------------+-------------------+\n'
'| Stack Name | Deployment Status |\n'
'+------------+-------------------+\n'
'| overcloud | DEPLOY_SUCCESS |\n'
'+-----------+-------------------+\n')
'+------------+-------------------+\n')
self.assertEqual(expected, self.cmd.app.stdout.getvalue())

View File

@ -1,412 +0,0 @@
# 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 mock
from osc_lib.tests import utils
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import plugin
from tripleoclient.tests import fakes
from tripleoclient.v1 import overcloud_plan
class TestStringCapture(object):
def __init__(self):
self.capture_string = ''
def write(self, msg):
self.capture_string = self.capture_string + msg
def getvalue(self):
return self.capture_string
def flush(self):
return
class TestOvercloudPlanList(utils.TestCommand):
def setUp(self):
super(TestOvercloudPlanList, self).setUp()
self.app.client_manager.tripleoclient = plugin.ClientWrapper(
instance=fakes.FakeInstanceData
)
self.cmd = overcloud_plan.ListPlans(self.app, None)
@mock.patch("tripleoclient.workflows.plan_management."
"list_deployment_plans",
autospec=True)
def test_list_empty(self, mock_list_plans):
mock_list_plans.return_value = []
result = self.cmd.take_action(None)
self.assertEqual(0, len(result[1]))
@mock.patch("tripleoclient.workflows.plan_management."
"list_deployment_plans",
autospec=True)
def test_list(self, mock_list_plans):
mock_list_plans.return_value = (['test-plan-1', 'test-plan-2'])
result = self.cmd.take_action(None)
self.assertEqual(1, len(result[0]))
self.assertEqual([('test-plan-1',), ('test-plan-2',)], result[1])
class TestOvercloudDeletePlan(fakes.FakePlaybookExecution):
def setUp(self):
super(TestOvercloudDeletePlan, self).setUp()
self.cmd = overcloud_plan.DeletePlan(self.app, None)
@mock.patch("tripleo_common.actions.plan.DeletePlanAction.run",
return_value=None)
def test_delete_plan(self, mock_run):
parsed_args = self.check_parser(self.cmd, ['test-plan'],
[('plans', ['test-plan'])])
self.cmd.take_action(parsed_args)
@mock.patch("tripleo_common.actions.plan.DeletePlanAction.run",
return_value=None)
def test_delete_multiple_plans(self, mock_run):
argslist = ['test-plan1', 'test-plan2']
verifylist = [('plans', ['test-plan1', 'test-plan2'])]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.cmd.take_action(parsed_args)
class TestOvercloudCreatePlan(utils.TestCommand):
def setUp(self):
super(TestOvercloudCreatePlan, self).setUp()
app_args = mock.Mock()
app_args.verbose_level = 1
self.app.options = fakes.FakeOptions()
self.cmd = overcloud_plan.CreatePlan(self.app, app_args)
self.app.client_manager.workflow_engine = mock.Mock()
self.tripleoclient = mock.Mock()
self.app.client_manager.tripleoclient = self.tripleoclient
self.swift = self.app.client_manager.tripleoclient.object_store
self.swift.get_account = mock.MagicMock()
self.mock_tar = mock.patch(
'tripleo_common.utils.tarball.create_tarball',
autospec=True
)
self.mock_tar.start()
def tearDown(self):
super(TestOvercloudCreatePlan, self).tearDown()
self.mock_tar.stop()
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_default_plan(self, mock_tmp, mock_cd, mock_run_playbook):
# Setup
arglist = ['overcast']
verifylist = [
('name', 'overcast'),
('templates', None)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# Run
self.cmd.take_action(parsed_args)
# Verify
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "overcast",
"generate_passwords": True,
"use_default_templates": True,
"disable_image_params_prepare": False,
},
verbosity=3,
)
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_custom_plan(self, mock_tmp, mock_cd,
mock_run_playbook):
# Setup
arglist = ['overcast', '--templates', '/fake/path']
verifylist = [
('name', 'overcast'),
('templates', '/fake/path')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# Run
self.cmd.take_action(parsed_args)
# Verify
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "overcast",
"generate_passwords": True,
"use_default_templates": False,
"disable_image_params_prepare": False,
},
verbosity=3,
)
self.swift.get_account.assert_called_once()
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_custom_plan_plan_environment_file(
self, mock_tmp, mock_cd, mock_run_playbook):
# Setup
arglist = ['overcast', '--templates', '/fake/path',
'-p', 'the_plan_environment.yaml']
verifylist = [
('name', 'overcast'),
('templates', '/fake/path'),
('plan_environment_file', 'the_plan_environment.yaml')
]
self.app.options = fakes.FakeOptions()
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_open = mock.mock_open()
# Run
with mock.patch('six.moves.builtins.open', mock_open):
self.cmd.take_action(parsed_args)
mock_open.assert_has_calls(
[mock.call('the_plan_environment.yaml', 'rb')])
# Verify
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "overcast",
"generate_passwords": True,
"plan_environment": "the_plan_environment.yaml",
"use_default_templates": False,
"disable_image_params_prepare": False,
},
verbosity=3,
)
self.swift.get_account.assert_called_once()
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_default_plan_with_password_gen_disabled(
self, mock_tmp, mock_cd, mock_run_playbook):
# Setup
arglist = ['overcast', '--disable-password-generation',
'--disable-container-prepare']
verifylist = [
('name', 'overcast'),
('templates', None),
('disable_password_generation', True),
('disable_container_prepare', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# Run
self.app.options = fakes.FakeOptions()
self.cmd.take_action(parsed_args)
# Verify
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "overcast",
"generate_passwords": False,
"use_default_templates": True,
"disable_image_params_prepare": True,
},
verbosity=3,
)
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_custom_plan_from_source_url(
self, mock_tmp, mock_cd, mock_run_playbook):
# Setup
arglist = ['overcast', '--source-url', 'http://tripleo.org/templates']
verifylist = [
('name', 'overcast'),
('templates', None),
('source_url', 'http://tripleo.org/templates')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# Run
self.cmd.take_action(parsed_args)
# Verify
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "overcast",
"generate_passwords": True,
"use_default_templates": False,
"source_url": "http://tripleo.org/templates",
"disable_image_params_prepare": False,
},
verbosity=3,
)
class TestOvercloudDeployPlan(utils.TestCommand):
def setUp(self):
super(TestOvercloudDeployPlan, self).setUp()
app_args = mock.Mock()
app_args.verbose_level = 1
self.app.options = fakes.FakeOptions()
self.cmd = overcloud_plan.DeployPlan(self.app, app_args)
sleep_patch = mock.patch('time.sleep')
self.addCleanup(sleep_patch.stop)
sleep_patch.start()
@mock.patch("tripleoclient.utils.update_deployment_status", autospec=True)
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('tripleoclient.utils.wait_for_stack_ready', autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_overcloud_deploy_plan(self, mock_tmp, mock_cd,
mock_for_stack_ready,
mock_run_playbook,
mock_update_status):
# Setup
arglist = ['--run-validations', 'overcast']
verifylist = [
('name', 'overcast'),
('run_validations', True),
('timeout', 240)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.orch = self.app.client_manager.orchestration = mock.Mock()
# No existing stack, this is a new deploy.
self.orch.stacks.get.return_value = None
mock_for_stack_ready.return_value = True
# Run
self.cmd.take_action(parsed_args)
mock_run_playbook.assert_called_with(
'cli-deploy-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
timeout=240,
extra_vars={
"container": "overcast",
"run_validations": True,
"skip_deploy_identifier": False,
},
verbosity=3,
)
mock_update_status.assert_called()
class TestOvercloudExportPlan(utils.TestCommand):
def setUp(self):
super(TestOvercloudExportPlan, self).setUp()
self.cmd = overcloud_plan.ExportPlan(self.app, None)
self.app.client_manager = mock.Mock()
self.clients = self.app.client_manager
# Mock urlopen
f = mock.Mock()
f.read.return_value = 'tarball contents'
urlopen_patcher = mock.patch('six.moves.urllib.request.urlopen',
return_value=f)
self.mock_urlopen = urlopen_patcher.start()
self.addCleanup(self.mock_urlopen.stop)
@mock.patch(
'tripleoclient.workflows.plan_management.export_deployment_plan',
autospec=True)
def test_export_plan(self, export_deployment_plan_mock):
parsed_args = self.check_parser(self.cmd, ['test-plan'],
[('plan', 'test-plan')])
export_deployment_plan_mock.return_value = 'http://fake-url.com'
with mock.patch('six.moves.builtins.open', mock.mock_open()):
self.cmd.take_action(parsed_args)
export_deployment_plan_mock.assert_called_once_with(
self.clients, 'test-plan')
@mock.patch('os.path.exists')
def test_export_plan_outfile_exists(self, exists_mock):
parsed_args = self.check_parser(self.cmd, ['test-plan'],
[('plan', 'test-plan')])
exists_mock.return_value = True
self.assertRaises(exceptions.PlanExportError,
self.cmd.take_action, parsed_args)
@mock.patch(
'tripleoclient.workflows.plan_management.export_deployment_plan',
autospec=True)
@mock.patch('os.path.exists')
def test_export_plan_outfile_exists_with_overwrite(
self, exists_mock, export_deployment_plan_mock):
arglist = ['-f', 'test-plan']
verifylist = [
('plan', 'test-plan'),
('force_overwrite', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
exists_mock.return_value = True
export_deployment_plan_mock.return_value = 'http://fake-url.com'
with mock.patch('six.moves.builtins.open', mock.mock_open()):
self.cmd.take_action(parsed_args)
export_deployment_plan_mock.assert_called_once_with(
self.clients, 'test-plan')

View File

@ -1,169 +0,0 @@
# 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 mock
from osc_lib import exceptions
from osc_lib.tests import utils
from tripleoclient.v1 import overcloud_plan_roles
class BaseTestCommand(utils.TestCommand):
def setUp(self):
super(BaseTestCommand, self).setUp()
tc = self.app.client_manager.tripleoclient = mock.Mock()
tc.object_store.get_object.return_value = (
{},
'{"result": [{"name":"Controller","description":"Test desc",'
'"random": "abcd"},{"name":"Test"}]}'
)
tc.object_store.get_container.return_value = (
'container',
[
{
"name": "Controller",
"description": "Test desc",
"random": "abcd",
"efg": "123",
"ServicesDefault": [
"b",
"c",
"a"
]
}
]
)
self.tripleoclient = tc
class TestOvercloudListCurrentRoles(BaseTestCommand):
def setUp(self):
super(TestOvercloudListCurrentRoles, self).setUp()
self.cmd = overcloud_plan_roles.ListRoles(self.app, None)
@mock.patch(
'tripleo_common.actions.plan.ListRolesAction.run',
autospec=True,
return_value=[]
)
def test_list_empty_on_non_default_plan(self, mock_list):
arglist = ['--name', 'overcast', '--current']
verifylist = [('name', 'overcast'), ('current', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertEqual(0, len(result[1]))
@mock.patch(
'tripleo_common.actions.plan.ListRolesAction.run',
autospec=True,
return_value=["ObjectStorage", "Controller"]
)
def test_list(self, mock_list):
arglist = ['--current']
verifylist = [('name', 'overcloud'), ('current', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertEqual(2, len(result[1]))
self.assertEqual([('Controller',), ('ObjectStorage',)], result[1])
@mock.patch(
'tripleo_common.actions.plan.ListRolesAction.run',
autospec=True,
return_value=[
{
"name": "Controller",
"description": "Test desc",
"random": "abcd"
},
{"name": "Test"}
]
)
def test_list_with_details(self, mock_list):
parsed_args = self.check_parser(self.cmd,
['--current', '--detail'],
[])
result = self.cmd.take_action(parsed_args)
data = result[1]
self.assertEqual(2, len(data))
self.assertEqual(data[0][0], "Controller")
self.assertEqual(data[0][3], "random: abcd")
self.assertEqual(data[1][0], "Test")
self.assertEqual(data[1][3], "")
@mock.patch(
'tripleo_common.actions.plan.ListRolesAction.run',
autospec=True,
return_value=[]
)
def test_list_with_details_empty(self, mock_list):
parsed_args = self.check_parser(self.cmd,
['--current', '--detail'],
[])
result = self.cmd.take_action(parsed_args)
self.assertEqual(0, len(result[1]))
@mock.patch(
'tripleo_common.actions.plan.ListRolesAction.run',
autospec=True,
return_value=[
{"name": "Compute"},
{"name": "Random"},
{"name": "BlockStorage", "ServicesDefault": ["c", "b", "a"]}
]
)
def test_list_with_details_sorted(self, mock_list):
parsed_args = self.check_parser(self.cmd,
['--current', '--detail'],
[])
result = self.cmd.take_action(parsed_args)
self.assertEqual(3, len(result[1]))
# Test main list sorted
self.assertEqual(result[1][0][0], "BlockStorage")
self.assertEqual(result[1][1][0], "Compute")
self.assertEqual(result[1][2][0], "Random")
# Test service sublist sorted
self.assertEqual(result[1][0][2], "a\nb\nc")
class TestOvercloudShowRole(BaseTestCommand):
def setUp(self):
super(TestOvercloudShowRole, self).setUp()
self.cmd = overcloud_plan_roles.ShowRole(self.app, None)
self.app.client_manager.tripleoclient = self.tripleoclient
@mock.patch(
'tripleo_common.actions.plan.ListRolesAction.run',
autospec=True,
return_value=[]
)
def test_role_not_found(self, mock_list):
arglist = ['--name', 'overcast', 'doesntexist']
verifylist = [('name', 'overcast'), ('role', 'doesntexist')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(exceptions.CommandError,
self.cmd.take_action,
parsed_args)

View File

@ -174,46 +174,6 @@ class TestParameterWorkflows(utils.TestCommand):
]
mock_playbook.assert_has_calls(calls, any_order=True)
def test_check_deprecated_params_no_output(self):
parameters.check_deprecated_parameters(
self.app.client_manager,
container='container-name')
def test_check_deprecated_params_user_defined(self):
with mock.patch('tripleoclient.workflows.parameters.LOG') as mock_log:
parameters.check_deprecated_parameters(
self.app.client_manager,
container='container-name')
self.assertTrue(mock_log.warning.called)
def test_check_deprecated_params_user_not_defined(self):
with mock.patch('tripleoclient.workflows.parameters.LOG') as mock_log:
parameters.check_deprecated_parameters(
self.app.client_manager,
container='container-name')
self.assertFalse(mock_log.log.warning.called)
def test_check_deprecated_multiple_parameters(self):
with mock.patch('tripleoclient.workflows.parameters.LOG') as mock_log:
parameters.check_deprecated_parameters(
self.app.client_manager,
container='container-name')
self.assertTrue(mock_log.warning.called)
def test_check_unused_multiple_parameters(self):
with mock.patch('tripleoclient.workflows.parameters.LOG') as mock_log:
parameters.check_deprecated_parameters(
self.app.client_manager,
container='container-name')
self.assertTrue(mock_log.warning.called)
def test_check_invalid_role_specific_parameters(self):
with mock.patch('tripleoclient.workflows.parameters.LOG') as mock_log:
parameters.check_deprecated_parameters(
self.app.client_manager,
container='container-name')
self.assertTrue(mock_log.warning.called)
@mock.patch(
'tripleo_common.utils.stack_parameters.generate_fencing_parameters',
return_value={})

View File

@ -1,397 +0,0 @@
# -*- coding: utf-8 -*-
# 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 mock
from osc_lib.tests import utils
from tripleoclient import constants
from tripleoclient.tests import base
from tripleoclient.workflows import plan_management
class TestPlanCreationWorkflows(utils.TestCommand):
def setUp(self):
super(TestPlanCreationWorkflows, self).setUp()
self.tripleoclient = mock.Mock()
self.app.client_manager.tripleoclient = self.tripleoclient
self.tripleoclient.object_store.get_account = mock.MagicMock()
self.mock_tar = mock.patch(
'tripleo_common.utils.tarball.create_tarball',
autospec=True
)
self.mock_tar.start()
def tearDown(self):
super(TestPlanCreationWorkflows, self).tearDown()
self.mock_tar.stop()
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_plan_from_templates_success(self, mock_tmp, mock_cd,
mock_run_playbook):
plan_management.create_plan_from_templates(
self.app.client_manager,
'test-overcloud',
'/tht-root/')
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "test-overcloud",
"generate_passwords": True,
"use_default_templates": False,
"disable_image_params_prepare": False,
},
verbosity=0,
)
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('tripleoclient.utils.rel_or_abs_path')
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_plan_from_templates_roles_data(self, mock_tmp, mock_cd,
mock_norm_path,
mock_run_playbook):
mock_open_context = mock.mock_open()
with mock.patch('six.moves.builtins.open', mock_open_context):
plan_management.create_plan_from_templates(
self.app.client_manager,
'test-overcloud',
'/tht-root/',
'the_roles_file.yaml')
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "test-overcloud",
"generate_passwords": True,
"use_default_templates": False,
"disable_image_params_prepare": False,
},
verbosity=0,
)
self.assertIn(mock.call('the_roles_file.yaml', '/tht-root/'),
mock_norm_path.call_args_list)
self.tripleoclient.object_store.put_object.assert_called_with(
'test-overcloud', 'roles_data.yaml', mock_open_context())
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_plan_from_templates_plan_env_data(self, mock_tmp, mock_cd,
mock_run_playbook):
mock_open_context = mock.mock_open()
with mock.patch('six.moves.builtins.open', mock_open_context):
plan_management.create_plan_from_templates(
self.app.client_manager,
'test-overcloud',
'/tht-root/',
plan_env_file='the-plan-environment.yaml')
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "test-overcloud",
"generate_passwords": True,
"plan_environment": "the-plan-environment.yaml",
"use_default_templates": False,
"disable_image_params_prepare": False,
},
verbosity=0,
)
mock_open_context.assert_has_calls(
[mock.call('the-plan-environment.yaml', 'rb')])
self.tripleoclient.object_store.put_object.assert_called_with(
'test-overcloud', 'plan-environment.yaml', mock_open_context())
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_plan_from_templates_networks_data(self, mock_tmp, mock_cd,
mock_run_playbook):
mock_open_context = mock.mock_open()
with mock.patch('six.moves.builtins.open', mock_open_context):
plan_management.create_plan_from_templates(
self.app.client_manager,
'test-overcloud',
'/tht-root/',
networks_file='the-network-data.yaml')
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "test-overcloud",
"generate_passwords": True,
"use_default_templates": False,
"disable_image_params_prepare": False,
},
verbosity=0,
)
mock_open_context.assert_has_calls(
[mock.call('the-network-data.yaml', 'rb')])
self.tripleoclient.object_store.put_object.assert_called_with(
'test-overcloud', 'network_data.yaml', mock_open_context())
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_create_plan_with_password_gen_disabled(self, mock_tmp, mock_cd,
mock_run_playbook):
plan_management.create_plan_from_templates(
self.app.client_manager,
'test-overcloud',
'/tht-root/',
generate_passwords=False,
disable_image_params_prepare=True)
mock_run_playbook.assert_called_with(
'cli-create-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "test-overcloud",
"generate_passwords": False,
"use_default_templates": False,
"disable_image_params_prepare": True,
},
verbosity=0,
)
class TestPlanUpdateWorkflows(base.TestCommand):
def setUp(self):
super(TestPlanUpdateWorkflows, self).setUp()
self.app.client_manager.tripleoclient = self.tripleoclient = \
mock.Mock()
self.tripleoclient.object_store = self.object_store = mock.Mock()
self.object_store.get_container.return_value = (
{},
[
{'name': 'plan-environment.yaml'},
{'name': 'user-environment.yaml'},
{'name': 'roles_data.yaml'},
{'name': 'network_data.yaml'},
{'name': 'user-files/somecustomfile.yaml'},
{'name': 'user-files/othercustomfile.yaml'},
{'name': 'this-should-not-be-persisted.yaml'},
]
)
def get_object(*args, **kwargs):
if args[0] != 'test-overcloud':
raise RuntimeError('Unexpected container')
if args[1] == 'plan-environment.yaml':
return {}, ('passwords: somepasswords\n'
'plan-environment.yaml: mock content\n')
# Generic return valuebased on param,
# e.g. 'plan-environment.yaml: mock content'
return {}, '{0}: mock content\n'.format(args[1])
self.object_store.get_object.side_effect = get_object
self.mock_tar = mock.patch(
'tripleo_common.utils.tarball.create_tarball',
autospec=True
)
self.mock_tar.start()
def tearDown(self):
super(TestPlanUpdateWorkflows, self).tearDown()
self.mock_tar.stop()
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('tripleo_common.utils.swift.empty_container',
autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_update_plan_from_templates_keep_env(
self, mock_tmp, mock_cd, mock_empty_container,
mock_run_playbook):
plan_management.update_plan_from_templates(
self.app.client_manager,
'test-overcloud',
'/tht-root/',
keep_env=True)
mock_empty_container.assert_called_once_with(
self.object_store, 'test-overcloud')
# make sure we're pushing the saved files back to plan
self.object_store.put_object.assert_has_calls(
[
mock.call('test-overcloud', 'plan-environment.yaml',
'passwords: somepasswords\n'
'plan-environment.yaml: mock content\n'),
mock.call('test-overcloud', 'user-environment.yaml',
'user-environment.yaml: mock content\n'),
mock.call('test-overcloud', 'roles_data.yaml',
'roles_data.yaml: mock content\n'),
mock.call('test-overcloud', 'network_data.yaml',
'network_data.yaml: mock content\n'),
mock.call('test-overcloud', 'user-files/somecustomfile.yaml',
'user-files/somecustomfile.yaml: mock content\n'),
mock.call('test-overcloud', 'user-files/othercustomfile.yaml',
'user-files/othercustomfile.yaml: mock content\n'),
],
any_order=True,
)
mock_run_playbook.assert_called_with(
'cli-update-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "test-overcloud",
"generate_passwords": True,
"disable_image_params_prepare": False,
},
verbosity=1,
)
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('tripleo_common.utils.swift.empty_container',
autospec=True)
def test_update_plan_from_templates_recreate_env(
self, mock_empty_container, mock_run_playbook):
plan_management.update_plan_from_templates(
self.app.client_manager,
'test-overcloud',
'/tht-root/')
mock_empty_container.assert_called_once_with(
self.object_store, 'test-overcloud')
# make sure passwords got persisted
self.object_store.put_object.assert_called_with(
'test-overcloud', 'plan-environment.yaml',
'passwords: somepasswords\n'
'plan-environment.yaml: mock content\n'
)
mock_run_playbook.assert_called_with(
'cli-update-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "test-overcloud",
"generate_passwords": True,
"disable_image_params_prepare": False,
},
verbosity=1,
)
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
@mock.patch('tripleoclient.workflows.plan_management._update_passwords',
autospec=True)
@mock.patch('yaml.safe_load',
autospec=True)
@mock.patch('tripleo_common.utils.swift.empty_container',
autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
def test_update_plan_from_templates_recreate_env_missing_passwords(
self, mock_tmp, mock_cd, mock_empty_container,
mock_yaml_safe_load, mock_update_passwords, mock_run_playbook):
plan_management.update_plan_from_templates(
self.app.client_manager,
'test-overcloud',
'/tht-root/',
disable_image_params_prepare=True)
# A dictionary without the "passwords" key is provided in
# the _load_passwords method.
mock_yaml_safe_load.return_value = {}
# Ensure that the passwords variable is passed with a value of None.
mock_update_passwords.assert_called_with(
mock.ANY, 'test-overcloud', None)
mock_run_playbook.assert_called_with(
'cli-update-deployment-plan.yaml',
'undercloud,',
mock.ANY,
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": "test-overcloud",
"generate_passwords": True,
"disable_image_params_prepare": True,
},
verbosity=1,
)
class TestUpdatePasswords(base.TestCase):
YAML_CONTENTS = """version: 1.0
name: overcloud
template: overcloud.yaml
parameter_defaults:
ControllerCount: 7
"""
def setUp(self):
super(TestUpdatePasswords, self).setUp()
self.swift_client = mock.MagicMock()
self.swift_client.get_object.return_value = ({}, self.YAML_CONTENTS)
self.plan_name = "overcast"
def test_update_passwords(self):
plan_management._update_passwords(self.swift_client,
self.plan_name,
{'AdminPassword': "1234"})
result = self.swift_client.put_object.call_args_list[0][0][2]
# Check new data is in
self.assertIn("passwords:\n", result)
self.assertIn("\n AdminPassword: '1234'", result)
# Check previous data still is too
self.assertIn("name: overcloud", result)
def test_no_passwords(self):
plan_management._update_passwords(self.swift_client,
self.plan_name,
[])
self.swift_client.put_object.assert_not_called()
def test_no_plan_environment(self):
self.swift_client.get_object.side_effect = (
Exception("404"))
plan_management._update_passwords(self.swift_client,
self.plan_name,
{'SecretPassword': 'abcd'})
self.swift_client.put_object.assert_not_called()

View File

@ -280,9 +280,6 @@ class DeployOvercloud(command.Command):
template_file=template_path)
files = dict(list(template_files.items()) + list(env_files.items()))
# Fix if required
# workflow_params.check_deprecated_parameters(self.clients, stack_name)
self.log.info("Deploying templates in the directory {0}".format(
os.path.abspath(tht_root)))
deployment.deploy_without_plan(
@ -1081,18 +1078,18 @@ class GetDeploymentStatus(command.Command):
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
plan = parsed_args.plan
stack = parsed_args.plan
status = deployment.get_deployment_status(
self.app.client_manager,
plan
stack
)
if not status:
self.log.info('No deployment was found for %s' % plan)
self.log.info('No deployment was found for %s' % stack)
return
table = PrettyTable(
['Plan Name', 'Deployment Status'])
table.add_row([plan, status])
['Stack Name', 'Deployment Status'])
table.add_row([stack, status])
print(table, file=self.app.stdout)

View File

@ -1,227 +0,0 @@
# 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.path
from osc_lib.i18n import _
from six.moves.urllib import request
from tripleoclient import command
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import utils
from tripleoclient.workflows import deployment
from tripleoclient.workflows import plan_management
class ListPlans(command.Lister):
"""List overcloud deployment plans."""
log = logging.getLogger(__name__ + ".ListPlans")
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager
plans = plan_management.list_deployment_plans(clients)
result = []
for r in plans:
result.append((r,))
return (("Plan Name",), result)
class DeletePlan(command.Command):
"""Delete an overcloud deployment plan.
The plan will not be deleted if a stack exists with the same name.
"""
log = logging.getLogger(__name__ + ".DeletePlan")
def get_parser(self, prog_name):
parser = super(DeletePlan, self).get_parser(prog_name)
parser.add_argument('plans', metavar='<name>', nargs="+",
help=_('Name of the plan(s) to delete'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager
for plan in parsed_args.plans:
print("Deleting plan %s..." % plan)
plan_management.delete_deployment_plan(clients,
container=plan)
class CreatePlan(command.Command):
"""Create a deployment plan"""
log = logging.getLogger(__name__ + ".CreatePlan")
def get_parser(self, prog_name):
parser = super(CreatePlan, self).get_parser(prog_name)
source_group = parser.add_mutually_exclusive_group()
parser.add_argument(
'name',
help=_('The name of the plan, which is used for the object '
'storage container, workflow environment and orchestration '
'stack names.'))
source_group.add_argument(
'--templates',
help=_('The directory containing the Heat templates to deploy. '
'If this or --source_url isn\'t provided, the templates '
'packaged on the Undercloud will be used.'),
)
parser.add_argument(
'--plan-environment-file', '-p',
help=_('Plan Environment file, overrides the default %s in the '
'--templates directory') % constants.PLAN_ENVIRONMENT
)
parser.add_argument(
'--disable-password-generation',
action='store_true',
default=False,
help=_('Disable password generation.')
)
source_group.add_argument(
'--source-url',
help=_('The url of a git repository containing the Heat templates '
'to deploy. If this or --templates isn\'t provided, the '
'templates packaged on the Undercloud will be used.')
)
parser.add_argument(
'--disable-container-prepare',
action='store_true',
default=False,
help=_('Disable the container preparation actions to prevent '
'container tags from being updated and new containers '
'from being fetched. If you skip this but do not have '
'the container parameters configured, the deployment '
'action may fail.')
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager
name = parsed_args.name
use_default_templates = False
generate_passwords = not parsed_args.disable_password_generation
source_url = parsed_args.source_url
# if the templates and source_url params are not used, then
# use the default templates
if not parsed_args.templates and not parsed_args.source_url:
use_default_templates = True
# Needs this to avoid too long lines
disable_container_prepare = parsed_args.disable_container_prepare
if parsed_args.templates:
plan_management.create_plan_from_templates(
clients, name, parsed_args.templates,
generate_passwords=generate_passwords,
plan_env_file=parsed_args.plan_environment_file,
verbosity_level=utils.playbook_verbosity(self=self),
disable_image_params_prepare=disable_container_prepare
)
else:
plan_management.create_deployment_plan(
container=name,
generate_passwords=generate_passwords,
source_url=source_url,
use_default_templates=use_default_templates,
verbosity_level=utils.playbook_verbosity(self=self),
disable_image_params_prepare=disable_container_prepare
)
class DeployPlan(command.Command):
"""Deploy a deployment plan"""
log = logging.getLogger(__name__ + ".DeployPlan")
def get_parser(self, prog_name):
parser = super(DeployPlan, self).get_parser(prog_name)
parser.add_argument('name', help=_('The name of the plan to deploy.'))
parser.add_argument('--timeout', '-t', metavar='<TIMEOUT>',
type=int, default=240,
help=_('Deployment timeout in minutes.'))
parser.add_argument('--run-validations', action='store_true',
default=False,
help=_('Run the pre-deployment validations. These '
'external validations are from the TripleO '
'Validations project.'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
clients = self.app.client_manager
orchestration_client = clients.orchestration
stack = utils.get_stack(orchestration_client, parsed_args.name)
print("Starting to deploy plan: {}".format(parsed_args.name))
deployment.deploy_and_wait(
log=self.log,
clients=clients,
stack=stack,
plan_name=parsed_args.name,
verbose_level=utils.playbook_verbosity(self=self),
timeout=parsed_args.timeout,
run_validations=parsed_args.run_validations,
)
class ExportPlan(command.Command):
"""Export a deployment plan"""
log = logging.getLogger(__name__ + ".ExportPlan")
def get_parser(self, prog_name):
parser = super(ExportPlan, self).get_parser(prog_name)
parser.add_argument('plan', metavar='<name>',
help=_('Name of the plan to export.'))
parser.add_argument('--output-file', '-o', metavar='<output file>',
help=_('Name of the output file for export. '
'It will default to "<name>.tar.gz".'))
parser.add_argument('--force-overwrite', '-f', action='store_true',
default=False,
help=_('Overwrite output file if it exists.'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
plan = parsed_args.plan
outfile = parsed_args.output_file or '%s.tar.gz' % plan
if os.path.exists(outfile) and not parsed_args.force_overwrite:
raise exceptions.PlanExportError(
"File '%s' already exists, not exporting." % outfile)
print("Exporting plan %s..." % plan)
tempurl = plan_management.export_deployment_plan(
self.app.client_manager, plan
)
f = request.urlopen(tempurl)
tarball_contents = f.read()
f.close()
with open(outfile, 'wb') as f:
f.write(tarball_contents)

View File

@ -1,158 +0,0 @@
# 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
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib.i18n import _
from tripleoclient.workflows import roles
class ListRoles(command.Lister):
"""List the current and available roles in a given plan"""
log = logging.getLogger(__name__ + ".ListRoles")
def get_parser(self, prog_name):
parser = super(ListRoles, self).get_parser(prog_name)
parser.add_argument(
'--name',
dest='name',
default='overcloud',
help=_('The name of the plan, which is used for the object '
'storage container, workflow environment and orchestration '
'stack names.'),
)
parser.add_argument(
'--detail',
action='store_true',
help=_('Include details about each role'))
parser.add_argument(
'--current',
action='store_true',
help=_('Only show the information for the roles currently enabled '
'for the plan.'))
return parser
def take_action(self, parsed_args):
self.log.debug('take_action({})'.format(parsed_args))
if parsed_args.current:
result = roles.list_roles(
self.app.client_manager,
container=parsed_args.name,
detail=parsed_args.detail)
else:
result = roles.list_available_roles(
self.app.client_manager,
container=parsed_args.name)
# The workflow returns all the details by default, trim
# them down if not required.
if not parsed_args.detail:
result = [r['name'] for r in result]
if parsed_args.detail:
if result:
result.sort(key=lambda r: r['name'])
role_list = self.format_role_details(result)
column_names = ("Role Name",
"Description",
"Services Default",
"Other Details")
return (column_names, role_list)
else:
if result:
result.sort()
return (("Role Name",), [(r,) for r in result])
def format_role_details(self, result):
role_list = []
for r in result:
name = r.pop('name')
description = service_defaults = ''
detail = []
if 'description' in r:
description = r.pop('description')
if 'ServicesDefault' in r:
r['ServicesDefault'].sort()
service_defaults = '\n'.join(r.pop('ServicesDefault'))
for k, v in r.items():
detail.append("%s: %s" % (k, v))
role_list.append((name, description, service_defaults,
'\n'.join(detail)))
return role_list
class ShowRole(command.ShowOne):
"""Show details for a specific role, given a plan"""
log = logging.getLogger(__name__ + ".ShowRole")
def get_parser(self, prog_name):
parser = super(ShowRole, self).get_parser(prog_name)
parser.add_argument(
'--name',
dest='name',
default='overcloud',
help=_('The name of the plan, which is used for the object '
'storage container, workflow environment and orchestration '
'stack names.'),
)
parser.add_argument('role',
metavar="<role>",
help=_('Name of the role to look up.'))
return parser
def take_action(self, parsed_args):
self.log.debug('take_action({})'.format(parsed_args))
role = self.get_role_details(parsed_args.name, parsed_args.role)
if not role:
raise exceptions.CommandError(
"Could not find role %s" % parsed_args.role)
return self.format_role(role)
def get_role_details(self, name, role_name):
result = roles.list_available_roles(
self.app.client_manager,
container=name)
for r in result:
if r['name'] == role_name:
return r
return []
def format_role(self, role):
column_names = ['Name']
data = [role.pop('name')]
if 'description' in role:
column_names.append('Description')
data.append(role.pop('description'))
if 'ServicesDefault' in role:
column_names.append('Services Default')
role['ServicesDefault'].sort()
data.append('\n'.join(role.pop('ServicesDefault')))
other_fields = list(role.keys())
other_fields.sort()
for field in other_fields:
column_names.append(field)
data.append(role[field])
return column_names, data

View File

@ -30,96 +30,6 @@ from tripleoclient import utils
_WORKFLOW_TIMEOUT = 360 # 6 * 60 seconds
def deploy(container, run_validations, skip_deploy_identifier,
timeout, verbosity=0):
"""Run the deployment playbook.
:param container: Name of the container
:type container: String
:param run_validations: Enable or disable validations
:type run_validations: Boolean
:param skip_deploy_identifier: Enable or disable validations
:type skip_deploy_identifier: Boolean
:param timeout: Deployment timeout (minutes).
:type timeout: Integer
:param verbosity: Verbosity level
:type verbosity: Integer
"""
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
"cli-deploy-deployment-plan.yaml",
'undercloud,',
workdir=tmp,
playbook_dir=ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
timeout=timeout,
extra_vars={
"container": container,
"run_validations": run_validations,
"skip_deploy_identifier": skip_deploy_identifier,
}
)
print("Success.")
def deploy_and_wait(log, clients, stack, plan_name, verbose_level,
timeout=None, run_validations=False,
skip_deploy_identifier=False, deployment_options={}):
"""Start the deploy and wait for it to finish"""
orchestration_client = clients.orchestration
if stack is None:
log.info("Performing Heat stack create")
action = 'CREATE'
marker = None
else:
log.info("Performing Heat stack update")
# Make sure existing parameters for stack are reused
# Find the last top-level event to use for the first marker
events = event_utils.get_events(orchestration_client,
stack_id=plan_name,
event_args={'sort_dir': 'desc',
'limit': 1})
marker = events[0].id if events else None
action = 'UPDATE'
set_deployment_status(plan_name,
status='DEPLOYING')
try:
deploy(
container=plan_name,
run_validations=run_validations,
skip_deploy_identifier=skip_deploy_identifier,
timeout=timeout,
verbosity=verbose_level)
except Exception:
set_deployment_status(plan_name,
status='DEPLOY_FAILED')
raise
# TODO(rabi) Simplify call to get events as we don't need to wait
# for stack to be ready anymore i.e just get the events.
create_result = utils.wait_for_stack_ready(
orchestration_client, plan_name, marker, action)
if not create_result:
shell.OpenStackShell().run(["stack", "failures", "list", plan_name])
set_deployment_status(
plan_name,
status='DEPLOY_FAILED'
)
if stack is None:
raise exceptions.DeploymentError("Heat Stack create failed.")
else:
raise exceptions.DeploymentError("Heat Stack update failed.")
def create_overcloudrc(stack, rc_params, no_proxy='',
output_dir=CLOUD_HOME_DIR):
overcloudrcs = rc_utils._create_overcloudrc(

View File

@ -12,7 +12,6 @@
import logging
import os
import re
import yaml
from heatclient.common import template_utils
@ -20,7 +19,6 @@ from tripleo_common.utils import stack_parameters as stk_parameters
from tripleoclient.constants import ANSIBLE_TRIPLEO_PLAYBOOKS
from tripleoclient.constants import OVERCLOUD_YAML_NAME
from tripleoclient.constants import UNUSED_PARAMETER_EXCLUDES_RE
from tripleoclient import exceptions
from tripleoclient import utils
from tripleoclient.workflows import roles
@ -111,118 +109,6 @@ def build_derived_params_environment(clients, stack_name,
)
def check_deprecated_parameters(clients, container):
"""Checks for deprecated parameters in plan and adds warning if present.
:param clients: application client object.
:type clients: Object
:param container: Name of the stack container.
:type container: String
"""
role_name_list = roles.list_available_roles(
clients=clients,
container=container
)
flattened_parms = stk_parameters.get_flattened_parameters(
clients.tripleoclient.object_store,
clients.orchestration, container=container)
user_params = flattened_parms.get('environment_parameters', {})
heat_resource_tree = flattened_parms.get('heat_resource_tree', {})
heat_resource_tree_params = heat_resource_tree.get('parameters', {})
heat_resource_tree_resources = heat_resource_tree.get('resources', {})
plan_params = heat_resource_tree_params.keys()
parameter_groups = [
i.get('parameter_groups')
for i in heat_resource_tree_resources.values()
if i.get('parameter_groups')
]
params_role_specific_tag = [
i.get('name')
for i in heat_resource_tree_params.values()
if 'tags' in i and 'role_specific' in i['tags']
]
r = re.compile(".*Count")
filtered_names = list(filter(r.match, plan_params))
valid_role_name_list = list()
for name in filtered_names:
default = heat_resource_tree_params[name].get('default', 0)
if default and int(default) > 0:
role_name = name.rstrip('Count')
if [i for i in role_name_list if i.get('name') == role_name]:
valid_role_name_list.append(role_name)
deprecated_params = [
i[0] for i in parameter_groups
if i[0].get('label') == 'deprecated'
]
# We are setting a frozenset here because python 3 complains that dict is
# a unhashable type.
# On user_defined, we check if the size is higher than 0 because an empty
# frozenset still is a subset of a frozenset, so we can't use issubset
# here.
user_params_keys = frozenset(user_params.keys())
deprecated_result = [
{
'parameter': i,
'deprecated': True,
'user_defined': len(
[x for x in frozenset(i) if x in user_params_keys]) > 0
}
for i in deprecated_params
]
unused_params = [i for i in user_params.keys() if i not in plan_params]
user_provided_role_specific = [
v for i in role_name_list
for k, v in user_params.items()
if k in i
]
invalid_role_specific_params = [
i for i in user_provided_role_specific
if i in params_role_specific_tag
]
deprecated_parameters = [
param['parameter'] for param in deprecated_result
if param.get('user_defined')
]
if deprecated_parameters:
deprecated_join = ', '.join(deprecated_parameters)
LOG.warning(
'WARNING: Following parameter(s) are deprecated and still '
'defined. Deprecated parameters will be removed soon!'
' {deprecated_join}'.format(
deprecated_join=deprecated_join
)
)
# exclude our known params that may not be used
ignore_re = re.compile('|'.join(UNUSED_PARAMETER_EXCLUDES_RE))
unused_params = [p for p in unused_params if not ignore_re.search(p)]
if unused_params:
unused_join = ', '.join(unused_params)
LOG.warning(
'WARNING: Following parameter(s) are defined but not '
'currently used in the deployment plan. These parameters '
'may be valid but not in use due to the service or '
'deployment configuration.'
' {unused_join}'.format(
unused_join=unused_join
)
)
if invalid_role_specific_params:
invalid_join = ', '.join(invalid_role_specific_params)
LOG.warning(
'WARNING: Following parameter(s) are not supported as '
'role-specific inputs. {invalid_join}'.format(
invalid_join=invalid_join
)
)
def generate_fencing_parameters(clients, nodes_json, delay, ipmi_level,
ipmi_cipher, ipmi_lanplus):
"""Generate and return fencing parameters.

View File

@ -1,412 +0,0 @@
# 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 tripleo_common.actions import plan
from tripleo_common.utils import plan as plan_utils
from tripleo_common.utils import swift as swiftutils
from tripleo_common.utils import template as temputils
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import utils
LOG = logging.getLogger(__name__)
# Plan management workflows should generally be quick. However, the creation
# of the default plan in instack has demonstrated that sometimes it can take
# several minutes. The previous timeout of 6 minutes from Instack does not
# seem to be sufficient anymore. Bumping this to 20 minutes. It doesn't mean
# that it will take 20 minutes, but just that the listen for completion will
# timeout after 20 minutes. If it takes longer than that, something is really
# wrong.
_WORKFLOW_TIMEOUT = 20 * 60 # 20 minutes * 60 seconds
def _upload_templates(swift_client, container_name, tht_root, roles_file=None,
plan_env_file=None, networks_file=None):
"""tarball up a given directory and upload it to Swift to be extracted"""
temputils.upload_templates_as_tarball(
swift=swift_client,
dir_to_upload=tht_root,
container=container_name
)
# Optional override of the roles_data.yaml file
if roles_file:
_upload_file(swift_client, container_name,
constants.OVERCLOUD_ROLES_FILE,
utils.rel_or_abs_path(roles_file, tht_root))
# Optional override of the network_data.yaml file
if networks_file:
_upload_file(swift_client, container_name,
constants.OVERCLOUD_NETWORKS_FILE, networks_file)
# Optional override of the plan-environment.yaml file
if plan_env_file:
# TODO(jpalanis): Instead of overriding default file,
# merging the user override plan-environment with default
# plan-environment file will avoid explict merging issues.
_upload_file(swift_client, container_name,
constants.PLAN_ENVIRONMENT, plan_env_file)
def create_deployment_plan(container, generate_passwords,
use_default_templates=False, source_url=None,
verbosity_level=0, plan_env_file=None,
disable_image_params_prepare=False):
"""Create a deployment plan.
:param container: Container name to push to.
:type container: String
:param generate_passwords: Whether to generate password
:type generate_passwords: Boolean
:param use_default_templates: Whether to use default template files
:type use_default_templates: Boolean
:param source_url: The url of a git repository containing the Heat
templates to deploy
:type source_url: String
:param verbosity_level: Verbosity level used in playbook execution
:type verbosity_level: Integer
:param plan_env_file: Path to plan environment file
:type plan_env_file: String
:param disable_image_params_prepare: Disable container params prepare task
:type disable_image_params_prepare: Boolean
"""
extra_vars = {
"container": container,
"generate_passwords": generate_passwords,
"use_default_templates": use_default_templates,
"disable_image_params_prepare": disable_image_params_prepare
}
if source_url:
extra_vars['source_url'] = source_url
if plan_env_file:
extra_vars['plan_environment'] = plan_env_file
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
"cli-create-deployment-plan.yaml",
'undercloud,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars=extra_vars,
verbosity=verbosity_level
)
print("Success.")
def delete_deployment_plan(clients, container):
"""Delete a deployment plan.
:param clients: Application client object.
:type clients: Object
:param container: Container name to pull from.
:type container: String
"""
context = clients.tripleoclient.create_mistral_context()
result = plan.DeletePlanAction(container=container).run(context=context)
# The action returns None if there are no errors.
if result:
raise RuntimeError(result)
def update_deployment_plan(clients, container, generate_passwords,
verbosity_level=0,
disable_image_params_prepare=False):
"""Update a deployment plan.
:param clients: Application client object.
:type clients: Object
:param container: Container name to pull from.
:type container: String
:param generate_passwords: Whether to generate password
:type generate_passwords: Boolean
:param verbosity_level: Verbosity level used in playbook execution
:type verbosity_level: Integer
:param disable_image_params_prepare: Disable container params prepare task
:type disable_image_params_prepare: Boolean
"""
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
"cli-update-deployment-plan.yaml",
'undercloud,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"container": container,
"generate_passwords": generate_passwords,
"disable_image_params_prepare": disable_image_params_prepare
},
verbosity=verbosity_level
)
print("Success.")
def list_deployment_plans(clients):
mistral_context = clients.tripleoclient.create_mistral_context()
return plan.ListPlansAction().run(mistral_context)
def create_plan_from_templates(clients, name, tht_root, roles_file=None,
generate_passwords=True, plan_env_file=None,
networks_file=None, verbosity_level=0,
disable_image_params_prepare=False):
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
"cli-undercloud-local-artifacts.yaml",
'undercloud,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"stack": name,
},
verbosity=verbosity_level
)
swift_client = clients.tripleoclient.object_store
print("Creating Swift container to store the plan")
plan_utils.create_plan_container(swift_client, name)
print("Creating plan from template files in: {}".format(tht_root))
_upload_templates(swift_client, name, tht_root,
utils.rel_or_abs_path(roles_file, tht_root),
plan_env_file, networks_file)
try:
create_deployment_plan(
container=name,
generate_passwords=generate_passwords,
plan_env_file=plan_env_file,
verbosity_level=verbosity_level,
disable_image_params_prepare=disable_image_params_prepare)
except exceptions.WorkflowServiceError:
swiftutils.delete_container(swift_client, name)
raise
def update_plan_from_templates(clients, name, tht_root, roles_file=None,
generate_passwords=True, plan_env_file=None,
networks_file=None, keep_env=False,
verbosity_level=1,
disable_image_params_prepare=False):
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
"cli-undercloud-local-artifacts.yaml",
'undercloud,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
extra_vars={
"stack": name,
},
verbosity=verbosity_level
)
swift_client = clients.tripleoclient.object_store
passwords = None
keep_file_contents = {}
roles_file = utils.rel_or_abs_path(roles_file, tht_root)
if keep_env:
# Dict items are (remote_name, local_name). local_name may be
# None in which case we only try to load from Swift (remote).
keep_map = {
constants.PLAN_ENVIRONMENT: plan_env_file,
constants.USER_ENVIRONMENT: None,
constants.OVERCLOUD_ROLES_FILE: roles_file,
constants.OVERCLOUD_NETWORKS_FILE: networks_file,
}
# Also try to fetch any files under 'user-files/'
# dir. local_name is always None for these
keep_map.update(dict(map(
lambda path: (path, None),
_list_user_files(swift_client, name))))
keep_file_contents = _load_content_or_file(
swift_client, name, keep_map)
else:
passwords = _load_passwords(swift_client, name)
# TODO(dmatthews): Removing the existing plan files should probably be
# a Mistral action.
print("Removing the current plan files")
swiftutils.empty_container(swift_client, name)
# Until we have a well defined plan update workflow in
# tripleo-common we need to manually reset the environments and
# parameter_defaults here. This is to ensure that no environments
# are in the plan environment but not actually in swift.
# See bug: https://bugs.launchpad.net/tripleo/+bug/1623431
#
# Currently this is being done incidentally because we overwrite
# the existing plan-environment.yaml with the skeleton one in THT
# when updating the templates. Once LP#1623431 is resolved we may
# need to special-case plan-environment.yaml to avoid this.
print("Uploading new plan files")
if keep_env:
_upload_templates(swift_client, name, tht_root)
for filename in keep_file_contents:
_upload_file_content(swift_client, name, filename,
keep_file_contents[filename])
else:
_upload_templates(swift_client, name, tht_root, roles_file,
plan_env_file, networks_file)
_update_passwords(swift_client, name, passwords)
update_deployment_plan(
clients, container=name,
generate_passwords=generate_passwords,
verbosity_level=verbosity_level,
disable_image_params_prepare=disable_image_params_prepare)
def _load_content_or_file(swift_client, container, remote_and_local_map):
# mapping (remote_name, content)
file_contents = {}
plan_files = _list_plan_files(swift_client, container)
for remote_name in remote_and_local_map:
LOG.debug("Attempting to load {0}".format(remote_name))
local_name = remote_and_local_map[remote_name]
# it's possible that the file doesn't exist in Swift and isn't
# passed on filesystem, in which case we won't do anything
content = None
# local override takes priority
if local_name:
LOG.debug("Using provided file {0}".format(local_name))
with open(os.path.abspath(local_name)) as local_content:
content = local_content.read()
elif remote_name in plan_files:
LOG.debug("Preserving plan file {0}".format(remote_name))
content = swift_client.get_object(container, remote_name)[1]
if content:
file_contents[remote_name] = content
return file_contents
def _list_user_files(swift_client, container):
return list(filter(lambda path: path.startswith('user-files/'),
_list_plan_files(swift_client, container)))
def _list_plan_files(swift_client, container):
return list(map(lambda i: i['name'],
swift_client.get_container(
container, full_listing=True)[1]))
def _upload_file(swift_client, container, filename, local_filename):
with open(local_filename, 'rb') as file_content:
swiftutils.put_object_string(
swift=swift_client,
container=container,
object_name=filename,
contents=file_content
)
# short function, just alias for interface parity with _upload_plan_file
def _upload_file_content(swift_client, container, filename, content):
LOG.debug("Uploading {0} to plan".format(filename))
swiftutils.put_object_string(
swift=swift_client,
container=container,
object_name=filename,
contents=content
)
def _load_passwords(swift_client, name):
plan_env = yaml.safe_load(swift_client.get_object(
name, constants.PLAN_ENVIRONMENT)[1])
if "passwords" in plan_env:
return plan_env['passwords']
else:
LOG.warning("No passwords found in existing plan {}. "
"Updating plan with passwords.".format(name))
def _update_passwords(swift_client, name, passwords):
# Update the plan environment with the generated passwords. This
# will be solved more elegantly once passwords are saved in a
# separate environment (https://review.opendev.org/#/c/467909/)
if passwords:
try:
env = yaml.safe_load(swift_client.get_object(
name, constants.PLAN_ENVIRONMENT)[1])
env['passwords'] = passwords
swiftutils.put_object_string(
swift=swift_client,
container=name,
object_name=constants.PLAN_ENVIRONMENT,
contents=yaml.safe_dump(env, default_flow_style=False)
)
except Exception as exp:
# The plan likely has not been migrated to using Swift yet.
LOG.debug("Could not find plan environment %s in %s - error: %s",
constants.PLAN_ENVIRONMENT, name, exp)
def export_deployment_plan(clients, plan_name):
plan_path = os.path.join('/var/lib/tripleo/stacks', plan_name)
if os.path.exists(plan_path):
print("Plan information can be found here: {}".format(plan_path))
return plan_path
else:
# NOTE(cloudnull): When swift is no longer in service remove this.
export_container = "plan-exports"
delete_after = 3600
mistral_context = clients.tripleoclient.create_mistral_context()
action = plan.ExportPlanAction(plan_name, delete_after=delete_after,
exports_container=export_container)
result = action.run(mistral_context)
if result:
raise exceptions.WorkflowServiceError(
'Exception exporting plan: {}'.format(result.error))
url = swiftutils.get_temp_url(
clients.tripleoclient.object_store,
container=export_container,
object_name="{}.tar.gz".format(plan_name)
)
print(url)
return url

View File

@ -14,10 +14,6 @@ import logging
import yaml
from tripleo_common.actions import plan
# TODO(cloudnull): Convert to a swiftutils in tripleo-common
# from tripleo_common.utils import swift as swiftutils
from tripleoclient import utils
LOG = logging.getLogger(__name__)
@ -60,54 +56,3 @@ def get_roles(clients, roles_file, tht_root,
valid_roles.append(name)
return valid_roles
def list_available_roles(clients, container='overcloud'):
"""Return a list of available roles.
:param clients: openstack clients
:type clients: Object
:param container: Name of swift object container
:type container: String
:returns: List
"""
LOG.info('Pulling role list from: {}'.format(container))
obj_client = clients.tripleoclient.object_store
available_yaml_roles = list()
LOG.info('Indexing roles from: {}'.format(container))
# TODO(cloudnull): Convert to a swiftutils in tripleo-common
for obj in obj_client.get_container(container)[-1]:
name = obj['name']
if name.startswith('roles/') and name.endswith(('yml', 'yaml')):
role_data = yaml.safe_load(
obj_client.get_object(container, name)[-1]
)
available_yaml_roles.append(role_data[0])
return available_yaml_roles
def list_roles(clients, container, detail=False):
"""Return a list of roles.
:param clients: openstack clients
:type clients: Object
:param container: Name of swift object container
:type container: String
:param detail: Enable or disable extra detail
:type detail: Boolean
:returns: List
"""
context = clients.tripleoclient.create_mistral_context()
LOG.info('Pulling roles from: {}'.format(container))
return plan.ListRolesAction(
container=container,
detail=detail
).run(context=context)