Merge "Add parent field to project creation"

This commit is contained in:
Jenkins 2015-04-30 03:16:20 +00:00 committed by Gerrit Code Review
commit 634004f33e
4 changed files with 129 additions and 2 deletions
doc/source/command-objects
openstackclient
identity/v3
tests/identity/v3

@ -25,6 +25,12 @@ Create new project
.. versionadded:: 3 .. versionadded:: 3
.. option:: --parent <project>
Parent of the project (name or ID)
.. versionadded:: 3
.. option:: --description <description> .. option:: --description <description>
Project description Project description

@ -46,6 +46,11 @@ class CreateProject(show.ShowOne):
metavar='<domain>', metavar='<domain>',
help='Domain owning the project (name or ID)', help='Domain owning the project (name or ID)',
) )
parser.add_argument(
'--parent',
metavar='<project>',
help='Parent of the project (name or ID)',
)
parser.add_argument( parser.add_argument(
'--description', '--description',
metavar='<description>', metavar='<description>',
@ -85,6 +90,13 @@ class CreateProject(show.ShowOne):
domain = common.find_domain(identity_client, domain = common.find_domain(identity_client,
parsed_args.domain).id parsed_args.domain).id
parent = None
if parsed_args.parent:
parent = utils.find_resource(
identity_client.projects,
parsed_args.parent,
).id
enabled = True enabled = True
if parsed_args.disable: if parsed_args.disable:
enabled = False enabled = False
@ -96,6 +108,7 @@ class CreateProject(show.ShowOne):
project = identity_client.projects.create( project = identity_client.projects.create(
name=parsed_args.name, name=parsed_args.name,
domain=domain, domain=domain,
parent=parent,
description=parsed_args.description, description=parsed_args.description,
enabled=enabled, enabled=enabled,
**kwargs **kwargs
@ -110,8 +123,6 @@ class CreateProject(show.ShowOne):
raise e raise e
project._info.pop('links') project._info.pop('links')
# TODO(stevemar): Remove the line below when we support multitenancy
project._info.pop('parent_id', None)
return zip(*sorted(six.iteritems(project._info))) return zip(*sorted(six.iteritems(project._info)))

@ -135,6 +135,16 @@ REGION = {
'links': base_url + 'regions/' + region_id, 'links': base_url + 'regions/' + region_id,
} }
PROJECT_WITH_PARENT = {
'id': project_id + '-with-parent',
'name': project_name + ' and their parents',
'description': project_description + ' plus another four',
'enabled': True,
'domain_id': domain_id,
'parent_id': project_id,
'links': base_url + 'projects/' + (project_id + '-with-parent'),
}
role_id = 'r1' role_id = 'r1'
role_name = 'roller' role_name = 'roller'

@ -16,6 +16,7 @@
import copy import copy
import mock import mock
from openstackclient.common import exceptions
from openstackclient.identity.v3 import project from openstackclient.identity.v3 import project
from openstackclient.tests import fakes from openstackclient.tests import fakes
from openstackclient.tests.identity.v3 import fakes as identity_fakes from openstackclient.tests.identity.v3 import fakes as identity_fakes
@ -60,6 +61,7 @@ class TestProjectCreate(TestProject):
identity_fakes.project_name, identity_fakes.project_name,
] ]
verifylist = [ verifylist = [
('parent', None),
('enable', False), ('enable', False),
('disable', False), ('disable', False),
('name', identity_fakes.project_name), ('name', identity_fakes.project_name),
@ -75,6 +77,7 @@ class TestProjectCreate(TestProject):
'domain': None, 'domain': None,
'description': None, 'description': None,
'enabled': True, 'enabled': True,
'parent': None,
} }
# ProjectManager.create(name=, domain=, description=, # ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs) # enabled=, **kwargs)
@ -103,6 +106,7 @@ class TestProjectCreate(TestProject):
('enable', False), ('enable', False),
('disable', False), ('disable', False),
('name', identity_fakes.project_name), ('name', identity_fakes.project_name),
('parent', None),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -115,6 +119,7 @@ class TestProjectCreate(TestProject):
'domain': None, 'domain': None,
'description': 'new desc', 'description': 'new desc',
'enabled': True, 'enabled': True,
'parent': None,
} }
# ProjectManager.create(name=, domain=, description=, # ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs) # enabled=, **kwargs)
@ -143,6 +148,7 @@ class TestProjectCreate(TestProject):
('enable', False), ('enable', False),
('disable', False), ('disable', False),
('name', identity_fakes.project_name), ('name', identity_fakes.project_name),
('parent', None),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -155,6 +161,7 @@ class TestProjectCreate(TestProject):
'domain': identity_fakes.domain_id, 'domain': identity_fakes.domain_id,
'description': None, 'description': None,
'enabled': True, 'enabled': True,
'parent': None,
} }
# ProjectManager.create(name=, domain=, description=, # ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs) # enabled=, **kwargs)
@ -183,6 +190,7 @@ class TestProjectCreate(TestProject):
('enable', False), ('enable', False),
('disable', False), ('disable', False),
('name', identity_fakes.project_name), ('name', identity_fakes.project_name),
('parent', None),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mocker = mock.Mock() mocker = mock.Mock()
@ -197,6 +205,7 @@ class TestProjectCreate(TestProject):
'domain': identity_fakes.domain_id, 'domain': identity_fakes.domain_id,
'description': None, 'description': None,
'enabled': True, 'enabled': True,
'parent': None,
} }
self.projects_mock.create.assert_called_with( self.projects_mock.create.assert_called_with(
**kwargs **kwargs
@ -221,6 +230,7 @@ class TestProjectCreate(TestProject):
('enable', True), ('enable', True),
('disable', False), ('disable', False),
('name', identity_fakes.project_name), ('name', identity_fakes.project_name),
('parent', None),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -233,6 +243,7 @@ class TestProjectCreate(TestProject):
'domain': None, 'domain': None,
'description': None, 'description': None,
'enabled': True, 'enabled': True,
'parent': None,
} }
# ProjectManager.create(name=, domain=, description=, # ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs) # enabled=, **kwargs)
@ -260,6 +271,7 @@ class TestProjectCreate(TestProject):
('enable', False), ('enable', False),
('disable', True), ('disable', True),
('name', identity_fakes.project_name), ('name', identity_fakes.project_name),
('parent', None),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -272,6 +284,7 @@ class TestProjectCreate(TestProject):
'domain': None, 'domain': None,
'description': None, 'description': None,
'enabled': False, 'enabled': False,
'parent': None,
} }
# ProjectManager.create(name=, domain=, # ProjectManager.create(name=, domain=,
# description=, enabled=, **kwargs) # description=, enabled=, **kwargs)
@ -311,6 +324,7 @@ class TestProjectCreate(TestProject):
'domain': None, 'domain': None,
'description': None, 'description': None,
'enabled': True, 'enabled': True,
'parent': None,
'fee': 'fi', 'fee': 'fi',
'fo': 'fum', 'fo': 'fum',
} }
@ -331,6 +345,92 @@ class TestProjectCreate(TestProject):
) )
self.assertEqual(datalist, data) self.assertEqual(datalist, data)
def test_project_create_parent(self):
self.projects_mock.get.return_value = fakes.FakeResource(
None,
copy.deepcopy(identity_fakes.PROJECT),
loaded=True,
)
self.projects_mock.create.return_value = fakes.FakeResource(
None,
copy.deepcopy(identity_fakes.PROJECT_WITH_PARENT),
loaded=True,
)
arglist = [
'--domain', identity_fakes.PROJECT_WITH_PARENT['domain_id'],
'--parent', identity_fakes.PROJECT['name'],
identity_fakes.PROJECT_WITH_PARENT['name'],
]
verifylist = [
('domain', identity_fakes.PROJECT_WITH_PARENT['domain_id']),
('parent', identity_fakes.PROJECT['name']),
('enable', False),
('disable', False),
('name', identity_fakes.PROJECT_WITH_PARENT['name']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
kwargs = {
'name': identity_fakes.PROJECT_WITH_PARENT['name'],
'domain': identity_fakes.PROJECT_WITH_PARENT['domain_id'],
'parent': identity_fakes.PROJECT['id'],
'description': None,
'enabled': True,
}
self.projects_mock.create.assert_called_with(
**kwargs
)
collist = (
'description',
'domain_id',
'enabled',
'id',
'name',
'parent_id',
)
self.assertEqual(columns, collist)
datalist = (
identity_fakes.PROJECT_WITH_PARENT['description'],
identity_fakes.PROJECT_WITH_PARENT['domain_id'],
identity_fakes.PROJECT_WITH_PARENT['enabled'],
identity_fakes.PROJECT_WITH_PARENT['id'],
identity_fakes.PROJECT_WITH_PARENT['name'],
identity_fakes.PROJECT['id'],
)
self.assertEqual(data, datalist)
def test_project_create_invalid_parent(self):
self.projects_mock.resource_class.__name__ = 'Project'
self.projects_mock.get.side_effect = exceptions.NotFound(
'Invalid parent')
self.projects_mock.find.side_effect = exceptions.NotFound(
'Invalid parent')
arglist = [
'--domain', identity_fakes.domain_name,
'--parent', 'invalid',
identity_fakes.project_name,
]
verifylist = [
('domain', identity_fakes.domain_name),
('parent', 'invalid'),
('enable', False),
('disable', False),
('name', identity_fakes.project_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
class TestProjectDelete(TestProject): class TestProjectDelete(TestProject):