258 lines
9.3 KiB
Python
258 lines
9.3 KiB
Python
# 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)
|