From 844e8c7829816ccf33cbc046d2c9c0d6ae8e071c Mon Sep 17 00:00:00 2001 From: enthurohini Date: Sun, 28 Feb 2016 13:34:52 +0530 Subject: [PATCH] Add environment create/delete to openstack CLI $ openstack environment create [--join-net-id ] [--join-subnet-id ] [--region ] $ openstack environment delete [--abandon] [ ...] Partially implements: blueprint openstack-client-plugin-support Change-Id: Ic8d97d8fb79ba5ce9e489b4083f26ef96f4e43e9 --- muranoclient/osc/v1/environment.py | 151 ++++++++++++-- .../tests/unit/osc/v1/test_environment.py | 194 +++++++++++++++++- ...-environment-command-85b31973e101fd1f.yaml | 6 + setup.cfg | 2 + 4 files changed, 328 insertions(+), 25 deletions(-) create mode 100644 releasenotes/notes/added-environment-command-85b31973e101fd1f.yaml diff --git a/muranoclient/osc/v1/environment.py b/muranoclient/osc/v1/environment.py index d291edf8..760d9784 100644 --- a/muranoclient/osc/v1/environment.py +++ b/muranoclient/osc/v1/environment.py @@ -12,19 +12,22 @@ """Application-catalog v1 stack action implementation""" +import uuid + from cliff import lister from cliff import show from muranoclient.common import utils as murano_utils +from muranoclient.openstack.common.apiclient import exceptions from openstackclient.common import utils from oslo_log import log as logging from oslo_serialization import jsonutils +LOG = logging.getLogger(__name__) + class ListEnvironments(lister.Lister): """Lists environments""" - log = logging.getLogger(__name__ + ".ListEnvironments") - def get_parser(self, prog_name): parser = super(ListEnvironments, self).get_parser(prog_name) parser.add_argument( @@ -37,13 +40,12 @@ class ListEnvironments(lister.Lister): return parser def take_action(self, parsed_args): - self.log.debug("take_action({0})".format(parsed_args)) + LOG.debug("take_action({0})".format(parsed_args)) client = self.app.client_manager.application_catalog data = client.environments.list(parsed_args.all_tenants) - columns = ('id', 'name', 'created', 'updated') + columns = ('id', 'name', 'status', 'created', 'updated') column_headers = [c.capitalize() for c in columns] - return ( column_headers, list(utils.get_item_properties( @@ -56,8 +58,6 @@ class ListEnvironments(lister.Lister): class ShowEnvironment(show.ShowOne): """Display environment details""" - log = logging.getLogger(__name__ + ".ShowEnvironment") - def get_parser(self, prog_name): parser = super(ShowEnvironment, self).get_parser(prog_name) parser.add_argument( @@ -81,7 +81,7 @@ class ShowEnvironment(show.ShowOne): return parser def take_action(self, parsed_args): - self.log.debug("take_action({0})".format(parsed_args)) + LOG.debug("take_action({0})".format(parsed_args)) client = self.app.client_manager.application_catalog environment = utils.find_resource(client.environments, @@ -100,8 +100,6 @@ class ShowEnvironment(show.ShowOne): class RenameEnvironment(lister.Lister): """Rename an environment.""" - log = logging.getLogger(__name__ + ".RenameEnvironment") - def get_parser(self, prog_name): parser = super(RenameEnvironment, self).get_parser(prog_name) parser.add_argument( @@ -118,14 +116,14 @@ class RenameEnvironment(lister.Lister): return parser def take_action(self, parsed_args): - self.log.debug("take_action({0})".format(parsed_args)) + LOG.debug("take_action({0})".format(parsed_args)) client = self.app.client_manager.application_catalog environment = utils.find_resource(client.environments, parsed_args.id) data = client.environments.update(environment.id, parsed_args.name) - columns = ('id', 'name', 'created', 'updated') + columns = ('id', 'name', 'status', 'created', 'updated') column_headers = [c.capitalize() for c in columns] return ( @@ -140,8 +138,6 @@ class RenameEnvironment(lister.Lister): class EnvironmentSessionCreate(show.ShowOne): """Creates a new configuration session for environment ID.""" - log = logging.getLogger(__name__ + ".EnvironmentSessionCreate") - def get_parser(self, prog_name): parser = super(EnvironmentSessionCreate, self).get_parser(prog_name) parser.add_argument( @@ -153,7 +149,7 @@ class EnvironmentSessionCreate(show.ShowOne): return parser def take_action(self, parsed_args): - self.log.debug("take_action({0})".format(parsed_args)) + LOG.debug("take_action({0})".format(parsed_args)) client = self.app.client_manager.application_catalog environment_id = parsed_args.id @@ -161,3 +157,128 @@ class EnvironmentSessionCreate(show.ShowOne): sessionid = murano_utils.text_wrap_formatter(session_id) return (['id'], [sessionid]) + + +class EnvironmentCreate(lister.Lister): + """Create an environment.""" + + def get_parser(self, prog_name): + parser = super(EnvironmentCreate, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar="", + help="Environment name.", + ) + parser.add_argument( + '--region', + metavar="", + help="Name of the target OpenStack region.", + ) + parser.add_argument( + '--join-subnet-id', + metavar="", + help="Subnetwork id to join.", + ) + parser.add_argument( + '--join-net-id', + metavar="", + help="Network id to join.", + ) + + return parser + + def take_action(self, parsed_args): + LOG.debug("take_action({0})".format(parsed_args)) + client = self.app.client_manager.application_catalog + + body = {"name": parsed_args.name, "region": parsed_args.region} + if parsed_args.join_net_id or parsed_args.join_subnet_id: + res = { + 'defaultNetworks': { + 'environment': { + '?': { + 'id': uuid.uuid4().hex, + 'type': + 'io.murano.resources.ExistingNeutronNetwork' + }, + }, + 'flat': None + } + } + if parsed_args.join_net_id: + res['defaultNetworks']['environment']['internalNetworkName'] \ + = parsed_args.join_net_id + if parsed_args.join_subnet_id: + res['defaultNetworks']['environment']['internalSubnetworkName' + ] = \ + parsed_args.join_subnet_id + + body.update(res) + + data = client.environments.create(body) + + columns = ('id', 'name', 'status', 'created', 'updated') + column_headers = [c.capitalize() for c in columns] + + return ( + column_headers, + [utils.get_item_properties( + data, + columns, + )] + ) + + +class EnvironmentDelete(lister.Lister): + """Delete an environment.""" + + def get_parser(self, prog_name): + parser = super(EnvironmentDelete, self).get_parser(prog_name) + parser.add_argument( + 'id', + metavar="", + nargs="+", + help="Id or name of environment(s) to delete.", + ) + parser.add_argument( + '--abandon', + action='store_true', + default=False, + help="If set will abandon environment without deleting any of its" + " resources.", + ) + + return parser + + def take_action(self, parsed_args): + LOG.debug("take_action({0})".format(parsed_args)) + client = self.app.client_manager.application_catalog + + abandon = getattr(parsed_args, 'abandon', False) + failure_count = 0 + for environment_id in parsed_args.id: + try: + environment = murano_utils.find_resource(client.environments, + environment_id) + client.environments.delete(environment.id, abandon) + except exceptions.NotFound: + failure_count += 1 + print("Failed to delete '{0}'; environment not found". + format(environment_id)) + + if failure_count == len(parsed_args.id): + raise exceptions.CommandError("Unable to find and delete any of " + "the specified environments.") + + data = client.environments.list() + + columns = ('id', 'name', 'status', 'created', 'updated') + column_headers = [c.capitalize() for c in columns] + + return ( + column_headers, + list(utils.get_item_properties( + s, + columns, + ) for s in data) + ) diff --git a/muranoclient/tests/unit/osc/v1/test_environment.py b/muranoclient/tests/unit/osc/v1/test_environment.py index 775a51a4..bf561604 100644 --- a/muranoclient/tests/unit/osc/v1/test_environment.py +++ b/muranoclient/tests/unit/osc/v1/test_environment.py @@ -50,7 +50,8 @@ class TestListEnvironment(TestEnvironment): verifylist = [] mock_util.return_value = ('1234', 'Environment of all tenants', - '2015-12-16T17:31:54', '2015-12-16T17:31:54' + 'fake deployed', '2015-12-16T17:31:54', + '2015-12-16T17:31:54' ) parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -58,12 +59,13 @@ class TestListEnvironment(TestEnvironment): columns, data = self.cmd.take_action(parsed_args) # Check that columns are correct - expected_columns = ['Id', 'Name', 'Created', 'Updated'] + expected_columns = ['Id', 'Name', 'Status', 'Created', 'Updated'] self.assertEqual(expected_columns, columns) # Check that data is correct expected_data = [('1234', 'Environment of all tenants', - '2015-12-16T17:31:54', '2015-12-16T17:31:54')] + 'fake deployed', '2015-12-16T17:31:54', + '2015-12-16T17:31:54')] self.assertEqual(expected_data, data) @mock.patch('openstackclient.common.utils.get_item_properties') @@ -72,7 +74,8 @@ class TestListEnvironment(TestEnvironment): verifylist = [('all_tenants', True)] mock_util.return_value = ('1234', 'Environment of all tenants', - '2015-12-16T17:31:54', '2015-12-16T17:31:54' + 'fake deployed', '2015-12-16T17:31:54', + '2015-12-16T17:31:54' ) parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -80,12 +83,13 @@ class TestListEnvironment(TestEnvironment): columns, data = self.cmd.take_action(parsed_args) # Check that columns are correct - expected_columns = ['Id', 'Name', 'Created', 'Updated'] + expected_columns = ['Id', 'Name', 'Status', 'Created', 'Updated'] self.assertEqual(expected_columns, columns) # Check that data is correct expected_data = [('1234', 'Environment of all tenants', - '2015-12-16T17:31:54', '2015-12-16T17:31:54')] + 'fake deployed', '2015-12-16T17:31:54', + '2015-12-16T17:31:54')] self.assertEqual(expected_data, data) @@ -175,19 +179,20 @@ class TestRenameEnvironment(TestEnvironment): arglist = ['1234', 'fake-1'] verifylist = [('id', '1234'), ('name', 'fake-1')] - mock_util.return_value = ('1234', 'fake-1', '2015-12-16T17:31:54', - '2015-12-16T17:31:54') + mock_util.return_value = ('1234', 'fake-1', 'fake deployed', + '2015-12-16T17:31:54', '2015-12-16T17:31:54' + ) parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) # Check that columns are correct - expected_columns = ['Id', 'Name', 'Created', 'Updated'] + expected_columns = ['Id', 'Name', 'Status', 'Created', 'Updated'] self.assertEqual(expected_columns, columns) # Check that data is correct - expected_data = [('1234', 'fake-1', + expected_data = [('1234', 'fake-1', 'fake deployed', '2015-12-16T17:31:54', '2015-12-16T17:31:54')] self.assertEqual(expected_data, data) @@ -217,3 +222,172 @@ class TestEnvironmentSessionCreate(TestEnvironment): # Check that data is correct expected_data = ['1abc2xyz'] self.assertEqual(expected_data, data) + + +class TestEnvironmentCreate(TestEnvironment): + def setUp(self): + super(TestEnvironmentCreate, self).setUp() + self.environment_mock.create.return_value = [api_env.Environment(None, + ENV_INFO)] + + # Command to test + self.cmd = osc_env.EnvironmentCreate(self.app, None) + + @mock.patch('openstackclient.common.utils.get_item_properties') + def test_environment_create_with_no_option(self, mock_util): + arglist = ['fake'] + verifylist = [('name', 'fake')] + + mock_util.return_value = ('1234', 'fake', 'ready', + '2015-12-16T17:31:54', '2015-12-16T17:31:54') + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ['Id', 'Name', 'Status', 'Created', 'Updated'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('1234', 'fake', 'ready', + '2015-12-16T17:31:54', '2015-12-16T17:31:54')] + self.assertEqual(expected_data, data) + + @mock.patch('openstackclient.common.utils.get_item_properties') + def test_environment_create_with_region_option(self, mock_util): + arglist = ['fake', '--region', 'region_one'] + verifylist = [('name', 'fake'), ('region', 'region_one')] + + mock_util.return_value = ('1234', 'fake', 'ready', + '2015-12-16T17:31:54', '2015-12-16T17:31:54') + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that correct arguments are passed + self.environment_mock.create.assert_has_calls([mock.call( + {'name': 'fake', 'region': 'region_one'})]) + + # Check that columns are correct + expected_columns = ['Id', 'Name', 'Status', 'Created', 'Updated'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('1234', 'fake', 'ready', + '2015-12-16T17:31:54', '2015-12-16T17:31:54')] + self.assertEqual(expected_data, data) + + @mock.patch('openstackclient.common.utils.get_item_properties') + def test_environment_create_with_net_option(self, mock_util): + arglist = ['fake', '--join-net-id', 'x1y2z3'] + verifylist = [('name', 'fake'), ('join_net_id', 'x1y2z3')] + + mock_util.return_value = ('1234', 'fake', 'ready', + '2015-12-16T17:31:54', '2015-12-16T17:31:54') + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + expected_call = { + 'defaultNetworks': { + 'environment': { + 'internalNetworkName': 'x1y2z3', + '?': { + 'type': 'io.murano.resources.ExistingNeutronNetwork', + 'id': mock.ANY + } + }, + 'flat': None + }, + 'name': 'fake', + 'region': None + } + + # Check that correct arguments are passed + self.environment_mock.create.assert_called_with(expected_call) + + # Check that columns are correct + expected_columns = ['Id', 'Name', 'Status', 'Created', 'Updated'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('1234', 'fake', 'ready', + '2015-12-16T17:31:54', '2015-12-16T17:31:54')] + self.assertEqual(expected_data, data) + + @mock.patch('openstackclient.common.utils.get_item_properties') + def test_environment_create_with_subnet_option(self, mock_util): + arglist = ['fake', '--join-subnet-id', 'x1y2z3'] + verifylist = [('name', 'fake'), ('join_subnet_id', 'x1y2z3')] + + mock_util.return_value = ('1234', 'fake', 'ready', + '2015-12-16T17:31:54', '2015-12-16T17:31:54') + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + expected_call = { + 'defaultNetworks': { + 'environment': { + 'internalSubnetworkName': 'x1y2z3', + '?': { + 'type': 'io.murano.resources.ExistingNeutronNetwork', + 'id': mock.ANY + } + }, + 'flat': None + }, + 'name': 'fake', + 'region': None + } + + # Check that correct arguments are passed + self.environment_mock.create.assert_called_with(expected_call) + + # Check that columns are correct + expected_columns = ['Id', 'Name', 'Status', 'Created', 'Updated'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('1234', 'fake', 'ready', + '2015-12-16T17:31:54', '2015-12-16T17:31:54')] + self.assertEqual(expected_data, data) + + +class TestEnvironmentDelete(TestEnvironment): + def setUp(self): + super(TestEnvironmentDelete, self).setUp() + self.environment_mock.delete.return_value = None + self.environment_mock.list.return_value = [api_env.Environment(None, + ENV_INFO)] + + # Command to test + self.cmd = osc_env.EnvironmentDelete(self.app, None) + + @mock.patch('openstackclient.common.utils.get_item_properties') + def test_environment_delete(self, mock_util): + arglist = ['fake1', 'fake2'] + verifylist = [('id', ['fake1', 'fake2'])] + + mock_util.return_value = ('1234', 'Environment of all tenants', + 'fake deployed', '2015-12-16T17:31:54', + '2015-12-16T17:31:54' + ) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ['Id', 'Name', 'Status', 'Created', 'Updated'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('1234', 'Environment of all tenants', + 'fake deployed', '2015-12-16T17:31:54', + '2015-12-16T17:31:54')] + self.assertEqual(expected_data, data) diff --git a/releasenotes/notes/added-environment-command-85b31973e101fd1f.yaml b/releasenotes/notes/added-environment-command-85b31973e101fd1f.yaml new file mode 100644 index 00000000..c9d4f972 --- /dev/null +++ b/releasenotes/notes/added-environment-command-85b31973e101fd1f.yaml @@ -0,0 +1,6 @@ +--- +features: + - New OSC command ``environment create [--join-net-id ] + [--join-subnet-id ] [--region ] `` + - New OSC command ``environment delete [--abandon] + [ ...]`` diff --git a/setup.cfg b/setup.cfg index 98657b4b..8d84638b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,6 +38,8 @@ openstack.application_catalog.v1 = environment_show = muranoclient.osc.v1.environment:ShowEnvironment environment_rename = muranoclient.osc.v1.environment:RenameEnvironment environment_session_create = muranoclient.osc.v1.environment:EnvironmentSessionCreate + environment_create = muranoclient.osc.v1.environment:EnvironmentCreate + environment_delete = muranoclient.osc.v1.environment:EnvironmentDelete [global] setup-hooks =