# Copyright (c) 2016 EMC Corporation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import oslo_serialization from cinder.i18n import _ from cinder.volume.drivers.coprhd.helpers import commoncoprhdapi as common from cinder.volume.drivers.coprhd.helpers import consistencygroup from cinder.volume.drivers.coprhd.helpers import volume class Snapshot(common.CoprHDResource): # Commonly used URIs for the 'Snapshot' module URI_SNAPSHOTS = '/{0}/snapshots/{1}' URI_BLOCK_SNAPSHOTS = '/block/snapshots/{0}' URI_SEARCH_SNAPSHOT_BY_TAG = '/block/snapshots/search?tag={0}' URI_SNAPSHOT_LIST = '/{0}/{1}/{2}/protection/snapshots' URI_SNAPSHOT_TASKS_BY_OPID = '/vdc/tasks/{0}' URI_RESOURCE_DEACTIVATE = '{0}/deactivate' URI_CONSISTENCY_GROUP = "/block/consistency-groups" URI_CONSISTENCY_GROUPS_SNAPSHOT_INSTANCE = ( URI_CONSISTENCY_GROUP + "/{0}/protection/snapshots/{1}") URI_CONSISTENCY_GROUPS_SNAPSHOT_DEACTIVATE = ( URI_CONSISTENCY_GROUPS_SNAPSHOT_INSTANCE + "/deactivate") URI_BLOCK_SNAPSHOTS_TAG = URI_BLOCK_SNAPSHOTS + '/tags' VOLUMES = 'volumes' CG = 'consistency-groups' BLOCK = 'block' timeout = 300 def snapshot_list_uri(self, otype, otypename, ouri): """Makes REST API call to list snapshots under a volume. :param otype: block :param otypename: either volume or consistency-group should be provided :param ouri: uri of volume or consistency-group :returns: list of snapshots """ (s, h) = common.service_json_request( self.ipaddr, self.port, "GET", Snapshot.URI_SNAPSHOT_LIST.format(otype, otypename, ouri), None) o = common.json_decode(s) return o['snapshot'] def snapshot_show_uri(self, otype, resource_uri, suri): """Retrieves snapshot details based on snapshot Name or Label. :param otype: block :param suri: uri of the Snapshot. :param resource_uri: uri of the source resource :returns: Snapshot details in JSON response payload """ if(resource_uri is not None and resource_uri.find('BlockConsistencyGroup') > 0): (s, h) = common.service_json_request( self.ipaddr, self.port, "GET", Snapshot.URI_CONSISTENCY_GROUPS_SNAPSHOT_INSTANCE.format( resource_uri, suri), None) else: (s, h) = common.service_json_request( self.ipaddr, self.port, "GET", Snapshot.URI_SNAPSHOTS.format(otype, suri), None) return common.json_decode(s) def snapshot_query(self, storageres_type, storageres_typename, resuri, snapshot_name): if resuri is not None: uris = self.snapshot_list_uri( storageres_type, storageres_typename, resuri) for uri in uris: snapshot = self.snapshot_show_uri( storageres_type, resuri, uri['id']) if (False == common.get_node_value(snapshot, 'inactive') and snapshot['name'] == snapshot_name): return snapshot['id'] raise common.CoprHdError( common.CoprHdError.SOS_FAILURE_ERR, (_("snapshot with the name: " "%s Not Found") % snapshot_name)) def storage_resource_query(self, storageres_type, volume_name, cg_name, project, tenant): resourcepath = "/" + project if tenant is not None: resourcepath = tenant + resourcepath resUri = None resourceObj = None if Snapshot.BLOCK == storageres_type and volume_name is not None: resourceObj = volume.Volume(self.ipaddr, self.port) resUri = resourceObj.volume_query(resourcepath, volume_name) elif Snapshot.BLOCK == storageres_type and cg_name is not None: resourceObj = consistencygroup.ConsistencyGroup( self.ipaddr, self.port) resUri = resourceObj.consistencygroup_query( cg_name, project, tenant) else: resourceObj = None return resUri def snapshot_create(self, otype, typename, ouri, snaplabel, inactive, sync, readonly=False, synctimeout=0): """New snapshot is created, for a given volume. :param otype: block type should be provided :param typename: either volume or consistency-groups should be provided :param ouri: uri of volume :param snaplabel: name of the snapshot :param inactive: if true, the snapshot will not activate the synchronization between source and target volumes :param sync: synchronous request :param synctimeout: Query for task status for 'synctimeout' secs. If the task doesn't complete in synctimeout secs, an exception is thrown """ # check snapshot is already exist is_snapshot_exist = True try: self.snapshot_query(otype, typename, ouri, snaplabel) except common.CoprHdError as e: if e.err_code == common.CoprHdError.NOT_FOUND_ERR: is_snapshot_exist = False else: raise if is_snapshot_exist: raise common.CoprHdError( common.CoprHdError.ENTRY_ALREADY_EXISTS_ERR, (_("Snapshot with name %(snaplabel)s" " already exists under %(typename)s") % {'snaplabel': snaplabel, 'typename': typename })) parms = { 'name': snaplabel, # if true, the snapshot will not activate the synchronization # between source and target volumes 'create_inactive': inactive } if readonly is True: parms['read_only'] = readonly body = oslo_serialization.jsonutils.dumps(parms) # REST api call (s, h) = common.service_json_request( self.ipaddr, self.port, "POST", Snapshot.URI_SNAPSHOT_LIST.format(otype, typename, ouri), body) o = common.json_decode(s) task = o["task"][0] if sync: return ( common.block_until_complete( otype, task['resource']['id'], task["id"], self.ipaddr, self.port, synctimeout) ) else: return o def snapshot_delete_uri(self, otype, resource_uri, suri, sync, synctimeout=0): """Delete a snapshot by uri. :param otype: block :param resource_uri: uri of the source resource :param suri: Uri of the Snapshot :param sync: To perform operation synchronously :param synctimeout: Query for task status for 'synctimeout' secs. If the task doesn't complete in synctimeout secs, an exception is thrown """ s = None if resource_uri.find("Volume") > 0: (s, h) = common.service_json_request( self.ipaddr, self.port, "POST", Snapshot.URI_RESOURCE_DEACTIVATE.format( Snapshot.URI_BLOCK_SNAPSHOTS.format(suri)), None) elif resource_uri.find("BlockConsistencyGroup") > 0: (s, h) = common.service_json_request( self.ipaddr, self.port, "POST", Snapshot.URI_CONSISTENCY_GROUPS_SNAPSHOT_DEACTIVATE.format( resource_uri, suri), None) o = common.json_decode(s) task = o["task"][0] if sync: return ( common.block_until_complete( otype, task['resource']['id'], task["id"], self.ipaddr, self.port, synctimeout) ) else: return o def snapshot_delete(self, storageres_type, storageres_typename, resource_uri, name, sync, synctimeout=0): snapshotUri = self.snapshot_query( storageres_type, storageres_typename, resource_uri, name) self.snapshot_delete_uri( storageres_type, resource_uri, snapshotUri, sync, synctimeout)