Coverage for manila/api/v2/share_groups.py: 97%
221 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright 2015 Alex Meade
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16from http import client as http_client
18from oslo_log import log
19from oslo_utils import uuidutils
20import webob
21from webob import exc
23from manila.api import common
24from manila.api.openstack import api_version_request as api_version
25from manila.api.openstack import wsgi
26from manila.api.views import share_groups as share_group_views
27from manila import db
28from manila import exception
29from manila.i18n import _
30from manila.share import share_types
31from manila.share_group import api as share_group_api
32from manila.share_group import share_group_types
35LOG = log.getLogger(__name__)
36SG_GRADUATION_VERSION = '2.55'
39class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
40 """The Share Groups API controller for the OpenStack API."""
42 resource_name = 'share_group'
43 _view_builder_class = share_group_views.ShareGroupViewBuilder
45 def __init__(self):
46 super(ShareGroupController, self).__init__()
47 self.share_group_api = share_group_api.API()
49 def _get_share_group(self, context, share_group_id):
50 try:
51 return self.share_group_api.get(context, share_group_id)
52 except exception.NotFound:
53 msg = _("Share group %s not found.") % share_group_id
54 raise exc.HTTPNotFound(explanation=msg)
56 @wsgi.Controller.authorize('get')
57 def _show(self, req, id):
58 """Return data about the given share group."""
59 context = req.environ['manila.context']
60 share_group = self._get_share_group(context, id)
61 return self._view_builder.detail(req, share_group)
63 @wsgi.Controller.api_version('2.31', '2.54', experimental=True)
64 def show(self, req, id):
65 return self._show(req, id)
67 @wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
68 def show(self, req, id): # pylint: disable=function-redefined # noqa F811
69 return self._show(req, id)
71 @wsgi.Controller.authorize('delete')
72 def _delete_share_group(self, req, id):
73 """Delete a share group."""
74 context = req.environ['manila.context']
76 LOG.info("Delete share group with id: %s", id, context=context)
77 share_group = self._get_share_group(context, id)
78 try:
79 self.share_group_api.delete(context, share_group)
80 except exception.InvalidShareGroup as e:
81 raise exc.HTTPConflict(explanation=e.msg)
82 return webob.Response(status_int=http_client.ACCEPTED)
84 @wsgi.Controller.api_version('2.31', '2.54', experimental=True)
85 def delete(self, req, id):
86 return self._delete_share_group(req, id)
88 @wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
89 def delete(self, req, id): # pylint: disable=function-redefined # noqa F811
90 return self._delete_share_group(req, id)
92 @wsgi.Controller.api_version('2.31', '2.54', experimental=True)
93 def index(self, req):
94 return self._get_share_groups(req, is_detail=False)
96 @wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
97 def index(self, req): # pylint: disable=function-redefined # noqa F811
98 return self._get_share_groups(req, is_detail=False)
100 @wsgi.Controller.api_version('2.31', '2.54', experimental=True)
101 def detail(self, req):
102 return self._get_share_groups(req, is_detail=True)
104 @wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
105 def detail(self, req): # pylint: disable=function-redefined # noqa F811
106 return self._get_share_groups(req, is_detail=True)
108 @wsgi.Controller.authorize('get_all')
109 def _get_share_groups(self, req, is_detail):
110 """Returns a summary or detail list of share groups."""
111 context = req.environ['manila.context']
113 search_opts = {}
114 search_opts.update(req.GET)
116 # Remove keys that are not related to share group attrs
117 search_opts.pop('limit', None)
118 search_opts.pop('offset', None)
119 sort_key = search_opts.pop('sort_key', 'created_at')
120 sort_dir = search_opts.pop('sort_dir', 'desc')
121 if req.api_version_request < api_version.APIVersionRequest("2.36"):
122 search_opts.pop('name~', None)
123 search_opts.pop('description~', None)
124 if 'group_type_id' in search_opts: 124 ↛ 125line 124 didn't jump to line 125 because the condition on line 124 was never true
125 search_opts['share_group_type_id'] = search_opts.pop(
126 'group_type_id')
128 share_groups = self.share_group_api.get_all(
129 context, detailed=is_detail, search_opts=search_opts,
130 sort_dir=sort_dir, sort_key=sort_key,
131 )
133 limited_list = common.limited(share_groups, req)
135 if is_detail:
136 share_groups = self._view_builder.detail_list(req, limited_list)
137 else:
138 share_groups = self._view_builder.summary_list(req, limited_list)
139 return share_groups
141 @wsgi.Controller.authorize('update')
142 def _update_share_group(self, req, id, body):
143 """Update a share group."""
144 context = req.environ['manila.context']
146 if not self.is_valid_body(body, 'share_group'):
147 msg = _("'share_group' is missing from the request body.")
148 raise exc.HTTPBadRequest(explanation=msg)
150 share_group_data = body['share_group']
151 valid_update_keys = {'name', 'description'}
152 invalid_fields = set(share_group_data.keys()) - valid_update_keys
153 if invalid_fields:
154 msg = _("The fields %s are invalid or not allowed to be updated.")
155 raise exc.HTTPBadRequest(explanation=msg % invalid_fields)
157 share_group = self._get_share_group(context, id)
158 share_group = self.share_group_api.update(
159 context, share_group, share_group_data)
160 return self._view_builder.detail(req, share_group)
162 @wsgi.Controller.api_version('2.31', '2.54', experimental=True)
163 def update(self, req, id, body):
164 return self._update_share_group(req, id, body)
166 @wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
167 def update(self, req, id, body): # pylint: disable=function-redefined # noqa F811
168 return self._update_share_group(req, id, body)
170 @wsgi.Controller.authorize('create')
171 def _create(self, req, body):
172 """Creates a new share group."""
173 context = req.environ['manila.context']
175 if not self.is_valid_body(body, 'share_group'):
176 msg = _("'share_group' is missing from the request body.")
177 raise exc.HTTPBadRequest(explanation=msg)
179 share_group = body['share_group']
180 valid_fields = {
181 'name',
182 'description',
183 'share_types',
184 'share_group_type_id',
185 'source_share_group_snapshot_id',
186 'share_network_id',
187 'availability_zone',
188 }
189 invalid_fields = set(share_group.keys()) - valid_fields
190 if invalid_fields:
191 msg = _("The fields %s are invalid.") % invalid_fields
192 raise exc.HTTPBadRequest(explanation=msg)
194 if ('share_types' in share_group and
195 'source_share_group_snapshot_id' in share_group):
196 msg = _("Cannot supply both 'share_types' and "
197 "'source_share_group_snapshot_id' attributes.")
198 raise exc.HTTPBadRequest(explanation=msg)
200 if not (share_group.get('share_types') or
201 'source_share_group_snapshot_id' in share_group):
202 default_share_type = share_types.get_default_share_type()
203 if default_share_type:
204 share_group['share_types'] = [default_share_type['id']]
205 else:
206 msg = _("Must specify at least one share type as a default "
207 "share type has not been configured.")
208 raise exc.HTTPBadRequest(explanation=msg)
210 kwargs = {}
212 if 'name' in share_group:
213 kwargs['name'] = share_group.get('name')
214 if 'description' in share_group:
215 kwargs['description'] = share_group.get('description')
217 _share_types = share_group.get('share_types')
218 if _share_types:
219 if not all([uuidutils.is_uuid_like(st) for st in _share_types]):
220 msg = _("The 'share_types' attribute must be a list of uuids")
221 raise exc.HTTPBadRequest(explanation=msg)
222 kwargs['share_type_ids'] = _share_types
224 if ('share_network_id' in share_group and
225 'source_share_group_snapshot_id' in share_group):
226 msg = _("Cannot supply both 'share_network_id' and "
227 "'source_share_group_snapshot_id' attributes as the share "
228 "network is inherited from the source.")
229 raise exc.HTTPBadRequest(explanation=msg)
231 availability_zone = share_group.get('availability_zone')
232 if availability_zone:
233 if 'source_share_group_snapshot_id' in share_group:
234 msg = _(
235 "Cannot supply both 'availability_zone' and "
236 "'source_share_group_snapshot_id' attributes as the "
237 "availability zone is inherited from the source.")
238 raise exc.HTTPBadRequest(explanation=msg)
239 try:
240 az = db.availability_zone_get(context, availability_zone)
241 kwargs['availability_zone_id'] = az.id
242 kwargs['availability_zone'] = az.name
243 except exception.AvailabilityZoneNotFound as e:
244 raise exc.HTTPNotFound(explanation=e.msg)
246 if 'source_share_group_snapshot_id' in share_group:
247 source_share_group_snapshot_id = share_group.get(
248 'source_share_group_snapshot_id')
249 if not uuidutils.is_uuid_like(source_share_group_snapshot_id):
250 msg = _("The 'source_share_group_snapshot_id' attribute "
251 "must be a uuid.")
252 raise exc.HTTPBadRequest(explanation=msg)
253 kwargs['source_share_group_snapshot_id'] = (
254 source_share_group_snapshot_id)
255 elif 'share_network_id' in share_group:
256 share_network_id = share_group.get('share_network_id')
257 if not uuidutils.is_uuid_like(share_network_id):
258 msg = _("The 'share_network_id' attribute must be a uuid.")
259 raise exc.HTTPBadRequest(explanation=msg)
260 kwargs['share_network_id'] = share_network_id
262 if 'share_group_type_id' in share_group:
263 share_group_type_id = share_group.get('share_group_type_id')
264 if not uuidutils.is_uuid_like(share_group_type_id): 264 ↛ 265line 264 didn't jump to line 265 because the condition on line 264 was never true
265 msg = _("The 'share_group_type_id' attribute must be a uuid.")
266 raise exc.HTTPBadRequest(explanation=msg)
267 kwargs['share_group_type_id'] = share_group_type_id
268 else: # get default
269 def_share_group_type = share_group_types.get_default()
270 if def_share_group_type:
271 kwargs['share_group_type_id'] = def_share_group_type['id']
272 else:
273 msg = _("Must specify a share group type as a default "
274 "share group type has not been configured.")
275 raise exc.HTTPBadRequest(explanation=msg)
277 try:
278 new_share_group = self.share_group_api.create(context, **kwargs)
279 except exception.InvalidShareGroupSnapshot as e:
280 raise exc.HTTPConflict(explanation=e.msg)
281 except (exception.ShareGroupSnapshotNotFound,
282 exception.InvalidInput) as e:
283 raise exc.HTTPBadRequest(explanation=str(e))
285 return self._view_builder.detail(
286 req, {k: v for k, v in new_share_group.items()})
288 @wsgi.Controller.api_version('2.31', '2.54', experimental=True)
289 @wsgi.response(202)
290 def create(self, req, body):
291 return self._create(req, body)
293 @wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
294 @wsgi.response(202)
295 def create(self, req, body): # pylint: disable=function-redefined # noqa F811
296 return self._create(req, body)
298 def _update(self, *args, **kwargs):
299 db.share_group_update(*args, **kwargs)
301 def _get(self, *args, **kwargs):
302 return self.share_group_api.get(*args, **kwargs)
304 def _delete(self, context, resource, force=True):
305 # Delete all share group snapshots
306 for snap in resource['snapshots']: 306 ↛ 307line 306 didn't jump to line 307 because the loop on line 306 never started
307 db.share_group_snapshot_destroy(context, snap['id'])
309 # Delete all shares in share group
310 for share in db.get_all_shares_by_share_group(context, resource['id']): 310 ↛ 311line 310 didn't jump to line 311 because the loop on line 310 never started
311 db.share_delete(context, share['id'])
313 db.share_group_destroy(context.elevated(), resource['id'])
315 @wsgi.Controller.api_version('2.31', '2.54', experimental=True)
316 @wsgi.action('reset_status')
317 def share_group_reset_status(self, req, id, body):
318 return self._reset_status(req, id, body)
320 # pylint: disable=function-redefined
321 @wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
322 @wsgi.action('reset_status')
323 def share_group_reset_status(self, req, id, body): # noqa F811
324 return self._reset_status(req, id, body)
326 # pylint: enable=function-redefined
327 @wsgi.Controller.api_version('2.31', '2.54', experimental=True)
328 @wsgi.action('force_delete')
329 def share_group_force_delete(self, req, id, body):
330 return self._force_delete(req, id, body)
332 # pylint: disable=function-redefined
333 @wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
334 @wsgi.action('force_delete')
335 def share_group_force_delete(self, req, id, body): # noqa F811
336 return self._force_delete(req, id, body)
339def create_resource():
340 return wsgi.Resource(ShareGroupController())