summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-07-25 00:52:41 +0000
committerGerrit Code Review <review@openstack.org>2018-07-25 00:52:41 +0000
commitd177727a6e51500170338b4fd5a7e98179ff968f (patch)
tree0a504cec872f9db072e54755184bd7772a5a6269
parente78a2e7b0937c69ca5dfbbe0f0d2117fc13195a1 (diff)
parent641a4faac1d021518350925b4297677eeff98fde (diff)
Merge "Implement support for registered limits"
-rw-r--r--doc/source/cli/command-objects/registered-limit.rst140
-rw-r--r--lower-constraints.txt2
-rw-r--r--openstackclient/identity/v3/registered_limit.py260
-rw-r--r--openstackclient/tests/functional/identity/v3/common.py37
-rw-r--r--openstackclient/tests/functional/identity/v3/test_registered_limit.py184
-rw-r--r--openstackclient/tests/unit/identity/v3/fakes.py23
-rw-r--r--openstackclient/tests/unit/identity/v3/test_registered_limit.py510
-rw-r--r--releasenotes/notes/bp-unified-limits-58f166401534a4ff.yaml7
-rw-r--r--requirements.txt2
-rw-r--r--setup.cfg6
10 files changed, 1169 insertions, 2 deletions
diff --git a/doc/source/cli/command-objects/registered-limit.rst b/doc/source/cli/command-objects/registered-limit.rst
new file mode 100644
index 0000000..586fd1f
--- /dev/null
+++ b/doc/source/cli/command-objects/registered-limit.rst
@@ -0,0 +1,140 @@
1================
2registered limit
3================
4
5Identity v3
6
7Registered limits are used to define default limits for resources within a
8deployment.
9
10registered limit create
11-----------------------
12
13Create a new registered limit
14
15.. program:: registered limit create
16.. code:: bash
17
18 openstack registered limit create
19 [--description <description>]
20 [--region <region>]
21 --service <service>
22 --default-limit <default-limit>
23 <resource-name>
24
25.. option:: --description <description>
26
27 Useful description of the registered limit or its purpose
28
29.. option:: --region <region>
30
31 Region that the limit should be applied to
32
33.. describe:: --service <service>
34
35 The service that is responsible for the resource being limited (required)
36
37.. describe:: --default-limit <default-limit>
38
39 The default limit for projects to assume unless explicitly overridden
40 (required)
41
42.. describe:: <resource-name>
43
44 The name of the resource to limit (e.g. cores or volumes)
45
46registered limit delete
47-----------------------
48
49Delete registered limit(s)
50
51.. program:: registered limit delete
52.. code:: bash
53
54 openstack registered limit delete
55 <registered-limit-id> [<registered-limit-id> ...]
56
57
58.. describe:: <registered-limit-id>
59
60 Registered limit(s) to delete (ID)
61
62registered limit list
63---------------------
64
65List registered limits
66
67.. program:: registered limit list
68.. code:: bash
69
70 openstack registered limit list
71 [--service <service>]
72 [--resource-name <resource-name>]
73 [--region <region>]
74
75.. option:: --service <service>
76
77 The service to filter the response by (name or ID)
78
79.. option:: --resource-name <resource-name>
80
81 The name of the resource to filter the response by
82
83.. option:: --region <region>
84
85 The region name to filter the response by
86
87registered limit show
88---------------------
89
90Display details about a registered limit
91
92.. program:: registered limit show
93.. code:: bash
94
95 openstack registered limit show
96 <registered-limit-id>
97
98.. describe:: <registered-limit-id>
99
100 Registered limit to display (ID)
101
102registered limit set
103--------------------
104
105Update a registered limit
106
107.. program:: registered limit set
108.. code:: bash
109
110 openstack registered limit set
111 [--service <service>]
112 [--resource-name <resource-name>]
113 [--default-limit <default-limit>]
114 [--description <description>]
115 [--region <region>]
116 <registered-limit-id>
117
118.. option:: --service <service>
119
120 The service of the registered limit to update (ID or name)
121
122.. option:: --resource-name <resource-name>
123
124 The name of the resource for the limit
125
126.. option:: --default-limit <default-limit>
127
128 The default limit for projects to assume for a given resource
129
130.. option:: --description <description>
131
132 A useful description of the limit or its purpose
133
134.. option:: --region <region>
135
136 The region the limit should apply to
137
138.. describe:: <registered-limit-id>
139
140 Registered limit to display (ID)
diff --git a/lower-constraints.txt b/lower-constraints.txt
index 5951937..6cb1b44 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -95,7 +95,7 @@ python-heatclient==1.10.0
95python-ironic-inspector-client==1.5.0 95python-ironic-inspector-client==1.5.0
96python-ironicclient==2.3.0 96python-ironicclient==2.3.0
97python-karborclient==0.6.0 97python-karborclient==0.6.0
98python-keystoneclient==3.15.0 98python-keystoneclient==3.17.0
99python-mimeparse==1.6.0 99python-mimeparse==1.6.0
100python-mistralclient==3.1.0 100python-mistralclient==3.1.0
101python-muranoclient==0.8.2 101python-muranoclient==0.8.2
diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py
new file mode 100644
index 0000000..72e0729
--- /dev/null
+++ b/openstackclient/identity/v3/registered_limit.py
@@ -0,0 +1,260 @@
1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12#
13
14"""Registered limits action implementations."""
15
16import logging
17
18from osc_lib.command import command
19from osc_lib import exceptions
20from osc_lib import utils
21import six
22
23from openstackclient.i18n import _
24from openstackclient.identity import common as common_utils
25
26LOG = logging.getLogger(__name__)
27
28
29class CreateRegisteredLimit(command.ShowOne):
30 _description = _("Create a registered limit")
31
32 def get_parser(self, prog_name):
33 parser = super(CreateRegisteredLimit, self).get_parser(prog_name)
34 parser.add_argument(
35 '--description',
36 metavar='<description>',
37 help=_('Description of the registered limit'),
38 )
39 parser.add_argument(
40 '--region',
41 metavar='<region>',
42 help=_('Region for the registered limit to affect'),
43 )
44 parser.add_argument(
45 '--service',
46 metavar='<service>',
47 required=True,
48 help=_('Service responsible for the resource to limit (required)'),
49 )
50 parser.add_argument(
51 '--default-limit',
52 type=int,
53 metavar='<default-limit>',
54 required=True,
55 help=_('The default limit for the resources to assume (required)'),
56 )
57 parser.add_argument(
58 'resource_name',
59 metavar='<resource-name>',
60 help=_('The name of the resource to limit'),
61 )
62 return parser
63
64 def take_action(self, parsed_args):
65 identity_client = self.app.client_manager.identity
66
67 service = utils.find_resource(
68 identity_client.services, parsed_args.service
69 )
70 region = None
71 if parsed_args.region:
72 region = utils.find_resource(
73 identity_client.regions, parsed_args.region
74 )
75
76 registered_limit = identity_client.registered_limits.create(
77 service,
78 parsed_args.resource_name,
79 parsed_args.default_limit,
80 description=parsed_args.description,
81 region=region
82 )
83
84 registered_limit._info.pop('links', None)
85 return zip(*sorted(six.iteritems(registered_limit._info)))
86
87
88class DeleteRegisteredLimit(command.Command):
89 _description = _("Delete a registered limit")
90
91 def get_parser(self, prog_name):
92 parser = super(DeleteRegisteredLimit, self).get_parser(prog_name)
93 parser.add_argument(
94 'registered_limit_id',
95 metavar='<registered-limit-id>',
96 nargs="+",
97 help=_('Registered limit to delete (ID)'),
98 )
99 return parser
100
101 def take_action(self, parsed_args):
102 identity_client = self.app.client_manager.identity
103
104 errors = 0
105 for registered_limit_id in parsed_args.registered_limit_id:
106 try:
107 identity_client.registered_limits.delete(registered_limit_id)
108 except Exception as e:
109 errors += 1
110 from pprint import pprint
111 pprint(type(e))
112 LOG.error(_("Failed to delete registered limit with ID "
113 "'%(id)s': %(e)s"),
114 {'id': registered_limit_id, 'e': e})
115
116 if errors > 0:
117 total = len(parsed_args.registered_limit_id)
118 msg = (_("%(errors)s of %(total)s registered limits failed to "
119 "delete.") % {'errors': errors, 'total': total})
120 raise exceptions.CommandError(msg)
121
122
123class ListRegisteredLimit(command.Lister):
124 _description = _("List registered limits")
125
126 def get_parser(self, prog_name):
127 parser = super(ListRegisteredLimit, self).get_parser(prog_name)
128 parser.add_argument(
129 '--service',
130 metavar='<service>',
131 help=_('Service responsible for the resource to limit'),
132 )
133 parser.add_argument(
134 '--resource-name',
135 metavar='<resource-name>',
136 dest='resource_name',
137 help=_('The name of the resource to limit'),
138 )
139 parser.add_argument(
140 '--region',
141 metavar='<region>',
142 help=_('Region for the limit to affect.'),
143 )
144 return parser
145
146 def take_action(self, parsed_args):
147 identity_client = self.app.client_manager.identity
148
149 service = None
150 if parsed_args.service:
151 service = common_utils.find_service(
152 identity_client, parsed_args.service
153 )
154 region = None
155 if parsed_args.region:
156 region = utils.find_resource(
157 identity_client.regions, parsed_args.region
158 )
159
160 registered_limits = identity_client.registered_limits.list(
161 service=service,
162 resource_name=parsed_args.resource_name,
163 region=region
164 )
165
166 columns = (
167 'ID', 'Service ID', 'Resource Name', 'Default Limit',
168 'Description', 'Region ID'
169 )
170 return (
171 columns,
172 (utils.get_item_properties(s, columns) for s in registered_limits),
173 )
174
175
176class SetRegisteredLimit(command.ShowOne):
177 _description = _("Update information about a registered limit")
178
179 def get_parser(self, prog_name):
180 parser = super(SetRegisteredLimit, self).get_parser(prog_name)
181 parser.add_argument(
182 'registered_limit_id',
183 metavar='<registered-limit-id>',
184 help=_('Registered limit to update (ID)'),
185 )
186 parser.add_argument(
187 '--service',
188 metavar='<service>',
189 help=_('Service responsible for the resource to limit'),
190 )
191 parser.add_argument(
192 '--resource-name',
193 metavar='<resource-name>',
194 help=_('The name of the resource to limit'),
195 )
196 parser.add_argument(
197 '--default-limit',
198 metavar='<default-limit>',
199 type=int,
200 help=_('The default limit for the resources to assume'),
201 )
202 parser.add_argument(
203 '--description',
204 metavar='<description>',
205 help=_('Description of the registered limit'),
206 )
207 parser.add_argument(
208 '--region',
209 metavar='<region>',
210 help=_('Region for the registered limit to affect.'),
211 )
212 return parser
213
214 def take_action(self, parsed_args):
215 identity_client = self.app.client_manager.identity
216
217 service = None
218 if parsed_args.service:
219 service = common_utils.find_service(
220 identity_client, parsed_args.service
221 )
222
223 region = None
224 if parsed_args.region:
225 region = utils.find_resource(
226 identity_client.regions, parsed_args.region
227 )
228
229 registered_limit = identity_client.registered_limits.update(
230 parsed_args.registered_limit_id,
231 service=service,
232 resource_name=parsed_args.resource_name,
233 default_limit=parsed_args.default_limit,
234 description=parsed_args.description,
235 region=region
236 )
237
238 registered_limit._info.pop('links', None)
239 return zip(*sorted(six.iteritems(registered_limit._info)))
240
241
242class ShowRegisteredLimit(command.ShowOne):
243 _description = _("Display registered limit details")
244
245 def get_parser(self, prog_name):
246 parser = super(ShowRegisteredLimit, self).get_parser(prog_name)
247 parser.add_argument(
248 'registered_limit_id',
249 metavar='<registered-limit-id>',
250 help=_('Registered limit to display (ID)'),
251 )
252 return parser
253
254 def take_action(self, parsed_args):
255 identity_client = self.app.client_manager.identity
256 registered_limit = identity_client.registered_limits.get(
257 parsed_args.registered_limit_id
258 )
259 registered_limit._info.pop('links', None)
260 return zip(*sorted(six.iteritems(registered_limit._info)))
diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py
index 54132be..525a31a 100644
--- a/openstackclient/tests/functional/identity/v3/common.py
+++ b/openstackclient/tests/functional/identity/v3/common.py
@@ -54,6 +54,11 @@ class IdentityTests(base.TestCase):
54 'Auth URL'] 54 'Auth URL']
55 IMPLIED_ROLE_LIST_HEADERS = ['Prior Role ID', 'Prior Role Name', 55 IMPLIED_ROLE_LIST_HEADERS = ['Prior Role ID', 'Prior Role Name',
56 'Implied Role ID', 'Implied Role Name'] 56 'Implied Role ID', 'Implied Role Name']
57 REGISTERED_LIMIT_FIELDS = ['id', 'service_id', 'resource_name',
58 'default_limit', 'description', 'region_id']
59 REGISTERED_LIMIT_LIST_HEADERS = ['ID', 'Service ID', 'Resource Name',
60 'Default Limit', 'Description',
61 'Region ID']
57 62
58 @classmethod 63 @classmethod
59 def setUpClass(cls): 64 def setUpClass(cls):
@@ -319,3 +324,35 @@ class IdentityTests(base.TestCase):
319 items = self.parse_show(raw_output) 324 items = self.parse_show(raw_output)
320 self.assert_show_fields(items, self.SERVICE_PROVIDER_FIELDS) 325 self.assert_show_fields(items, self.SERVICE_PROVIDER_FIELDS)
321 return service_provider 326 return service_provider
327
328 def _create_dummy_registered_limit(self, add_clean_up=True):
329 service_name = self._create_dummy_service()
330 resource_name = data_utils.rand_name('resource_name')
331 params = {
332 'service_name': service_name,
333 'default_limit': 10,
334 'resource_name': resource_name
335 }
336 raw_output = self.openstack(
337 'registered limit create'
338 ' --service %(service_name)s'
339 ' --default-limit %(default_limit)s'
340 ' %(resource_name)s' % params
341 )
342 items = self.parse_show(raw_output)
343 registered_limit_id = self._extract_value_from_items('id', items)
344
345 if add_clean_up:
346 self.addCleanup(
347 self.openstack,
348 'registered limit delete %s' % registered_limit_id
349 )
350
351 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
352 return registered_limit_id
353
354 def _extract_value_from_items(self, key, items):
355 for d in items:
356 for k, v in d.iteritems():
357 if k == key:
358 return v
diff --git a/openstackclient/tests/functional/identity/v3/test_registered_limit.py b/openstackclient/tests/functional/identity/v3/test_registered_limit.py
new file mode 100644
index 0000000..09e90ce
--- /dev/null
+++ b/openstackclient/tests/functional/identity/v3/test_registered_limit.py
@@ -0,0 +1,184 @@
1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13from tempest.lib.common.utils import data_utils
14
15from openstackclient.tests.functional.identity.v3 import common
16
17
18class RegisteredLimitTestCase(common.IdentityTests):
19
20 def test_registered_limit_create_with_service_name(self):
21 self._create_dummy_registered_limit()
22
23 def test_registered_limit_create_with_service_id(self):
24 service_name = self._create_dummy_service()
25 raw_output = self.openstack(
26 'service show'
27 ' %(service_name)s' % {'service_name': service_name}
28 )
29 service_items = self.parse_show(raw_output)
30 service_id = self._extract_value_from_items('id', service_items)
31
32 raw_output = self.openstack(
33 'registered limit create'
34 ' --service %(service_id)s'
35 ' --default-limit %(default_limit)s'
36 ' %(resource_name)s' % {
37 'service_id': service_id,
38 'default_limit': 10,
39 'resource_name': 'cores'
40 }
41 )
42 items = self.parse_show(raw_output)
43 registered_limit_id = self._extract_value_from_items('id', items)
44 self.addCleanup(
45 self.openstack,
46 'registered limit delete'
47 ' %(registered_limit_id)s' % {
48 'registered_limit_id': registered_limit_id
49 }
50 )
51
52 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
53
54 def test_registered_limit_create_with_options(self):
55 service_name = self._create_dummy_service()
56 region_id = self._create_dummy_region()
57 params = {
58 'service_name': service_name,
59 'resource_name': 'cores',
60 'default_limit': 10,
61 'description': 'default limit for cores',
62 'region_id': region_id
63 }
64
65 raw_output = self.openstack(
66 'registered limit create'
67 ' --description \'%(description)s\''
68 ' --region %(region_id)s'
69 ' --service %(service_name)s'
70 ' --default-limit %(default_limit)s'
71 ' %(resource_name)s' % params
72 )
73 items = self.parse_show(raw_output)
74 registered_limit_id = self._extract_value_from_items('id', items)
75 self.addCleanup(
76 self.openstack,
77 'registered limit delete %(registered_limit_id)s' % {
78 'registered_limit_id': registered_limit_id
79 }
80 )
81
82 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
83
84 def test_registered_limit_show(self):
85 registered_limit_id = self._create_dummy_registered_limit()
86 raw_output = self.openstack(
87 'registered limit show %(registered_limit_id)s' % {
88 'registered_limit_id': registered_limit_id
89 }
90 )
91 items = self.parse_show(raw_output)
92 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
93
94 def test_registered_limit_set_region_id(self):
95 region_id = self._create_dummy_region()
96 registered_limit_id = self._create_dummy_registered_limit()
97
98 params = {
99 'registered_limit_id': registered_limit_id,
100 'region_id': region_id
101 }
102 raw_output = self.openstack(
103 'registered limit set'
104 ' %(registered_limit_id)s'
105 ' --region %(region_id)s' % params
106 )
107 items = self.parse_show(raw_output)
108 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
109
110 def test_registered_limit_set_description(self):
111 registered_limit_id = self._create_dummy_registered_limit()
112 params = {
113 'registered_limit_id': registered_limit_id,
114 'description': 'updated description'
115 }
116 raw_output = self.openstack(
117 'registered limit set'
118 ' %(registered_limit_id)s'
119 ' --description \'%(description)s\'' % params
120 )
121 items = self.parse_show(raw_output)
122 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
123
124 def test_registered_limit_set_service(self):
125 registered_limit_id = self._create_dummy_registered_limit()
126 service_name = self._create_dummy_service()
127 params = {
128 'registered_limit_id': registered_limit_id,
129 'service': service_name
130 }
131 raw_output = self.openstack(
132 'registered limit set'
133 ' %(registered_limit_id)s'
134 ' --service %(service)s' % params
135 )
136 items = self.parse_show(raw_output)
137 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
138
139 def test_registered_limit_set_default_limit(self):
140 registered_limit_id = self._create_dummy_registered_limit()
141 params = {
142 'registered_limit_id': registered_limit_id,
143 'default_limit': 20
144 }
145 raw_output = self.openstack(
146 'registered limit set'
147 ' %(registered_limit_id)s'
148 ' --default-limit %(default_limit)s' % params
149 )
150 items = self.parse_show(raw_output)
151 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
152
153 def test_registered_limit_set_resource_name(self):
154 registered_limit_id = self._create_dummy_registered_limit()
155 resource_name = data_utils.rand_name('resource_name')
156 params = {
157 'registered_limit_id': registered_limit_id,
158 'resource_name': resource_name
159 }
160 raw_output = self.openstack(
161 'registered limit set'
162 ' %(registered_limit_id)s'
163 ' --resource-name %(resource_name)s' % params
164 )
165 items = self.parse_show(raw_output)
166 self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS)
167
168 def test_registered_limit_list(self):
169 self._create_dummy_registered_limit()
170 raw_output = self.openstack('registered limit list')
171 items = self.parse_listing(raw_output)
172 self.assert_table_structure(items, self.REGISTERED_LIMIT_LIST_HEADERS)
173
174 def test_registered_limit_delete(self):
175 registered_limit_id = self._create_dummy_registered_limit(
176 add_clean_up=False
177 )
178 raw_output = self.openstack(
179 'registered limit delete'
180 ' %(registered_limit_id)s' % {
181 'registered_limit_id': registered_limit_id
182 }
183 )
184 self.assertEqual(0, len(raw_output))
diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py
index 7aa9cd7..3cae451 100644
--- a/openstackclient/tests/unit/identity/v3/fakes.py
+++ b/openstackclient/tests/unit/identity/v3/fakes.py
@@ -486,6 +486,27 @@ APP_CRED_OPTIONS = {
486 'secret': app_cred_secret 486 'secret': app_cred_secret
487} 487}
488 488
489registered_limit_id = 'registered-limit-id'
490registered_limit_default_limit = 10
491registered_limit_description = 'default limit of foobars'
492registered_limit_resource_name = 'foobars'
493REGISTERED_LIMIT = {
494 'id': registered_limit_id,
495 'default_limit': registered_limit_default_limit,
496 'resource_name': registered_limit_resource_name,
497 'service_id': service_id,
498 'description': None,
499 'region_id': None
500}
501REGISTERED_LIMIT_OPTIONS = {
502 'id': registered_limit_id,
503 'default_limit': registered_limit_default_limit,
504 'resource_name': registered_limit_resource_name,
505 'service_id': service_id,
506 'description': registered_limit_description,
507 'region_id': region_id
508}
509
489 510
490def fake_auth_ref(fake_token, fake_service=None): 511def fake_auth_ref(fake_token, fake_service=None):
491 """Create an auth_ref using keystoneauth's fixtures""" 512 """Create an auth_ref using keystoneauth's fixtures"""
@@ -578,6 +599,8 @@ class FakeIdentityv3Client(object):
578 {}) 599 {})
579 self.inference_rules = mock.Mock() 600 self.inference_rules = mock.Mock()
580 self.inference_rules.resource_class = fakes.FakeResource(None, {}) 601 self.inference_rules.resource_class = fakes.FakeResource(None, {})
602 self.registered_limits = mock.Mock()
603 self.registered_limits.resource_class = fakes.FakeResource(None, {})
581 604
582 605
583class FakeFederationManager(object): 606class FakeFederationManager(object):
diff --git a/openstackclient/tests/unit/identity/v3/test_registered_limit.py b/openstackclient/tests/unit/identity/v3/test_registered_limit.py
new file mode 100644
index 0000000..262ca4f
--- /dev/null
+++ b/openstackclient/tests/unit/identity/v3/test_registered_limit.py
@@ -0,0 +1,510 @@
1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13import copy
14
15from keystoneauth1.exceptions import http as ksa_exceptions
16from osc_lib import exceptions
17
18from openstackclient.identity.v3 import registered_limit
19from openstackclient.tests.unit import fakes
20from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
21
22
23class TestRegisteredLimit(identity_fakes.TestIdentityv3):
24
25 def setUp(self):
26 super(TestRegisteredLimit, self).setUp()
27
28 identity_manager = self.app.client_manager.identity
29 self.registered_limit_mock = identity_manager.registered_limits
30
31 self.services_mock = identity_manager.services
32 self.services_mock.reset_mock()
33
34 self.regions_mock = identity_manager.regions
35 self.regions_mock.reset_mock()
36
37
38class TestRegisteredLimitCreate(TestRegisteredLimit):
39
40 def setUp(self):
41 super(TestRegisteredLimitCreate, self).setUp()
42
43 self.service = fakes.FakeResource(
44 None,
45 copy.deepcopy(identity_fakes.SERVICE),
46 loaded=True
47 )
48 self.services_mock.get.return_value = self.service
49
50 self.region = fakes.FakeResource(
51 None,
52 copy.deepcopy(identity_fakes.REGION),
53 loaded=True
54 )
55 self.regions_mock.get.return_value = self.region
56
57 self.cmd = registered_limit.CreateRegisteredLimit(self.app, None)
58
59 def test_registered_limit_create_without_options(self):
60 self.registered_limit_mock.create.return_value = fakes.FakeResource(
61 None,
62 copy.deepcopy(identity_fakes.REGISTERED_LIMIT),
63 loaded=True
64 )
65
66 resource_name = identity_fakes.registered_limit_resource_name
67 default_limit = identity_fakes.registered_limit_default_limit
68 arglist = [
69 '--service', identity_fakes.service_id,
70 '--default-limit', '10',
71 resource_name,
72 ]
73
74 verifylist = [
75 ('service', identity_fakes.service_id),
76 ('default_limit', default_limit),
77 ('resource_name', resource_name)
78 ]
79 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
80
81 columns, data = self.cmd.take_action(parsed_args)
82
83 kwargs = {'description': None, 'region': None}
84 self.registered_limit_mock.create.assert_called_with(
85 self.service, resource_name, default_limit, **kwargs
86 )
87
88 collist = ('default_limit', 'description', 'id', 'region_id',
89 'resource_name', 'service_id')
90
91 self.assertEqual(collist, columns)
92 datalist = (
93 identity_fakes.registered_limit_default_limit,
94 None,
95 identity_fakes.registered_limit_id,
96 None,
97 identity_fakes.registered_limit_resource_name,
98 identity_fakes.service_id
99 )
100 self.assertEqual(datalist, data)
101
102 def test_registered_limit_create_with_options(self):
103 self.registered_limit_mock.create.return_value = fakes.FakeResource(
104 None,
105 copy.deepcopy(identity_fakes.REGISTERED_LIMIT_OPTIONS),
106 loaded=True
107 )
108
109 resource_name = identity_fakes.registered_limit_resource_name
110 default_limit = identity_fakes.registered_limit_default_limit
111 description = identity_fakes.registered_limit_description
112 arglist = [
113 '--region', identity_fakes.region_id,
114 '--description', description,
115 '--service', identity_fakes.service_id,
116 '--default-limit', '10',
117 resource_name
118 ]
119
120 verifylist = [
121 ('region', identity_fakes.region_id),
122 ('description', description),
123 ('service', identity_fakes.service_id),
124 ('default_limit', default_limit),
125 ('resource_name', resource_name)
126 ]
127 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
128
129 columns, data = self.cmd.take_action(parsed_args)
130
131 kwargs = {'description': description, 'region': self.region}
132 self.registered_limit_mock.create.assert_called_with(
133 self.service, resource_name, default_limit, **kwargs
134 )
135
136 collist = ('default_limit', 'description', 'id', 'region_id',
137 'resource_name', 'service_id')
138
139 self.assertEqual(collist, columns)
140 datalist = (
141 identity_fakes.registered_limit_default_limit,
142 description,
143 identity_fakes.registered_limit_id,
144 identity_fakes.region_id,
145 identity_fakes.registered_limit_resource_name,
146 identity_fakes.service_id
147 )
148 self.assertEqual(datalist, data)
149
150
151class TestRegisteredLimitDelete(TestRegisteredLimit):
152
153 def setUp(self):
154 super(TestRegisteredLimitDelete, self).setUp()
155
156 self.cmd = registered_limit.DeleteRegisteredLimit(self.app, None)
157
158 def test_registered_limit_delete(self):
159 self.registered_limit_mock.delete.return_value = None
160
161 arglist = [identity_fakes.registered_limit_id]
162 verifylist = [
163 ('registered_limit_id', [identity_fakes.registered_limit_id])
164 ]
165 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
166
167 result = self.cmd.take_action(parsed_args)
168
169 self.registered_limit_mock.delete.assert_called_with(
170 identity_fakes.registered_limit_id
171 )
172 self.assertIsNone(result)
173
174 def test_registered_limit_delete_with_exception(self):
175 return_value = ksa_exceptions.NotFound()
176 self.registered_limit_mock.delete.side_effect = return_value
177
178 arglist = ['fake-registered-limit-id']
179 verifylist = [
180 ('registered_limit_id', ['fake-registered-limit-id'])
181 ]
182 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
183
184 try:
185 self.cmd.take_action(parsed_args)
186 self.fail('CommandError should be raised.')
187 except exceptions.CommandError as e:
188 self.assertEqual(
189 '1 of 1 registered limits failed to delete.', str(e)
190 )
191
192
193class TestRegisteredLimitShow(TestRegisteredLimit):
194
195 def setUp(self):
196 super(TestRegisteredLimitShow, self).setUp()
197
198 self.registered_limit_mock.get.return_value = fakes.FakeResource(
199 None,
200 copy.deepcopy(identity_fakes.REGISTERED_LIMIT),
201 loaded=True
202 )
203
204 self.cmd = registered_limit.ShowRegisteredLimit(self.app, None)
205
206 def test_registered_limit_show(self):
207 arglist = [identity_fakes.registered_limit_id]
208 verifylist = [
209 ('registered_limit_id', identity_fakes.registered_limit_id)
210 ]
211 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
212
213 columns, data = self.cmd.take_action(parsed_args)
214
215 self.registered_limit_mock.get.assert_called_with(
216 identity_fakes.registered_limit_id
217 )
218
219 collist = (
220 'default_limit', 'description', 'id', 'region_id', 'resource_name',
221 'service_id'
222 )
223 self.assertEqual(collist, columns)
224 datalist = (
225 identity_fakes.registered_limit_default_limit,
226 None,
227 identity_fakes.registered_limit_id,
228 None,
229 identity_fakes.registered_limit_resource_name,
230 identity_fakes.service_id
231 )
232 self.assertEqual(datalist, data)
233
234
235class TestRegisteredLimitSet(TestRegisteredLimit):
236
237 def setUp(self):
238 super(TestRegisteredLimitSet, self).setUp()
239 self.cmd = registered_limit.SetRegisteredLimit(self.app, None)
240
241 def test_registered_limit_set_description(self):
242 registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT)
243 registered_limit['description'] = (
244 identity_fakes.registered_limit_description
245 )
246 self.registered_limit_mock.update.return_value = fakes.FakeResource(
247 None, registered_limit, loaded=True
248 )
249
250 arglist = [
251 '--description', identity_fakes.registered_limit_description,
252 identity_fakes.registered_limit_id
253 ]
254 verifylist = [
255 ('description', identity_fakes.registered_limit_description),
256 ('registered_limit_id', identity_fakes.registered_limit_id)
257 ]
258 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
259
260 columns, data = self.cmd.take_action(parsed_args)
261
262 self.registered_limit_mock.update.assert_called_with(
263 identity_fakes.registered_limit_id,
264 service=None,
265 resource_name=None,
266 default_limit=None,
267 description=identity_fakes.registered_limit_description,
268 region=None
269 )
270
271 collist = (
272 'default_limit', 'description', 'id', 'region_id', 'resource_name',
273 'service_id'
274 )
275 self.assertEqual(collist, columns)
276 datalist = (
277 identity_fakes.registered_limit_default_limit,
278 identity_fakes.registered_limit_description,
279 identity_fakes.registered_limit_id,
280 None,
281 identity_fakes.registered_limit_resource_name,
282 identity_fakes.service_id
283 )
284 self.assertEqual(datalist, data)
285
286 def test_registered_limit_set_default_limit(self):
287 registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT)
288 default_limit = 20
289 registered_limit['default_limit'] = default_limit
290 self.registered_limit_mock.update.return_value = fakes.FakeResource(
291 None, registered_limit, loaded=True
292 )
293
294 arglist = [
295 '--default-limit', str(default_limit),
296 identity_fakes.registered_limit_id
297 ]
298 verifylist = [
299 ('default_limit', default_limit),
300 ('registered_limit_id', identity_fakes.registered_limit_id)
301 ]
302 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
303
304 columns, data = self.cmd.take_action(parsed_args)
305
306 self.registered_limit_mock.update.assert_called_with(
307 identity_fakes.registered_limit_id,
308 service=None,
309 resource_name=None,
310 default_limit=default_limit,
311 description=None,
312 region=None
313 )
314
315 collist = (
316 'default_limit', 'description', 'id', 'region_id', 'resource_name',
317 'service_id'
318 )
319 self.assertEqual(collist, columns)
320 datalist = (
321 default_limit,
322 None,
323 identity_fakes.registered_limit_id,
324 None,
325 identity_fakes.registered_limit_resource_name,
326 identity_fakes.service_id
327 )
328 self.assertEqual(datalist, data)
329
330 def test_registered_limit_set_resource_name(self):
331 registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT)
332 resource_name = 'volumes'
333 registered_limit['resource_name'] = resource_name
334 self.registered_limit_mock.update.return_value = fakes.FakeResource(
335 None, registered_limit, loaded=True
336 )
337
338 arglist = [
339 '--resource-name', resource_name,
340 identity_fakes.registered_limit_id
341 ]
342 verifylist = [
343 ('resource_name', resource_name),
344 ('registered_limit_id', identity_fakes.registered_limit_id)
345 ]
346 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
347
348 columns, data = self.cmd.take_action(parsed_args)
349
350 self.registered_limit_mock.update.assert_called_with(
351 identity_fakes.registered_limit_id,
352 service=None,
353 resource_name=resource_name,
354 default_limit=None,
355 description=None,
356 region=None
357 )
358
359 collist = (
360 'default_limit', 'description', 'id', 'region_id', 'resource_name',
361 'service_id'
362 )
363 self.assertEqual(collist, columns)
364 datalist = (
365 identity_fakes.registered_limit_default_limit,
366 None,
367 identity_fakes.registered_limit_id,
368 None,
369 resource_name,
370 identity_fakes.service_id
371 )
372 self.assertEqual(datalist, data)
373
374 def test_registered_limit_set_service(self):
375 registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT)
376 service = identity_fakes.FakeService.create_one_service()
377 registered_limit['service_id'] = service.id
378 self.registered_limit_mock.update.return_value = fakes.FakeResource(
379 None, registered_limit, loaded=True
380 )
381 self.services_mock.get.return_value = service
382
383 arglist = [
384 '--service', service.id,
385 identity_fakes.registered_limit_id
386 ]
387 verifylist = [
388 ('service', service.id),
389 ('registered_limit_id', identity_fakes.registered_limit_id)
390 ]
391 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
392
393 columns, data = self.cmd.take_action(parsed_args)
394
395 self.registered_limit_mock.update.assert_called_with(
396 identity_fakes.registered_limit_id,
397 service=service,
398 resource_name=None,
399 default_limit=None,
400 description=None,
401 region=None
402 )
403
404 collist = (
405 'default_limit', 'description', 'id', 'region_id', 'resource_name',
406 'service_id'
407 )
408 self.assertEqual(collist, columns)
409 datalist = (
410 identity_fakes.registered_limit_default_limit,
411 None,
412 identity_fakes.registered_limit_id,
413 None,
414 identity_fakes.registered_limit_resource_name,
415 service.id
416 )
417 self.assertEqual(datalist, data)
418
419 def test_registered_limit_set_region(self):
420 registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT)
421 region = identity_fakes.REGION
422 region['id'] = 'RegionTwo'
423 region = fakes.FakeResource(
424 None,
425 copy.deepcopy(region),
426 loaded=True
427 )
428 registered_limit['region_id'] = region.id
429 self.registered_limit_mock.update.return_value = fakes.FakeResource(
430 None, registered_limit, loaded=True
431 )
432 self.regions_mock.get.return_value = region
433
434 arglist = [
435 '--region', region.id,
436 identity_fakes.registered_limit_id
437 ]
438 verifylist = [
439 ('region', region.id),
440 ('registered_limit_id', identity_fakes.registered_limit_id)
441 ]
442 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
443
444 columns, data = self.cmd.take_action(parsed_args)
445
446 self.registered_limit_mock.update.assert_called_with(
447 identity_fakes.registered_limit_id,
448 service=None,
449 resource_name=None,
450 default_limit=None,
451 description=None,
452 region=region
453 )
454
455 collist = (
456 'default_limit', 'description', 'id', 'region_id', 'resource_name',
457 'service_id'
458 )
459 self.assertEqual(collist, columns)
460 datalist = (
461 identity_fakes.registered_limit_default_limit,
462 None,
463 identity_fakes.registered_limit_id,
464 region.id,
465 identity_fakes.registered_limit_resource_name,
466 identity_fakes.service_id
467 )
468 self.assertEqual(datalist, data)
469
470
471class TestRegisteredLimitList(TestRegisteredLimit):
472
473 def setUp(self):
474 super(TestRegisteredLimitList, self).setUp()
475
476 self.registered_limit_mock.get.return_value = fakes.FakeResource(
477 None,
478 copy.deepcopy(identity_fakes.REGISTERED_LIMIT),
479 loaded=True
480 )
481
482 self.cmd = registered_limit.ShowRegisteredLimit(self.app, None)
483
484 def test_limit_show(self):
485 arglist = [identity_fakes.registered_limit_id]
486 verifylist = [
487 ('registered_limit_id', identity_fakes.registered_limit_id)
488 ]
489 parsed_args = self.check_parser(self.cmd, arglist, verifylist)
490
491 columns, data = self.cmd.take_action(parsed_args)
492
493 self.registered_limit_mock.get.assert_called_with(
494 identity_fakes.registered_limit_id
495 )
496
497 collist = (
498 'default_limit', 'description', 'id', 'region_id', 'resource_name',
499 'service_id'
500 )
501 self.assertEqual(collist, columns)
502 datalist = (
503 identity_fakes.registered_limit_default_limit,
504 None,
505 identity_fakes.registered_limit_id,
506 None,
507 identity_fakes.registered_limit_resource_name,
508 identity_fakes.service_id
509 )
510 self.assertEqual(datalist, data)
diff --git a/releasenotes/notes/bp-unified-limits-58f166401534a4ff.yaml b/releasenotes/notes/bp-unified-limits-58f166401534a4ff.yaml
new file mode 100644
index 0000000..20050bb
--- /dev/null
+++ b/releasenotes/notes/bp-unified-limits-58f166401534a4ff.yaml
@@ -0,0 +1,7 @@
1---
2features:
3 - |
4 [`bp unified-limits <https://blueprints.launchpad.net/keystone/+spec/unified-limit>`_]
5 Support has been added for managing registered limits in keystone via the
6 ``registered limit`` command. Registered limits define limits of resources
7 for projects to assume by default.
diff --git a/requirements.txt b/requirements.txt
index c3c1650..2ee03a5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,6 +12,6 @@ osc-lib>=1.10.0 # Apache-2.0
12oslo.i18n>=3.15.3 # Apache-2.0 12oslo.i18n>=3.15.3 # Apache-2.0
13oslo.utils>=3.33.0 # Apache-2.0 13oslo.utils>=3.33.0 # Apache-2.0
14python-glanceclient>=2.8.0 # Apache-2.0 14python-glanceclient>=2.8.0 # Apache-2.0
15python-keystoneclient>=3.15.0 # Apache-2.0 15python-keystoneclient>=3.17.0 # Apache-2.0
16python-novaclient>=9.1.0 # Apache-2.0 16python-novaclient>=9.1.0 # Apache-2.0
17python-cinderclient>=3.3.0 # Apache-2.0 17python-cinderclient>=3.3.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index f9c0b3e..f85d709 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -301,6 +301,12 @@ openstack.identity.v3 =
301 region_set = openstackclient.identity.v3.region:SetRegion 301 region_set = openstackclient.identity.v3.region:SetRegion
302 region_show = openstackclient.identity.v3.region:ShowRegion 302 region_show = openstackclient.identity.v3.region:ShowRegion
303 303
304 registered_limit_create = openstackclient.identity.v3.registered_limit:CreateRegisteredLimit
305 registered_limit_delete = openstackclient.identity.v3.registered_limit:DeleteRegisteredLimit
306 registered_limit_list = openstackclient.identity.v3.registered_limit:ListRegisteredLimit
307 registered_limit_set = openstackclient.identity.v3.registered_limit:SetRegisteredLimit
308 registered_limit_show = openstackclient.identity.v3.registered_limit:ShowRegisteredLimit
309
304 request_token_authorize = openstackclient.identity.v3.token:AuthorizeRequestToken 310 request_token_authorize = openstackclient.identity.v3.token:AuthorizeRequestToken
305 request_token_create = openstackclient.identity.v3.token:CreateRequestToken 311 request_token_create = openstackclient.identity.v3.token:CreateRequestToken
306 312