diff options
| author | Colleen Murphy <colleen@puppetlabs.com> | 2015-02-16 23:21:00 -0800 |
|---|---|---|
| committer | Colleen Murphy <colleen@puppetlabs.com> | 2015-03-19 23:49:02 -0700 |
| commit | 6c224f5acfeb2288f2f4be41aee112fd01cbf4d0 (patch) | |
| tree | 9f8e460435c4f7410370438b5883eca218f7ccf4 /openstackclient | |
| parent | 9400effd4b7653045657630e0909b3dc303ec59e (diff) | |
| download | python-openstackclient-6c224f5acfeb2288f2f4be41aee112fd01cbf4d0.tar.gz | |
Add project and domain params to network create
Without this patch, openstackclient has no way to specify to which
project a network belongs upon creation. Instead, it uses the project
ID that the user is authenticating with to fill the tenant_id column.
This is a problem because an admin user is unable to specify a project
for a non-admin network. To fix this and to improve feature parity with
the neutron client, this patch adds project and domain parameters to
the network create command and uses the given project name to look up
the project ID.
Neutron does not allow the project to be changed after creation, so no
such parameter has been added to the neutron set command.
Neutron calls the field 'tenant_id', but this change exposes the
parameter as '--project' to support the newer terminology.
If no project is specified, the client defaults to the previous
behavior of using the auth project.
Change-Id: Ia33ff7d599542c5b88baf2a69b063a23089a3cc4
Diffstat (limited to 'openstackclient')
| -rw-r--r-- | openstackclient/network/v2/network.py | 21 | ||||
| -rw-r--r-- | openstackclient/tests/identity/v2_0/fakes.py | 7 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2/test_network.py | 100 |
3 files changed, 128 insertions, 0 deletions
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 1a79c80a..9b246642 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -22,6 +22,7 @@ from cliff import show from openstackclient.common import exceptions from openstackclient.common import utils +from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -82,6 +83,14 @@ class CreateNetwork(show.ShowOne): action='store_false', help='Do not share the network between projects', ) + parser.add_argument( + '--project', + metavar='<project>', + help="Owner's project (name or ID)") + parser.add_argument( + '--domain', + metavar='<domain>', + help="Owner's domain (name or ID)") return parser def take_action(self, parsed_args): @@ -101,6 +110,18 @@ class CreateNetwork(show.ShowOne): 'admin_state_up': parsed_args.admin_state} if parsed_args.shared is not None: body['shared'] = parsed_args.shared + if parsed_args.project is not None: + identity_client = self.app.client_manager.identity + if parsed_args.domain is not None: + domain = identity_common.find_domain(identity_client, + parsed_args.domain) + project_id = utils.find_resource(identity_client.projects, + parsed_args.project, + domain_id=domain.id).id + else: + project_id = utils.find_resource(identity_client.projects, + parsed_args.project).id + body['tenant_id'] = project_id return {'network': body} diff --git a/openstackclient/tests/identity/v2_0/fakes.py b/openstackclient/tests/identity/v2_0/fakes.py index b136f841..6688606a 100644 --- a/openstackclient/tests/identity/v2_0/fakes.py +++ b/openstackclient/tests/identity/v2_0/fakes.py @@ -142,6 +142,13 @@ class FakeIdentityv2Client(object): self.auth_token = kwargs['token'] self.management_url = kwargs['endpoint'] + def __getattr__(self, name): + # Map v3 'projects' back to v2 'tenants' + if name == "projects": + return self.tenants + else: + raise AttributeError(name) + class TestIdentityv2(utils.TestCommand): def setUp(self): diff --git a/openstackclient/tests/network/v2/test_network.py b/openstackclient/tests/network/v2/test_network.py index e14dd88b..90085f28 100644 --- a/openstackclient/tests/network/v2/test_network.py +++ b/openstackclient/tests/network/v2/test_network.py @@ -16,6 +16,9 @@ import mock from openstackclient.common import exceptions from openstackclient.network.v2 import network +from openstackclient.tests import fakes +from openstackclient.tests.identity.v2_0 import fakes as identity_fakes_v2 +from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3 from openstackclient.tests.network import common RESOURCE = 'network' @@ -65,6 +68,7 @@ class TestCreateNetwork(common.TestNetworkBase): ('name', FAKE_NAME), ('admin_state', True), ('shared', None), + ('project', None), ] mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) self.app.client_manager.network.create_network = mocker @@ -85,15 +89,36 @@ class TestCreateNetwork(common.TestNetworkBase): arglist = [ "--disable", "--share", + "--project", identity_fakes_v3.project_name, + "--domain", identity_fakes_v3.domain_name, FAKE_NAME, ] + self.given_show_options verifylist = [ ('admin_state', False), ('shared', True), + ('project', identity_fakes_v3.project_name), + ('domain', identity_fakes_v3.domain_name), ('name', FAKE_NAME), ] + self.then_show_options mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) self.app.client_manager.network.create_network = mocker + identity_client = identity_fakes_v3.FakeIdentityv3Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.identity = identity_client + self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes_v3.PROJECT), + loaded=True, + ) + self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes_v3.DOMAIN), + loaded=True, + ) cmd = network.CreateNetwork(self.app, self.namespace) parsed_args = self.check_parser(cmd, arglist, verifylist) @@ -104,6 +129,7 @@ class TestCreateNetwork(common.TestNetworkBase): 'admin_state_up': False, 'name': FAKE_NAME, 'shared': True, + 'tenant_id': identity_fakes_v3.project_id, } }) self.assertEqual(FILTERED, result) @@ -135,6 +161,80 @@ class TestCreateNetwork(common.TestNetworkBase): }) self.assertEqual(FILTERED, result) + def test_create_with_project_identityv2(self): + arglist = [ + "--project", identity_fakes_v2.project_name, + FAKE_NAME, + + ] + verifylist = [ + ('admin_state', True), + ('shared', None), + ('name', FAKE_NAME), + ('project', identity_fakes_v2.project_name), + ] + mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) + self.app.client_manager.network.create_network = mocker + identity_client = identity_fakes_v2.FakeIdentityv2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.identity = identity_client + self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes_v2.PROJECT), + loaded=True, + ) + cmd = network.CreateNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = list(cmd.take_action(parsed_args)) + + mocker.assert_called_with({ + RESOURCE: { + 'admin_state_up': True, + 'name': FAKE_NAME, + 'tenant_id': identity_fakes_v2.project_id, + } + }) + self.assertEqual(FILTERED, result) + + def test_create_with_domain_identityv2(self): + arglist = [ + "--project", identity_fakes_v3.project_name, + "--domain", identity_fakes_v3.domain_name, + FAKE_NAME, + ] + verifylist = [ + ('admin_state', True), + ('shared', None), + ('project', identity_fakes_v3.project_name), + ('domain', identity_fakes_v3.domain_name), + ('name', FAKE_NAME), + ] + mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) + self.app.client_manager.network.create_network = mocker + identity_client = identity_fakes_v2.FakeIdentityv2Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.identity = identity_client + self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes_v2.PROJECT), + loaded=True, + ) + cmd = network.CreateNetwork(self.app, self.namespace) + parsed_args = self.check_parser(cmd, arglist, verifylist) + + self.assertRaises( + AttributeError, + cmd.take_action, + parsed_args, + ) + class TestDeleteNetwork(common.TestNetworkBase): def test_delete(self): |
