Huawei: Support backup snapshot

Implement attach snapshot for backup snapshot feature
in Huawei driver.

DocImpact
Implements: blueprint huawei-support-backup-snapshot

Change-Id: I0173d397760bc17c01af792b4c4cc2bcbf45fedd
This commit is contained in:
huananhuawei 2016-06-14 17:28:26 +08:00 committed by huanan
parent eccc58f7af
commit 3eb855260e
9 changed files with 232 additions and 57 deletions

View File

@ -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):

View File

@ -4553,6 +4553,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):

View File

@ -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

View File

@ -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'

View File

@ -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,

View File

@ -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': {}}

View File

@ -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:

View File

@ -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.')

View File

@ -0,0 +1,3 @@
---
features:
- Huawei support backup snapshot optimal path