ScaleIO Driver: Backup volume via snapshot
When a volume is in-use, the ScaleIO driver can achieve the backup of that volume through creating a temp snapshot, and then backing-up the snapshot. Change-Id: I6330cb1b6644de5f470b198f035933ff676c1bce
This commit is contained in:
parent
d6cfe5ed88
commit
7b5bbc951a
@ -0,0 +1,102 @@
|
||||
# Copyright (c) 2017 Dell Inc. or its subsidiaries.
|
||||
# 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 mock
|
||||
|
||||
from cinder import context
|
||||
from cinder.tests.unit import fake_constants as fake
|
||||
from cinder.tests.unit import fake_snapshot
|
||||
from cinder.tests.unit import fake_volume
|
||||
from cinder.tests.unit.volume.drivers.dell_emc import scaleio
|
||||
|
||||
|
||||
class TestInitializeConnectionSnapshot(scaleio.TestScaleIODriver):
|
||||
|
||||
def setUp(self):
|
||||
super(TestInitializeConnectionSnapshot, self).setUp()
|
||||
self.snapshot_id = 'SNAPID'
|
||||
self.ctx = context.RequestContext('fake', 'fake', auth_token=True)
|
||||
self.fake_path = '/fake/path/vol-xx'
|
||||
self.volume = fake_volume.fake_volume_obj(
|
||||
self.ctx, **{'provider_id': fake.PROVIDER_ID})
|
||||
self.connector = {}
|
||||
|
||||
def test_backup_can_use_snapshots(self):
|
||||
"""Make sure the driver can use snapshots for backup."""
|
||||
use_snaps = self.driver.backup_use_temp_snapshot()
|
||||
self.assertTrue(use_snaps)
|
||||
|
||||
def test_initialize_connection_without_size(self):
|
||||
"""Test initializing when we do not know the snapshot size.
|
||||
|
||||
ScaleIO can determine QOS specs based upon volume/snapshot size
|
||||
The QOS keys should always be returned
|
||||
"""
|
||||
snapshot = fake_snapshot.fake_snapshot_obj(
|
||||
self.ctx, **{'volume': self.volume,
|
||||
'provider_id': self.snapshot_id})
|
||||
props = self.driver.initialize_connection_snapshot(
|
||||
snapshot,
|
||||
self.connector)
|
||||
# validate the volume type
|
||||
self.assertEqual(props['driver_volume_type'], 'scaleio')
|
||||
# make sure a volume name and id exist
|
||||
self.assertIsNotNone(props['data']['scaleIO_volname'])
|
||||
self.assertEqual(self.snapshot_id,
|
||||
props['data']['scaleIO_volume_id'])
|
||||
# make sure QOS properties are set
|
||||
self.assertTrue('iopsLimit' in props['data'])
|
||||
|
||||
def test_initialize_connection_with_size(self):
|
||||
"""Test initializing when we know the snapshot size.
|
||||
|
||||
ScaleIO can determine QOS specs based upon volume/snapshot size
|
||||
The QOS keys should always be returned
|
||||
"""
|
||||
snapshot = fake_snapshot.fake_snapshot_obj(
|
||||
self.ctx, **{'volume': self.volume,
|
||||
'provider_id': self.snapshot_id,
|
||||
'volume_size': 8})
|
||||
props = self.driver.initialize_connection_snapshot(
|
||||
snapshot,
|
||||
self.connector)
|
||||
# validate the volume type
|
||||
self.assertEqual(props['driver_volume_type'], 'scaleio')
|
||||
# make sure a volume name and id exist
|
||||
self.assertIsNotNone(props['data']['scaleIO_volname'])
|
||||
self.assertEqual(self.snapshot_id,
|
||||
props['data']['scaleIO_volume_id'])
|
||||
# make sure QOS properties are set
|
||||
self.assertTrue('iopsLimit' in props['data'])
|
||||
|
||||
def test_qos_specs(self):
|
||||
"""Ensure QOS specs are honored if present."""
|
||||
qos = {'maxIOPS': 1000, 'maxBWS': 2048}
|
||||
snapshot = fake_snapshot.fake_snapshot_obj(
|
||||
self.ctx, **{'volume': self.volume,
|
||||
'provider_id': self.snapshot_id,
|
||||
'volume_size': 8})
|
||||
extraspecs = {}
|
||||
self.driver._get_volumetype_qos = mock.MagicMock()
|
||||
self.driver._get_volumetype_qos.return_value = qos
|
||||
self.driver._get_volumetype_extraspecs = mock.MagicMock()
|
||||
self.driver._get_volumetype_extraspecs.return_value = extraspecs
|
||||
|
||||
props = self.driver.initialize_connection_snapshot(
|
||||
snapshot,
|
||||
self.connector)
|
||||
|
||||
self.assertEqual(1000, int(props['data']['iopsLimit']))
|
||||
self.assertEqual(2048, int(props['data']['bandwidthLimit']))
|
@ -863,23 +863,36 @@ class ScaleIODriver(driver.VolumeDriver):
|
||||
return self._delete_volume(snap_id)
|
||||
|
||||
def initialize_connection(self, volume, connector, **kwargs):
|
||||
"""Initializes the connection and returns connection info.
|
||||
return self._initialize_connection(volume, connector, volume.size)
|
||||
|
||||
def _initialize_connection(self, vol_or_snap, connector, vol_size):
|
||||
"""Initializes a connection and returns connection info.
|
||||
|
||||
The scaleio driver returns a driver_volume_type of 'scaleio'.
|
||||
"""
|
||||
|
||||
LOG.debug("Connector is %s.", connector)
|
||||
try:
|
||||
ip = connector['ip']
|
||||
except Exception:
|
||||
ip = 'unknown'
|
||||
|
||||
LOG.debug("Initializing connection for %(vol)s, "
|
||||
"to SDC at %(sdc)s",
|
||||
{'vol': vol_or_snap.id,
|
||||
'sdc': ip})
|
||||
|
||||
connection_properties = dict(self.connection_properties)
|
||||
|
||||
volname = self._id_to_base64(volume.id)
|
||||
volname = self._id_to_base64(vol_or_snap.id)
|
||||
connection_properties['scaleIO_volname'] = volname
|
||||
connection_properties['scaleIO_volume_id'] = volume.provider_id
|
||||
extra_specs = self._get_volumetype_extraspecs(volume)
|
||||
qos_specs = self._get_volumetype_qos(volume)
|
||||
connection_properties['scaleIO_volume_id'] = vol_or_snap.provider_id
|
||||
|
||||
if vol_size is not None:
|
||||
extra_specs = self._get_volumetype_extraspecs(vol_or_snap)
|
||||
qos_specs = self._get_volumetype_qos(vol_or_snap)
|
||||
storage_type = extra_specs.copy()
|
||||
storage_type.update(qos_specs)
|
||||
LOG.info("Volume type is %s.", storage_type)
|
||||
round_volume_size = self._round_to_num_gran(volume.size)
|
||||
round_volume_size = self._round_to_num_gran(vol_size)
|
||||
iops_limit = self._get_iops_limit(round_volume_size, storage_type)
|
||||
bandwidth_limit = self._get_bandwidth_limit(round_volume_size,
|
||||
storage_type)
|
||||
@ -887,6 +900,7 @@ class ScaleIODriver(driver.VolumeDriver):
|
||||
LOG.info("bandwidth limit is %s", bandwidth_limit)
|
||||
connection_properties['iopsLimit'] = iops_limit
|
||||
connection_properties['bandwidthLimit'] = bandwidth_limit
|
||||
|
||||
return {'driver_volume_type': 'scaleio',
|
||||
'data': connection_properties}
|
||||
|
||||
@ -940,7 +954,22 @@ class ScaleIODriver(driver.VolumeDriver):
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
LOG.debug("scaleio driver terminate connection.")
|
||||
self._terminate_connection(volume, connector)
|
||||
|
||||
def _terminate_connection(self, volume_or_snap, connector):
|
||||
"""Terminate connection to a volume or snapshot
|
||||
|
||||
With ScaleIO, snaps and volumes are terminated identically
|
||||
"""
|
||||
try:
|
||||
ip = connector['ip']
|
||||
except Exception:
|
||||
ip = 'unknown'
|
||||
|
||||
LOG.debug("Terminating connection for %(vol)s, "
|
||||
"to SDC at %(sdc)s",
|
||||
{'vol': volume_or_snap.id,
|
||||
'sdc': ip})
|
||||
|
||||
def _update_volume_stats(self):
|
||||
stats = {}
|
||||
@ -1812,3 +1841,28 @@ class ScaleIODriver(driver.VolumeDriver):
|
||||
def check_for_export(self, context, volume_id):
|
||||
"""Make sure volume is exported."""
|
||||
pass
|
||||
|
||||
def initialize_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||
# return self._initialize_connection(snapshot, connector)
|
||||
"""Initializes a connection and returns connection info."""
|
||||
try:
|
||||
vol_size = snapshot['volume_size']
|
||||
except Exception:
|
||||
vol_size = None
|
||||
|
||||
return self._initialize_connection(snapshot, connector, vol_size)
|
||||
|
||||
def terminate_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||
"""Terminates a connection to a snapshot."""
|
||||
return self._terminate_connection(snapshot, connector)
|
||||
|
||||
def create_export_snapshot(self, context, volume, connector):
|
||||
"""Driver entry point to get the export info for a snapshot."""
|
||||
pass
|
||||
|
||||
def remove_export_snapshot(self, context, volume):
|
||||
"""Driver entry point to remove an export for a snapshot."""
|
||||
pass
|
||||
|
||||
def backup_use_temp_snapshot(self):
|
||||
return True
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Add support to backup volume using snapshot in the Unity driver,
|
||||
which enables backing up of volumes that are in-use.
|
Loading…
Reference in New Issue
Block a user