diff --git a/releasenotes/notes/overcloud-status-69d3cc931f50930e.yaml b/releasenotes/notes/overcloud-status-69d3cc931f50930e.yaml new file mode 100644 index 000000000..21a5177a5 --- /dev/null +++ b/releasenotes/notes/overcloud-status-69d3cc931f50930e.yaml @@ -0,0 +1,4 @@ +--- +features: + - A new command, openstack overcloud status, is added to show + the status of a deployment plan when using config-download. diff --git a/setup.cfg b/setup.cfg index 5d5652e16..447aea580 100644 --- a/setup.cfg +++ b/setup.cfg @@ -58,6 +58,7 @@ openstack.tripleoclient.v1 = overcloud_delete = tripleoclient.v1.overcloud_delete:DeleteOvercloud overcloud_credentials = tripleoclient.v1.overcloud_credentials:OvercloudCredentials overcloud_deploy = tripleoclient.v1.overcloud_deploy:DeployOvercloud + overcloud_status = tripleoclient.v1.overcloud_deploy:GetDeploymentStatus overcloud_image_build = tripleoclient.v1.overcloud_image:BuildOvercloudImage overcloud_image_upload = tripleoclient.v1.overcloud_image:UploadOvercloudImage overcloud_node_configure = tripleoclient.v1.overcloud_node:ConfigureNode diff --git a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py index 6a50fe043..828695fb3 100644 --- a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py +++ b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py @@ -23,6 +23,7 @@ import yaml from heatclient import exc as hc_exc import mock from osc_lib import exceptions as oscexc +from osc_lib.tests import utils from swiftclient.exceptions import ClientException as ObjectClientException from tripleoclient import constants @@ -1853,3 +1854,45 @@ class TestArgumentValidation(fakes.TestDeployOvercloud): def test_validate_env_dir_ignore_default_not_existing(self): full_path = os.path.expanduser(constants.DEFAULT_ENV_DIRECTORY) self.assertIsNone(self.validate([full_path])) + + +class TestGetDeploymentStatus(utils.TestCommand): + + def setUp(self): + super(TestGetDeploymentStatus, self).setUp() + self.cmd = overcloud_deploy.GetDeploymentStatus(self.app, None) + self.app.client_manager = mock.Mock() + self.clients = self.app.client_manager + + @mock.patch( + 'tripleoclient.workflows.deployment.get_deployment_status', + autospec=True) + def test_get_deployment_status(self, mock_get_deployment_status): + parsed_args = self.check_parser(self.cmd, [], []) + self.cmd.app.stdout = six.StringIO() + + status = { + 'workflow_status': { + 'payload': { + 'execution': { + 'created_at': 'yesterday', + 'updated_at': 'today' + }, + 'plan_name': 'testplan', + 'deployment_status': 'SUCCESS' + } + } + } + + mock_get_deployment_status.return_value = status + + self.cmd.take_action(parsed_args) + + expected = ( + '+-----------+-----------+---------+-------------------+\n' + '| Plan Name | Created | Updated | Deployment Status |\n' + '+-----------+-----------+---------+-------------------+\n' + '| testplan | yesterday | today | SUCCESS |\n' + '+-----------+-----------+---------+-------------------+\n') + + self.assertEqual(expected, self.cmd.app.stdout.getvalue()) diff --git a/tripleoclient/tests/v1/test_overcloud_plan.py b/tripleoclient/tests/v1/test_overcloud_plan.py index 79ea69f50..be4e5dc52 100644 --- a/tripleoclient/tests/v1/test_overcloud_plan.py +++ b/tripleoclient/tests/v1/test_overcloud_plan.py @@ -18,6 +18,20 @@ from tripleoclient import exceptions 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): diff --git a/tripleoclient/v1/overcloud_deploy.py b/tripleoclient/v1/overcloud_deploy.py index 23741314f..9911860a5 100644 --- a/tripleoclient/v1/overcloud_deploy.py +++ b/tripleoclient/v1/overcloud_deploy.py @@ -18,6 +18,7 @@ import argparse import logging import os import os.path +from prettytable import PrettyTable import re import shutil import six @@ -933,3 +934,36 @@ class DeployOvercloud(command.Command): print("Overcloud Horizon Dashboard URL: {0}".format(horizon_url)) print("Overcloud rc file: {0}".format(rcpath)) print("Overcloud Deployed") + + +class GetDeploymentStatus(command.Command): + """Check status of a deployment plan""" + + log = logging.getLogger(__name__ + ".GetDeploymentStatus") + + def get_parser(self, prog_name): + parser = super(GetDeploymentStatus, self).get_parser(prog_name) + parser.add_argument('--plan', '--stack', + help=_('Name of the stack/plan. ' + '(default: overcloud)'), + default='overcloud') + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + plan = parsed_args.plan + + status = deployment.get_deployment_status( + self.app.client_manager, + plan=plan + ) + + payload = status['workflow_status']['payload'] + execution = payload['execution'] + table = PrettyTable( + ['Plan Name', 'Created', 'Updated', 'Deployment Status']) + table.add_row([payload['plan_name'], + execution['created_at'], + execution['updated_at'], + payload['deployment_status']]) + print(table, file=self.app.stdout) diff --git a/tripleoclient/v1/overcloud_plan.py b/tripleoclient/v1/overcloud_plan.py index 429f1e68a..87ec5d0e4 100644 --- a/tripleoclient/v1/overcloud_plan.py +++ b/tripleoclient/v1/overcloud_plan.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +from __future__ import print_function + import logging import os.path diff --git a/tripleoclient/workflows/deployment.py b/tripleoclient/workflows/deployment.py index 1ad6246b8..25895264e 100644 --- a/tripleoclient/workflows/deployment.py +++ b/tripleoclient/workflows/deployment.py @@ -26,6 +26,9 @@ from tripleoclient import utils from tripleoclient.workflows import base +_WORKFLOW_TIMEOUT = 360 # 6 * 60 seconds + + def deploy(log, clients, **workflow_input): workflow_client = clients.workflow_engine @@ -188,3 +191,27 @@ def get_horizon_url(clients, **workflow_input): assert payload['status'] == "SUCCESS" return payload['horizon_url'] + + +def get_deployment_status(clients, **workflow_input): + workflow_client = clients.workflow_engine + tripleoclients = clients.tripleoclient + + execution = base.start_workflow( + workflow_client, + 'tripleo.deployment.v1.get_deployment_status', + workflow_input=workflow_input + ) + + with tripleoclients.messaging_websocket() as ws: + for payload in base.wait_for_messages(workflow_client, ws, execution, + _WORKFLOW_TIMEOUT): + if 'message' in payload: + print(payload['message']) + + if payload['status'] == 'SUCCESS': + return payload['deployment_status'] + else: + raise exceptions.WorkflowServiceError( + 'Exception getting deployment status: {}'.format( + payload.get('message', '')))