1# Copyright (c) 2017 Dell Inc. or its subsidiaries. 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16import ast 17from copy import deepcopy 18import datetime 19import tempfile 20import time 21from xml.dom import minidom 22 23import mock 24import requests 25import six 26 27from cinder import context 28from cinder import exception 29from cinder import objects 30from cinder.objects import fields 31from cinder.objects import group 32from cinder.objects import group_snapshot 33from cinder.objects import volume_type 34from cinder import test 35from cinder.tests.unit import fake_group 36from cinder.tests.unit import fake_snapshot 37from cinder.tests.unit import fake_volume 38from cinder.tests.unit import utils as test_utils 39from cinder.volume.drivers.dell_emc.vmax import common 40from cinder.volume.drivers.dell_emc.vmax import fc 41from cinder.volume.drivers.dell_emc.vmax import iscsi 42from cinder.volume.drivers.dell_emc.vmax import masking 43from cinder.volume.drivers.dell_emc.vmax import provision 44from cinder.volume.drivers.dell_emc.vmax import rest 45from cinder.volume.drivers.dell_emc.vmax import utils 46from cinder.volume import utils as volume_utils 47from cinder.volume import volume_types 48from cinder.zonemanager import utils as fczm_utils 49 50CINDER_EMC_CONFIG_DIR = '/etc/cinder/' 51 52 53class VMAXCommonData(object): 54 # array info 55 array = '000197800123' 56 uni_array = u'000197800123' 57 array_herc = '000197900123' 58 srp = 'SRP_1' 59 srp2 = 'SRP_2' 60 slo = 'Diamond' 61 workload = 'DSS' 62 port_group_name_f = 'OS-fibre-PG' 63 port_group_name_i = 'OS-iscsi-PG' 64 masking_view_name_f = 'OS-HostX-F-OS-fibre-PG-MV' 65 masking_view_name_i = 'OS-HostX-SRP_1-I-OS-iscsi-PG-MV' 66 initiatorgroup_name_f = 'OS-HostX-F-IG' 67 initiatorgroup_name_i = 'OS-HostX-I-IG' 68 parent_sg_f = 'OS-HostX-F-OS-fibre-PG-SG' 69 parent_sg_i = 'OS-HostX-I-OS-iscsi-PG-SG' 70 storagegroup_name_f = 'OS-HostX-SRP_1-DiamondDSS-OS-fibre-PG' 71 storagegroup_name_i = 'OS-HostX-SRP_1-Diamond-DSS-OS-iscsi-PG' 72 defaultstoragegroup_name = 'OS-SRP_1-Diamond-DSS-SG' 73 storagegroup_list = [defaultstoragegroup_name] 74 default_sg_no_slo = 'OS-no_SLO-SG' 75 default_sg_compr_disabled = 'OS-SRP_1-Diamond-DSS-CD-SG' 76 default_sg_re_enabled = 'OS-SRP_1-Diamond-DSS-RE-SG' 77 failed_resource = 'OS-failed-resource' 78 fake_host = 'HostX@Backend#Diamond+DSS+SRP_1+000197800123' 79 new_host = 'HostX@Backend#Silver+OLTP+SRP_1+000197800123' 80 none_host = 'HostX@Backend#Diamond+None+SRP_1+000197800123' 81 version = '3.1.0' 82 volume_wwn = '600000345' 83 remote_array = '000197800124' 84 device_id = '00001' 85 device_id2 = '00002' 86 device_id3 = '00003' 87 rdf_group_name = '23_24_007' 88 rdf_group_no = '70' 89 u4v_version = '84' 90 storagegroup_name_source = 'Grp_source_sg' 91 storagegroup_name_target = 'Grp_target_sg' 92 group_snapshot_name = 'Grp_snapshot' 93 target_group_name = 'Grp_target' 94 storagegroup_name_with_id = 'GrpId_group_name' 95 rdf_managed_async_grp = "OS-%s-Asynchronous-rdf-sg" % rdf_group_name 96 volume_id = '2b06255d-f5f0-4520-a953-b029196add6a' 97 98 # connector info 99 wwpn1 = "123456789012345" 100 wwpn2 = "123456789054321" 101 wwnn1 = "223456789012345" 102 initiator = 'iqn.1993-08.org.debian: 01: 222' 103 ip, ip2 = u'123.456.7.8', u'123.456.7.9' 104 iqn = u'iqn.1992-04.com.emc:600009700bca30c01e3e012e00000001,t,0x0001' 105 iqn2 = u'iqn.1992-04.com.emc:600009700bca30c01e3e012e00000002,t,0x0001' 106 connector = {'ip': ip, 107 'initiator': initiator, 108 'wwpns': [wwpn1, wwpn2], 109 'wwnns': [wwnn1], 110 'host': 'HostX'} 111 112 fabric_name_prefix = "fakeFabric" 113 end_point_map = {connector['wwpns'][0]: [wwnn1], 114 connector['wwpns'][1]: [wwnn1]} 115 target_wwns = [wwnn1] 116 zoning_mappings = { 117 'array': u'000197800123', 118 'init_targ_map': end_point_map, 119 'initiator_group': initiatorgroup_name_f, 120 'port_group': port_group_name_f, 121 'target_wwns': target_wwns} 122 zoning_mappings_metro = deepcopy(zoning_mappings) 123 zoning_mappings_metro.update({'metro_port_group': port_group_name_f, 124 'metro_ig': initiatorgroup_name_f, 125 'metro_array': remote_array}) 126 127 device_map = {} 128 for wwn in connector['wwpns']: 129 fabric_name = ''.join([fabric_name_prefix, 130 wwn[-2:]]) 131 target_wwn = wwn[::-1] 132 fabric_map = {'initiator_port_wwn_list': [wwn], 133 'target_port_wwn_list': [target_wwn] 134 } 135 device_map[fabric_name] = fabric_map 136 137 iscsi_device_info = {'maskingview': masking_view_name_i, 138 'ip_and_iqn': [{'ip': ip, 139 'iqn': initiator}], 140 'is_multipath': True, 141 'array': array, 142 'controller': {'host': '10.00.00.00'}, 143 'hostlunid': 3} 144 iscsi_device_info_metro = deepcopy(iscsi_device_info) 145 iscsi_device_info_metro['metro_ip_and_iqn'] = [{'ip': ip2, 'iqn': iqn2}] 146 iscsi_device_info_metro['metro_hostlunid'] = 2 147 148 fc_device_info = {'maskingview': masking_view_name_f, 149 'array': array, 150 'controller': {'host': '10.00.00.00'}, 151 'hostlunid': 3} 152 153 # cinder volume info 154 ctx = context.RequestContext('admin', 'fake', True) 155 provider_location = {'array': six.text_type(array), 156 'device_id': device_id} 157 158 provider_location2 = {'array': six.text_type(array), 159 'device_id': device_id2} 160 161 provider_location3 = {'array': six.text_type(remote_array), 162 'device_id': device_id2} 163 164 provider_location4 = {'array': six.text_type(uni_array), 165 'device_id': device_id} 166 167 legacy_provider_location = { 168 'classname': 'Symm_StorageVolume', 169 'keybindings': {'CreationClassName': u'Symm_StorageVolume', 170 'SystemName': u'SYMMETRIX+000197800123', 171 'DeviceID': device_id, 172 'SystemCreationClassName': u'Symm_StorageSystem'}} 173 174 legacy_provider_location2 = { 175 'classname': 'Symm_StorageVolume', 176 'keybindings': {'CreationClassName': u'Symm_StorageVolume', 177 'SystemName': u'SYMMETRIX+000197800123', 178 'DeviceID': device_id2, 179 'SystemCreationClassName': u'Symm_StorageSystem'}} 180 181 test_volume_type = fake_volume.fake_volume_type_obj( 182 context=ctx 183 ) 184 185 test_volume = fake_volume.fake_volume_obj( 186 context=ctx, name='vol1', size=2, provider_auth=None, 187 provider_location=six.text_type(provider_location), 188 volume_type=test_volume_type, host=fake_host, 189 replication_driver_data=six.text_type(provider_location3)) 190 191 test_attached_volume = fake_volume.fake_volume_obj( 192 context=ctx, name='vol1', size=2, provider_auth=None, 193 provider_location=six.text_type(provider_location), host=fake_host, 194 volume_type=test_volume_type, attach_status="attached", 195 replication_driver_data=six.text_type(provider_location3)) 196 197 test_legacy_vol = fake_volume.fake_volume_obj( 198 context=ctx, name='vol1', size=2, provider_auth=None, 199 provider_location=six.text_type(legacy_provider_location), 200 replication_driver_data=six.text_type(legacy_provider_location2), 201 host=fake_host, volume_type=test_volume_type) 202 203 snapshot_id = '390eeb4d-0f56-4a02-ba14-167167967014' 204 205 test_clone_volume = fake_volume.fake_volume_obj( 206 context=ctx, name='vol1', size=2, provider_auth=None, 207 provider_location=six.text_type(provider_location2), 208 host=fake_host, source_volid=test_volume.id, 209 snapshot_id=snapshot_id, _name_id=test_volume.id) 210 211 test_volume_snap_manage = fake_volume.fake_volume_obj( 212 context=ctx, name='vol1', size=2, provider_auth=None, 213 display_name='vol1', 214 provider_location=six.text_type(provider_location), 215 volume_type=test_volume_type, host=fake_host, 216 replication_driver_data=six.text_type(provider_location4)) 217 218 snapshot_display_id = 'my_snap' 219 managed_snap_id = 'OS-390eeb4d-0f56-4a02-ba14-167167967014' 220 test_snapshot_snap_name = 'OS-' + snapshot_id[:6] + snapshot_id[-9:] 221 222 snap_location = {'snap_name': test_snapshot_snap_name, 223 'source_id': device_id} 224 225 test_snapshot = fake_snapshot.fake_snapshot_obj( 226 context=ctx, id=snapshot_id, 227 name='my_snap', size=2, 228 provider_location=six.text_type(snap_location), 229 host=fake_host, volume=test_volume) 230 231 test_legacy_snapshot = fake_snapshot.fake_snapshot_obj( 232 context=ctx, id=test_volume.id, name='my_snap', size=2, 233 provider_location=six.text_type(legacy_provider_location), 234 host=fake_host, volume=test_volume) 235 236 test_failed_snap = fake_snapshot.fake_snapshot_obj( 237 context=ctx, 238 id='4732de9b-98a4-4b6d-ae4b-3cafb3d34220', 239 name=failed_resource, 240 size=2, 241 provider_location=six.text_type(snap_location), 242 host=fake_host, volume=test_volume) 243 244 test_snapshot_manage = fake_snapshot.fake_snapshot_obj( 245 context=ctx, id=snapshot_id, 246 name='my_snap', size=2, 247 provider_location=six.text_type(snap_location), 248 host=fake_host, volume=test_volume_snap_manage, 249 display_name='my_snap') 250 251 location_info = {'location_info': '000197800123#SRP_1#Diamond#DSS', 252 'storage_protocol': 'FC'} 253 test_host = {'capabilities': location_info, 254 'host': fake_host} 255 256 # extra-specs 257 vol_type_extra_specs = {'pool_name': u'Diamond+DSS+SRP_1+000197800123'} 258 vol_type_extra_specs_compr_disabled = { 259 'pool_name': u'Diamond+DSS+SRP_1+000197800123', 260 'storagetype:disablecompression': "true"} 261 vol_type_extra_specs_rep_enabled = { 262 'pool_name': u'Diamond+DSS+SRP_1+000197800123', 263 'replication_enabled': '<is> True'} 264 extra_specs = {'pool_name': u'Diamond+DSS+SRP_1+000197800123', 265 'slo': slo, 266 'workload': workload, 267 'srp': srp, 268 'array': array, 269 'interval': 3, 270 'retries': 120} 271 extra_specs_disable_compression = deepcopy(extra_specs) 272 extra_specs_disable_compression[utils.DISABLECOMPRESSION] = "true" 273 extra_specs_intervals_set = deepcopy(extra_specs) 274 extra_specs_intervals_set['interval'] = 1 275 extra_specs_intervals_set['retries'] = 1 276 extra_specs_rep_enabled = deepcopy(extra_specs) 277 extra_specs_rep_enabled['replication_enabled'] = True 278 rep_extra_specs = deepcopy(extra_specs_rep_enabled) 279 rep_extra_specs['array'] = remote_array 280 rep_extra_specs['interval'] = 0 281 rep_extra_specs['retries'] = 0 282 rep_extra_specs['srp'] = srp2 283 rep_extra_specs['rep_mode'] = 'Synchronous' 284 rep_extra_specs2 = deepcopy(rep_extra_specs) 285 rep_extra_specs2[utils.PORTGROUPNAME] = port_group_name_f 286 test_volume_type_1 = volume_type.VolumeType( 287 id='2b06255d-f5f0-4520-a953-b029196add6a', name='abc', 288 extra_specs=extra_specs 289 ) 290 test_volume_type_list = volume_type.VolumeTypeList( 291 objects=[test_volume_type_1]) 292 293 test_vol_grp_name_id_only = 'ec870a2f-6bf7-4152-aa41-75aad8e2ea96' 294 test_vol_grp_name = 'Grp_source_sg_%s' % test_vol_grp_name_id_only 295 test_fo_vol_group = 'fo_vol_group_%s' % test_vol_grp_name_id_only 296 297 test_group_1 = group.Group( 298 context=None, name=storagegroup_name_source, 299 group_id='abc', size=1, 300 id=test_vol_grp_name_id_only, status='available', 301 provider_auth=None, volume_type_ids=['abc'], 302 group_type_id='grptypeid', 303 volume_types=test_volume_type_list, 304 host=fake_host, provider_location=six.text_type(provider_location)) 305 306 test_group_failed = group.Group( 307 context=None, name=failed_resource, 308 group_id='14b8894e-54ec-450a-b168-c172a16ed166', 309 size=1, 310 id='318c721c-51ad-4160-bfe1-ebde2273836f', 311 status='available', 312 provider_auth=None, volume_type_ids=['abc'], 313 group_type_id='grptypeid', 314 volume_types=test_volume_type_list, 315 host=fake_host, provider_location=six.text_type(provider_location), 316 replication_status=fields.ReplicationStatus.DISABLED) 317 318 test_rep_group = fake_group.fake_group_obj( 319 context=ctx, name=storagegroup_name_source, 320 id=test_vol_grp_name_id_only, host=fake_host, 321 replication_status=fields.ReplicationStatus.ENABLED) 322 323 test_group = fake_group.fake_group_obj( 324 context=ctx, name=storagegroup_name_source, 325 id=test_vol_grp_name_id_only, host=fake_host) 326 327 test_group_without_name = fake_group.fake_group_obj( 328 context=ctx, name=None, 329 id=test_vol_grp_name_id_only, host=fake_host) 330 331 test_group_snapshot_1 = group_snapshot.GroupSnapshot( 332 context=None, id='6560405d-b89a-4f79-9e81-ad1752f5a139', 333 group_id='876d9fbb-de48-4948-9f82-15c913ed05e7', 334 name=group_snapshot_name, 335 group_type_id='c6934c26-dde8-4bf8-a765-82b3d0130e9f', 336 status='available', 337 group=test_group_1) 338 339 test_group_snapshot_failed = group_snapshot.GroupSnapshot( 340 context=None, id='0819dd5e-9aa1-4ec7-9dda-c78e51b2ad76', 341 group_id='1fc735cb-d36c-4352-8aa6-dc1e16b5a0a7', 342 name=failed_resource, 343 group_type_id='6b70de13-98c5-46b2-8f24-e4e96a8988fa', 344 status='available', 345 group=test_group_failed) 346 347 test_volume_group_member = fake_volume.fake_volume_obj( 348 context=ctx, name='vol1', size=2, provider_auth=None, 349 provider_location=six.text_type(provider_location), 350 volume_type=test_volume_type, host=fake_host, 351 replication_driver_data=six.text_type(provider_location3), 352 group_id=test_vol_grp_name_id_only) 353 354 # masking view dict 355 masking_view_dict = { 356 'array': array, 357 'connector': connector, 358 'device_id': device_id, 359 'init_group_name': initiatorgroup_name_f, 360 'initiator_check': False, 361 'maskingview_name': masking_view_name_f, 362 'parent_sg_name': parent_sg_f, 363 'srp': srp, 364 'storagetype:disablecompression': False, 365 utils.PORTGROUPNAME: port_group_name_f, 366 'slo': slo, 367 'storagegroup_name': storagegroup_name_f, 368 'volume_name': test_volume.name, 369 'workload': workload, 370 'replication_enabled': False} 371 372 masking_view_dict_no_slo = deepcopy(masking_view_dict) 373 masking_view_dict_no_slo.update( 374 {'slo': None, 'workload': None, 375 'storagegroup_name': 'OS-HostX-No_SLO-OS-fibre-PG'}) 376 377 masking_view_dict_compression_disabled = deepcopy(masking_view_dict) 378 masking_view_dict_compression_disabled.update( 379 {'storagetype:disablecompression': True, 380 'storagegroup_name': 'OS-HostX-SRP_1-DiamondDSS-OS-fibre-PG-CD'}) 381 382 masking_view_dict_replication_enabled = deepcopy(masking_view_dict) 383 masking_view_dict_replication_enabled.update( 384 {'replication_enabled': True, 385 'storagegroup_name': 'OS-HostX-SRP_1-DiamondDSS-OS-fibre-PG-RE'}) 386 387 # vmax data 388 # sloprovisioning 389 compression_info = {"symmetrixId": ["000197800128"]} 390 inititiatorgroup = [{"initiator": [wwpn1], 391 "hostId": initiatorgroup_name_f, 392 "maskingview": [masking_view_name_f]}, 393 {"initiator": [initiator], 394 "hostId": initiatorgroup_name_i, 395 "maskingview": [masking_view_name_i]}] 396 397 initiator_list = [{"host": initiatorgroup_name_f, 398 "initiatorId": wwpn1, 399 "maskingview": [masking_view_name_f]}, 400 {"host": initiatorgroup_name_i, 401 "initiatorId": initiator, 402 "maskingview": [masking_view_name_i]}, 403 {"initiatorId": [ 404 "FA-1D:4:" + wwpn1, 405 "SE-4E:0:" + initiator]}] 406 407 maskingview = [{"maskingViewId": masking_view_name_f, 408 "portGroupId": port_group_name_f, 409 "storageGroupId": storagegroup_name_f, 410 "hostId": initiatorgroup_name_f, 411 "maskingViewConnection": [ 412 {"host_lun_address": "0003"}]}, 413 {"maskingViewId": masking_view_name_i, 414 "portGroupId": port_group_name_i, 415 "storageGroupId": storagegroup_name_i, 416 "hostId": initiatorgroup_name_i, 417 "maskingViewConnection": [ 418 {"host_lun_address": "0003"}]}, 419 {}] 420 421 portgroup = [{"portGroupId": port_group_name_f, 422 "symmetrixPortKey": [ 423 {"directorId": "FA-1D", 424 "portId": "FA-1D:4"}], 425 "maskingview": [masking_view_name_f]}, 426 {"portGroupId": port_group_name_i, 427 "symmetrixPortKey": [ 428 {"directorId": "SE-4E", 429 "portId": "SE-4E:0"}], 430 "maskingview": [masking_view_name_i]}] 431 432 port_list = [ 433 {"symmetrixPort": {"num_of_masking_views": 1, 434 "maskingview": [masking_view_name_f], 435 "identifier": wwnn1, 436 "symmetrixPortKey": { 437 "directorId": "FA-1D", 438 "portId": "4"}, 439 "portgroup": [port_group_name_f]}}, 440 {"symmetrixPort": {"identifier": initiator, 441 "symmetrixPortKey": { 442 "directorId": "SE-4E", 443 "portId": "0"}, 444 "ip_addresses": [ip], 445 "num_of_masking_views": 1, 446 "maskingview": [masking_view_name_i], 447 "portgroup": [port_group_name_i]}}] 448 449 sg_details = [{"srp": srp, 450 "num_of_vols": 2, 451 "cap_gb": 2, 452 "storageGroupId": defaultstoragegroup_name, 453 "slo": slo, 454 "workload": workload}, 455 {"srp": srp, 456 "num_of_vols": 2, 457 "cap_gb": 2, 458 "storageGroupId": storagegroup_name_f, 459 "slo": slo, 460 "workload": workload, 461 "maskingview": [masking_view_name_f], 462 "parent_storage_group": [parent_sg_f]}, 463 {"srp": srp, 464 "num_of_vols": 2, 465 "cap_gb": 2, 466 "storageGroupId": storagegroup_name_i, 467 "slo": slo, 468 "workload": workload, 469 "maskingview": [masking_view_name_i], 470 "parent_storage_group": [parent_sg_i]}, 471 {"num_of_vols": 2, 472 "cap_gb": 2, 473 "storageGroupId": parent_sg_f, 474 "num_of_child_sgs": 1, 475 "child_storage_group": [storagegroup_name_f], 476 "maskingview": [masking_view_name_f]}, 477 {"num_of_vols": 2, 478 "cap_gb": 2, 479 "storageGroupId": parent_sg_i, 480 "num_of_child_sgs": 1, 481 "child_storage_group": [storagegroup_name_i], 482 "maskingview": [masking_view_name_i], } 483 ] 484 485 sg_details_rep = [{"childNames": [], 486 "numDevicesNonGk": 2, 487 "isLinkTarget": False, 488 "rdf": True, 489 "capacityGB": 2.0, 490 "name": storagegroup_name_source, 491 "snapVXSnapshots": ['6560405d-752f5a139'], 492 "symmetrixId": array, 493 "numSnapVXSnapshots": 1}] 494 495 sg_rdf_details = [{"storageGroupName": test_vol_grp_name, 496 "symmetrixId": array, 497 "modes": ["Synchronous"], 498 "rdfGroupNumber": rdf_group_no, 499 "states": ["Synchronized"]}, 500 {"storageGroupName": test_fo_vol_group, 501 "symmetrixId": array, 502 "modes": ["Synchronous"], 503 "rdfGroupNumber": rdf_group_no, 504 "states": ["Failed Over"]}] 505 506 sg_list = {"storageGroupId": [storagegroup_name_f, 507 defaultstoragegroup_name]} 508 509 sg_list_rep = [storagegroup_name_with_id] 510 511 srp_details = {"srpSloDemandId": ["Bronze", "Diamond", "Gold", 512 "None", "Optimized", "Silver"], 513 "srpId": srp, 514 "total_used_cap_gb": 5244.7, 515 "total_usable_cap_gb": 20514.4, 516 "total_subscribed_cap_gb": 84970.1, 517 "fba_used_capacity": 5244.7, 518 "reserved_cap_percent": 10} 519 520 array_info_wl = {'RestServerIp': '1.1.1.1', 'RestServerPort': 3448, 521 'RestUserName': 'smc', 'RestPassword': 'smc', 522 'SSLVerify': False, 'SerialNumber': array, 523 'srpName': 'SRP_1', 'PortGroup': port_group_name_i, 524 'SLO': 'Diamond', 'Workload': 'OLTP'} 525 526 array_info_no_wl = {'RestServerIp': '1.1.1.1', 'RestServerPort': 3448, 527 'RestUserName': 'smc', 'RestPassword': 'smc', 528 'SSLVerify': False, 'SerialNumber': array, 529 'srpName': 'SRP_1', 'PortGroup': port_group_name_i, 530 'SLO': 'Diamond'} 531 532 volume_details = [{"cap_gb": 2, 533 "num_of_storage_groups": 1, 534 "volumeId": device_id, 535 "volume_identifier": "OS-%s" % test_volume.id, 536 "wwn": volume_wwn, 537 "snapvx_target": 'false', 538 "snapvx_source": 'false', 539 "storageGroupId": [defaultstoragegroup_name, 540 storagegroup_name_f]}, 541 {"cap_gb": 1, 542 "num_of_storage_groups": 1, 543 "volumeId": device_id2, 544 "volume_identifier": "OS-%s" % test_volume.id, 545 "wwn": '600012345', 546 "storageGroupId": [defaultstoragegroup_name, 547 storagegroup_name_f]}, 548 {"cap_gb": 1, 549 "num_of_storage_groups": 0, 550 "volumeId": device_id3, 551 "volume_identifier": '123', 552 "wwn": '600012345'}] 553 554 volume_list = [ 555 {"resultList": {"result": [{"volumeId": device_id}]}}, 556 {"resultList": {"result": [{"volumeId": device_id2}]}}, 557 {"resultList": {"result": [{"volumeId": device_id}, 558 {"volumeId": device_id2}]}}] 559 560 private_vol_details = { 561 "resultList": { 562 "result": [{ 563 "timeFinderInfo": { 564 "snapVXSession": [ 565 {"srcSnapshotGenInfo": [ 566 {"snapshotHeader": { 567 "snapshotName": "temp-1", 568 "device": device_id}, 569 "lnkSnapshotGenInfo": [ 570 {"targetDevice": device_id2}]}]}, 571 {"tgtSrcSnapshotGenInfo": { 572 "snapshotName": "temp-1", 573 "targetDevice": device_id2, 574 "sourceDevice": device_id}}], 575 "snapVXSrc": 'true', 576 "snapVXTgt": 'true'}, 577 "rdfInfo": {"RDFSession": [ 578 {"SRDFStatus": "Ready", 579 "pairState": "Synchronized", 580 "remoteDeviceID": device_id2, 581 "remoteSymmetrixID": remote_array}]}}]}} 582 583 # Service Levels / Workloads 584 workloadtype = {"workloadId": ["OLTP", "OLTP_REP", "DSS", "DSS_REP"]} 585 srp_slo_details = {"serviceLevelDemand": [ 586 {"serviceLevelId": "None"}, {"serviceLevelId": "Diamond"}, 587 {"serviceLevelId": "Gold"}, {"serviceLevelId": "Optimized"}]} 588 slo_details = ['None', 'Diamond', 'Gold', 'Optimized'] 589 powermax_slo_details = {"sloId": ["Bronze", "Diamond", "Gold", 590 "Optimized", "Platinum", "Silver"]} 591 powermax_model_details = {"symmetrixId": array, 592 "model": "PowerMax_2000", 593 "ucode": "5978.1091.1092"} 594 vmax_slo_details = {"sloId": ["Diamond", "Optimized"]} 595 vmax_model_details = {"model": "VMAX450F"} 596 597 # replication 598 volume_snap_vx = {"snapshotLnks": [], 599 "snapshotSrcs": [ 600 {"generation": 0, 601 "linkedDevices": [ 602 {"targetDevice": device_id2, 603 "percentageCopied": 100, 604 "state": "Copied", 605 "copy": True, 606 "defined": True, 607 "linked": True}], 608 "snapshotName": test_snapshot_snap_name, 609 "state": "Established"}]} 610 capabilities = {"symmetrixCapability": [{"rdfCapable": True, 611 "snapVxCapable": True, 612 "symmetrixId": "0001111111"}, 613 {"symmetrixId": array, 614 "snapVxCapable": True, 615 "rdfCapable": True}]} 616 group_snap_vx = {"generation": 0, 617 "isLinked": False, 618 "numUniqueTracks": 0, 619 "isRestored": False, 620 "name": group_snapshot_name, 621 "numStorageGroupVolumes": 1, 622 "state": ["Established"], 623 "timeToLiveExpiryDate": "N/A", 624 "isExpired": False, 625 "numSharedTracks": 0, 626 "timestamp": "00:30:50 Fri, 02 Jun 2017 IST +0100", 627 "numSourceVolumes": 1 628 } 629 group_snap_vx_1 = {"generation": 0, 630 "isLinked": False, 631 "numUniqueTracks": 0, 632 "isRestored": False, 633 "name": group_snapshot_name, 634 "numStorageGroupVolumes": 1, 635 "state": ["Copied"], 636 "timeToLiveExpiryDate": "N/A", 637 "isExpired": False, 638 "numSharedTracks": 0, 639 "timestamp": "00:30:50 Fri, 02 Jun 2017 IST +0100", 640 "numSourceVolumes": 1, 641 "linkedStorageGroup": 642 {"name": target_group_name, 643 "percentageCopied": 100}, 644 } 645 grp_snapvx_links = [{"name": target_group_name, 646 "percentageCopied": 100}, 647 {"name": "another-target", 648 "percentageCopied": 90}] 649 650 rdf_group_list = {"rdfGroupID": [{"rdfgNumber": rdf_group_no, 651 "label": rdf_group_name}]} 652 rdf_group_details = {"modes": ["Synchronous"], 653 "remoteSymmetrix": remote_array, 654 "label": rdf_group_name, 655 "type": "Dynamic", 656 "numDevices": 1, 657 "remoteRdfgNumber": rdf_group_no, 658 "rdfgNumber": rdf_group_no} 659 rdf_group_vol_details = {"remoteRdfGroupNumber": rdf_group_no, 660 "localSymmetrixId": array, 661 "volumeConfig": "RDF1+TDEV", 662 "localRdfGroupNumber": rdf_group_no, 663 "localVolumeName": device_id, 664 "rdfpairState": "Synchronized", 665 "remoteVolumeName": device_id2, 666 "localVolumeState": "Ready", 667 "rdfMode": "Synchronous", 668 "remoteVolumeState": "Write Disabled", 669 "remoteSymmetrixId": remote_array} 670 671 # system 672 job_list = [{"status": "SUCCEEDED", 673 "jobId": "12345", 674 "result": "created", 675 "resourceLink": "storagegroup/%s" % storagegroup_name_f}, 676 {"status": "RUNNING", "jobId": "55555"}, 677 {"status": "FAILED", "jobId": "09999"}] 678 symmetrix = [{"symmetrixId": array, 679 "model": "VMAX250F", 680 "ucode": "5977.1091.1092"}, 681 {"symmetrixId": array_herc, 682 "model": "VMAXHERC", 683 "ucode": "5978.1091.1092"}] 684 version_details = {"version": "V9.0.0.1"} 685 686 headroom = {"headroom": [{"headroomCapacity": 20348.29}]} 687 688 689class FakeLookupService(object): 690 def get_device_mapping_from_network(self, initiator_wwns, target_wwns): 691 return VMAXCommonData.device_map 692 693 694class FakeResponse(object): 695 696 def __init__(self, status_code, return_object): 697 self.status_code = status_code 698 self.return_object = return_object 699 700 def json(self): 701 if self.return_object: 702 return self.return_object 703 else: 704 raise ValueError 705 706 707class FakeRequestsSession(object): 708 709 def __init__(self, *args, **kwargs): 710 self.data = VMAXCommonData() 711 712 def request(self, method, url, params=None, data=None): 713 return_object = '' 714 status_code = 200 715 if method == 'GET': 716 status_code, return_object = self._get_request(url, params) 717 718 elif method == 'POST' or method == 'PUT': 719 status_code, return_object = self._post_or_put(url, data) 720 721 elif method == 'DELETE': 722 status_code, return_object = self._delete(url) 723 724 elif method == 'TIMEOUT': 725 raise requests.Timeout 726 727 elif method == 'EXCEPTION': 728 raise Exception 729 730 return FakeResponse(status_code, return_object) 731 732 def _get_request(self, url, params): 733 status_code = 200 734 return_object = None 735 if self.data.failed_resource in url: 736 status_code = 500 737 return_object = self.data.job_list[2] 738 elif 'sloprovisioning' in url: 739 if 'volume' in url: 740 return_object = self._sloprovisioning_volume(url, params) 741 elif 'storagegroup' in url: 742 return_object = self._sloprovisioning_sg(url) 743 elif 'maskingview' in url: 744 return_object = self._sloprovisioning_mv(url) 745 elif 'portgroup' in url: 746 return_object = self._sloprovisioning_pg(url) 747 elif 'director' in url: 748 return_object = self._sloprovisioning_port(url) 749 elif 'host' in url: 750 return_object = self._sloprovisioning_ig(url) 751 elif 'initiator' in url: 752 return_object = self._sloprovisioning_initiator(url) 753 elif 'srp' in url: 754 return_object = self.data.srp_details 755 elif 'workloadtype' in url: 756 return_object = self.data.workloadtype 757 elif 'compressionCapable' in url: 758 return_object = self.data.compression_info 759 elif 'slo' in url: 760 return_object = self.data.powermax_slo_details 761 762 elif 'replication' in url: 763 return_object = self._replication(url) 764 765 elif 'system' in url: 766 return_object = self._system(url) 767 768 elif 'headroom' in url: 769 return_object = self.data.headroom 770 771 return status_code, return_object 772 773 def _sloprovisioning_volume(self, url, params): 774 return_object = self.data.volume_list[2] 775 if '/private' in url: 776 return_object = self.data.private_vol_details 777 elif params: 778 if '1' in params.values(): 779 return_object = self.data.volume_list[0] 780 elif '2' in params.values(): 781 return_object = self.data.volume_list[1] 782 else: 783 for vol in self.data.volume_details: 784 if vol['volumeId'] in url: 785 return_object = vol 786 break 787 return return_object 788 789 def _sloprovisioning_sg(self, url): 790 return_object = self.data.sg_list 791 for sg in self.data.sg_details: 792 if sg['storageGroupId'] in url: 793 return_object = sg 794 break 795 return return_object 796 797 def _sloprovisioning_mv(self, url): 798 if self.data.masking_view_name_i in url: 799 return_object = self.data.maskingview[1] 800 else: 801 return_object = self.data.maskingview[0] 802 return return_object 803 804 def _sloprovisioning_pg(self, url): 805 return_object = None 806 for pg in self.data.portgroup: 807 if pg['portGroupId'] in url: 808 return_object = pg 809 break 810 return return_object 811 812 def _sloprovisioning_port(self, url): 813 return_object = None 814 for port in self.data.port_list: 815 if port['symmetrixPort']['symmetrixPortKey']['directorId'] in url: 816 return_object = port 817 break 818 return return_object 819 820 def _sloprovisioning_ig(self, url): 821 return_object = None 822 for ig in self.data.inititiatorgroup: 823 if ig['hostId'] in url: 824 return_object = ig 825 break 826 return return_object 827 828 def _sloprovisioning_initiator(self, url): 829 return_object = self.data.initiator_list[2] 830 if self.data.wwpn1 in url: 831 return_object = self.data.initiator_list[0] 832 elif self.data.initiator in url: 833 return_object = self.data.initiator_list[1] 834 return return_object 835 836 def _replication(self, url): 837 return_object = None 838 if 'storagegroup' in url: 839 return_object = self._replication_sg(url) 840 elif 'rdf_group' in url: 841 if self.data.device_id in url: 842 return_object = self.data.rdf_group_vol_details 843 elif self.data.rdf_group_no in url: 844 return_object = self.data.rdf_group_details 845 else: 846 return_object = self.data.rdf_group_list 847 elif 'snapshot' in url: 848 return_object = self.data.volume_snap_vx 849 elif 'capabilities' in url: 850 return_object = self.data.capabilities 851 return return_object 852 853 def _replication_sg(self, url): 854 return_object = None 855 if 'generation' in url: 856 return_object = self.data.group_snap_vx 857 elif 'rdf_group' in url: 858 for sg in self.data.sg_rdf_details: 859 if sg['storageGroupName'] in url: 860 return_object = sg 861 break 862 elif 'storagegroup' in url: 863 return_object = self.data.sg_details_rep[0] 864 return return_object 865 866 def _system(self, url): 867 return_object = None 868 if 'job' in url: 869 for job in self.data.job_list: 870 if job['jobId'] in url: 871 return_object = job 872 break 873 elif 'version' in url: 874 return_object = self.data.version_details 875 else: 876 for symm in self.data.symmetrix: 877 if symm['symmetrixId'] in url: 878 return_object = symm 879 break 880 return return_object 881 882 def _post_or_put(self, url, payload): 883 return_object = self.data.job_list[0] 884 status_code = 201 885 if self.data.failed_resource in url: 886 status_code = 500 887 return_object = self.data.job_list[2] 888 elif payload: 889 payload = ast.literal_eval(payload) 890 if self.data.failed_resource in payload.values(): 891 status_code = 500 892 return_object = self.data.job_list[2] 893 if payload.get('executionOption'): 894 status_code = 202 895 return status_code, return_object 896 897 def _delete(self, url): 898 if self.data.failed_resource in url: 899 status_code = 500 900 return_object = self.data.job_list[2] 901 else: 902 status_code = 204 903 return_object = None 904 return status_code, return_object 905 906 def session(self): 907 return FakeRequestsSession() 908 909 910class FakeConfiguration(object): 911 912 def __init__(self, emc_file=None, volume_backend_name=None, 913 interval=0, retries=0, replication_device=None, **kwargs): 914 self.cinder_dell_emc_config_file = emc_file 915 self.interval = interval 916 self.retries = retries 917 self.volume_backend_name = volume_backend_name 918 self.config_group = volume_backend_name 919 self.san_is_local = False 920 if replication_device: 921 self.replication_device = [replication_device] 922 for key, value in kwargs.items(): 923 if key == 'san_login': 924 self.san_login = value 925 elif key == 'san_password': 926 self.san_password = value 927 elif key == 'san_ip': 928 self.san_ip = value 929 elif key == 'san_rest_port': 930 self.san_rest_port = value 931 elif key == 'vmax_srp': 932 self.vmax_srp = value 933 elif key == 'vmax_service_level': 934 self.vmax_service_level = value 935 elif key == 'vmax_workload': 936 self.vmax_workload = value 937 elif key == 'vmax_port_groups': 938 self.vmax_port_groups = value 939 elif key == 'vmax_array': 940 self.vmax_array = value 941 elif key == 'use_chap_auth': 942 self.use_chap_auth = value 943 elif key == 'chap_username': 944 self.chap_username = value 945 elif key == 'chap_password': 946 self.chap_password = value 947 elif key == 'driver_ssl_cert_verify': 948 self.driver_ssl_cert_verify = value 949 elif key == 'driver_ssl_cert_path': 950 self.driver_ssl_cert_path = value 951 952 def safe_get(self, key): 953 try: 954 return getattr(self, key) 955 except Exception: 956 return None 957 958 def append_config_values(self, values): 959 pass 960 961 962class FakeXML(object): 963 964 def __init__(self): 965 """""" 966 self.tempdir = tempfile.mkdtemp() 967 self.data = VMAXCommonData() 968 969 def create_fake_config_file(self, config_group, portgroup, 970 ssl_verify=False): 971 972 doc = minidom.Document() 973 emc = doc.createElement("EMC") 974 doc.appendChild(emc) 975 doc = self.add_array_info(doc, emc, portgroup, ssl_verify) 976 filename = 'cinder_dell_emc_config_%s.xml' % config_group 977 config_file_path = self.tempdir + '/' + filename 978 979 f = open(config_file_path, 'w') 980 doc.writexml(f) 981 f.close() 982 return config_file_path 983 984 def add_array_info(self, doc, emc, portgroup_name, ssl_verify): 985 array = doc.createElement("Array") 986 arraytext = doc.createTextNode(self.data.array) 987 emc.appendChild(array) 988 array.appendChild(arraytext) 989 990 ecomserverip = doc.createElement("RestServerIp") 991 ecomserveriptext = doc.createTextNode("1.1.1.1") 992 emc.appendChild(ecomserverip) 993 ecomserverip.appendChild(ecomserveriptext) 994 995 ecomserverport = doc.createElement("RestServerPort") 996 ecomserverporttext = doc.createTextNode("8443") 997 emc.appendChild(ecomserverport) 998 ecomserverport.appendChild(ecomserverporttext) 999 1000 ecomusername = doc.createElement("RestUserName") 1001 ecomusernametext = doc.createTextNode("smc") 1002 emc.appendChild(ecomusername) 1003 ecomusername.appendChild(ecomusernametext) 1004 1005 ecompassword = doc.createElement("RestPassword") 1006 ecompasswordtext = doc.createTextNode("smc") 1007 emc.appendChild(ecompassword) 1008 ecompassword.appendChild(ecompasswordtext) 1009 1010 portgroup = doc.createElement("PortGroup") 1011 portgrouptext = doc.createTextNode(portgroup_name) 1012 portgroup.appendChild(portgrouptext) 1013 1014 portgroups = doc.createElement("PortGroups") 1015 portgroups.appendChild(portgroup) 1016 emc.appendChild(portgroups) 1017 1018 srp = doc.createElement("SRP") 1019 srptext = doc.createTextNode("SRP_1") 1020 emc.appendChild(srp) 1021 srp.appendChild(srptext) 1022 1023 if ssl_verify: 1024 restcert = doc.createElement("SSLCert") 1025 restcerttext = doc.createTextNode("/path/cert.crt") 1026 emc.appendChild(restcert) 1027 restcert.appendChild(restcerttext) 1028 1029 restverify = doc.createElement("SSLVerify") 1030 restverifytext = doc.createTextNode("/path/cert.pem") 1031 emc.appendChild(restverify) 1032 restverify.appendChild(restverifytext) 1033 return doc 1034 1035 1036class VMAXUtilsTest(test.TestCase): 1037 def setUp(self): 1038 self.data = VMAXCommonData() 1039 volume_utils.get_max_over_subscription_ratio = mock.Mock() 1040 super(VMAXUtilsTest, self).setUp() 1041 config_group = 'UtilsTests' 1042 fake_xml = FakeXML().create_fake_config_file( 1043 config_group, self.data.port_group_name_i, True) 1044 configuration = FakeConfiguration(fake_xml, config_group) 1045 rest.VMAXRest._establish_rest_session = mock.Mock( 1046 return_value=FakeRequestsSession()) 1047 driver = iscsi.VMAXISCSIDriver(configuration=configuration) 1048 self.driver = driver 1049 self.common = self.driver.common 1050 self.utils = self.common.utils 1051 1052 def test_get_volumetype_extra_specs(self): 1053 with mock.patch.object(volume_types, 'get_volume_type_extra_specs', 1054 return_value={'specs'}) as type_mock: 1055 # path 1: volume_type_id not passed in 1056 self.data.test_volume.volume_type_id = ( 1057 self.data.test_volume_type.id) 1058 self.utils.get_volumetype_extra_specs(self.data.test_volume) 1059 volume_types.get_volume_type_extra_specs.assert_called_once_with( 1060 self.data.test_volume_type.id) 1061 type_mock.reset_mock() 1062 # path 2: volume_type_id passed in 1063 self.utils.get_volumetype_extra_specs(self.data.test_volume, '123') 1064 volume_types.get_volume_type_extra_specs.assert_called_once_with( 1065 '123') 1066 type_mock.reset_mock() 1067 # path 3: no type_id 1068 self.utils.get_volumetype_extra_specs(self.data.test_clone_volume) 1069 (volume_types.get_volume_type_extra_specs. 1070 assert_not_called()) 1071 1072 def test_get_volumetype_extra_specs_exception(self): 1073 extra_specs = self.utils.get_volumetype_extra_specs( 1074 {'name': 'no_type_id'}) 1075 self.assertEqual({}, extra_specs) 1076 1077 def test_get_random_portgroup(self): 1078 # 4 portgroups 1079 data = ("<?xml version='1.0' encoding='UTF-8'?>\n<EMC>\n" 1080 "<PortGroups>" 1081 "<PortGroup>OS-PG1</PortGroup>\n" 1082 "<PortGroup>OS-PG2</PortGroup>\n" 1083 "<PortGroup>OS-PG3</PortGroup>\n" 1084 "<PortGroup>OS-PG4</PortGroup>\n" 1085 "</PortGroups>" 1086 "</EMC>") 1087 dom = minidom.parseString(data) 1088 portgroup = self.utils._get_random_portgroup(dom) 1089 self.assertIn('OS-PG', portgroup) 1090 1091 # Duplicate portgroups 1092 data = ("<?xml version='1.0' encoding='UTF-8'?>\n<EMC>\n" 1093 "<PortGroups>" 1094 "<PortGroup>OS-PG1</PortGroup>\n" 1095 "<PortGroup>OS-PG1</PortGroup>\n" 1096 "<PortGroup>OS-PG1</PortGroup>\n" 1097 "<PortGroup>OS-PG2</PortGroup>\n" 1098 "</PortGroups>" 1099 "</EMC>") 1100 dom = minidom.parseString(data) 1101 portgroup = self.utils._get_random_portgroup(dom) 1102 self.assertIn('OS-PG', portgroup) 1103 1104 def test_get_random_portgroup_none(self): 1105 # Missing PortGroup tag 1106 data = ("<?xml version='1.0' encoding='UTF-8'?>\n<EMC>\n" 1107 "</EMC>") 1108 dom = minidom.parseString(data) 1109 self.assertIsNone(self.utils._get_random_portgroup(dom)) 1110 1111 # Missing portgroups 1112 data = ("<?xml version='1.0' encoding='UTF-8'?>\n<EMC>\n" 1113 "<PortGroups>" 1114 "</PortGroups>" 1115 "</EMC>") 1116 dom = minidom.parseString(data) 1117 self.assertIsNone(self.utils._get_random_portgroup(dom)) 1118 1119 def test_get_host_short_name(self): 1120 host_under_16_chars = 'host_13_chars' 1121 host1 = self.utils.get_host_short_name( 1122 host_under_16_chars) 1123 self.assertEqual(host_under_16_chars, host1) 1124 1125 host_over_16_chars = ( 1126 'host_over_16_chars_host_over_16_chars_host_over_16_chars') 1127 # Check that the same md5 value is retrieved from multiple calls 1128 host2 = self.utils.get_host_short_name( 1129 host_over_16_chars) 1130 host3 = self.utils.get_host_short_name( 1131 host_over_16_chars) 1132 self.assertEqual(host2, host3) 1133 host_with_period = 'hostname.with.many.parts' 1134 ref_host_name = self.utils.generate_unique_trunc_host('hostname') 1135 host4 = self.utils.get_host_short_name(host_with_period) 1136 self.assertEqual(ref_host_name, host4) 1137 1138 def test_get_volume_element_name(self): 1139 volume_id = 'ea95aa39-080b-4f11-9856-a03acf9112ad' 1140 volume_element_name = self.utils.get_volume_element_name(volume_id) 1141 expect_vol_element_name = ('OS-' + volume_id) 1142 self.assertEqual(expect_vol_element_name, volume_element_name) 1143 1144 def test_parse_file_to_get_array_map(self): 1145 kwargs = ( 1146 {'RestServerIp': '1.1.1.1', 1147 'RestServerPort': '8443', 1148 'RestUserName': 'smc', 1149 'RestPassword': 'smc', 1150 'SSLCert': '/path/cert.crt', 1151 'SSLVerify': '/path/cert.pem', 1152 'SerialNumber': self.data.array, 1153 'srpName': 'SRP_1', 1154 'PortGroup': self.data.port_group_name_i}) 1155 array_info = self.utils.parse_file_to_get_array_map( 1156 self.common.configuration.cinder_dell_emc_config_file) 1157 self.assertEqual(kwargs, array_info) 1158 1159 @mock.patch.object(utils.VMAXUtils, 1160 '_get_connection_info') 1161 @mock.patch.object(utils.VMAXUtils, 1162 '_get_random_portgroup') 1163 def test_parse_file_to_get_array_map_errors(self, mock_port, mock_conn): 1164 tempdir = tempfile.mkdtemp() 1165 doc = minidom.Document() 1166 emc = doc.createElement("EMC") 1167 doc.appendChild(emc) 1168 filename = 'cinder_dell_emc_config_%s.xml' % 'fake_xml' 1169 config_file_path = tempdir + '/' + filename 1170 f = open(config_file_path, 'w') 1171 doc.writexml(f) 1172 f.close() 1173 array_info = self.utils.parse_file_to_get_array_map( 1174 config_file_path) 1175 self.assertIsNone(array_info['SerialNumber']) 1176 1177 def test_parse_file_to_get_array_map_conn_errors(self): 1178 tempdir = tempfile.mkdtemp() 1179 doc = minidom.Document() 1180 emc = doc.createElement("EMC") 1181 doc.appendChild(emc) 1182 filename = 'cinder_dell_emc_config_%s.xml' % 'fake_xml' 1183 config_file_path = tempdir + '/' + filename 1184 f = open(config_file_path, 'w') 1185 doc.writexml(f) 1186 f.close() 1187 self.assertRaises(exception.VolumeBackendAPIException, 1188 self.utils.parse_file_to_get_array_map, 1189 config_file_path) 1190 1191 def test_truncate_string(self): 1192 # string is less than max number 1193 str_to_truncate = 'string' 1194 response = self.utils.truncate_string(str_to_truncate, 10) 1195 self.assertEqual(str_to_truncate, response) 1196 1197 def test_get_default_oversubscription_ratio(self): 1198 default_ratio = 20.0 1199 max_over_sub_ratio1 = 30.0 1200 returned_max = self.utils.get_default_oversubscription_ratio( 1201 max_over_sub_ratio1) 1202 self.assertEqual(max_over_sub_ratio1, returned_max) 1203 max_over_sub_ratio2 = 0.5 1204 returned_max = self.utils.get_default_oversubscription_ratio( 1205 max_over_sub_ratio2) 1206 self.assertEqual(default_ratio, returned_max) 1207 1208 def test_get_default_storage_group_name_slo_workload(self): 1209 srp_name = self.data.srp 1210 slo = self.data.slo 1211 workload = self.data.workload 1212 sg_name = self.utils.get_default_storage_group_name( 1213 srp_name, slo, workload) 1214 self.assertEqual(self.data.defaultstoragegroup_name, sg_name) 1215 1216 def test_get_default_storage_group_name_no_slo(self): 1217 srp_name = self.data.srp 1218 slo = None 1219 workload = None 1220 sg_name = self.utils.get_default_storage_group_name( 1221 srp_name, slo, workload) 1222 self.assertEqual(self.data.default_sg_no_slo, sg_name) 1223 1224 def test_get_default_storage_group_name_compr_disabled(self): 1225 srp_name = self.data.srp 1226 slo = self.data.slo 1227 workload = self.data.workload 1228 sg_name = self.utils.get_default_storage_group_name( 1229 srp_name, slo, workload, True) 1230 self.assertEqual(self.data.default_sg_compr_disabled, sg_name) 1231 1232 def test_get_time_delta(self): 1233 start_time = 1487781721.09 1234 end_time = 1487781758.16 1235 delta = end_time - start_time 1236 ref_delta = six.text_type(datetime.timedelta(seconds=int(delta))) 1237 time_delta = self.utils.get_time_delta(start_time, end_time) 1238 self.assertEqual(ref_delta, time_delta) 1239 1240 def test_get_short_protocol_type(self): 1241 # iscsi 1242 short_i_protocol = self.utils.get_short_protocol_type('iscsi') 1243 self.assertEqual('I', short_i_protocol) 1244 # fc 1245 short_f_protocol = self.utils.get_short_protocol_type('FC') 1246 self.assertEqual('F', short_f_protocol) 1247 # else 1248 other_protocol = self.utils.get_short_protocol_type('OTHER') 1249 self.assertEqual('OTHER', other_protocol) 1250 1251 def test_get_temp_snap_name(self): 1252 clone_name = "12345" 1253 source_device_id = self.data.device_id 1254 ref_name = "temp-00001-12345" 1255 snap_name = self.utils.get_temp_snap_name( 1256 clone_name, source_device_id) 1257 self.assertEqual(ref_name, snap_name) 1258 1259 def test_get_array_and_device_id(self): 1260 volume = deepcopy(self.data.test_volume) 1261 external_ref = {u'source-name': u'00002'} 1262 array, device_id = self.utils.get_array_and_device_id( 1263 volume, external_ref) 1264 self.assertEqual(self.data.array, array) 1265 self.assertEqual('00002', device_id) 1266 1267 def test_get_array_and_device_id_exception(self): 1268 volume = deepcopy(self.data.test_volume) 1269 external_ref = {u'source-name': None} 1270 self.assertRaises(exception.VolumeBackendAPIException, 1271 self.utils.get_array_and_device_id, 1272 volume, external_ref) 1273 1274 def test_get_pg_short_name(self): 1275 pg_under_12_chars = 'pg_11_chars' 1276 pg1 = self.utils.get_pg_short_name(pg_under_12_chars) 1277 self.assertEqual(pg_under_12_chars, pg1) 1278 1279 pg_over_12_chars = 'portgroup_over_12_characters' 1280 # Check that the same md5 value is retrieved from multiple calls 1281 pg2 = self.utils.get_pg_short_name(pg_over_12_chars) 1282 pg3 = self.utils.get_pg_short_name(pg_over_12_chars) 1283 self.assertEqual(pg2, pg3) 1284 1285 def test_is_compression_disabled_true(self): 1286 extra_specs = self.data.extra_specs_disable_compression 1287 do_disable_compression = self.utils.is_compression_disabled( 1288 extra_specs) 1289 self.assertTrue(do_disable_compression) 1290 1291 def test_is_compression_disabled_false(self): 1292 # Path 1: no compression extra spec set 1293 extra_specs = self.data.extra_specs 1294 do_disable_compression = self.utils.is_compression_disabled( 1295 extra_specs) 1296 self.assertFalse(do_disable_compression) 1297 # Path 2: compression extra spec set to false 1298 extra_specs2 = deepcopy(extra_specs) 1299 extra_specs2.update({utils.DISABLECOMPRESSION: 'false'}) 1300 do_disable_compression2 = self.utils.is_compression_disabled( 1301 extra_specs) 1302 self.assertFalse(do_disable_compression2) 1303 1304 def test_change_compression_type_true(self): 1305 source_compr_disabled_true = 'true' 1306 new_type_compr_disabled = { 1307 'extra_specs': {utils.DISABLECOMPRESSION: 'no'}} 1308 ans = self.utils.change_compression_type( 1309 source_compr_disabled_true, new_type_compr_disabled) 1310 self.assertTrue(ans) 1311 1312 def test_change_compression_type_false(self): 1313 source_compr_disabled_true = True 1314 new_type_compr_disabled = { 1315 'extra_specs': {utils.DISABLECOMPRESSION: 'true'}} 1316 ans = self.utils.change_compression_type( 1317 source_compr_disabled_true, new_type_compr_disabled) 1318 self.assertFalse(ans) 1319 1320 def test_is_replication_enabled(self): 1321 is_re = self.utils.is_replication_enabled( 1322 self.data.vol_type_extra_specs_rep_enabled) 1323 self.assertTrue(is_re) 1324 is_re2 = self.utils.is_replication_enabled(self.data.extra_specs) 1325 self.assertFalse(is_re2) 1326 1327 def test_get_replication_config(self): 1328 # Success, allow_extend false 1329 rep_device_list1 = [{'target_device_id': self.data.remote_array, 1330 'remote_pool': self.data.srp, 1331 'remote_port_group': self.data.port_group_name_f, 1332 'rdf_group_label': self.data.rdf_group_name}] 1333 rep_config1 = self.utils.get_replication_config(rep_device_list1) 1334 self.assertEqual(self.data.remote_array, rep_config1['array']) 1335 # Success, allow_extend true 1336 rep_device_list2 = rep_device_list1 1337 rep_device_list2[0]['allow_extend'] = 'true' 1338 rep_config2 = self.utils.get_replication_config(rep_device_list2) 1339 self.assertTrue(rep_config2['allow_extend']) 1340 # No rep_device_list 1341 rep_device_list3 = [] 1342 rep_config3 = self.utils.get_replication_config(rep_device_list3) 1343 self.assertIsNone(rep_config3) 1344 # Exception 1345 rep_device_list4 = [{'target_device_id': self.data.remote_array, 1346 'remote_pool': self.data.srp}] 1347 self.assertRaises(exception.VolumeBackendAPIException, 1348 self.utils.get_replication_config, rep_device_list4) 1349 # Success, mode is async 1350 rep_device_list5 = rep_device_list2 1351 rep_device_list5[0]['mode'] = 'async' 1352 rep_config5 = self.utils.get_replication_config(rep_device_list5) 1353 self.assertEqual(utils.REP_ASYNC, rep_config5['mode']) 1354 # Success, mode is metro - no other options set 1355 rep_device_list6 = rep_device_list5 1356 rep_device_list6[0]['mode'] = 'metro' 1357 rep_config6 = self.utils.get_replication_config(rep_device_list6) 1358 self.assertFalse(rep_config6['metro_bias']) 1359 self.assertFalse(rep_config6['allow_delete_metro']) 1360 # Success, mode is metro - metro options true 1361 rep_device_list7 = rep_device_list6 1362 rep_device_list6[0].update( 1363 {'allow_delete_metro': 'true', 'metro_use_bias': 'true'}) 1364 rep_config7 = self.utils.get_replication_config(rep_device_list7) 1365 self.assertTrue(rep_config7['metro_bias']) 1366 self.assertTrue(rep_config7['allow_delete_metro']) 1367 1368 def test_is_volume_failed_over(self): 1369 vol = deepcopy(self.data.test_volume) 1370 vol.replication_status = fields.ReplicationStatus.FAILED_OVER 1371 is_fo1 = self.utils.is_volume_failed_over(vol) 1372 self.assertTrue(is_fo1) 1373 is_fo2 = self.utils.is_volume_failed_over(self.data.test_volume) 1374 self.assertFalse(is_fo2) 1375 is_fo3 = self.utils.is_volume_failed_over(None) 1376 self.assertFalse(is_fo3) 1377 1378 def test_add_legacy_pools(self): 1379 pools = [{'pool_name': "Diamond+None+SRP_1+000197800111"}, 1380 {'pool_name': "Diamond+OLTP+SRP_1+000197800111"}] 1381 new_pools = self.utils.add_legacy_pools(pools) 1382 ref_pools = [{'pool_name': "Diamond+None+SRP_1+000197800111"}, 1383 {'pool_name': "Diamond+OLTP+SRP_1+000197800111"}, 1384 {'pool_name': "Diamond+SRP_1+000197800111"}] 1385 self.assertEqual(ref_pools, new_pools) 1386 1387 def test_update_volume_group_name(self): 1388 group = self.data.test_group_1 1389 ref_group_name = self.data.test_vol_grp_name 1390 vol_grp_name = self.utils.update_volume_group_name(group) 1391 self.assertEqual(ref_group_name, vol_grp_name) 1392 1393 def test_update_volume_group_name_id_only(self): 1394 group = self.data.test_group_without_name 1395 ref_group_name = self.data.test_vol_grp_name_id_only 1396 vol_grp_name = self.utils.update_volume_group_name(group) 1397 self.assertEqual(ref_group_name, vol_grp_name) 1398 1399 def test_get_volume_group_utils(self): 1400 array, intervals_retries = self.utils.get_volume_group_utils( 1401 self.data.test_group_1, interval=1, retries=1) 1402 ref_array = self.data.array 1403 self.assertEqual(ref_array, array) 1404 1405 def test_update_volume_model_updates(self): 1406 volume_model_updates = [{'id': '1', 'status': 'available'}] 1407 volumes = [self.data.test_volume] 1408 ref_val = {'id': self.data.test_volume.id, 1409 'status': 'error_deleting'} 1410 ret_val = self.utils.update_volume_model_updates( 1411 volume_model_updates, volumes, 'abc', status='error_deleting') 1412 self.assertEqual(ref_val, ret_val[1]) 1413 1414 def test_update_volume_model_updates_empty_update_list(self): 1415 volume_model_updates = [] 1416 volumes = [self.data.test_volume] 1417 ref_val = [{'id': self.data.test_volume.id, 1418 'status': 'available'}] 1419 ret_val = self.utils.update_volume_model_updates( 1420 volume_model_updates, volumes, 'abc') 1421 self.assertEqual(ref_val, ret_val) 1422 1423 def test_update_volume_model_updates_empty_vol_list(self): 1424 volume_model_updates = [] 1425 volumes = [] 1426 ref_val = [] 1427 ret_val = self.utils.update_volume_model_updates( 1428 volume_model_updates, volumes, 'abc') 1429 self.assertEqual(ref_val, ret_val) 1430 1431 def test_check_replication_matched(self): 1432 # Check 1: Volume is not part of a group 1433 self.utils.check_replication_matched( 1434 self.data.test_volume, self.data.extra_specs) 1435 group_volume = deepcopy(self.data.test_volume) 1436 group_volume.group = self.data.test_group 1437 with mock.patch.object(volume_utils, 'is_group_a_type', 1438 return_value=False): 1439 # Check 2: Both volume and group have the same rep status 1440 self.utils.check_replication_matched( 1441 group_volume, self.data.extra_specs) 1442 # Check 3: Volume and group have different rep status 1443 with mock.patch.object(self.utils, 'is_replication_enabled', 1444 return_value=True): 1445 self.assertRaises(exception.InvalidInput, 1446 self.utils.check_replication_matched, 1447 group_volume, self.data.extra_specs) 1448 1449 def test_check_rep_status_enabled(self): 1450 # Check 1: not replication enabled 1451 with mock.patch.object(volume_utils, 'is_group_a_type', 1452 return_value=False): 1453 self.utils.check_rep_status_enabled(self.data.test_group) 1454 # Check 2: replication enabled, status enabled 1455 with mock.patch.object(volume_utils, 'is_group_a_type', 1456 return_value=True): 1457 self.utils.check_rep_status_enabled(self.data.test_rep_group) 1458 # Check 3: replication enabled, status disabled 1459 self.assertRaises(exception.InvalidInput, 1460 self.utils.check_rep_status_enabled, 1461 self.data.test_group) 1462 1463 def test_get_replication_prefix(self): 1464 async_prefix = self.utils.get_replication_prefix(utils.REP_ASYNC) 1465 self.assertEqual('-RA', async_prefix) 1466 sync_prefix = self.utils.get_replication_prefix(utils.REP_SYNC) 1467 self.assertEqual('-RE', sync_prefix) 1468 metro_prefix = self.utils.get_replication_prefix(utils.REP_METRO) 1469 self.assertEqual('-RM', metro_prefix) 1470 1471 def test_get_async_rdf_managed_grp_name(self): 1472 rep_config = {'rdf_group_label': self.data.rdf_group_name, 1473 'mode': utils.REP_ASYNC} 1474 grp_name = self.utils.get_async_rdf_managed_grp_name(rep_config) 1475 self.assertEqual(self.data.rdf_managed_async_grp, grp_name) 1476 1477 def test_is_metro_device(self): 1478 rep_config = {'mode': utils.REP_METRO} 1479 is_metro = self.utils.is_metro_device( 1480 rep_config, self.data.rep_extra_specs) 1481 self.assertTrue(is_metro) 1482 rep_config2 = {'mode': utils.REP_ASYNC} 1483 is_metro2 = self.utils.is_metro_device( 1484 rep_config2, self.data.rep_extra_specs) 1485 self.assertFalse(is_metro2) 1486 1487 def test_does_vol_need_rdf_management_group(self): 1488 self.assertFalse(self.utils.does_vol_need_rdf_management_group( 1489 self.data.rep_extra_specs)) 1490 extra_specs = deepcopy(self.data.rep_extra_specs) 1491 extra_specs[utils.REP_MODE] = utils.REP_ASYNC 1492 self.assertTrue(self.utils.does_vol_need_rdf_management_group( 1493 extra_specs)) 1494 1495 def test_modify_snapshot_prefix_manage(self): 1496 snap_name = self.data.snapshot_id 1497 expected_snap_name = self.data.managed_snap_id 1498 1499 updated_name = self.utils.modify_snapshot_prefix( 1500 snap_name, manage=True) 1501 1502 self.assertEqual(expected_snap_name, updated_name) 1503 1504 def test_modify_snapshot_prefix_unmanage(self): 1505 snap_name = self.data.managed_snap_id 1506 expected_snap_name = self.data.snapshot_id 1507 1508 updated_name = self.utils.modify_snapshot_prefix( 1509 snap_name, unmanage=True) 1510 1511 self.assertEqual(expected_snap_name, updated_name) 1512 1513 1514class VMAXRestTest(test.TestCase): 1515 def setUp(self): 1516 self.data = VMAXCommonData() 1517 1518 super(VMAXRestTest, self).setUp() 1519 volume_utils.get_max_over_subscription_ratio = mock.Mock() 1520 config_group = 'RestTests' 1521 fake_xml = FakeXML().create_fake_config_file( 1522 config_group, self.data.port_group_name_f) 1523 configuration = FakeConfiguration(fake_xml, config_group) 1524 rest.VMAXRest._establish_rest_session = mock.Mock( 1525 return_value=FakeRequestsSession()) 1526 driver = fc.VMAXFCDriver(configuration=configuration) 1527 self.driver = driver 1528 self.common = self.driver.common 1529 self.rest = self.common.rest 1530 self.utils = self.common.utils 1531 1532 def test_rest_request_exception(self): 1533 sc, msg = self.rest.request('/fake_url', 'TIMEOUT') 1534 self.assertIsNone(sc) 1535 self.assertIsNone(msg) 1536 self.assertRaises(exception.VolumeBackendAPIException, 1537 self.rest.request, '', 'EXCEPTION') 1538 1539 def test_wait_for_job_complete(self): 1540 rc, job, status, task = self.rest.wait_for_job_complete( 1541 {'status': 'created', 'jobId': '12345'}, self.data.extra_specs) 1542 self.assertEqual(0, rc) 1543 1544 def test_wait_for_job_complete_failed(self): 1545 with mock.patch.object(self.rest, '_is_job_finished', 1546 side_effect=exception.BadHTTPResponseStatus): 1547 self.assertRaises(exception.VolumeBackendAPIException, 1548 self.rest.wait_for_job_complete, 1549 self.data.job_list[0], self.data.extra_specs) 1550 1551 def test_is_job_finished_false(self): 1552 job_id = "55555" 1553 complete, response, rc, status, task = self.rest._is_job_finished( 1554 job_id) 1555 self.assertFalse(complete) 1556 1557 def test_is_job_finished_failed(self): 1558 job_id = "55555" 1559 complete, response, rc, status, task = self.rest._is_job_finished( 1560 job_id) 1561 self.assertFalse(complete) 1562 with mock.patch.object(self.rest, 'request', 1563 return_value=(200, {'status': 'FAILED'})): 1564 complete, response, rc, status, task = ( 1565 self.rest._is_job_finished(job_id)) 1566 self.assertTrue(complete) 1567 self.assertEqual(-1, rc) 1568 1569 def test_check_status_code_success(self): 1570 status_code = 200 1571 self.rest.check_status_code_success( 1572 'test success', status_code, "") 1573 1574 def test_check_status_code_not_success(self): 1575 status_code = 500 1576 self.assertRaises(exception.VolumeBackendAPIException, 1577 self.rest.check_status_code_success, 1578 'test exception', status_code, "") 1579 1580 def test_wait_for_job_success(self): 1581 operation = 'test' 1582 status_code = 202 1583 job = self.data.job_list[0] 1584 extra_specs = self.data.extra_specs 1585 self.rest.wait_for_job( 1586 operation, status_code, job, extra_specs) 1587 1588 def test_wait_for_job_failed(self): 1589 operation = 'test' 1590 status_code = 202 1591 job = self.data.job_list[2] 1592 extra_specs = self.data.extra_specs 1593 with mock.patch.object(self.rest, 'wait_for_job_complete', 1594 return_value=(-1, '', '', '')): 1595 self.assertRaises(exception.VolumeBackendAPIException, 1596 self.rest.wait_for_job, 1597 operation, status_code, job, extra_specs) 1598 1599 def test_get_resource_present(self): 1600 array = self.data.array 1601 category = 'sloprovisioning' 1602 resource_type = 'storagegroup' 1603 resource = self.rest.get_resource(array, category, resource_type) 1604 self.assertEqual(self.data.sg_list, resource) 1605 1606 def test_get_resource_not_present(self): 1607 array = self.data.array 1608 category = 'sloprovisioning' 1609 resource_type = self.data.failed_resource 1610 resource = self.rest.get_resource(array, category, resource_type) 1611 self.assertIsNone(resource) 1612 1613 def test_create_resource_success(self): 1614 array = self.data.array 1615 category = '' 1616 resource_type = '' 1617 payload = {'someKey': 'someValue'} 1618 status_code, message = self.rest.create_resource( 1619 array, category, resource_type, payload) 1620 self.assertEqual(self.data.job_list[0], message) 1621 1622 def test_create_resource_failed(self): 1623 array = self.data.array 1624 category = '' 1625 resource_type = '' 1626 payload = {'someKey': self.data.failed_resource} 1627 self.assertRaises( 1628 exception.VolumeBackendAPIException, 1629 self.rest.create_resource, array, category, 1630 resource_type, payload) 1631 1632 def test_modify_resource(self): 1633 array = self.data.array 1634 category = '' 1635 resource_type = '' 1636 payload = {'someKey': 'someValue'} 1637 status_code, message = self.rest.modify_resource( 1638 array, category, resource_type, payload) 1639 self.assertEqual(self.data.job_list[0], message) 1640 1641 def test_modify_resource_failed(self): 1642 array = self.data.array 1643 category = '' 1644 resource_type = '' 1645 payload = {'someKey': self.data.failed_resource} 1646 self.assertRaises( 1647 exception.VolumeBackendAPIException, 1648 self.rest.modify_resource, array, category, 1649 resource_type, payload) 1650 1651 def test_delete_resource(self): 1652 operation = 'delete res resource' 1653 status_code = 204 1654 message = None 1655 array = self.data.array 1656 category = 'cat' 1657 resource_type = 'res' 1658 resource_name = 'name' 1659 with mock.patch.object(self.rest, 'check_status_code_success'): 1660 self.rest.delete_resource( 1661 array, category, resource_type, resource_name) 1662 self.rest.check_status_code_success.assert_called_with( 1663 operation, status_code, message) 1664 1665 def test_delete_resource_failed(self): 1666 array = self.data.array 1667 category = self.data.failed_resource 1668 resource_type = self.data.failed_resource 1669 resource_name = self.data.failed_resource 1670 self.assertRaises( 1671 exception.VolumeBackendAPIException, 1672 self.rest.modify_resource, array, category, 1673 resource_type, resource_name) 1674 1675 def test_get_array_serial(self): 1676 ref_details = self.data.symmetrix[0] 1677 array_details = self.rest.get_array_serial(self.data.array) 1678 self.assertEqual(ref_details, array_details) 1679 1680 def test_get_array_serial_failed(self): 1681 array_details = self.rest.get_array_serial(self.data.failed_resource) 1682 self.assertIsNone(array_details) 1683 1684 def test_get_uni_version(self): 1685 version, major_version = self.rest.get_uni_version() 1686 self.assertEqual('90', major_version) 1687 with mock.patch.object(self.rest, '_get_request', return_value=None): 1688 version, major_version = self.rest.get_uni_version() 1689 self.assertIsNone(major_version) 1690 1691 def test_get_srp_by_name(self): 1692 ref_details = self.data.srp_details 1693 srp_details = self.rest.get_srp_by_name( 1694 self.data.array, self.data.srp) 1695 self.assertEqual(ref_details, srp_details) 1696 1697 def test_get_slo_list_powermax(self): 1698 ref_settings = self.data.powermax_slo_details['sloId'] 1699 slo_settings = self.rest.get_slo_list(self.data.array) 1700 self.assertEqual(ref_settings, slo_settings) 1701 1702 def test_get_slo_list_vmax(self): 1703 ref_settings = ['Diamond'] 1704 with mock.patch.object(self.rest, 'get_resource', 1705 return_value=self.data.vmax_slo_details): 1706 slo_settings = self.rest.get_slo_list(self.data.array) 1707 self.assertEqual(ref_settings, slo_settings) 1708 1709 def test_get_workload_settings(self): 1710 ref_settings = self.data.workloadtype['workloadId'] 1711 wl_settings = self.rest.get_workload_settings( 1712 self.data.array) 1713 self.assertEqual(ref_settings, wl_settings) 1714 1715 def test_get_workload_settings_next_gen(self): 1716 with mock.patch.object(self.rest, 'is_next_gen_array', 1717 return_value=True): 1718 wl_settings = self.rest.get_workload_settings( 1719 self.data.array_herc) 1720 self.assertEqual(['None'], wl_settings) 1721 1722 def test_get_workload_settings_failed(self): 1723 wl_settings = self.rest.get_workload_settings( 1724 self.data.failed_resource) 1725 self.assertEqual([], wl_settings) 1726 1727 def test_is_compression_capable_true(self): 1728 compr_capable = self.rest.is_compression_capable('000197800128') 1729 self.assertTrue(compr_capable) 1730 1731 def test_is_compression_capable_false(self): 1732 compr_capable = self.rest.is_compression_capable(self.data.array) 1733 self.assertFalse(compr_capable) 1734 with mock.patch.object(self.rest, 'request', return_value=(200, {})): 1735 compr_capable = self.rest.is_compression_capable(self.data.array) 1736 self.assertFalse(compr_capable) 1737 1738 def test_get_storage_group(self): 1739 ref_details = self.data.sg_details[0] 1740 sg_details = self.rest.get_storage_group( 1741 self.data.array, self.data.defaultstoragegroup_name) 1742 self.assertEqual(ref_details, sg_details) 1743 1744 def test_create_storage_group(self): 1745 with mock.patch.object(self.rest, 'create_resource'): 1746 payload = {'someKey': 'someValue'} 1747 self.rest._create_storagegroup(self.data.array, payload) 1748 self.rest.create_resource.assert_called_once_with( 1749 self.data.array, 'sloprovisioning', 'storagegroup', payload) 1750 1751 def test_create_storage_group_success(self): 1752 sg_name = self.rest.create_storage_group( 1753 self.data.array, self.data.storagegroup_name_f, self.data.srp, 1754 self.data.slo, self.data.workload, self.data.extra_specs) 1755 self.assertEqual(self.data.storagegroup_name_f, sg_name) 1756 1757 def test_create_storage_group_next_gen(self): 1758 with mock.patch.object(self.rest, 'is_next_gen_array', 1759 return_value=True): 1760 with mock.patch.object(self.rest, '_create_storagegroup', 1761 return_value=(200, self.data.job_list[0])): 1762 self.rest.create_storage_group( 1763 self.data.array, self.data.storagegroup_name_f, 1764 self.data.srp, self.data.slo, self.data.workload, 1765 self.data.extra_specs) 1766 payload = {"srpId": self.data.srp, 1767 "storageGroupId": self.data.storagegroup_name_f, 1768 "emulation": "FBA", 1769 "sloBasedStorageGroupParam": [ 1770 {"num_of_vols": 0, 1771 "sloId": self.data.slo, 1772 "workloadSelection": 'NONE', 1773 "volumeAttribute": { 1774 "volume_size": "0", 1775 "capacityUnit": "GB"}}]} 1776 self.rest._create_storagegroup.assert_called_once_with( 1777 self.data.array, payload) 1778 1779 def test_create_storage_group_failed(self): 1780 self.assertRaises( 1781 exception.VolumeBackendAPIException, 1782 self.rest.create_storage_group, self.data.array, 1783 self.data.failed_resource, self.data.srp, self.data.slo, 1784 self.data.workload, self.data.extra_specs) 1785 1786 def test_create_storage_group_no_slo(self): 1787 sg_name = self.rest.create_storage_group( 1788 self.data.array, self.data.default_sg_no_slo, self.data.srp, 1789 None, None, self.data.extra_specs) 1790 self.assertEqual(self.data.default_sg_no_slo, sg_name) 1791 1792 def test_create_storage_group_compression_disabled(self): 1793 with mock.patch.object(self.rest, '_create_storagegroup', 1794 return_value=(200, self.data.job_list[0])): 1795 self.rest.create_storage_group( 1796 self.data.array, self.data.default_sg_compr_disabled, 1797 self.data.srp, self.data.slo, self.data.workload, 1798 self.data.extra_specs, True) 1799 payload = {"srpId": self.data.srp, 1800 "storageGroupId": self.data.default_sg_compr_disabled, 1801 "emulation": "FBA", 1802 "sloBasedStorageGroupParam": [ 1803 {"num_of_vols": 0, 1804 "sloId": self.data.slo, 1805 "workloadSelection": self.data.workload, 1806 "volumeAttribute": { 1807 "volume_size": "0", 1808 "capacityUnit": "GB"}, 1809 "noCompression": "true"}]} 1810 self.rest._create_storagegroup.assert_called_once_with( 1811 self.data.array, payload) 1812 1813 def test_modify_storage_group(self): 1814 array = self.data.array 1815 storagegroup = self.data.defaultstoragegroup_name 1816 payload = {'someKey': 'someValue'} 1817 version = self.data.u4v_version 1818 with mock.patch.object(self.rest, 'modify_resource'): 1819 self.rest.modify_storage_group(array, storagegroup, payload) 1820 self.rest.modify_resource.assert_called_once_with( 1821 self.data.array, 'sloprovisioning', 'storagegroup', 1822 payload, version, resource_name=storagegroup) 1823 1824 def test_create_volume_from_sg_success(self): 1825 volume_name = self.data.volume_details[0]['volume_identifier'] 1826 ref_dict = self.data.provider_location 1827 volume_dict = self.rest.create_volume_from_sg( 1828 self.data.array, volume_name, self.data.defaultstoragegroup_name, 1829 self.data.test_volume.size, self.data.extra_specs) 1830 self.assertEqual(ref_dict, volume_dict) 1831 1832 def test_create_volume_from_sg_failed(self): 1833 volume_name = self.data.volume_details[0]['volume_identifier'] 1834 self.assertRaises( 1835 exception.VolumeBackendAPIException, 1836 self.rest.create_volume_from_sg, self.data.array, 1837 volume_name, self.data.failed_resource, 1838 self.data.test_volume.size, self.data.extra_specs) 1839 1840 def test_create_volume_from_sg_cannot_retrieve_device_id(self): 1841 with mock.patch.object(self.rest, 'find_volume_device_id', 1842 return_value=None): 1843 volume_name = self.data.volume_details[0]['volume_identifier'] 1844 self.assertRaises( 1845 exception.VolumeBackendAPIException, 1846 self.rest.create_volume_from_sg, self.data.array, 1847 volume_name, self.data.failed_resource, 1848 self.data.test_volume.size, self.data.extra_specs) 1849 1850 def test_add_vol_to_sg_success(self): 1851 operation = 'Add volume to sg' 1852 status_code = 202 1853 message = self.data.job_list[0] 1854 with mock.patch.object(self.rest, 'wait_for_job'): 1855 device_id = self.data.device_id 1856 self.rest.add_vol_to_sg( 1857 self.data.array, self.data.storagegroup_name_f, device_id, 1858 self.data.extra_specs) 1859 self.rest.wait_for_job.assert_called_with( 1860 operation, status_code, message, self.data.extra_specs) 1861 1862 def test_add_vol_to_sg_failed(self): 1863 device_id = [self.data.device_id] 1864 self.assertRaises( 1865 exception.VolumeBackendAPIException, 1866 self.rest.add_vol_to_sg, self.data.array, 1867 self.data.failed_resource, device_id, 1868 self.data.extra_specs) 1869 1870 def test_remove_vol_from_sg_success(self): 1871 operation = 'Remove vol from sg' 1872 status_code = 202 1873 message = self.data.job_list[0] 1874 with mock.patch.object(self.rest, 'wait_for_job'): 1875 device_id = self.data.device_id 1876 self.rest.remove_vol_from_sg( 1877 self.data.array, self.data.storagegroup_name_f, device_id, 1878 self.data.extra_specs) 1879 self.rest.wait_for_job.assert_called_with( 1880 operation, status_code, message, self.data.extra_specs) 1881 1882 @mock.patch.object(time, 'sleep') 1883 def test_remove_vol_from_sg_failed(self, mock_sleep): 1884 device_id = [self.data.volume_details[0]['volumeId']] 1885 self.assertRaises( 1886 exception.VolumeBackendAPIException, 1887 self.rest.remove_vol_from_sg, self.data.array, 1888 self.data.failed_resource, device_id, 1889 self.data.extra_specs) 1890 1891 def test_get_vmax_default_storage_group(self): 1892 ref_storage_group = self.data.sg_details[0] 1893 ref_sg_name = self.data.defaultstoragegroup_name 1894 storagegroup, storagegroup_name = ( 1895 self.rest.get_vmax_default_storage_group( 1896 self.data.array, self.data.srp, 1897 self.data.slo, self.data.workload)) 1898 self.assertEqual(ref_sg_name, storagegroup_name) 1899 self.assertEqual(ref_storage_group, storagegroup) 1900 1901 def test_get_vmax_default_storage_group_next_gen(self): 1902 with mock.patch.object(self.rest, 'is_next_gen_array', 1903 return_value=True): 1904 __, storagegroup_name = self.rest.get_vmax_default_storage_group( 1905 self.data.array, self.data.srp, 1906 self.data.slo, self.data.workload) 1907 self.assertEqual('OS-SRP_1-Diamond-NONE-SG', storagegroup_name) 1908 1909 def test_delete_storage_group(self): 1910 operation = 'delete storagegroup resource' 1911 status_code = 204 1912 message = None 1913 with mock.patch.object(self.rest, 'check_status_code_success'): 1914 self.rest.delete_storage_group( 1915 self.data.array, self.data.storagegroup_name_f) 1916 self.rest.check_status_code_success.assert_called_with( 1917 operation, status_code, message) 1918 1919 def test_is_child_sg_in_parent_sg(self): 1920 is_child1 = self.rest.is_child_sg_in_parent_sg( 1921 self.data.array, self.data.storagegroup_name_f, 1922 self.data.parent_sg_f) 1923 is_child2 = self.rest.is_child_sg_in_parent_sg( 1924 self.data.array, self.data.defaultstoragegroup_name, 1925 self.data.parent_sg_f) 1926 self.assertTrue(is_child1) 1927 self.assertFalse(is_child2) 1928 1929 def test_add_child_sg_to_parent_sg(self): 1930 payload = {"editStorageGroupActionParam": { 1931 "expandStorageGroupParam": { 1932 "addExistingStorageGroupParam": { 1933 "storageGroupId": [self.data.storagegroup_name_f]}}}} 1934 with mock.patch.object(self.rest, 'modify_storage_group', 1935 return_value=(202, self.data.job_list[0])): 1936 self.rest.add_child_sg_to_parent_sg( 1937 self.data.array, self.data.storagegroup_name_f, 1938 self.data.parent_sg_f, self.data.extra_specs) 1939 self.rest.modify_storage_group.assert_called_once_with( 1940 self.data.array, self.data.parent_sg_f, payload) 1941 1942 def test_remove_child_sg_from_parent_sg(self): 1943 payload = {"editStorageGroupActionParam": { 1944 "removeStorageGroupParam": { 1945 "storageGroupId": [self.data.storagegroup_name_f], 1946 "force": 'true'}}} 1947 with mock.patch.object(self.rest, 'modify_storage_group', 1948 return_value=(202, self.data.job_list[0])): 1949 self.rest.remove_child_sg_from_parent_sg( 1950 self.data.array, self.data.storagegroup_name_f, 1951 self.data.parent_sg_f, self.data.extra_specs) 1952 self.rest.modify_storage_group.assert_called_once_with( 1953 self.data.array, self.data.parent_sg_f, payload) 1954 1955 def test_get_volume_list(self): 1956 ref_volumes = [self.data.device_id, self.data.device_id2] 1957 volumes = self.rest.get_volume_list(self.data.array, {}) 1958 self.assertEqual(ref_volumes, volumes) 1959 1960 def test_get_volume(self): 1961 ref_volumes = self.data.volume_details[0] 1962 device_id = self.data.device_id 1963 volumes = self.rest.get_volume(self.data.array, device_id) 1964 self.assertEqual(ref_volumes, volumes) 1965 1966 def test_get_private_volume(self): 1967 device_id = self.data.device_id 1968 ref_volume = self.data.private_vol_details['resultList']['result'][0] 1969 volume = self.rest._get_private_volume(self.data.array, device_id) 1970 self.assertEqual(ref_volume, volume) 1971 1972 def test_get_private_volume_exception(self): 1973 device_id = self.data.device_id 1974 with mock.patch.object(self.rest, 'get_resource', 1975 return_value={}): 1976 self.assertRaises(exception.VolumeBackendAPIException, 1977 self.rest._get_private_volume, 1978 self.data.array, device_id) 1979 1980 def test_modify_volume_success(self): 1981 array = self.data.array 1982 device_id = self.data.device_id 1983 payload = {'someKey': 'someValue'} 1984 with mock.patch.object(self.rest, 'modify_resource'): 1985 self.rest._modify_volume(array, device_id, payload) 1986 self.rest.modify_resource.assert_called_once_with( 1987 self.data.array, 'sloprovisioning', 'volume', 1988 payload, resource_name=device_id) 1989 1990 def test_modify_volume_failed(self): 1991 payload = {'someKey': self.data.failed_resource} 1992 device_id = self.data.device_id 1993 self.assertRaises( 1994 exception.VolumeBackendAPIException, 1995 self.rest._modify_volume, self.data.array, 1996 device_id, payload) 1997 1998 def test_extend_volume(self): 1999 device_id = self.data.device_id 2000 new_size = '3' 2001 extend_vol_payload = {"executionOption": "ASYNCHRONOUS", 2002 "editVolumeActionParam": { 2003 "expandVolumeParam": { 2004 "volumeAttribute": { 2005 "volume_size": new_size, 2006 "capacityUnit": "GB"}}}} 2007 with mock.patch.object(self.rest, '_modify_volume', 2008 return_value=(202, self.data.job_list[0])): 2009 self.rest.extend_volume(self.data.array, device_id, new_size, 2010 self.data.extra_specs) 2011 self.rest._modify_volume.assert_called_once_with( 2012 self.data.array, device_id, extend_vol_payload) 2013 2014 def test_delete_volume(self): 2015 device_id = self.data.device_id 2016 with mock.patch.object(self.rest, 'delete_resource'),\ 2017 mock.patch.object( 2018 self.rest, '_modify_volume', side_effect=[ 2019 None, None, None, exception.VolumeBackendAPIException]): 2020 for x in range(0, 2): 2021 self.rest.delete_volume(self.data.array, device_id) 2022 mod_call_count = self.rest._modify_volume.call_count 2023 self.assertEqual(4, mod_call_count) 2024 self.rest.delete_resource.assert_called_once_with( 2025 self.data.array, 'sloprovisioning', 'volume', device_id) 2026 2027 def test_rename_volume(self): 2028 device_id = self.data.device_id 2029 payload = {"editVolumeActionParam": { 2030 "modifyVolumeIdentifierParam": { 2031 "volumeIdentifier": { 2032 "identifier_name": 'new_name', 2033 "volumeIdentifierChoice": "identifier_name"}}}} 2034 payload2 = {"editVolumeActionParam": {"modifyVolumeIdentifierParam": { 2035 "volumeIdentifier": {"volumeIdentifierChoice": "none"}}}} 2036 with mock.patch.object(self.rest, '_modify_volume') as mock_mod: 2037 self.rest.rename_volume(self.data.array, device_id, 'new_name') 2038 mock_mod.assert_called_once_with( 2039 self.data.array, device_id, payload) 2040 mock_mod.reset_mock() 2041 self.rest.rename_volume(self.data.array, device_id, None) 2042 self.rest._modify_volume.assert_called_once_with( 2043 self.data.array, device_id, payload2) 2044 2045 def test_check_volume_device_id(self): 2046 element_name = self.utils.get_volume_element_name( 2047 self.data.test_volume.id) 2048 found_dev_id = self.rest.check_volume_device_id( 2049 self.data.array, self.data.device_id, element_name) 2050 self.assertEqual(self.data.device_id, found_dev_id) 2051 found_dev_id2 = self.rest.check_volume_device_id( 2052 self.data.array, self.data.device_id3, element_name) 2053 self.assertIsNone(found_dev_id2) 2054 2055 def test_check_volume_device_id_host_migration_case(self): 2056 element_name = self.utils.get_volume_element_name( 2057 self.data.test_clone_volume.id) 2058 found_dev_id = self.rest.check_volume_device_id( 2059 self.data.array, self.data.device_id, element_name, 2060 name_id=self.data.test_clone_volume._name_id) 2061 self.assertEqual(self.data.device_id, found_dev_id) 2062 2063 def test_find_mv_connections_for_vol(self): 2064 device_id = self.data.device_id 2065 ref_lun_id = int((self.data.maskingview[0]['maskingViewConnection'] 2066 [0]['host_lun_address']), 16) 2067 host_lun_id = self.rest.find_mv_connections_for_vol( 2068 self.data.array, self.data.masking_view_name_f, device_id) 2069 self.assertEqual(ref_lun_id, host_lun_id) 2070 2071 def test_find_mv_connections_for_vol_failed(self): 2072 # no masking view info retrieved 2073 device_id = self.data.volume_details[0]['volumeId'] 2074 host_lun_id = self.rest.find_mv_connections_for_vol( 2075 self.data.array, self.data.failed_resource, device_id) 2076 self.assertIsNone(host_lun_id) 2077 # no connection info received 2078 with mock.patch.object(self.rest, 'get_resource', 2079 return_value={'no_conn': 'no_info'}): 2080 host_lun_id2 = self.rest.find_mv_connections_for_vol( 2081 self.data.array, self.data.masking_view_name_f, device_id) 2082 self.assertIsNone(host_lun_id2) 2083 2084 def test_get_storage_groups_from_volume(self): 2085 array = self.data.array 2086 device_id = self.data.device_id 2087 ref_list = self.data.volume_details[0]['storageGroupId'] 2088 sg_list = self.rest.get_storage_groups_from_volume(array, device_id) 2089 self.assertEqual(ref_list, sg_list) 2090 2091 def test_get_num_vols_in_sg(self): 2092 num_vol = self.rest.get_num_vols_in_sg( 2093 self.data.array, self.data.defaultstoragegroup_name) 2094 self.assertEqual(2, num_vol) 2095 2096 def test_get_num_vols_in_sg_no_num(self): 2097 with mock.patch.object(self.rest, 'get_storage_group', 2098 return_value={}): 2099 num_vol = self.rest.get_num_vols_in_sg( 2100 self.data.array, self.data.defaultstoragegroup_name) 2101 self.assertEqual(0, num_vol) 2102 2103 def test_is_volume_in_storagegroup(self): 2104 # True 2105 array = self.data.array 2106 device_id = self.data.device_id 2107 storagegroup = self.data.defaultstoragegroup_name 2108 is_vol1 = self.rest.is_volume_in_storagegroup( 2109 array, device_id, storagegroup) 2110 # False 2111 with mock.patch.object(self.rest, 'get_storage_groups_from_volume', 2112 return_value=[]): 2113 is_vol2 = self.rest.is_volume_in_storagegroup( 2114 array, device_id, storagegroup) 2115 self.assertTrue(is_vol1) 2116 self.assertFalse(is_vol2) 2117 2118 def test_find_volume_device_number(self): 2119 array = self.data.array 2120 volume_name = self.data.volume_details[0]['volume_identifier'] 2121 ref_device = self.data.device_id 2122 device_number = self.rest.find_volume_device_id(array, volume_name) 2123 self.assertEqual(ref_device, device_number) 2124 2125 def test_find_volume_device_number_failed(self): 2126 array = self.data.array 2127 with mock.patch.object(self.rest, 'get_volume_list', 2128 return_value=[]): 2129 device_number = self.rest.find_volume_device_id( 2130 array, 'name') 2131 self.assertIsNone(device_number) 2132 2133 def test_get_volume_success(self): 2134 array = self.data.array 2135 device_id = self.data.device_id 2136 ref_volume = self.data.volume_details[0] 2137 volume = self.rest.get_volume(array, device_id) 2138 self.assertEqual(ref_volume, volume) 2139 2140 def test_get_volume_failed(self): 2141 array = self.data.array 2142 device_id = self.data.failed_resource 2143 self.assertRaises(exception.VolumeBackendAPIException, 2144 self.rest.get_volume, 2145 array, device_id) 2146 2147 def test_find_volume_identifier(self): 2148 array = self.data.array 2149 device_id = self.data.device_id 2150 ref_name = self.data.volume_details[0]['volume_identifier'] 2151 vol_name = self.rest.find_volume_identifier(array, device_id) 2152 self.assertEqual(ref_name, vol_name) 2153 2154 def test_get_volume_size(self): 2155 array = self.data.array 2156 device_id = self.data.device_id 2157 ref_size = self.data.test_volume.size 2158 size = self.rest.get_size_of_device_on_array(array, device_id) 2159 self.assertEqual(ref_size, size) 2160 2161 def test_get_volume_size_exception(self): 2162 array = self.data.array 2163 device_id = self.data.device_id 2164 with mock.patch.object(self.rest, 'get_volume', 2165 return_value=None): 2166 size = self.rest.get_size_of_device_on_array( 2167 array, device_id) 2168 self.assertIsNone(size) 2169 2170 def test_get_portgroup(self): 2171 array = self.data.array 2172 pg_name = self.data.port_group_name_f 2173 ref_pg = self.data.portgroup[0] 2174 portgroup = self.rest.get_portgroup(array, pg_name) 2175 self.assertEqual(ref_pg, portgroup) 2176 2177 def test_get_port_ids(self): 2178 array = self.data.array 2179 pg_name = self.data.port_group_name_f 2180 ref_ports = ["FA-1D:4"] 2181 port_ids = self.rest.get_port_ids(array, pg_name) 2182 self.assertEqual(ref_ports, port_ids) 2183 2184 def test_get_port_ids_no_portgroup(self): 2185 array = self.data.array 2186 pg_name = self.data.port_group_name_f 2187 with mock.patch.object(self.rest, 'get_portgroup', 2188 return_value=None): 2189 port_ids = self.rest.get_port_ids(array, pg_name) 2190 self.assertEqual([], port_ids) 2191 2192 def test_get_port(self): 2193 array = self.data.array 2194 port_id = "FA-1D:4" 2195 ref_port = self.data.port_list[0] 2196 port = self.rest.get_port(array, port_id) 2197 self.assertEqual(ref_port, port) 2198 2199 def test_get_iscsi_ip_address_and_iqn(self): 2200 array = self.data.array 2201 port_id = "SE-4E:0" 2202 ref_ip = [self.data.ip] 2203 ref_iqn = self.data.initiator 2204 ip_addresses, iqn = self.rest.get_iscsi_ip_address_and_iqn( 2205 array, port_id) 2206 self.assertEqual(ref_ip, ip_addresses) 2207 self.assertEqual(ref_iqn, iqn) 2208 2209 def test_get_iscsi_ip_address_and_iqn_no_port(self): 2210 array = self.data.array 2211 port_id = "SE-4E:0" 2212 with mock.patch.object(self.rest, 'get_port', return_value=None): 2213 ip_addresses, iqn = self.rest.get_iscsi_ip_address_and_iqn( 2214 array, port_id) 2215 self.assertIsNone(ip_addresses) 2216 self.assertIsNone(iqn) 2217 2218 def test_get_target_wwns(self): 2219 array = self.data.array 2220 pg_name = self.data.port_group_name_f 2221 ref_wwns = [self.data.wwnn1] 2222 target_wwns = self.rest.get_target_wwns(array, pg_name) 2223 self.assertEqual(ref_wwns, target_wwns) 2224 2225 def test_get_target_wwns_failed(self): 2226 array = self.data.array 2227 pg_name = self.data.port_group_name_f 2228 with mock.patch.object(self.rest, 'get_port', 2229 return_value=None): 2230 target_wwns = self.rest.get_target_wwns(array, pg_name) 2231 self.assertEqual([], target_wwns) 2232 2233 def test_get_initiator_group(self): 2234 array = self.data.array 2235 ig_name = self.data.initiatorgroup_name_f 2236 ref_ig = self.data.inititiatorgroup[0] 2237 response_ig = self.rest.get_initiator_group(array, ig_name) 2238 self.assertEqual(ref_ig, response_ig) 2239 2240 def test_get_initiator(self): 2241 array = self.data.array 2242 initiator_name = self.data.initiator 2243 ref_initiator = self.data.initiator_list[1] 2244 response_initiator = self.rest.get_initiator(array, initiator_name) 2245 self.assertEqual(ref_initiator, response_initiator) 2246 2247 def test_get_initiator_list(self): 2248 array = self.data.array 2249 with mock.patch.object(self.rest, 'get_resource', 2250 return_value={'initiatorId': '1234'}): 2251 init_list = self.rest.get_initiator_list(array) 2252 self.assertIsNotNone(init_list) 2253 2254 def test_get_initiator_list_none(self): 2255 array = self.data.array 2256 with mock.patch.object(self.rest, 'get_resource', return_value={}): 2257 init_list = self.rest.get_initiator_list(array) 2258 self.assertEqual([], init_list) 2259 2260 def test_get_initiator_group_from_initiator(self): 2261 initiator = self.data.wwpn1 2262 ref_group = self.data.initiatorgroup_name_f 2263 init_group = self.rest.get_initiator_group_from_initiator( 2264 self.data.array, initiator) 2265 self.assertEqual(ref_group, init_group) 2266 2267 def test_get_initiator_group_from_initiator_failed(self): 2268 initiator = self.data.wwpn1 2269 with mock.patch.object(self.rest, 'get_initiator', 2270 return_value=None): 2271 init_group = self.rest.get_initiator_group_from_initiator( 2272 self.data.array, initiator) 2273 self.assertIsNone(init_group) 2274 with mock.patch.object(self.rest, 'get_initiator', 2275 return_value={'name': 'no_host'}): 2276 init_group = self.rest.get_initiator_group_from_initiator( 2277 self.data.array, initiator) 2278 self.assertIsNone(init_group) 2279 2280 def test_create_initiator_group(self): 2281 init_group_name = self.data.initiatorgroup_name_f 2282 init_list = [self.data.wwpn1] 2283 extra_specs = self.data.extra_specs 2284 with mock.patch.object(self.rest, 'create_resource', 2285 return_value=(202, self.data.job_list[0])): 2286 payload = ({"executionOption": "ASYNCHRONOUS", 2287 "hostId": init_group_name, "initiatorId": init_list}) 2288 self.rest.create_initiator_group( 2289 self.data.array, init_group_name, init_list, extra_specs) 2290 self.rest.create_resource.assert_called_once_with( 2291 self.data.array, 'sloprovisioning', 'host', payload) 2292 2293 def test_delete_initiator_group(self): 2294 with mock.patch.object(self.rest, 'delete_resource'): 2295 self.rest.delete_initiator_group( 2296 self.data.array, self.data.initiatorgroup_name_f) 2297 self.rest.delete_resource.assert_called_once_with( 2298 self.data.array, 'sloprovisioning', 'host', 2299 self.data.initiatorgroup_name_f) 2300 2301 def test_get_masking_view(self): 2302 array = self.data.array 2303 masking_view_name = self.data.masking_view_name_f 2304 ref_mask_view = self.data.maskingview[0] 2305 masking_view = self.rest.get_masking_view(array, masking_view_name) 2306 self.assertEqual(ref_mask_view, masking_view) 2307 2308 def test_get_masking_views_from_storage_group(self): 2309 array = self.data.array 2310 storagegroup_name = self.data.storagegroup_name_f 2311 ref_mask_view = [self.data.masking_view_name_f] 2312 masking_view = self.rest.get_masking_views_from_storage_group( 2313 array, storagegroup_name) 2314 self.assertEqual(ref_mask_view, masking_view) 2315 2316 def test_get_masking_views_by_initiator_group(self): 2317 array = self.data.array 2318 initiatorgroup_name = self.data.initiatorgroup_name_f 2319 ref_mask_view = [self.data.masking_view_name_f] 2320 masking_view = self.rest.get_masking_views_by_initiator_group( 2321 array, initiatorgroup_name) 2322 self.assertEqual(ref_mask_view, masking_view) 2323 2324 def test_get_masking_views_by_initiator_group_failed(self): 2325 array = self.data.array 2326 initiatorgroup_name = self.data.initiatorgroup_name_f 2327 with mock.patch.object(self.rest, 'get_initiator_group', 2328 return_value=None): 2329 masking_view = self.rest.get_masking_views_by_initiator_group( 2330 array, initiatorgroup_name) 2331 self.assertEqual([], masking_view) 2332 with mock.patch.object(self.rest, 'get_initiator_group', 2333 return_value={'name': 'no_mv'}): 2334 masking_view = self.rest.get_masking_views_by_initiator_group( 2335 array, initiatorgroup_name) 2336 self.assertEqual([], masking_view) 2337 2338 def test_get_element_from_masking_view(self): 2339 array = self.data.array 2340 maskingview_name = self.data.masking_view_name_f 2341 # storage group 2342 ref_sg = self.data.storagegroup_name_f 2343 storagegroup = self.rest.get_element_from_masking_view( 2344 array, maskingview_name, storagegroup=True) 2345 self.assertEqual(ref_sg, storagegroup) 2346 # initiator group 2347 ref_ig = self.data.initiatorgroup_name_f 2348 initiatorgroup = self.rest.get_element_from_masking_view( 2349 array, maskingview_name, host=True) 2350 self.assertEqual(ref_ig, initiatorgroup) 2351 # portgroup 2352 ref_pg = self.data.port_group_name_f 2353 portgroup = self.rest.get_element_from_masking_view( 2354 array, maskingview_name, portgroup=True) 2355 self.assertEqual(ref_pg, portgroup) 2356 2357 def test_get_element_from_masking_view_failed(self): 2358 array = self.data.array 2359 maskingview_name = self.data.masking_view_name_f 2360 # no element chosen 2361 element = self.rest.get_element_from_masking_view( 2362 array, maskingview_name) 2363 self.assertIsNone(element) 2364 # cannot retrieve maskingview 2365 with mock.patch.object(self.rest, 'get_masking_view', 2366 return_value=None): 2367 self.assertRaises(exception.VolumeBackendAPIException, 2368 self.rest.get_element_from_masking_view, 2369 array, maskingview_name) 2370 2371 def test_get_common_masking_views(self): 2372 array = self.data.array 2373 initiatorgroup = self.data.initiatorgroup_name_f 2374 portgroup = self.data.port_group_name_f 2375 ref_maskingview = self.data.masking_view_name_f 2376 maskingview_list = self.rest.get_common_masking_views( 2377 array, portgroup, initiatorgroup) 2378 self.assertEqual(ref_maskingview, maskingview_list) 2379 2380 def test_get_common_masking_views_none(self): 2381 array = self.data.array 2382 initiatorgroup = self.data.initiatorgroup_name_f 2383 portgroup = self.data.port_group_name_f 2384 with mock.patch.object(self.rest, 'get_masking_view_list', 2385 return_value=[]): 2386 maskingview_list = self.rest.get_common_masking_views( 2387 array, portgroup, initiatorgroup) 2388 self.assertEqual([], maskingview_list) 2389 2390 def test_create_masking_view(self): 2391 maskingview_name = self.data.masking_view_name_f 2392 storagegroup_name = self.data.storagegroup_name_f 2393 port_group_name = self.data.port_group_name_f 2394 init_group_name = self.data.initiatorgroup_name_f 2395 extra_specs = self.data.extra_specs 2396 with mock.patch.object(self.rest, 'create_resource', 2397 return_value=(202, self.data.job_list[0])): 2398 payload = ({"executionOption": "ASYNCHRONOUS", 2399 "portGroupSelection": { 2400 "useExistingPortGroupParam": { 2401 "portGroupId": port_group_name}}, 2402 "maskingViewId": maskingview_name, 2403 "hostOrHostGroupSelection": { 2404 "useExistingHostParam": { 2405 "hostId": init_group_name}}, 2406 "storageGroupSelection": { 2407 "useExistingStorageGroupParam": { 2408 "storageGroupId": storagegroup_name}}}) 2409 self.rest.create_masking_view( 2410 self.data.array, maskingview_name, storagegroup_name, 2411 port_group_name, init_group_name, extra_specs) 2412 self.rest.create_resource.assert_called_once_with( 2413 self.data.array, 'sloprovisioning', 'maskingview', payload) 2414 2415 def test_delete_masking_view(self): 2416 with mock.patch.object(self.rest, 'delete_resource'): 2417 self.rest.delete_masking_view( 2418 self.data.array, self.data.masking_view_name_f) 2419 self.rest.delete_resource.assert_called_once_with( 2420 self.data.array, 'sloprovisioning', 'maskingview', 2421 self.data.masking_view_name_f) 2422 2423 def test_get_replication_capabilities(self): 2424 ref_response = self.data.capabilities['symmetrixCapability'][1] 2425 capabilities = self.rest.get_replication_capabilities(self.data.array) 2426 self.assertEqual(ref_response, capabilities) 2427 2428 def test_is_clone_licenced(self): 2429 licence = self.rest.is_snapvx_licensed(self.data.array) 2430 self.assertTrue(licence) 2431 false_response = {'rdfCapable': True, 2432 'snapVxCapable': False, 2433 'symmetrixId': '000197800123'} 2434 with mock.patch.object(self.rest, 'get_replication_capabilities', 2435 return_value=false_response): 2436 licence2 = self.rest.is_snapvx_licensed(self.data.array) 2437 self.assertFalse(licence2) 2438 2439 def test_is_clone_licenced_error(self): 2440 with mock.patch.object(self.rest, 'get_replication_capabilities', 2441 return_value=None): 2442 licence3 = self.rest.is_snapvx_licensed(self.data.array) 2443 self.assertFalse(licence3) 2444 2445 def test_create_volume_snap(self): 2446 snap_name = (self.data.volume_snap_vx 2447 ['snapshotSrcs'][0]['snapshotName']) 2448 device_id = self.data.device_id 2449 extra_specs = self.data.extra_specs 2450 payload = {"deviceNameListSource": [{"name": device_id}], 2451 "bothSides": 'false', "star": 'false', 2452 "force": 'false'} 2453 resource_type = 'snapshot/%(snap)s' % {'snap': snap_name} 2454 with mock.patch.object(self.rest, 'create_resource', 2455 return_value=(202, self.data.job_list[0])): 2456 self.rest.create_volume_snap( 2457 self.data.array, snap_name, device_id, extra_specs) 2458 self.rest.create_resource.assert_called_once_with( 2459 self.data.array, 'replication', resource_type, 2460 payload, private='/private') 2461 2462 def test_modify_volume_snap(self): 2463 array = self.data.array 2464 source_id = self.data.device_id 2465 target_id = (self.data.volume_snap_vx 2466 ['snapshotSrcs'][0]['linkedDevices'][0]['targetDevice']) 2467 snap_name = (self.data.volume_snap_vx 2468 ['snapshotSrcs'][0]['snapshotName']) 2469 extra_specs = self.data.extra_specs 2470 payload = {"deviceNameListSource": [{"name": source_id}], 2471 "deviceNameListTarget": [ 2472 {"name": target_id}], 2473 "copy": 'true', "action": "", 2474 "star": 'false', "force": 'false', 2475 "exact": 'false', "remote": 'false', 2476 "symforce": 'false', "nocopy": 'false'} 2477 payload_restore = {"deviceNameListSource": [{"name": source_id}], 2478 "deviceNameListTarget": [{"name": source_id}], 2479 "action": "Restore", 2480 "star": 'false', "force": 'false'} 2481 with mock.patch.object( 2482 self.rest, 'modify_resource', return_value=( 2483 202, self.data.job_list[0])) as mock_modify: 2484 # link 2485 payload["action"] = "Link" 2486 self.rest.modify_volume_snap( 2487 array, source_id, target_id, snap_name, extra_specs, link=True) 2488 self.rest.modify_resource.assert_called_once_with( 2489 array, 'replication', 'snapshot', payload, 2490 resource_name=snap_name, private='/private') 2491 # unlink 2492 mock_modify.reset_mock() 2493 payload["action"] = "Unlink" 2494 self.rest.modify_volume_snap( 2495 array, source_id, target_id, snap_name, 2496 extra_specs, unlink=True) 2497 self.rest.modify_resource.assert_called_once_with( 2498 array, 'replication', 'snapshot', payload, 2499 resource_name=snap_name, private='/private') 2500 # restore 2501 mock_modify.reset_mock() 2502 payload["action"] = "Restore" 2503 self.rest.modify_volume_snap( 2504 array, source_id, "", snap_name, 2505 extra_specs, unlink=False, restore=True) 2506 self.rest.modify_resource.assert_called_once_with( 2507 array, 'replication', 'snapshot', payload_restore, 2508 resource_name=snap_name, private='/private') 2509 # link or unlink, list of volumes 2510 mock_modify.reset_mock() 2511 payload["action"] = "Link" 2512 self.rest.modify_volume_snap( 2513 array, "", "", snap_name, 2514 extra_specs, unlink=False, link=True, 2515 list_volume_pairs=[(source_id, target_id)]) 2516 self.rest.modify_resource.assert_called_once_with( 2517 array, 'replication', 'snapshot', payload, 2518 resource_name=snap_name, private='/private') 2519 # none selected 2520 mock_modify.reset_mock() 2521 self.rest.modify_volume_snap( 2522 array, source_id, target_id, snap_name, 2523 extra_specs) 2524 self.rest.modify_resource.assert_not_called() 2525 2526 def test_delete_volume_snap(self): 2527 array = self.data.array 2528 snap_name = (self.data.volume_snap_vx 2529 ['snapshotSrcs'][0]['snapshotName']) 2530 source_device_id = self.data.device_id 2531 payload = {"deviceNameListSource": [{"name": source_device_id}]} 2532 with mock.patch.object(self.rest, 'delete_resource'): 2533 self.rest.delete_volume_snap(array, snap_name, source_device_id) 2534 self.rest.delete_resource.assert_called_once_with( 2535 array, 'replication', 'snapshot', snap_name, 2536 payload=payload, private='/private') 2537 2538 def test_delete_volume_snap_restore(self): 2539 array = self.data.array 2540 snap_name = (self.data.volume_snap_vx 2541 ['snapshotSrcs'][0]['snapshotName']) 2542 source_device_id = self.data.device_id 2543 payload = {"deviceNameListSource": [{"name": source_device_id}], 2544 "restore": True} 2545 with mock.patch.object(self.rest, 'delete_resource'): 2546 self.rest.delete_volume_snap( 2547 array, snap_name, source_device_id, restored=True) 2548 self.rest.delete_resource.assert_called_once_with( 2549 array, 'replication', 'snapshot', snap_name, 2550 payload=payload, private='/private') 2551 2552 def test_get_volume_snap_info(self): 2553 array = self.data.array 2554 source_device_id = self.data.device_id 2555 ref_snap_info = self.data.volume_snap_vx 2556 snap_info = self.rest.get_volume_snap_info(array, source_device_id) 2557 self.assertEqual(ref_snap_info, snap_info) 2558 2559 def test_get_volume_snap(self): 2560 array = self.data.array 2561 snap_name = (self.data.volume_snap_vx 2562 ['snapshotSrcs'][0]['snapshotName']) 2563 device_id = self.data.device_id 2564 ref_snap = self.data.volume_snap_vx['snapshotSrcs'][0] 2565 snap = self.rest.get_volume_snap(array, device_id, snap_name) 2566 self.assertEqual(ref_snap, snap) 2567 2568 def test_get_volume_snap_none(self): 2569 array = self.data.array 2570 snap_name = (self.data.volume_snap_vx 2571 ['snapshotSrcs'][0]['snapshotName']) 2572 device_id = self.data.device_id 2573 with mock.patch.object(self.rest, 'get_volume_snap_info', 2574 return_value=None): 2575 snap = self.rest.get_volume_snap(array, device_id, snap_name) 2576 self.assertIsNone(snap) 2577 with mock.patch.object(self.rest, 'get_volume_snap_info', 2578 return_value={'snapshotSrcs': []}): 2579 snap = self.rest.get_volume_snap(array, device_id, snap_name) 2580 self.assertIsNone(snap) 2581 2582 def test_get_sync_session(self): 2583 array = self.data.array 2584 source_id = self.data.device_id 2585 target_id = (self.data.volume_snap_vx 2586 ['snapshotSrcs'][0]['linkedDevices'][0]['targetDevice']) 2587 snap_name = (self.data.volume_snap_vx 2588 ['snapshotSrcs'][0]['snapshotName']) 2589 ref_sync = (self.data.volume_snap_vx 2590 ['snapshotSrcs'][0]['linkedDevices'][0]) 2591 sync = self.rest.get_sync_session( 2592 array, source_id, snap_name, target_id) 2593 self.assertEqual(ref_sync, sync) 2594 2595 def test_find_snap_vx_sessions(self): 2596 array = self.data.array 2597 source_id = self.data.device_id 2598 ref_sessions = [{'snap_name': 'temp-1', 2599 'source_vol': self.data.device_id, 2600 'target_vol_list': [self.data.device_id2]}, 2601 {'snap_name': 'temp-1', 2602 'source_vol': self.data.device_id, 2603 'target_vol_list': [self.data.device_id2]}] 2604 sessions = self.rest.find_snap_vx_sessions(array, source_id) 2605 self.assertEqual(ref_sessions, sessions) 2606 2607 def test_find_snap_vx_sessions_tgt_only(self): 2608 array = self.data.array 2609 source_id = self.data.device_id 2610 ref_sessions = [{'snap_name': 'temp-1', 2611 'source_vol': self.data.device_id, 2612 'target_vol_list': [self.data.device_id2]}] 2613 sessions = self.rest.find_snap_vx_sessions( 2614 array, source_id, tgt_only=True) 2615 self.assertEqual(ref_sessions, sessions) 2616 2617 def test_update_storagegroup_qos(self): 2618 sg_qos = {"srp": self.data.srp, "num_of_vols": 2, "cap_gb": 2, 2619 "storageGroupId": "OS-QOS-SG", 2620 "slo": self.data.slo, "workload": self.data.workload, 2621 "hostIOLimit": {"host_io_limit_io_sec": "4000", 2622 "dynamicDistribution": "Always", 2623 "host_io_limit_mb_sec": "4000"}} 2624 self.data.sg_details.append(sg_qos) 2625 array = self.data.array 2626 extra_specs = self.data.extra_specs 2627 extra_specs['qos'] = { 2628 'total_iops_sec': '4000', 'DistributionType': 'Always'} 2629 return_value = self.rest.update_storagegroup_qos( 2630 array, "OS-QOS-SG", extra_specs) 2631 self.assertEqual(False, return_value) 2632 extra_specs['qos'] = { 2633 'DistributionType': 'onFailure', 'total_bytes_sec': '419430400'} 2634 return_value = self.rest.update_storagegroup_qos( 2635 array, "OS-QOS-SG", extra_specs) 2636 self.assertTrue(return_value) 2637 2638 def test_update_storagegroup_qos_exception(self): 2639 array = self.data.array 2640 storage_group = self.data.defaultstoragegroup_name 2641 extra_specs = self.data.extra_specs 2642 extra_specs['qos'] = { 2643 'total_iops_sec': '4000', 'DistributionType': 'Wrong', 2644 'total_bytes_sec': '4194304000'} 2645 with mock.patch.object(self.rest, 'check_status_code_success', 2646 side_effect=[None, None, None, Exception]): 2647 self.assertRaises(exception.VolumeBackendAPIException, 2648 self.rest.update_storagegroup_qos, array, 2649 storage_group, extra_specs) 2650 extra_specs['qos']['DistributionType'] = 'Always' 2651 return_value = self.rest.update_storagegroup_qos( 2652 array, "OS-QOS-SG", extra_specs) 2653 self.assertFalse(return_value) 2654 2655 def test_validate_qos_input_exception(self): 2656 qos_extra_spec = { 2657 'total_iops_sec': 90, 'DistributionType': 'Wrong', 2658 'total_bytes_sec': 100} 2659 input_key = 'total_iops_sec' 2660 sg_value = 4000 2661 self.assertRaises(exception.VolumeBackendAPIException, 2662 self.rest.validate_qos_input, input_key, sg_value, 2663 qos_extra_spec, {}) 2664 input_key = 'total_bytes_sec' 2665 sg_value = 4000 2666 self.assertRaises(exception.VolumeBackendAPIException, 2667 self.rest.validate_qos_input, input_key, sg_value, 2668 qos_extra_spec, {}) 2669 2670 def test_validate_qos_distribution_type(self): 2671 qos_extra_spec = { 2672 'total_iops_sec': 4000, 'DistributionType': 'Always', 2673 'total_bytes_sec': 4194304000} 2674 input_prop_dict = {'total_iops_sec': 4000} 2675 sg_value = 'Always' 2676 ret_prop_dict = self.rest.validate_qos_distribution_type( 2677 sg_value, qos_extra_spec, input_prop_dict) 2678 self.assertEqual(input_prop_dict, ret_prop_dict) 2679 2680 @mock.patch.object(rest.VMAXRest, 'modify_storage_group', 2681 return_value=(202, VMAXCommonData.job_list[0])) 2682 def test_set_storagegroup_srp(self, mock_mod): 2683 self.rest.set_storagegroup_srp( 2684 self.data.array, self.data.test_vol_grp_name, 2685 self.data.srp2, self.data.extra_specs) 2686 mock_mod.assert_called_once() 2687 2688 def test_get_rdf_group(self): 2689 with mock.patch.object(self.rest, 'get_resource') as mock_get: 2690 self.rest.get_rdf_group(self.data.array, self.data.rdf_group_no) 2691 mock_get.assert_called_once_with( 2692 self.data.array, 'replication', 'rdf_group', 2693 self.data.rdf_group_no) 2694 2695 def test_get_rdf_group_list(self): 2696 rdf_list = self.rest.get_rdf_group_list(self.data.array) 2697 self.assertEqual(self.data.rdf_group_list, rdf_list) 2698 2699 def test_get_rdf_group_volume(self): 2700 vol_details = self.data.private_vol_details['resultList']['result'][0] 2701 with mock.patch.object( 2702 self.rest, '_get_private_volume', return_value=vol_details 2703 ) as mock_get: 2704 self.rest.get_rdf_group_volume( 2705 self.data.array, self.data.device_id) 2706 mock_get.assert_called_once_with( 2707 self.data.array, self.data.device_id) 2708 2709 def test_are_vols_rdf_paired(self): 2710 are_vols1, local_state, pair_state = self.rest.are_vols_rdf_paired( 2711 self.data.array, self.data.remote_array, self.data.device_id, 2712 self.data.device_id2) 2713 self.assertTrue(are_vols1) 2714 are_vols2, local_state, pair_state = self.rest.are_vols_rdf_paired( 2715 self.data.array, "00012345", self.data.device_id, 2716 self.data.device_id2) 2717 self.assertFalse(are_vols2) 2718 with mock.patch.object(self.rest, "get_rdf_group_volume", 2719 return_value=None): 2720 are_vols3, local, pair = self.rest.are_vols_rdf_paired( 2721 self.data.array, self.data.remote_array, self.data.device_id, 2722 self.data.device_id2) 2723 self.assertFalse(are_vols3) 2724 2725 def test_get_rdf_group_number(self): 2726 rdfg_num = self.rest.get_rdf_group_number( 2727 self.data.array, self.data.rdf_group_name) 2728 self.assertEqual(self.data.rdf_group_no, rdfg_num) 2729 with mock.patch.object(self.rest, 'get_rdf_group_list', 2730 return_value=None): 2731 rdfg_num2 = self.rest.get_rdf_group_number( 2732 self.data.array, self.data.rdf_group_name) 2733 self.assertIsNone(rdfg_num2) 2734 with mock.patch.object(self.rest, 'get_rdf_group', 2735 return_value=None): 2736 rdfg_num3 = self.rest.get_rdf_group_number( 2737 self.data.array, self.data.rdf_group_name) 2738 self.assertIsNone(rdfg_num3) 2739 2740 def test_create_rdf_device_pair(self): 2741 ref_dict = {'array': self.data.remote_array, 2742 'device_id': self.data.device_id2} 2743 extra_specs = deepcopy(self.data.extra_specs) 2744 extra_specs[utils.REP_MODE] = utils.REP_SYNC 2745 rdf_dict = self.rest.create_rdf_device_pair( 2746 self.data.array, self.data.device_id, self.data.rdf_group_no, 2747 self.data.device_id2, self.data.remote_array, extra_specs) 2748 self.assertEqual(ref_dict, rdf_dict) 2749 2750 def test_create_rdf_device_pair_async(self): 2751 ref_dict = {'array': self.data.remote_array, 2752 'device_id': self.data.device_id2} 2753 extra_specs = deepcopy(self.data.extra_specs) 2754 extra_specs[utils.REP_MODE] = utils.REP_ASYNC 2755 rdf_dict = self.rest.create_rdf_device_pair( 2756 self.data.array, self.data.device_id, self.data.rdf_group_no, 2757 self.data.device_id2, self.data.remote_array, extra_specs) 2758 self.assertEqual(ref_dict, rdf_dict) 2759 2760 def test_create_rdf_device_pair_metro(self): 2761 ref_dict = {'array': self.data.remote_array, 2762 'device_id': self.data.device_id2} 2763 extra_specs = deepcopy(self.data.extra_specs) 2764 extra_specs[utils.REP_MODE] = utils.REP_METRO 2765 extra_specs[utils.METROBIAS] = True 2766 rdf_dict = self.rest.create_rdf_device_pair( 2767 self.data.array, self.data.device_id, self.data.rdf_group_no, 2768 self.data.device_id2, self.data.remote_array, extra_specs) 2769 self.assertEqual(ref_dict, rdf_dict) 2770 2771 @mock.patch.object(rest.VMAXRest, 'get_rdf_group', 2772 side_effect=[{'numDevices': 0}, {'numDevices': 0}, 2773 {'numDevices': 1}]) 2774 def test_get_metro_payload_info(self, mock_rdfg): 2775 ref_payload = {"establish": 'true', "rdfType": 'RDF1'} 2776 payload1 = self.rest.get_metro_payload_info( 2777 self.data.array, ref_payload, self.data.rdf_group_no, {}) 2778 self.assertEqual(ref_payload, payload1) 2779 payload2 = self.rest.get_metro_payload_info( 2780 self.data.array, ref_payload, self.data.rdf_group_no, 2781 {'metro_bias': True}) 2782 self.assertEqual('true', payload2['metroBias']) 2783 ref_payload2 = {"establish": 'true', "rdfType": 'RDF1'} 2784 payload3 = self.rest.get_metro_payload_info( 2785 self.data.array, ref_payload2, self.data.rdf_group_no, {}) 2786 ref_payload3 = {"rdfType": 'NA', "format": 'true'} 2787 self.assertEqual(ref_payload3, payload3) 2788 2789 def test_modify_rdf_device_pair(self): 2790 resource_name = "70/volume/00001" 2791 common_opts = {"force": 'false', 2792 "symForce": 'false', 2793 "star": 'false', 2794 "hop2": 'false', 2795 "bypass": 'false'} 2796 suspend_payload = {"action": "Suspend", 2797 'executionOption': 'ASYNCHRONOUS', 2798 "suspend": common_opts} 2799 2800 failover_opts = deepcopy(common_opts) 2801 failover_opts.update({"establish": 'true', 2802 "restore": 'false', 2803 "remote": 'false', 2804 "immediate": 'false'}) 2805 failover_payload = {"action": "Failover", 2806 'executionOption': 'ASYNCHRONOUS', 2807 "failover": failover_opts} 2808 with mock.patch.object( 2809 self.rest, "modify_resource", 2810 return_value=(200, self.data.job_list[0])) as mock_mod: 2811 self.rest.modify_rdf_device_pair( 2812 self.data.array, self.data.device_id, self.data.rdf_group_no, 2813 self.data.extra_specs, suspend=True) 2814 mock_mod.assert_called_once_with( 2815 self.data.array, 'replication', 'rdf_group', 2816 suspend_payload, resource_name=resource_name, 2817 private='/private') 2818 mock_mod.reset_mock() 2819 self.rest.modify_rdf_device_pair( 2820 self.data.array, self.data.device_id, self.data.rdf_group_no, 2821 self.data.extra_specs, suspend=False) 2822 mock_mod.assert_called_once_with( 2823 self.data.array, 'replication', 'rdf_group', 2824 failover_payload, resource_name=resource_name, 2825 private='/private') 2826 2827 @mock.patch.object(rest.VMAXRest, 'delete_resource') 2828 def test_delete_rdf_pair(self, mock_del): 2829 self.rest.delete_rdf_pair( 2830 self.data.array, self.data.device_id, self.data.rdf_group_no) 2831 mock_del.assert_called_once() 2832 2833 def test_get_storage_group_rep(self): 2834 array = self.data.array 2835 source_group_name = self.data.storagegroup_name_source 2836 ref_details = self.data.sg_details_rep[0] 2837 volume_group = self.rest.get_storage_group_rep(array, 2838 source_group_name) 2839 self.assertEqual(volume_group, ref_details) 2840 2841 def test_get_volumes_in_storage_group(self): 2842 array = self.data.array 2843 storagegroup_name = self.data.storagegroup_name_source 2844 ref_volumes = [self.data.device_id, self.data.device_id2] 2845 volume_list = self.rest.get_volumes_in_storage_group( 2846 array, storagegroup_name) 2847 self.assertEqual(ref_volumes, volume_list) 2848 2849 def test_create_storagegroup_snap(self): 2850 array = self.data.array 2851 extra_specs = self.data.extra_specs 2852 source_group = self.data.storagegroup_name_source 2853 snap_name = self.data.group_snapshot_name 2854 with mock.patch.object( 2855 self.rest, "create_storagegroup_snap") as mock_create: 2856 self.rest.create_storagegroup_snap( 2857 array, source_group, snap_name, extra_specs) 2858 mock_create.assert_called_once_with(array, 2859 source_group, 2860 snap_name, 2861 extra_specs) 2862 2863 def test_get_storagegroup_rdf_details(self): 2864 details = self.rest.get_storagegroup_rdf_details( 2865 self.data.array, self.data.test_vol_grp_name, 2866 self.data.rdf_group_no) 2867 self.assertEqual(self.data.sg_rdf_details[0], details) 2868 2869 def test_verify_rdf_state(self): 2870 verify1 = self.rest._verify_rdf_state( 2871 self.data.array, self.data.test_vol_grp_name, 2872 self.data.rdf_group_no, 'Failover') 2873 self.assertTrue(verify1) 2874 verify2 = self.rest._verify_rdf_state( 2875 self.data.array, self.data.test_fo_vol_group, 2876 self.data.rdf_group_no, 'Establish') 2877 self.assertTrue(verify2) 2878 2879 def test_modify_storagegroup_rdf(self): 2880 with mock.patch.object( 2881 self.rest, 'modify_resource', 2882 return_value=(202, self.data.job_list[0])) as mock_mod: 2883 self.rest.modify_storagegroup_rdf( 2884 self.data.array, self.data.test_vol_grp_name, 2885 self.data.rdf_group_no, 'Failover', 2886 self.data.extra_specs) 2887 mock_mod.assert_called_once() 2888 2889 def test_delete_storagegroup_rdf(self): 2890 with mock.patch.object( 2891 self.rest, 'delete_resource') as mock_del: 2892 self.rest.delete_storagegroup_rdf( 2893 self.data.array, self.data.test_vol_grp_name, 2894 self.data.rdf_group_no) 2895 mock_del.assert_called_once() 2896 2897 def test_is_next_gen_array(self): 2898 is_next_gen = self.rest.is_next_gen_array(self.data.array) 2899 self.assertFalse(is_next_gen) 2900 is_next_gen2 = self.rest.is_next_gen_array(self.data.array_herc) 2901 self.assertTrue(is_next_gen2) 2902 2903 @mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', 2904 new=test_utils.ZeroIntervalLoopingCall) 2905 @mock.patch.object(rest.VMAXRest, 'are_vols_rdf_paired', 2906 side_effect=[('', '', 'syncinprog'), 2907 ('', '', 'consistent'), 2908 exception.CinderException]) 2909 def test_wait_for_rdf_consistent_state(self, mock_paired): 2910 self.rest.wait_for_rdf_consistent_state( 2911 self.data.array, self.data.remote_array, 2912 self.data.device_id, self.data.device_id2, 2913 self.data.extra_specs) 2914 self.assertEqual(2, mock_paired.call_count) 2915 self.assertRaises(exception.VolumeBackendAPIException, 2916 self.rest.wait_for_rdf_consistent_state, 2917 self.data.array, self.data.remote_array, 2918 self.data.device_id, self.data.device_id2, 2919 self.data.extra_specs) 2920 2921 @mock.patch.object(rest.VMAXRest, 'modify_resource', 2922 return_value=('200', 'JobComplete')) 2923 def test_modify_volume_snap_rename(self, mock_modify): 2924 array = self.data.array 2925 source_id = self.data.device_id 2926 old_snap_backend_name = self.data.snapshot_id 2927 new_snap_backend_name = self.data.managed_snap_id 2928 self.rest.modify_volume_snap( 2929 array, source_id, source_id, old_snap_backend_name, 2930 self.data.extra_specs, link=False, unlink=False, 2931 rename=True, new_snap_name=new_snap_backend_name) 2932 mock_modify.assert_called_once() 2933 2934 def test_set_rest_credentials(self): 2935 array_info = { 2936 'RestServerIp': '10.10.10.10', 2937 'RestServerPort': '8443', 2938 'RestUserName': 'user_test', 2939 'RestPassword': 'pass_test', 2940 'SSLVerify': True, 2941 } 2942 self.rest.set_rest_credentials(array_info) 2943 self.assertEqual('user_test', self.rest.user) 2944 self.assertEqual('pass_test', self.rest.passwd) 2945 self.assertTrue(self.rest.verify) 2946 self.assertEqual('https://10.10.10.10:8443/univmax/restapi', 2947 self.rest.base_uri) 2948 2949 def test_get_vmax_model(self): 2950 reference = 'PowerMax_2000' 2951 with mock.patch.object(self.rest, '_get_request', 2952 return_value=self.data.powermax_model_details): 2953 self.assertEqual(self.rest.get_vmax_model(self.data.array), 2954 reference) 2955 2956 2957class VMAXProvisionTest(test.TestCase): 2958 def setUp(self): 2959 self.data = VMAXCommonData() 2960 2961 super(VMAXProvisionTest, self).setUp() 2962 volume_utils.get_max_over_subscription_ratio = mock.Mock() 2963 config_group = 'ProvisionTests' 2964 self.fake_xml = FakeXML().create_fake_config_file( 2965 config_group, self.data.port_group_name_i) 2966 configuration = FakeConfiguration(self.fake_xml, config_group) 2967 rest.VMAXRest._establish_rest_session = mock.Mock( 2968 return_value=FakeRequestsSession()) 2969 driver = iscsi.VMAXISCSIDriver(configuration=configuration) 2970 self.driver = driver 2971 self.common = self.driver.common 2972 self.provision = self.common.provision 2973 self.utils = self.common.utils 2974 self.rest = self.common.rest 2975 2976 @mock.patch.object(rest.VMAXRest, 'create_storage_group', 2977 return_value=VMAXCommonData.storagegroup_name_f) 2978 @mock.patch.object(rest.VMAXRest, 'get_storage_group', 2979 side_effect=[ 2980 VMAXCommonData.storagegroup_name_f, None]) 2981 def test_create_storage_group(self, mock_get_sg, mock_create): 2982 array = self.data.array 2983 storagegroup_name = self.data.storagegroup_name_f 2984 srp = self.data.srp 2985 slo = self.data.slo 2986 workload = self.data.workload 2987 extra_specs = self.data.extra_specs 2988 for x in range(0, 2): 2989 storagegroup = self.provision.create_storage_group( 2990 array, storagegroup_name, srp, slo, workload, extra_specs) 2991 self.assertEqual(storagegroup_name, storagegroup) 2992 mock_create.assert_called_once() 2993 2994 def test_create_volume_from_sg(self): 2995 array = self.data.array 2996 storagegroup_name = self.data.storagegroup_name_f 2997 volumeId = self.data.test_volume.id 2998 volume_name = self.utils.get_volume_element_name(volumeId) 2999 volume_size = self.data.test_volume.size 3000 extra_specs = self.data.extra_specs 3001 ref_dict = self.data.provider_location 3002 volume_dict = self.provision.create_volume_from_sg( 3003 array, volume_name, storagegroup_name, volume_size, extra_specs) 3004 self.assertEqual(ref_dict, volume_dict) 3005 3006 def test_delete_volume_from_srp(self): 3007 array = self.data.array 3008 device_id = self.data.device_id 3009 volume_name = self.data.volume_details[0]['volume_identifier'] 3010 with mock.patch.object(self.provision.rest, 'delete_volume'): 3011 self.provision.delete_volume_from_srp( 3012 array, device_id, volume_name) 3013 self.provision.rest.delete_volume.assert_called_once_with( 3014 array, device_id) 3015 3016 def test_create_volume_snap_vx(self): 3017 array = self.data.array 3018 source_device_id = self.data.device_id 3019 snap_name = self.data.snap_location['snap_name'] 3020 extra_specs = self.data.extra_specs 3021 with mock.patch.object(self.provision.rest, 'create_volume_snap'): 3022 self.provision.create_volume_snapvx( 3023 array, source_device_id, snap_name, extra_specs) 3024 self.provision.rest.create_volume_snap.assert_called_once_with( 3025 array, snap_name, source_device_id, extra_specs) 3026 3027 def test_create_volume_replica_create_snap_true(self): 3028 array = self.data.array 3029 source_device_id = self.data.device_id 3030 target_device_id = self.data.device_id2 3031 snap_name = self.data.snap_location['snap_name'] 3032 extra_specs = self.data.extra_specs 3033 with mock.patch.object(self.provision, 'create_volume_snapvx'): 3034 with mock.patch.object(self.provision.rest, 'modify_volume_snap'): 3035 self.provision.create_volume_replica( 3036 array, source_device_id, target_device_id, 3037 snap_name, extra_specs, create_snap=True) 3038 self.provision.rest.modify_volume_snap.assert_called_once_with( 3039 array, source_device_id, target_device_id, snap_name, 3040 extra_specs, link=True) 3041 self.provision.create_volume_snapvx.assert_called_once_with( 3042 array, source_device_id, snap_name, extra_specs) 3043 3044 def test_create_volume_replica_create_snap_false(self): 3045 array = self.data.array 3046 source_device_id = self.data.device_id 3047 target_device_id = self.data.device_id2 3048 snap_name = self.data.snap_location['snap_name'] 3049 extra_specs = self.data.extra_specs 3050 with mock.patch.object(self.provision, 'create_volume_snapvx'): 3051 with mock.patch.object(self.provision.rest, 'modify_volume_snap'): 3052 self.provision.create_volume_replica( 3053 array, source_device_id, target_device_id, 3054 snap_name, extra_specs, create_snap=False) 3055 self.provision.rest.modify_volume_snap.assert_called_once_with( 3056 array, source_device_id, target_device_id, snap_name, 3057 extra_specs, link=True) 3058 self.provision.create_volume_snapvx.assert_not_called() 3059 3060 def test_break_replication_relationship(self): 3061 array = self.data.array 3062 source_device_id = self.data.device_id 3063 target_device_id = self.data.device_id2 3064 snap_name = self.data.snap_location['snap_name'] 3065 extra_specs = self.data.extra_specs 3066 with mock.patch.object(self.provision.rest, 'modify_volume_snap'): 3067 self.provision.break_replication_relationship( 3068 array, target_device_id, source_device_id, snap_name, 3069 extra_specs) 3070 (self.provision.rest.modify_volume_snap. 3071 assert_called_once_with( 3072 array, source_device_id, target_device_id, 3073 snap_name, extra_specs, 3074 list_volume_pairs=None, unlink=True)) 3075 3076 @mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', 3077 new=test_utils.ZeroIntervalLoopingCall) 3078 def test_unlink_volume(self): 3079 with mock.patch.object(self.rest, 'modify_volume_snap') as mock_mod: 3080 self.provision._unlink_volume( 3081 self.data.array, self.data.device_id, self.data.device_id2, 3082 self.data.snap_location['snap_name'], self.data.extra_specs) 3083 mock_mod.assert_called_once_with( 3084 self.data.array, self.data.device_id, self.data.device_id2, 3085 self.data.snap_location['snap_name'], self.data.extra_specs, 3086 list_volume_pairs=None, unlink=True) 3087 3088 @mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', 3089 new=test_utils.ZeroIntervalLoopingCall) 3090 def test_unlink_volume_exception(self): 3091 with mock.patch.object( 3092 self.rest, 'modify_volume_snap', side_effect=[ 3093 exception.VolumeBackendAPIException(data=''), ''] 3094 ) as mock_mod: 3095 self.provision._unlink_volume( 3096 self.data.array, self.data.device_id, self.data.device_id2, 3097 self.data.snap_location['snap_name'], self.data.extra_specs) 3098 self.assertEqual(2, mock_mod.call_count) 3099 3100 def test_delete_volume_snap(self): 3101 array = self.data.array 3102 source_device_id = self.data.device_id 3103 snap_name = self.data.snap_location['snap_name'] 3104 with mock.patch.object(self.provision.rest, 'delete_volume_snap'): 3105 self.provision.delete_volume_snap( 3106 array, snap_name, source_device_id) 3107 self.provision.rest.delete_volume_snap.assert_called_once_with( 3108 array, snap_name, source_device_id, False) 3109 3110 def test_delete_volume_snap_restore(self): 3111 array = self.data.array 3112 source_device_id = self.data.device_id 3113 snap_name = self.data.snap_location['snap_name'] 3114 restored = True 3115 with mock.patch.object(self.provision.rest, 'delete_volume_snap'): 3116 self.provision.delete_volume_snap( 3117 array, snap_name, source_device_id, restored) 3118 self.provision.rest.delete_volume_snap.assert_called_once_with( 3119 array, snap_name, source_device_id, True) 3120 3121 @mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', 3122 new=test_utils.ZeroIntervalLoopingCall) 3123 def test_restore_complete(self): 3124 array = self.data.array 3125 source_device_id = self.data.device_id 3126 snap_name = self.data.snap_location['snap_name'] 3127 extra_specs = self.data.extra_specs 3128 with mock.patch.object( 3129 self.provision, '_is_restore_complete', 3130 return_value=True): 3131 isrestored = self.provision.is_restore_complete( 3132 array, source_device_id, snap_name, extra_specs) 3133 self.assertTrue(isrestored) 3134 with mock.patch.object( 3135 self.provision, '_is_restore_complete', 3136 side_effect=exception.CinderException): 3137 self.assertRaises(exception.VolumeBackendAPIException, 3138 self.provision.is_restore_complete, 3139 array, source_device_id, snap_name, extra_specs) 3140 3141 def test_is_restore_complete(self): 3142 array = self.data.array 3143 source_device_id = self.data.device_id 3144 snap_name = self.data.snap_location['snap_name'] 3145 snap_details = { 3146 'linkedDevices': 3147 [{'targetDevice': source_device_id, 'state': "Restored"}]} 3148 with mock.patch.object(self.provision.rest, 3149 'get_volume_snap', return_value=snap_details): 3150 isrestored = self.provision._is_restore_complete( 3151 array, source_device_id, snap_name) 3152 self.assertTrue(isrestored) 3153 snap_details['linkedDevices'][0]['state'] = "Restoring" 3154 with mock.patch.object(self.provision.rest, 3155 'get_volume_snap', return_value=snap_details): 3156 isrestored = self.provision._is_restore_complete( 3157 array, source_device_id, snap_name) 3158 self.assertFalse(isrestored) 3159 3160 def test_revert_volume_snapshot(self): 3161 array = self.data.array 3162 source_device_id = self.data.device_id 3163 snap_name = self.data.snap_location['snap_name'] 3164 extra_specs = self.data.extra_specs 3165 with mock.patch.object( 3166 self.provision.rest, 'modify_volume_snap', return_value=None): 3167 self.provision.revert_volume_snapshot( 3168 array, source_device_id, snap_name, extra_specs) 3169 self.provision.rest.modify_volume_snap.assert_called_once_with( 3170 array, source_device_id, "", snap_name, 3171 extra_specs, restore=True) 3172 3173 def test_extend_volume(self): 3174 array = self.data.array 3175 device_id = self.data.device_id 3176 new_size = '3' 3177 extra_specs = self.data.extra_specs 3178 with mock.patch.object(self.provision.rest, 'extend_volume' 3179 ) as mock_ex: 3180 self.provision.extend_volume(array, device_id, new_size, 3181 extra_specs) 3182 mock_ex.assert_called_once_with( 3183 array, device_id, new_size, extra_specs) 3184 mock_ex.reset_mock() 3185 # Pass in rdf group 3186 self.provision.extend_volume(array, device_id, new_size, 3187 extra_specs, self.data.rdf_group_no) 3188 mock_ex.assert_called_once_with( 3189 array, device_id, new_size, extra_specs) 3190 3191 def test_get_srp_pool_stats(self): 3192 array = self.data.array 3193 array_info = self.common.pool_info['arrays_info'][0] 3194 ref_stats = (self.data.srp_details['total_usable_cap_gb'], 3195 float(self.data.srp_details['total_usable_cap_gb'] 3196 - self.data.srp_details['total_used_cap_gb']), 3197 self.data.srp_details['total_subscribed_cap_gb'], 3198 self.data.srp_details['reserved_cap_percent']) 3199 stats = self.provision.get_srp_pool_stats(array, array_info) 3200 self.assertEqual(ref_stats, stats) 3201 3202 def test_get_srp_pool_stats_errors(self): 3203 # cannot retrieve srp 3204 array = self.data.array 3205 array_info = {'srpName': self.data.failed_resource} 3206 ref_stats = (0, 0, 0, 0, False) 3207 stats = self.provision.get_srp_pool_stats(array, array_info) 3208 self.assertEqual(ref_stats, stats) 3209 # cannot report on all stats 3210 with mock.patch.object(self.provision.rest, 'get_srp_by_name', 3211 return_value={'total_usable_cap_gb': 33}): 3212 ref_stats = (33, 0, 0, 0) 3213 stats = self.provision.get_srp_pool_stats(array, array_info) 3214 self.assertEqual(ref_stats, stats) 3215 3216 def test_verify_slo_workload_true(self): 3217 # with slo and workload 3218 array = self.data.array 3219 slo = self.data.slo 3220 workload = self.data.workload 3221 srp = self.data.srp 3222 valid_slo, valid_workload = self.provision.verify_slo_workload( 3223 array, slo, workload, srp) 3224 self.assertTrue(valid_slo) 3225 self.assertTrue(valid_workload) 3226 # slo and workload = none 3227 slo2 = None 3228 workload2 = None 3229 valid_slo2, valid_workload2 = self.provision.verify_slo_workload( 3230 array, slo2, workload2, srp) 3231 self.assertTrue(valid_slo2) 3232 self.assertTrue(valid_workload2) 3233 slo2 = None 3234 workload2 = 'None' 3235 valid_slo2, valid_workload2 = self.provision.verify_slo_workload( 3236 array, slo2, workload2, srp) 3237 self.assertTrue(valid_slo2) 3238 self.assertTrue(valid_workload2) 3239 3240 def test_verify_slo_workload_false(self): 3241 # Both wrong 3242 array = self.data.array 3243 slo = 'Diamante' 3244 workload = 'DSSS' 3245 srp = self.data.srp 3246 valid_slo, valid_workload = self.provision.verify_slo_workload( 3247 array, slo, workload, srp) 3248 self.assertFalse(valid_slo) 3249 self.assertFalse(valid_workload) 3250 # Workload set, no slo set 3251 valid_slo, valid_workload = self.provision.verify_slo_workload( 3252 array, None, self.data.workload, srp) 3253 self.assertTrue(valid_slo) 3254 self.assertFalse(valid_workload) 3255 3256 def test_get_slo_workload_settings_from_storage_group(self): 3257 ref_settings = "Diamond+DSS" 3258 sg_slo_settings = ( 3259 self.provision.get_slo_workload_settings_from_storage_group( 3260 self.data.array, self.data.defaultstoragegroup_name)) 3261 self.assertEqual(ref_settings, sg_slo_settings) 3262 # No workload 3263 with mock.patch.object(self.provision.rest, 'get_storage_group', 3264 return_value={'slo': 'Silver'}): 3265 ref_settings2 = "Silver+NONE" 3266 sg_slo_settings2 = ( 3267 self.provision.get_slo_workload_settings_from_storage_group( 3268 self.data.array, 'no_workload_sg')) 3269 self.assertEqual(ref_settings2, sg_slo_settings2) 3270 # NextGen Array 3271 with mock.patch.object(self.rest, 'is_next_gen_array', 3272 return_value=True): 3273 ref_settings3 = "Diamond+NONE" 3274 sg_slo_settings3 = ( 3275 self.provision.get_slo_workload_settings_from_storage_group( 3276 self.data.array, self.data.defaultstoragegroup_name)) 3277 self.assertEqual(ref_settings3, sg_slo_settings3) 3278 3279 @mock.patch.object(rest.VMAXRest, 'wait_for_rdf_consistent_state') 3280 @mock.patch.object(rest.VMAXRest, 'delete_rdf_pair') 3281 @mock.patch.object(rest.VMAXRest, 'modify_rdf_device_pair') 3282 def test_break_rdf_relationship(self, mock_mod, mock_del, mock_wait): 3283 array = self.data.array 3284 device_id = self.data.device_id 3285 target_device = self.data.device_id2 3286 rdf_group_name = self.data.rdf_group_name 3287 rep_extra_specs = self.data.rep_extra_specs 3288 # State is suspended 3289 self.provision.break_rdf_relationship( 3290 array, device_id, target_device, 3291 rdf_group_name, rep_extra_specs, "Suspended") 3292 mock_mod.assert_not_called() 3293 mock_del.assert_called_once_with( 3294 array, device_id, rdf_group_name) 3295 mock_del.reset_mock() 3296 # State is synchronized 3297 self.provision.break_rdf_relationship( 3298 array, device_id, target_device, 3299 rdf_group_name, rep_extra_specs, "Synchronized") 3300 mock_mod.assert_called_once_with( 3301 array, device_id, rdf_group_name, rep_extra_specs, 3302 suspend=True) 3303 mock_del.assert_called_once_with( 3304 array, device_id, rdf_group_name) 3305 # sync still in progress 3306 self.provision.break_rdf_relationship( 3307 array, device_id, target_device, 3308 rdf_group_name, rep_extra_specs, "SyncInProg") 3309 mock_wait.assert_called_once() 3310 3311 @mock.patch.object(provision.VMAXProvision, 'disable_group_replication') 3312 @mock.patch.object(provision.VMAXProvision, 'delete_rdf_pair') 3313 def test_break_metro_rdf_pair(self, mock_del, mock_disable): 3314 self.provision.break_metro_rdf_pair( 3315 self.data.array, self.data.device_id, self.data.device_id2, 3316 self.data.rdf_group_no, self.data.rep_extra_specs, 'metro_grp') 3317 mock_del.assert_called_once() 3318 3319 def test_delete_rdf_pair_async(self): 3320 with mock.patch.object( 3321 self.provision.rest, 'delete_rdf_pair') as mock_del_rdf: 3322 extra_specs = deepcopy(self.data.extra_specs) 3323 extra_specs[utils.REP_MODE] = utils.REP_ASYNC 3324 self.provision.delete_rdf_pair( 3325 self.data.array, self.data.device_id, 3326 self.data.rdf_group_no, self.data.device_id2, extra_specs) 3327 mock_del_rdf.assert_called_once() 3328 3329 def test_failover_volume(self): 3330 array = self.data.array 3331 device_id = self.data.device_id 3332 rdf_group_name = self.data.rdf_group_name 3333 extra_specs = self.data.extra_specs 3334 with mock.patch.object( 3335 self.provision.rest, 'modify_rdf_device_pair') as mod_rdf: 3336 self.provision.failover_volume( 3337 array, device_id, rdf_group_name, 3338 extra_specs, '', True) 3339 mod_rdf.assert_called_once_with( 3340 array, device_id, rdf_group_name, extra_specs) 3341 mod_rdf.reset_mock() 3342 self.provision.failover_volume( 3343 array, device_id, rdf_group_name, 3344 extra_specs, '', False) 3345 mod_rdf.assert_called_once_with( 3346 array, device_id, rdf_group_name, extra_specs) 3347 3348 @mock.patch.object(rest.VMAXRest, 'get_storage_group', 3349 return_value=None) 3350 def test_create_volume_group_success(self, mock_get_sg): 3351 array = self.data.array 3352 group_name = self.data.storagegroup_name_source 3353 extra_specs = self.data.extra_specs 3354 ref_value = self.data.storagegroup_name_source 3355 storagegroup = self.provision.create_volume_group( 3356 array, group_name, extra_specs) 3357 self.assertEqual(ref_value, storagegroup) 3358 3359 def test_create_group_replica(self): 3360 array = self.data.array 3361 source_group = self.data.storagegroup_name_source 3362 snap_name = self.data.group_snapshot_name 3363 extra_specs = self.data.extra_specs 3364 with mock.patch.object( 3365 self.provision, 3366 'create_group_replica') as mock_create_replica: 3367 self.provision.create_group_replica( 3368 array, source_group, snap_name, extra_specs) 3369 mock_create_replica.assert_called_once_with( 3370 array, source_group, snap_name, extra_specs) 3371 3372 def test_delete_group_replica(self): 3373 array = self.data.array 3374 snap_name = self.data.group_snapshot_name 3375 source_group_name = self.data.storagegroup_name_source 3376 with mock.patch.object( 3377 self.provision, 3378 'delete_group_replica') as mock_delete_replica: 3379 self.provision.delete_group_replica(array, 3380 snap_name, 3381 source_group_name) 3382 mock_delete_replica.assert_called_once_with( 3383 array, snap_name, source_group_name) 3384 3385 def test_link_and_break_replica(self): 3386 array = self.data.array 3387 source_group_name = self.data.storagegroup_name_source 3388 target_group_name = self.data.target_group_name 3389 snap_name = self.data.group_snapshot_name 3390 extra_specs = self.data.extra_specs 3391 deleteSnapshot = False 3392 with mock.patch.object( 3393 self.provision, 3394 'link_and_break_replica') as mock_link_and_break_replica: 3395 self.provision.link_and_break_replica( 3396 array, source_group_name, 3397 target_group_name, snap_name, 3398 extra_specs, deleteSnapshot) 3399 mock_link_and_break_replica.assert_called_once_with( 3400 array, source_group_name, 3401 target_group_name, snap_name, 3402 extra_specs, deleteSnapshot) 3403 3404 @mock.patch.object(rest.VMAXRest, 'get_storage_group', 3405 side_effect=[None, VMAXCommonData.sg_details[1]]) 3406 @mock.patch.object(provision.VMAXProvision, 'create_volume_group') 3407 def test_get_or_create_volume_group(self, mock_create, mock_sg): 3408 for x in range(0, 2): 3409 self.provision.get_or_create_volume_group( 3410 self.data.array, self.data.test_group, self.data.extra_specs) 3411 self.assertEqual(2, mock_sg.call_count) 3412 self.assertEqual(1, mock_create.call_count) 3413 3414 @mock.patch.object(rest.VMAXRest, 'create_resource', 3415 return_value=(202, VMAXCommonData.job_list[0])) 3416 def test_replicate_group(self, mock_create): 3417 self.rest.replicate_group( 3418 self.data.array, self.data.test_rep_group, 3419 self.data.rdf_group_no, self.data.remote_array, 3420 self.data.extra_specs) 3421 mock_create.assert_called_once() 3422 3423 def test_enable_group_replication(self): 3424 with mock.patch.object(self.rest, 3425 'modify_storagegroup_rdf') as mock_mod: 3426 self.provision.enable_group_replication( 3427 self.data.array, self.data.test_vol_grp_name, 3428 self.data.rdf_group_no, self.data.extra_specs) 3429 mock_mod.assert_called_once() 3430 3431 def test_disable_group_replication(self): 3432 with mock.patch.object(self.rest, 3433 'modify_storagegroup_rdf') as mock_mod: 3434 self.provision.disable_group_replication( 3435 self.data.array, self.data.test_vol_grp_name, 3436 self.data.rdf_group_no, self.data.extra_specs) 3437 mock_mod.assert_called_once() 3438 3439 def test_failover_group(self): 3440 with mock.patch.object(self.rest, 3441 'modify_storagegroup_rdf') as mock_fo: 3442 # Failover 3443 self.provision.failover_group( 3444 self.data.array, self.data.test_vol_grp_name, 3445 self.data.rdf_group_no, self.data.extra_specs) 3446 mock_fo.assert_called_once_with( 3447 self.data.array, self.data.test_vol_grp_name, 3448 self.data.rdf_group_no, 'Failover', self.data.extra_specs) 3449 mock_fo.reset_mock() 3450 # Failback 3451 self.provision.failover_group( 3452 self.data.array, self.data.test_vol_grp_name, 3453 self.data.rdf_group_no, self.data.extra_specs, False) 3454 mock_fo.assert_called_once_with( 3455 self.data.array, self.data.test_vol_grp_name, 3456 self.data.rdf_group_no, 'Failback', self.data.extra_specs) 3457 3458 @mock.patch.object(rest.VMAXRest, 'modify_storagegroup_rdf') 3459 @mock.patch.object(rest.VMAXRest, 'delete_storagegroup_rdf') 3460 def test_delete_group_replication(self, mock_mod, mock_del): 3461 self.provision.delete_group_replication( 3462 self.data.array, self.data.test_vol_grp_name, 3463 self.data.rdf_group_no, self.data.extra_specs) 3464 mock_mod.assert_called_once() 3465 mock_del.assert_called_once() 3466 3467 @mock.patch.object( 3468 rest.VMAXRest, 'get_snap_linked_device_list', 3469 side_effect=[[{'targetDevice': VMAXCommonData.device_id2}], 3470 [{'targetDevice': VMAXCommonData.device_id2}, 3471 {'targetDevice': VMAXCommonData.device_id3}]]) 3472 @mock.patch.object(provision.VMAXProvision, '_unlink_volume') 3473 def test_delete_volume_snap_check_for_links(self, mock_unlink, mock_tgts): 3474 self.provision.delete_volume_snap_check_for_links( 3475 self.data.array, self.data.test_snapshot_snap_name, 3476 self.data.device_id, self.data.extra_specs) 3477 mock_unlink.assert_called_once_with( 3478 self.data.array, "", "", self.data.test_snapshot_snap_name, 3479 self.data.extra_specs, list_volume_pairs=[ 3480 (self.data.device_id, VMAXCommonData.device_id2)]) 3481 mock_unlink.reset_mock() 3482 self.provision.delete_volume_snap_check_for_links( 3483 self.data.array, self.data.test_snapshot_snap_name, 3484 self.data.device_id, self.data.extra_specs) 3485 self.assertEqual(2, mock_unlink.call_count) 3486 3487 3488class VMAXCommonTest(test.TestCase): 3489 def setUp(self): 3490 self.data = VMAXCommonData() 3491 3492 super(VMAXCommonTest, self).setUp() 3493 self.mock_object(volume_utils, 'get_max_over_subscription_ratio', 3494 return_value=1.0) 3495 config_group = 'CommonTests' 3496 self.fake_xml = FakeXML().create_fake_config_file( 3497 config_group, self.data.port_group_name_f) 3498 configuration = FakeConfiguration(self.fake_xml, config_group, 3499 1, 1) 3500 rest.VMAXRest._establish_rest_session = mock.Mock( 3501 return_value=FakeRequestsSession()) 3502 driver = fc.VMAXFCDriver(configuration=configuration) 3503 self.driver = driver 3504 self.common = self.driver.common 3505 self.masking = self.common.masking 3506 self.provision = self.common.provision 3507 self.rest = self.common.rest 3508 self.utils = self.common.utils 3509 self.utils.get_volumetype_extra_specs = ( 3510 mock.Mock(return_value=self.data.vol_type_extra_specs)) 3511 3512 @mock.patch.object(rest.VMAXRest, 3513 'set_rest_credentials') 3514 @mock.patch.object(common.VMAXCommon, 3515 '_get_slo_workload_combinations', 3516 return_value=[]) 3517 @mock.patch.object(utils.VMAXUtils, 3518 'parse_file_to_get_array_map', 3519 return_value=[]) 3520 def test_gather_info_no_opts(self, mock_parse, mock_combo, mock_rest): 3521 configuration = FakeConfiguration(None, 'config_group', None, None) 3522 fc.VMAXFCDriver(configuration=configuration) 3523 3524 @mock.patch.object(rest.VMAXRest, 'is_next_gen_array', 3525 return_value=True) 3526 @mock.patch.object(rest.VMAXRest, 'set_rest_credentials') 3527 @mock.patch.object(common.VMAXCommon, '_get_slo_workload_combinations', 3528 return_value=[]) 3529 @mock.patch.object(common.VMAXCommon, 'get_attributes_from_cinder_config', 3530 return_value=VMAXCommonData.array_info_wl) 3531 def test_gather_info_next_gen(self, mock_parse, mock_combo, mock_rest, 3532 mock_nextgen): 3533 self.common._gather_info() 3534 self.assertTrue(self.common.nextGen) 3535 3536 def test_get_slo_workload_combinations_powermax(self): 3537 finalarrayinfolist = self.common._get_slo_workload_combinations( 3538 self.data.array_info_no_wl) 3539 self.assertTrue(len(finalarrayinfolist) > 1) 3540 3541 @mock.patch.object(rest.VMAXRest, 'get_vmax_model', 3542 return_value=VMAXCommonData.vmax_model_details['model']) 3543 @mock.patch.object(rest.VMAXRest, 'get_slo_list', 3544 return_value=VMAXCommonData.vmax_slo_details['sloId']) 3545 def test_get_slo_workload_combinations_vmax(self, mck_slo, mck_model): 3546 finalarrayinfolist = self.common._get_slo_workload_combinations( 3547 self.data.array_info_wl) 3548 self.assertTrue(len(finalarrayinfolist) > 1) 3549 3550 @mock.patch.object(rest.VMAXRest, 'get_vmax_model', 3551 return_value=VMAXCommonData.powermax_model_details[ 3552 'model']) 3553 @mock.patch.object(rest.VMAXRest, 'get_workload_settings', 3554 return_value=[]) 3555 @mock.patch.object(rest.VMAXRest, 'get_slo_list', 3556 return_value=VMAXCommonData.powermax_slo_details[ 3557 'sloId']) 3558 def test_get_slo_workload_combinations_next_gen(self, mck_slo, mck_wl, 3559 mck_model): 3560 self.common.nextGen = True 3561 finalarrayinfolist = self.common._get_slo_workload_combinations( 3562 self.data.array_info_no_wl) 3563 self.assertTrue(len(finalarrayinfolist) == 14) 3564 3565 @mock.patch.object(rest.VMAXRest, 'get_vmax_model', 3566 return_value=VMAXCommonData.vmax_model_details[ 3567 'model']) 3568 @mock.patch.object(rest.VMAXRest, 'get_workload_settings', 3569 return_value=[]) 3570 @mock.patch.object(rest.VMAXRest, 'get_slo_list', 3571 return_value=VMAXCommonData.powermax_slo_details[ 3572 'sloId']) 3573 def test_get_slo_workload_combinations_next_gen_vmax( 3574 self, mck_slo, mck_wl, mck_model): 3575 self.common.nextGen = True 3576 finalarrayinfolist = self.common._get_slo_workload_combinations( 3577 self.data.array_info_no_wl) 3578 self.assertTrue(len(finalarrayinfolist) == 18) 3579 3580 def test_get_slo_workload_combinations_failed(self): 3581 array_info = {} 3582 self.assertRaises(exception.VolumeBackendAPIException, 3583 self.common._get_slo_workload_combinations, 3584 array_info) 3585 3586 def test_create_volume(self): 3587 ref_model_update = ( 3588 {'provider_location': six.text_type(self.data.provider_location)}) 3589 model_update = self.common.create_volume(self.data.test_volume) 3590 self.assertEqual(ref_model_update, model_update) 3591 3592 def test_create_volume_qos(self): 3593 ref_model_update = ( 3594 {'provider_location': six.text_type(self.data.provider_location)}) 3595 extra_specs = deepcopy(self.data.extra_specs_intervals_set) 3596 extra_specs['qos'] = { 3597 'total_iops_sec': '4000', 'DistributionType': 'Always'} 3598 with mock.patch.object(self.utils, 'get_volumetype_extra_specs', 3599 return_value=extra_specs): 3600 model_update = self.common.create_volume(self.data.test_volume) 3601 self.assertEqual(ref_model_update, model_update) 3602 3603 def test_create_volume_from_snapshot(self): 3604 ref_model_update = ( 3605 {'provider_location': six.text_type( 3606 self.data.provider_location)}) 3607 model_update = self.common.create_volume_from_snapshot( 3608 self.data.test_clone_volume, self.data.test_snapshot) 3609 self.assertEqual(ref_model_update, model_update) 3610 # Test from legacy snapshot 3611 model_update = self.common.create_volume_from_snapshot( 3612 self.data.test_clone_volume, self.data.test_legacy_snapshot) 3613 self.assertEqual(ref_model_update, model_update) 3614 3615 def test_cloned_volume(self): 3616 ref_model_update = ( 3617 {'provider_location': six.text_type( 3618 self.data.provider_location)}) 3619 model_update = self.common.create_cloned_volume( 3620 self.data.test_clone_volume, self.data.test_volume) 3621 self.assertEqual(ref_model_update, model_update) 3622 3623 def test_delete_volume(self): 3624 with mock.patch.object(self.common, '_delete_volume'): 3625 self.common.delete_volume(self.data.test_volume) 3626 self.common._delete_volume.assert_called_once_with( 3627 self.data.test_volume) 3628 3629 def test_create_snapshot(self): 3630 ref_model_update = ( 3631 {'provider_location': six.text_type( 3632 self.data.snap_location)}) 3633 model_update = self.common.create_snapshot( 3634 self.data.test_snapshot, self.data.test_volume) 3635 self.assertEqual(ref_model_update, model_update) 3636 3637 def test_delete_snapshot(self): 3638 snap_name = self.data.snap_location['snap_name'] 3639 sourcedevice_id = self.data.snap_location['source_id'] 3640 with mock.patch.object(self.provision, 'delete_volume_snap'): 3641 self.common.delete_snapshot(self.data.test_snapshot, 3642 self.data.test_volume) 3643 self.provision.delete_volume_snap.assert_called_once_with( 3644 self.data.array, snap_name, [sourcedevice_id]) 3645 3646 def test_delete_snapshot_not_found(self): 3647 with mock.patch.object(self.common, '_parse_snap_info', 3648 return_value=(None, 'Something')): 3649 with mock.patch.object(self.provision, 'delete_volume_snap'): 3650 self.common.delete_snapshot(self.data.test_snapshot, 3651 self.data.test_volume) 3652 self.provision.delete_volume_snap.assert_not_called() 3653 3654 def test_delete_legacy_snap(self): 3655 with mock.patch.object(self.common, '_delete_volume') as mock_del: 3656 self.common.delete_snapshot(self.data.test_legacy_snapshot, 3657 self.data.test_legacy_vol) 3658 mock_del.assert_called_once_with(self.data.test_legacy_snapshot) 3659 3660 def test_remove_members(self): 3661 array = self.data.array 3662 device_id = self.data.device_id 3663 volume = self.data.test_volume 3664 volume_name = self.data.test_volume.name 3665 extra_specs = self.data.extra_specs 3666 with mock.patch.object(self.masking, 3667 'remove_and_reset_members') as mock_rm: 3668 self.common._remove_members(array, volume, device_id, 3669 extra_specs, self.data.connector) 3670 mock_rm.assert_called_once_with( 3671 array, volume, device_id, volume_name, 3672 extra_specs, True, self.data.connector, async_grp=None) 3673 3674 def test_unmap_lun(self): 3675 array = self.data.array 3676 device_id = self.data.device_id 3677 volume = self.data.test_volume 3678 extra_specs = deepcopy(self.data.extra_specs_intervals_set) 3679 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 3680 connector = self.data.connector 3681 with mock.patch.object(self.common, '_remove_members'): 3682 self.common._unmap_lun(volume, connector) 3683 self.common._remove_members.assert_called_once_with( 3684 array, volume, device_id, extra_specs, 3685 connector, async_grp=None) 3686 3687 def test_unmap_lun_qos(self): 3688 array = self.data.array 3689 device_id = self.data.device_id 3690 volume = self.data.test_volume 3691 extra_specs = deepcopy(self.data.extra_specs_intervals_set) 3692 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 3693 extra_specs['qos'] = { 3694 'total_iops_sec': '4000', 'DistributionType': 'Always'} 3695 connector = self.data.connector 3696 with mock.patch.object(self.common, '_remove_members'): 3697 with mock.patch.object(self.utils, 'get_volumetype_extra_specs', 3698 return_value=extra_specs): 3699 self.common._unmap_lun(volume, connector) 3700 self.common._remove_members.assert_called_once_with( 3701 array, volume, device_id, extra_specs, 3702 connector, async_grp=None) 3703 3704 def test_unmap_lun_not_mapped(self): 3705 volume = self.data.test_volume 3706 connector = self.data.connector 3707 with mock.patch.object(self.common, 'find_host_lun_id', 3708 return_value=({}, False, [])): 3709 with mock.patch.object(self.common, '_remove_members'): 3710 self.common._unmap_lun(volume, connector) 3711 self.common._remove_members.assert_not_called() 3712 3713 def test_unmap_lun_connector_is_none(self): 3714 array = self.data.array 3715 device_id = self.data.device_id 3716 volume = self.data.test_volume 3717 extra_specs = deepcopy(self.data.extra_specs_intervals_set) 3718 extra_specs['storagetype:portgroupname'] = ( 3719 self.data.port_group_name_f) 3720 with mock.patch.object(self.common, '_remove_members'): 3721 self.common._unmap_lun(volume, None) 3722 self.common._remove_members.assert_called_once_with( 3723 array, volume, device_id, extra_specs, None, async_grp=None) 3724 3725 def test_initialize_connection_already_mapped(self): 3726 volume = self.data.test_volume 3727 connector = self.data.connector 3728 host_lun = (self.data.maskingview[0]['maskingViewConnection'][0] 3729 ['host_lun_address']) 3730 ref_dict = {'hostlunid': int(host_lun, 16), 3731 'maskingview': self.data.masking_view_name_f, 3732 'array': self.data.array, 3733 'device_id': self.data.device_id} 3734 device_info_dict = self.common.initialize_connection(volume, connector) 3735 self.assertEqual(ref_dict, device_info_dict) 3736 3737 def test_initialize_connection_already_mapped_next_gen(self): 3738 with mock.patch.object(self.rest, 'is_next_gen_array', 3739 return_value=True): 3740 volume = self.data.test_volume 3741 connector = self.data.connector 3742 host_lun = (self.data.maskingview[0]['maskingViewConnection'][0] 3743 ['host_lun_address']) 3744 ref_dict = {'hostlunid': int(host_lun, 16), 3745 'maskingview': self.data.masking_view_name_f, 3746 'array': self.data.array, 3747 'device_id': self.data.device_id} 3748 device_info_dict = self.common.initialize_connection(volume, 3749 connector) 3750 self.assertEqual(ref_dict, device_info_dict) 3751 3752 def test_initialize_connection_not_mapped(self): 3753 volume = self.data.test_volume 3754 connector = self.data.connector 3755 extra_specs = deepcopy(self.data.extra_specs_intervals_set) 3756 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 3757 masking_view_dict = self.common._populate_masking_dict( 3758 volume, connector, extra_specs) 3759 with mock.patch.object(self.common, 'find_host_lun_id', 3760 return_value=({}, False, [])): 3761 with mock.patch.object( 3762 self.common, '_attach_volume', return_value=( 3763 {}, self.data.port_group_name_f)): 3764 device_info_dict = self.common.initialize_connection(volume, 3765 connector) 3766 self.assertEqual({}, device_info_dict) 3767 self.common._attach_volume.assert_called_once_with( 3768 volume, connector, extra_specs, masking_view_dict, False) 3769 3770 @mock.patch.object(rest.VMAXRest, 'is_next_gen_array', 3771 return_value=True) 3772 @mock.patch.object(common.VMAXCommon, 'find_host_lun_id', 3773 return_value=({}, False, [])) 3774 @mock.patch.object(common.VMAXCommon, '_attach_volume', 3775 return_value=({}, VMAXCommonData.port_group_name_f)) 3776 def test_initialize_connection_not_mapped_next_gen(self, mock_attach, 3777 mock_id, mck_gen): 3778 volume = self.data.test_volume 3779 connector = self.data.connector 3780 device_info_dict = self.common.initialize_connection( 3781 volume, connector) 3782 self.assertEqual({}, device_info_dict) 3783 3784 def test_attach_volume_success(self): 3785 volume = self.data.test_volume 3786 connector = self.data.connector 3787 extra_specs = deepcopy(self.data.extra_specs) 3788 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 3789 masking_view_dict = self.common._populate_masking_dict( 3790 volume, connector, extra_specs) 3791 host_lun = (self.data.maskingview[0]['maskingViewConnection'][0] 3792 ['host_lun_address']) 3793 ref_dict = {'hostlunid': int(host_lun, 16), 3794 'maskingview': self.data.masking_view_name_f, 3795 'array': self.data.array, 3796 'device_id': self.data.device_id} 3797 with mock.patch.object(self.masking, 'setup_masking_view', 3798 return_value={ 3799 utils.PORTGROUPNAME: 3800 self.data.port_group_name_f}): 3801 device_info_dict, pg = self.common._attach_volume( 3802 volume, connector, extra_specs, masking_view_dict) 3803 self.assertEqual(ref_dict, device_info_dict) 3804 3805 @mock.patch.object(masking.VMAXMasking, 3806 'check_if_rollback_action_for_masking_required') 3807 @mock.patch.object(masking.VMAXMasking, 'setup_masking_view', 3808 return_value={}) 3809 @mock.patch.object(common.VMAXCommon, 'find_host_lun_id', 3810 return_value=({}, False, [])) 3811 def test_attach_volume_failed(self, mock_lun, mock_setup, mock_rollback): 3812 volume = self.data.test_volume 3813 connector = self.data.connector 3814 extra_specs = deepcopy(self.data.extra_specs) 3815 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 3816 masking_view_dict = self.common._populate_masking_dict( 3817 volume, connector, extra_specs) 3818 self.assertRaises(exception.VolumeBackendAPIException, 3819 self.common._attach_volume, volume, 3820 connector, extra_specs, 3821 masking_view_dict) 3822 device_id = self.data.device_id 3823 (mock_rollback.assert_called_once_with( 3824 self.data.array, volume, device_id, {})) 3825 3826 def test_terminate_connection(self): 3827 volume = self.data.test_volume 3828 connector = self.data.connector 3829 with mock.patch.object(self.common, '_unmap_lun'): 3830 self.common.terminate_connection(volume, connector) 3831 self.common._unmap_lun.assert_called_once_with( 3832 volume, connector) 3833 3834 @mock.patch.object(rest.VMAXRest, 'is_next_gen_array', return_value=True) 3835 @mock.patch.object(common.VMAXCommon, '_sync_check') 3836 @mock.patch.object(provision.VMAXProvision, 'extend_volume') 3837 def test_extend_volume_success(self, mock_extend, mock_sync, mock_newgen): 3838 volume = self.data.test_volume 3839 array = self.data.array 3840 device_id = self.data.device_id 3841 new_size = self.data.test_volume.size 3842 ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) 3843 ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 3844 with mock.patch.object(self.rest, 'is_vol_in_rep_session', 3845 side_effect=[(False, False, None), 3846 (False, True, None)]): 3847 self.common.extend_volume(volume, new_size) 3848 mock_extend.assert_called_once_with( 3849 array, device_id, new_size, ref_extra_specs) 3850 # Success, with snapshot, on new VMAX array 3851 mock_extend.reset_mock() 3852 self.common.extend_volume(volume, new_size) 3853 mock_extend.assert_called_once_with( 3854 array, device_id, new_size, ref_extra_specs) 3855 3856 def test_extend_volume_failed_snap_src(self): 3857 volume = self.data.test_volume 3858 new_size = self.data.test_volume.size 3859 with mock.patch.object(self.rest, 'is_vol_in_rep_session', 3860 return_value=(False, True, None)): 3861 self.assertRaises(exception.VolumeBackendAPIException, 3862 self.common.extend_volume, volume, new_size) 3863 3864 def test_extend_volume_failed_no_device_id(self): 3865 volume = self.data.test_volume 3866 new_size = self.data.test_volume.size 3867 with mock.patch.object(self.common, '_find_device_on_array', 3868 return_value=None): 3869 self.assertRaises(exception.VolumeBackendAPIException, 3870 self.common.extend_volume, volume, new_size) 3871 3872 def test_extend_volume_failed_wrong_size(self): 3873 volume = self.data.test_volume 3874 new_size = 1 3875 self.assertRaises(exception.VolumeBackendAPIException, 3876 self.common.extend_volume, volume, new_size) 3877 3878 def test_update_volume_stats(self): 3879 data = self.common.update_volume_stats() 3880 self.assertEqual('CommonTests', data['volume_backend_name']) 3881 3882 def test_update_volume_stats_no_wlp(self): 3883 with mock.patch.object(self.common, '_update_srp_stats', 3884 return_value=('123s#SRP_1#None#None', 3885 100, 90, 90, 10)): 3886 data = self.common.update_volume_stats() 3887 self.assertEqual('CommonTests', data['volume_backend_name']) 3888 3889 def test_set_config_file_and_get_extra_specs(self): 3890 volume = self.data.test_volume 3891 extra_specs, config_file, qos_specs = ( 3892 self.common._set_config_file_and_get_extra_specs(volume)) 3893 self.assertEqual(self.data.vol_type_extra_specs, extra_specs) 3894 self.assertEqual(self.fake_xml, config_file) 3895 3896 def test_set_config_file_and_get_extra_specs_no_specs(self): 3897 volume = self.data.test_volume 3898 ref_config = '/etc/cinder/cinder_dell_emc_config.xml' 3899 with mock.patch.object(self.utils, 'get_volumetype_extra_specs', 3900 return_value=None): 3901 extra_specs, config_file, qos_specs = ( 3902 self.common._set_config_file_and_get_extra_specs(volume)) 3903 self.assertIsNone(extra_specs) 3904 self.assertEqual(ref_config, config_file) 3905 3906 def test_update_srp_stats_with_wl(self): 3907 with mock.patch.object(self.rest, 'get_srp_by_name', 3908 return_value=self.data.srp_details): 3909 location_info, __, __, __, __ = self.common._update_srp_stats( 3910 self.data.array_info_wl) 3911 self.assertEqual(location_info, '000197800123#SRP_1#Diamond#OLTP') 3912 3913 def test_update_srp_stats_no_wl(self): 3914 with mock.patch.object(self.rest, 'get_srp_by_name', 3915 return_value=self.data.srp_details): 3916 location_info, __, __, __, __ = self.common._update_srp_stats( 3917 self.data.array_info_no_wl) 3918 self.assertEqual(location_info, '000197800123#SRP_1#Diamond') 3919 3920 def test_find_device_on_array_success(self): 3921 volume = self.data.test_volume 3922 extra_specs = self.data.extra_specs 3923 ref_device_id = self.data.device_id 3924 founddevice_id = self.common._find_device_on_array(volume, extra_specs) 3925 self.assertEqual(ref_device_id, founddevice_id) 3926 3927 def test_find_device_on_array_provider_location_not_string(self): 3928 volume = fake_volume.fake_volume_obj( 3929 context='cxt', provider_location=None) 3930 extra_specs = self.data.extra_specs 3931 founddevice_id = self.common._find_device_on_array( 3932 volume, extra_specs) 3933 self.assertIsNone(founddevice_id) 3934 3935 def test_find_legacy_device_on_array(self): 3936 volume = self.data.test_legacy_vol 3937 extra_specs = self.data.extra_specs 3938 ref_device_id = self.data.device_id 3939 founddevice_id = self.common._find_device_on_array(volume, extra_specs) 3940 self.assertEqual(ref_device_id, founddevice_id) 3941 3942 def test_find_host_lun_id_attached(self): 3943 volume = self.data.test_volume 3944 extra_specs = self.data.extra_specs 3945 host = 'HostX' 3946 host_lun = (self.data.maskingview[0]['maskingViewConnection'][0] 3947 ['host_lun_address']) 3948 ref_masked = {'hostlunid': int(host_lun, 16), 3949 'maskingview': self.data.masking_view_name_f, 3950 'array': self.data.array, 3951 'device_id': self.data.device_id} 3952 maskedvols, __, __ = self.common.find_host_lun_id( 3953 volume, host, extra_specs) 3954 self.assertEqual(ref_masked, maskedvols) 3955 3956 def test_find_host_lun_id_not_attached(self): 3957 volume = self.data.test_volume 3958 extra_specs = self.data.extra_specs 3959 host = 'HostX' 3960 with mock.patch.object(self.rest, 'find_mv_connections_for_vol', 3961 return_value=None): 3962 maskedvols, __, __ = self.common.find_host_lun_id( 3963 volume, host, extra_specs) 3964 self.assertEqual({}, maskedvols) 3965 3966 @mock.patch.object(common.VMAXCommon, 'get_remote_target_device', 3967 return_value=VMAXCommonData.device_id2) 3968 def test_find_host_lun_id_rep_extra_specs(self, mock_tgt): 3969 self.common.find_host_lun_id( 3970 self.data.test_volume, 'HostX', 3971 self.data.extra_specs, self.data.rep_extra_specs) 3972 mock_tgt.assert_called_once() 3973 3974 def test_get_masking_views_from_volume(self): 3975 array = self.data.array 3976 device_id = self.data.device_id 3977 host = 'HostX' 3978 ref_mv_list = [self.data.masking_view_name_f] 3979 maskingview_list, __ = self.common.get_masking_views_from_volume( 3980 array, self.data.test_volume, device_id, host) 3981 self.assertEqual(ref_mv_list, maskingview_list) 3982 # is metro 3983 with mock.patch.object(self.utils, 'is_metro_device', 3984 return_value=True): 3985 __, is_metro = self.common.get_masking_views_from_volume( 3986 array, self.data.test_volume, device_id, host) 3987 self.assertTrue(is_metro) 3988 3989 def test_get_masking_views_from_volume_wrong_host(self): 3990 array = self.data.array 3991 device_id = self.data.device_id 3992 host = 'DifferentHost' 3993 maskingview_list, __ = self.common.get_masking_views_from_volume( 3994 array, self.data.test_volume, device_id, host) 3995 self.assertEqual([], maskingview_list) 3996 3997 def test_find_host_lun_id_no_host_check(self): 3998 volume = self.data.test_volume 3999 extra_specs = self.data.extra_specs 4000 host_lun = (self.data.maskingview[0]['maskingViewConnection'][0] 4001 ['host_lun_address']) 4002 ref_masked = {'hostlunid': int(host_lun, 16), 4003 'maskingview': self.data.masking_view_name_f, 4004 'array': self.data.array, 4005 'device_id': self.data.device_id} 4006 maskedvols, __, __ = self.common.find_host_lun_id( 4007 volume, None, extra_specs) 4008 self.assertEqual(ref_masked, maskedvols) 4009 4010 def test_register_config_file_from_config_group_exists(self): 4011 config_group_name = 'CommonTests' 4012 config_file = self.common._register_config_file_from_config_group( 4013 config_group_name) 4014 self.assertEqual(self.fake_xml, config_file) 4015 4016 def test_register_config_file_from_config_group_does_not_exist(self): 4017 config_group_name = 'IncorrectName' 4018 self.assertRaises(exception.VolumeBackendAPIException, 4019 self.common._register_config_file_from_config_group, 4020 config_group_name) 4021 4022 def test_initial_setup_success(self): 4023 volume = self.data.test_volume 4024 ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) 4025 ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4026 extra_specs = self.common._initial_setup(volume) 4027 self.assertEqual(ref_extra_specs, extra_specs) 4028 4029 def test_initial_setup_failed(self): 4030 volume = self.data.test_volume 4031 with mock.patch.object(self.utils, 'parse_file_to_get_array_map', 4032 return_value=None): 4033 self.assertRaises(exception.VolumeBackendAPIException, 4034 self.common._initial_setup, volume) 4035 4036 @mock.patch.object(common.VMAXCommon, 'get_remote_target_device', 4037 return_value=VMAXCommonData.device_id2) 4038 def test_populate_masking_dict(self, mock_tgt): 4039 volume = self.data.test_volume 4040 connector = self.data.connector 4041 extra_specs = deepcopy(self.data.extra_specs) 4042 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4043 extra_specs[utils.WORKLOAD] = self.data.workload 4044 ref_mv_dict = self.data.masking_view_dict 4045 self.common.nextGen = False 4046 masking_view_dict = self.common._populate_masking_dict( 4047 volume, connector, extra_specs) 4048 self.assertEqual(ref_mv_dict, masking_view_dict) 4049 # Metro volume, pass in rep_extra_specs and retrieve target device 4050 rep_extra_specs = deepcopy(self.data.rep_extra_specs) 4051 rep_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4052 self.common._populate_masking_dict( 4053 volume, connector, extra_specs, rep_extra_specs) 4054 mock_tgt.assert_called_once() 4055 # device_id is None 4056 with mock.patch.object(self.common, '_find_device_on_array', 4057 return_value=None): 4058 self.assertRaises(exception.VolumeBackendAPIException, 4059 self.common._populate_masking_dict, 4060 volume, connector, extra_specs) 4061 4062 def test_populate_masking_dict_no_slo(self): 4063 volume = self.data.test_volume 4064 connector = self.data.connector 4065 extra_specs = { 4066 'slo': None, 4067 'workload': None, 4068 'srp': self.data.srp, 4069 'array': self.data.array, 4070 utils.PORTGROUPNAME: self.data.port_group_name_f} 4071 ref_mv_dict = self.data.masking_view_dict_no_slo 4072 masking_view_dict = self.common._populate_masking_dict( 4073 volume, connector, extra_specs) 4074 self.assertEqual(ref_mv_dict, masking_view_dict) 4075 4076 def test_populate_masking_dict_compr_disabled(self): 4077 volume = self.data.test_volume 4078 connector = self.data.connector 4079 extra_specs = deepcopy(self.data.extra_specs) 4080 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4081 extra_specs[utils.DISABLECOMPRESSION] = "true" 4082 ref_mv_dict = self.data.masking_view_dict_compression_disabled 4083 extra_specs[utils.WORKLOAD] = self.data.workload 4084 masking_view_dict = self.common._populate_masking_dict( 4085 volume, connector, extra_specs) 4086 self.assertEqual(ref_mv_dict, masking_view_dict) 4087 4088 def test_populate_masking_dict_next_gen(self): 4089 volume = self.data.test_volume 4090 connector = self.data.connector 4091 extra_specs = deepcopy(self.data.extra_specs) 4092 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4093 self.common.nextGen = True 4094 masking_view_dict = self.common._populate_masking_dict( 4095 volume, connector, extra_specs) 4096 self.assertEqual('NONE', masking_view_dict[utils.WORKLOAD]) 4097 4098 def test_create_cloned_volume(self): 4099 volume = self.data.test_clone_volume 4100 source_volume = self.data.test_volume 4101 extra_specs = self.data.extra_specs 4102 ref_dict = self.data.provider_location 4103 clone_dict = self.common._create_cloned_volume( 4104 volume, source_volume, extra_specs) 4105 self.assertEqual(ref_dict, clone_dict) 4106 4107 def test_create_cloned_volume_is_snapshot(self): 4108 volume = self.data.test_snapshot 4109 source_volume = self.data.test_volume 4110 extra_specs = self.data.extra_specs 4111 ref_dict = self.data.snap_location 4112 clone_dict = self.common._create_cloned_volume( 4113 volume, source_volume, extra_specs, True, False) 4114 self.assertEqual(ref_dict, clone_dict) 4115 4116 def test_create_cloned_volume_from_snapshot(self): 4117 volume = self.data.test_clone_volume 4118 source_volume = self.data.test_snapshot 4119 extra_specs = self.data.extra_specs 4120 ref_dict = self.data.provider_location 4121 clone_dict = self.common._create_cloned_volume( 4122 volume, source_volume, extra_specs, False, True) 4123 self.assertEqual(ref_dict, clone_dict) 4124 4125 def test_create_cloned_volume_not_licenced(self): 4126 volume = self.data.test_clone_volume 4127 source_volume = self.data.test_volume 4128 extra_specs = self.data.extra_specs 4129 with mock.patch.object(self.rest, 'is_snapvx_licensed', 4130 return_value=False): 4131 self.assertRaises(exception.VolumeBackendAPIException, 4132 self.common._create_cloned_volume, 4133 volume, source_volume, extra_specs) 4134 4135 def test_parse_snap_info_found(self): 4136 ref_device_id = self.data.device_id 4137 ref_snap_name = self.data.snap_location['snap_name'] 4138 sourcedevice_id, foundsnap_name = self.common._parse_snap_info( 4139 self.data.array, self.data.test_snapshot) 4140 self.assertEqual(ref_device_id, sourcedevice_id) 4141 self.assertEqual(ref_snap_name, foundsnap_name) 4142 4143 def test_parse_snap_info_not_found(self): 4144 ref_snap_name = None 4145 with mock.patch.object(self.rest, 'get_volume_snap', 4146 return_value=None): 4147 __, foundsnap_name = self.common._parse_snap_info( 4148 self.data.array, self.data.test_snapshot) 4149 self.assertIsNone(ref_snap_name, foundsnap_name) 4150 4151 def test_parse_snap_info_exception(self): 4152 with mock.patch.object( 4153 self.rest, 'get_volume_snap', 4154 side_effect=exception.VolumeBackendAPIException): 4155 __, foundsnap_name = self.common._parse_snap_info( 4156 self.data.array, self.data.test_snapshot) 4157 self.assertIsNone(foundsnap_name) 4158 4159 def test_parse_snap_info_provider_location_not_string(self): 4160 snapshot = fake_snapshot.fake_snapshot_obj( 4161 context='ctxt', provider_loaction={'not': 'string'}) 4162 sourcedevice_id, foundsnap_name = self.common._parse_snap_info( 4163 self.data.array, snapshot) 4164 self.assertIsNone(foundsnap_name) 4165 4166 def test_create_snapshot_success(self): 4167 array = self.data.array 4168 snapshot = self.data.test_snapshot 4169 source_device_id = self.data.device_id 4170 extra_specs = self.data.extra_specs 4171 ref_dict = {'snap_name': self.data.test_snapshot_snap_name, 4172 'source_id': self.data.device_id} 4173 snap_dict = self.common._create_snapshot( 4174 array, snapshot, source_device_id, extra_specs) 4175 self.assertEqual(ref_dict, snap_dict) 4176 4177 def test_create_snapshot_exception(self): 4178 array = self.data.array 4179 snapshot = self.data.test_snapshot 4180 source_device_id = self.data.device_id 4181 extra_specs = self.data.extra_specs 4182 with mock.patch.object( 4183 self.provision, 'create_volume_snapvx', 4184 side_effect=exception.VolumeBackendAPIException): 4185 self.assertRaises(exception.VolumeBackendAPIException, 4186 self.common._create_snapshot, 4187 array, snapshot, source_device_id, extra_specs) 4188 4189 @mock.patch.object(masking.VMAXMasking, 'remove_vol_from_storage_group') 4190 def test_delete_volume_from_srp(self, mock_rm): 4191 array = self.data.array 4192 device_id = self.data.device_id 4193 volume_name = self.data.test_volume.name 4194 ref_extra_specs = self.data.extra_specs_intervals_set 4195 ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4196 volume = self.data.test_volume 4197 with mock.patch.object(self.common, '_sync_check'): 4198 with mock.patch.object(self.common, '_delete_from_srp'): 4199 self.common._delete_volume(volume) 4200 self.common._delete_from_srp.assert_called_once_with( 4201 array, device_id, volume_name, ref_extra_specs) 4202 4203 def test_delete_volume_not_found(self): 4204 volume = self.data.test_volume 4205 with mock.patch.object(self.common, '_find_device_on_array', 4206 return_value=None): 4207 with mock.patch.object(self.common, '_delete_from_srp'): 4208 self.common._delete_volume(volume) 4209 self.common._delete_from_srp.assert_not_called() 4210 4211 def test_create_volume_success(self): 4212 volume_name = '1' 4213 volume_size = self.data.test_volume.size 4214 extra_specs = self.data.extra_specs 4215 ref_dict = self.data.provider_location 4216 volume_dict = self.common._create_volume( 4217 volume_name, volume_size, extra_specs) 4218 self.assertEqual(ref_dict, volume_dict) 4219 4220 def test_create_volume_success_next_gen(self): 4221 volume_name = '1' 4222 volume_size = self.data.test_volume.size 4223 extra_specs = self.data.extra_specs 4224 self.common.nextGen = True 4225 with mock.patch.object(self.utils, 'is_compression_disabled', 4226 return_value=True): 4227 with mock.patch.object(self.rest, 'is_next_gen_array', 4228 return_value=True): 4229 with mock.patch.object(self.masking, 4230 'get_or_create_default_storage_group'): 4231 self.common._create_volume( 4232 volume_name, volume_size, extra_specs) 4233 (self.masking.get_or_create_default_storage_group 4234 .assert_called_once_with(extra_specs['array'], 4235 extra_specs[utils.SRP], 4236 extra_specs[utils.SLO], 4237 'NONE', extra_specs, True)) 4238 4239 def test_create_volume_failed(self): 4240 volume_name = self.data.test_volume.name 4241 volume_size = self.data.test_volume.size 4242 extra_specs = self.data.extra_specs 4243 with mock.patch.object(self.masking, 4244 'get_or_create_default_storage_group', 4245 return_value=self.data.failed_resource): 4246 with mock.patch.object(self.rest, 'delete_storage_group'): 4247 # path 1: not last vol in sg 4248 with mock.patch.object(self.rest, 'get_num_vols_in_sg', 4249 return_value=2): 4250 self.assertRaises(exception.VolumeBackendAPIException, 4251 self.common._create_volume, 4252 volume_name, volume_size, extra_specs) 4253 self.rest.delete_storage_group.assert_not_called() 4254 # path 2: last vol in sg, delete sg 4255 with mock.patch.object(self.rest, 'get_num_vols_in_sg', 4256 return_value=0): 4257 self.assertRaises(exception.VolumeBackendAPIException, 4258 self.common._create_volume, 4259 volume_name, volume_size, extra_specs) 4260 (self.rest.delete_storage_group. 4261 assert_called_once_with(self.data.array, 4262 self.data.failed_resource)) 4263 4264 def test_create_volume_incorrect_slo(self): 4265 volume_name = self.data.test_volume.name 4266 volume_size = self.data.test_volume.size 4267 extra_specs = {'slo': 'Diamondz', 4268 'workload': 'DSSSS', 4269 'srp': self.data.srp, 4270 'array': self.data.array} 4271 self.assertRaises( 4272 exception.VolumeBackendAPIException, 4273 self.common._create_volume, 4274 volume_name, volume_size, extra_specs) 4275 4276 def test_set_vmax_extra_specs(self): 4277 srp_record = self.utils.parse_file_to_get_array_map( 4278 self.fake_xml) 4279 extra_specs = self.common._set_vmax_extra_specs( 4280 self.data.vol_type_extra_specs, srp_record) 4281 ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) 4282 ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4283 self.assertEqual(ref_extra_specs, extra_specs) 4284 4285 def test_set_vmax_extra_specs_no_srp_name(self): 4286 with mock.patch.object(self.rest, 'get_slo_list', 4287 return_value=[]): 4288 extra_specs = self.common._set_vmax_extra_specs( 4289 {}, self.data.array_info_wl) 4290 self.assertIsNone(extra_specs['slo']) 4291 4292 def test_set_vmax_extra_specs_compr_disabled(self): 4293 with mock.patch.object(self.rest, 'is_compression_capable', 4294 return_value=True): 4295 srp_record = self.utils.parse_file_to_get_array_map( 4296 self.fake_xml) 4297 extra_specs = self.common._set_vmax_extra_specs( 4298 self.data.vol_type_extra_specs_compr_disabled, srp_record) 4299 ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) 4300 ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4301 ref_extra_specs[utils.DISABLECOMPRESSION] = "true" 4302 self.assertEqual(ref_extra_specs, extra_specs) 4303 4304 def test_set_vmax_extra_specs_compr_disabled_not_compr_capable(self): 4305 srp_record = self.utils.parse_file_to_get_array_map( 4306 self.fake_xml) 4307 extra_specs = self.common._set_vmax_extra_specs( 4308 self.data.vol_type_extra_specs_compr_disabled, srp_record) 4309 ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) 4310 ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4311 self.assertEqual(ref_extra_specs, extra_specs) 4312 4313 def test_set_vmax_extra_specs_portgroup_as_spec(self): 4314 srp_record = self.utils.parse_file_to_get_array_map( 4315 self.fake_xml) 4316 extra_specs = self.common._set_vmax_extra_specs( 4317 {utils.PORTGROUPNAME: 'extra_spec_pg'}, srp_record) 4318 self.assertEqual('extra_spec_pg', extra_specs[utils.PORTGROUPNAME]) 4319 4320 def test_set_vmax_extra_specs_no_portgroup_set(self): 4321 fake_xml = FakeXML().create_fake_config_file( 4322 'test_no_pg_set', '') 4323 srp_record = self.utils.parse_file_to_get_array_map(fake_xml) 4324 self.assertRaises(exception.VolumeBackendAPIException, 4325 self.common._set_vmax_extra_specs, 4326 {}, srp_record) 4327 4328 def test_set_vmax_extra_specs_next_gen(self): 4329 srp_record = self.utils.parse_file_to_get_array_map( 4330 self.fake_xml) 4331 self.common.nextGen = True 4332 extra_specs = self.common._set_vmax_extra_specs( 4333 self.data.vol_type_extra_specs, srp_record) 4334 ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) 4335 ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4336 self.assertEqual('NONE', extra_specs[utils.WORKLOAD]) 4337 4338 def test_delete_volume_from_srp_success(self): 4339 array = self.data.array 4340 device_id = self.data.device_id 4341 volume_name = self.data.test_volume.name 4342 extra_specs = self.data.extra_specs 4343 with mock.patch.object( 4344 self.provision, 'delete_volume_from_srp') as mock_del: 4345 self.common._delete_from_srp(array, device_id, volume_name, 4346 extra_specs) 4347 mock_del.assert_called_once_with(array, device_id, volume_name) 4348 4349 def test_delete_volume_from_srp_failed(self): 4350 array = self.data.array 4351 device_id = self.data.failed_resource 4352 volume_name = self.data.test_volume.name 4353 extra_specs = self.data.extra_specs 4354 with mock.patch.object(self.masking, 4355 'add_volume_to_default_storage_group'): 4356 self.assertRaises(exception.VolumeBackendAPIException, 4357 self.common._delete_from_srp, array, 4358 device_id, volume_name, extra_specs) 4359 (self.masking.add_volume_to_default_storage_group. 4360 assert_called_once_with( 4361 array, device_id, volume_name, extra_specs)) 4362 4363 @mock.patch.object(utils.VMAXUtils, 'is_replication_enabled', 4364 side_effect=[False, True]) 4365 def test_remove_vol_and_cleanup_replication(self, mock_rep_enabled): 4366 array = self.data.array 4367 device_id = self.data.device_id 4368 volume = self.data.test_volume 4369 volume_name = self.data.test_volume.name 4370 extra_specs = self.data.extra_specs 4371 with mock.patch.object( 4372 self.masking, 'remove_and_reset_members') as mock_rm: 4373 with mock.patch.object( 4374 self.common, 'cleanup_lun_replication') as mock_clean: 4375 self.common._remove_vol_and_cleanup_replication( 4376 array, device_id, volume_name, extra_specs, volume) 4377 mock_rm.assert_called_once_with( 4378 array, volume, device_id, volume_name, extra_specs, False) 4379 mock_clean.assert_not_called() 4380 self.common._remove_vol_and_cleanup_replication( 4381 array, device_id, volume_name, extra_specs, volume) 4382 mock_clean.assert_called_once_with( 4383 volume, volume_name, device_id, extra_specs) 4384 4385 @mock.patch.object(utils.VMAXUtils, 'is_volume_failed_over', 4386 side_effect=[True, False]) 4387 @mock.patch.object(common.VMAXCommon, '_get_replication_extra_specs', 4388 return_value=VMAXCommonData.rep_extra_specs) 4389 def test_get_target_wwns_from_masking_view(self, mock_rep_specs, mock_fo): 4390 ref_wwns = [self.data.wwnn1] 4391 for x in range(0, 2): 4392 target_wwns = self.common._get_target_wwns_from_masking_view( 4393 self.data.device_id, self.data.connector['host'], 4394 self.data.extra_specs) 4395 self.assertEqual(ref_wwns, target_wwns) 4396 4397 @mock.patch.object(common.VMAXCommon, 'get_port_group_from_masking_view', 4398 return_value=None) 4399 def test_get_target_wwns_from_masking_view_no_mv(self, mock_pgmv): 4400 with mock.patch.object(self.common, '_get_masking_views_from_volume', 4401 return_value=[]): 4402 self.common._get_target_wwns_from_masking_view( 4403 self.data.device_id, self.data.connector['host'], 4404 self.data.extra_specs) 4405 mock_pgmv.assert_not_called() 4406 4407 @mock.patch.object(common.VMAXCommon, '_get_replication_extra_specs', 4408 return_value=VMAXCommonData.rep_extra_specs) 4409 @mock.patch.object(common.VMAXCommon, 'get_remote_target_device', 4410 return_value=(VMAXCommonData.device_id2,)) 4411 @mock.patch.object(utils.VMAXUtils, 'is_metro_device', 4412 side_effect=[False, True]) 4413 def test_get_target_wwns(self, mock_metro, mock_tgt, mock_specs): 4414 __, metro_wwns = self.common.get_target_wwns_from_masking_view( 4415 self.data.test_volume, self.data.connector) 4416 self.assertEqual([], metro_wwns) 4417 # Is metro volume 4418 __, metro_wwns = self.common.get_target_wwns_from_masking_view( 4419 self.data.test_volume, self.data.connector) 4420 self.assertEqual([self.data.wwnn1], metro_wwns) 4421 4422 def test_get_port_group_from_masking_view(self): 4423 array = self.data.array 4424 maskingview_name = self.data.masking_view_name_f 4425 with mock.patch.object(self.rest, 4426 'get_element_from_masking_view'): 4427 self.common.get_port_group_from_masking_view( 4428 array, maskingview_name) 4429 self.rest.get_element_from_masking_view.assert_called_once_with( 4430 array, maskingview_name, portgroup=True) 4431 4432 def test_get_initiator_group_from_masking_view(self): 4433 array = self.data.array 4434 maskingview_name = self.data.masking_view_name_f 4435 with mock.patch.object(self.rest, 4436 'get_element_from_masking_view'): 4437 self.common.get_initiator_group_from_masking_view( 4438 array, maskingview_name) 4439 self.rest.get_element_from_masking_view.assert_called_once_with( 4440 array, maskingview_name, host=True) 4441 4442 def test_get_common_masking_views(self): 4443 array = self.data.array 4444 portgroup_name = self.data.port_group_name_f 4445 initiator_group_name = self.data.initiatorgroup_name_f 4446 with mock.patch.object(self.rest, 'get_common_masking_views'): 4447 self.common.get_common_masking_views( 4448 array, portgroup_name, initiator_group_name) 4449 self.rest.get_common_masking_views.assert_called_once_with( 4450 array, portgroup_name, initiator_group_name) 4451 4452 def test_get_ip_and_iqn(self): 4453 ref_ip_iqn = [{'iqn': self.data.initiator, 4454 'ip': self.data.ip}] 4455 port = self.data.portgroup[1]['symmetrixPortKey'][0]['portId'] 4456 ip_iqn_list = self.common._get_ip_and_iqn(self.data.array, port) 4457 self.assertEqual(ref_ip_iqn, ip_iqn_list) 4458 4459 def test_find_ip_and_iqns(self): 4460 ref_ip_iqn = [{'iqn': self.data.initiator, 4461 'ip': self.data.ip}] 4462 ip_iqn_list = self.common._find_ip_and_iqns( 4463 self.data.array, self.data.port_group_name_i) 4464 self.assertEqual(ref_ip_iqn, ip_iqn_list) 4465 4466 def test_create_replica_snap_name(self): 4467 array = self.data.array 4468 clone_volume = self.data.test_clone_volume 4469 source_device_id = self.data.device_id 4470 snap_name = self.data.snap_location['snap_name'] 4471 ref_dict = self.data.provider_location 4472 clone_dict = self.common._create_replica( 4473 array, clone_volume, source_device_id, 4474 self.data.extra_specs, snap_name) 4475 self.assertEqual(ref_dict, clone_dict) 4476 4477 def test_create_replica_no_snap_name(self): 4478 array = self.data.array 4479 clone_volume = self.data.test_clone_volume 4480 source_device_id = self.data.device_id 4481 snap_name = "temp-" + source_device_id + clone_volume.id 4482 ref_dict = self.data.provider_location 4483 with mock.patch.object(self.utils, 'get_temp_snap_name', 4484 return_value=snap_name): 4485 clone_dict = self.common._create_replica( 4486 array, clone_volume, source_device_id, 4487 self.data.extra_specs) 4488 self.assertEqual(ref_dict, clone_dict) 4489 self.utils.get_temp_snap_name.assert_called_once_with( 4490 ('OS-' + clone_volume.id), source_device_id) 4491 4492 def test_create_replica_failed_cleanup_target(self): 4493 array = self.data.array 4494 clone_volume = self.data.test_clone_volume 4495 device_id = self.data.device_id 4496 snap_name = self.data.failed_resource 4497 clone_name = 'OS-' + clone_volume.id 4498 extra_specs = self.data.extra_specs 4499 with mock.patch.object(self.common, '_cleanup_target'): 4500 self.assertRaises( 4501 exception.VolumeBackendAPIException, 4502 self.common._create_replica, array, clone_volume, 4503 device_id, self.data.extra_specs, snap_name) 4504 self.common._cleanup_target.assert_called_once_with( 4505 array, device_id, device_id, clone_name, 4506 snap_name, extra_specs) 4507 4508 def test_create_replica_failed_no_target(self): 4509 array = self.data.array 4510 clone_volume = self.data.test_clone_volume 4511 source_device_id = self.data.device_id 4512 snap_name = self.data.failed_resource 4513 with mock.patch.object(self.common, '_create_volume', 4514 return_value={'device_id': None}): 4515 with mock.patch.object(self.common, '_cleanup_target'): 4516 self.assertRaises( 4517 exception.VolumeBackendAPIException, 4518 self.common._create_replica, array, clone_volume, 4519 source_device_id, self.data.extra_specs, snap_name) 4520 self.common._cleanup_target.assert_not_called() 4521 4522 @mock.patch.object( 4523 masking.VMAXMasking, 4524 'remove_and_reset_members') 4525 def test_cleanup_target_sync_present(self, mock_remove): 4526 array = self.data.array 4527 clone_volume = self.data.test_clone_volume 4528 source_device_id = self.data.device_id 4529 target_device_id = self.data.device_id2 4530 snap_name = self.data.failed_resource 4531 clone_name = clone_volume.name 4532 extra_specs = self.data.extra_specs 4533 with mock.patch.object(self.rest, 'get_sync_session', 4534 return_value='session'): 4535 with mock.patch.object(self.provision, 4536 'break_replication_relationship'): 4537 self.common._cleanup_target( 4538 array, target_device_id, source_device_id, 4539 clone_name, snap_name, extra_specs) 4540 (self.provision.break_replication_relationship. 4541 assert_called_with( 4542 array, target_device_id, source_device_id, 4543 snap_name, extra_specs)) 4544 4545 def test_cleanup_target_no_sync(self): 4546 array = self.data.array 4547 clone_volume = self.data.test_clone_volume 4548 source_device_id = self.data.device_id 4549 target_device_id = self.data.device_id2 4550 snap_name = self.data.failed_resource 4551 clone_name = clone_volume.name 4552 extra_specs = self.data.extra_specs 4553 with mock.patch.object(self.rest, 'get_sync_session', 4554 return_value=None): 4555 with mock.patch.object(self.common, 4556 '_delete_from_srp'): 4557 self.common._cleanup_target( 4558 array, target_device_id, source_device_id, 4559 clone_name, snap_name, extra_specs) 4560 self.common._delete_from_srp.assert_called_once_with( 4561 array, target_device_id, clone_name, 4562 extra_specs) 4563 4564 @mock.patch.object( 4565 provision.VMAXProvision, 4566 'delete_volume_snap') 4567 @mock.patch.object( 4568 provision.VMAXProvision, 4569 'break_replication_relationship') 4570 def test_sync_check_temp_snap(self, mock_break, mock_delete): 4571 array = self.data.array 4572 device_id = self.data.device_id 4573 target = self.data.volume_details[1]['volumeId'] 4574 volume_name = self.data.test_volume.name 4575 extra_specs = self.data.extra_specs 4576 snap_name = 'temp-1' 4577 with mock.patch.object(self.rest, 'get_volume_snap', 4578 return_value=snap_name): 4579 self.common._sync_check(array, device_id, volume_name, 4580 extra_specs) 4581 mock_break.assert_called_with( 4582 array, target, device_id, snap_name, extra_specs) 4583 mock_delete.assert_called_with(array, snap_name, device_id) 4584 # Delete legacy temp snap 4585 mock_delete.reset_mock() 4586 snap_name2 = 'EMC_SMI_12345' 4587 sessions = [{'source_vol': device_id, 4588 'snap_name': snap_name2, 4589 'target_vol_list': []}] 4590 with mock.patch.object(self.rest, 'find_snap_vx_sessions', 4591 return_value=sessions): 4592 with mock.patch.object(self.rest, 'get_volume_snap', 4593 return_value=snap_name2): 4594 self.common._sync_check(array, device_id, volume_name, 4595 extra_specs) 4596 mock_delete.assert_called_once_with( 4597 array, snap_name2, device_id) 4598 4599 @mock.patch.object( 4600 provision.VMAXProvision, 4601 'delete_volume_snap') 4602 @mock.patch.object( 4603 provision.VMAXProvision, 4604 'break_replication_relationship') 4605 def test_sync_check_not_temp_snap(self, mock_break, mock_delete): 4606 array = self.data.array 4607 device_id = self.data.device_id 4608 target = self.data.volume_details[1]['volumeId'] 4609 volume_name = self.data.test_volume.name 4610 extra_specs = self.data.extra_specs 4611 snap_name = 'OS-1' 4612 sessions = [{'source_vol': device_id, 4613 'snap_name': snap_name, 4614 'target_vol_list': [target]}] 4615 with mock.patch.object(self.rest, 'find_snap_vx_sessions', 4616 return_value=sessions): 4617 self.common._sync_check(array, device_id, volume_name, 4618 extra_specs) 4619 mock_break.assert_called_with( 4620 array, target, device_id, snap_name, extra_specs) 4621 mock_delete.assert_not_called() 4622 4623 @mock.patch.object( 4624 provision.VMAXProvision, 4625 'break_replication_relationship') 4626 def test_sync_check_no_sessions(self, mock_break): 4627 array = self.data.array 4628 device_id = self.data.device_id 4629 volume_name = self.data.test_volume.name 4630 extra_specs = self.data.extra_specs 4631 with mock.patch.object(self.rest, 'find_snap_vx_sessions', 4632 return_value=None): 4633 self.common._sync_check(array, device_id, volume_name, 4634 extra_specs) 4635 mock_break.assert_not_called() 4636 4637 def test_manage_existing_success(self): 4638 external_ref = {u'source-name': u'00002'} 4639 provider_location = {'device_id': u'00002', 'array': u'000197800123'} 4640 ref_update = {'provider_location': six.text_type(provider_location)} 4641 with mock.patch.object( 4642 self.common, '_check_lun_valid_for_cinder_management'): 4643 model_update = self.common.manage_existing( 4644 self.data.test_volume, external_ref) 4645 self.assertEqual(ref_update, model_update) 4646 4647 @mock.patch.object( 4648 rest.VMAXRest, 'get_masking_views_from_storage_group', 4649 return_value=None) 4650 @mock.patch.object( 4651 rest.VMAXRest, 'is_vol_in_rep_session', 4652 return_value=(False, False, None)) 4653 def test_check_lun_valid_for_cinder_management(self, mock_rep, mock_mv): 4654 external_ref = {u'source-name': u'00003'} 4655 self.common._check_lun_valid_for_cinder_management( 4656 self.data.array, self.data.device_id3, 4657 self.data.test_volume.id, external_ref) 4658 4659 @mock.patch.object( 4660 rest.VMAXRest, 'get_volume', 4661 side_effect=[ 4662 None, 4663 VMAXCommonData.volume_details[2], 4664 VMAXCommonData.volume_details[2], 4665 VMAXCommonData.volume_details[1]]) 4666 @mock.patch.object( 4667 rest.VMAXRest, 'get_masking_views_from_storage_group', 4668 side_effect=[VMAXCommonData.sg_details[1]['maskingview'], 4669 None]) 4670 @mock.patch.object(rest.VMAXRest, 'get_storage_groups_from_volume', 4671 return_value=[VMAXCommonData.defaultstoragegroup_name]) 4672 @mock.patch.object(rest.VMAXRest, 'is_vol_in_rep_session', 4673 side_effect=[(True, False, []), (False, False, None)]) 4674 def test_check_lun_valid_for_cinder_management_exception( 4675 self, mock_rep, mock_sg, mock_mvs, mock_get_vol): 4676 external_ref = {u'source-name': u'00003'} 4677 for x in range(0, 3): 4678 self.assertRaises( 4679 exception.ManageExistingInvalidReference, 4680 self.common._check_lun_valid_for_cinder_management, 4681 self.data.array, self.data.device_id3, 4682 self.data.test_volume.id, external_ref) 4683 self.assertRaises(exception.ManageExistingAlreadyManaged, 4684 self.common._check_lun_valid_for_cinder_management, 4685 self.data.array, self.data.device_id3, 4686 self.data.test_volume.id, external_ref) 4687 4688 def test_manage_existing_get_size(self): 4689 external_ref = {u'source-name': u'00001'} 4690 size = self.common.manage_existing_get_size( 4691 self.data.test_volume, external_ref) 4692 self.assertEqual(2, size) 4693 4694 def test_manage_existing_get_size_exception(self): 4695 external_ref = {u'source-name': u'00001'} 4696 with mock.patch.object(self.rest, 'get_size_of_device_on_array', 4697 return_value=3.5): 4698 self.assertRaises(exception.ManageExistingInvalidReference, 4699 self.common.manage_existing_get_size, 4700 self.data.test_volume, external_ref) 4701 4702 @mock.patch.object(common.VMAXCommon, 4703 '_remove_vol_and_cleanup_replication') 4704 def test_unmanage_success(self, mock_rm): 4705 volume = self.data.test_volume 4706 with mock.patch.object(self.rest, 'rename_volume'): 4707 self.common.unmanage(volume) 4708 self.rest.rename_volume.assert_called_once_with( 4709 self.data.array, self.data.device_id, 4710 self.data.test_volume.id) 4711 4712 def test_unmanage_device_not_found(self): 4713 volume = self.data.test_volume 4714 with mock.patch.object(self.common, '_find_device_on_array', 4715 return_value=None): 4716 with mock.patch.object(self.rest, 'rename_volume'): 4717 self.common.unmanage(volume) 4718 self.rest.rename_volume.assert_not_called() 4719 4720 @mock.patch.object(common.VMAXCommon, 4721 '_slo_workload_migration') 4722 def test_retype(self, mock_migrate): 4723 device_id = self.data.device_id 4724 volume_name = self.data.test_volume.name 4725 extra_specs = self.data.extra_specs_intervals_set 4726 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 4727 volume = self.data.test_volume 4728 new_type = {'extra_specs': {}} 4729 host = {'host': self.data.new_host} 4730 self.common.retype(volume, new_type, host) 4731 mock_migrate.assert_called_once_with( 4732 device_id, volume, host, volume_name, new_type, extra_specs) 4733 mock_migrate.reset_mock() 4734 with mock.patch.object( 4735 self.common, '_find_device_on_array', return_value=None): 4736 self.common.retype(volume, new_type, host) 4737 mock_migrate.assert_not_called() 4738 mock_migrate.reset_mock() 4739 volume2 = self.data.test_attached_volume 4740 self.common.retype(volume2, new_type, host) 4741 mock_migrate.assert_not_called() 4742 4743 def test_slo_workload_migration_valid(self): 4744 device_id = self.data.device_id 4745 volume_name = self.data.test_volume.name 4746 extra_specs = self.data.extra_specs 4747 new_type = {'extra_specs': {}} 4748 volume = self.data.test_volume 4749 host = {'host': self.data.new_host} 4750 with mock.patch.object(self.common, '_migrate_volume'): 4751 self.common._slo_workload_migration( 4752 device_id, volume, host, volume_name, new_type, extra_specs) 4753 self.common._migrate_volume.assert_called_once_with( 4754 extra_specs[utils.ARRAY], volume, device_id, 4755 extra_specs[utils.SRP], 'Silver', 4756 'OLTP', volume_name, new_type, extra_specs) 4757 4758 def test_slo_workload_migration_not_valid(self): 4759 device_id = self.data.device_id 4760 volume_name = self.data.test_volume.name 4761 extra_specs = self.data.extra_specs 4762 volume = self.data.test_volume 4763 new_type = {'extra_specs': {}} 4764 host = {'host': self.data.new_host} 4765 with mock.patch.object(self.common, 4766 '_is_valid_for_storage_assisted_migration', 4767 return_value=(False, 'Silver', 'OLTP')): 4768 migrate_status = self.common._slo_workload_migration( 4769 device_id, volume, host, volume_name, new_type, extra_specs) 4770 self.assertFalse(migrate_status) 4771 4772 def test_slo_workload_migration_same_hosts(self): 4773 device_id = self.data.device_id 4774 volume_name = self.data.test_volume.name 4775 extra_specs = self.data.extra_specs 4776 volume = self.data.test_volume 4777 host = {'host': self.data.fake_host} 4778 new_type = {'extra_specs': {}} 4779 migrate_status = self.common._slo_workload_migration( 4780 device_id, volume, host, volume_name, new_type, extra_specs) 4781 self.assertFalse(migrate_status) 4782 4783 def test_slo_workload_migration_same_host_change_compression(self): 4784 device_id = self.data.device_id 4785 volume_name = self.data.test_volume.name 4786 extra_specs = self.data.extra_specs 4787 volume = self.data.test_volume 4788 host = {'host': self.data.fake_host} 4789 new_type = {'extra_specs': {utils.DISABLECOMPRESSION: "true"}} 4790 with mock.patch.object( 4791 self.common, '_is_valid_for_storage_assisted_migration', 4792 return_value=(True, self.data.slo, self.data.workload)): 4793 with mock.patch.object(self.common, '_migrate_volume'): 4794 migrate_status = self.common._slo_workload_migration( 4795 device_id, volume, host, volume_name, new_type, 4796 extra_specs) 4797 self.assertTrue(bool(migrate_status)) 4798 self.common._migrate_volume.assert_called_once_with( 4799 extra_specs[utils.ARRAY], volume, device_id, 4800 extra_specs[utils.SRP], self.data.slo, 4801 self.data.workload, volume_name, new_type, extra_specs) 4802 4803 @mock.patch.object(masking.VMAXMasking, 'remove_and_reset_members') 4804 def test_migrate_volume_success(self, mock_remove): 4805 with mock.patch.object(self.rest, 'is_volume_in_storagegroup', 4806 return_value=True): 4807 device_id = self.data.device_id 4808 volume_name = self.data.test_volume.name 4809 extra_specs = self.data.extra_specs 4810 volume = self.data.test_volume 4811 new_type = {'extra_specs': {}} 4812 migrate_status = self.common._migrate_volume( 4813 self.data.array, volume, device_id, self.data.srp, 4814 self.data.slo, self.data.workload, volume_name, 4815 new_type, extra_specs) 4816 self.assertTrue(migrate_status) 4817 target_extra_specs = { 4818 'array': self.data.array, 'interval': 3, 4819 'retries': 120, 'slo': self.data.slo, 4820 'srp': self.data.srp, 'workload': self.data.workload} 4821 mock_remove.assert_called_once_with( 4822 self.data.array, volume, device_id, volume_name, 4823 target_extra_specs, reset=True) 4824 mock_remove.reset_mock() 4825 with mock.patch.object( 4826 self.rest, 'get_storage_groups_from_volume', 4827 return_value=[]): 4828 migrate_status = self.common._migrate_volume( 4829 self.data.array, volume, device_id, self.data.srp, 4830 self.data.slo, self.data.workload, volume_name, 4831 new_type, extra_specs) 4832 self.assertTrue(migrate_status) 4833 mock_remove.assert_not_called() 4834 4835 @mock.patch.object(masking.VMAXMasking, 'remove_and_reset_members') 4836 def test_migrate_volume_failed_get_new_sg_failed(self, mock_remove): 4837 device_id = self.data.device_id 4838 volume_name = self.data.test_volume.name 4839 extra_specs = self.data.extra_specs 4840 new_type = {'extra_specs': {}} 4841 with mock.patch.object( 4842 self.masking, 'get_or_create_default_storage_group', 4843 side_effect=exception.VolumeBackendAPIException): 4844 migrate_status = self.common._migrate_volume( 4845 self.data.array, self.data.test_volume, device_id, 4846 self.data.srp, self.data.slo, 4847 self.data.workload, volume_name, new_type, extra_specs) 4848 self.assertFalse(migrate_status) 4849 4850 def test_migrate_volume_failed_vol_not_added(self): 4851 device_id = self.data.device_id 4852 volume_name = self.data.test_volume.name 4853 extra_specs = self.data.extra_specs 4854 new_type = {'extra_specs': {}} 4855 with mock.patch.object( 4856 self.rest, 'is_volume_in_storagegroup', 4857 return_value=False): 4858 migrate_status = self.common._migrate_volume( 4859 self.data.array, self.data.test_volume, device_id, 4860 self.data.srp, self.data.slo, 4861 self.data.workload, volume_name, new_type, extra_specs) 4862 self.assertFalse(migrate_status) 4863 4864 def test_is_valid_for_storage_assisted_migration_true(self): 4865 device_id = self.data.device_id 4866 host = {'host': self.data.new_host} 4867 volume_name = self.data.test_volume.name 4868 ref_return = (True, 'Silver', 'OLTP') 4869 return_val = self.common._is_valid_for_storage_assisted_migration( 4870 device_id, host, self.data.array, 4871 self.data.srp, volume_name, False) 4872 self.assertEqual(ref_return, return_val) 4873 # No current sgs found 4874 with mock.patch.object(self.rest, 'get_storage_groups_from_volume', 4875 return_value=None): 4876 return_val = self.common._is_valid_for_storage_assisted_migration( 4877 device_id, host, self.data.array, self.data.srp, 4878 volume_name, False) 4879 self.assertEqual(ref_return, return_val) 4880 host = {'host': 'HostX@Backend#Silver+SRP_1+000197800123'} 4881 ref_return = (True, 'Silver', 'NONE') 4882 return_val = self.common._is_valid_for_storage_assisted_migration( 4883 device_id, host, self.data.array, 4884 self.data.srp, volume_name, False) 4885 self.assertEqual(ref_return, return_val) 4886 4887 def test_is_valid_for_storage_assisted_migration_false(self): 4888 device_id = self.data.device_id 4889 volume_name = self.data.test_volume.name 4890 ref_return = (False, None, None) 4891 # IndexError 4892 host = {'host': 'HostX@Backend#Silver+SRP_1+000197800123+dummy+data'} 4893 return_val = self.common._is_valid_for_storage_assisted_migration( 4894 device_id, host, self.data.array, 4895 self.data.srp, volume_name, False) 4896 self.assertEqual(ref_return, return_val) 4897 # Wrong array 4898 host2 = {'host': 'HostX@Backend#Silver+OLTP+SRP_1+00012345678'} 4899 return_val = self.common._is_valid_for_storage_assisted_migration( 4900 device_id, host2, self.data.array, 4901 self.data.srp, volume_name, False) 4902 self.assertEqual(ref_return, return_val) 4903 # Wrong srp 4904 host3 = {'host': 'HostX@Backend#Silver+OLTP+SRP_2+000197800123'} 4905 return_val = self.common._is_valid_for_storage_assisted_migration( 4906 device_id, host3, self.data.array, 4907 self.data.srp, volume_name, False) 4908 self.assertEqual(ref_return, return_val) 4909 # Already in correct sg 4910 host4 = {'host': self.data.fake_host} 4911 return_val = self.common._is_valid_for_storage_assisted_migration( 4912 device_id, host4, self.data.array, 4913 self.data.srp, volume_name, False) 4914 self.assertEqual(ref_return, return_val) 4915 4916 def test_is_valid_for_storage_assisted_migration_none(self): 4917 device_id = self.data.device_id 4918 host = {'host': self.data.none_host} 4919 volume_name = self.data.test_volume.name 4920 # Testing for 'NONE' Workload 4921 ref_return = (True, 'Diamond', 'NONE') 4922 return_val = self.common._is_valid_for_storage_assisted_migration( 4923 device_id, host, self.data.array, 4924 self.data.srp, volume_name, False) 4925 self.assertEqual(ref_return, return_val) 4926 4927 def test_is_valid_for_storage_assisted_migration_next_gen(self): 4928 device_id = self.data.device_id 4929 host = {'host': self.data.new_host} 4930 volume_name = self.data.test_volume.name 4931 ref_return = (True, 'Silver', 'NONE') 4932 with mock.patch.object(self.rest, 'is_next_gen_array', 4933 return_value=True): 4934 return_val = self.common._is_valid_for_storage_assisted_migration( 4935 device_id, host, self.data.array, 4936 self.data.srp, volume_name, False) 4937 self.assertEqual(ref_return, return_val) 4938 4939 def test_find_volume_group(self): 4940 group = self.data.test_group_1 4941 array = self.data.array 4942 volume_group = self.common._find_volume_group(array, group) 4943 ref_group = self.data.sg_details_rep[0] 4944 self.assertEqual(ref_group, volume_group) 4945 4946 def test_get_volume_device_ids(self): 4947 array = self.data.array 4948 volumes = [self.data.test_volume] 4949 ref_device_ids = [self.data.device_id] 4950 device_ids = self.common._get_volume_device_ids(volumes, array) 4951 self.assertEqual(ref_device_ids, device_ids) 4952 4953 def test_get_members_of_volume_group(self): 4954 array = self.data.array 4955 group_name = self.data.storagegroup_name_source 4956 ref_volumes = [self.data.device_id, self.data.device_id2] 4957 member_device_ids = self.common._get_members_of_volume_group( 4958 array, group_name) 4959 self.assertEqual(ref_volumes, member_device_ids) 4960 4961 def test_get_members_of_volume_group_empty(self): 4962 array = self.data.array 4963 group_name = self.data.storagegroup_name_source 4964 with mock.patch.object( 4965 self.rest, 'get_volumes_in_storage_group', 4966 return_value=None): 4967 member_device_ids = self.common._get_members_of_volume_group( 4968 array, group_name 4969 ) 4970 self.assertIsNone(member_device_ids) 4971 4972 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 4973 return_value=True) 4974 def test_create_group_replica(self, mock_check): 4975 source_group = self.data.test_group_1 4976 snap_name = self.data.group_snapshot_name 4977 with mock.patch.object( 4978 self.common, 4979 '_create_group_replica') as mock_create_replica: 4980 self.common._create_group_replica( 4981 source_group, snap_name) 4982 mock_create_replica.assert_called_once_with( 4983 source_group, snap_name) 4984 4985 def test_create_group_replica_exception(self): 4986 source_group = self.data.test_group_failed 4987 snap_name = self.data.group_snapshot_name 4988 with mock.patch.object( 4989 volume_utils, 'is_group_a_cg_snapshot_type', 4990 return_value=True): 4991 self.assertRaises(exception.VolumeBackendAPIException, 4992 self.common._create_group_replica, 4993 source_group, 4994 snap_name) 4995 4996 def test_create_group_snapshot(self): 4997 context = None 4998 group_snapshot = self.data.test_group_snapshot_1 4999 snapshots = [] 5000 ref_model_update = {'status': fields.GroupStatus.AVAILABLE} 5001 with mock.patch.object( 5002 volume_utils, 'is_group_a_cg_snapshot_type', 5003 return_value=True): 5004 model_update, snapshots_model_update = ( 5005 self.common.create_group_snapshot( 5006 context, group_snapshot, snapshots)) 5007 self.assertEqual(ref_model_update, model_update) 5008 5009 def test_create_group_snapshot_exception(self): 5010 context = None 5011 group_snapshot = self.data.test_group_snapshot_failed 5012 snapshots = [] 5013 with mock.patch.object( 5014 volume_utils, 'is_group_a_cg_snapshot_type', 5015 return_value=True): 5016 self.assertRaises(exception.VolumeBackendAPIException, 5017 self.common.create_group_snapshot, 5018 context, 5019 group_snapshot, 5020 snapshots) 5021 5022 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5023 return_value=True) 5024 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=False) 5025 def test_create_group(self, mock_type, mock_cg_type): 5026 ref_model_update = {'status': fields.GroupStatus.AVAILABLE} 5027 model_update = self.common.create_group(None, self.data.test_group_1) 5028 self.assertEqual(ref_model_update, model_update) 5029 5030 @mock.patch.object(provision.VMAXProvision, 'create_volume_group', 5031 side_effect=exception.CinderException) 5032 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=False) 5033 def test_create_group_exception(self, mock_type, mock_create): 5034 context = None 5035 group = self.data.test_group_failed 5036 with mock.patch.object( 5037 volume_utils, 'is_group_a_cg_snapshot_type', 5038 return_value=True): 5039 self.assertRaises(exception.VolumeBackendAPIException, 5040 self.common.create_group, 5041 context, group) 5042 5043 def test_delete_group_snapshot(self): 5044 group_snapshot = self.data.test_group_snapshot_1 5045 snapshots = [] 5046 context = None 5047 ref_model_update = {'status': fields.GroupSnapshotStatus.DELETED} 5048 with mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5049 return_value=True): 5050 model_update, snapshots_model_update = ( 5051 self.common.delete_group_snapshot(context, 5052 group_snapshot, snapshots)) 5053 self.assertEqual(ref_model_update, model_update) 5054 5055 def test_delete_group_snapshot_success(self): 5056 group_snapshot = self.data.test_group_snapshot_1 5057 snapshots = [] 5058 ref_model_update = {'status': fields.GroupSnapshotStatus.DELETED} 5059 with mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5060 return_value=True): 5061 model_update, snapshots_model_update = ( 5062 self.common._delete_group_snapshot(group_snapshot, 5063 snapshots)) 5064 self.assertEqual(ref_model_update, model_update) 5065 5066 def test_delete_group_snapshot_failed(self): 5067 group_snapshot = self.data.test_group_snapshot_failed 5068 snapshots = [] 5069 ref_model_update = ( 5070 {'status': fields.GroupSnapshotStatus.ERROR_DELETING}) 5071 with mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5072 return_value=True): 5073 model_update, snapshots_model_update = ( 5074 self.common._delete_group_snapshot(group_snapshot, 5075 snapshots)) 5076 self.assertEqual(ref_model_update, model_update) 5077 5078 @mock.patch.object(volume_utils, 'is_group_a_type', 5079 return_value=False) 5080 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5081 return_value=True) 5082 def test_update_group(self, mock_cg_type, mock_type_check): 5083 group = self.data.test_group_1 5084 add_vols = [self.data.test_volume] 5085 remove_vols = [] 5086 ref_model_update = {'status': fields.GroupStatus.AVAILABLE} 5087 model_update, __, __ = self.common.update_group(group, 5088 add_vols, 5089 remove_vols) 5090 self.assertEqual(ref_model_update, model_update) 5091 5092 @mock.patch.object(common.VMAXCommon, '_find_volume_group', 5093 return_value=None) 5094 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5095 return_value=True) 5096 def test_update_group_not_found(self, mock_check, mock_grp): 5097 self.assertRaises(exception.GroupNotFound, self.common.update_group, 5098 self.data.test_group_1, [], []) 5099 5100 @mock.patch.object(common.VMAXCommon, '_find_volume_group', 5101 side_effect=exception.VolumeBackendAPIException) 5102 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5103 return_value=True) 5104 def test_update_group_exception(self, mock_check, mock_grp): 5105 self.assertRaises(exception.VolumeBackendAPIException, 5106 self.common.update_group, 5107 self.data.test_group_1, [], []) 5108 5109 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=False) 5110 def test_delete_group(self, mock_check): 5111 group = self.data.test_group_1 5112 volumes = [self.data.test_volume] 5113 context = None 5114 ref_model_update = {'status': fields.GroupStatus.DELETED} 5115 with mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5116 return_value=True),\ 5117 mock.patch.object(self.rest, 'get_volumes_in_storage_group', 5118 return_value=[]): 5119 model_update, __ = self.common.delete_group( 5120 context, group, volumes) 5121 self.assertEqual(ref_model_update, model_update) 5122 5123 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=False) 5124 def test_delete_group_success(self, mock_check): 5125 group = self.data.test_group_1 5126 volumes = [] 5127 ref_model_update = {'status': fields.GroupStatus.DELETED} 5128 with mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5129 return_value=True),\ 5130 mock.patch.object(self.rest, 'get_volumes_in_storage_group', 5131 return_value=[]): 5132 model_update, __ = self.common._delete_group(group, volumes) 5133 self.assertEqual(ref_model_update, model_update) 5134 5135 def test_delete_group_already_deleted(self): 5136 group = self.data.test_group_failed 5137 ref_model_update = {'status': fields.GroupStatus.DELETED} 5138 volumes = [] 5139 with mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5140 return_value=True): 5141 model_update, __ = self.common._delete_group(group, volumes) 5142 self.assertEqual(ref_model_update, model_update) 5143 5144 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=False) 5145 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5146 return_value=True) 5147 def test_delete_group_failed(self, mock_check, mock_type_check): 5148 group = self.data.test_group_1 5149 volumes = [] 5150 ref_model_update = {'status': fields.GroupStatus.ERROR_DELETING} 5151 with mock.patch.object( 5152 self.rest, 'delete_storage_group', 5153 side_effect=exception.VolumeBackendAPIException): 5154 model_update, __ = self.common._delete_group( 5155 group, volumes) 5156 self.assertEqual(ref_model_update, model_update) 5157 5158 @mock.patch.object( 5159 common.VMAXCommon, '_get_clone_vol_info', 5160 return_value=(VMAXCommonData.device_id, 5161 VMAXCommonData.extra_specs, 1, 'tgt_vol')) 5162 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 5163 return_value=True) 5164 @mock.patch.object(volume_utils, 'is_group_a_type', 5165 return_value=False) 5166 def test_create_group_from_src_success(self, mock_type, 5167 mock_cg_type, mock_info): 5168 ref_model_update = {'status': fields.GroupStatus.AVAILABLE} 5169 model_update, volumes_model_update = ( 5170 self.common.create_group_from_src( 5171 None, self.data.test_group_1, [self.data.test_volume], 5172 self.data.test_group_snapshot_1, [], None, [])) 5173 self.assertEqual(ref_model_update, model_update) 5174 5175 @mock.patch.object( 5176 common.VMAXCommon, '_remove_vol_and_cleanup_replication') 5177 @mock.patch.object( 5178 masking.VMAXMasking, 'remove_volumes_from_storage_group') 5179 def test_rollback_create_group_from_src( 5180 self, mock_rm, mock_clean): 5181 rollback_dict = { 5182 'target_group_name': self.data.target_group_name, 5183 'snap_name': 'snap1', 'source_group_name': 'src_grp', 5184 'volumes': (self.data.device_id, self.data.extra_specs, 5185 self.data.test_volume), 5186 'device_ids': [self.data.device_id], 5187 'interval_retries_dict': self.data.extra_specs} 5188 for x in range(0, 2): 5189 self.common._rollback_create_group_from_src( 5190 self.data.array, rollback_dict) 5191 self.assertEqual(2, mock_rm.call_count) 5192 5193 def test_get_snap_src_dev_list(self): 5194 src_dev_ids = self.common._get_snap_src_dev_list( 5195 self.data.array, [self.data.test_snapshot]) 5196 ref_dev_ids = [self.data.device_id] 5197 self.assertEqual(ref_dev_ids, src_dev_ids) 5198 5199 def test_get_clone_vol_info(self): 5200 ref_dev_id = self.data.device_id 5201 source_vols = [self.data.test_volume, 5202 self.data.test_attached_volume] 5203 src_snapshots = [self.data.test_snapshot] 5204 src_dev_id1, extra_specs1, vol_size1, tgt_vol_name1 = ( 5205 self.common._get_clone_vol_info( 5206 self.data.test_clone_volume, source_vols, [])) 5207 src_dev_id2, extra_specs2, vol_size2, tgt_vol_name2 = ( 5208 self.common._get_clone_vol_info( 5209 self.data.test_clone_volume, [], src_snapshots)) 5210 self.assertEqual(ref_dev_id, src_dev_id1) 5211 self.assertEqual(ref_dev_id, src_dev_id2) 5212 5213 def test_get_attributes_from_cinder_config(self): 5214 kwargs_expected = ( 5215 {'RestServerIp': '1.1.1.1', 5216 'RestServerPort': 8443, 5217 'RestUserName': 'smc', 5218 'RestPassword': 'smc', 5219 'SSLVerify': False, 5220 'SerialNumber': self.data.array, 5221 'srpName': 'SRP_1', 5222 'PortGroup': self.data.port_group_name_i}) 5223 backup_conf = self.common.configuration 5224 configuration = FakeConfiguration( 5225 None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc', 5226 vmax_array=self.data.array, vmax_srp='SRP_1', san_password='smc', 5227 san_rest_port=8443, vmax_port_groups=[self.data.port_group_name_i]) 5228 self.common.configuration = configuration 5229 kwargs_returned = self.common.get_attributes_from_cinder_config() 5230 self.assertEqual(kwargs_expected, kwargs_returned) 5231 self.common.configuration = backup_conf 5232 kwargs = self.common.get_attributes_from_cinder_config() 5233 self.assertIsNone(kwargs) 5234 5235 def test_get_ssl_attributes_from_cinder_config(self): 5236 conf = FakeConfiguration( 5237 None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc', 5238 vmax_array=self.data.array, vmax_srp='SRP_1', san_password='smc', 5239 vmax_port_groups=[self.data.port_group_name_i], 5240 driver_ssl_cert_verify=True, 5241 driver_ssl_cert_path='/path/to/cert') 5242 5243 self.common.configuration = conf 5244 conf_returned = self.common.get_attributes_from_cinder_config() 5245 self.assertEqual('/path/to/cert', conf_returned['SSLVerify']) 5246 5247 conf.driver_ssl_cert_verify = True 5248 conf.driver_ssl_cert_path = None 5249 conf_returned = self.common.get_attributes_from_cinder_config() 5250 self.assertTrue(conf_returned['SSLVerify']) 5251 5252 conf.driver_ssl_cert_verify = False 5253 conf.driver_ssl_cert_path = None 5254 conf_returned = self.common.get_attributes_from_cinder_config() 5255 self.assertFalse(conf_returned['SSLVerify']) 5256 5257 @mock.patch.object(rest.VMAXRest, 5258 'get_size_of_device_on_array', 5259 return_value=2.0) 5260 def test_manage_snapshot_get_size_success(self, mock_get_size): 5261 size = self.common.manage_existing_snapshot_get_size( 5262 self.data.test_snapshot) 5263 self.assertEqual(2, size) 5264 5265 @mock.patch.object(rest.VMAXRest, 'get_volume_snap', 5266 return_value={'snap_name': 'snap_name'}) 5267 def test_manage_snapshot_success(self, mock_snap): 5268 snapshot = self.data.test_snapshot_manage 5269 existing_ref = {u'source-name': u'test_snap'} 5270 updates_response = self.common.manage_existing_snapshot( 5271 snapshot, existing_ref) 5272 5273 prov_loc = {'source_id': self.data.device_id, 5274 'snap_name': 'OS-%s' % existing_ref['source-name']} 5275 5276 updates = { 5277 'display_name': self.data.test_snapshot_manage.display_name, 5278 'provider_location': six.text_type(prov_loc)} 5279 5280 self.assertEqual(updates_response, updates) 5281 5282 def test_manage_snapshot_fail_already_managed(self): 5283 snapshot = self.data.test_snapshot_manage 5284 existing_ref = {u'source-name': u'OS-test_snap'} 5285 5286 self.assertRaises(exception.VolumeBackendAPIException, 5287 self.common.manage_existing_snapshot, 5288 snapshot, existing_ref) 5289 5290 @mock.patch.object(utils.VMAXUtils, 5291 'is_volume_failed_over', 5292 return_value=True) 5293 def test_manage_snapshot_fail_vol_failed_over(self, mock_failed): 5294 snapshot = self.data.test_snapshot_manage 5295 existing_ref = {u'source-name': u'test_snap'} 5296 self.assertRaises(exception.VolumeBackendAPIException, 5297 self.common.manage_existing_snapshot, 5298 snapshot, existing_ref) 5299 5300 @mock.patch.object(rest.VMAXRest, 'get_volume_snap', return_value=False) 5301 def test_manage_snapshot_fail_vol_not_snap_src(self, mock_snap): 5302 snapshot = self.data.test_snapshot_manage 5303 existing_ref = {u'source-name': u'test_snap'} 5304 self.assertRaises(exception.VolumeBackendAPIException, 5305 self.common.manage_existing_snapshot, 5306 snapshot, existing_ref) 5307 5308 @mock.patch.object(utils.VMAXUtils, 'modify_snapshot_prefix', 5309 side_effect=exception.VolumeBackendAPIException) 5310 def test_manage_snapshot_fail_add_prefix(self, mock_mod): 5311 snapshot = self.data.test_snapshot_manage 5312 existing_ref = {u'source-name': u'test_snap'} 5313 self.assertRaises(exception.VolumeBackendAPIException, 5314 self.common.manage_existing_snapshot, 5315 snapshot, existing_ref) 5316 5317 @mock.patch.object(common.VMAXCommon, '_sync_check') 5318 @mock.patch.object(rest.VMAXRest, 'modify_volume_snap') 5319 def test_unmanage_snapshot_success(self, mock_mod, mock_sync): 5320 self.common.unmanage_snapshot(self.data.test_snapshot_manage) 5321 mock_mod.assert_called_once() 5322 5323 @mock.patch.object( 5324 utils.VMAXUtils, 'is_volume_failed_over', return_value=True) 5325 def test_unmanage_snapshot_fail_failover(self, mock_failed): 5326 self.assertRaises(exception.VolumeBackendAPIException, 5327 self.common.unmanage_snapshot, 5328 self.data.test_snapshot_manage) 5329 5330 @mock.patch.object(rest.VMAXRest, 5331 'modify_volume_snap', 5332 side_effect=exception.VolumeBackendAPIException) 5333 def test_unmanage_snapshot_fail_rename(self, mock_snap): 5334 self.assertRaises(exception.VolumeBackendAPIException, 5335 self.common.unmanage_snapshot, 5336 self.data.test_snapshot_manage) 5337 5338 @mock.patch.object(provision.VMAXProvision, 'is_restore_complete', 5339 return_value=True) 5340 @mock.patch.object(common.VMAXCommon, '_sync_check') 5341 @mock.patch.object(provision.VMAXProvision, 5342 'revert_volume_snapshot') 5343 def test_revert_to_snapshot(self, mock_revert, mock_sync, mock_complete): 5344 volume = self.data.test_volume 5345 snapshot = self.data.test_snapshot 5346 array = self.data.array 5347 device_id = self.data.device_id 5348 snap_name = self.data.snap_location['snap_name'] 5349 extra_specs = deepcopy(self.data.extra_specs_intervals_set) 5350 extra_specs['storagetype:portgroupname'] = ( 5351 self.data.port_group_name_f) 5352 self.common.revert_to_snapshot(volume, snapshot) 5353 mock_revert.assert_called_once_with( 5354 array, device_id, snap_name, extra_specs) 5355 5356 @mock.patch.object(utils.VMAXUtils, 'is_replication_enabled', 5357 return_value=True) 5358 def test_revert_to_snapshot_replicated(self, mock_rep): 5359 volume = self.data.test_volume 5360 snapshot = self.data.test_snapshot 5361 self.assertRaises(exception.VolumeDriverException, 5362 self.common.revert_to_snapshot, volume, snapshot) 5363 5364 def test_get_initiator_check_flag(self): 5365 self.common.configuration.initiator_check = False 5366 initiator_check = self.common._get_initiator_check_flag() 5367 self.assertFalse(initiator_check) 5368 5369 def test_get_initiator_check_flag_true(self): 5370 self.common.configuration.initiator_check = True 5371 initiator_check = self.common._get_initiator_check_flag() 5372 self.assertTrue(initiator_check) 5373 5374 def test_get_slo_workload_combo_from_cinder_conf(self): 5375 configuration = FakeConfiguration( 5376 None, 'ProvisionTests', 1, 1, san_ip='1.1.1.1', san_login='smc', 5377 vmax_array=self.data.array, vmax_srp='SRP_1', san_password='smc', 5378 san_rest_port=8443, vmax_port_groups=[self.data.port_group_name_i]) 5379 # vmax_service_level='Diamond', vmax_workload='DSS') 5380 self.common.configuration = configuration 5381 5382 self.common.configuration.vmax_service_level = 'Diamond' 5383 self.common.configuration.vmax_workload = 'DSS' 5384 response1 = self.common.get_attributes_from_cinder_config() 5385 self.assertEqual('Diamond', response1['ServiceLevel']) 5386 self.assertEqual('DSS', response1['Workload']) 5387 5388 self.common.configuration.vmax_service_level = 'Diamond' 5389 self.common.configuration.vmax_workload = None 5390 response2 = self.common.get_attributes_from_cinder_config() 5391 self.assertEqual(self.common.configuration.vmax_service_level, 5392 response2['ServiceLevel']) 5393 self.assertIsNone(response2['Workload']) 5394 5395 expected_response = { 5396 'RestServerIp': '1.1.1.1', 5397 'RestServerPort': 8443, 5398 'RestUserName': 'smc', 5399 'RestPassword': 'smc', 5400 'SSLVerify': False, 5401 'SerialNumber': '000197800123', 5402 'srpName': 'SRP_1', 5403 'PortGroup': 'OS-iscsi-PG'} 5404 5405 self.common.configuration.vmax_service_level = None 5406 self.common.configuration.vmax_workload = 'DSS' 5407 response3 = self.common.get_attributes_from_cinder_config() 5408 self.assertEqual(expected_response, response3) 5409 5410 self.common.configuration.vmax_service_level = None 5411 self.common.configuration.vmax_workload = None 5412 response4 = self.common.get_attributes_from_cinder_config() 5413 self.assertEqual(expected_response, response4) 5414 5415 5416class VMAXFCTest(test.TestCase): 5417 def setUp(self): 5418 self.data = VMAXCommonData() 5419 5420 super(VMAXFCTest, self).setUp() 5421 config_group = 'FCTests' 5422 volume_utils.get_max_over_subscription_ratio = mock.Mock() 5423 self.fake_xml = FakeXML().create_fake_config_file( 5424 config_group, self.data.port_group_name_f) 5425 self.configuration = FakeConfiguration(self.fake_xml, config_group) 5426 rest.VMAXRest._establish_rest_session = mock.Mock( 5427 return_value=FakeRequestsSession()) 5428 driver = fc.VMAXFCDriver(configuration=self.configuration) 5429 self.driver = driver 5430 self.common = self.driver.common 5431 self.masking = self.common.masking 5432 self.utils = self.common.utils 5433 self.utils.get_volumetype_extra_specs = ( 5434 mock.Mock(return_value=self.data.vol_type_extra_specs)) 5435 5436 def test_create_volume(self): 5437 with mock.patch.object(self.common, 'create_volume'): 5438 self.driver.create_volume(self.data.test_volume) 5439 self.common.create_volume.assert_called_once_with( 5440 self.data.test_volume) 5441 5442 def test_create_volume_from_snapshot(self): 5443 volume = self.data.test_clone_volume 5444 snapshot = self.data.test_snapshot 5445 with mock.patch.object(self.common, 'create_volume_from_snapshot'): 5446 self.driver.create_volume_from_snapshot(volume, snapshot) 5447 self.common.create_volume_from_snapshot.assert_called_once_with( 5448 volume, snapshot) 5449 5450 def test_create_cloned_volume(self): 5451 volume = self.data.test_clone_volume 5452 src_volume = self.data.test_volume 5453 with mock.patch.object(self.common, 'create_cloned_volume'): 5454 self.driver.create_cloned_volume(volume, src_volume) 5455 self.common.create_cloned_volume.assert_called_once_with( 5456 volume, src_volume) 5457 5458 def test_delete_volume(self): 5459 with mock.patch.object(self.common, 'delete_volume'): 5460 self.driver.delete_volume(self.data.test_volume) 5461 self.common.delete_volume.assert_called_once_with( 5462 self.data.test_volume) 5463 5464 def test_create_snapshot(self): 5465 with mock.patch.object(self.common, 'create_snapshot'): 5466 self.driver.create_snapshot(self.data.test_snapshot) 5467 self.common.create_snapshot.assert_called_once_with( 5468 self.data.test_snapshot, self.data.test_snapshot.volume) 5469 5470 def test_delete_snapshot(self): 5471 with mock.patch.object(self.common, 'delete_snapshot'): 5472 self.driver.delete_snapshot(self.data.test_snapshot) 5473 self.common.delete_snapshot.assert_called_once_with( 5474 self.data.test_snapshot, self.data.test_snapshot.volume) 5475 5476 def test_initialize_connection(self): 5477 with mock.patch.object(self.common, 'initialize_connection', 5478 return_value=self.data.fc_device_info): 5479 with mock.patch.object(self.driver, 'populate_data'): 5480 self.driver.initialize_connection(self.data.test_volume, 5481 self.data.connector) 5482 self.common.initialize_connection.assert_called_once_with( 5483 self.data.test_volume, self.data.connector) 5484 self.driver.populate_data.assert_called_once_with( 5485 self.data.fc_device_info, self.data.test_volume, 5486 self.data.connector) 5487 5488 def test_populate_data(self): 5489 with mock.patch.object(self.driver, '_build_initiator_target_map', 5490 return_value=([], {})): 5491 ref_data = { 5492 'driver_volume_type': 'fibre_channel', 5493 'data': {'target_lun': self.data.fc_device_info['hostlunid'], 5494 'target_discovered': True, 5495 'target_wwn': [], 5496 'initiator_target_map': {}}} 5497 data = self.driver.populate_data(self.data.fc_device_info, 5498 self.data.test_volume, 5499 self.data.connector) 5500 self.assertEqual(ref_data, data) 5501 self.driver._build_initiator_target_map.assert_called_once_with( 5502 self.data.test_volume, self.data.connector) 5503 5504 def test_terminate_connection(self): 5505 with mock.patch.object(self.common, 'terminate_connection'): 5506 self.driver.terminate_connection(self.data.test_volume, 5507 self.data.connector) 5508 self.common.terminate_connection.assert_called_once_with( 5509 self.data.test_volume, self.data.connector) 5510 5511 def test_terminate_connection_no_zoning_mappings(self): 5512 with mock.patch.object(self.driver, '_get_zoning_mappings', 5513 return_value=None): 5514 with mock.patch.object(self.common, 'terminate_connection'): 5515 self.driver.terminate_connection(self.data.test_volume, 5516 self.data.connector) 5517 self.common.terminate_connection.assert_not_called() 5518 5519 def test_get_zoning_mappings(self): 5520 ref_mappings = self.data.zoning_mappings 5521 zoning_mappings = self.driver._get_zoning_mappings( 5522 self.data.test_volume, self.data.connector) 5523 self.assertEqual(ref_mappings, zoning_mappings) 5524 # Legacy vol 5525 zoning_mappings2 = self.driver._get_zoning_mappings( 5526 self.data.test_legacy_vol, self.data.connector) 5527 self.assertEqual(ref_mappings, zoning_mappings2) 5528 5529 def test_get_zoning_mappings_no_mv(self): 5530 with mock.patch.object(self.common, 'get_masking_views_from_volume', 5531 return_value=(None, False)): 5532 zoning_mappings = self.driver._get_zoning_mappings( 5533 self.data.test_volume, self.data.connector) 5534 self.assertEqual({}, zoning_mappings) 5535 5536 @mock.patch.object( 5537 common.VMAXCommon, 'get_masking_views_from_volume', 5538 return_value=([VMAXCommonData.masking_view_name_f], True)) 5539 def test_get_zoning_mappings_metro(self, mock_mv): 5540 ref_mappings = self.data.zoning_mappings_metro 5541 zoning_mappings = self.driver._get_zoning_mappings( 5542 self.data.test_volume, self.data.connector) 5543 self.assertEqual(ref_mappings, zoning_mappings) 5544 5545 def test_cleanup_zones_other_vols_mapped(self): 5546 ref_data = {'driver_volume_type': 'fibre_channel', 5547 'data': {}} 5548 data = self.driver._cleanup_zones(self.data.zoning_mappings) 5549 self.assertEqual(ref_data, data) 5550 5551 def test_cleanup_zones_no_vols_mapped(self): 5552 zoning_mappings = self.data.zoning_mappings 5553 ref_data = {'driver_volume_type': 'fibre_channel', 5554 'data': {'target_wwn': zoning_mappings['target_wwns'], 5555 'initiator_target_map': 5556 zoning_mappings['init_targ_map']}} 5557 with mock.patch.object(self.common, 'get_common_masking_views', 5558 return_value=[]): 5559 data = self.driver._cleanup_zones(self.data.zoning_mappings) 5560 self.assertEqual(ref_data, data) 5561 5562 def test_build_initiator_target_map(self): 5563 ref_target_map = {'123456789012345': ['543210987654321'], 5564 '123456789054321': ['123450987654321']} 5565 with mock.patch.object(fczm_utils, 'create_lookup_service', 5566 return_value=FakeLookupService()): 5567 driver = fc.VMAXFCDriver(configuration=self.configuration) 5568 with mock.patch.object(driver.common, 5569 'get_target_wwns_from_masking_view', 5570 return_value=(self.data.target_wwns, [])): 5571 targets, target_map = driver._build_initiator_target_map( 5572 self.data.test_volume, self.data.connector) 5573 self.assertEqual(ref_target_map, target_map) 5574 5575 def test_extend_volume(self): 5576 with mock.patch.object(self.common, 'extend_volume'): 5577 self.driver.extend_volume(self.data.test_volume, '3') 5578 self.common.extend_volume.assert_called_once_with( 5579 self.data.test_volume, '3') 5580 5581 def test_get_volume_stats(self): 5582 with mock.patch.object(self.driver, 'update_volume_stats'): 5583 # no refresh 5584 self.driver.get_volume_stats() 5585 self.driver.update_volume_stats.assert_not_called() 5586 # with refresh 5587 self.driver.get_volume_stats(True) 5588 self.driver.update_volume_stats.assert_called_once_with() 5589 5590 def test_update_volume_stats(self): 5591 with mock.patch.object(self.common, 'update_volume_stats', 5592 return_value={}): 5593 self.driver.update_volume_stats() 5594 self.common.update_volume_stats.assert_called_once_with() 5595 5596 def test_check_for_setup_error(self): 5597 self.driver.check_for_setup_error() 5598 5599 def test_ensure_export(self): 5600 self.driver.ensure_export('context', 'volume') 5601 5602 def test_create_export(self): 5603 self.driver.create_export('context', 'volume', 'connector') 5604 5605 def test_remove_export(self): 5606 self.driver.remove_export('context', 'volume') 5607 5608 def test_check_for_export(self): 5609 self.driver.check_for_export('context', 'volume_id') 5610 5611 def test_manage_existing(self): 5612 with mock.patch.object(self.common, 'manage_existing', 5613 return_value={}): 5614 external_ref = {u'source-name': u'00002'} 5615 self.driver.manage_existing(self.data.test_volume, external_ref) 5616 self.common.manage_existing.assert_called_once_with( 5617 self.data.test_volume, external_ref) 5618 5619 def test_manage_existing_get_size(self): 5620 with mock.patch.object(self.common, 'manage_existing_get_size', 5621 return_value='1'): 5622 external_ref = {u'source-name': u'00002'} 5623 self.driver.manage_existing_get_size( 5624 self.data.test_volume, external_ref) 5625 self.common.manage_existing_get_size.assert_called_once_with( 5626 self.data.test_volume, external_ref) 5627 5628 def test_unmanage_volume(self): 5629 with mock.patch.object(self.common, 'unmanage', 5630 return_value={}): 5631 self.driver.unmanage(self.data.test_volume) 5632 self.common.unmanage.assert_called_once_with( 5633 self.data.test_volume) 5634 5635 def test_retype(self): 5636 host = {'host': self.data.new_host} 5637 new_type = {'extra_specs': {}} 5638 with mock.patch.object(self.common, 'retype', 5639 return_value=True): 5640 self.driver.retype({}, self.data.test_volume, new_type, '', host) 5641 self.common.retype.assert_called_once_with( 5642 self.data.test_volume, new_type, host) 5643 5644 def test_failover_host(self): 5645 with mock.patch.object( 5646 self.common, 'failover_host', 5647 return_value=(self.data.remote_array, [], [])) as mock_fo: 5648 self.driver.failover_host(self.data.ctx, [self.data.test_volume]) 5649 mock_fo.assert_called_once_with([self.data.test_volume], None, 5650 None) 5651 5652 def test_enable_replication(self): 5653 with mock.patch.object( 5654 self.common, 'enable_replication') as mock_er: 5655 self.driver.enable_replication( 5656 self.data.ctx, self.data.test_group, [self.data.test_volume]) 5657 mock_er.assert_called_once() 5658 5659 def test_disable_replication(self): 5660 with mock.patch.object( 5661 self.common, 'disable_replication') as mock_dr: 5662 self.driver.disable_replication( 5663 self.data.ctx, self.data.test_group, [self.data.test_volume]) 5664 mock_dr.assert_called_once() 5665 5666 def test_failover_replication(self): 5667 with mock.patch.object( 5668 self.common, 'failover_replication') as mock_fo: 5669 self.driver.failover_replication( 5670 self.data.ctx, self.data.test_group, [self.data.test_volume]) 5671 mock_fo.assert_called_once() 5672 5673 5674class VMAXISCSITest(test.TestCase): 5675 def setUp(self): 5676 self.data = VMAXCommonData() 5677 5678 super(VMAXISCSITest, self).setUp() 5679 config_group = 'ISCSITests' 5680 self.fake_xml = FakeXML().create_fake_config_file( 5681 config_group, self.data.port_group_name_i) 5682 volume_utils.get_max_over_subscription_ratio = mock.Mock() 5683 configuration = FakeConfiguration(self.fake_xml, config_group) 5684 rest.VMAXRest._establish_rest_session = mock.Mock( 5685 return_value=FakeRequestsSession()) 5686 driver = iscsi.VMAXISCSIDriver(configuration=configuration) 5687 self.driver = driver 5688 self.common = self.driver.common 5689 self.masking = self.common.masking 5690 self.utils = self.common.utils 5691 self.utils.get_volumetype_extra_specs = ( 5692 mock.Mock(return_value=self.data.vol_type_extra_specs)) 5693 5694 def test_create_volume(self): 5695 with mock.patch.object(self.common, 'create_volume'): 5696 self.driver.create_volume(self.data.test_volume) 5697 self.common.create_volume.assert_called_once_with( 5698 self.data.test_volume) 5699 5700 def test_create_volume_from_snapshot(self): 5701 volume = self.data.test_clone_volume 5702 snapshot = self.data.test_snapshot 5703 with mock.patch.object(self.common, 'create_volume_from_snapshot'): 5704 self.driver.create_volume_from_snapshot(volume, snapshot) 5705 self.common.create_volume_from_snapshot.assert_called_once_with( 5706 volume, snapshot) 5707 5708 def test_create_cloned_volume(self): 5709 volume = self.data.test_clone_volume 5710 src_volume = self.data.test_volume 5711 with mock.patch.object(self.common, 'create_cloned_volume'): 5712 self.driver.create_cloned_volume(volume, src_volume) 5713 self.common.create_cloned_volume.assert_called_once_with( 5714 volume, src_volume) 5715 5716 def test_delete_volume(self): 5717 with mock.patch.object(self.common, 'delete_volume'): 5718 self.driver.delete_volume(self.data.test_volume) 5719 self.common.delete_volume.assert_called_once_with( 5720 self.data.test_volume) 5721 5722 def test_create_snapshot(self): 5723 with mock.patch.object(self.common, 'create_snapshot'): 5724 self.driver.create_snapshot(self.data.test_snapshot) 5725 self.common.create_snapshot.assert_called_once_with( 5726 self.data.test_snapshot, self.data.test_snapshot.volume) 5727 5728 def test_delete_snapshot(self): 5729 with mock.patch.object(self.common, 'delete_snapshot'): 5730 self.driver.delete_snapshot(self.data.test_snapshot) 5731 self.common.delete_snapshot.assert_called_once_with( 5732 self.data.test_snapshot, self.data.test_snapshot.volume) 5733 5734 def test_initialize_connection(self): 5735 ref_dict = {'maskingview': self.data.masking_view_name_f, 5736 'array': self.data.array, 5737 'hostlunid': 3, 5738 'device_id': self.data.device_id, 5739 'ip_and_iqn': [{'ip': self.data.ip, 5740 'iqn': self.data.initiator}], 5741 'is_multipath': False} 5742 with mock.patch.object(self.driver, 'get_iscsi_dict'): 5743 with mock.patch.object( 5744 self.common, 'get_port_group_from_masking_view', 5745 return_value=self.data.port_group_name_i): 5746 self.driver.initialize_connection(self.data.test_volume, 5747 self.data.connector) 5748 self.driver.get_iscsi_dict.assert_called_once_with( 5749 ref_dict, self.data.test_volume) 5750 5751 def test_get_iscsi_dict_success(self): 5752 ip_and_iqn = self.common._find_ip_and_iqns( 5753 self.data.array, self.data.port_group_name_i) 5754 host_lun_id = self.data.iscsi_device_info['hostlunid'] 5755 volume = self.data.test_volume 5756 device_info = self.data.iscsi_device_info 5757 ref_data = {'driver_volume_type': 'iscsi', 'data': {}} 5758 with mock.patch.object( 5759 self.driver, 'vmax_get_iscsi_properties', return_value={}): 5760 data = self.driver.get_iscsi_dict(device_info, volume) 5761 self.assertEqual(ref_data, data) 5762 self.driver.vmax_get_iscsi_properties.assert_called_once_with( 5763 volume, ip_and_iqn, True, host_lun_id, None, None) 5764 5765 def test_get_iscsi_dict_exception(self): 5766 device_info = {'ip_and_iqn': ''} 5767 self.assertRaises(exception.VolumeBackendAPIException, 5768 self.driver.get_iscsi_dict, 5769 device_info, self.data.test_volume) 5770 5771 def test_get_iscsi_dict_metro(self): 5772 ip_and_iqn = self.common._find_ip_and_iqns( 5773 self.data.array, self.data.port_group_name_i) 5774 host_lun_id = self.data.iscsi_device_info_metro['hostlunid'] 5775 volume = self.data.test_volume 5776 device_info = self.data.iscsi_device_info_metro 5777 ref_data = {'driver_volume_type': 'iscsi', 'data': {}} 5778 with mock.patch.object( 5779 self.driver, 'vmax_get_iscsi_properties', return_value={}): 5780 data = self.driver.get_iscsi_dict(device_info, volume) 5781 self.assertEqual(ref_data, data) 5782 self.driver.vmax_get_iscsi_properties.assert_called_once_with( 5783 volume, ip_and_iqn, True, host_lun_id, 5784 self.data.iscsi_device_info_metro['metro_ip_and_iqn'], 5785 self.data.iscsi_device_info_metro['metro_hostlunid']) 5786 5787 def test_vmax_get_iscsi_properties_one_target_no_auth(self): 5788 vol = deepcopy(self.data.test_volume) 5789 ip_and_iqn = self.common._find_ip_and_iqns( 5790 self.data.array, self.data.port_group_name_i) 5791 host_lun_id = self.data.iscsi_device_info['hostlunid'] 5792 ref_properties = { 5793 'target_discovered': True, 5794 'target_iqn': ip_and_iqn[0]['iqn'].split(",")[0], 5795 'target_portal': ip_and_iqn[0]['ip'] + ":3260", 5796 'target_lun': host_lun_id, 5797 'volume_id': self.data.test_volume.id} 5798 iscsi_properties = self.driver.vmax_get_iscsi_properties( 5799 vol, ip_and_iqn, True, host_lun_id, [], None) 5800 self.assertEqual(type(ref_properties), type(iscsi_properties)) 5801 self.assertEqual(ref_properties, iscsi_properties) 5802 5803 def test_vmax_get_iscsi_properties_multiple_targets(self): 5804 ip_and_iqn = [{'ip': self.data.ip, 'iqn': self.data.initiator}, 5805 {'ip': self.data.ip, 'iqn': self.data.iqn}] 5806 host_lun_id = self.data.iscsi_device_info['hostlunid'] 5807 ref_properties = { 5808 'target_portals': ( 5809 [t['ip'] + ":3260" for t in ip_and_iqn]), 5810 'target_iqns': ( 5811 [t['iqn'].split(",")[0] for t in ip_and_iqn]), 5812 'target_luns': [host_lun_id] * len(ip_and_iqn), 5813 'target_discovered': True, 5814 'target_iqn': ip_and_iqn[0]['iqn'].split(",")[0], 5815 'target_portal': ip_and_iqn[0]['ip'] + ":3260", 5816 'target_lun': host_lun_id, 5817 'volume_id': self.data.test_volume.id} 5818 iscsi_properties = self.driver.vmax_get_iscsi_properties( 5819 self.data.test_volume, ip_and_iqn, True, host_lun_id, [], None) 5820 self.assertEqual(ref_properties, iscsi_properties) 5821 5822 def test_vmax_get_iscsi_properties_auth(self): 5823 vol = deepcopy(self.data.test_volume) 5824 backup_conf = self.common.configuration 5825 configuration = FakeConfiguration( 5826 None, 'ISCSITests', 1, 1, san_ip='1.1.1.1', san_login='smc', 5827 vmax_array=self.data.array, vmax_srp='SRP_1', san_password='smc', 5828 san_rest_port=8443, use_chap_auth=True, 5829 chap_username='auth_username', chap_password='auth_secret', 5830 vmax_port_groups=[self.data.port_group_name_i]) 5831 self.driver.configuration = configuration 5832 ip_and_iqn = [{'ip': self.data.ip, 'iqn': self.data.initiator}, 5833 {'ip': self.data.ip, 'iqn': self.data.iqn}] 5834 host_lun_id = self.data.iscsi_device_info['hostlunid'] 5835 ref_properties = { 5836 'target_portals': ( 5837 [t['ip'] + ":3260" for t in ip_and_iqn]), 5838 'target_iqns': ( 5839 [t['iqn'].split(",")[0] for t in ip_and_iqn]), 5840 'target_luns': [host_lun_id] * len(ip_and_iqn), 5841 'target_discovered': True, 5842 'target_iqn': ip_and_iqn[0]['iqn'].split(",")[0], 5843 'target_portal': ip_and_iqn[0]['ip'] + ":3260", 5844 'target_lun': host_lun_id, 5845 'volume_id': self.data.test_volume.id, 5846 'auth_method': 'CHAP', 5847 'auth_username': 'auth_username', 5848 'auth_password': 'auth_secret'} 5849 iscsi_properties = self.driver.vmax_get_iscsi_properties( 5850 vol, ip_and_iqn, True, host_lun_id, None, None) 5851 self.assertEqual(ref_properties, iscsi_properties) 5852 self.driver.configuration = backup_conf 5853 5854 def test_vmax_get_iscsi_properties_metro(self): 5855 ip_and_iqn = [{'ip': self.data.ip, 'iqn': self.data.iqn}] 5856 total_ip_list = [{'ip': self.data.ip, 'iqn': self.data.iqn}, 5857 {'ip': self.data.ip2, 'iqn': self.data.iqn2}] 5858 host_lun_id = self.data.iscsi_device_info['hostlunid'] 5859 host_lun_id2 = self.data.iscsi_device_info_metro['metro_hostlunid'] 5860 ref_properties = { 5861 'target_portals': ( 5862 [t['ip'] + ":3260" for t in total_ip_list]), 5863 'target_iqns': ( 5864 [t['iqn'].split(",")[0] for t in total_ip_list]), 5865 'target_luns': [host_lun_id, host_lun_id2], 5866 'target_discovered': True, 5867 'target_iqn': ip_and_iqn[0]['iqn'].split(",")[0], 5868 'target_portal': ip_and_iqn[0]['ip'] + ":3260", 5869 'target_lun': host_lun_id, 5870 'volume_id': self.data.test_volume.id} 5871 iscsi_properties = self.driver.vmax_get_iscsi_properties( 5872 self.data.test_volume, ip_and_iqn, True, host_lun_id, 5873 self.data.iscsi_device_info_metro['metro_ip_and_iqn'], 5874 self.data.iscsi_device_info_metro['metro_hostlunid']) 5875 self.assertEqual(ref_properties, iscsi_properties) 5876 5877 def test_terminate_connection(self): 5878 with mock.patch.object(self.common, 'terminate_connection'): 5879 self.driver.terminate_connection(self.data.test_volume, 5880 self.data.connector) 5881 self.common.terminate_connection.assert_called_once_with( 5882 self.data.test_volume, self.data.connector) 5883 5884 def test_extend_volume(self): 5885 with mock.patch.object(self.common, 'extend_volume'): 5886 self.driver.extend_volume(self.data.test_volume, '3') 5887 self.common.extend_volume.assert_called_once_with( 5888 self.data.test_volume, '3') 5889 5890 def test_get_volume_stats(self): 5891 with mock.patch.object(self.driver, 'update_volume_stats'): 5892 # no refresh 5893 self.driver.get_volume_stats() 5894 self.driver.update_volume_stats.assert_not_called() 5895 # with refresh 5896 self.driver.get_volume_stats(True) 5897 self.driver.update_volume_stats.assert_called_once_with() 5898 5899 def test_update_volume_stats(self): 5900 with mock.patch.object(self.common, 'update_volume_stats', 5901 return_value={}): 5902 self.driver.update_volume_stats() 5903 self.common.update_volume_stats.assert_called_once_with() 5904 5905 def test_check_for_setup_error(self): 5906 self.driver.check_for_setup_error() 5907 5908 def test_ensure_export(self): 5909 self.driver.ensure_export('context', 'volume') 5910 5911 def test_create_export(self): 5912 self.driver.create_export('context', 'volume', 'connector') 5913 5914 def test_remove_export(self): 5915 self.driver.remove_export('context', 'volume') 5916 5917 def test_check_for_export(self): 5918 self.driver.check_for_export('context', 'volume_id') 5919 5920 def test_manage_existing(self): 5921 with mock.patch.object(self.common, 'manage_existing', 5922 return_value={}): 5923 external_ref = {u'source-name': u'00002'} 5924 self.driver.manage_existing(self.data.test_volume, external_ref) 5925 self.common.manage_existing.assert_called_once_with( 5926 self.data.test_volume, external_ref) 5927 5928 def test_manage_existing_get_size(self): 5929 with mock.patch.object(self.common, 'manage_existing_get_size', 5930 return_value='1'): 5931 external_ref = {u'source-name': u'00002'} 5932 self.driver.manage_existing_get_size( 5933 self.data.test_volume, external_ref) 5934 self.common.manage_existing_get_size.assert_called_once_with( 5935 self.data.test_volume, external_ref) 5936 5937 def test_unmanage_volume(self): 5938 with mock.patch.object(self.common, 'unmanage', 5939 return_value={}): 5940 self.driver.unmanage(self.data.test_volume) 5941 self.common.unmanage.assert_called_once_with( 5942 self.data.test_volume) 5943 5944 def test_retype(self): 5945 host = {'host': self.data.new_host} 5946 new_type = {'extra_specs': {}} 5947 with mock.patch.object(self.common, 'retype', 5948 return_value=True): 5949 self.driver.retype({}, self.data.test_volume, new_type, '', host) 5950 self.common.retype.assert_called_once_with( 5951 self.data.test_volume, new_type, host) 5952 5953 def test_failover_host(self): 5954 with mock.patch.object(self.common, 'failover_host', 5955 return_value={}) as mock_fo: 5956 self.driver.failover_host({}, [self.data.test_volume]) 5957 mock_fo.assert_called_once_with([self.data.test_volume], None, 5958 None) 5959 5960 def test_enable_replication(self): 5961 with mock.patch.object( 5962 self.common, 'enable_replication') as mock_er: 5963 self.driver.enable_replication( 5964 self.data.ctx, self.data.test_group, [self.data.test_volume]) 5965 mock_er.assert_called_once() 5966 5967 def test_disable_replication(self): 5968 with mock.patch.object( 5969 self.common, 'disable_replication') as mock_dr: 5970 self.driver.disable_replication( 5971 self.data.ctx, self.data.test_group, [self.data.test_volume]) 5972 mock_dr.assert_called_once() 5973 5974 def test_failover_replication(self): 5975 with mock.patch.object( 5976 self.common, 'failover_replication') as mock_fo: 5977 self.driver.failover_replication( 5978 self.data.ctx, self.data.test_group, [self.data.test_volume]) 5979 mock_fo.assert_called_once() 5980 5981 5982class VMAXMaskingTest(test.TestCase): 5983 def setUp(self): 5984 self.data = VMAXCommonData() 5985 5986 super(VMAXMaskingTest, self).setUp() 5987 5988 volume_utils.get_max_over_subscription_ratio = mock.Mock() 5989 configuration = mock.Mock() 5990 configuration.safe_get.return_value = 'MaskingTests' 5991 configuration.config_group = 'MaskingTests' 5992 self._gather_info = common.VMAXCommon._gather_info 5993 common.VMAXCommon._gather_info = mock.Mock() 5994 rest.VMAXRest._establish_rest_session = mock.Mock( 5995 return_value=FakeRequestsSession()) 5996 driver = common.VMAXCommon( 5997 'iSCSI', self.data.version, configuration=configuration) 5998 driver_fc = common.VMAXCommon( 5999 'FC', self.data.version, configuration=configuration) 6000 self.driver = driver 6001 self.driver_fc = driver_fc 6002 self.mask = self.driver.masking 6003 self.extra_specs = self.data.extra_specs 6004 self.extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_i 6005 self.maskingviewdict = self.driver._populate_masking_dict( 6006 self.data.test_volume, self.data.connector, self.extra_specs) 6007 self.maskingviewdict['extra_specs'] = self.extra_specs 6008 self.device_id = self.data.device_id 6009 self.volume_name = self.data.volume_details[0]['volume_identifier'] 6010 6011 def tearDown(self): 6012 super(VMAXMaskingTest, self).tearDown() 6013 common.VMAXCommon._gather_info = self._gather_info 6014 6015 @mock.patch.object( 6016 masking.VMAXMasking, 6017 'get_or_create_masking_view_and_map_lun') 6018 def test_setup_masking_view(self, mock_get_or_create_mv): 6019 self.driver.masking.setup_masking_view( 6020 self.data.array, self.data.test_volume, 6021 self.maskingviewdict, self.extra_specs) 6022 mock_get_or_create_mv.assert_called_once() 6023 6024 @mock.patch.object( 6025 masking.VMAXMasking, 6026 '_check_adding_volume_to_storage_group') 6027 @mock.patch.object( 6028 masking.VMAXMasking, 6029 '_move_vol_from_default_sg', 6030 return_value=None) 6031 @mock.patch.object( 6032 masking.VMAXMasking, 6033 '_get_or_create_masking_view', 6034 side_effect=[None, "Error in masking view retrieval", 6035 exception.VolumeBackendAPIException]) 6036 @mock.patch.object( 6037 rest.VMAXRest, 6038 'get_element_from_masking_view', 6039 side_effect=[VMAXCommonData.port_group_name_i, 6040 Exception('Exception')]) 6041 def test_get_or_create_masking_view_and_map_lun( 6042 self, mock_masking_view_element, mock_masking, mock_move, 6043 mock_add_volume): 6044 rollback_dict = ( 6045 self.driver.masking.get_or_create_masking_view_and_map_lun( 6046 self.data.array, self.data.test_volume, 6047 self.maskingviewdict['maskingview_name'], 6048 self.maskingviewdict, self.extra_specs)) 6049 self.assertEqual(self.maskingviewdict, rollback_dict) 6050 self.assertRaises( 6051 exception.VolumeBackendAPIException, 6052 self.driver.masking.get_or_create_masking_view_and_map_lun, 6053 self.data.array, self.data.test_volume, 6054 self.maskingviewdict['maskingview_name'], 6055 self.maskingviewdict, self.extra_specs) 6056 self.maskingviewdict['slo'] = None 6057 self.assertRaises( 6058 exception.VolumeBackendAPIException, 6059 self.driver.masking.get_or_create_masking_view_and_map_lun, 6060 self.data.array, self.data.test_volume, 6061 self.maskingviewdict['maskingview_name'], 6062 self.maskingviewdict, self.extra_specs) 6063 6064 @mock.patch.object( 6065 masking.VMAXMasking, 6066 '_check_adding_volume_to_storage_group', 6067 return_value=None) 6068 @mock.patch.object( 6069 rest.VMAXRest, 6070 'move_volume_between_storage_groups', 6071 side_effect=[None, exception.VolumeBackendAPIException(data='')]) 6072 @mock.patch.object( 6073 rest.VMAXRest, 6074 'is_volume_in_storagegroup', 6075 side_effect=[True, False, True]) 6076 def test_move_vol_from_default_sg( 6077 self, mock_volume_in_sg, mock_move_volume, mock_add): 6078 msg = None 6079 for x in range(0, 2): 6080 msg = self.driver.masking._move_vol_from_default_sg( 6081 self.data.array, self.device_id, self.volume_name, 6082 self.data.defaultstoragegroup_name, 6083 self.data.storagegroup_name_i, self.extra_specs) 6084 mock_move_volume.assert_called_once() 6085 mock_add.assert_called_once() 6086 self.assertIsNone(msg) 6087 msg = self.driver.masking._move_vol_from_default_sg( 6088 self.data.array, self.device_id, self.volume_name, 6089 self.data.defaultstoragegroup_name, 6090 self.data.storagegroup_name_i, self.extra_specs) 6091 self.assertIsNotNone(msg) 6092 6093 @mock.patch.object( 6094 rest.VMAXRest, 6095 'get_masking_view', 6096 side_effect=[VMAXCommonData.maskingview, 6097 VMAXCommonData.maskingview, None]) 6098 @mock.patch.object( 6099 masking.VMAXMasking, 6100 '_validate_existing_masking_view', 6101 side_effect=[(VMAXCommonData.maskingview[1]['storageGroupId'], 6102 None), (None, "Error Message")]) 6103 @mock.patch.object( 6104 masking.VMAXMasking, 6105 '_create_new_masking_view', 6106 return_value=None) 6107 def test_get_or_create_masking_view( 6108 self, mock_create_mv, mock_validate_mv, 6109 mock_get_mv): 6110 for x in range(0, 3): 6111 self.driver.masking._get_or_create_masking_view( 6112 self.data.array, self.maskingviewdict, 6113 self.data.defaultstoragegroup_name, self.extra_specs) 6114 mock_create_mv.assert_called_once() 6115 6116 @mock.patch.object( 6117 masking.VMAXMasking, 6118 '_get_or_create_storage_group', 6119 side_effect=["Storage group not found", None, 6120 "Storage group not found", None, None, None, 6121 None, None, None, None, None]) 6122 @mock.patch.object( 6123 masking.VMAXMasking, 6124 '_check_port_group', 6125 side_effect=[(None, "Port group error"), (None, None), (None, None), 6126 (None, None)]) 6127 @mock.patch.object( 6128 masking.VMAXMasking, 6129 '_get_or_create_initiator_group', 6130 side_effect=[(None, "Initiator group error"), (None, None), 6131 (None, None)]) 6132 @mock.patch.object( 6133 masking.VMAXMasking, 6134 '_move_vol_from_default_sg', 6135 side_effect=["Storage group error", None]) 6136 @mock.patch.object( 6137 masking.VMAXMasking, 6138 'create_masking_view', 6139 return_value=None) 6140 def test_create_new_masking_view( 6141 self, mock_create_mv, mock_move, mock_create_IG, 6142 mock_check_PG, mock_create_SG): 6143 for x in range(0, 6): 6144 self.driver.masking._create_new_masking_view( 6145 self.data.array, self.maskingviewdict, 6146 self.maskingviewdict['maskingview_name'], 6147 self.data.defaultstoragegroup_name, self.extra_specs) 6148 mock_create_mv.assert_called_once() 6149 6150 @mock.patch.object( 6151 masking.VMAXMasking, 6152 '_check_existing_storage_group', 6153 side_effect=[(VMAXCommonData.storagegroup_name_i, None), 6154 (VMAXCommonData.storagegroup_name_i, None), 6155 (None, "Error Checking existing storage group")]) 6156 @mock.patch.object( 6157 rest.VMAXRest, 6158 'get_element_from_masking_view', 6159 return_value=VMAXCommonData.port_group_name_i) 6160 @mock.patch.object( 6161 masking.VMAXMasking, 6162 '_check_port_group', 6163 side_effect=[(None, None), (None, "Error checking pg")]) 6164 @mock.patch.object( 6165 masking.VMAXMasking, 6166 '_check_existing_initiator_group', 6167 return_value=(VMAXCommonData.initiatorgroup_name_i, None)) 6168 def test_validate_existing_masking_view( 6169 self, mock_check_ig, mock_check_pg, mock_get_mv_element, 6170 mock_check_sg): 6171 for x in range(0, 3): 6172 self.driver.masking._validate_existing_masking_view( 6173 self.data.array, self.maskingviewdict, 6174 self.maskingviewdict['maskingview_name'], 6175 self.data.defaultstoragegroup_name, self.extra_specs) 6176 self.assertEqual(3, mock_check_sg.call_count) 6177 mock_get_mv_element.assert_called_with( 6178 self.data.array, self.maskingviewdict['maskingview_name'], 6179 portgroup=True) 6180 mock_check_ig.assert_called_once() 6181 6182 @mock.patch.object( 6183 rest.VMAXRest, 6184 'get_storage_group', 6185 side_effect=[VMAXCommonData.storagegroup_name_i, None, None]) 6186 @mock.patch.object( 6187 provision.VMAXProvision, 6188 'create_storage_group', 6189 side_effect=[VMAXCommonData.storagegroup_name_i, None]) 6190 def test_get_or_create_storage_group(self, mock_sg, mock_get_sg): 6191 for x in range(0, 2): 6192 self.driver.masking._get_or_create_storage_group( 6193 self.data.array, self.maskingviewdict, 6194 self.data.storagegroup_name_i, self.extra_specs) 6195 self.driver.masking._get_or_create_storage_group( 6196 self.data.array, self.maskingviewdict, 6197 self.data.storagegroup_name_i, self.extra_specs, True) 6198 self.assertEqual(3, mock_get_sg.call_count) 6199 self.assertEqual(2, mock_sg.call_count) 6200 6201 @mock.patch.object( 6202 masking.VMAXMasking, 6203 '_move_vol_from_default_sg', 6204 return_value=None) 6205 @mock.patch.object( 6206 masking.VMAXMasking, 6207 '_get_or_create_storage_group', 6208 return_value=None) 6209 @mock.patch.object( 6210 rest.VMAXRest, 6211 'get_element_from_masking_view', 6212 return_value=VMAXCommonData.parent_sg_i) 6213 @mock.patch.object( 6214 rest.VMAXRest, 6215 'is_child_sg_in_parent_sg', 6216 side_effect=[True, False]) 6217 @mock.patch.object( 6218 masking.VMAXMasking, 6219 '_check_add_child_sg_to_parent_sg', 6220 return_value=None) 6221 def test_check_existing_storage_group_success( 6222 self, mock_add_sg, mock_is_child, mock_get_mv_element, 6223 mock_create_sg, mock_move): 6224 masking_view_dict = deepcopy(self.data.masking_view_dict) 6225 masking_view_dict['extra_specs'] = self.data.extra_specs 6226 with mock.patch.object(self.driver.rest, 'get_storage_group', 6227 side_effect=[ 6228 VMAXCommonData.parent_sg_i, 6229 VMAXCommonData.storagegroup_name_i]): 6230 _, msg = ( 6231 self.driver.masking._check_existing_storage_group( 6232 self.data.array, self.maskingviewdict['maskingview_name'], 6233 self.data.defaultstoragegroup_name, masking_view_dict)) 6234 self.assertIsNone(msg) 6235 mock_create_sg.assert_not_called() 6236 with mock.patch.object(self.driver.rest, 'get_storage_group', 6237 side_effect=[ 6238 VMAXCommonData.parent_sg_i, None]): 6239 _, msg = ( 6240 self.driver.masking._check_existing_storage_group( 6241 self.data.array, self.maskingviewdict['maskingview_name'], 6242 self.data.defaultstoragegroup_name, masking_view_dict)) 6243 self.assertIsNone(msg) 6244 mock_create_sg.assert_called_once_with( 6245 self.data.array, masking_view_dict, 6246 VMAXCommonData.storagegroup_name_f, 6247 self.data.extra_specs) 6248 6249 @mock.patch.object( 6250 masking.VMAXMasking, 6251 '_move_vol_from_default_sg', 6252 side_effect=[None, "Error Message"]) 6253 @mock.patch.object( 6254 rest.VMAXRest, 6255 'is_child_sg_in_parent_sg', 6256 side_effect=[True, False, False]) 6257 @mock.patch.object( 6258 rest.VMAXRest, 6259 'get_element_from_masking_view', 6260 return_value=VMAXCommonData.parent_sg_i) 6261 @mock.patch.object( 6262 rest.VMAXRest, 6263 'get_storage_group', 6264 side_effect=[None, VMAXCommonData.parent_sg_i, None, 6265 VMAXCommonData.parent_sg_i, None, 6266 VMAXCommonData.parent_sg_i, None]) 6267 def test_check_existing_storage_group_failed( 6268 self, mock_get_sg, mock_get_mv_element, mock_child, mock_move): 6269 masking_view_dict = deepcopy(self.data.masking_view_dict) 6270 masking_view_dict['extra_specs'] = self.data.extra_specs 6271 for x in range(0, 4): 6272 _, msg = ( 6273 self.driver.masking._check_existing_storage_group( 6274 self.data.array, self.maskingviewdict['maskingview_name'], 6275 self.data.defaultstoragegroup_name, masking_view_dict)) 6276 self.assertIsNotNone(msg) 6277 self.assertEqual(7, mock_get_sg.call_count) 6278 self.assertEqual(1, mock_move.call_count) 6279 6280 @mock.patch.object(rest.VMAXRest, 'get_portgroup', 6281 side_effect=[VMAXCommonData.port_group_name_i, None]) 6282 def test_check_port_group( 6283 self, mock_get_pg): 6284 for x in range(0, 2): 6285 _, msg = self.driver.masking._check_port_group( 6286 self.data.array, self.maskingviewdict['maskingview_name']) 6287 self.assertIsNotNone(msg) 6288 self.assertEqual(2, mock_get_pg.call_count) 6289 6290 @mock.patch.object( 6291 masking.VMAXMasking, '_find_initiator_group', 6292 side_effect=[VMAXCommonData.initiatorgroup_name_i, None, None]) 6293 @mock.patch.object(masking.VMAXMasking, '_create_initiator_group', 6294 side_effect=[VMAXCommonData.initiatorgroup_name_i, None] 6295 ) 6296 def test_get_or_create_initiator_group(self, mock_create_ig, mock_find_ig): 6297 self.driver.masking._get_or_create_initiator_group( 6298 self.data.array, self.data.initiatorgroup_name_i, 6299 self.data.connector, self.extra_specs) 6300 mock_create_ig.assert_not_called() 6301 found_init_group, msg = ( 6302 self.driver.masking._get_or_create_initiator_group( 6303 self.data.array, self.data.initiatorgroup_name_i, 6304 self.data.connector, self.extra_specs)) 6305 self.assertIsNone(msg) 6306 found_init_group, msg = ( 6307 self.driver.masking._get_or_create_initiator_group( 6308 self.data.array, self.data.initiatorgroup_name_i, 6309 self.data.connector, self.extra_specs)) 6310 self.assertIsNotNone(msg) 6311 6312 def test_check_existing_initiator_group(self): 6313 with mock.patch.object( 6314 rest.VMAXRest, 'get_element_from_masking_view', 6315 return_value=VMAXCommonData.inititiatorgroup): 6316 ig_from_mv, msg = ( 6317 self.driver.masking._check_existing_initiator_group( 6318 self.data.array, self.maskingviewdict['maskingview_name'], 6319 self.maskingviewdict, self.data.storagegroup_name_i, 6320 self.data.port_group_name_i, self.extra_specs)) 6321 self.assertEqual(self.data.inititiatorgroup, ig_from_mv) 6322 6323 def test_check_adding_volume_to_storage_group(self): 6324 with mock.patch.object( 6325 masking.VMAXMasking, '_create_initiator_group'): 6326 with mock.patch.object( 6327 rest.VMAXRest, 'is_volume_in_storagegroup', 6328 side_effect=[True, False]): 6329 msg = ( 6330 self.driver.masking._check_adding_volume_to_storage_group( 6331 self.data.array, self.device_id, 6332 self.data.storagegroup_name_i, 6333 self.maskingviewdict[utils.VOL_NAME], 6334 self.maskingviewdict[utils.EXTRA_SPECS])) 6335 self.assertIsNone(msg) 6336 msg = ( 6337 self.driver.masking._check_adding_volume_to_storage_group( 6338 self.data.array, self.device_id, 6339 self.data.storagegroup_name_i, 6340 self.maskingviewdict[utils.VOL_NAME], 6341 self.maskingviewdict[utils.EXTRA_SPECS])) 6342 6343 @mock.patch.object(rest.VMAXRest, 'add_vol_to_sg') 6344 def test_add_volume_to_storage_group(self, mock_add_volume): 6345 self.driver.masking.add_volume_to_storage_group( 6346 self.data.array, self.device_id, self.data.storagegroup_name_i, 6347 self.volume_name, self.extra_specs) 6348 mock_add_volume.assert_called_once() 6349 6350 @mock.patch.object(rest.VMAXRest, 'remove_vol_from_sg') 6351 def test_remove_vol_from_storage_group(self, mock_remove_volume): 6352 with mock.patch.object( 6353 rest.VMAXRest, 'is_volume_in_storagegroup', 6354 side_effect=[False, True]): 6355 self.driver.masking.remove_vol_from_storage_group( 6356 self.data.array, self.device_id, self.data.storagegroup_name_i, 6357 self.volume_name, self.extra_specs) 6358 mock_remove_volume.assert_called_once() 6359 self.assertRaises( 6360 exception.VolumeBackendAPIException, 6361 self.driver.masking.remove_vol_from_storage_group, 6362 self.data.array, self.device_id, self.data.storagegroup_name_i, 6363 self.volume_name, self.extra_specs) 6364 6365 def test_find_initiator_names(self): 6366 foundinitiatornames = self.driver.masking.find_initiator_names( 6367 self.data.connector) 6368 self.assertEqual(self.data.connector['initiator'], 6369 foundinitiatornames[0]) 6370 foundinitiatornames = self.driver_fc.masking.find_initiator_names( 6371 self.data.connector) 6372 self.assertEqual(self.data.connector['wwpns'][0], 6373 foundinitiatornames[0]) 6374 connector = {'ip': self.data.ip, 'initiator': None, 'host': 'HostX'} 6375 self.assertRaises( 6376 exception.VolumeBackendAPIException, 6377 self.driver.masking.find_initiator_names, connector) 6378 self.assertRaises( 6379 exception.VolumeBackendAPIException, 6380 self.driver_fc.masking.find_initiator_names, connector) 6381 6382 def test_find_initiator_group_found(self): 6383 with mock.patch.object( 6384 rest.VMAXRest, 'get_initiator_list', 6385 return_value=self.data.initiator_list[2]['initiatorId']): 6386 with mock.patch.object( 6387 rest.VMAXRest, 'get_initiator_group_from_initiator', 6388 return_value=self.data.initiator_list): 6389 found_init_group_nam = ( 6390 self.driver.masking._find_initiator_group( 6391 self.data.array, ['FA-1D:4:123456789012345'])) 6392 self.assertEqual(self.data.initiator_list, 6393 found_init_group_nam) 6394 6395 def test_find_initiator_group_not_found(self): 6396 with mock.patch.object( 6397 rest.VMAXRest, 'get_initiator_list', 6398 return_value=self.data.initiator_list[2]['initiatorId']): 6399 with mock.patch.object( 6400 rest.VMAXRest, 'get_initiator_group_from_initiator', 6401 return_value=None): 6402 found_init_group_nam = ( 6403 self.driver.masking._find_initiator_group( 6404 self.data.array, ['Error'])) 6405 self.assertIsNone(found_init_group_nam) 6406 6407 def test_create_masking_view(self): 6408 with mock.patch.object(rest.VMAXRest, 'create_masking_view', 6409 side_effect=[None, Exception]): 6410 error_message = self.driver.masking.create_masking_view( 6411 self.data.array, self.maskingviewdict['maskingview_name'], 6412 self.data.storagegroup_name_i, self.data.port_group_name_i, 6413 self.data.initiatorgroup_name_i, self.extra_specs) 6414 self.assertIsNone(error_message) 6415 error_message = self.driver.masking.create_masking_view( 6416 self.data.array, self.maskingviewdict['maskingview_name'], 6417 self.data.storagegroup_name_i, self.data.port_group_name_i, 6418 self.data.initiatorgroup_name_i, self.extra_specs) 6419 self.assertIsNotNone(error_message) 6420 6421 @mock.patch.object(masking.VMAXMasking, '_check_ig_rollback') 6422 def test_check_if_rollback_action_for_masking_required(self, 6423 mock_check_ig): 6424 with mock.patch.object(rest.VMAXRest, 6425 'get_storage_groups_from_volume', 6426 side_effect=[ 6427 exception.VolumeBackendAPIException, 6428 self.data.storagegroup_list, 6429 self.data.storagegroup_list, None, 6430 None, ]): 6431 self.assertRaises( 6432 exception.VolumeBackendAPIException, 6433 self.mask.check_if_rollback_action_for_masking_required, 6434 self.data.array, self.data.test_volume, 6435 self.device_id, self.maskingviewdict) 6436 with mock.patch.object(masking.VMAXMasking, 6437 'remove_and_reset_members'): 6438 self.maskingviewdict[ 6439 'default_sg_name'] = self.data.defaultstoragegroup_name 6440 error_message = ( 6441 self.mask.check_if_rollback_action_for_masking_required( 6442 self.data.array, self.data.test_volume, 6443 self.device_id, self.maskingviewdict)) 6444 self.assertIsNone(error_message) 6445 6446 @mock.patch.object(rest.VMAXRest, 'delete_masking_view') 6447 @mock.patch.object(rest.VMAXRest, 'delete_initiator_group') 6448 @mock.patch.object(rest.VMAXRest, 'get_initiator_group') 6449 @mock.patch.object(masking.VMAXMasking, '_find_initiator_group', 6450 return_value=VMAXCommonData.initiatorgroup_name_i) 6451 def test_verify_initiator_group_from_masking_view( 6452 self, mock_find_ig, mock_get_ig, mock_delete_ig, mock_delete_mv): 6453 self.mask._verify_initiator_group_from_masking_view( 6454 self.data.array, self.maskingviewdict['maskingview_name'], 6455 self.maskingviewdict, self.data.initiatorgroup_name_i, 6456 self.data.storagegroup_name_i, self.data.port_group_name_i, 6457 self.extra_specs) 6458 mock_get_ig.assert_not_called() 6459 mock_get_ig.return_value = False 6460 self.mask._verify_initiator_group_from_masking_view( 6461 self.data.array, self.maskingviewdict['maskingview_name'], 6462 self.maskingviewdict, 'OS-Wrong-Host-I-IG', 6463 self.data.storagegroup_name_i, self.data.port_group_name_i, 6464 self.extra_specs) 6465 mock_get_ig.assert_called() 6466 6467 @mock.patch.object(rest.VMAXRest, 'delete_masking_view') 6468 @mock.patch.object(rest.VMAXRest, 'delete_initiator_group') 6469 @mock.patch.object(rest.VMAXRest, 'get_initiator_group', 6470 return_value=True) 6471 @mock.patch.object(masking.VMAXMasking, '_find_initiator_group', 6472 return_value=VMAXCommonData.initiatorgroup_name_i) 6473 def test_verify_initiator_group_from_masking_view2( 6474 self, mock_find_ig, mock_get_ig, mock_delete_ig, mock_delete_mv): 6475 mock_delete_mv.side_effect = [None, Exception] 6476 self.mask._verify_initiator_group_from_masking_view( 6477 self.data.array, self.maskingviewdict['maskingview_name'], 6478 self.maskingviewdict, 'OS-Wrong-Host-I-IG', 6479 self.data.storagegroup_name_i, self.data.port_group_name_i, 6480 self.extra_specs) 6481 mock_delete_mv.assert_called() 6482 _, found_ig_from_connector = ( 6483 self.mask._verify_initiator_group_from_masking_view( 6484 self.data.array, self.maskingviewdict['maskingview_name'], 6485 self.maskingviewdict, 'OS-Wrong-Host-I-IG', 6486 self.data.storagegroup_name_i, self.data.port_group_name_i, 6487 self.extra_specs)) 6488 self.assertEqual(self.data.initiatorgroup_name_i, 6489 found_ig_from_connector) 6490 6491 @mock.patch.object(rest.VMAXRest, 'create_initiator_group') 6492 def test_create_initiator_group(self, mock_create_ig): 6493 initiator_names = self.mask.find_initiator_names(self.data.connector) 6494 ret_init_group_name = self.mask._create_initiator_group( 6495 self.data.array, self.data.initiatorgroup_name_i, initiator_names, 6496 self.extra_specs) 6497 self.assertEqual(self.data.initiatorgroup_name_i, ret_init_group_name) 6498 6499 @mock.patch.object(masking.VMAXMasking, 6500 '_last_volume_delete_initiator_group') 6501 def test_check_ig_rollback(self, mock_last_volume): 6502 with mock.patch.object(masking.VMAXMasking, '_find_initiator_group', 6503 side_effect=[ 6504 None, 'FAKE-I-IG', 6505 self.data.initiatorgroup_name_i]): 6506 for x in range(0, 2): 6507 self.mask._check_ig_rollback(self.data.array, 6508 self.data.initiatorgroup_name_i, 6509 self.data.connector) 6510 mock_last_volume.assert_not_called() 6511 self.mask._check_ig_rollback( 6512 self.data.array, self.data.initiatorgroup_name_i, 6513 self.data.connector) 6514 mock_last_volume.assert_called() 6515 6516 @mock.patch.object(masking.VMAXMasking, '_cleanup_deletion') 6517 def test_remove_and_reset_members(self, mock_cleanup): 6518 self.mask.remove_and_reset_members( 6519 self.data.array, self.device_id, self.data.test_volume, 6520 self.volume_name, self.extra_specs, reset=False) 6521 mock_cleanup.assert_called_once() 6522 6523 @mock.patch.object(rest.VMAXRest, 'get_storage_groups_from_volume', 6524 side_effect=[[VMAXCommonData.storagegroup_name_i], 6525 [VMAXCommonData.storagegroup_name_i], 6526 [VMAXCommonData.storagegroup_name_i, 6527 VMAXCommonData.storagegroup_name_f]]) 6528 @mock.patch.object(masking.VMAXMasking, 'remove_volume_from_sg') 6529 @mock.patch.object(masking.VMAXMasking, 6530 'add_volume_to_default_storage_group') 6531 def test_cleanup_deletion(self, mock_add, mock_remove_vol, mock_get_sg): 6532 self.mask._cleanup_deletion( 6533 self.data.array, self.data.test_volume, self.device_id, 6534 self.volume_name, self.extra_specs, None, True, None) 6535 mock_add.assert_not_called() 6536 self.mask._cleanup_deletion( 6537 self.data.array, self.data.test_volume, self.device_id, 6538 self.volume_name, self.extra_specs, 6539 self.data.connector, True, None) 6540 mock_add.assert_not_called() 6541 self.mask._cleanup_deletion( 6542 self.data.array, self.data.test_volume, self.device_id, 6543 self.volume_name, self.extra_specs, None, True, None) 6544 mock_add.assert_called_once_with( 6545 self.data.array, self.device_id, 6546 self.volume_name, self.extra_specs, volume=self.data.test_volume) 6547 6548 @mock.patch.object(masking.VMAXMasking, '_last_vol_in_sg') 6549 @mock.patch.object(masking.VMAXMasking, '_multiple_vols_in_sg') 6550 def test_remove_volume_from_sg(self, mock_multiple_vols, mock_last_vol): 6551 with mock.patch.object( 6552 rest.VMAXRest, 'get_masking_views_from_storage_group', 6553 return_value=None): 6554 with mock.patch.object( 6555 rest.VMAXRest, 'get_num_vols_in_sg', 6556 side_effect=[2, 1]): 6557 self.mask.remove_volume_from_sg( 6558 self.data.array, self.device_id, self.volume_name, 6559 self.data.defaultstoragegroup_name, self.extra_specs) 6560 mock_last_vol.assert_not_called() 6561 self.mask.remove_volume_from_sg( 6562 self.data.array, self.device_id, self.volume_name, 6563 self.data.defaultstoragegroup_name, self.extra_specs) 6564 mock_last_vol.assert_called() 6565 6566 @mock.patch.object(masking.VMAXMasking, '_last_vol_in_sg') 6567 @mock.patch.object(masking.VMAXMasking, '_multiple_vols_in_sg') 6568 def test_remove_volume_from_sg_2(self, mock_multiple_vols, mock_last_vol): 6569 with mock.patch.object( 6570 rest.VMAXRest, 'is_volume_in_storagegroup', 6571 return_value=True): 6572 with mock.patch.object( 6573 rest.VMAXRest, 'get_masking_views_from_storage_group', 6574 return_value=[self.data.masking_view_name_i]): 6575 with mock.patch.object( 6576 rest.VMAXRest, 'get_num_vols_in_sg', 6577 side_effect=[2, 1]): 6578 self.mask.remove_volume_from_sg( 6579 self.data.array, self.device_id, self.volume_name, 6580 self.data.storagegroup_name_i, self.extra_specs) 6581 mock_last_vol.assert_not_called() 6582 self.mask.remove_volume_from_sg( 6583 self.data.array, self.device_id, self.volume_name, 6584 self.data.storagegroup_name_i, self.extra_specs) 6585 mock_last_vol.assert_called() 6586 6587 @mock.patch.object(masking.VMAXMasking, '_last_vol_masking_views', 6588 return_value=True) 6589 @mock.patch.object(masking.VMAXMasking, '_last_vol_no_masking_views', 6590 return_value=True) 6591 def test_last_vol_in_sg(self, mock_no_mv, mock_mv): 6592 mv_list = [self.data.masking_view_name_i, 6593 self.data.masking_view_name_f] 6594 with mock.patch.object(rest.VMAXRest, 6595 'get_masking_views_from_storage_group', 6596 side_effect=[mv_list, []]): 6597 for x in range(0, 2): 6598 self.mask._last_vol_in_sg( 6599 self.data.array, self.device_id, self.volume_name, 6600 self.data.storagegroup_name_i, self.extra_specs, 6601 self.data.connector) 6602 self.assertEqual(1, mock_mv.call_count) 6603 self.assertEqual(1, mock_no_mv.call_count) 6604 6605 @mock.patch.object(masking.VMAXMasking, '_remove_last_vol_and_delete_sg') 6606 @mock.patch.object(masking.VMAXMasking, '_delete_cascaded_storage_groups') 6607 @mock.patch.object(rest.VMAXRest, 'get_num_vols_in_sg', 6608 side_effect=[1, 3]) 6609 @mock.patch.object(rest.VMAXRest, 'delete_storage_group') 6610 @mock.patch.object(masking.VMAXMasking, 'get_parent_sg_from_child', 6611 side_effect=[None, 'parent_sg_name', 'parent_sg_name']) 6612 def test_last_vol_no_masking_views( 6613 self, mock_get_parent, mock_delete, mock_num_vols, 6614 mock_delete_casc, mock_remove): 6615 for x in range(0, 3): 6616 self.mask._last_vol_no_masking_views( 6617 self.data.array, self.data.storagegroup_name_i, 6618 self.device_id, self.volume_name, self.extra_specs, 6619 False) 6620 self.assertEqual(1, mock_delete.call_count) 6621 self.assertEqual(1, mock_delete_casc.call_count) 6622 self.assertEqual(1, mock_remove.call_count) 6623 6624 @mock.patch.object(masking.VMAXMasking, '_remove_last_vol_and_delete_sg') 6625 @mock.patch.object(masking.VMAXMasking, '_delete_mv_ig_and_sg') 6626 @mock.patch.object(masking.VMAXMasking, '_get_num_vols_from_mv', 6627 side_effect=[(1, 'parent_name'), (3, 'parent_name')]) 6628 def test_last_vol_masking_views( 6629 self, mock_num_vols, mock_delete_all, mock_remove): 6630 for x in range(0, 2): 6631 self.mask._last_vol_masking_views( 6632 self.data.array, self.data.storagegroup_name_i, 6633 [self.data.masking_view_name_i], self.device_id, 6634 self.volume_name, self.extra_specs, self.data.connector, 6635 True) 6636 self.assertEqual(1, mock_delete_all.call_count) 6637 self.assertEqual(1, mock_remove.call_count) 6638 6639 @mock.patch.object(masking.VMAXMasking, 6640 'add_volume_to_default_storage_group') 6641 @mock.patch.object(rest.VMAXRest, 'get_num_vols_in_sg') 6642 @mock.patch.object(masking.VMAXMasking, 'remove_vol_from_storage_group') 6643 def test_multiple_vols_in_sg(self, mock_remove_vol, mock_get_volumes, 6644 mock_add): 6645 self.mask._multiple_vols_in_sg( 6646 self.data.array, self.device_id, self.data.storagegroup_name_i, 6647 self.volume_name, self.extra_specs, False) 6648 mock_remove_vol.assert_called_once() 6649 self.mask._multiple_vols_in_sg( 6650 self.data.array, self.device_id, self.data.storagegroup_name_i, 6651 self.volume_name, self.extra_specs, True) 6652 mock_add.assert_called_once() 6653 6654 @mock.patch.object(rest.VMAXRest, 'get_element_from_masking_view') 6655 @mock.patch.object(masking.VMAXMasking, '_last_volume_delete_masking_view') 6656 @mock.patch.object(masking.VMAXMasking, 6657 '_last_volume_delete_initiator_group') 6658 @mock.patch.object(masking.VMAXMasking, '_delete_cascaded_storage_groups') 6659 def test_delete_mv_ig_and_sg(self, mock_delete_sg, mock_delete_ig, 6660 mock_delete_mv, mock_get_element): 6661 self.mask._delete_mv_ig_and_sg( 6662 self.data.array, self.data.device_id, 6663 self.data.masking_view_name_i, 6664 self.data.storagegroup_name_i, self.data.parent_sg_i, 6665 self.data.connector, True, self.data.extra_specs) 6666 mock_delete_sg.assert_called_once() 6667 6668 @mock.patch.object(rest.VMAXRest, 'delete_masking_view') 6669 def test_last_volume_delete_masking_view(self, mock_delete_mv): 6670 self.mask._last_volume_delete_masking_view( 6671 self.data.array, self.data.masking_view_name_i) 6672 mock_delete_mv.assert_called_once() 6673 6674 @mock.patch.object(rest.VMAXRest, 'move_volume_between_storage_groups') 6675 @mock.patch.object(masking.VMAXMasking, 6676 'get_or_create_default_storage_group') 6677 @mock.patch.object(masking.VMAXMasking, 'add_volume_to_storage_group') 6678 def test_add_volume_to_default_storage_group( 6679 self, mock_add_sg, mock_get_sg, mock_move): 6680 self.mask.add_volume_to_default_storage_group( 6681 self.data.array, self.device_id, self.volume_name, 6682 self.extra_specs) 6683 mock_add_sg.assert_called_once() 6684 self.mask.add_volume_to_default_storage_group( 6685 self.data.array, self.device_id, self.volume_name, 6686 self.extra_specs, src_sg=self.data.storagegroup_name_i) 6687 mock_move.assert_called_once() 6688 mock_add_sg.reset_mock() 6689 vol_grp_member = deepcopy(self.data.test_volume) 6690 vol_grp_member.group_id = self.data.test_vol_grp_name_id_only 6691 vol_grp_member.group = self.data.test_group 6692 self.mask.add_volume_to_default_storage_group( 6693 self.data.array, self.device_id, self.volume_name, 6694 self.extra_specs, volume=vol_grp_member) 6695 self.assertEqual(2, mock_add_sg.call_count) 6696 6697 def test_add_volume_to_default_storage_group_next_gen(self): 6698 with mock.patch.object(rest.VMAXRest, 'is_next_gen_array', 6699 return_value=True): 6700 with mock.patch.object( 6701 self.mask, 'get_or_create_default_storage_group'): 6702 self.mask.add_volume_to_default_storage_group( 6703 self.data.array, self.device_id, self.volume_name, 6704 self.extra_specs) 6705 (self.mask.get_or_create_default_storage_group 6706 .assert_called_once_with(self.data.array, self.data.srp, 6707 self.extra_specs[utils.SLO], 6708 'NONE', self.extra_specs, False, 6709 False, None)) 6710 6711 @mock.patch.object(provision.VMAXProvision, 'create_storage_group') 6712 def test_get_or_create_default_storage_group(self, mock_create_sg): 6713 with mock.patch.object( 6714 rest.VMAXRest, 'get_vmax_default_storage_group', 6715 return_value=(None, self.data.storagegroup_name_i)): 6716 storage_group_name = self.mask.get_or_create_default_storage_group( 6717 self.data.array, self.data.srp, self.data.slo, 6718 self.data.workload, self.extra_specs) 6719 self.assertEqual(self.data.storagegroup_name_i, storage_group_name) 6720 with mock.patch.object( 6721 rest.VMAXRest, 'get_vmax_default_storage_group', 6722 return_value=("test_sg", self.data.storagegroup_name_i)): 6723 with mock.patch.object( 6724 rest.VMAXRest, 'get_masking_views_from_storage_group', 6725 return_value=self.data.masking_view_name_i): 6726 self.assertRaises( 6727 exception.VolumeBackendAPIException, 6728 self.mask.get_or_create_default_storage_group, 6729 self.data.array, self.data.srp, self.data.slo, 6730 self.data.workload, self.extra_specs) 6731 6732 @mock.patch.object(masking.VMAXMasking, 6733 'add_volume_to_default_storage_group') 6734 @mock.patch.object(rest.VMAXRest, 'remove_child_sg_from_parent_sg') 6735 @mock.patch.object(rest.VMAXRest, 'delete_storage_group') 6736 @mock.patch.object(masking.VMAXMasking, 'remove_vol_from_storage_group') 6737 def test_remove_last_vol_and_delete_sg(self, mock_vol_sg, 6738 mock_delete_sg, mock_rm, mock_add): 6739 self.mask._remove_last_vol_and_delete_sg( 6740 self.data.array, self.device_id, self.volume_name, 6741 self.data.storagegroup_name_i, self.extra_specs) 6742 self.mask._remove_last_vol_and_delete_sg( 6743 self.data.array, self.device_id, self.volume_name, 6744 self.data.storagegroup_name_i, self.extra_specs, 6745 self.data.parent_sg_i, True) 6746 self.assertEqual(2, mock_delete_sg.call_count) 6747 self.assertEqual(1, mock_vol_sg.call_count) 6748 self.assertEqual(1, mock_rm.call_count) 6749 self.assertEqual(1, mock_add.call_count) 6750 6751 @mock.patch.object(rest.VMAXRest, 'delete_initiator_group') 6752 def test_last_volume_delete_initiator_group(self, mock_delete_ig): 6753 self.mask._last_volume_delete_initiator_group( 6754 self.data.array, self.data.initiatorgroup_name_f, 'Wrong_Host') 6755 mock_delete_ig.assert_not_called() 6756 self.mask._last_volume_delete_initiator_group( 6757 self.data.array, self.data.initiatorgroup_name_f, None) 6758 mock_delete_ig.assert_not_called() 6759 mv_list = [self.data.masking_view_name_i, 6760 self.data.masking_view_name_f] 6761 with mock.patch.object(rest.VMAXRest, 6762 'get_masking_views_by_initiator_group', 6763 side_effect=[mv_list, []]): 6764 self.mask._last_volume_delete_initiator_group( 6765 self.data.array, self.data.initiatorgroup_name_i, 6766 self.data.connector['host']) 6767 mock_delete_ig.assert_not_called() 6768 self.mask._last_volume_delete_initiator_group( 6769 self.data.array, self.data.initiatorgroup_name_i, 6770 self.data.connector['host']) 6771 mock_delete_ig.assert_called_once() 6772 6773 def test_populate_masking_dict_init_check_false(self): 6774 extra_specs = self.data.extra_specs 6775 connector = self.data.connector 6776 with mock.patch.object(self.driver, '_get_initiator_check_flag', 6777 return_value=False): 6778 masking_view_dict = self.driver._populate_masking_dict( 6779 self.data.test_volume, connector, extra_specs) 6780 self.assertFalse(masking_view_dict['initiator_check']) 6781 6782 def test_populate_masking_dict_init_check_true(self): 6783 extra_specs = self.data.extra_specs 6784 connector = self.data.connector 6785 with mock.patch.object(self.driver, '_get_initiator_check_flag', 6786 return_value=True): 6787 masking_view_dict = self.driver._populate_masking_dict( 6788 self.data.test_volume, connector, extra_specs) 6789 self.assertTrue(masking_view_dict['initiator_check']) 6790 6791 def test_check_existing_initiator_group_verify_true(self): 6792 mv_dict = deepcopy(self.data.masking_view_dict) 6793 mv_dict['initiator_check'] = True 6794 with mock.patch.object( 6795 rest.VMAXRest, 'get_element_from_masking_view', 6796 return_value=VMAXCommonData.initiatorgroup_name_f): 6797 with mock.patch.object( 6798 self.mask, '_verify_initiator_group_from_masking_view', 6799 return_value=(True, self.data.initiatorgroup_name_f)): 6800 self.mask._check_existing_initiator_group( 6801 self.data.array, self.data.masking_view_name_f, 6802 mv_dict, self.data.storagegroup_name_f, 6803 self.data.port_group_name_f, self.data.extra_specs) 6804 (self.mask._verify_initiator_group_from_masking_view. 6805 assert_called_once_with( 6806 self.data.array, self.data.masking_view_name_f, 6807 mv_dict, self.data.initiatorgroup_name_f, 6808 self.data.storagegroup_name_f, 6809 self.data.port_group_name_f, self.data.extra_specs)) 6810 6811 @mock.patch.object(masking.VMAXMasking, 'add_child_sg_to_parent_sg', 6812 side_effect=[ 6813 None, exception.VolumeBackendAPIException]) 6814 @mock.patch.object(rest.VMAXRest, 'is_child_sg_in_parent_sg', 6815 side_effect=[True, False, False]) 6816 def test_check_add_child_sg_to_parent_sg(self, mock_is_child, mock_add): 6817 for x in range(0, 3): 6818 message = self.mask._check_add_child_sg_to_parent_sg( 6819 self.data.array, self.data.storagegroup_name_i, 6820 self.data.parent_sg_i, self.data.extra_specs) 6821 self.assertIsNotNone(message) 6822 6823 @mock.patch.object(rest.VMAXRest, 'add_child_sg_to_parent_sg') 6824 @mock.patch.object(rest.VMAXRest, 'is_child_sg_in_parent_sg', 6825 side_effect=[True, False]) 6826 def test_add_child_sg_to_parent_sg(self, mock_is_child, mock_add): 6827 for x in range(0, 2): 6828 self.mask.add_child_sg_to_parent_sg( 6829 self.data.array, self.data.storagegroup_name_i, 6830 self.data.parent_sg_i, self.data.extra_specs) 6831 self.assertEqual(1, mock_add.call_count) 6832 6833 def test_get_parent_sg_from_child(self): 6834 with mock.patch.object(self.driver.rest, 'get_storage_group', 6835 side_effect=[None, self.data.sg_details[1]]): 6836 sg_name = self.mask.get_parent_sg_from_child( 6837 self.data.array, self.data.storagegroup_name_i) 6838 self.assertIsNone(sg_name) 6839 sg_name2 = self.mask.get_parent_sg_from_child( 6840 self.data.array, self.data.storagegroup_name_f) 6841 self.assertEqual(self.data.parent_sg_f, sg_name2) 6842 6843 @mock.patch.object(rest.VMAXRest, 'get_element_from_masking_view', 6844 return_value='parent_sg') 6845 @mock.patch.object(rest.VMAXRest, 'get_num_vols_in_sg', 6846 return_value=2) 6847 def test_get_num_vols_from_mv(self, mock_num, mock_element): 6848 num_vols, sg = self.mask._get_num_vols_from_mv( 6849 self.data.array, self.data.masking_view_name_f) 6850 self.assertEqual(2, num_vols) 6851 6852 @mock.patch.object(masking.VMAXMasking, 6853 'add_volume_to_default_storage_group') 6854 @mock.patch.object(rest.VMAXRest, 'delete_storage_group') 6855 def test_delete_cascaded(self, mock_delete, mock_add): 6856 self.mask._delete_cascaded_storage_groups( 6857 self.data.array, self.data.masking_view_name_f, 6858 self.data.parent_sg_f, self.data.extra_specs, 6859 self.data.device_id, False) 6860 self.assertEqual(2, mock_delete.call_count) 6861 mock_add.assert_not_called() 6862 # Delete legacy masking view, parent sg = child sg 6863 mock_delete.reset_mock() 6864 self.mask._delete_cascaded_storage_groups( 6865 self.data.array, self.data.masking_view_name_f, 6866 self.data.masking_view_name_f, self.data.extra_specs, 6867 self.data.device_id, True) 6868 self.assertEqual(1, mock_delete.call_count) 6869 mock_add.assert_called_once() 6870 6871 @mock.patch.object(masking.VMAXMasking, 'add_child_sg_to_parent_sg') 6872 @mock.patch.object(masking.VMAXMasking, 6873 'move_volume_between_storage_groups') 6874 @mock.patch.object(provision.VMAXProvision, 'create_storage_group') 6875 def test_pre_live_migration(self, mock_create_sg, mock_move, mock_add): 6876 with mock.patch.object( 6877 rest.VMAXRest, 'get_storage_group', 6878 side_effect=[None, self.data.sg_details[1]["storageGroupId"]] 6879 ): 6880 source_sg = self.data.sg_details[2]["storageGroupId"] 6881 source_parent_sg = self.data.sg_details[4]["storageGroupId"] 6882 source_nf_sg = source_parent_sg[:-2] + 'NONFAST' 6883 self.data.iscsi_device_info['device_id'] = self.data.device_id 6884 self.mask.pre_live_migration( 6885 source_nf_sg, source_sg, source_parent_sg, False, 6886 self.data.iscsi_device_info, None) 6887 mock_create_sg.assert_called_once() 6888 6889 @mock.patch.object(rest.VMAXRest, 'delete_storage_group') 6890 @mock.patch.object(rest.VMAXRest, 'remove_child_sg_from_parent_sg') 6891 def test_post_live_migration(self, mock_remove_child_sg, mock_delete_sg): 6892 self.data.iscsi_device_info['source_sg'] = self.data.sg_details[2][ 6893 "storageGroupId"] 6894 self.data.iscsi_device_info['source_parent_sg'] = self.data.sg_details[ 6895 4]["storageGroupId"] 6896 with mock.patch.object( 6897 rest.VMAXRest, 'get_num_vols_in_sg', side_effect=[0, 1]): 6898 self.mask.post_live_migration(self.data.iscsi_device_info, None) 6899 mock_remove_child_sg.assert_called_once() 6900 mock_delete_sg.assert_called_once() 6901 6902 @mock.patch.object(masking.VMAXMasking, 6903 'move_volume_between_storage_groups') 6904 @mock.patch.object(rest.VMAXRest, 'delete_storage_group') 6905 @mock.patch.object(rest.VMAXRest, 'remove_child_sg_from_parent_sg') 6906 @mock.patch.object(masking.VMAXMasking, 'remove_volume_from_sg') 6907 def test_failed_live_migration( 6908 self, mock_remove_volume, mock_remove_child_sg, mock_delete_sg, 6909 mock_move): 6910 device_dict = self.data.iscsi_device_info 6911 device_dict['device_id'] = self.data.device_id 6912 device_dict['source_sg'] = self.data.sg_details[2]["storageGroupId"] 6913 device_dict['source_parent_sg'] = self.data.sg_details[4][ 6914 "storageGroupId"] 6915 device_dict['source_nf_sg'] = ( 6916 self.data.sg_details[4]["storageGroupId"][:-2] + 'NONFAST') 6917 sg_list = [device_dict['source_nf_sg']] 6918 with mock.patch.object( 6919 rest.VMAXRest, 'is_child_sg_in_parent_sg', 6920 side_effect=[True, False]): 6921 self.mask.failed_live_migration(device_dict, sg_list, None) 6922 mock_remove_volume.assert_not_called() 6923 mock_remove_child_sg.assert_called_once() 6924 6925 6926class VMAXCommonReplicationTest(test.TestCase): 6927 def setUp(self): 6928 self.data = VMAXCommonData() 6929 6930 super(VMAXCommonReplicationTest, self).setUp() 6931 config_group = 'CommonReplicationTests' 6932 self.fake_xml = FakeXML().create_fake_config_file( 6933 config_group, self.data.port_group_name_f) 6934 self.replication_device = { 6935 'target_device_id': self.data.remote_array, 6936 'remote_port_group': self.data.port_group_name_f, 6937 'remote_pool': self.data.srp2, 6938 'rdf_group_label': self.data.rdf_group_name, 6939 'allow_extend': 'True'} 6940 volume_utils.get_max_over_subscription_ratio = mock.Mock() 6941 configuration = FakeConfiguration( 6942 self.fake_xml, config_group, 6943 replication_device=self.replication_device) 6944 rest.VMAXRest._establish_rest_session = mock.Mock( 6945 return_value=FakeRequestsSession()) 6946 driver = fc.VMAXFCDriver(configuration=configuration) 6947 iscsi_fake_xml = FakeXML().create_fake_config_file( 6948 config_group, self.data.port_group_name_i) 6949 iscsi_config = FakeConfiguration( 6950 iscsi_fake_xml, config_group, 6951 replication_device=self.replication_device) 6952 iscsi_driver = iscsi.VMAXISCSIDriver(configuration=iscsi_config) 6953 self.iscsi_common = iscsi_driver.common 6954 self.driver = driver 6955 self.common = self.driver.common 6956 self.masking = self.common.masking 6957 self.provision = self.common.provision 6958 self.rest = self.common.rest 6959 self.utils = self.common.utils 6960 self.utils.get_volumetype_extra_specs = ( 6961 mock.Mock( 6962 return_value=self.data.vol_type_extra_specs_rep_enabled)) 6963 self.extra_specs = deepcopy(self.data.extra_specs_rep_enabled) 6964 self.extra_specs['retries'] = 0 6965 self.extra_specs['interval'] = 0 6966 self.extra_specs['rep_mode'] = 'Synchronous' 6967 self.async_rep_device = { 6968 'target_device_id': self.data.remote_array, 6969 'remote_port_group': self.data.port_group_name_f, 6970 'remote_pool': self.data.srp2, 6971 'rdf_group_label': self.data.rdf_group_name, 6972 'allow_extend': 'True', 'mode': 'async'} 6973 async_configuration = FakeConfiguration( 6974 self.fake_xml, config_group, 6975 replication_device=self.async_rep_device) 6976 self.async_driver = fc.VMAXFCDriver(configuration=async_configuration) 6977 self.metro_rep_device = { 6978 'target_device_id': self.data.remote_array, 6979 'remote_port_group': self.data.port_group_name_f, 6980 'remote_pool': self.data.srp2, 6981 'rdf_group_label': self.data.rdf_group_name, 6982 'allow_extend': 'True', 'mode': 'metro'} 6983 metro_configuration = FakeConfiguration( 6984 self.fake_xml, config_group, 6985 replication_device=self.metro_rep_device) 6986 self.metro_driver = fc.VMAXFCDriver(configuration=metro_configuration) 6987 6988 def test_get_replication_info(self): 6989 self.common._get_replication_info() 6990 self.assertTrue(self.common.replication_enabled) 6991 6992 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 6993 return_value=False) 6994 @mock.patch.object(objects.Group, 'get_by_id', 6995 return_value=VMAXCommonData.test_rep_group) 6996 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=True) 6997 @mock.patch.object(utils.VMAXUtils, 'check_replication_matched', 6998 return_value=True) 6999 @mock.patch.object(masking.VMAXMasking, 'add_volume_to_storage_group') 7000 @mock.patch.object( 7001 common.VMAXCommon, '_replicate_volume', 7002 return_value={ 7003 'replication_driver_data': 7004 VMAXCommonData.test_volume.replication_driver_data}) 7005 def test_create_replicated_volume(self, mock_rep, mock_add, mock_match, 7006 mock_check, mock_get, mock_cg): 7007 extra_specs = deepcopy(self.extra_specs) 7008 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7009 vol_identifier = self.utils.get_volume_element_name( 7010 self.data.test_volume.id) 7011 self.common.create_volume(self.data.test_volume) 7012 volume_dict = self.data.provider_location 7013 mock_rep.assert_called_once_with( 7014 self.data.test_volume, vol_identifier, volume_dict, 7015 extra_specs) 7016 # Add volume to replication group 7017 self.common.create_volume(self.data.test_volume_group_member) 7018 mock_add.assert_called_once() 7019 7020 def test_create_cloned_replicated_volume(self): 7021 extra_specs = deepcopy(self.extra_specs) 7022 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7023 with mock.patch.object(self.common, '_replicate_volume', 7024 return_value={}) as mock_rep: 7025 self.common.create_cloned_volume( 7026 self.data.test_clone_volume, self.data.test_volume) 7027 volume_dict = self.data.provider_location 7028 mock_rep.assert_called_once_with( 7029 self.data.test_clone_volume, 7030 self.data.test_clone_volume.name, volume_dict, extra_specs) 7031 7032 def test_create_replicated_volume_from_snap(self): 7033 extra_specs = deepcopy(self.extra_specs) 7034 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7035 with mock.patch.object(self.common, '_replicate_volume', 7036 return_value={}) as mock_rep: 7037 self.common.create_volume_from_snapshot( 7038 self.data.test_clone_volume, self.data.test_snapshot) 7039 volume_dict = self.data.provider_location 7040 mock_rep.assert_called_once_with( 7041 self.data.test_clone_volume, 7042 "snapshot-%s" % self.data.snapshot_id, 7043 volume_dict, 7044 extra_specs) 7045 7046 def test_replicate_volume(self): 7047 volume_dict = self.data.provider_location 7048 rs_enabled = fields.ReplicationStatus.ENABLED 7049 with mock.patch.object(self.common, 'setup_volume_replication', 7050 return_value=(rs_enabled, {})) as mock_setup: 7051 self.common._replicate_volume( 7052 self.data.test_volume, "1", volume_dict, self.extra_specs) 7053 mock_setup.assert_called_once_with( 7054 self.data.array, self.data.test_volume, 7055 self.data.device_id, self.extra_specs) 7056 7057 def test_replicate_volume_exception(self): 7058 volume_dict = self.data.provider_location 7059 with mock.patch.object( 7060 self.common, 'setup_volume_replication', 7061 side_effect=exception.VolumeBackendAPIException(data='')): 7062 with mock.patch.object( 7063 self.common, '_cleanup_replication_source') as mock_clean: 7064 self.assertRaises(exception.VolumeBackendAPIException, 7065 self.common._replicate_volume, 7066 self.data.test_volume, 7067 "1", volume_dict, self.extra_specs) 7068 mock_clean.assert_called_once_with( 7069 self.data.array, self.data.test_volume, "1", 7070 volume_dict, self.extra_specs) 7071 7072 @mock.patch.object(common.VMAXCommon, '_remove_members') 7073 @mock.patch.object(common.VMAXCommon, 7074 '_get_replication_extra_specs', 7075 return_value=VMAXCommonData.rep_extra_specs) 7076 @mock.patch.object(utils.VMAXUtils, 'is_volume_failed_over', 7077 return_value=True) 7078 def test_unmap_lun_volume_failed_over(self, mock_fo, mock_es, mock_rm): 7079 extra_specs = deepcopy(self.extra_specs) 7080 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7081 rep_config = self.utils.get_replication_config( 7082 [self.replication_device]) 7083 self.common._unmap_lun(self.data.test_volume, self.data.connector) 7084 mock_es.assert_called_once_with(extra_specs, rep_config) 7085 7086 @mock.patch.object(common.VMAXCommon, '_remove_members') 7087 @mock.patch.object(common.VMAXCommon, 7088 '_get_replication_extra_specs', 7089 return_value=VMAXCommonData.rep_extra_specs) 7090 @mock.patch.object(utils.VMAXUtils, 'is_metro_device', return_value=True) 7091 def test_unmap_lun_metro(self, mock_md, mock_es, mock_rm): 7092 extra_specs = deepcopy(self.extra_specs) 7093 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7094 self.common._unmap_lun(self.data.test_volume, self.data.connector) 7095 self.assertEqual(2, mock_rm.call_count) 7096 7097 @mock.patch.object(utils.VMAXUtils, 'is_volume_failed_over', 7098 return_value=True) 7099 def test_initialize_connection_vol_failed_over(self, mock_fo): 7100 extra_specs = deepcopy(self.extra_specs) 7101 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7102 rep_extra_specs = deepcopy(VMAXCommonData.rep_extra_specs) 7103 rep_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7104 rep_config = self.utils.get_replication_config( 7105 [self.replication_device]) 7106 with mock.patch.object(self.common, '_get_replication_extra_specs', 7107 return_value=rep_extra_specs) as mock_es: 7108 self.common.initialize_connection( 7109 self.data.test_volume, self.data.connector) 7110 mock_es.assert_called_once_with(extra_specs, rep_config) 7111 7112 @mock.patch.object(utils.VMAXUtils, 'is_metro_device', return_value=True) 7113 def test_initialize_connection_vol_metro(self, mock_md): 7114 metro_connector = deepcopy(self.data.connector) 7115 metro_connector['multipath'] = True 7116 info_dict = self.common.initialize_connection( 7117 self.data.test_volume, metro_connector) 7118 ref_dict = {'array': self.data.array, 7119 'device_id': self.data.device_id, 7120 'hostlunid': 3, 7121 'maskingview': self.data.masking_view_name_f, 7122 'metro_hostlunid': 3} 7123 self.assertEqual(ref_dict, info_dict) 7124 7125 @mock.patch.object(rest.VMAXRest, 'get_iscsi_ip_address_and_iqn', 7126 return_value=([VMAXCommonData.ip], 7127 VMAXCommonData.initiator)) 7128 @mock.patch.object(common.VMAXCommon, '_get_replication_extra_specs', 7129 return_value=VMAXCommonData.rep_extra_specs) 7130 @mock.patch.object(utils.VMAXUtils, 'is_metro_device', return_value=True) 7131 def test_initialize_connection_vol_metro_iscsi(self, mock_md, mock_es, 7132 mock_ip): 7133 metro_connector = deepcopy(self.data.connector) 7134 metro_connector['multipath'] = True 7135 info_dict = self.iscsi_common.initialize_connection( 7136 self.data.test_volume, metro_connector) 7137 ref_dict = {'array': self.data.array, 7138 'device_id': self.data.device_id, 7139 'hostlunid': 3, 7140 'maskingview': self.data.masking_view_name_f, 7141 'ip_and_iqn': [{'ip': self.data.ip, 7142 'iqn': self.data.initiator}], 7143 'metro_hostlunid': 3, 7144 'is_multipath': True, 7145 'metro_ip_and_iqn': [{'ip': self.data.ip, 7146 'iqn': self.data.initiator}]} 7147 self.assertEqual(ref_dict, info_dict) 7148 7149 @mock.patch.object(utils.VMAXUtils, 'is_metro_device', return_value=True) 7150 def test_initialize_connection_no_multipath_iscsi(self, mock_md): 7151 info_dict = self.iscsi_common.initialize_connection( 7152 self.data.test_volume, self.data.connector) 7153 self.assertIsNone(info_dict) 7154 7155 def test_attach_metro_volume(self): 7156 rep_extra_specs = deepcopy(VMAXCommonData.rep_extra_specs) 7157 rep_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7158 hostlunid, remote_port_group = self.common._attach_metro_volume( 7159 self.data.test_volume, self.data.connector, 7160 self.data.extra_specs, rep_extra_specs) 7161 self.assertEqual(self.data.port_group_name_f, remote_port_group) 7162 7163 @mock.patch.object(rest.VMAXRest, 'is_vol_in_rep_session', 7164 return_value=(False, False, None)) 7165 @mock.patch.object(common.VMAXCommon, 'extend_volume_is_replicated') 7166 @mock.patch.object(common.VMAXCommon, '_sync_check') 7167 def test_extend_volume_rep_enabled(self, mock_sync, mock_ex_re, 7168 mock_is_re): 7169 extra_specs = deepcopy(self.extra_specs) 7170 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7171 volume_name = self.data.test_volume.name 7172 self.common.extend_volume(self.data.test_volume, '5') 7173 mock_ex_re.assert_called_once_with( 7174 self.data.array, self.data.test_volume, 7175 self.data.device_id, volume_name, "5", extra_specs) 7176 7177 def test_set_config_file_get_extra_specs_rep_enabled(self): 7178 extra_specs, _, _ = self.common._set_config_file_and_get_extra_specs( 7179 self.data.test_volume) 7180 self.assertTrue(extra_specs['replication_enabled']) 7181 7182 def test_populate_masking_dict_is_re(self): 7183 extra_specs = deepcopy(self.extra_specs) 7184 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7185 masking_dict = self.common._populate_masking_dict( 7186 self.data.test_volume, self.data.connector, extra_specs) 7187 self.assertTrue(masking_dict['replication_enabled']) 7188 self.assertEqual('OS-HostX-SRP_1-DiamondDSS-OS-fibre-PG-RE', 7189 masking_dict[utils.SG_NAME]) 7190 7191 @mock.patch.object(common.VMAXCommon, 7192 '_replicate_volume', 7193 return_value={}) 7194 def test_manage_existing_is_replicated(self, mock_rep): 7195 extra_specs = deepcopy(self.extra_specs) 7196 extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7197 external_ref = {u'source-name': u'00002'} 7198 volume_name = self.utils.get_volume_element_name( 7199 self.data.test_volume.id) 7200 provider_location = {'device_id': u'00002', 'array': self.data.array} 7201 with mock.patch.object( 7202 self.common, '_check_lun_valid_for_cinder_management'): 7203 self.common.manage_existing( 7204 self.data.test_volume, external_ref) 7205 mock_rep.assert_called_once_with( 7206 self.data.test_volume, volume_name, provider_location, 7207 extra_specs, delete_src=False) 7208 7209 @mock.patch.object(masking.VMAXMasking, 'remove_and_reset_members') 7210 def test_setup_volume_replication(self, mock_rm): 7211 rep_status, rep_data = self.common.setup_volume_replication( 7212 self.data.array, self.data.test_volume, self.data.device_id, 7213 self.extra_specs) 7214 self.assertEqual(fields.ReplicationStatus.ENABLED, rep_status) 7215 self.assertEqual({'array': self.data.remote_array, 7216 'device_id': self.data.device_id}, rep_data) 7217 7218 @mock.patch.object(masking.VMAXMasking, 'remove_and_reset_members') 7219 @mock.patch.object(common.VMAXCommon, '_create_volume') 7220 def test_setup_volume_replication_target(self, mock_create, mock_rm): 7221 rep_status, rep_data = self.common.setup_volume_replication( 7222 self.data.array, self.data.test_volume, self.data.device_id, 7223 self.extra_specs, self.data.device_id2) 7224 self.assertEqual(fields.ReplicationStatus.ENABLED, rep_status) 7225 self.assertEqual({'array': self.data.remote_array, 7226 'device_id': self.data.device_id2}, rep_data) 7227 mock_create.assert_not_called() 7228 7229 @mock.patch.object(common.VMAXCommon, '_cleanup_remote_target') 7230 def test_cleanup_lun_replication_success(self, mock_clean): 7231 rep_extra_specs = deepcopy(self.data.rep_extra_specs) 7232 rep_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f 7233 self.common.cleanup_lun_replication( 7234 self.data.test_volume, "1", self.data.device_id, 7235 self.extra_specs) 7236 mock_clean.assert_called_once_with( 7237 self.data.array, self.data.test_volume, 7238 self.data.remote_array, self.data.device_id, 7239 self.data.device_id2, self.data.rdf_group_no, "1", 7240 rep_extra_specs) 7241 # Cleanup legacy replication 7242 self.common.cleanup_lun_replication( 7243 self.data.test_legacy_vol, "1", self.data.device_id, 7244 self.extra_specs) 7245 mock_clean.assert_called_once_with( 7246 self.data.array, self.data.test_volume, 7247 self.data.remote_array, self.data.device_id, 7248 self.data.device_id2, self.data.rdf_group_no, "1", 7249 rep_extra_specs) 7250 7251 @mock.patch.object(common.VMAXCommon, '_cleanup_remote_target') 7252 def test_cleanup_lun_replication_no_target(self, mock_clean): 7253 with mock.patch.object(self.common, 'get_remote_target_device', 7254 return_value=(None, '', '', '', '')): 7255 self.common.cleanup_lun_replication( 7256 self.data.test_volume, "1", self.data.device_id, 7257 self.extra_specs) 7258 mock_clean.assert_not_called() 7259 7260 @mock.patch.object( 7261 common.VMAXCommon, 'get_remote_target_device', 7262 return_value=(VMAXCommonData.device_id2, '', '', '', '')) 7263 @mock.patch.object(common.VMAXCommon, 7264 '_add_volume_to_async_rdf_managed_grp') 7265 def test_cleanup_lun_replication_exception(self, mock_add, mock_tgt): 7266 self.assertRaises(exception.VolumeBackendAPIException, 7267 self.common.cleanup_lun_replication, 7268 self.data.test_volume, "1", self.data.device_id, 7269 self.extra_specs) 7270 # is metro or async volume 7271 extra_specs = deepcopy(self.extra_specs) 7272 extra_specs[utils.REP_MODE] = utils.REP_METRO 7273 self.assertRaises(exception.VolumeBackendAPIException, 7274 self.common.cleanup_lun_replication, 7275 self.data.test_volume, "1", self.data.device_id, 7276 extra_specs) 7277 mock_add.assert_called_once() 7278 7279 @mock.patch.object(common.VMAXCommon, '_cleanup_metro_target') 7280 @mock.patch.object(masking.VMAXMasking, 'remove_vol_from_storage_group') 7281 @mock.patch.object(common.VMAXCommon, '_delete_from_srp') 7282 @mock.patch.object(provision.VMAXProvision, 'break_rdf_relationship') 7283 def test_cleanup_remote_target(self, mock_break, mock_del, 7284 mock_rm, mock_clean_metro): 7285 with mock.patch.object(self.rest, 'are_vols_rdf_paired', 7286 return_value=(False, '', '')): 7287 self.common._cleanup_remote_target( 7288 self.data.array, self.data.test_volume, 7289 self.data.remote_array, self.data.device_id, 7290 self.data.device_id2, self.data.rdf_group_name, 7291 "vol1", self.data.rep_extra_specs) 7292 mock_break.assert_not_called() 7293 self.common._cleanup_remote_target( 7294 self.data.array, self.data.test_volume, 7295 self.data.remote_array, self.data.device_id, 7296 self.data.device_id2, self.data.rdf_group_name, 7297 "vol1", self.data.rep_extra_specs) 7298 mock_break.assert_called_once_with( 7299 self.data.array, self.data.device_id, 7300 self.data.device_id2, self.data.rdf_group_name, 7301 self.data.rep_extra_specs, "Synchronized") 7302 # is metro volume 7303 with mock.patch.object(self.utils, 'is_metro_device', 7304 return_value=True): 7305 self.common._cleanup_remote_target( 7306 self.data.array, self.data.test_volume, 7307 self.data.remote_array, self.data.device_id, 7308 self.data.device_id2, self.data.rdf_group_name, 7309 "vol1", self.data.rep_extra_specs) 7310 mock_clean_metro.assert_called_once() 7311 7312 def test_cleanup_remote_target_exception(self): 7313 extra_specs = deepcopy(self.data.rep_extra_specs) 7314 extra_specs['mode'] = utils.REP_METRO 7315 self.assertRaises(exception.VolumeBackendAPIException, 7316 self.metro_driver.common._cleanup_remote_target, 7317 self.data.array, self.data.test_volume, 7318 self.data.remote_array, 7319 self.data.device_id, self.data.device_id2, 7320 self.data.rdf_group_name, "vol1", extra_specs) 7321 7322 @mock.patch.object(provision.VMAXProvision, 'enable_group_replication') 7323 @mock.patch.object(rest.VMAXRest, 'get_num_vols_in_sg', 7324 side_effect=[2, 0]) 7325 def test_cleanup_metro_target(self, mock_vols, mock_enable): 7326 # allow delete is True 7327 specs = {'allow_del_metro': True} 7328 for x in range(0, 2): 7329 self.common._cleanup_metro_target( 7330 self.data.array, self.data.device_id, self.data.device_id2, 7331 self.data.rdf_group_no, specs) 7332 mock_enable.assert_called_once() 7333 # allow delete is False 7334 specs['allow_del_metro'] = False 7335 self.assertRaises(exception.VolumeBackendAPIException, 7336 self.common._cleanup_metro_target, 7337 self.data.array, self.data.device_id, 7338 self.data.device_id2, 7339 self.data.rdf_group_no, specs) 7340 7341 @mock.patch.object(common.VMAXCommon, 7342 '_remove_vol_and_cleanup_replication') 7343 @mock.patch.object(masking.VMAXMasking, 'remove_vol_from_storage_group') 7344 @mock.patch.object(common.VMAXCommon, '_delete_from_srp') 7345 def test_cleanup_replication_source(self, mock_del, mock_rm, mock_clean): 7346 self.common._cleanup_replication_source( 7347 self.data.array, self.data.test_volume, "vol1", 7348 {'device_id': self.data.device_id}, self.extra_specs) 7349 mock_del.assert_called_once_with( 7350 self.data.array, self.data.device_id, "vol1", self.extra_specs) 7351 7352 def test_get_rdf_details(self): 7353 rdf_group_no, remote_array = self.common.get_rdf_details( 7354 self.data.array) 7355 self.assertEqual(self.data.rdf_group_no, rdf_group_no) 7356 self.assertEqual(self.data.remote_array, remote_array) 7357 7358 def test_get_rdf_details_exception(self): 7359 with mock.patch.object(self.rest, 'get_rdf_group_number', 7360 return_value=None): 7361 self.assertRaises(exception.VolumeBackendAPIException, 7362 self.common.get_rdf_details, self.data.array) 7363 7364 def test_failover_host(self): 7365 volumes = [self.data.test_volume, self.data.test_clone_volume] 7366 with mock.patch.object(self.common, '_failover_volume', 7367 return_value={}) as mock_fo: 7368 self.common.failover_host(volumes) 7369 self.assertEqual(2, mock_fo.call_count) 7370 7371 def test_failover_host_exception(self): 7372 volumes = [self.data.test_volume, self.data.test_clone_volume] 7373 self.assertRaises(exception.VolumeBackendAPIException, 7374 self.common.failover_host, 7375 volumes, secondary_id="default") 7376 7377 @mock.patch.object(common.VMAXCommon, 'failover_replication', 7378 return_value=({}, {})) 7379 @mock.patch.object(common.VMAXCommon, '_failover_volume', 7380 return_value={}) 7381 def test_failover_host_groups(self, mock_fv, mock_fg): 7382 volumes = [self.data.test_volume_group_member] 7383 group1 = self.data.test_group 7384 self.common.failover_host(volumes, None, [group1]) 7385 mock_fv.assert_not_called() 7386 mock_fg.assert_called_once() 7387 7388 def test_failover_volume(self): 7389 ref_model_update = { 7390 'volume_id': self.data.test_volume.id, 7391 'updates': 7392 {'replication_status': fields.ReplicationStatus.FAILED_OVER, 7393 'replication_driver_data': six.text_type( 7394 self.data.provider_location), 7395 'provider_location': six.text_type( 7396 self.data.provider_location3)}} 7397 model_update = self.common._failover_volume( 7398 self.data.test_volume, True, self.extra_specs) 7399 self.assertEqual(ref_model_update, model_update) 7400 ref_model_update2 = { 7401 'volume_id': self.data.test_volume.id, 7402 'updates': 7403 {'replication_status': fields.ReplicationStatus.ENABLED, 7404 'replication_driver_data': six.text_type( 7405 self.data.provider_location), 7406 'provider_location': six.text_type( 7407 self.data.provider_location3)}} 7408 model_update2 = self.common._failover_volume( 7409 self.data.test_volume, False, self.extra_specs) 7410 self.assertEqual(ref_model_update2, model_update2) 7411 7412 def test_failover_legacy_volume(self): 7413 ref_model_update = { 7414 'volume_id': self.data.test_volume.id, 7415 'updates': 7416 {'replication_status': fields.ReplicationStatus.FAILED_OVER, 7417 'replication_driver_data': six.text_type( 7418 self.data.legacy_provider_location), 7419 'provider_location': six.text_type( 7420 self.data.legacy_provider_location2)}} 7421 model_update = self.common._failover_volume( 7422 self.data.test_legacy_vol, True, self.extra_specs) 7423 self.assertEqual(ref_model_update, model_update) 7424 7425 def test_failover_volume_exception(self): 7426 with mock.patch.object( 7427 self.provision, 'failover_volume', 7428 side_effect=exception.VolumeBackendAPIException): 7429 ref_model_update = { 7430 'volume_id': self.data.test_volume.id, 7431 'updates': {'replication_status': 7432 fields.ReplicationStatus.FAILOVER_ERROR, 7433 'replication_driver_data': six.text_type( 7434 self.data.provider_location3), 7435 'provider_location': six.text_type( 7436 self.data.provider_location)}} 7437 model_update = self.common._failover_volume( 7438 self.data.test_volume, True, self.extra_specs) 7439 self.assertEqual(ref_model_update, model_update) 7440 7441 @mock.patch.object( 7442 common.VMAXCommon, '_find_device_on_array', 7443 side_effect=[None, VMAXCommonData.device_id, 7444 VMAXCommonData.device_id, VMAXCommonData.device_id]) 7445 @mock.patch.object( 7446 common.VMAXCommon, '_get_masking_views_from_volume', 7447 side_effect=['OS-host-MV', None, exception.VolumeBackendAPIException]) 7448 def test_recover_volumes_on_failback(self, mock_mv, mock_dev): 7449 recovery1 = self.common.recover_volumes_on_failback( 7450 self.data.test_volume, self.extra_specs) 7451 self.assertEqual('error', recovery1['updates']['status']) 7452 recovery2 = self.common.recover_volumes_on_failback( 7453 self.data.test_volume, self.extra_specs) 7454 self.assertEqual('in-use', recovery2['updates']['status']) 7455 recovery3 = self.common.recover_volumes_on_failback( 7456 self.data.test_volume, self.extra_specs) 7457 self.assertEqual('available', recovery3['updates']['status']) 7458 recovery4 = self.common.recover_volumes_on_failback( 7459 self.data.test_volume, self.extra_specs) 7460 self.assertEqual('available', recovery4['updates']['status']) 7461 7462 def test_get_remote_target_device(self): 7463 target_device1, _, _, _, _ = ( 7464 self.common.get_remote_target_device( 7465 self.data.array, self.data.test_volume, self.data.device_id)) 7466 self.assertEqual(self.data.device_id2, target_device1) 7467 target_device2, _, _, _, _ = ( 7468 self.common.get_remote_target_device( 7469 self.data.array, self.data.test_clone_volume, 7470 self.data.device_id)) 7471 self.assertIsNone(target_device2) 7472 with mock.patch.object(self.rest, 'are_vols_rdf_paired', 7473 return_value=(False, '')): 7474 target_device3, _, _, _, _ = ( 7475 self.common.get_remote_target_device( 7476 self.data.array, self.data.test_volume, 7477 self.data.device_id)) 7478 self.assertIsNone(target_device3) 7479 with mock.patch.object(self.rest, 'get_volume', 7480 return_value=None): 7481 target_device4, _, _, _, _ = ( 7482 self.common.get_remote_target_device( 7483 self.data.array, self.data.test_volume, 7484 self.data.device_id)) 7485 self.assertIsNone(target_device4) 7486 7487 @mock.patch.object(common.VMAXCommon, 'setup_volume_replication') 7488 @mock.patch.object(provision.VMAXProvision, 'extend_volume') 7489 @mock.patch.object(provision.VMAXProvision, 'break_rdf_relationship') 7490 @mock.patch.object(masking.VMAXMasking, 'remove_and_reset_members') 7491 def test_extend_volume_is_replicated(self, mock_remove, 7492 mock_break, mock_extend, mock_setup): 7493 self.common.extend_volume_is_replicated( 7494 self.data.array, self.data.test_volume, self.data.device_id, 7495 'vol1', '5', self.data.extra_specs_rep_enabled) 7496 self.assertEqual(2, mock_remove.call_count) 7497 self.assertEqual(2, mock_extend.call_count) 7498 mock_remove.reset_mock() 7499 mock_extend.reset_mock() 7500 with mock.patch.object(self.rest, 'is_next_gen_array', 7501 return_value=True): 7502 self.common.extend_volume_is_replicated( 7503 self.data.array, self.data.test_volume, self.data.device_id, 7504 'vol1', '5', self.data.extra_specs_rep_enabled) 7505 mock_remove.assert_not_called() 7506 self.assertEqual(2, mock_extend.call_count) 7507 7508 def test_extend_volume_is_replicated_exception(self): 7509 self.assertRaises(exception.VolumeBackendAPIException, 7510 self.common.extend_volume_is_replicated, 7511 self.data.failed_resource, self.data.test_volume, 7512 self.data.device_id, 'vol1', '1', 7513 self.data.extra_specs_rep_enabled) 7514 with mock.patch.object(self.utils, 'is_metro_device', 7515 return_value=True): 7516 self.assertRaises(exception.VolumeBackendAPIException, 7517 self.common.extend_volume_is_replicated, 7518 self.data.array, self.data.test_volume, 7519 self.data.device_id, 'vol1', '1', 7520 self.data.extra_specs_rep_enabled) 7521 7522 @mock.patch.object(common.VMAXCommon, 'add_volume_to_replication_group') 7523 @mock.patch.object(masking.VMAXMasking, 'remove_and_reset_members') 7524 def test_enable_rdf(self, mock_remove, mock_add): 7525 rep_config = self.utils.get_replication_config( 7526 [self.replication_device]) 7527 self.common.enable_rdf( 7528 self.data.array, self.data.test_volume, self.data.device_id, 7529 self.data.rdf_group_no, rep_config, 'OS-1', 7530 self.data.remote_array, self.data.device_id2, self.extra_specs) 7531 self.assertEqual(2, mock_remove.call_count) 7532 self.assertEqual(2, mock_add.call_count) 7533 7534 @mock.patch.object(masking.VMAXMasking, 'remove_vol_from_storage_group') 7535 @mock.patch.object(common.VMAXCommon, '_cleanup_remote_target') 7536 def test_enable_rdf_exception(self, mock_cleanup, mock_rm): 7537 rep_config = self.utils.get_replication_config( 7538 [self.replication_device]) 7539 self.assertRaises( 7540 exception.VolumeBackendAPIException, self.common.enable_rdf, 7541 self.data.array, self.data.test_volume, self.data.device_id, 7542 self.data.failed_resource, rep_config, 'OS-1', 7543 self.data.remote_array, self.data.device_id2, self.extra_specs) 7544 self.assertEqual(1, mock_cleanup.call_count) 7545 7546 def test_add_volume_to_replication_group(self): 7547 sg_name = self.common.add_volume_to_replication_group( 7548 self.data.array, self.data.device_id, 'vol1', 7549 self.extra_specs) 7550 self.assertEqual(self.data.default_sg_re_enabled, sg_name) 7551 7552 @mock.patch.object(masking.VMAXMasking, 7553 'get_or_create_default_storage_group', 7554 side_effect=exception.VolumeBackendAPIException) 7555 def test_add_volume_to_replication_group_exception(self, mock_get): 7556 self.assertRaises( 7557 exception.VolumeBackendAPIException, 7558 self.common.add_volume_to_replication_group, 7559 self.data.array, self.data.device_id, 'vol1', 7560 self.extra_specs) 7561 7562 def test_get_replication_extra_specs(self): 7563 rep_config = self.utils.get_replication_config( 7564 [self.replication_device]) 7565 # Path one - disable compression 7566 extra_specs1 = deepcopy(self.extra_specs) 7567 extra_specs1[utils.DISABLECOMPRESSION] = "true" 7568 ref_specs1 = deepcopy(self.data.rep_extra_specs2) 7569 rep_extra_specs1 = self.common._get_replication_extra_specs( 7570 extra_specs1, rep_config) 7571 self.assertEqual(ref_specs1, rep_extra_specs1) 7572 # Path two - disable compression, not all flash 7573 ref_specs2 = deepcopy(self.data.rep_extra_specs2) 7574 with mock.patch.object(self.rest, 'is_compression_capable', 7575 return_value=False): 7576 rep_extra_specs2 = self.common._get_replication_extra_specs( 7577 extra_specs1, rep_config) 7578 self.assertEqual(ref_specs2, rep_extra_specs2) 7579 # Path three - slo not valid 7580 extra_specs3 = deepcopy(self.extra_specs) 7581 ref_specs3 = deepcopy(ref_specs1) 7582 ref_specs3['slo'] = None 7583 ref_specs3['workload'] = None 7584 with mock.patch.object(self.provision, 'verify_slo_workload', 7585 return_value=(False, False)): 7586 rep_extra_specs3 = self.common._get_replication_extra_specs( 7587 extra_specs3, rep_config) 7588 self.assertEqual(ref_specs3, rep_extra_specs3) 7589 7590 def test_get_secondary_stats(self): 7591 rep_config = self.utils.get_replication_config( 7592 [self.replication_device]) 7593 array_map = self.utils.parse_file_to_get_array_map( 7594 self.common.pool_info['config_file']) 7595 finalarrayinfolist = self.common._get_slo_workload_combinations( 7596 array_map) 7597 array_info = finalarrayinfolist[0] 7598 ref_info = deepcopy(array_info) 7599 ref_info['SerialNumber'] = six.text_type(rep_config['array']) 7600 ref_info['srpName'] = rep_config['srp'] 7601 secondary_info = self.common.get_secondary_stats_info( 7602 rep_config, array_info) 7603 self.assertEqual(ref_info, secondary_info) 7604 7605 def test_replicate_group(self): 7606 volume_model_update = { 7607 'id': self.data.test_volume.id, 7608 'provider_location': self.data.test_volume.provider_location} 7609 vols_model_update = self.common._replicate_group( 7610 self.data.array, [volume_model_update], 7611 self.data.test_vol_grp_name, self.extra_specs) 7612 ref_rep_data = six.text_type({'array': self.data.remote_array, 7613 'device_id': self.data.device_id2}) 7614 ref_vol_update = { 7615 'id': self.data.test_volume.id, 7616 'provider_location': self.data.test_volume.provider_location, 7617 'replication_driver_data': ref_rep_data, 7618 'replication_status': fields.ReplicationStatus.ENABLED} 7619 self.assertEqual(ref_vol_update, vols_model_update[0]) 7620 7621 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 7622 return_value=False) 7623 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=True) 7624 def test_create_replicaton_group(self, mock_type, mock_cg_type): 7625 ref_model_update = { 7626 'status': fields.GroupStatus.AVAILABLE, 7627 'replication_status': fields.ReplicationStatus.ENABLED} 7628 model_update = self.common.create_group(None, self.data.test_group_1) 7629 self.assertEqual(ref_model_update, model_update) 7630 # Replication mode is async 7631 self.assertRaises(exception.InvalidInput, 7632 self.async_driver.common.create_group, 7633 None, self.data.test_group_1) 7634 7635 def test_enable_replication(self): 7636 # Case 1: Group not replicated 7637 with mock.patch.object(volume_utils, 'is_group_a_type', 7638 return_value=False): 7639 self.assertRaises(NotImplementedError, 7640 self.common.enable_replication, 7641 None, self.data.test_group, 7642 [self.data.test_volume]) 7643 with mock.patch.object(volume_utils, 'is_group_a_type', 7644 return_value=True): 7645 # Case 2: Empty group 7646 model_update, __ = self.common.enable_replication( 7647 None, self.data.test_group, []) 7648 self.assertEqual({}, model_update) 7649 # Case 3: Successfully enabled 7650 model_update, __ = self.common.enable_replication( 7651 None, self.data.test_group, [self.data.test_volume]) 7652 self.assertEqual(fields.ReplicationStatus.ENABLED, 7653 model_update['replication_status']) 7654 # Case 4: Exception 7655 model_update, __ = self.common.enable_replication( 7656 None, self.data.test_group_failed, [self.data.test_volume]) 7657 self.assertEqual(fields.ReplicationStatus.ERROR, 7658 model_update['replication_status']) 7659 7660 def test_disable_replication(self): 7661 # Case 1: Group not replicated 7662 with mock.patch.object(volume_utils, 'is_group_a_type', 7663 return_value=False): 7664 self.assertRaises(NotImplementedError, 7665 self.common.disable_replication, 7666 None, self.data.test_group, 7667 [self.data.test_volume]) 7668 with mock.patch.object(volume_utils, 'is_group_a_type', 7669 return_value=True): 7670 # Case 2: Empty group 7671 model_update, __ = self.common.disable_replication( 7672 None, self.data.test_group, []) 7673 self.assertEqual({}, model_update) 7674 # Case 3: Successfully disabled 7675 model_update, __ = self.common.disable_replication( 7676 None, self.data.test_group, [self.data.test_volume]) 7677 self.assertEqual(fields.ReplicationStatus.DISABLED, 7678 model_update['replication_status']) 7679 # Case 4: Exception 7680 model_update, __ = self.common.disable_replication( 7681 None, self.data.test_group_failed, [self.data.test_volume]) 7682 self.assertEqual(fields.ReplicationStatus.ERROR, 7683 model_update['replication_status']) 7684 7685 def test_failover_replication(self): 7686 with mock.patch.object(volume_utils, 'is_group_a_type', 7687 return_value=True): 7688 # Case 1: Empty group 7689 model_update, __ = self.common.failover_replication( 7690 None, self.data.test_group, []) 7691 self.assertEqual({}, model_update) 7692 # Case 2: Successfully failed over 7693 model_update, __ = self.common.failover_replication( 7694 None, self.data.test_group, [self.data.test_volume]) 7695 self.assertEqual(fields.ReplicationStatus.FAILED_OVER, 7696 model_update['replication_status']) 7697 # Case 3: Successfully failed back 7698 model_update, __ = self.common.failover_replication( 7699 None, self.data.test_group, [self.data.test_volume], 7700 secondary_backend_id='default') 7701 self.assertEqual(fields.ReplicationStatus.ENABLED, 7702 model_update['replication_status']) 7703 # Case 4: Exception 7704 model_update, __ = self.common.failover_replication( 7705 None, self.data.test_group_failed, [self.data.test_volume]) 7706 self.assertEqual(fields.ReplicationStatus.ERROR, 7707 model_update['replication_status']) 7708 7709 @mock.patch.object(utils.VMAXUtils, 'get_volume_group_utils', 7710 return_value=(VMAXCommonData.array, {})) 7711 @mock.patch.object(common.VMAXCommon, '_cleanup_group_replication') 7712 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=True) 7713 def test_delete_replication_group(self, mock_check, 7714 mock_cleanup, mock_utils): 7715 self.common._delete_group(self.data.test_rep_group, []) 7716 mock_cleanup.assert_called_once() 7717 7718 @mock.patch.object(masking.VMAXMasking, 7719 'remove_volumes_from_storage_group') 7720 @mock.patch.object(utils.VMAXUtils, 'check_rep_status_enabled') 7721 @mock.patch.object(common.VMAXCommon, 7722 '_remove_remote_vols_from_volume_group') 7723 @mock.patch.object(common.VMAXCommon, '_add_remote_vols_to_volume_group') 7724 @mock.patch.object(volume_utils, 'is_group_a_type', return_value=True) 7725 @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type', 7726 return_value=True) 7727 def test_update_replicated_group(self, mock_cg_type, mock_type_check, 7728 mock_add, mock_remove, mock_check, 7729 mock_rm): 7730 add_vols = [self.data.test_volume] 7731 remove_vols = [self.data.test_clone_volume] 7732 self.common.update_group( 7733 self.data.test_group_1, add_vols, remove_vols) 7734 mock_add.assert_called_once() 7735 mock_remove.assert_called_once() 7736 7737 @mock.patch.object(masking.VMAXMasking, 7738 'add_volumes_to_storage_group') 7739 def test_add_remote_vols_to_volume_group(self, mock_add): 7740 self.common._add_remote_vols_to_volume_group( 7741 self.data.remote_array, [self.data.test_volume], 7742 self.data.test_rep_group, self.data.rep_extra_specs) 7743 mock_add.assert_called_once() 7744 7745 @mock.patch.object(masking.VMAXMasking, 7746 'remove_volumes_from_storage_group') 7747 def test_remove_remote_vols_from_volume_group(self, mock_rm): 7748 self.common._remove_remote_vols_from_volume_group( 7749 self.data.remote_array, [self.data.test_volume], 7750 self.data.test_rep_group, self.data.rep_extra_specs) 7751 mock_rm.assert_called_once() 7752 7753 @mock.patch.object(masking.VMAXMasking, 'remove_and_reset_members') 7754 @mock.patch.object(masking.VMAXMasking, 7755 'remove_volumes_from_storage_group') 7756 def test_cleanup_group_replication(self, mock_rm, mock_rm_reset): 7757 self.common._cleanup_group_replication( 7758 self.data.array, self.data.test_vol_grp_name, 7759 [self.data.device_id], self.extra_specs) 7760 mock_rm.assert_called_once() 7761 7762 @mock.patch.object(masking.VMAXMasking, 'add_volume_to_storage_group') 7763 def test_add_volume_to_async_group(self, mock_add): 7764 extra_specs = deepcopy(self.extra_specs) 7765 extra_specs['rep_mode'] = utils.REP_ASYNC 7766 self.async_driver.common._add_volume_to_async_rdf_managed_grp( 7767 self.data.array, self.data.device_id, 'name', 7768 self.data.remote_array, self.data.device_id2, extra_specs) 7769 self.assertEqual(2, mock_add.call_count) 7770 7771 def test_add_volume_to_async_group_exception(self): 7772 extra_specs = deepcopy(self.extra_specs) 7773 extra_specs['rep_mode'] = utils.REP_ASYNC 7774 self.assertRaises( 7775 exception.VolumeBackendAPIException, 7776 self.async_driver.common._add_volume_to_async_rdf_managed_grp, 7777 self.data.failed_resource, self.data.device_id, 'name', 7778 self.data.remote_array, self.data.device_id2, extra_specs) 7779 7780 @mock.patch.object(common.VMAXCommon, 7781 '_add_volume_to_async_rdf_managed_grp') 7782 @mock.patch.object(masking.VMAXMasking, 'remove_and_reset_members') 7783 def test_setup_volume_replication_async(self, mock_rm, mock_add): 7784 extra_specs = deepcopy(self.extra_specs) 7785 extra_specs['rep_mode'] = utils.REP_ASYNC 7786 rep_status, rep_data = ( 7787 self.async_driver.common.setup_volume_replication( 7788 self.data.array, self.data.test_volume, 7789 self.data.device_id, extra_specs)) 7790 self.assertEqual(fields.ReplicationStatus.ENABLED, rep_status) 7791 self.assertEqual({'array': self.data.remote_array, 7792 'device_id': self.data.device_id}, rep_data) 7793 mock_add.assert_called_once() 7794 7795 @mock.patch.object(common.VMAXCommon, '_failover_replication', 7796 return_value=({}, {})) 7797 @mock.patch.object(common.VMAXCommon, '_failover_volume', 7798 return_value={}) 7799 def test_failover_host_async(self, mock_fv, mock_fg): 7800 volumes = [self.data.test_volume] 7801 extra_specs = deepcopy(self.extra_specs) 7802 extra_specs['rep_mode'] = utils.REP_ASYNC 7803 with mock.patch.object(common.VMAXCommon, '_initial_setup', 7804 return_value=extra_specs): 7805 self.async_driver.common.failover_host(volumes, None, []) 7806 mock_fv.assert_not_called() 7807 mock_fg.assert_called_once() 7808