Merge "Add support for force backup for Nimble Storage"
This commit is contained in:
commit
ac179a3a43
@ -23,6 +23,7 @@ from cinder import context
|
|||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder.objects import volume as obj_volume
|
from cinder.objects import volume as obj_volume
|
||||||
from cinder import test
|
from cinder import test
|
||||||
|
from cinder.tests.unit import fake_constants as fake
|
||||||
from cinder.volume.drivers import nimble
|
from cinder.volume.drivers import nimble
|
||||||
from cinder.volume import volume_types
|
from cinder.volume import volume_types
|
||||||
|
|
||||||
@ -31,6 +32,8 @@ CONF = cfg.CONF
|
|||||||
NIMBLE_CLIENT = 'cinder.volume.drivers.nimble.client'
|
NIMBLE_CLIENT = 'cinder.volume.drivers.nimble.client'
|
||||||
NIMBLE_URLLIB2 = 'six.moves.urllib.request'
|
NIMBLE_URLLIB2 = 'six.moves.urllib.request'
|
||||||
NIMBLE_RANDOM = 'cinder.volume.drivers.nimble.random'
|
NIMBLE_RANDOM = 'cinder.volume.drivers.nimble.random'
|
||||||
|
NIMBLE_ISCSI_DRIVER = 'cinder.volume.drivers.nimble.NimbleISCSIDriver'
|
||||||
|
DRIVER_VERSION = '3.0.0'
|
||||||
|
|
||||||
FAKE_ENUM_STRING = """
|
FAKE_ENUM_STRING = """
|
||||||
<simpleType name="SmErrorType">
|
<simpleType name="SmErrorType">
|
||||||
@ -119,6 +122,23 @@ FAKE_GET_VOL_INFO_RESPONSE = {
|
|||||||
'agent-type': 1,
|
'agent-type': 1,
|
||||||
'online': False}}
|
'online': False}}
|
||||||
|
|
||||||
|
FAKE_GET_VOL_INFO_BACKUP_RESPONSE = {
|
||||||
|
'err-list': {'err-list': [{'code': 0}]},
|
||||||
|
'vol': {'target-name': 'iqn.test',
|
||||||
|
'name': 'test_vol',
|
||||||
|
'agent-type': 1,
|
||||||
|
'clone': 1,
|
||||||
|
'base-snap': 'test-backup-snap',
|
||||||
|
'parent-vol': 'volume-' + fake.VOLUME2_ID,
|
||||||
|
'online': False}}
|
||||||
|
|
||||||
|
FAKE_GET_SNAP_INFO_BACKUP_RESPONSE = {
|
||||||
|
'err-list': {'err-list': [{'code': 0}]},
|
||||||
|
'snap': {'description': "backup-vol-" + fake.VOLUME2_ID,
|
||||||
|
'name': 'test-backup-snap',
|
||||||
|
'vol': 'volume-' + fake.VOLUME_ID}
|
||||||
|
}
|
||||||
|
|
||||||
FAKE_GET_VOL_INFO_ONLINE = {
|
FAKE_GET_VOL_INFO_ONLINE = {
|
||||||
'err-list': {'err-list': [{'code': 0}]},
|
'err-list': {'err-list': [{'code': 0}]},
|
||||||
'vol': {'target-name': 'iqn.test',
|
'vol': {'target-name': 'iqn.test',
|
||||||
@ -137,8 +157,7 @@ FAKE_GET_VOL_INFO_RESPONSE_WITH_SET_AGENT_TYPE = {
|
|||||||
'name': 'test_vol',
|
'name': 'test_vol',
|
||||||
'agent-type': 5}}
|
'agent-type': 5}}
|
||||||
|
|
||||||
|
FAKE_TYPE_ID = fake.VOLUME_TYPE_ID
|
||||||
FAKE_TYPE_ID = 12345
|
|
||||||
|
|
||||||
|
|
||||||
def create_configuration(username, password, ip_address,
|
def create_configuration(username, password, ip_address,
|
||||||
@ -478,6 +497,8 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase):
|
|||||||
mock.Mock(return_value=[]))
|
mock.Mock(return_value=[]))
|
||||||
@NimbleDriverBaseTestCase.client_mock_decorator(create_configuration(
|
@NimbleDriverBaseTestCase.client_mock_decorator(create_configuration(
|
||||||
'nimble', 'nimble_pass', '10.18.108.55', 'default', '*'))
|
'nimble', 'nimble_pass', '10.18.108.55', 'default', '*'))
|
||||||
|
@mock.patch(NIMBLE_ISCSI_DRIVER + ".is_volume_backup_clone", mock.Mock(
|
||||||
|
return_value = ['', '']))
|
||||||
def test_delete_volume(self):
|
def test_delete_volume(self):
|
||||||
self.mock_client_service.service.onlineVol.return_value = \
|
self.mock_client_service.service.onlineVol.return_value = \
|
||||||
FAKE_GENERIC_POSITIVE_RESPONSE
|
FAKE_GENERIC_POSITIVE_RESPONSE
|
||||||
@ -495,6 +516,46 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase):
|
|||||||
request={'name': 'testvolume', 'sid': 'a9b9aba7'})]
|
request={'name': 'testvolume', 'sid': 'a9b9aba7'})]
|
||||||
self.mock_client_service.assert_has_calls(expected_calls)
|
self.mock_client_service.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
|
@mock.patch(NIMBLE_URLLIB2)
|
||||||
|
@mock.patch(NIMBLE_CLIENT)
|
||||||
|
@NimbleDriverBaseTestCase.client_mock_decorator(create_configuration(
|
||||||
|
'nimble', 'nimble_pass', '10.18.108.55', 'default', '*'))
|
||||||
|
@mock.patch(NIMBLE_ISCSI_DRIVER + ".is_volume_backup_clone", mock.Mock(
|
||||||
|
return_value=['test-backup-snap', 'volume-' + fake.VOLUME_ID]))
|
||||||
|
@mock.patch.object(obj_volume.VolumeList, 'get_all_by_host')
|
||||||
|
def test_delete_volume_with_backup(self, mock_volume_list):
|
||||||
|
mock_volume_list.return_value = []
|
||||||
|
self.mock_client_service.service.onlineVol.return_value = \
|
||||||
|
FAKE_GENERIC_POSITIVE_RESPONSE
|
||||||
|
self.mock_client_service.service.deleteVol.return_value = \
|
||||||
|
FAKE_GENERIC_POSITIVE_RESPONSE
|
||||||
|
self.mock_client_service.service.dissocProtPol.return_value = \
|
||||||
|
FAKE_GENERIC_POSITIVE_RESPONSE
|
||||||
|
self.mock_client_service.service.onlineSnap.return_value = \
|
||||||
|
FAKE_GENERIC_POSITIVE_RESPONSE
|
||||||
|
self.mock_client_service.service.deleteSnap.return_value = \
|
||||||
|
FAKE_GENERIC_POSITIVE_RESPONSE
|
||||||
|
|
||||||
|
self.driver.delete_volume({'name': 'testvolume'})
|
||||||
|
expected_calls = [mock.call.service.onlineVol(
|
||||||
|
request={
|
||||||
|
'online': False, 'name': 'testvolume', 'sid': 'a9b9aba7'}),
|
||||||
|
mock.call.service.dissocProtPol(
|
||||||
|
request={'vol-name': 'testvolume', 'sid': 'a9b9aba7'}),
|
||||||
|
mock.call.service.deleteVol(
|
||||||
|
request={'name': 'testvolume', 'sid': 'a9b9aba7'}),
|
||||||
|
mock.call.service.onlineSnap(
|
||||||
|
request={'vol': 'volume-' + fake.VOLUME_ID,
|
||||||
|
'name': 'test-backup-snap',
|
||||||
|
'online': False,
|
||||||
|
'sid': 'a9b9aba7'}),
|
||||||
|
mock.call.service.deleteSnap(
|
||||||
|
request={'vol': 'volume-' + fake.VOLUME_ID,
|
||||||
|
'name': 'test-backup-snap',
|
||||||
|
'sid': 'a9b9aba7'})]
|
||||||
|
|
||||||
|
self.mock_client_service.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
@mock.patch(NIMBLE_URLLIB2)
|
@mock.patch(NIMBLE_URLLIB2)
|
||||||
@mock.patch(NIMBLE_CLIENT)
|
@mock.patch(NIMBLE_CLIENT)
|
||||||
@mock.patch.object(obj_volume.VolumeList, 'get_all_by_host',
|
@mock.patch.object(obj_volume.VolumeList, 'get_all_by_host',
|
||||||
@ -517,18 +578,19 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase):
|
|||||||
|
|
||||||
@mock.patch(NIMBLE_URLLIB2)
|
@mock.patch(NIMBLE_URLLIB2)
|
||||||
@mock.patch(NIMBLE_CLIENT)
|
@mock.patch(NIMBLE_CLIENT)
|
||||||
@mock.patch.object(obj_volume.VolumeList, 'get_all_by_host',
|
|
||||||
mock.Mock(return_value=[]))
|
|
||||||
@mock.patch.object(volume_types, 'get_volume_type_extra_specs',
|
@mock.patch.object(volume_types, 'get_volume_type_extra_specs',
|
||||||
mock.Mock(type_id=FAKE_TYPE_ID, return_value={
|
mock.Mock(type_id=FAKE_TYPE_ID,
|
||||||
'nimble:perfpol-name': 'default',
|
return_value=
|
||||||
|
{'nimble:perfpol-name': 'default',
|
||||||
'nimble:encryption': 'yes',
|
'nimble:encryption': 'yes',
|
||||||
'nimble:multi-initiator': 'false'}))
|
'nimble:multi-initiator': 'false'}))
|
||||||
@NimbleDriverBaseTestCase.client_mock_decorator(create_configuration(
|
@NimbleDriverBaseTestCase.client_mock_decorator(create_configuration(
|
||||||
'nimble', 'nimble_pass', '10.18.108.55', 'default', '*', False))
|
'nimble', 'nimble_pass', '10.18.108.55', 'default', '*', False))
|
||||||
|
@mock.patch.object(obj_volume.VolumeList, 'get_all_by_host')
|
||||||
@mock.patch(NIMBLE_RANDOM)
|
@mock.patch(NIMBLE_RANDOM)
|
||||||
def test_create_cloned_volume(self, mock_random):
|
def test_create_cloned_volume(self, mock_random, mock_volume_list):
|
||||||
mock_random.sample.return_value = 'abcdefghijkl'
|
mock_random.sample.return_value = fake.VOLUME_ID
|
||||||
|
mock_volume_list.return_value = []
|
||||||
self.mock_client_service.service.snapVol.return_value = \
|
self.mock_client_service.service.snapVol.return_value = \
|
||||||
FAKE_GENERIC_POSITIVE_RESPONSE
|
FAKE_GENERIC_POSITIVE_RESPONSE
|
||||||
self.mock_client_service.service.cloneVol.return_value = \
|
self.mock_client_service.service.cloneVol.return_value = \
|
||||||
@ -537,25 +599,36 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase):
|
|||||||
FAKE_GET_VOL_INFO_RESPONSE
|
FAKE_GET_VOL_INFO_RESPONSE
|
||||||
self.mock_client_service.service.getNetConfig.return_value = \
|
self.mock_client_service.service.getNetConfig.return_value = \
|
||||||
FAKE_POSITIVE_NETCONFIG_RESPONSE
|
FAKE_POSITIVE_NETCONFIG_RESPONSE
|
||||||
|
|
||||||
|
volume = obj_volume.Volume(context.get_admin_context(),
|
||||||
|
id=fake.VOLUME_ID,
|
||||||
|
size=5.0,
|
||||||
|
_name_id=None,
|
||||||
|
display_name='',
|
||||||
|
volume_type_id=FAKE_TYPE_ID
|
||||||
|
)
|
||||||
|
src_volume = obj_volume.Volume(context.get_admin_context(),
|
||||||
|
id=fake.VOLUME2_ID,
|
||||||
|
_name_id=None,
|
||||||
|
size=5.0)
|
||||||
self.assertEqual({
|
self.assertEqual({
|
||||||
'provider_location': '172.18.108.21:3260 iqn.test 0',
|
'provider_location': '172.18.108.21:3260 iqn.test 0',
|
||||||
'provider_auth': None},
|
'provider_auth': None},
|
||||||
self.driver.create_cloned_volume({'name': 'volume',
|
self.driver.create_cloned_volume(volume, src_volume))
|
||||||
'size': 5,
|
|
||||||
'volume_type_id': FAKE_TYPE_ID},
|
|
||||||
{'name': 'testvolume',
|
|
||||||
'size': 5}))
|
|
||||||
expected_calls = [mock.call.service.snapVol(
|
expected_calls = [mock.call.service.snapVol(
|
||||||
request={
|
request={
|
||||||
'vol': 'testvolume',
|
'vol': "volume-" + fake.VOLUME2_ID,
|
||||||
'snapAttr': {'name': 'openstack-clone-volume-abcdefghijkl',
|
'snapAttr': {'name': 'openstack-clone-volume-' +
|
||||||
|
fake.VOLUME_ID +
|
||||||
|
"-" + fake.VOLUME_ID,
|
||||||
'description': ''},
|
'description': ''},
|
||||||
'sid': 'a9b9aba7'}),
|
'sid': 'a9b9aba7'}),
|
||||||
mock.call.service.cloneVol(
|
mock.call.service.cloneVol(
|
||||||
request={
|
request={
|
||||||
'snap-name': 'openstack-clone-volume-abcdefghijkl',
|
'snap-name': 'openstack-clone-volume-' + fake.VOLUME_ID +
|
||||||
|
"-" + fake.VOLUME_ID,
|
||||||
'attr': {'snap-quota': sys.maxsize,
|
'attr': {'snap-quota': sys.maxsize,
|
||||||
'name': 'volume',
|
'name': 'volume-' + fake.VOLUME_ID,
|
||||||
'quota': 5368709120,
|
'quota': 5368709120,
|
||||||
'reserve': 5368709120,
|
'reserve': 5368709120,
|
||||||
'online': True,
|
'online': True,
|
||||||
@ -564,7 +637,7 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase):
|
|||||||
'multi-initiator': 'false',
|
'multi-initiator': 'false',
|
||||||
'perfpol-name': 'default',
|
'perfpol-name': 'default',
|
||||||
'agent-type': 5},
|
'agent-type': 5},
|
||||||
'name': 'testvolume',
|
'name': 'volume-' + fake.VOLUME2_ID,
|
||||||
'sid': 'a9b9aba7'})]
|
'sid': 'a9b9aba7'})]
|
||||||
self.mock_client_service.assert_has_calls(expected_calls)
|
self.mock_client_service.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
@ -744,7 +817,7 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase):
|
|||||||
def test_get_volume_stats(self):
|
def test_get_volume_stats(self):
|
||||||
self.mock_client_service.service.getGroupConfig.return_value = \
|
self.mock_client_service.service.getGroupConfig.return_value = \
|
||||||
FAKE_POSITIVE_GROUP_CONFIG_RESPONSE
|
FAKE_POSITIVE_GROUP_CONFIG_RESPONSE
|
||||||
expected_res = {'driver_version': '2.0.2',
|
expected_res = {'driver_version': DRIVER_VERSION,
|
||||||
'vendor_name': 'Nimble',
|
'vendor_name': 'Nimble',
|
||||||
'volume_backend_name': 'NIMBLE',
|
'volume_backend_name': 'NIMBLE',
|
||||||
'storage_protocol': 'iSCSI',
|
'storage_protocol': 'iSCSI',
|
||||||
@ -757,6 +830,33 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase):
|
|||||||
expected_res,
|
expected_res,
|
||||||
self.driver.get_volume_stats(refresh=True))
|
self.driver.get_volume_stats(refresh=True))
|
||||||
|
|
||||||
|
@mock.patch(NIMBLE_URLLIB2)
|
||||||
|
@mock.patch(NIMBLE_CLIENT)
|
||||||
|
@mock.patch.object(obj_volume.VolumeList, 'get_all_by_host',
|
||||||
|
mock.Mock(return_value=[]))
|
||||||
|
@NimbleDriverBaseTestCase.client_mock_decorator(create_configuration(
|
||||||
|
'nimble', 'nimble_pass', '10.18.108.55', 'default', '*'))
|
||||||
|
def test_is_volume_backup_clone(self):
|
||||||
|
self.mock_client_service.service.getVolInfo.return_value = \
|
||||||
|
FAKE_GET_VOL_INFO_BACKUP_RESPONSE
|
||||||
|
self.mock_client_service.service.getSnapInfo.return_value = \
|
||||||
|
FAKE_GET_SNAP_INFO_BACKUP_RESPONSE
|
||||||
|
volume = obj_volume.Volume(context.get_admin_context(),
|
||||||
|
id=fake.VOLUME_ID,
|
||||||
|
_name_id=None)
|
||||||
|
self.assertEqual(("test-backup-snap", "volume-" + fake.VOLUME_ID),
|
||||||
|
self.driver.is_volume_backup_clone(volume))
|
||||||
|
expected_calls = [
|
||||||
|
mock.call.service.getVolInfo(
|
||||||
|
request={'name': 'volume-' + fake.VOLUME_ID,
|
||||||
|
'sid': 'a9b9aba7'}),
|
||||||
|
mock.call.service.getSnapInfo(
|
||||||
|
request={'sid': 'a9b9aba7',
|
||||||
|
'vol': 'volume-' + fake.VOLUME2_ID,
|
||||||
|
'name': 'test-backup-snap'})
|
||||||
|
]
|
||||||
|
self.mock_client_service.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
|
|
||||||
class NimbleDriverSnapshotTestCase(NimbleDriverBaseTestCase):
|
class NimbleDriverSnapshotTestCase(NimbleDriverBaseTestCase):
|
||||||
|
|
||||||
@ -880,7 +980,7 @@ class NimbleDriverConnectionTestCase(NimbleDriverBaseTestCase):
|
|||||||
expected_res = {
|
expected_res = {
|
||||||
'driver_volume_type': 'iscsi',
|
'driver_volume_type': 'iscsi',
|
||||||
'data': {
|
'data': {
|
||||||
'target_lun': '14',
|
'target_lun': 14,
|
||||||
'volume_id': 12,
|
'volume_id': 12,
|
||||||
'target_iqn': '13',
|
'target_iqn': '13',
|
||||||
'target_discovered': False,
|
'target_discovered': False,
|
||||||
@ -923,7 +1023,7 @@ class NimbleDriverConnectionTestCase(NimbleDriverBaseTestCase):
|
|||||||
expected_res = {
|
expected_res = {
|
||||||
'driver_volume_type': 'iscsi',
|
'driver_volume_type': 'iscsi',
|
||||||
'data': {
|
'data': {
|
||||||
'target_lun': '14',
|
'target_lun': 14,
|
||||||
'volume_id': 12,
|
'volume_id': 12,
|
||||||
'target_iqn': '13',
|
'target_iqn': '13',
|
||||||
'target_discovered': False,
|
'target_discovered': False,
|
||||||
|
@ -23,6 +23,7 @@ import math
|
|||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import six
|
import six
|
||||||
|
import ssl
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ from cinder.volume.drivers.san import san
|
|||||||
from cinder.volume import volume_types
|
from cinder.volume import volume_types
|
||||||
|
|
||||||
|
|
||||||
DRIVER_VERSION = '2.0.2'
|
DRIVER_VERSION = '3.0.0'
|
||||||
AES_256_XTS_CIPHER = 2
|
AES_256_XTS_CIPHER = 2
|
||||||
DEFAULT_CIPHER = 3
|
DEFAULT_CIPHER = 3
|
||||||
EXTRA_SPEC_ENCRYPTION = 'nimble:encryption'
|
EXTRA_SPEC_ENCRYPTION = 'nimble:encryption'
|
||||||
@ -63,6 +64,12 @@ SM_SUBNET_MGMT_PLUS_DATA = 4
|
|||||||
LUN_ID = '0'
|
LUN_ID = '0'
|
||||||
WARN_LEVEL = 0.8
|
WARN_LEVEL = 0.8
|
||||||
|
|
||||||
|
# Work around for ubuntu_openssl_bug_965371. Python soap client suds
|
||||||
|
# throws the error ssl-certificate-verify-failed-error, workaround to disable
|
||||||
|
# ssl check for now
|
||||||
|
if hasattr(ssl, '_create_unverified_context'):
|
||||||
|
ssl._create_default_https_context = ssl._create_unverified_context
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
nimble_opts = [
|
nimble_opts = [
|
||||||
@ -103,6 +110,7 @@ class NimbleISCSIDriver(san.SanISCSIDriver):
|
|||||||
Added Manage/Unmanage volume support
|
Added Manage/Unmanage volume support
|
||||||
2.0.1 - Added multi-initiator support through extra-specs
|
2.0.1 - Added multi-initiator support through extra-specs
|
||||||
2.0.2 - Fixed supporting extra specs while cloning vols
|
2.0.2 - Fixed supporting extra specs while cloning vols
|
||||||
|
3.0.0 - Newton Support for Force Backup
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = DRIVER_VERSION
|
VERSION = DRIVER_VERSION
|
||||||
@ -218,14 +226,57 @@ class NimbleISCSIDriver(san.SanISCSIDriver):
|
|||||||
self.configuration.nimble_pool_name, reserve)
|
self.configuration.nimble_pool_name, reserve)
|
||||||
return self._get_model_info(volume['name'])
|
return self._get_model_info(volume['name'])
|
||||||
|
|
||||||
|
def is_volume_backup_clone(self, volume):
|
||||||
|
"""Check if the volume is created through cinder-backup workflow.
|
||||||
|
|
||||||
|
:param volume: reference to volume from delete_volume()
|
||||||
|
"""
|
||||||
|
vol_info = self.APIExecutor.get_vol_info(volume.name)
|
||||||
|
if vol_info['clone'] and vol_info['base-snap'] and vol_info[
|
||||||
|
'parent-vol']:
|
||||||
|
LOG.debug("Nimble base-snap exists for volume :%s", volume['name'])
|
||||||
|
volume_name_prefix = volume.name.replace(volume.id, "")
|
||||||
|
LOG.debug("volume_name_prefix : %s", volume_name_prefix)
|
||||||
|
snap_info = self.APIExecutor.get_snap_info(vol_info['base-snap'],
|
||||||
|
vol_info['parent-vol'])
|
||||||
|
if snap_info['description'] and "backup-vol-" in snap_info[
|
||||||
|
'description']:
|
||||||
|
parent_vol_id = vol_info['parent-vol'
|
||||||
|
].replace(volume_name_prefix, "")
|
||||||
|
if "backup-vol-" + parent_vol_id in snap_info['description']:
|
||||||
|
LOG.info(_LI("nimble backup-snapshot exists name: %s"),
|
||||||
|
snap_info['name'])
|
||||||
|
return snap_info['name'], snap_info['vol']
|
||||||
|
return "", ""
|
||||||
|
|
||||||
def delete_volume(self, volume):
|
def delete_volume(self, volume):
|
||||||
"""Delete the specified volume."""
|
"""Delete the specified volume."""
|
||||||
|
snap_name, vol_name = self.is_volume_backup_clone(volume)
|
||||||
self.APIExecutor.online_vol(volume['name'], False,
|
self.APIExecutor.online_vol(volume['name'], False,
|
||||||
ignore_list=['SM-enoent'])
|
ignore_list=['SM-enoent'])
|
||||||
self.APIExecutor.dissociate_volcoll(volume['name'],
|
self.APIExecutor.dissociate_volcoll(volume['name'],
|
||||||
ignore_list=['SM-enoent'])
|
ignore_list=['SM-enoent'])
|
||||||
self.APIExecutor.delete_vol(volume['name'], ignore_list=['SM-enoent'])
|
self.APIExecutor.delete_vol(volume['name'], ignore_list=['SM-enoent'])
|
||||||
|
|
||||||
|
# Nimble backend does not delete the snapshot from the parent volume
|
||||||
|
# if there is a dependent clone. So the deletes need to be in reverse
|
||||||
|
# order i.e.
|
||||||
|
# 1. First delete the clone volume used for backup
|
||||||
|
# 2. Delete the base snapshot used for clone from the parent volume.
|
||||||
|
# This is only done for the force backup clone operation as it is
|
||||||
|
# a temporary operation in which we are certain that the snapshot does
|
||||||
|
# not need to be preserved after the backup is completed.
|
||||||
|
|
||||||
|
if snap_name and vol_name:
|
||||||
|
self.APIExecutor.online_snap(vol_name,
|
||||||
|
False,
|
||||||
|
snap_name,
|
||||||
|
ignore_list=['SM-ealready',
|
||||||
|
'SM-enoent'])
|
||||||
|
self.APIExecutor.delete_snap(vol_name,
|
||||||
|
snap_name,
|
||||||
|
ignore_list=['SM-enoent'])
|
||||||
|
|
||||||
def _generate_random_string(self, length):
|
def _generate_random_string(self, length):
|
||||||
"""Generates random_string."""
|
"""Generates random_string."""
|
||||||
char_set = string.ascii_lowercase
|
char_set = string.ascii_lowercase
|
||||||
@ -259,7 +310,7 @@ class NimbleISCSIDriver(san.SanISCSIDriver):
|
|||||||
snapshot = {'volume_name': src_vref['name'],
|
snapshot = {'volume_name': src_vref['name'],
|
||||||
'name': snapshot_name,
|
'name': snapshot_name,
|
||||||
'volume_size': src_vref['size'],
|
'volume_size': src_vref['size'],
|
||||||
'display_name': '',
|
'display_name': volume.display_name,
|
||||||
'display_description': ''}
|
'display_description': ''}
|
||||||
self.APIExecutor.snap_vol(snapshot)
|
self.APIExecutor.snap_vol(snapshot)
|
||||||
self._clone_volume_from_snapshot(volume, snapshot)
|
self._clone_volume_from_snapshot(volume, snapshot)
|
||||||
@ -486,7 +537,7 @@ class NimbleISCSIDriver(san.SanISCSIDriver):
|
|||||||
properties['target_discovered'] = False # whether discovery was used
|
properties['target_discovered'] = False # whether discovery was used
|
||||||
properties['target_portal'] = iscsi_portal
|
properties['target_portal'] = iscsi_portal
|
||||||
properties['target_iqn'] = iqn
|
properties['target_iqn'] = iqn
|
||||||
properties['target_lun'] = lun_num
|
properties['target_lun'] = int(lun_num)
|
||||||
properties['volume_id'] = volume['id'] # used by xen currently
|
properties['volume_id'] = volume['id'] # used by xen currently
|
||||||
return {
|
return {
|
||||||
'driver_volume_type': 'iscsi',
|
'driver_volume_type': 'iscsi',
|
||||||
@ -757,6 +808,31 @@ class NimbleAPIExecutor(object):
|
|||||||
vol_name)
|
vol_name)
|
||||||
return response['vol']
|
return response['vol']
|
||||||
|
|
||||||
|
@_connection_checker
|
||||||
|
@_response_checker
|
||||||
|
def _execute_get_snap_info(self, snap_name, vol_name):
|
||||||
|
LOG.info(_LI('Getting snapshot information for %(vol_name)s '
|
||||||
|
'%(snap_name)s'), {'vol_name': vol_name,
|
||||||
|
'snap_name': snap_name})
|
||||||
|
return self.client.service.getSnapInfo(request={'sid': self.sid,
|
||||||
|
'vol': vol_name,
|
||||||
|
'name': snap_name})
|
||||||
|
|
||||||
|
def get_snap_info(self, snap_name, vol_name):
|
||||||
|
"""Get snapshot information.
|
||||||
|
|
||||||
|
:param snap_name: snapshot name
|
||||||
|
:param vol_name: volume name
|
||||||
|
:return: response object
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self._execute_get_snap_info(snap_name, vol_name)
|
||||||
|
LOG.info(_LI('Successfully got snapshot information for snapshot '
|
||||||
|
'%(snap)s and %(volume)s'),
|
||||||
|
{'snap': snap_name,
|
||||||
|
'volume': vol_name})
|
||||||
|
return response['snap']
|
||||||
|
|
||||||
@_connection_checker
|
@_connection_checker
|
||||||
@_response_checker
|
@_response_checker
|
||||||
def online_vol(self, vol_name, online_flag, *args, **kwargs):
|
def online_vol(self, vol_name, online_flag, *args, **kwargs):
|
||||||
@ -802,10 +878,12 @@ class NimbleAPIExecutor(object):
|
|||||||
volume_name = snapshot['volume_name']
|
volume_name = snapshot['volume_name']
|
||||||
snap_name = snapshot['name']
|
snap_name = snapshot['name']
|
||||||
# Set snapshot description
|
# Set snapshot description
|
||||||
display_list = [getattr(snapshot, 'display_name', ''),
|
display_list = [getattr(snapshot, 'display_name', snapshot[
|
||||||
|
'display_name']),
|
||||||
getattr(snapshot, 'display_description', '')]
|
getattr(snapshot, 'display_description', '')]
|
||||||
snap_description = ':'.join(filter(None, display_list))
|
snap_description = ':'.join(filter(None, display_list))
|
||||||
# Limit to 254 characters
|
# Limit to 254 characters
|
||||||
|
LOG.debug("snap_description %s", snap_description)
|
||||||
snap_description = snap_description[:254]
|
snap_description = snap_description[:254]
|
||||||
LOG.info(_LI('Creating snapshot for volume_name=%(vol)s'
|
LOG.info(_LI('Creating snapshot for volume_name=%(vol)s'
|
||||||
' snap_name=%(name)s snap_description=%(desc)s'),
|
' snap_name=%(name)s snap_description=%(desc)s'),
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Support Force backup of in-use cinder volumes
|
||||||
|
for Nimble Storage.
|
Loading…
x
Reference in New Issue
Block a user