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
21
22from openstackclient.i18n import _
23from openstackclient.identity import common as common_utils
24
25LOG = logging.getLogger(__name__)
26
27
28class CreateRegisteredLimit(command.ShowOne):
29    _description = _("Create a registered limit")
30
31    def get_parser(self, prog_name):
32        parser = super(CreateRegisteredLimit, self).get_parser(prog_name)
33        parser.add_argument(
34            '--description',
35            metavar='<description>',
36            help=_('Description of the registered limit'),
37        )
38        parser.add_argument(
39            '--region',
40            metavar='<region>',
41            help=_('Region for the registered limit to affect'),
42        )
43        parser.add_argument(
44            '--service',
45            metavar='<service>',
46            required=True,
47            help=_('Service responsible for the resource to limit (required)'),
48        )
49        parser.add_argument(
50            '--default-limit',
51            type=int,
52            metavar='<default-limit>',
53            required=True,
54            help=_('The default limit for the resources to assume (required)'),
55        )
56        parser.add_argument(
57            'resource_name',
58            metavar='<resource-name>',
59            help=_('The name of the resource to limit'),
60        )
61        return parser
62
63    def take_action(self, parsed_args):
64        identity_client = self.app.client_manager.identity
65
66        service = utils.find_resource(
67            identity_client.services, parsed_args.service
68        )
69        region = None
70        if parsed_args.region:
71            val = getattr(parsed_args, 'region', None)
72            if 'None' not in val:
73                # NOTE (vishakha): Due to bug #1799153 and for any another
74                # related case where GET resource API does not support the
75                # filter by name, osc_lib.utils.find_resource() method cannot
76                # be used because that method try to fall back to list all the
77                # resource if requested resource cannot be get via name. Which
78                # ends up with NoUniqueMatch error.
79                # So osc_lib.utils.find_resource() function cannot be used for
80                # 'regions', using common_utils.get_resource() instead.
81                region = common_utils.get_resource(
82                    identity_client.regions, parsed_args.region
83                )
84
85        registered_limit = identity_client.registered_limits.create(
86            service,
87            parsed_args.resource_name,
88            parsed_args.default_limit,
89            description=parsed_args.description,
90            region=region
91        )
92
93        registered_limit._info.pop('links', None)
94        return zip(*sorted(registered_limit._info.items()))
95
96
97class DeleteRegisteredLimit(command.Command):
98    _description = _("Delete a registered limit")
99
100    def get_parser(self, prog_name):
101        parser = super(DeleteRegisteredLimit, self).get_parser(prog_name)
102        parser.add_argument(
103            'registered_limit_id',
104            metavar='<registered-limit-id>',
105            nargs="+",
106            help=_('Registered limit to delete (ID)'),
107        )
108        return parser
109
110    def take_action(self, parsed_args):
111        identity_client = self.app.client_manager.identity
112
113        errors = 0
114        for registered_limit_id in parsed_args.registered_limit_id:
115            try:
116                identity_client.registered_limits.delete(registered_limit_id)
117            except Exception as e:
118                errors += 1
119                from pprint import pprint
120                pprint(type(e))
121                LOG.error(_("Failed to delete registered limit with ID "
122                            "'%(id)s': %(e)s"),
123                          {'id': registered_limit_id, 'e': e})
124
125        if errors > 0:
126            total = len(parsed_args.registered_limit_id)
127            msg = (_("%(errors)s of %(total)s registered limits failed to "
128                   "delete.") % {'errors': errors, 'total': total})
129            raise exceptions.CommandError(msg)
130
131
132class ListRegisteredLimit(command.Lister):
133    _description = _("List registered limits")
134
135    def get_parser(self, prog_name):
136        parser = super(ListRegisteredLimit, self).get_parser(prog_name)
137        parser.add_argument(
138            '--service',
139            metavar='<service>',
140            help=_('Service responsible for the resource to limit'),
141        )
142        parser.add_argument(
143            '--resource-name',
144            metavar='<resource-name>',
145            dest='resource_name',
146            help=_('The name of the resource to limit'),
147        )
148        parser.add_argument(
149            '--region',
150            metavar='<region>',
151            help=_('Region for the limit to affect.'),
152        )
153        return parser
154
155    def take_action(self, parsed_args):
156        identity_client = self.app.client_manager.identity
157
158        service = None
159        if parsed_args.service:
160            service = common_utils.find_service(
161                identity_client, parsed_args.service
162            )
163        region = None
164        if parsed_args.region:
165            val = getattr(parsed_args, 'region', None)
166            if 'None' not in val:
167                # NOTE (vishakha): Due to bug #1799153 and for any another
168                # related case where GET resource API does not support the
169                # filter by name, osc_lib.utils.find_resource() method cannot
170                # be used because that method try to fall back to list all the
171                # resource if requested resource cannot be get via name. Which
172                # ends up with NoUniqueMatch error.
173                # So osc_lib.utils.find_resource() function cannot be used for
174                # 'regions', using common_utils.get_resource() instead.
175                region = common_utils.get_resource(
176                    identity_client.regions, parsed_args.region
177                )
178
179        registered_limits = identity_client.registered_limits.list(
180            service=service,
181            resource_name=parsed_args.resource_name,
182            region=region
183        )
184
185        columns = (
186            'ID', 'Service ID', 'Resource Name', 'Default Limit',
187            'Description', 'Region ID'
188        )
189        return (
190            columns,
191            (utils.get_item_properties(s, columns) for s in registered_limits),
192        )
193
194
195class SetRegisteredLimit(command.ShowOne):
196    _description = _("Update information about a registered limit")
197
198    def get_parser(self, prog_name):
199        parser = super(SetRegisteredLimit, self).get_parser(prog_name)
200        parser.add_argument(
201            'registered_limit_id',
202            metavar='<registered-limit-id>',
203            help=_('Registered limit to update (ID)'),
204        )
205        parser.add_argument(
206            '--service',
207            metavar='<service>',
208            help=_('Service to be updated responsible for the resource to '
209                   'limit. Either --service, --resource-name or --region must '
210                   'be different than existing value otherwise it will be '
211                   'duplicate entry')
212        )
213        parser.add_argument(
214            '--resource-name',
215            metavar='<resource-name>',
216            help=_('Resource to be updated responsible for the resource to '
217                   'limit. Either --service, --resource-name or --region must '
218                   'be different than existing value otherwise it will be '
219                   'duplicate entry'),
220        )
221        parser.add_argument(
222            '--default-limit',
223            metavar='<default-limit>',
224            type=int,
225            help=_('The default limit for the resources to assume'),
226        )
227        parser.add_argument(
228            '--description',
229            metavar='<description>',
230            help=_('Description to update of the registered limit'),
231        )
232        parser.add_argument(
233            '--region',
234            metavar='<region>',
235            help=_('Region for the registered limit to affect. Either '
236                   '--service, --resource-name or --region must be '
237                   'different than existing value otherwise it will be '
238                   'duplicate entry'),
239        )
240        return parser
241
242    def take_action(self, parsed_args):
243        identity_client = self.app.client_manager.identity
244
245        service = None
246        if parsed_args.service:
247            service = common_utils.find_service(
248                identity_client, parsed_args.service
249            )
250
251        region = None
252        if parsed_args.region:
253            val = getattr(parsed_args, 'region', None)
254            if 'None' not in val:
255                # NOTE (vishakha): Due to bug #1799153 and for any another
256                # related case where GET resource API does not support the
257                # filter by name, osc_lib.utils.find_resource() method cannot
258                # be used because that method try to fall back to list all the
259                # resource if requested resource cannot be get via name. Which
260                # ends up with NoUniqueMatch error.
261                # So osc_lib.utils.find_resource() function cannot be used for
262                # 'regions', using common_utils.get_resource() instead.
263                region = common_utils.get_resource(
264                    identity_client.regions, parsed_args.region
265                )
266
267        registered_limit = identity_client.registered_limits.update(
268            parsed_args.registered_limit_id,
269            service=service,
270            resource_name=parsed_args.resource_name,
271            default_limit=parsed_args.default_limit,
272            description=parsed_args.description,
273            region=region
274        )
275
276        registered_limit._info.pop('links', None)
277        return zip(*sorted(registered_limit._info.items()))
278
279
280class ShowRegisteredLimit(command.ShowOne):
281    _description = _("Display registered limit details")
282
283    def get_parser(self, prog_name):
284        parser = super(ShowRegisteredLimit, self).get_parser(prog_name)
285        parser.add_argument(
286            'registered_limit_id',
287            metavar='<registered-limit-id>',
288            help=_('Registered limit to display (ID)'),
289        )
290        return parser
291
292    def take_action(self, parsed_args):
293        identity_client = self.app.client_manager.identity
294        registered_limit = identity_client.registered_limits.get(
295            parsed_args.registered_limit_id
296        )
297        registered_limit._info.pop('links', None)
298        return zip(*sorted(registered_limit._info.items()))
299