NEC driver: Optimal path for non-disruptive backup
This patch enables optimal path for non-disruptive backup for the NEC volume driver. Change-Id: Ied9f3353be02a1b80081ab0d41f45f1a7c563468 Implements: blueprint nec-nondisruptive-backup
This commit is contained in:
parent
453df126ab
commit
55e8befc4c
@ -94,8 +94,8 @@ xml_out = '''
|
|||||||
<OBJECT name="Logical Disk">
|
<OBJECT name="Logical Disk">
|
||||||
<SECTION name="LD Detail Information">
|
<SECTION name="LD Detail Information">
|
||||||
<UNIT name="LDN(h)">0006</UNIT>
|
<UNIT name="LDN(h)">0006</UNIT>
|
||||||
<UNIT name="OS Type"> </UNIT>
|
<UNIT name="OS Type">LX</UNIT>
|
||||||
<UNIT name="LD Name">20000009910200140006</UNIT>
|
<UNIT name="LD Name">287RbQoP7VdwR1WsPC2fZT_l</UNIT>
|
||||||
<UNIT name="LD Capacity">10737418240</UNIT>
|
<UNIT name="LD Capacity">10737418240</UNIT>
|
||||||
<UNIT name="Pool No.(h)">0000</UNIT>
|
<UNIT name="Pool No.(h)">0000</UNIT>
|
||||||
<UNIT name="Purpose">---</UNIT>
|
<UNIT name="Purpose">---</UNIT>
|
||||||
@ -385,7 +385,7 @@ def patch_get_conf_properties(self, conf=None):
|
|||||||
'pool_pools': [0, 1],
|
'pool_pools': [0, 1],
|
||||||
'pool_backup_pools': [2, 3],
|
'pool_backup_pools': [2, 3],
|
||||||
'pool_actual_free_capacity': 50000000000,
|
'pool_actual_free_capacity': 50000000000,
|
||||||
'ldset_name': 'LX:OpenStack0',
|
'ldset_name': '',
|
||||||
'ldset_controller_node_name': 'LX:node0',
|
'ldset_controller_node_name': 'LX:node0',
|
||||||
'ld_name_format': 'LX:%s',
|
'ld_name_format': 'LX:%s',
|
||||||
'ld_backupname_format': 'LX:%s_back',
|
'ld_backupname_format': 'LX:%s_back',
|
||||||
@ -717,7 +717,7 @@ class VolumeCreateTest(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
|||||||
self.vol.status = 'available'
|
self.vol.status = 'available'
|
||||||
with self.assertRaisesRegexp(exception.NotFound,
|
with self.assertRaisesRegexp(exception.NotFound,
|
||||||
'Logical Disk `LX:37mA82`'
|
'Logical Disk `LX:37mA82`'
|
||||||
' does not exist.'):
|
' could not be found.'):
|
||||||
self._validate_migrate_volume(self.vol, self.xml)
|
self._validate_migrate_volume(self.vol, self.xml)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
||||||
@ -760,7 +760,7 @@ class BindLDTest(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
|||||||
self.max_ld_count) = self.configs(self.xml)
|
self.max_ld_count) = self.configs(self.xml)
|
||||||
mock_bindld = mock.Mock()
|
mock_bindld = mock.Mock()
|
||||||
self._bind_ld = mock_bindld
|
self._bind_ld = mock_bindld
|
||||||
self._bind_ld.return_value = (0, 0, 0)
|
self._bind_ld.return_value = 0, 0, 0
|
||||||
|
|
||||||
def test_bindld_CreateVolume(self):
|
def test_bindld_CreateVolume(self):
|
||||||
self.vol.id = "AAAAAAAA"
|
self.vol.id = "AAAAAAAA"
|
||||||
@ -820,7 +820,7 @@ class BindLDTest_Snap(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
|||||||
self.max_ld_count) = self.configs(self.xml)
|
self.max_ld_count) = self.configs(self.xml)
|
||||||
mock_bindld = mock.Mock()
|
mock_bindld = mock.Mock()
|
||||||
self._bind_ld = mock_bindld
|
self._bind_ld = mock_bindld
|
||||||
self._bind_ld.return_value = (0, 0, 0)
|
self._bind_ld.return_value = 0, 0, 0
|
||||||
mock_bindsnap = mock.Mock()
|
mock_bindsnap = mock.Mock()
|
||||||
self._create_snapshot = mock_bindsnap
|
self._create_snapshot = mock_bindsnap
|
||||||
|
|
||||||
@ -901,7 +901,7 @@ class ExportTest(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
|||||||
self.vol.size = 10
|
self.vol.size = 10
|
||||||
self.vol.status = None
|
self.vol.status = None
|
||||||
self.vol.migration_status = None
|
self.vol.migration_status = None
|
||||||
connector = {'wwpns': ["1000-0090-FAA0-723A", "1000-0090-FAA0-723B"]}
|
connector = {'wwpns': ["10000090FAA0786A", "10000090FAA0786B"]}
|
||||||
self.fc_do_export(None, self.vol, connector)
|
self.fc_do_export(None, self.vol, connector)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
||||||
@ -960,7 +960,7 @@ class ExportTest(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
|||||||
def test_fc_initialize_connection(self):
|
def test_fc_initialize_connection(self):
|
||||||
self.vol.id = "46045673-41e7-44a7-9333-02f07feab04b"
|
self.vol.id = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||||
self.vol.migration_status = None
|
self.vol.migration_status = None
|
||||||
connector = {'wwpns': ["1000-0090-FAA0-723A", "1000-0090-FAA0-723B"]}
|
connector = {'wwpns': ["10000090FAA0786A", "10000090FAA0786B"]}
|
||||||
info = self._fc_initialize_connection(self.vol, connector)
|
info = self._fc_initialize_connection(self.vol, connector)
|
||||||
self.assertEqual('fibre_channel', info['driver_volume_type'])
|
self.assertEqual('fibre_channel', info['driver_volume_type'])
|
||||||
self.assertEqual('2100000991020012', info['data']['target_wwn'][0])
|
self.assertEqual('2100000991020012', info['data']['target_wwn'][0])
|
||||||
@ -969,28 +969,28 @@ class ExportTest(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
|||||||
self.assertEqual('2A00000991020012', info['data']['target_wwn'][3])
|
self.assertEqual('2A00000991020012', info['data']['target_wwn'][3])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2100000991020012',
|
'2100000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][0])
|
info['data']['initiator_target_map']['10000090FAA0786A'][0])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2100000991020012',
|
'2100000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][0])
|
info['data']['initiator_target_map']['10000090FAA0786B'][0])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2200000991020012',
|
'2200000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][1])
|
info['data']['initiator_target_map']['10000090FAA0786A'][1])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2200000991020012',
|
'2200000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][1])
|
info['data']['initiator_target_map']['10000090FAA0786B'][1])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2900000991020012',
|
'2900000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][2])
|
info['data']['initiator_target_map']['10000090FAA0786A'][2])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2900000991020012',
|
'2900000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][2])
|
info['data']['initiator_target_map']['10000090FAA0786B'][2])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2A00000991020012',
|
'2A00000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][3])
|
info['data']['initiator_target_map']['10000090FAA0786A'][3])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2A00000991020012',
|
'2A00000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][3])
|
info['data']['initiator_target_map']['10000090FAA0786B'][3])
|
||||||
|
|
||||||
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
||||||
patch_execute)
|
patch_execute)
|
||||||
@ -998,7 +998,7 @@ class ExportTest(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
|||||||
patch_view_all)
|
patch_view_all)
|
||||||
def test_fc_terminate_connection(self):
|
def test_fc_terminate_connection(self):
|
||||||
self.vol.id = "46045673-41e7-44a7-9333-02f07feab04b"
|
self.vol.id = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||||
connector = {'wwpns': ["1000-0090-FAA0-723A", "1000-0090-FAA0-723B"]}
|
connector = {'wwpns': ["10000090FAA0786A", "10000090FAA0786B"]}
|
||||||
info = self._fc_terminate_connection(self.vol, connector)
|
info = self._fc_terminate_connection(self.vol, connector)
|
||||||
self.assertEqual('fibre_channel', info['driver_volume_type'])
|
self.assertEqual('fibre_channel', info['driver_volume_type'])
|
||||||
self.assertEqual('2100000991020012', info['data']['target_wwn'][0])
|
self.assertEqual('2100000991020012', info['data']['target_wwn'][0])
|
||||||
@ -1007,28 +1007,28 @@ class ExportTest(volume_helper.MStorageDSVDriver, unittest.TestCase):
|
|||||||
self.assertEqual('2A00000991020012', info['data']['target_wwn'][3])
|
self.assertEqual('2A00000991020012', info['data']['target_wwn'][3])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2100000991020012',
|
'2100000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][0])
|
info['data']['initiator_target_map']['10000090FAA0786A'][0])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2100000991020012',
|
'2100000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][0])
|
info['data']['initiator_target_map']['10000090FAA0786B'][0])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2200000991020012',
|
'2200000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][1])
|
info['data']['initiator_target_map']['10000090FAA0786A'][1])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2200000991020012',
|
'2200000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][1])
|
info['data']['initiator_target_map']['10000090FAA0786B'][1])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2900000991020012',
|
'2900000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][2])
|
info['data']['initiator_target_map']['10000090FAA0786A'][2])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2900000991020012',
|
'2900000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][2])
|
info['data']['initiator_target_map']['10000090FAA0786B'][2])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2A00000991020012',
|
'2A00000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723A'][3])
|
info['data']['initiator_target_map']['10000090FAA0786A'][3])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'2A00000991020012',
|
'2A00000991020012',
|
||||||
info['data']['initiator_target_map']['1000-0090-FAA0-723B'][3])
|
info['data']['initiator_target_map']['10000090FAA0786B'][3])
|
||||||
|
|
||||||
|
|
||||||
class DeleteDSVVolume_test(volume_helper.MStorageDSVDriver,
|
class DeleteDSVVolume_test(volume_helper.MStorageDSVDriver,
|
||||||
@ -1063,3 +1063,115 @@ class DeleteDSVVolume_test(volume_helper.MStorageDSVDriver,
|
|||||||
self._cli.query_BV_SV_status.return_value = 'snap/active'
|
self._cli.query_BV_SV_status.return_value = 'snap/active'
|
||||||
ret = self.delete_snapshot(self.vol)
|
ret = self.delete_snapshot(self.vol)
|
||||||
self.assertIsNone(ret)
|
self.assertIsNone(ret)
|
||||||
|
|
||||||
|
|
||||||
|
class NonDisruptiveBackup_test(volume_helper.MStorageDSVDriver,
|
||||||
|
unittest.TestCase):
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.volume_common.MStorageVolumeCommon.'
|
||||||
|
'get_conf_properties', patch_get_conf_properties)
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI.'
|
||||||
|
'view_all', patch_view_all)
|
||||||
|
def setUp(self):
|
||||||
|
self.do_setup(None)
|
||||||
|
self.vol = DummyVolume()
|
||||||
|
self.vol.id = "46045673-41e7-44a7-9333-02f07feab04b"
|
||||||
|
self.volvolume_id = "1febb976-86d0-42ed-9bc0-4aa3e158f27d"
|
||||||
|
self.volsize = 10
|
||||||
|
self.volstatus = None
|
||||||
|
self.volmigration_status = None
|
||||||
|
self._properties = self.get_conf_properties()
|
||||||
|
self._cli = self._properties['cli']
|
||||||
|
self.xml = self._cli.view_all()
|
||||||
|
(self.pools,
|
||||||
|
self.lds,
|
||||||
|
self.ldsets,
|
||||||
|
self.used_ldns,
|
||||||
|
self.hostports,
|
||||||
|
self.max_ld_count) = self.configs(self.xml)
|
||||||
|
|
||||||
|
def test_validate_ld_exist(self):
|
||||||
|
ldname = self._validate_ld_exist(
|
||||||
|
self.lds, self.vol.id, self._properties['ld_name_format'])
|
||||||
|
self.assertEqual('LX:287RbQoP7VdwR1WsPC2fZT', ldname)
|
||||||
|
self.vol.id = "00000000-0000-0000-0000-6b6d96553b4b"
|
||||||
|
with self.assertRaisesRegexp(exception.NotFound,
|
||||||
|
'Logical Disk `LX:XXXXXXXX`'
|
||||||
|
' could not be found.'):
|
||||||
|
self._validate_ld_exist(
|
||||||
|
self.lds, self.vol.id, self._properties['ld_name_format'])
|
||||||
|
|
||||||
|
def test_validate_iscsildset_exist(self):
|
||||||
|
connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255"}
|
||||||
|
ldset = self._validate_iscsildset_exist(self.ldsets, connector)
|
||||||
|
self.assertEqual('LX:OpenStack0', ldset['ldsetname'])
|
||||||
|
connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255XX"}
|
||||||
|
with self.assertRaisesRegexp(exception.NotFound,
|
||||||
|
'Appropriate Logical Disk Set'
|
||||||
|
' could not be found.'):
|
||||||
|
self._validate_iscsildset_exist(self.ldsets, connector)
|
||||||
|
|
||||||
|
def test_validate_fcldset_exist(self):
|
||||||
|
connector = {'wwpns': ["10000090FAA0786A", "10000090FAA0786B"]}
|
||||||
|
ldset = self._validate_fcldset_exist(self.ldsets, connector)
|
||||||
|
self.assertEqual('LX:OpenStack1', ldset['ldsetname'])
|
||||||
|
connector = {'wwpns': ["10000090FAA0786X", "10000090FAA0786Y"]}
|
||||||
|
with self.assertRaisesRegexp(exception.NotFound,
|
||||||
|
'Appropriate Logical Disk Set'
|
||||||
|
' could not be found.'):
|
||||||
|
self._validate_fcldset_exist(self.ldsets, connector)
|
||||||
|
|
||||||
|
def test_enumerate_iscsi_portals(self):
|
||||||
|
connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255"}
|
||||||
|
ldset = self._validate_iscsildset_exist(self.ldsets, connector)
|
||||||
|
self.assertEqual('LX:OpenStack0', ldset['ldsetname'])
|
||||||
|
portal = self._enumerate_iscsi_portals(self.hostports, ldset)
|
||||||
|
self.assertEqual('192.168.1.90:3260', portal[0])
|
||||||
|
self.assertEqual('192.168.1.91:3260', portal[1])
|
||||||
|
self.assertEqual('192.168.2.92:3260', portal[2])
|
||||||
|
self.assertEqual('192.168.2.93:3260', portal[3])
|
||||||
|
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.volume_common.MStorageVolumeCommon.'
|
||||||
|
'get_conf_properties', patch_get_conf_properties)
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI.'
|
||||||
|
'view_all', patch_view_all)
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
||||||
|
patch_execute)
|
||||||
|
def test_initialize_connection_snapshot(self):
|
||||||
|
connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255"}
|
||||||
|
loc = "127.0.0.1:3260:1 iqn.2010-10.org.openstack:volume-00000001 88"
|
||||||
|
self.vol.provider_location = loc
|
||||||
|
ret = self.iscsi_initialize_connection_snapshot(self.vol, connector)
|
||||||
|
self.assertIsNotNone(ret)
|
||||||
|
self.assertEqual('iscsi', ret['driver_volume_type'])
|
||||||
|
|
||||||
|
connector = {'wwpns': ["10000090FAA0786A", "10000090FAA0786B"]}
|
||||||
|
ret = self.fc_initialize_connection_snapshot(self.vol, connector)
|
||||||
|
self.assertIsNotNone(ret)
|
||||||
|
self.assertEqual('fibre_channel', ret['driver_volume_type'])
|
||||||
|
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.volume_common.MStorageVolumeCommon.'
|
||||||
|
'get_conf_properties', patch_get_conf_properties)
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI.'
|
||||||
|
'view_all', patch_view_all)
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
||||||
|
patch_execute)
|
||||||
|
def test_terminate_connection_snapshot(self):
|
||||||
|
connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255"}
|
||||||
|
self.iscsi_terminate_connection_snapshot(self.vol, connector)
|
||||||
|
|
||||||
|
connector = {'wwpns': ["10000090FAA0786A", "10000090FAA0786B"]}
|
||||||
|
ret = self.fc_terminate_connection_snapshot(self.vol, connector)
|
||||||
|
self.assertEqual('fibre_channel', ret['driver_volume_type'])
|
||||||
|
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.volume_common.MStorageVolumeCommon.'
|
||||||
|
'get_conf_properties', patch_get_conf_properties)
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI.'
|
||||||
|
'view_all', patch_view_all)
|
||||||
|
@mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute',
|
||||||
|
patch_execute)
|
||||||
|
def test_remove_export_snapshot(self):
|
||||||
|
self.remove_export_snapshot(None, self.vol)
|
||||||
|
|
||||||
|
def test_backup_use_temp_snapshot(self):
|
||||||
|
ret = self.backup_use_temp_snapshot()
|
||||||
|
self.assertTrue(ret)
|
||||||
|
@ -582,6 +582,35 @@ class MStorageISMCLI(object):
|
|||||||
if force_delete:
|
if force_delete:
|
||||||
self.unbind(ldname)
|
self.unbind(ldname)
|
||||||
|
|
||||||
|
def lvbind(self, bvname, lvname, lvnumber):
|
||||||
|
"""Link Volume create."""
|
||||||
|
cmd = ('iSMcfg lvbind -bvname %(bvname)s '
|
||||||
|
'-lvn %(lvnumber)d -lvname %(lvname)s'
|
||||||
|
% {'bvname': bvname,
|
||||||
|
'lvnumber': lvnumber,
|
||||||
|
'lvname': lvname})
|
||||||
|
self._execute(cmd)
|
||||||
|
|
||||||
|
def lvunbind(self, lvname):
|
||||||
|
"""Link Volume delete."""
|
||||||
|
cmd = ('iSMcfg lvunbind -ldname %(lvname)s'
|
||||||
|
% {'lvname': lvname})
|
||||||
|
self._execute(cmd)
|
||||||
|
|
||||||
|
def lvlink(self, svname, lvname):
|
||||||
|
"""Link to snapshot volume."""
|
||||||
|
cmd = ('iSMsc_link -lv %(lvname)s -lvflg ld '
|
||||||
|
'-sv %(svname)s -svflg ld -lvacc ro'
|
||||||
|
% {'lvname': lvname,
|
||||||
|
'svname': svname})
|
||||||
|
self._execute(cmd)
|
||||||
|
|
||||||
|
def lvunlink(self, lvname):
|
||||||
|
"""Unlink from snapshot volume."""
|
||||||
|
cmd = ('iSMsc_unlink -lv %(lvname)s -lvflg ld'
|
||||||
|
% {'lvname': lvname})
|
||||||
|
self._execute(cmd)
|
||||||
|
|
||||||
|
|
||||||
class UnpairWait(object):
|
class UnpairWait(object):
|
||||||
error_updates = {'status': 'error',
|
error_updates = {'status': 'error',
|
||||||
|
@ -47,6 +47,19 @@ class MStorageISCSIDriver(volume_helper.MStorageDSVDriver,
|
|||||||
def terminate_connection(self, volume, connector, **kwargs):
|
def terminate_connection(self, volume, connector, **kwargs):
|
||||||
return self.iscsi_terminate_connection(volume, connector)
|
return self.iscsi_terminate_connection(volume, connector)
|
||||||
|
|
||||||
|
def create_export_snapshot(self, context, snapshot, connector):
|
||||||
|
return self.iscsi_do_export_snapshot(context, snapshot, connector)
|
||||||
|
|
||||||
|
def initialize_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||||
|
return self.iscsi_initialize_connection_snapshot(snapshot,
|
||||||
|
connector,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def terminate_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||||
|
return self.iscsi_terminate_connection_snapshot(snapshot,
|
||||||
|
connector,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
|
||||||
@interface.volumedriver
|
@interface.volumedriver
|
||||||
class MStorageFCDriver(volume_helper.MStorageDSVDriver,
|
class MStorageFCDriver(volume_helper.MStorageDSVDriver,
|
||||||
@ -74,3 +87,16 @@ class MStorageFCDriver(volume_helper.MStorageDSVDriver,
|
|||||||
@fczm_utils.remove_fc_zone
|
@fczm_utils.remove_fc_zone
|
||||||
def terminate_connection(self, volume, connector, **kwargs):
|
def terminate_connection(self, volume, connector, **kwargs):
|
||||||
return self.fc_terminate_connection(volume, connector)
|
return self.fc_terminate_connection(volume, connector)
|
||||||
|
|
||||||
|
def create_export_snapshot(self, context, snapshot, connector):
|
||||||
|
return self.fc_do_export_snapshot(context, snapshot, connector)
|
||||||
|
|
||||||
|
def initialize_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||||
|
return self.fc_initialize_connection_snapshot(snapshot,
|
||||||
|
connector,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def terminate_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||||
|
return self.fc_terminate_connection_snapshot(snapshot,
|
||||||
|
connector,
|
||||||
|
**kwargs)
|
||||||
|
@ -150,7 +150,7 @@ def convert_to_id(value62):
|
|||||||
class MStorageVolumeCommon(object):
|
class MStorageVolumeCommon(object):
|
||||||
"""M-Series Storage volume common class."""
|
"""M-Series Storage volume common class."""
|
||||||
|
|
||||||
VERSION = '1.8.2'
|
VERSION = '1.9.1'
|
||||||
WIKI_NAME = 'NEC_Cinder_CI'
|
WIKI_NAME = 'NEC_Cinder_CI'
|
||||||
|
|
||||||
def do_setup(self, context):
|
def do_setup(self, context):
|
||||||
@ -876,7 +876,7 @@ class MStorageVolumeCommon(object):
|
|||||||
specs['upperreport'] = None
|
specs['upperreport'] = None
|
||||||
LOG.debug('qos parameter not found.')
|
LOG.debug('qos parameter not found.')
|
||||||
else:
|
else:
|
||||||
if ('upperlimit' in specs) and (specs['upperlimit'] is not None):
|
if 'upperlimit' in specs and specs['upperlimit'] is not None:
|
||||||
if self.validates_number(specs['upperlimit']) is True:
|
if self.validates_number(specs['upperlimit']) is True:
|
||||||
upper_limit = int(specs['upperlimit'], 10)
|
upper_limit = int(specs['upperlimit'], 10)
|
||||||
if ((upper_limit != 0) and
|
if ((upper_limit != 0) and
|
||||||
@ -889,7 +889,7 @@ class MStorageVolumeCommon(object):
|
|||||||
else:
|
else:
|
||||||
specs['upperlimit'] = None
|
specs['upperlimit'] = None
|
||||||
|
|
||||||
if ('lowerlimit' in specs) and (specs['lowerlimit'] is not None):
|
if 'lowerlimit' in specs and specs['lowerlimit'] is not None:
|
||||||
if self.validates_number(specs['lowerlimit']) is True:
|
if self.validates_number(specs['lowerlimit']) is True:
|
||||||
lower_limit = int(specs['lowerlimit'], 10)
|
lower_limit = int(specs['lowerlimit'], 10)
|
||||||
if (lower_limit != 0 and (lower_limit < 10 or
|
if (lower_limit != 0 and (lower_limit < 10 or
|
||||||
|
@ -85,7 +85,7 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
for pool in nominated_pools:
|
for pool in nominated_pools:
|
||||||
nld = len(pool['ld_list'])
|
nld = len(pool['ld_list'])
|
||||||
if (nld < self._numofld_per_pool and
|
if (nld < self._numofld_per_pool and
|
||||||
((selected_pool == -1) or (min_ldn > nld))):
|
(selected_pool == -1 or min_ldn > nld)):
|
||||||
selected_pool = pool['pool_num']
|
selected_pool = pool['pool_num']
|
||||||
min_ldn = nld
|
min_ldn = nld
|
||||||
if selected_pool < 0:
|
if selected_pool < 0:
|
||||||
@ -135,7 +135,7 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
size = option * units.Gi
|
size = option * units.Gi
|
||||||
pools = [pool for (pn, pool) in pools.items()
|
pools = [pool for (pn, pool) in pools.items()
|
||||||
if pool['free'] >= size and
|
if pool['free'] >= size and
|
||||||
(pn in self._properties['pool_backup_pools'])]
|
pn in self._properties['pool_backup_pools']]
|
||||||
return self._return_poolnumber(pools)
|
return self._return_poolnumber(pools)
|
||||||
|
|
||||||
def _select_volddr_poolnumber(self, volume, pools, xml, option):
|
def _select_volddr_poolnumber(self, volume, pools, xml, option):
|
||||||
@ -143,7 +143,7 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
size = option * units.Gi
|
size = option * units.Gi
|
||||||
pools = [pool for (pn, pool) in pools.items()
|
pools = [pool for (pn, pool) in pools.items()
|
||||||
if pool['free'] >= size and
|
if pool['free'] >= size and
|
||||||
(pn in self._properties['pool_pools'])]
|
pn in self._properties['pool_pools']]
|
||||||
return self._return_poolnumber(pools)
|
return self._return_poolnumber(pools)
|
||||||
|
|
||||||
def _bind_ld(self, volume, capacity, validator,
|
def _bind_ld(self, volume, capacity, validator,
|
||||||
@ -214,6 +214,78 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
'ldn': selected_ldn, 'pool': selected_pool})
|
'ldn': selected_ldn, 'pool': selected_pool})
|
||||||
return ldname, selected_ldn, selected_pool
|
return ldname, selected_ldn, selected_pool
|
||||||
|
|
||||||
|
def _validate_ld_exist(self, lds, vol_id, name_format):
|
||||||
|
ldname = self.get_ldname(vol_id, name_format)
|
||||||
|
if ldname not in lds:
|
||||||
|
msg = _('Logical Disk `%s` could not be found.') % ldname
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.NotFound(msg)
|
||||||
|
return ldname
|
||||||
|
|
||||||
|
def _validate_iscsildset_exist(self, ldsets, connector, metadata=None):
|
||||||
|
ldset = self.get_ldset(ldsets, metadata)
|
||||||
|
if ldset is None:
|
||||||
|
for tldset in six.itervalues(ldsets):
|
||||||
|
if 'initiator_list' not in tldset:
|
||||||
|
continue
|
||||||
|
n = tldset['initiator_list'].count(connector['initiator'])
|
||||||
|
if n > 0:
|
||||||
|
ldset = tldset
|
||||||
|
break
|
||||||
|
if ldset is None:
|
||||||
|
msg = _('Appropriate Logical Disk Set could not be found.')
|
||||||
|
raise exception.NotFound(msg)
|
||||||
|
if len(ldset['portal_list']) < 1:
|
||||||
|
msg = (_('Logical Disk Set `%s` has no portal.') %
|
||||||
|
ldset['ldsetname'])
|
||||||
|
raise exception.NotFound(msg)
|
||||||
|
return ldset
|
||||||
|
|
||||||
|
def _validate_fcldset_exist(self, ldsets, connector, metadata=None):
|
||||||
|
ldset = self.get_ldset(ldsets, metadata)
|
||||||
|
if ldset is None:
|
||||||
|
for conect in connector['wwpns']:
|
||||||
|
length = len(conect)
|
||||||
|
findwwpn = '-'.join([conect[i:i + 4]
|
||||||
|
for i in range(0, length, 4)])
|
||||||
|
findwwpn = findwwpn.upper()
|
||||||
|
for tldset in six.itervalues(ldsets):
|
||||||
|
if 'wwpn' in tldset and findwwpn in tldset['wwpn']:
|
||||||
|
ldset = tldset
|
||||||
|
break
|
||||||
|
if ldset is not None:
|
||||||
|
break
|
||||||
|
if ldset is None:
|
||||||
|
msg = _('Appropriate Logical Disk Set could not be found.')
|
||||||
|
raise exception.NotFound(msg)
|
||||||
|
return ldset
|
||||||
|
|
||||||
|
def _enumerate_iscsi_portals(self, hostports, ldset, prefered_director=0):
|
||||||
|
nominated = []
|
||||||
|
for director in [prefered_director, 1 - prefered_director]:
|
||||||
|
if director not in hostports:
|
||||||
|
continue
|
||||||
|
dirportal = []
|
||||||
|
for port in hostports[director]:
|
||||||
|
if not port['protocol'].lower() == 'iscsi':
|
||||||
|
continue
|
||||||
|
for portal in ldset['portal_list']:
|
||||||
|
if portal.startswith(port['ip'] + ':'):
|
||||||
|
dirportal.append(portal)
|
||||||
|
break
|
||||||
|
if (self._properties['portal_number'] > 0 and
|
||||||
|
len(dirportal) > self._properties['portal_number']):
|
||||||
|
nominated.extend(random.sample(
|
||||||
|
dirportal, self._properties['portal_number']))
|
||||||
|
else:
|
||||||
|
nominated.extend(dirportal)
|
||||||
|
|
||||||
|
if len(nominated) == 0:
|
||||||
|
raise exception.NotFound(
|
||||||
|
_('No portal matches to any host ports.'))
|
||||||
|
|
||||||
|
return nominated
|
||||||
|
|
||||||
def create_volume(self, volume):
|
def create_volume(self, volume):
|
||||||
msgparm = ('Volume ID = %(id)s, Size = %(size)dGB'
|
msgparm = ('Volume ID = %(id)s, Size = %(size)dGB'
|
||||||
% {'id': volume.id, 'size': volume.size})
|
% {'id': volume.id, 'size': volume.size})
|
||||||
@ -320,16 +392,9 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
||||||
self.configs(xml))
|
self.configs(xml))
|
||||||
|
|
||||||
ldname = self.get_ldname(volume.id,
|
|
||||||
self._properties['ld_name_format'])
|
|
||||||
|
|
||||||
# get volume.
|
# get volume.
|
||||||
if ldname not in lds:
|
ldname = self._validate_ld_exist(
|
||||||
msg = (_('Logical Disk has unbound already '
|
lds, volume.id, self._properties['ld_name_format'])
|
||||||
'(name=%(name)s, id=%(id)s).') %
|
|
||||||
{'name': ldname, 'id': volume.id})
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
|
||||||
ld = lds[ldname]
|
ld = lds[ldname]
|
||||||
ldn = ld['ldn']
|
ldn = ld['ldn']
|
||||||
|
|
||||||
@ -455,8 +520,10 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
"""Validate source volume information."""
|
"""Validate source volume information."""
|
||||||
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
||||||
self.configs(xml))
|
self.configs(xml))
|
||||||
ldname = self.get_ldname(volume.id,
|
|
||||||
self._properties['ld_name_format'])
|
# get ld object
|
||||||
|
ldname = self._validate_ld_exist(
|
||||||
|
lds, volume.id, self._properties['ld_name_format'])
|
||||||
|
|
||||||
# check volume status.
|
# check volume status.
|
||||||
if volume.status != 'available':
|
if volume.status != 'available':
|
||||||
@ -464,11 +531,7 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
# get ld object and check rpl attribute.
|
# check rpl attribute.
|
||||||
if ldname not in lds:
|
|
||||||
msg = _('Logical Disk `%s` does not exist.') % ldname
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.NotFound(msg)
|
|
||||||
ld = lds[ldname]
|
ld = lds[ldname]
|
||||||
if ld['Purpose'] != '---':
|
if ld['Purpose'] != '---':
|
||||||
msg = (_('Specified Logical Disk %(ld)s '
|
msg = (_('Specified Logical Disk %(ld)s '
|
||||||
@ -574,6 +637,9 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
def check_for_export(self, context, volume_id):
|
def check_for_export(self, context, volume_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def backup_use_temp_snapshot(self):
|
||||||
|
return True
|
||||||
|
|
||||||
def iscsi_do_export(self, _ctx, volume, connector, ensure=False):
|
def iscsi_do_export(self, _ctx, volume, connector, ensure=False):
|
||||||
msgparm = ('Volume ID = %(id)s, '
|
msgparm = ('Volume ID = %(id)s, '
|
||||||
'Initiator Name = %(initiator)s'
|
'Initiator Name = %(initiator)s'
|
||||||
@ -590,6 +656,9 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
{'msgparm': msgparm, 'exception': e})
|
{'msgparm': msgparm, 'exception': e})
|
||||||
|
|
||||||
def _iscsi_do_export(self, _ctx, volume, connector, ensure):
|
def _iscsi_do_export(self, _ctx, volume, connector, ensure):
|
||||||
|
LOG.debug('_iscsi_do_export'
|
||||||
|
'(Volume ID = %(id)s, connector = %(connector)s) Start.',
|
||||||
|
{'id': volume.id, 'connector': connector})
|
||||||
while True:
|
while True:
|
||||||
xml = self._cli.view_all(self._properties['ismview_path'])
|
xml = self._cli.view_all(self._properties['ismview_path'])
|
||||||
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
||||||
@ -607,32 +676,21 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
LOG.debug('image to volume or volume to image:%s',
|
LOG.debug('image to volume or volume to image:%s',
|
||||||
volume.status)
|
volume.status)
|
||||||
# migrate.
|
# migrate.
|
||||||
elif (volume.migration_status is not None and
|
elif (hasattr(volume, 'migration_status') and
|
||||||
|
volume.migration_status is not None and
|
||||||
self._properties['ldset_controller_node_name'] != ''):
|
self._properties['ldset_controller_node_name'] != ''):
|
||||||
metadata['ldset'] = (
|
metadata['ldset'] = (
|
||||||
self._properties['ldset_controller_node_name'])
|
self._properties['ldset_controller_node_name'])
|
||||||
LOG.debug('migrate:%s', volume.migration_status)
|
LOG.debug('migrate:%s', volume.migration_status)
|
||||||
|
|
||||||
ldset = self.get_ldset(ldsets, metadata)
|
ldset = self._validate_iscsildset_exist(
|
||||||
if ldset is None:
|
ldsets, connector, metadata)
|
||||||
for tldset in six.itervalues(ldsets):
|
|
||||||
n = tldset['initiator_list'].count(connector['initiator'])
|
|
||||||
if ('initiator_list' in tldset and n > 0):
|
|
||||||
ldset = tldset
|
|
||||||
LOG.debug('ldset=%s.', ldset)
|
|
||||||
break
|
|
||||||
if ldset is None:
|
|
||||||
msg = _('Appropriate Logical Disk Set could not be found.')
|
|
||||||
raise exception.NotFound(msg)
|
|
||||||
|
|
||||||
if len(ldset['portal_list']) < 1:
|
|
||||||
msg = (_('Logical Disk Set `%s` has no portal.') %
|
|
||||||
ldset['ldsetname'])
|
|
||||||
raise exception.NotFound(msg)
|
|
||||||
|
|
||||||
|
if (hasattr(volume, 'migration_status') and
|
||||||
|
volume.migration_status is not None and
|
||||||
|
'target:' in volume.migration_status):
|
||||||
LOG.debug('migration_status:%s', volume.migration_status)
|
LOG.debug('migration_status:%s', volume.migration_status)
|
||||||
migstat = volume.migration_status
|
migstat = volume.migration_status
|
||||||
if migstat is not None and 'target:' in migstat:
|
|
||||||
index = migstat.find('target:')
|
index = migstat.find('target:')
|
||||||
if index != -1:
|
if index != -1:
|
||||||
migstat = migstat[len('target:'):]
|
migstat = migstat[len('target:'):]
|
||||||
@ -675,29 +733,8 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
|
|
||||||
# enumerate portals for iscsi multipath.
|
# enumerate portals for iscsi multipath.
|
||||||
prefered_director = ld['pool_num'] % 2
|
prefered_director = ld['pool_num'] % 2
|
||||||
nominated = []
|
nominated = self._enumerate_iscsi_portals(hostports, ldset,
|
||||||
for director in [prefered_director, 1 - prefered_director]:
|
prefered_director)
|
||||||
if director not in hostports:
|
|
||||||
continue
|
|
||||||
dirportal = []
|
|
||||||
for port in hostports[director]:
|
|
||||||
if not port['protocol'] == 'iSCSI':
|
|
||||||
continue
|
|
||||||
for portal in ldset['portal_list']:
|
|
||||||
if portal.startswith(port['ip'] + ':'):
|
|
||||||
dirportal.append(portal)
|
|
||||||
break
|
|
||||||
if ((self._properties['portal_number'] > 0) and
|
|
||||||
(len(dirportal) > self._properties['portal_number'])):
|
|
||||||
nominated.extend(random.sample(
|
|
||||||
dirportal, self._properties['portal_number']))
|
|
||||||
else:
|
|
||||||
nominated.extend(dirportal)
|
|
||||||
|
|
||||||
if len(nominated) == 0:
|
|
||||||
raise exception.NotFound(
|
|
||||||
_('Any portal not match to any host ports.'))
|
|
||||||
|
|
||||||
location = ('%(list)s,1 %(iqn)s %(lun)d'
|
location = ('%(list)s,1 %(iqn)s %(lun)d'
|
||||||
% {'list': ';'.join(nominated),
|
% {'list': ';'.join(nominated),
|
||||||
'iqn': ldset['lds'][ld['ldn']]['iqn'],
|
'iqn': ldset['lds'][ld['ldn']]['iqn'],
|
||||||
@ -725,6 +762,9 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
{'msgparm': msgparm, 'exception': e})
|
{'msgparm': msgparm, 'exception': e})
|
||||||
|
|
||||||
def _fc_do_export(self, _ctx, volume, connector, ensure):
|
def _fc_do_export(self, _ctx, volume, connector, ensure):
|
||||||
|
LOG.debug('_fc_do_export'
|
||||||
|
'(Volume ID = %(id)s, connector = %(connector)s) Start.',
|
||||||
|
{'id': volume.id, 'connector': connector})
|
||||||
while True:
|
while True:
|
||||||
xml = self._cli.view_all(self._properties['ismview_path'])
|
xml = self._cli.view_all(self._properties['ismview_path'])
|
||||||
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
||||||
@ -742,30 +782,15 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
LOG.debug('image to volume or volume to image:%s',
|
LOG.debug('image to volume or volume to image:%s',
|
||||||
volume.status)
|
volume.status)
|
||||||
# migrate.
|
# migrate.
|
||||||
elif (volume.migration_status is not None and
|
elif (hasattr(volume, 'migration_status') and
|
||||||
|
volume.migration_status is not None and
|
||||||
self._properties['ldset_controller_node_name'] != ''
|
self._properties['ldset_controller_node_name'] != ''
|
||||||
):
|
):
|
||||||
metadata['ldset'] = (
|
metadata['ldset'] = (
|
||||||
self._properties['ldset_controller_node_name'])
|
self._properties['ldset_controller_node_name'])
|
||||||
LOG.debug('migrate:%s', volume.migration_status)
|
LOG.debug('migrate:%s', volume.migration_status)
|
||||||
|
|
||||||
ldset = self.get_ldset(ldsets, metadata)
|
ldset = self._validate_fcldset_exist(ldsets, connector, metadata)
|
||||||
if ldset is None:
|
|
||||||
for conect in connector['wwpns']:
|
|
||||||
length = len(conect)
|
|
||||||
findwwpn = '-'.join([conect[i:i + 4]
|
|
||||||
for i in range(0, length, 4)])
|
|
||||||
findwwpn = findwwpn.upper()
|
|
||||||
for tldset in six.itervalues(ldsets):
|
|
||||||
if 'wwpn' in tldset and findwwpn in tldset['wwpn']:
|
|
||||||
ldset = tldset
|
|
||||||
LOG.debug('ldset=%s.', ldset)
|
|
||||||
break
|
|
||||||
if ldset is not None:
|
|
||||||
break
|
|
||||||
if ldset is None:
|
|
||||||
msg = _('Logical Disk Set could not be found.')
|
|
||||||
raise exception.NotFound(msg)
|
|
||||||
|
|
||||||
# get free lun.
|
# get free lun.
|
||||||
luns = []
|
luns = []
|
||||||
@ -779,9 +804,11 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
break
|
break
|
||||||
target_lun += 1
|
target_lun += 1
|
||||||
|
|
||||||
|
if (hasattr(volume, 'migration_status') and
|
||||||
|
volume.migration_status is not None and
|
||||||
|
'target:' in volume.migration_status):
|
||||||
LOG.debug('migration_status:%s', volume.migration_status)
|
LOG.debug('migration_status:%s', volume.migration_status)
|
||||||
migstat = volume.migration_status
|
migstat = volume.migration_status
|
||||||
if migstat is not None and 'target:' in migstat:
|
|
||||||
index = migstat.find('target:')
|
index = migstat.find('target:')
|
||||||
if index != -1:
|
if index != -1:
|
||||||
migstat = migstat[len('target:'):]
|
migstat = migstat[len('target:'):]
|
||||||
@ -828,6 +855,127 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
{'ensure': 'ensure_' if ensure else '',
|
{'ensure': 'ensure_' if ensure else '',
|
||||||
'ld': ldname})
|
'ld': ldname})
|
||||||
|
|
||||||
|
def iscsi_do_export_snapshot(self, context, snapshot, connector):
|
||||||
|
"""Exports the snapshot."""
|
||||||
|
msgparm = 'Snapshot ID = %s' % snapshot.id
|
||||||
|
try:
|
||||||
|
ret = self._iscsi_do_export_snapshot(
|
||||||
|
context, snapshot, connector,
|
||||||
|
self._properties['diskarray_name'])
|
||||||
|
LOG.info('Create Export Snapshot (%s)', msgparm)
|
||||||
|
return ret
|
||||||
|
except exception.CinderException as e:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.warning('Failed to Create Export Snapshot '
|
||||||
|
'(%(msgparm)s) (%(exception)s)',
|
||||||
|
{'msgparm': msgparm, 'exception': e})
|
||||||
|
|
||||||
|
@coordination.synchronized('mstorage_bind_execute_{diskarray_name}')
|
||||||
|
def _iscsi_do_export_snapshot(self, context, snapshot, connector,
|
||||||
|
diskarray_name):
|
||||||
|
LOG.debug('_iscsi_do_export_snapshot(Snapshot ID = %s) Start.',
|
||||||
|
snapshot.id)
|
||||||
|
xml = self._cli.view_all(self._properties['ismview_path'])
|
||||||
|
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
||||||
|
self.configs(xml))
|
||||||
|
|
||||||
|
LOG.debug('validate data.')
|
||||||
|
svname = self._validate_ld_exist(
|
||||||
|
lds, snapshot.id, self._properties['ld_name_format'])
|
||||||
|
bvname = self._validate_ld_exist(
|
||||||
|
lds, snapshot.volume_id, self._properties['ld_name_format'])
|
||||||
|
lvname = svname + '_l'
|
||||||
|
ldset = self._validate_iscsildset_exist(ldsets, connector)
|
||||||
|
svstatus = self._cli.query_BV_SV_status(bvname[3:], svname[3:])
|
||||||
|
if svstatus != 'snap/active':
|
||||||
|
msg = _('Logical Disk (%s) is invalid snapshot.') % svname
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
lvldn = self._select_ldnumber(used_ldns, max_ld_count)
|
||||||
|
|
||||||
|
LOG.debug('configure backend.')
|
||||||
|
self._cli.lvbind(bvname, lvname[3:], lvldn)
|
||||||
|
self._cli.lvlink(svname[3:], lvname[3:])
|
||||||
|
self._cli.addldsetld(ldset['ldsetname'], lvname)
|
||||||
|
|
||||||
|
xml = self._cli.view_all(self._properties['ismview_path'])
|
||||||
|
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
||||||
|
self.configs(xml))
|
||||||
|
ld = lds[lvname]
|
||||||
|
ldset = self._validate_iscsildset_exist(ldsets, connector)
|
||||||
|
|
||||||
|
LOG.debug('enumerate portals for iscsi multipath.')
|
||||||
|
prefered_director = ld['pool_num'] % 2
|
||||||
|
nominated = self._enumerate_iscsi_portals(hostports, ldset,
|
||||||
|
prefered_director)
|
||||||
|
location = ('%(list)s,1 %(iqn)s %(lun)d'
|
||||||
|
% {'list': ';'.join(nominated),
|
||||||
|
'iqn': ldset['lds'][ld['ldn']]['iqn'],
|
||||||
|
'lun': ldset['lds'][ld['ldn']]['lun']})
|
||||||
|
|
||||||
|
LOG.debug('create_export_snapshot location:(%s)', location)
|
||||||
|
return {'provider_location': location}
|
||||||
|
|
||||||
|
def fc_do_export_snapshot(self, context, snapshot, connector,
|
||||||
|
ensure=False):
|
||||||
|
msgparm = ('Volume ID = %(id)s, '
|
||||||
|
'Initiator WWPNs = %(wwpns)s'
|
||||||
|
% {'id': snapshot.id,
|
||||||
|
'wwpns': connector['wwpns']})
|
||||||
|
try:
|
||||||
|
ret = self._fc_do_export_snapshot(
|
||||||
|
context, snapshot, connector, ensure,
|
||||||
|
self._properties['diskarray_name'])
|
||||||
|
LOG.info('Created FC Export snapshot(%s)', msgparm)
|
||||||
|
return ret
|
||||||
|
except exception.CinderException as e:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.warning('Failed to Create FC Export snapshot'
|
||||||
|
'(%(msgparm)s) (%(exception)s)',
|
||||||
|
{'msgparm': msgparm, 'exception': e})
|
||||||
|
|
||||||
|
@coordination.synchronized('mstorage_bind_execute_{diskarray_name}')
|
||||||
|
def _fc_do_export_snapshot(self, context, snapshot, connector, ensure,
|
||||||
|
diskarray_name):
|
||||||
|
LOG.debug('_fc_do_export_snapshot(Snapshot ID = %s) Start.',
|
||||||
|
snapshot.id)
|
||||||
|
xml = self._cli.view_all(self._properties['ismview_path'])
|
||||||
|
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
||||||
|
self.configs(xml))
|
||||||
|
|
||||||
|
LOG.debug('validate data.')
|
||||||
|
svname = self._validate_ld_exist(
|
||||||
|
lds, snapshot.id, self._properties['ld_name_format'])
|
||||||
|
bvname = self._validate_ld_exist(
|
||||||
|
lds, snapshot.volume_id, self._properties['ld_name_format'])
|
||||||
|
lvname = svname + '_l'
|
||||||
|
ldset = self._validate_fcldset_exist(ldsets, connector)
|
||||||
|
svstatus = self._cli.query_BV_SV_status(bvname[3:], svname[3:])
|
||||||
|
if svstatus != 'snap/active':
|
||||||
|
msg = _('Logical Disk (%s) is invalid snapshot.') % svname
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
lvldn = self._select_ldnumber(used_ldns, max_ld_count)
|
||||||
|
|
||||||
|
LOG.debug('configure backend.')
|
||||||
|
self._cli.lvbind(bvname, lvname[3:], lvldn)
|
||||||
|
self._cli.lvlink(svname[3:], lvname[3:])
|
||||||
|
|
||||||
|
luns = []
|
||||||
|
ldsetlds = ldset['lds']
|
||||||
|
for ld in six.itervalues(ldsetlds):
|
||||||
|
luns.append(ld['lun'])
|
||||||
|
target_lun = 0
|
||||||
|
for lun in sorted(luns):
|
||||||
|
if target_lun < lun:
|
||||||
|
break
|
||||||
|
target_lun += 1
|
||||||
|
|
||||||
|
self._cli.addldsetld(ldset['ldsetname'], lvname, target_lun)
|
||||||
|
LOG.debug('Add LD `%(ld)s` to LD Set `%(ldset)s`.',
|
||||||
|
{'ld': lvname, 'ldset': ldset['ldsetname']})
|
||||||
|
LOG.debug('%(ensure)sexport LD `%(ld)s`.',
|
||||||
|
{'ensure': 'ensure_' if ensure else '',
|
||||||
|
'ld': lvname})
|
||||||
|
|
||||||
def remove_export(self, context, volume):
|
def remove_export(self, context, volume):
|
||||||
msgparm = 'Volume ID = %s' % volume.id
|
msgparm = 'Volume ID = %s' % volume.id
|
||||||
try:
|
try:
|
||||||
@ -859,7 +1007,8 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
LOG.debug('image to volume or volume to image:%s',
|
LOG.debug('image to volume or volume to image:%s',
|
||||||
volume.status)
|
volume.status)
|
||||||
# migrate.
|
# migrate.
|
||||||
elif (volume.migration_status is not None and
|
elif (hasattr(volume, 'migration_status') and
|
||||||
|
volume.migration_status is not None and
|
||||||
self._properties['ldset_controller_node_name'] != ''
|
self._properties['ldset_controller_node_name'] != ''
|
||||||
):
|
):
|
||||||
metadata['ldset'] = (
|
metadata['ldset'] = (
|
||||||
@ -868,9 +1017,11 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
|
|
||||||
ldset = self.get_ldset(ldsets, metadata)
|
ldset = self.get_ldset(ldsets, metadata)
|
||||||
|
|
||||||
|
if (hasattr(volume, 'migration_status') and
|
||||||
|
volume.migration_status is not None and
|
||||||
|
'target:' in volume.migration_status):
|
||||||
LOG.debug('migration_status:%s', volume.migration_status)
|
LOG.debug('migration_status:%s', volume.migration_status)
|
||||||
migstat = volume.migration_status
|
migstat = volume.migration_status
|
||||||
if migstat is not None and 'target:' in migstat:
|
|
||||||
index = migstat.find('target:')
|
index = migstat.find('target:')
|
||||||
if index != -1:
|
if index != -1:
|
||||||
migstat = migstat[len('target:'):]
|
migstat = migstat[len('target:'):]
|
||||||
@ -928,6 +1079,69 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
|
|
||||||
LOG.debug('_remove_export(Volume ID = %s) End.', volume.id)
|
LOG.debug('_remove_export(Volume ID = %s) End.', volume.id)
|
||||||
|
|
||||||
|
def remove_export_snapshot(self, context, snapshot):
|
||||||
|
"""Removes an export for a snapshot."""
|
||||||
|
msgparm = 'Snapshot ID = %s' % snapshot.id
|
||||||
|
try:
|
||||||
|
self._remove_export_snapshot(context, snapshot)
|
||||||
|
LOG.info('Removed Export Snapshot(%s)', msgparm)
|
||||||
|
except exception.CinderException as e:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.warning('Failed to Remove Export Snapshot'
|
||||||
|
'(%(msgparm)s) (%(exception)s)',
|
||||||
|
{'msgparm': msgparm, 'exception': e})
|
||||||
|
|
||||||
|
def _remove_export_snapshot(self, context, snapshot):
|
||||||
|
LOG.debug('_remove_export_snapshot(Snapshot ID = %s) Start.',
|
||||||
|
snapshot.id)
|
||||||
|
xml = self._cli.view_all(self._properties['ismview_path'])
|
||||||
|
pools, lds, ldsets, used_ldns, hostports, max_ld_count = (
|
||||||
|
self.configs(xml))
|
||||||
|
|
||||||
|
LOG.debug('validate data.')
|
||||||
|
svname = self._validate_ld_exist(
|
||||||
|
lds, snapshot.id, self._properties['ld_name_format'])
|
||||||
|
lvname = svname + '_l'
|
||||||
|
if lvname not in lds:
|
||||||
|
LOG.debug('Logical Disk `%s` is already unexported.', lvname)
|
||||||
|
return
|
||||||
|
|
||||||
|
ld = lds[lvname]
|
||||||
|
ldsetlist = []
|
||||||
|
if ld is None:
|
||||||
|
msg = _('Exported snapshot could not be found.')
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
for tldset in six.itervalues(ldsets):
|
||||||
|
if ld['ldn'] in tldset['lds']:
|
||||||
|
ldsetlist.append(tldset)
|
||||||
|
if len(ldsetlist) == 0:
|
||||||
|
LOG.debug('Specified Logical Disk is already removed.')
|
||||||
|
return
|
||||||
|
|
||||||
|
LOG.debug('configure backend.')
|
||||||
|
for tagetldset in ldsetlist:
|
||||||
|
retnum, errnum = self._cli.delldsetld(tagetldset['ldsetname'],
|
||||||
|
lvname)
|
||||||
|
if retnum is not True:
|
||||||
|
msg = (_('Failed to remove export Logical Disk from '
|
||||||
|
'Logical Disk Set (%s)') % errnum)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
LOG.debug('LD `%(ld)s` deleted from LD Set `%(ldset)s`.',
|
||||||
|
{'ld': lvname, 'ldset': tagetldset['ldsetname']})
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._cli.lvunlink(lvname[3:])
|
||||||
|
except Exception:
|
||||||
|
LOG.debug('LV unlink error.')
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._cli.lvunbind(lvname)
|
||||||
|
except Exception:
|
||||||
|
LOG.debug('LV unbind error.')
|
||||||
|
|
||||||
|
LOG.debug('_remove_export_snapshot(Snapshot ID = %s) End.',
|
||||||
|
snapshot.id)
|
||||||
|
|
||||||
def iscsi_initialize_connection(self, volume, connector):
|
def iscsi_initialize_connection(self, volume, connector):
|
||||||
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
||||||
% {'id': volume.id, 'connector': connector})
|
% {'id': volume.id, 'connector': connector})
|
||||||
@ -991,24 +1205,43 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
'info': info})
|
'info': info})
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def iscsi_terminate_connection(self, volume, connector):
|
def iscsi_initialize_connection_snapshot(self, snapshot, connector,
|
||||||
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
**kwargs):
|
||||||
% {'id': volume.id, 'connector': connector})
|
"""Allow connection to connector and return connection info.
|
||||||
|
|
||||||
|
:param snapshot: The snapshot to be attached
|
||||||
|
:param connector: Dictionary containing information about what
|
||||||
|
is being connected to.
|
||||||
|
:returns conn_info: A dictionary of connection information. This
|
||||||
|
can optionally include a "initiator_updates"
|
||||||
|
field.
|
||||||
|
"""
|
||||||
|
msgparm = ('Snapshot ID = %(id)s, Connector = %(connector)s'
|
||||||
|
% {'id': snapshot.id, 'connector': connector})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ret = self._iscsi_terminate_connection(volume, connector)
|
ret = self._iscsi_initialize_connection(snapshot, connector)
|
||||||
LOG.info('Terminated iSCSI Connection (%s)', msgparm)
|
LOG.info('Initialized iSCSI Connection snapshot(%s)', msgparm)
|
||||||
return ret
|
return ret
|
||||||
except exception.CinderException as e:
|
except exception.CinderException as e:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.warning('Failed to Terminate iSCSI Connection '
|
LOG.warning('Failed to Initialize iSCSI Connection snapshot'
|
||||||
'(%(msgparm)s) (%(exception)s)',
|
'(%(msgparm)s) (%(exception)s)',
|
||||||
{'msgparm': msgparm, 'exception': e})
|
{'msgparm': msgparm, 'exception': e})
|
||||||
|
return ret
|
||||||
|
|
||||||
def _iscsi_terminate_connection(self, volume, connector):
|
def iscsi_terminate_connection(self, volume, connector):
|
||||||
LOG.debug('execute _iscsi_terminate_connection'
|
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
||||||
'(Volume ID = %(id)s, connector = %(connector)s).',
|
% {'id': volume.id, 'connector': connector})
|
||||||
{'id': volume.id, 'connector': connector})
|
LOG.info('Terminated iSCSI Connection (%s)', msgparm)
|
||||||
|
|
||||||
|
def iscsi_terminate_connection_snapshot(self, snapshot, connector,
|
||||||
|
**kwargs):
|
||||||
|
"""Disallow connection from connector."""
|
||||||
|
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
||||||
|
% {'id': snapshot.id, 'connector': connector})
|
||||||
|
self.remove_export_snapshot(None, snapshot)
|
||||||
|
LOG.info('Terminated iSCSI Connection snapshot(%s)', msgparm)
|
||||||
|
|
||||||
def fc_initialize_connection(self, volume, connector):
|
def fc_initialize_connection(self, volume, connector):
|
||||||
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
||||||
@ -1068,14 +1301,16 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
fc_ports = []
|
fc_ports = []
|
||||||
for director, hostport in hostports.items():
|
for director, hostport in hostports.items():
|
||||||
for port in hostport:
|
for port in hostport:
|
||||||
if port['protocol'] == 'FC':
|
if port['protocol'].lower() == 'fc':
|
||||||
fc_ports.append(port)
|
fc_ports.append(port)
|
||||||
target_wwns, init_targ_map = (
|
target_wwns, init_targ_map = (
|
||||||
self._build_initiator_target_map(connector, fc_ports))
|
self._build_initiator_target_map(connector, fc_ports))
|
||||||
|
|
||||||
|
if (hasattr(volume, 'migration_status') and
|
||||||
|
volume.migration_status is not None and
|
||||||
|
'target:' in volume.migration_status):
|
||||||
LOG.debug('migration_status:%s', volume.migration_status)
|
LOG.debug('migration_status:%s', volume.migration_status)
|
||||||
migstat = volume.migration_status
|
migstat = volume.migration_status
|
||||||
if migstat is not None and 'target:' in migstat:
|
|
||||||
index = migstat.find('target:')
|
index = migstat.find('target:')
|
||||||
if index != -1:
|
if index != -1:
|
||||||
migstat = migstat[len('target:'):]
|
migstat = migstat[len('target:'):]
|
||||||
@ -1094,6 +1329,10 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
{'ld': ldname, 'id': volume.id})
|
{'ld': ldname, 'id': volume.id})
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.NotFound(msg)
|
raise exception.NotFound(msg)
|
||||||
|
lvname = ldname + '_l'
|
||||||
|
if lvname in lds:
|
||||||
|
ldn = lds[lvname]['ldn']
|
||||||
|
else:
|
||||||
ldn = lds[ldname]['ldn']
|
ldn = lds[ldname]['ldn']
|
||||||
|
|
||||||
lun = None
|
lun = None
|
||||||
@ -1116,6 +1355,20 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
'info': info})
|
'info': info})
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
def fc_initialize_connection_snapshot(self, snapshot, connector):
|
||||||
|
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
||||||
|
% {'id': snapshot.id, 'connector': connector})
|
||||||
|
|
||||||
|
try:
|
||||||
|
ret = self._fc_initialize_connection(snapshot, connector)
|
||||||
|
LOG.info('Initialized FC Connection snapshot(%s)', msgparm)
|
||||||
|
return ret
|
||||||
|
except exception.CinderException as e:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.warning('Failed to Initialize FC Connection snapshot'
|
||||||
|
'(%(msgparm)s) (%(exception)s)',
|
||||||
|
{'msgparm': msgparm, 'exception': e})
|
||||||
|
|
||||||
def fc_terminate_connection(self, volume, connector):
|
def fc_terminate_connection(self, volume, connector):
|
||||||
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
||||||
% {'id': volume.id, 'connector': connector})
|
% {'id': volume.id, 'connector': connector})
|
||||||
@ -1144,7 +1397,7 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
fc_ports = []
|
fc_ports = []
|
||||||
for director, hostport in hostports.items():
|
for director, hostport in hostports.items():
|
||||||
for port in hostport:
|
for port in hostport:
|
||||||
if port['protocol'] == 'FC':
|
if port['protocol'].lower() == 'fc':
|
||||||
fc_ports.append(port)
|
fc_ports.append(port)
|
||||||
target_wwns, init_targ_map = (
|
target_wwns, init_targ_map = (
|
||||||
self._build_initiator_target_map(connector, fc_ports))
|
self._build_initiator_target_map(connector, fc_ports))
|
||||||
@ -1160,6 +1413,20 @@ class MStorageDriver(volume_common.MStorageVolumeCommon):
|
|||||||
'info': info})
|
'info': info})
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
def fc_terminate_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||||
|
msgparm = ('Volume ID = %(id)s, Connector = %(connector)s'
|
||||||
|
% {'id': snapshot.id, 'connector': connector})
|
||||||
|
try:
|
||||||
|
ret = self._fc_terminate_connection(snapshot, connector)
|
||||||
|
LOG.info('Terminated FC Connection snapshot(%s)', msgparm)
|
||||||
|
self.remove_export_snapshot(None, snapshot)
|
||||||
|
return ret
|
||||||
|
except exception.CinderException as e:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.warning('Failed to Terminate FC Connection snapshot'
|
||||||
|
'(%(msgparm)s) (%(exception)s)',
|
||||||
|
{'msgparm': msgparm, 'exception': e})
|
||||||
|
|
||||||
def _build_initiator_target_map(self, connector, fc_ports):
|
def _build_initiator_target_map(self, connector, fc_ports):
|
||||||
target_wwns = []
|
target_wwns = []
|
||||||
for port in fc_ports:
|
for port in fc_ports:
|
||||||
@ -1344,12 +1611,8 @@ class MStorageDSVDriver(MStorageDriver):
|
|||||||
raise exception.ParameterNotFound(param='backup_pools')
|
raise exception.ParameterNotFound(param='backup_pools')
|
||||||
|
|
||||||
# get BV name.
|
# get BV name.
|
||||||
ldname = self.get_ldname(snapshot.volume_id,
|
ldname = self._validate_ld_exist(
|
||||||
self._properties['ld_name_format'])
|
lds, snapshot.volume_id, self._properties['ld_name_format'])
|
||||||
if ldname not in lds:
|
|
||||||
msg = _('Logical Disk `%s` has unbound already.') % ldname
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.NotFound(msg)
|
|
||||||
|
|
||||||
selected_pool = self._select_dsv_poolnumber(snapshot, pools, None)
|
selected_pool = self._select_dsv_poolnumber(snapshot, pools, None)
|
||||||
snapshotname = self._convert_id2snapname(snapshot)
|
snapshotname = self._convert_id2snapname(snapshot)
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Enable backup snapshot optimal path by implementing attach
|
||||||
|
and detach snapshot in the NEC driver.
|
Loading…
x
Reference in New Issue
Block a user