Merge "Huawei: Support backup snapshot"
This commit is contained in:
commit
e23ba3673f
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Tests for huawei drivers."""
|
||||
import collections
|
||||
import copy
|
||||
import ddt
|
||||
import json
|
||||
@ -44,6 +45,9 @@ from cinder.volume import volume_types
|
||||
|
||||
admin_contex = context.get_admin_context()
|
||||
|
||||
vol_attrs = ('id', 'lun_type', 'provider_location', 'metadata')
|
||||
Volume = collections.namedtuple('Volume', vol_attrs)
|
||||
|
||||
PROVIDER_LOCATION = '11'
|
||||
HOST = 'ubuntu001@backend001#OpenStack_Pool'
|
||||
ID = '21ec7341-9256-497b-97d9-ef48edcf0635'
|
||||
@ -484,6 +488,18 @@ FAKE_GET_SNAPSHOT_INFO_RESPONSE = """
|
||||
}
|
||||
"""
|
||||
|
||||
FAKE_SNAPSHOT_COUNT_RESPONSE = """
|
||||
{
|
||||
"data":{
|
||||
"COUNT":"2"
|
||||
},
|
||||
"error":{
|
||||
"code":0,
|
||||
"description":"0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
# A fake response of get iscsi response
|
||||
|
||||
FAKE_GET_ISCSI_INFO_RESPONSE = """
|
||||
@ -1208,6 +1224,14 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/lun/associate/cachepartition?ID=1'
|
||||
'/DELETE'] = (
|
||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/snapshot/associate?TYPE=27&ASSOCIATEOBJTYPE=21'
|
||||
'&ASSOCIATEOBJID=1/GET'] = (
|
||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/snapshot/associate?TYPE=27&ASSOCIATEOBJTYPE=256'
|
||||
'&ASSOCIATEOBJID=11/GET'] = (
|
||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/lungroup?range=[0-8191]/GET'] = (
|
||||
FAKE_QUERY_LUN_GROUP_INFO_RESPONSE)
|
||||
|
||||
@ -1236,10 +1260,26 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/lungroup/associate?ID=11&ASSOCIATEOBJTYPE=11'
|
||||
'&ASSOCIATEOBJID=11/DELETE'] = (
|
||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/lungroup/associate?ID=11&ASSOCIATEOBJTYPE=27'
|
||||
'&ASSOCIATEOBJID=11/DELETE'] = (
|
||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/lun/count?TYPE=11&ASSOCIATEOBJTYPE=256'
|
||||
'&ASSOCIATEOBJID=11/GET'] = (
|
||||
FAKE_LUN_COUNT_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/snapshot/count?TYPE=27&ASSOCIATEOBJTYPE=256'
|
||||
'&ASSOCIATEOBJID=1/GET'] = (
|
||||
FAKE_SNAPSHOT_COUNT_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/snapshot/count?TYPE=27&ASSOCIATEOBJTYPE=256'
|
||||
'&ASSOCIATEOBJID=11/GET'] = (
|
||||
FAKE_SNAPSHOT_COUNT_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/lungroup/associate?TYPE=256&ASSOCIATEOBJTYPE=27'
|
||||
'&ASSOCIATEOBJID=11/GET'] = (
|
||||
FAKE_LUN_ASSOCIATE_RESPONSE)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/lun/expand/PUT'] = (
|
||||
FAKE_LUN_INFO_RESPONSE)
|
||||
|
||||
@ -2104,6 +2144,9 @@ class HuaweiTestBase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(HuaweiTestBase, self).setUp()
|
||||
self.configuration = mock.Mock(spec=conf.Configuration)
|
||||
self.driver = FakeISCSIStorage(configuration=self.configuration)
|
||||
self.driver.do_setup()
|
||||
|
||||
self.volume = fake_volume.fake_volume_obj(
|
||||
admin_contex, host=HOST, provider_location=PROVIDER_LOCATION,
|
||||
@ -2232,6 +2275,19 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
|
||||
self.assertEqual(driver_data, model_update['replication_driver_data'])
|
||||
self.assertEqual('available', model_update['replication_status'])
|
||||
|
||||
@mock.patch.object(huawei_driver.HuaweiISCSIDriver,
|
||||
'initialize_connection',
|
||||
return_value={"data": {'target_lun': 1}})
|
||||
def test_initialize_connection_snapshot_success(self, mock_iscsi_init):
|
||||
iscsi_properties = self.driver.initialize_connection_snapshot(
|
||||
self.snapshot, FakeConnector)
|
||||
volume = Volume(id=self.snapshot.id,
|
||||
provider_location=self.snapshot.provider_location,
|
||||
lun_type='27',
|
||||
metadata=None)
|
||||
self.assertEqual(1, iscsi_properties['data']['target_lun'])
|
||||
mock_iscsi_init.assert_called_with(volume, FakeConnector)
|
||||
|
||||
def test_initialize_connection_success_multipath_portgroup(self):
|
||||
temp_connector = copy.deepcopy(FakeConnector)
|
||||
temp_connector['multipath'] = True
|
||||
@ -2361,12 +2417,23 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
|
||||
driver.initialize_connection,
|
||||
self.volume, temp_connector)
|
||||
|
||||
@mock.patch.object(huawei_driver.HuaweiISCSIDriver,
|
||||
'terminate_connection')
|
||||
def test_terminate_connection_snapshot_success(self, mock_iscsi_term):
|
||||
self.driver.terminate_connection_snapshot(self.snapshot,
|
||||
FakeConnector)
|
||||
volume = Volume(id=self.snapshot.id,
|
||||
provider_location=self.snapshot.provider_location,
|
||||
lun_type='27',
|
||||
metadata=None)
|
||||
mock_iscsi_term.assert_called_with(volume, FakeConnector)
|
||||
|
||||
def test_terminate_connection_success(self):
|
||||
self.driver.terminate_connection(self.volume, FakeConnector)
|
||||
|
||||
def test_get_volume_status(self):
|
||||
data = self.driver.get_volume_stats()
|
||||
self.assertEqual('2.0.7', data['driver_version'])
|
||||
self.assertEqual('2.0.8', data['driver_version'])
|
||||
|
||||
@mock.patch.object(rest_client.RestClient, 'get_lun_info',
|
||||
return_value={"CAPACITY": 6291456})
|
||||
@ -3650,6 +3717,19 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
|
||||
self.volume)
|
||||
self.assertEqual('1', lun_info['provider_location'])
|
||||
|
||||
@mock.patch.object(huawei_driver.HuaweiFCDriver,
|
||||
'initialize_connection',
|
||||
return_value={"data": {'target_lun': 1}})
|
||||
def test_initialize_connection_snapshot_success(self, mock_fc_init):
|
||||
iscsi_properties = self.driver.initialize_connection_snapshot(
|
||||
self.snapshot, FakeConnector)
|
||||
volume = Volume(id=self.snapshot.id,
|
||||
provider_location=self.snapshot.provider_location,
|
||||
lun_type='27',
|
||||
metadata=None)
|
||||
self.assertEqual(1, iscsi_properties['data']['target_lun'])
|
||||
mock_fc_init.assert_called_with(volume, FakeConnector)
|
||||
|
||||
def test_initialize_connection_success(self):
|
||||
iscsi_properties = self.driver.initialize_connection(self.volume,
|
||||
FakeConnector)
|
||||
@ -3683,6 +3763,17 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
|
||||
FakeConnector)
|
||||
self.assertEqual(1, fc_properties['data']['target_lun'])
|
||||
|
||||
@mock.patch.object(huawei_driver.HuaweiFCDriver,
|
||||
'terminate_connection')
|
||||
def test_terminate_connection_snapshot_success(self, mock_fc_term):
|
||||
self.driver.terminate_connection_snapshot(self.snapshot,
|
||||
FakeConnector)
|
||||
volume = Volume(id=self.snapshot.id,
|
||||
provider_location=self.snapshot.provider_location,
|
||||
lun_type='27',
|
||||
metadata=None)
|
||||
mock_fc_term.assert_called_with(volume, FakeConnector)
|
||||
|
||||
def test_terminate_connection_success(self):
|
||||
self.driver.client.terminateFlag = True
|
||||
self.driver.terminate_connection(self.volume, FakeConnector)
|
||||
@ -3715,7 +3806,7 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
|
||||
'get_remote_device_by_wwn',
|
||||
mock.Mock(return_value=remote_device_info))
|
||||
data = self.driver.get_volume_stats()
|
||||
self.assertEqual('2.0.7', data['driver_version'])
|
||||
self.assertEqual('2.0.8', data['driver_version'])
|
||||
self.assertTrue(data['pools'][0]['replication_enabled'])
|
||||
self.assertListEqual(['sync', 'async'],
|
||||
data['pools'][0]['replication_type'])
|
||||
@ -3732,7 +3823,7 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
|
||||
'try_get_remote_wwn',
|
||||
mock.Mock(return_value={}))
|
||||
data = self.driver.get_volume_stats()
|
||||
self.assertEqual('2.0.7', data['driver_version'])
|
||||
self.assertEqual('2.0.8', data['driver_version'])
|
||||
self.assertNotIn('replication_enabled', data['pools'][0])
|
||||
|
||||
def test_extend_volume(self):
|
||||
|
@ -4589,6 +4589,10 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
'is_snapshot': False}
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
def test_backup_use_temp_snapshot_config(self):
|
||||
local_conf = self.volume.driver.configuration.local_conf
|
||||
self.assertFalse(local_conf.backup_use_temp_snapshot)
|
||||
|
||||
@mock.patch.object(QUOTAS, 'reserve',
|
||||
side_effect = OVER_SNAPSHOT_QUOTA_EXCEPTION)
|
||||
def test_existing_snapshot_failed_quota_reserve(self, mock_reserve):
|
||||
|
@ -258,6 +258,11 @@ volume_opts = [
|
||||
choices=['iscsi', 'fc'],
|
||||
help='Protocol for transferring data between host and '
|
||||
'storage back-end.'),
|
||||
cfg.BoolOpt('backup_use_temp_snapshot',
|
||||
default=False,
|
||||
help='If this is set to True, the backup_use_temp_snapshot '
|
||||
'path will be used during the backup. Otherwise, it '
|
||||
'will use backup_use_temp_volume path.'),
|
||||
]
|
||||
|
||||
# for backward compatibility
|
||||
|
@ -19,6 +19,8 @@ STATUS_RUNNING = '10'
|
||||
STATUS_VOLUME_READY = '27'
|
||||
STATUS_LUNCOPY_READY = '40'
|
||||
STATUS_QOS_ACTIVE = '2'
|
||||
LUN_TYPE = '11'
|
||||
SNAPSHOT_TYPE = '27'
|
||||
|
||||
BLOCK_STORAGE_POOL_TYPE = '1'
|
||||
FILE_SYSTEM_POOL_TYPE = '2'
|
||||
|
@ -61,7 +61,7 @@ class FCZoneHelper(object):
|
||||
{"portg": portg, "views": views[0]})
|
||||
# In fact, there is just one view for one port group.
|
||||
lungroup = self.client.get_lungroup_by_view(views[0])
|
||||
lun_num = self.client.get_lunnum_from_lungroup(lungroup)
|
||||
lun_num = self.client.get_obj_count_from_lungroup(lungroup)
|
||||
ports_in_portg = self.client.get_ports_by_portg(portg)
|
||||
LOG.debug("PortGroup %(portg)s contains ports: %(ports)s.",
|
||||
{"portg": portg, "ports": ports_in_portg})
|
||||
@ -133,10 +133,11 @@ class FCZoneHelper(object):
|
||||
'initiators': fabric_connected_initiators})
|
||||
return fabric_connected_ports, fabric_connected_initiators
|
||||
|
||||
def _get_lun_engine_contrs(self, engines, lun_id):
|
||||
def _get_lun_engine_contrs(self, engines, lun_id,
|
||||
lun_type=constants.LUN_TYPE):
|
||||
contrs = []
|
||||
engine_id = None
|
||||
lun_info = self.client.get_lun_info(lun_id)
|
||||
lun_info = self.client.get_lun_info(lun_id, lun_type)
|
||||
lun_contr_id = lun_info['OWNINGCONTROLLER']
|
||||
for engine in engines:
|
||||
contrs = json.loads(engine['NODELIST'])
|
||||
@ -172,11 +173,13 @@ class FCZoneHelper(object):
|
||||
new_portg_id = self.client.create_portg(portg_name, description)
|
||||
return new_portg_id
|
||||
|
||||
def build_ini_targ_map(self, wwns, host_id, lun_id):
|
||||
def build_ini_targ_map(self, wwns, host_id, lun_id,
|
||||
lun_type=constants.LUN_TYPE):
|
||||
engines = self.client.get_all_engines()
|
||||
LOG.debug("Get array engines: %s", engines)
|
||||
|
||||
contrs, engine_id = self._get_lun_engine_contrs(engines, lun_id)
|
||||
contrs, engine_id = self._get_lun_engine_contrs(engines, lun_id,
|
||||
lun_type)
|
||||
|
||||
# Check if there is already a port group in the view.
|
||||
# If yes and have already considered the engine,
|
||||
|
@ -74,6 +74,8 @@ CONF.register_opts(huawei_opts)
|
||||
|
||||
snap_attrs = ('id', 'volume_id', 'volume', 'provider_location')
|
||||
Snapshot = collections.namedtuple('Snapshot', snap_attrs)
|
||||
vol_attrs = ('id', 'lun_type', 'provider_location', 'metadata')
|
||||
Volume = collections.namedtuple('Volume', vol_attrs)
|
||||
|
||||
|
||||
class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
@ -1158,6 +1160,18 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
"""Remove an export for a volume."""
|
||||
pass
|
||||
|
||||
def create_export_snapshot(self, context, snapshot, connector):
|
||||
"""Export a snapshot."""
|
||||
pass
|
||||
|
||||
def remove_export_snapshot(self, context, snapshot):
|
||||
"""Remove an export for a snapshot."""
|
||||
pass
|
||||
|
||||
def backup_use_temp_snapshot(self):
|
||||
# This config option has a default to be False, So just return it.
|
||||
return self.configuration.safe_get("backup_use_temp_snapshot")
|
||||
|
||||
def _copy_volume(self, volume, copy_name, src_lun, tgt_lun):
|
||||
luncopy_id = self.client.create_luncopy(copy_name,
|
||||
src_lun,
|
||||
@ -1756,6 +1770,37 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
|
||||
return secondary_id, volumes_update
|
||||
|
||||
def initialize_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||
"""Map a snapshot to a host and return target iSCSI information."""
|
||||
# From the volume structure.
|
||||
volume = Volume(id=snapshot.id,
|
||||
provider_location=snapshot.provider_location,
|
||||
lun_type=constants.SNAPSHOT_TYPE,
|
||||
metadata=None)
|
||||
|
||||
return self.initialize_connection(volume, connector)
|
||||
|
||||
def terminate_connection_snapshot(self, snapshot, connector, **kwargs):
|
||||
"""Delete map between a snapshot and a host."""
|
||||
# From the volume structure.
|
||||
volume = Volume(id=snapshot.id,
|
||||
provider_location=snapshot.provider_location,
|
||||
lun_type=constants.SNAPSHOT_TYPE,
|
||||
metadata=None)
|
||||
|
||||
return self.terminate_connection(volume, connector)
|
||||
|
||||
def get_lun_id_and_type(self, volume):
|
||||
if hasattr(volume, 'lun_type'):
|
||||
lun_id = volume.provider_location
|
||||
lun_type = constants.SNAPSHOT_TYPE
|
||||
else:
|
||||
lun_id = self._check_volume_exist_on_array(
|
||||
volume, constants.VOLUME_NOT_EXISTS_RAISE)
|
||||
lun_type = constants.LUN_TYPE
|
||||
|
||||
return lun_id, lun_type
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
||||
@ -1784,9 +1829,10 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
||||
Hypermetro consistency group support
|
||||
Consistency group support
|
||||
Cgsnapshot support
|
||||
2.0.8 - Backup snapshot optimal path support
|
||||
"""
|
||||
|
||||
VERSION = "2.0.7"
|
||||
VERSION = "2.0.8"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HuaweiISCSIDriver, self).__init__(*args, **kwargs)
|
||||
@ -1804,9 +1850,7 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
||||
@utils.synchronized('huawei', external=True)
|
||||
def initialize_connection(self, volume, connector):
|
||||
"""Map a volume to a host and return target iSCSI information."""
|
||||
lun_id = self._check_volume_exist_on_array(
|
||||
volume, constants.VOLUME_NOT_EXISTS_RAISE)
|
||||
|
||||
lun_id, lun_type = self.get_lun_id_and_type(volume)
|
||||
initiator_name = connector['initiator']
|
||||
LOG.info(_LI(
|
||||
'initiator name: %(initiator_name)s, '
|
||||
@ -1837,9 +1881,11 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
||||
|
||||
# Mapping lungroup and hostgroup to view.
|
||||
self.client.do_mapping(lun_id, hostgroup_id,
|
||||
host_id, portgroup_id)
|
||||
host_id, portgroup_id,
|
||||
lun_type)
|
||||
|
||||
hostlun_id = self.client.get_host_lun_id(host_id, lun_id)
|
||||
hostlun_id = self.client.get_host_lun_id(host_id, lun_id,
|
||||
lun_type)
|
||||
|
||||
LOG.info(_LI("initialize_connection, host lun id is: %s."),
|
||||
hostlun_id)
|
||||
@ -1877,8 +1923,7 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
||||
@utils.synchronized('huawei', external=True)
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
"""Delete map between a volume and a host."""
|
||||
lun_id = self._check_volume_exist_on_array(
|
||||
volume, constants.VOLUME_NOT_EXISTS_WARN)
|
||||
lun_id, lun_type = self.get_lun_id_and_type(volume)
|
||||
initiator_name = connector['initiator']
|
||||
host_name = connector['host']
|
||||
lungroup_id = None
|
||||
@ -1912,10 +1957,12 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
||||
|
||||
# Remove lun from lungroup.
|
||||
if lun_id and lungroup_id:
|
||||
lungroup_ids = self.client.get_lungroupids_by_lunid(lun_id)
|
||||
lungroup_ids = self.client.get_lungroupids_by_lunid(
|
||||
lun_id, lun_type)
|
||||
if lungroup_id in lungroup_ids:
|
||||
self.client.remove_lun_from_lungroup(lungroup_id,
|
||||
lun_id)
|
||||
lun_id,
|
||||
lun_type)
|
||||
else:
|
||||
LOG.warning(_LW("LUN is not in lungroup. "
|
||||
"LUN ID: %(lun_id)s. "
|
||||
@ -1925,7 +1972,7 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
||||
|
||||
# Remove portgroup from mapping view if no lun left in lungroup.
|
||||
if lungroup_id:
|
||||
left_lunnum = self.client.get_lunnum_from_lungroup(lungroup_id)
|
||||
left_lunnum = self.client.get_obj_count_from_lungroup(lungroup_id)
|
||||
|
||||
if portgroup_id and view_id and (int(left_lunnum) <= 0):
|
||||
if self.client.is_portgroup_associated_to_view(view_id,
|
||||
@ -1981,9 +2028,10 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
Hypermetro consistency group support
|
||||
Consistency group support
|
||||
Cgsnapshot support
|
||||
2.0.8 - Backup snapshot optimal path support
|
||||
"""
|
||||
|
||||
VERSION = "2.0.7"
|
||||
VERSION = "2.0.8"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HuaweiFCDriver, self).__init__(*args, **kwargs)
|
||||
@ -2002,9 +2050,7 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
@utils.synchronized('huawei', external=True)
|
||||
@fczm_utils.AddFCZone
|
||||
def initialize_connection(self, volume, connector):
|
||||
lun_id = self._check_volume_exist_on_array(
|
||||
volume, constants.VOLUME_NOT_EXISTS_RAISE)
|
||||
|
||||
lun_id, lun_type = self.get_lun_id_and_type(volume)
|
||||
wwns = connector['wwpns']
|
||||
LOG.info(_LI(
|
||||
'initialize_connection, initiator: %(wwpns)s,'
|
||||
@ -2027,7 +2073,8 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
zone_helper = fc_zone_helper.FCZoneHelper(self.fcsan, self.client)
|
||||
try:
|
||||
(tgt_port_wwns, portg_id, init_targ_map) = (
|
||||
zone_helper.build_ini_targ_map(wwns, host_id, lun_id))
|
||||
zone_helper.build_ini_targ_map(wwns, host_id, lun_id,
|
||||
lun_type))
|
||||
except Exception as err:
|
||||
self.remove_host_with_check(host_id)
|
||||
msg = _('build_ini_targ_map fails. %s') % err
|
||||
@ -2065,8 +2112,10 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
# Add host into hostgroup.
|
||||
hostgroup_id = self.client.add_host_to_hostgroup(host_id)
|
||||
map_info = self.client.do_mapping(lun_id, hostgroup_id,
|
||||
host_id, portg_id)
|
||||
host_lun_id = self.client.get_host_lun_id(host_id, lun_id)
|
||||
host_id, portg_id,
|
||||
lun_type)
|
||||
host_lun_id = self.client.get_host_lun_id(host_id, lun_id,
|
||||
lun_type)
|
||||
|
||||
# Return FC properties.
|
||||
fc_info = {'driver_volume_type': 'fibre_channel',
|
||||
@ -2143,9 +2192,7 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
@fczm_utils.RemoveFCZone
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
"""Delete map between a volume and a host."""
|
||||
lun_id = self._check_volume_exist_on_array(
|
||||
volume, constants.VOLUME_NOT_EXISTS_WARN)
|
||||
|
||||
lun_id, lun_type = self.get_lun_id_and_type(volume)
|
||||
wwns = connector['wwpns']
|
||||
|
||||
host_name = connector['host']
|
||||
@ -2165,10 +2212,12 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
lungroup_id = self.client.find_lungroup_from_map(view_id)
|
||||
|
||||
if lun_id and lungroup_id:
|
||||
lungroup_ids = self.client.get_lungroupids_by_lunid(lun_id)
|
||||
lungroup_ids = self.client.get_lungroupids_by_lunid(lun_id,
|
||||
lun_type)
|
||||
if lungroup_id in lungroup_ids:
|
||||
self.client.remove_lun_from_lungroup(lungroup_id,
|
||||
lun_id)
|
||||
lun_id,
|
||||
lun_type)
|
||||
else:
|
||||
LOG.warning(_LW("LUN is not in lungroup. "
|
||||
"LUN ID: %(lun_id)s. "
|
||||
@ -2179,7 +2228,7 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
else:
|
||||
LOG.warning(_LW("Can't find lun on the array."))
|
||||
if lungroup_id:
|
||||
left_lunnum = self.client.get_lunnum_from_lungroup(lungroup_id)
|
||||
left_lunnum = self.client.get_obj_count_from_lungroup(lungroup_id)
|
||||
if int(left_lunnum) > 0:
|
||||
fc_info = {'driver_volume_type': 'fibre_channel',
|
||||
'data': {}}
|
||||
|
@ -235,7 +235,7 @@ class HuaweiHyperMetro(object):
|
||||
lungroup_id = self.rmt_client.find_lungroup_from_map(
|
||||
view_id)
|
||||
if lungroup_id:
|
||||
left_lunnum = self.rmt_client.get_lunnum_from_lungroup(
|
||||
left_lunnum = self.rmt_client.get_obj_count_from_lungroup(
|
||||
lungroup_id)
|
||||
|
||||
if int(left_lunnum) > 0:
|
||||
|
@ -414,7 +414,8 @@ class RestClient(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def do_mapping(self, lun_id, hostgroup_id, host_id, portgroup_id=None):
|
||||
def do_mapping(self, lun_id, hostgroup_id, host_id, portgroup_id=None,
|
||||
lun_type=constants.LUN_TYPE):
|
||||
"""Add hostgroup and lungroup to mapping view."""
|
||||
lungroup_name = constants.LUNGROUP_PREFIX + host_id
|
||||
mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
|
||||
@ -434,9 +435,10 @@ class RestClient(object):
|
||||
if lungroup_id is None:
|
||||
lungroup_id = self._create_lungroup(lungroup_name)
|
||||
is_associated = self._is_lun_associated_to_lungroup(lungroup_id,
|
||||
lun_id)
|
||||
lun_id,
|
||||
lun_type)
|
||||
if not is_associated:
|
||||
self.associate_lun_to_lungroup(lungroup_id, lun_id)
|
||||
self.associate_lun_to_lungroup(lungroup_id, lun_id, lun_type)
|
||||
|
||||
if view_id is None:
|
||||
view_id = self._add_mapping_view(mapping_view_name)
|
||||
@ -468,7 +470,7 @@ class RestClient(object):
|
||||
LOG.error(_LE(
|
||||
'Error occurred when adding hostgroup and lungroup to '
|
||||
'view. Remove lun from lungroup now.'))
|
||||
self.remove_lun_from_lungroup(lungroup_id, lun_id)
|
||||
self.remove_lun_from_lungroup(lungroup_id, lun_id, lun_type)
|
||||
|
||||
return map_info
|
||||
|
||||
@ -602,9 +604,10 @@ class RestClient(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_host_lun_id(self, host_id, lun_id):
|
||||
url = ("/lun/associate?TYPE=11&ASSOCIATEOBJTYPE=21"
|
||||
"&ASSOCIATEOBJID=%s" % (host_id))
|
||||
def get_host_lun_id(self, host_id, lun_id, lun_type=constants.LUN_TYPE):
|
||||
cmd_type = 'lun' if lun_type == constants.LUN_TYPE else 'snapshot'
|
||||
url = ("/%s/associate?TYPE=%s&ASSOCIATEOBJTYPE=21"
|
||||
"&ASSOCIATEOBJID=%s" % (cmd_type, lun_type, host_id))
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Find host lun id error.'))
|
||||
|
||||
@ -692,10 +695,13 @@ class RestClient(object):
|
||||
|
||||
return False
|
||||
|
||||
def _is_lun_associated_to_lungroup(self, lungroup_id, lun_id):
|
||||
def _is_lun_associated_to_lungroup(self, lungroup_id, lun_id,
|
||||
lun_type=constants.LUN_TYPE):
|
||||
"""Check whether the lun is associated to the lungroup."""
|
||||
url = ("/lun/associate?TYPE=11&"
|
||||
"ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id)
|
||||
cmd_type = 'lun' if lun_type == constants.LUN_TYPE else 'snapshot'
|
||||
url = ("/%s/associate?TYPE=%s&"
|
||||
"ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s"
|
||||
% (cmd_type, lun_type, lungroup_id))
|
||||
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Check lungroup associate error.'))
|
||||
@ -716,19 +722,21 @@ class RestClient(object):
|
||||
self._assert_rest_result(result, _('Associate host to hostgroup '
|
||||
'error.'))
|
||||
|
||||
def associate_lun_to_lungroup(self, lungroup_id, lun_id):
|
||||
def associate_lun_to_lungroup(self, lungroup_id, lun_id,
|
||||
lun_type=constants.LUN_TYPE):
|
||||
"""Associate lun to lungroup."""
|
||||
url = "/lungroup/associate"
|
||||
data = {"ID": lungroup_id,
|
||||
"ASSOCIATEOBJTYPE": "11",
|
||||
"ASSOCIATEOBJTYPE": lun_type,
|
||||
"ASSOCIATEOBJID": lun_id}
|
||||
result = self.call(url, data)
|
||||
self._assert_rest_result(result, _('Associate lun to lungroup error.'))
|
||||
|
||||
def remove_lun_from_lungroup(self, lungroup_id, lun_id):
|
||||
def remove_lun_from_lungroup(self, lungroup_id, lun_id,
|
||||
lun_type=constants.LUN_TYPE):
|
||||
"""Remove lun from lungroup."""
|
||||
url = ("/lungroup/associate?ID=%s&ASSOCIATEOBJTYPE=11"
|
||||
"&ASSOCIATEOBJID=%s" % (lungroup_id, lun_id))
|
||||
url = ("/lungroup/associate?ID=%s&ASSOCIATEOBJTYPE=%s"
|
||||
"&ASSOCIATEOBJID=%s" % (lungroup_id, lun_type, lun_id))
|
||||
|
||||
result = self.call(url, None, 'DELETE')
|
||||
self._assert_rest_result(
|
||||
@ -942,16 +950,25 @@ class RestClient(object):
|
||||
result = self.call(url, None, "DELETE")
|
||||
self._assert_rest_result(result, _('Delete mapping view error.'))
|
||||
|
||||
def get_lunnum_from_lungroup(self, lungroup_id):
|
||||
"""Check if there are still other luns associated to the lungroup."""
|
||||
def get_obj_count_from_lungroup(self, lungroup_id):
|
||||
"""Get all objects count associated to the lungroup."""
|
||||
lun_count = self._get_obj_count_from_lungroup_by_type(
|
||||
lungroup_id, constants.LUN_TYPE)
|
||||
snapshot_count = self._get_obj_count_from_lungroup_by_type(
|
||||
lungroup_id, constants.SNAPSHOT_TYPE)
|
||||
return int(lun_count) + int(snapshot_count)
|
||||
|
||||
def _get_obj_count_from_lungroup_by_type(self, lungroup_id,
|
||||
lun_type=constants.LUN_TYPE):
|
||||
cmd_type = 'lun' if lun_type == constants.LUN_TYPE else 'snapshot'
|
||||
lunnum = 0
|
||||
if not lungroup_id:
|
||||
return lunnum
|
||||
|
||||
url = ("/lun/count?TYPE=11&ASSOCIATEOBJTYPE=256&"
|
||||
"ASSOCIATEOBJID=%s" % lungroup_id)
|
||||
url = ("/%s/count?TYPE=%s&ASSOCIATEOBJTYPE=256&"
|
||||
"ASSOCIATEOBJID=%s" % (cmd_type, lun_type, lungroup_id))
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Find lun number error.'))
|
||||
self._assert_rest_result(result, _('Find obj number error.'))
|
||||
if 'data' in result:
|
||||
lunnum = int(result['data']['COUNT'])
|
||||
return lunnum
|
||||
@ -1457,10 +1474,10 @@ class RestClient(object):
|
||||
|
||||
return result['data']['IOCLASSID']
|
||||
|
||||
def get_lungroupids_by_lunid(self, lun_id):
|
||||
def get_lungroupids_by_lunid(self, lun_id, lun_type=constants.LUN_TYPE):
|
||||
"""Get lungroup ids by lun id."""
|
||||
url = ("/lungroup/associate?TYPE=256"
|
||||
"&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s" % lun_id)
|
||||
"&ASSOCIATEOBJTYPE=%s&ASSOCIATEOBJID=%s" % (lun_type, lun_id))
|
||||
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Get lungroup id by lun id error.'))
|
||||
@ -1472,8 +1489,9 @@ class RestClient(object):
|
||||
|
||||
return lungroup_ids
|
||||
|
||||
def get_lun_info(self, lun_id):
|
||||
url = "/lun/" + lun_id
|
||||
def get_lun_info(self, lun_id, lun_type = constants.LUN_TYPE):
|
||||
cmd_type = 'lun' if lun_type == constants.LUN_TYPE else 'snapshot'
|
||||
url = ("/%s/%s" % (cmd_type, lun_id))
|
||||
result = self.call(url, None, "GET")
|
||||
|
||||
msg = _('Get volume error.')
|
||||
|
3
releasenotes/notes/backup-snapshot-6e7447db930c31f6.yaml
Normal file
3
releasenotes/notes/backup-snapshot-6e7447db930c31f6.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Huawei support backup snapshot optimal path
|
Loading…
Reference in New Issue
Block a user