1# Copyright (C) 2016 EMC Corporation.
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.
15
16"""The group_snapshots API."""
17
18from oslo_log import log as logging
19import six
20from six.moves import http_client
21import webob
22from webob import exc
23
24from cinder.api import common
25from cinder.api import microversions as mv
26from cinder.api.openstack import wsgi
27from cinder.api.schemas import group_snapshots as snapshot
28from cinder.api.v3.views import group_snapshots as group_snapshot_views
29from cinder.api import validation
30from cinder import exception
31from cinder import group as group_api
32from cinder.i18n import _
33from cinder import rpc
34from cinder.volume import group_types
35
36LOG = logging.getLogger(__name__)
37
38
39class GroupSnapshotsController(wsgi.Controller):
40    """The group_snapshots API controller for the OpenStack API."""
41
42    _view_builder_class = group_snapshot_views.ViewBuilder
43
44    def __init__(self):
45        self.group_snapshot_api = group_api.API()
46        super(GroupSnapshotsController, self).__init__()
47
48    def _check_default_cgsnapshot_type(self, group_type_id):
49        if group_types.is_default_cgsnapshot_type(group_type_id):
50            msg = (_("Group_type %(group_type)s is reserved for migrating "
51                     "CGs to groups. Migrated group snapshots can only be "
52                     "operated by CG snapshot APIs.")
53                   % {'group_type': group_type_id})
54            raise exc.HTTPBadRequest(explanation=msg)
55
56    @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
57    def show(self, req, id):
58        """Return data about the given group_snapshot."""
59        LOG.debug('show called for member %s', id)
60        context = req.environ['cinder.context']
61
62        group_snapshot = self.group_snapshot_api.get_group_snapshot(
63            context,
64            group_snapshot_id=id)
65
66        self._check_default_cgsnapshot_type(group_snapshot.group_type_id)
67
68        return self._view_builder.detail(req, group_snapshot)
69
70    @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
71    def delete(self, req, id):
72        """Delete a group_snapshot."""
73        LOG.debug('delete called for member %s', id)
74        context = req.environ['cinder.context']
75
76        LOG.info('Delete group_snapshot with id: %s', id, context=context)
77
78        try:
79            group_snapshot = self.group_snapshot_api.get_group_snapshot(
80                context,
81                group_snapshot_id=id)
82            self._check_default_cgsnapshot_type(group_snapshot.group_type_id)
83            self.group_snapshot_api.delete_group_snapshot(context,
84                                                          group_snapshot)
85        except exception.InvalidGroupSnapshot as e:
86            raise exc.HTTPBadRequest(explanation=six.text_type(e))
87        except (exception.GroupSnapshotNotFound,
88                exception.PolicyNotAuthorized):
89            # Not found exception will be handled at the wsgi level
90            raise
91        except Exception:
92            msg = _("Error occurred when deleting group snapshot %s.") % id
93            LOG.exception(msg)
94            raise exc.HTTPBadRequest(explanation=msg)
95
96        return webob.Response(status_int=http_client.ACCEPTED)
97
98    @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
99    def index(self, req):
100        """Returns a summary list of group_snapshots."""
101        return self._get_group_snapshots(req, is_detail=False)
102
103    @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
104    def detail(self, req):
105        """Returns a detailed list of group_snapshots."""
106        return self._get_group_snapshots(req, is_detail=True)
107
108    def _get_group_snapshots(self, req, is_detail):
109        """Returns a list of group_snapshots through view builder."""
110
111        context = req.environ['cinder.context']
112        req_version = req.api_version_request
113        filters = marker = limit = offset = sort_keys = sort_dirs = None
114        if req_version.matches(mv.GROUP_SNAPSHOT_PAGINATION):
115            filters = req.params.copy()
116            marker, limit, offset = common.get_pagination_params(filters)
117            sort_keys, sort_dirs = common.get_sort_params(filters)
118
119        if req_version.matches(mv.RESOURCE_FILTER):
120            support_like = (True if req_version.matches(
121                mv.LIKE_FILTER) else False)
122            common.reject_invalid_filters(context, filters, 'group_snapshot',
123                                          support_like)
124
125        group_snapshots = self.group_snapshot_api.get_all_group_snapshots(
126            context, filters=filters, marker=marker, limit=limit,
127            offset=offset, sort_keys=sort_keys, sort_dirs=sort_dirs)
128        if is_detail:
129            group_snapshots = self._view_builder.detail_list(req,
130                                                             group_snapshots)
131        else:
132            group_snapshots = self._view_builder.summary_list(req,
133                                                              group_snapshots)
134
135        new_group_snapshots = []
136        for grp_snap in group_snapshots['group_snapshots']:
137            try:
138                # Only show group snapshots not migrated from CG snapshots
139                self._check_default_cgsnapshot_type(grp_snap['group_type_id'])
140                if not is_detail:
141                    grp_snap.pop('group_type_id', None)
142                new_group_snapshots.append(grp_snap)
143            except exc.HTTPBadRequest:
144                # Skip migrated group snapshot
145                pass
146
147        group_snapshots['group_snapshots'] = new_group_snapshots
148        return group_snapshots
149
150    @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
151    @wsgi.response(http_client.ACCEPTED)
152    @validation.schema(snapshot.create)
153    def create(self, req, body):
154        """Create a new group_snapshot."""
155        LOG.debug('Creating new group_snapshot %s', body)
156
157        context = req.environ['cinder.context']
158        group_snapshot = body['group_snapshot']
159
160        group_id = group_snapshot['group_id']
161
162        group = self.group_snapshot_api.get(context, group_id)
163        self._check_default_cgsnapshot_type(group.group_type_id)
164        name = group_snapshot.get('name', None)
165        description = group_snapshot.get('description', None)
166
167        LOG.info("Creating group_snapshot %(name)s.",
168                 {'name': name},
169                 context=context)
170
171        try:
172            new_group_snapshot = self.group_snapshot_api.create_group_snapshot(
173                context, group, name, description)
174        except (exception.InvalidGroup,
175                exception.InvalidGroupSnapshot,
176                exception.InvalidVolume) as error:
177            raise exc.HTTPBadRequest(explanation=error.msg)
178
179        retval = self._view_builder.summary(req, new_group_snapshot)
180
181        return retval
182
183    @wsgi.Controller.api_version(mv.GROUP_SNAPSHOT_RESET_STATUS)
184    @wsgi.action("reset_status")
185    @validation.schema(snapshot.reset_status)
186    def reset_status(self, req, id, body):
187        return self._reset_status(req, id, body)
188
189    def _reset_status(self, req, id, body):
190        """Reset status on group snapshots"""
191
192        context = req.environ['cinder.context']
193        status = body['reset_status']['status'].lower()
194
195        LOG.debug("Updating group '%(id)s' with "
196                  "'%(update)s'", {'id': id,
197                                   'update': status})
198        try:
199            notifier = rpc.get_notifier('groupSnapshotStatusUpdate')
200            notifier.info(context, 'groupsnapshots.reset_status.start',
201                          {'id': id,
202                           'update': status})
203            gsnapshot = self.group_snapshot_api.get_group_snapshot(context, id)
204
205            self.group_snapshot_api.reset_group_snapshot_status(context,
206                                                                gsnapshot,
207                                                                status)
208            notifier.info(context, 'groupsnapshots.reset_status.end',
209                          {'id': id,
210                           'update': status})
211        except exception.GroupSnapshotNotFound as error:
212            # Not found exception will be handled at the wsgi level
213            notifier.error(context, 'groupsnapshots.reset_status',
214                           {'error_message': error.msg,
215                            'id': id})
216            raise
217        except exception.InvalidGroupSnapshotStatus as error:
218            notifier.error(context, 'groupsnapshots.reset_status',
219                           {'error_message': error.msg,
220                            'id': id})
221            raise exc.HTTPBadRequest(explanation=error.msg)
222        return webob.Response(status_int=http_client.ACCEPTED)
223
224
225def create_resource():
226    return wsgi.Resource(GroupSnapshotsController())
227