Convert enable-ssh-admin.sh to python
Instead of using the script from the templates, use python for the enable-ssh-admin logic. This will allow for more properly handling failures. This also fixes a race condition where sshd has not already started on some of the nodes before we try and connect via ssh. A timeout is added where we wait for the port to come up. If the timeout has passed and the port is still not up, then an exception is raised. Change-Id: I3431d2ec724a880baf0de8f586490d145bedf870 Closes-Bug: #1769230
This commit is contained in:
parent
dba64b6b70
commit
6cc67c3398
@ -63,3 +63,7 @@ UPGRADE_CONVERGE_ENV = "environments/lifecycle/upgrade-converge.yaml"
|
|||||||
FFWD_UPGRADE_PREPARE_ENV = "environments/lifecycle/ffwd-upgrade-prepare.yaml"
|
FFWD_UPGRADE_PREPARE_ENV = "environments/lifecycle/ffwd-upgrade-prepare.yaml"
|
||||||
FFWD_UPGRADE_CONVERGE_ENV = "environments/lifecycle/ffwd-upgrade-converge.yaml"
|
FFWD_UPGRADE_CONVERGE_ENV = "environments/lifecycle/ffwd-upgrade-converge.yaml"
|
||||||
CEPH_UPGRADE_PREPARE_ENV = "environments/lifecycle/ceph-upgrade-prepare.yaml"
|
CEPH_UPGRADE_PREPARE_ENV = "environments/lifecycle/ceph-upgrade-prepare.yaml"
|
||||||
|
|
||||||
|
ENABLE_SSH_ADMIN_TIMEOUT = 300
|
||||||
|
ENABLE_SSH_ADMIN_STATUS_INTERVAL = 5
|
||||||
|
ENABLE_SSH_ADMIN_SSH_PORT_TIMEOUT = 300
|
||||||
|
@ -70,6 +70,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
self.cmd._download_missing_files_from_plan = self.real_download_missing
|
self.cmd._download_missing_files_from_plan = self.real_download_missing
|
||||||
shutil.rmtree = self.real_shutil
|
shutil.rmtree = self.real_shutil
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -109,7 +111,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_events, mock_tarball,
|
mock_events, mock_tarball,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_list_plans,
|
mock_list_plans,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
arglist = ['--templates', '--ceph-storage-scale', '3']
|
arglist = ['--templates', '--ceph-storage-scale', '3']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
@ -182,6 +186,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
|
|
||||||
mock_create_tempest_deployer_input.assert_called_with()
|
mock_create_tempest_deployer_input.assert_called_with()
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -227,7 +233,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_invoke_plan_env_wf,
|
mock_invoke_plan_env_wf,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_list_plans,
|
mock_list_plans,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
arglist = ['--templates', '--ceph-storage-scale', '3',
|
arglist = ['--templates', '--ceph-storage-scale', '3',
|
||||||
'--control-flavor', 'oooq_control', '--no-cleanup']
|
'--control-flavor', 'oooq_control', '--no-cleanup']
|
||||||
@ -324,6 +332,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
self.assertEqual(env_map.get('parameter_defaults'),
|
self.assertEqual(env_map.get('parameter_defaults'),
|
||||||
parameters_env.get('parameter_defaults'))
|
parameters_env.get('parameter_defaults'))
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -360,16 +370,19 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
@mock.patch('shutil.copytree', autospec=True)
|
@mock.patch('shutil.copytree', autospec=True)
|
||||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||||
def test_tht_deploy_with_plan_environment_file(
|
def test_tht_deploy_with_plan_environment_file(
|
||||||
self, mock_tmpdir, mock_copy, mock_time, mock_sleep, mock_uuid1,
|
self, mock_tmpdir, mock_copy, mock_time, mock_sleep, mock_uuid1,
|
||||||
mock_get_template_contents, wait_for_stack_ready_mock,
|
mock_get_template_contents, wait_for_stack_ready_mock,
|
||||||
mock_remove_known_hosts, mock_overcloudrc, mock_write_overcloudrc,
|
mock_remove_known_hosts, mock_overcloudrc, mock_write_overcloudrc,
|
||||||
mock_create_tempest_deployer, mock_create_parameters_env,
|
mock_create_tempest_deployer, mock_create_parameters_env,
|
||||||
mock_validate_args,
|
mock_validate_args,
|
||||||
mock_breakpoints_cleanup,
|
mock_breakpoints_cleanup,
|
||||||
mock_tarball, mock_postconfig,
|
mock_tarball, mock_postconfig,
|
||||||
mock_get_overcloud_endpoint, mock_shutil_rmtree,
|
mock_get_overcloud_endpoint, mock_shutil_rmtree,
|
||||||
mock_invoke_plan_env_wf, mock_get_horizon_url,
|
mock_invoke_plan_env_wf, mock_get_horizon_url,
|
||||||
mock_list_plans, mock_config_download):
|
mock_list_plans, mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
arglist = ['--templates', '-p', 'the-plan-environment.yaml']
|
arglist = ['--templates', '-p', 'the-plan-environment.yaml']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('templates', '/usr/share/openstack-tripleo-heat-templates/'),
|
('templates', '/usr/share/openstack-tripleo-heat-templates/'),
|
||||||
@ -468,6 +481,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
clients.tripleoclient.object_store.put_object.assert_called()
|
clients.tripleoclient.object_store.put_object.assert_called()
|
||||||
self.assertTrue(mock_invoke_plan_env_wf.called)
|
self.assertTrue(mock_invoke_plan_env_wf.called)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -514,7 +529,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_breakpoints_cleanup, mock_tarball,
|
mock_breakpoints_cleanup, mock_tarball,
|
||||||
mock_postconfig, mock_get_overcloud_endpoint,
|
mock_postconfig, mock_get_overcloud_endpoint,
|
||||||
mock_deprecated_params, mock_get_horizon_url,
|
mock_deprecated_params, mock_get_horizon_url,
|
||||||
mock_list_plans, mock_config_downlad):
|
mock_list_plans, mock_config_downlad,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
arglist = ['--templates', '--skip-deploy-identifier']
|
arglist = ['--templates', '--skip-deploy-identifier']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
@ -574,6 +591,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
deploy_plan_call_input = deploy_plan_call[1]['workflow_input']
|
deploy_plan_call_input = deploy_plan_call[1]['workflow_input']
|
||||||
self.assertTrue(deploy_plan_call_input['skip_deploy_identifier'])
|
self.assertTrue(deploy_plan_call_input['skip_deploy_identifier'])
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -608,7 +627,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_events, mock_tarball,
|
mock_events, mock_tarball,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_list_plans,
|
mock_list_plans,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
arglist = ['--templates', '/home/stack/tripleo-heat-templates']
|
arglist = ['--templates', '/home/stack/tripleo-heat-templates']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
@ -693,6 +714,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
self.cmd.take_action, parsed_args)
|
self.cmd.take_action, parsed_args)
|
||||||
self.assertFalse(mock_deploy_tht.called)
|
self.assertFalse(mock_deploy_tht.called)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -717,7 +740,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_utils_endpoint, mock_utils_createrc,
|
mock_utils_endpoint, mock_utils_createrc,
|
||||||
mock_utils_tempest, mock_tarball,
|
mock_utils_tempest, mock_tarball,
|
||||||
mock_get_horizon_url, mock_list_plans,
|
mock_get_horizon_url, mock_list_plans,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
clients = self.app.client_manager
|
clients = self.app.client_manager
|
||||||
mock_list_plans.return_value = []
|
mock_list_plans.return_value = []
|
||||||
@ -937,6 +962,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
parsed_args)
|
parsed_args)
|
||||||
self.assertIn('/tmp/notthere', str(error))
|
self.assertIn('/tmp/notthere', str(error))
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@ -952,7 +979,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_create_ocrc,
|
mock_create_ocrc,
|
||||||
mock_create_tempest_deployer_input,
|
mock_create_tempest_deployer_input,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
clients = self.app.client_manager
|
clients = self.app.client_manager
|
||||||
workflow_client = clients.workflow_engine
|
workflow_client = clients.workflow_engine
|
||||||
@ -979,6 +1008,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
|
|
||||||
mock_create_tempest_deployer_input.assert_called_with()
|
mock_create_tempest_deployer_input.assert_called_with()
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -1019,7 +1050,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_events, mock_tarball,
|
mock_events, mock_tarball,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_list_plans,
|
mock_list_plans,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
arglist = ['--templates', '--rhel-reg',
|
arglist = ['--templates', '--rhel-reg',
|
||||||
'--reg-sat-url', 'https://example.com',
|
'--reg-sat-url', 'https://example.com',
|
||||||
@ -1230,6 +1263,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
self.assertFalse(mock_create_ocrc.called)
|
self.assertFalse(mock_create_ocrc.called)
|
||||||
self.assertFalse(mock_create_tempest_deployer_input.called)
|
self.assertFalse(mock_create_tempest_deployer_input.called)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -1256,7 +1291,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_create_tempest_deployer_input,
|
mock_create_tempest_deployer_input,
|
||||||
mock_tarball, mock_get_horizon_url,
|
mock_tarball, mock_get_horizon_url,
|
||||||
mock_list_plans,
|
mock_list_plans,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
clients = self.app.client_manager
|
clients = self.app.client_manager
|
||||||
|
|
||||||
mock_list_plans.return_value = []
|
mock_list_plans.return_value = []
|
||||||
@ -1420,6 +1457,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
self.cmd.take_action,
|
self.cmd.take_action,
|
||||||
parsed_args)
|
parsed_args)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
@ -1464,7 +1503,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_deploy_post_config,
|
mock_deploy_post_config,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_list_plans,
|
mock_list_plans,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
|
|
||||||
arglist = ['--templates', '--ceph-storage-scale', '3',
|
arglist = ['--templates', '--ceph-storage-scale', '3',
|
||||||
'--control-scale', '3', '--ntp-server', 'ntp']
|
'--control-scale', '3', '--ntp-server', 'ntp']
|
||||||
@ -1616,6 +1657,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
self.assertRaises(exceptions.StackInProgress,
|
self.assertRaises(exceptions.StackInProgress,
|
||||||
self.cmd.take_action, parsed_args)
|
self.cmd.take_action, parsed_args)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@ -1633,7 +1676,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_overcloud_endpoint,
|
mock_overcloud_endpoint,
|
||||||
mock_create_tempest_deployer_input,
|
mock_create_tempest_deployer_input,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
clients = self.app.client_manager
|
clients = self.app.client_manager
|
||||||
orchestration_client = clients.orchestration
|
orchestration_client = clients.orchestration
|
||||||
orchestration_client.stacks.get.return_value = mock.Mock()
|
orchestration_client.stacks.get.return_value = mock.Mock()
|
||||||
@ -1648,6 +1693,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
self.assertNotCalled(self.cmd._predeploy_verify_capabilities)
|
self.assertNotCalled(self.cmd._predeploy_verify_capabilities)
|
||||||
mock_create_tempest_deployer_input.assert_called_with()
|
mock_create_tempest_deployer_input.assert_called_with()
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@ -1665,7 +1712,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_overcloud_endpoint,
|
mock_overcloud_endpoint,
|
||||||
mock_create_tempest_deployer_input,
|
mock_create_tempest_deployer_input,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
clients = self.app.client_manager
|
clients = self.app.client_manager
|
||||||
orchestration_client = clients.orchestration
|
orchestration_client = clients.orchestration
|
||||||
orchestration_client.stacks.get.return_value = mock.Mock()
|
orchestration_client.stacks.get.return_value = mock.Mock()
|
||||||
@ -1707,6 +1756,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
self.assertRaises(exceptions.InvalidConfiguration,
|
self.assertRaises(exceptions.InvalidConfiguration,
|
||||||
self.cmd.take_action, parsed_args)
|
self.cmd.take_action, parsed_args)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@ -1724,7 +1775,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_get_overcloud_endpoint,
|
mock_get_overcloud_endpoint,
|
||||||
mock_provision, mock_tempest_deploy_input,
|
mock_provision, mock_tempest_deploy_input,
|
||||||
mock_get_horizon_url,
|
mock_get_horizon_url,
|
||||||
mock_config_download):
|
mock_config_download,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
arglist = ['--templates', '--deployed-server', '--disable-validations']
|
arglist = ['--templates', '--deployed-server', '--disable-validations']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('templates', '/usr/share/openstack-tripleo-heat-templates/'),
|
('templates', '/usr/share/openstack-tripleo-heat-templates/'),
|
||||||
@ -1764,6 +1817,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
parsed_args)
|
parsed_args)
|
||||||
self.assertFalse(mock_deploy_tmpdir.called)
|
self.assertFalse(mock_deploy_tmpdir.called)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.get_overcloud_hosts')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.enable_ssh_admin')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
@mock.patch('tripleoclient.workflows.deployment.get_horizon_url',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
@mock.patch('tripleoclient.workflows.deployment.config_download')
|
||||||
@ -1780,7 +1835,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
mock_overcloudrc, mock_write_overcloudrc,
|
mock_overcloudrc, mock_write_overcloudrc,
|
||||||
mock_overcloud_endpoint,
|
mock_overcloud_endpoint,
|
||||||
mock_create_tempest_deployer_input,
|
mock_create_tempest_deployer_input,
|
||||||
mock_config_download, mock_get_horizon_url):
|
mock_config_download, mock_get_horizon_url,
|
||||||
|
mock_enable_ssh_admin,
|
||||||
|
mock_get_overcloud_hosts):
|
||||||
clients = self.app.client_manager
|
clients = self.app.client_manager
|
||||||
orchestration_client = clients.orchestration
|
orchestration_client = clients.orchestration
|
||||||
orchestration_client.stacks.get.return_value = mock.Mock()
|
orchestration_client.stacks.get.return_value = mock.Mock()
|
||||||
@ -1794,6 +1851,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||||||
|
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
self.assertTrue(mock_deploy_tmpdir.called)
|
self.assertTrue(mock_deploy_tmpdir.called)
|
||||||
|
self.assertTrue(mock_enable_ssh_admin.called)
|
||||||
|
self.assertTrue(mock_get_overcloud_hosts.called)
|
||||||
self.assertTrue(mock_config_download.called)
|
self.assertTrue(mock_config_download.called)
|
||||||
|
|
||||||
def test_download_missing_files_from_plan(self):
|
def test_download_missing_files_from_plan(self):
|
||||||
|
87
tripleoclient/tests/workflows/test_deployment.py
Normal file
87
tripleoclient/tests/workflows/test_deployment.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# -*- 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.workflows import deployment
|
||||||
|
|
||||||
|
|
||||||
|
class TestDeploymentWorkflows(utils.TestCommand):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestDeploymentWorkflows, self).setUp()
|
||||||
|
|
||||||
|
self.app.client_manager.workflow_engine = self.workflow = mock.Mock()
|
||||||
|
self.tripleoclient = mock.Mock()
|
||||||
|
self.websocket = mock.Mock()
|
||||||
|
self.websocket.__enter__ = lambda s: self.websocket
|
||||||
|
self.websocket.__exit__ = lambda s, *exc: None
|
||||||
|
self.tripleoclient.messaging_websocket.return_value = self.websocket
|
||||||
|
self.app.client_manager.tripleoclient = self.tripleoclient
|
||||||
|
|
||||||
|
self.message_success = iter([{
|
||||||
|
"execution": {"id": "IDID"},
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"message": "Success.",
|
||||||
|
"registered_nodes": [],
|
||||||
|
}])
|
||||||
|
self.message_failed = iter([{
|
||||||
|
"execution": {"id": "IDID"},
|
||||||
|
"status": "FAIL",
|
||||||
|
"message": "Fail.",
|
||||||
|
}])
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.wait_for_ssh_port')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.time.sleep')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.shutil.rmtree')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.open')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.tempfile')
|
||||||
|
@mock.patch('tripleoclient.workflows.deployment.subprocess.check_call')
|
||||||
|
def test_enable_ssh_admin(self, mock_check_call, mock_tempfile,
|
||||||
|
mock_open, mock_rmtree, mock_sleep,
|
||||||
|
mock_wait_for_ssh_port):
|
||||||
|
log = mock.Mock()
|
||||||
|
hosts = 'a', 'b', 'c'
|
||||||
|
ssh_user = 'test-user'
|
||||||
|
ssh_key = 'test-key'
|
||||||
|
|
||||||
|
mock_tempfile.mkdtemp.return_value = '/foo'
|
||||||
|
mock_read = mock.Mock()
|
||||||
|
mock_read.read.return_value = 'key'
|
||||||
|
mock_open.return_value = mock_read
|
||||||
|
mock_state = mock.Mock()
|
||||||
|
mock_state.state = 'SUCCESS'
|
||||||
|
self.workflow.executions.get.return_value = mock_state
|
||||||
|
deployment.enable_ssh_admin(log, self.app.client_manager,
|
||||||
|
hosts, ssh_user, ssh_key)
|
||||||
|
|
||||||
|
# once for ssh-keygen, then twice per host
|
||||||
|
self.assertEqual(7, mock_check_call.call_count)
|
||||||
|
|
||||||
|
# execution ran
|
||||||
|
self.assertEqual(1, self.workflow.executions.create.call_count)
|
||||||
|
call_args = self.workflow.executions.create.call_args
|
||||||
|
self.assertEqual('tripleo.access.v1.enable_ssh_admin', call_args[0][0])
|
||||||
|
self.assertEqual(('a', 'b', 'c'),
|
||||||
|
call_args[1]['workflow_input']['ssh_servers'])
|
||||||
|
self.assertEqual('test-user',
|
||||||
|
call_args[1]['workflow_input']['ssh_user'])
|
||||||
|
self.assertEqual('key',
|
||||||
|
call_args[1]['workflow_input']['ssh_private_key'])
|
||||||
|
|
||||||
|
# tmpdir should be cleaned up
|
||||||
|
self.assertEqual(1, mock_rmtree.call_count)
|
||||||
|
self.assertEqual('/foo', mock_rmtree.call_args[0][0])
|
@ -625,6 +625,8 @@ class DeployOvercloud(command.Command):
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--overcloud-ssh-key',
|
'--overcloud-ssh-key',
|
||||||
|
default=os.path.join(
|
||||||
|
os.path.expanduser('~'), '.ssh', 'id_rsa'),
|
||||||
help=_('Key path for ssh access to overcloud nodes.')
|
help=_('Key path for ssh access to overcloud nodes.')
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -899,9 +901,13 @@ class DeployOvercloud(command.Command):
|
|||||||
if parsed_args.config_download:
|
if parsed_args.config_download:
|
||||||
print("Deploying overcloud configuration")
|
print("Deploying overcloud configuration")
|
||||||
|
|
||||||
|
hosts = deployment.get_overcloud_hosts(self.clients, stack)
|
||||||
|
deployment.enable_ssh_admin(self.log, self.clients,
|
||||||
|
hosts,
|
||||||
|
parsed_args.overcloud_ssh_user,
|
||||||
|
parsed_args.overcloud_ssh_key)
|
||||||
deployment.config_download(self.log, self.clients, stack,
|
deployment.config_download(self.log, self.clients, stack,
|
||||||
parsed_args.templates,
|
parsed_args.templates,
|
||||||
parsed_args.deployed_server,
|
|
||||||
parsed_args.overcloud_ssh_user,
|
parsed_args.overcloud_ssh_user,
|
||||||
parsed_args.overcloud_ssh_key,
|
parsed_args.overcloud_ssh_key,
|
||||||
parsed_args.output_dir,
|
parsed_args.output_dir,
|
||||||
|
@ -14,12 +14,16 @@ from __future__ import print_function
|
|||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from heatclient.common import event_utils
|
from heatclient.common import event_utils
|
||||||
from openstackclient import shell
|
from openstackclient import shell
|
||||||
|
|
||||||
|
from tripleoclient import constants
|
||||||
from tripleoclient import exceptions
|
from tripleoclient import exceptions
|
||||||
from tripleoclient import utils
|
from tripleoclient import utils
|
||||||
|
|
||||||
@ -104,46 +108,129 @@ def overcloudrc(workflow_client, **input_):
|
|||||||
**input_)
|
**input_)
|
||||||
|
|
||||||
|
|
||||||
def config_download(log, clients, stack, templates, deployed_server,
|
def get_overcloud_hosts(clients, stack):
|
||||||
ssh_user, ssh_key, output_dir, verbosity=1):
|
|
||||||
role_net_hostname_map = utils.get_role_net_hostname_map(stack)
|
role_net_hostname_map = utils.get_role_net_hostname_map(stack)
|
||||||
hostnames = []
|
hostnames = []
|
||||||
for role in role_net_hostname_map:
|
for role in role_net_hostname_map:
|
||||||
hostnames.extend(role_net_hostname_map[role].get('ctlplane', []))
|
hostnames.extend(role_net_hostname_map[role].get('ctlplane', []))
|
||||||
|
|
||||||
ips = []
|
hosts = []
|
||||||
hosts_entry = utils.get_hosts_entry(stack)
|
hosts_entry = utils.get_hosts_entry(stack)
|
||||||
for hostname in hostnames:
|
for hostname in hostnames:
|
||||||
for line in hosts_entry.split('\n'):
|
for line in hosts_entry.split('\n'):
|
||||||
match = re.search('\s*%s\s*' % hostname, line)
|
match = re.search('\s*%s\s*' % hostname, line)
|
||||||
if match:
|
if match:
|
||||||
ips.append(line.split(' ')[0])
|
hosts.append(line.split(' ')[0])
|
||||||
|
|
||||||
script_path = os.path.join(templates,
|
return hosts
|
||||||
'deployed-server',
|
|
||||||
'scripts',
|
|
||||||
'enable-ssh-admin.sh')
|
|
||||||
|
|
||||||
env = os.environ.copy()
|
|
||||||
env.update(dict(OVERCLOUD_HOSTS=' '.join(ips),
|
|
||||||
OVERCLOUD_SSH_USER=ssh_user))
|
|
||||||
|
|
||||||
if ssh_key:
|
|
||||||
env['OVERCLOUD_SSH_KEY'] = ssh_key
|
|
||||||
|
|
||||||
proc = subprocess.Popen([script_path], env=env, shell=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT)
|
|
||||||
|
|
||||||
|
def wait_for_ssh_port(host):
|
||||||
|
start = int(time.time())
|
||||||
while True:
|
while True:
|
||||||
line = proc.stdout.readline().decode('utf-8')
|
now = int(time.time())
|
||||||
if line:
|
if (now - start) > constants.ENABLE_SSH_ADMIN_SSH_PORT_TIMEOUT:
|
||||||
log.info(line.rstrip())
|
raise exceptions.DeploymentError(
|
||||||
if line == '' and proc.poll() is not None:
|
"Timed out waiting for port 22 from %s" % host)
|
||||||
break
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise RuntimeError('%s failed.' % script_path)
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
socket.socket().connect((host, 22))
|
||||||
|
return
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
def enable_ssh_admin(log, clients, hosts, ssh_user, ssh_key):
|
||||||
|
print("Enabling ssh admin (tripleo-admin) for hosts:")
|
||||||
|
print(" ".join(hosts))
|
||||||
|
print("Using ssh user %s for initial connection." % ssh_user)
|
||||||
|
print("Using ssh key at %s for initial connection." % ssh_key)
|
||||||
|
|
||||||
|
ssh_options = ("-o ConnectionAttempts=6 "
|
||||||
|
"-o ConnectTimeout=30 "
|
||||||
|
"-o StrictHostKeyChecking=no "
|
||||||
|
"-o UserKnownHostsFile=/dev/null")
|
||||||
|
tmp_key_dir = tempfile.mkdtemp()
|
||||||
|
tmp_key_private = os.path.join(tmp_key_dir, 'id_rsa')
|
||||||
|
tmp_key_public = os.path.join(tmp_key_dir, 'id_rsa.pub')
|
||||||
|
tmp_key_comment = "TripleO split stack short term key"
|
||||||
|
|
||||||
|
try:
|
||||||
|
tmp_key_command = ["ssh-keygen", "-N", "", "-t", "rsa", "-b", "4096",
|
||||||
|
"-f", tmp_key_private, "-C", tmp_key_comment]
|
||||||
|
subprocess.check_call(tmp_key_command, stderr=subprocess.STDOUT)
|
||||||
|
tmp_key_public_contents = open(tmp_key_public).read()
|
||||||
|
|
||||||
|
for host in hosts:
|
||||||
|
wait_for_ssh_port(host)
|
||||||
|
copy_tmp_key_command = ["ssh"] + ssh_options.split()
|
||||||
|
copy_tmp_key_command += \
|
||||||
|
["-o", "StrictHostKeyChecking=no",
|
||||||
|
"-i", ssh_key, "-l", ssh_user, host,
|
||||||
|
"echo -e '\n%s' >> $HOME/.ssh/authorized_keys" %
|
||||||
|
tmp_key_public_contents]
|
||||||
|
print("Inserting TripleO short term key for %s" % host)
|
||||||
|
subprocess.check_call(copy_tmp_key_command,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
print("Starting ssh admin enablement workflow")
|
||||||
|
|
||||||
|
workflow_client = clients.workflow_engine
|
||||||
|
|
||||||
|
workflow_input = {
|
||||||
|
"ssh_user": ssh_user,
|
||||||
|
"ssh_servers": hosts,
|
||||||
|
"ssh_private_key": open(tmp_key_private).read(),
|
||||||
|
}
|
||||||
|
|
||||||
|
execution = base.start_workflow(
|
||||||
|
workflow_client,
|
||||||
|
'tripleo.access.v1.enable_ssh_admin',
|
||||||
|
workflow_input=workflow_input
|
||||||
|
)
|
||||||
|
|
||||||
|
start = int(time.time())
|
||||||
|
while True:
|
||||||
|
now = int(time.time())
|
||||||
|
if (now - start) > constants.ENABLE_SSH_ADMIN_TIMEOUT:
|
||||||
|
raise exceptions.DeploymentError(
|
||||||
|
"ssh admin enablement workflow - TIMED OUT.")
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
execution = workflow_client.executions.get(execution.id)
|
||||||
|
state = execution.state
|
||||||
|
|
||||||
|
if state == 'RUNNING':
|
||||||
|
if (now - start) % constants.ENABLE_SSH_ADMIN_STATUS_INTERVAL\
|
||||||
|
== 0:
|
||||||
|
print("ssh admin enablement workflow - RUNNING.")
|
||||||
|
continue
|
||||||
|
elif state == 'SUCCESS':
|
||||||
|
print("ssh admin enablement workflow - COMPLETE.")
|
||||||
|
break
|
||||||
|
elif state == 'FAILED':
|
||||||
|
raise exceptions.DeploymentError(
|
||||||
|
"ssh admin enablement workflow - FAILED.")
|
||||||
|
|
||||||
|
for host in hosts:
|
||||||
|
rm_tmp_key_command = ["ssh"] + ssh_options.split()
|
||||||
|
rm_tmp_key_command += \
|
||||||
|
["-l", ssh_user, host,
|
||||||
|
"sed -i -e '/%s/d' $HOME/.ssh/authorized_keys" %
|
||||||
|
tmp_key_comment]
|
||||||
|
print("Removing TripleO short term key from %s" % host)
|
||||||
|
subprocess.check_call(rm_tmp_key_command, stderr=subprocess.STDOUT)
|
||||||
|
finally:
|
||||||
|
print("Removing short term keys locally")
|
||||||
|
shutil.rmtree(tmp_key_dir)
|
||||||
|
|
||||||
|
print("Enabling ssh admin - COMPLETE.")
|
||||||
|
|
||||||
|
|
||||||
|
def config_download(log, clients, stack, templates,
|
||||||
|
ssh_user, ssh_key, output_dir, verbosity=1):
|
||||||
workflow_client = clients.workflow_engine
|
workflow_client = clients.workflow_engine
|
||||||
tripleoclients = clients.tripleoclient
|
tripleoclients = clients.tripleoclient
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user