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