# Copyright (c) - 2015, Alex Meade # Copyright (c) - 2015, Yogesh Kshirsagar # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from cinder.volume import configuration as conf from cinder.volume.drivers.netapp.eseries import utils import cinder.volume.drivers.netapp.options as na_opts MULTIATTACH_HOST_GROUP = { 'clusterRef': '8500000060080E500023C7340036035F515B78FC', 'label': utils.MULTI_ATTACH_HOST_GROUP_NAME, } FOREIGN_HOST_GROUP = { 'clusterRef': '8500000060080E500023C7340036035F515B78FD', 'label': 'FOREIGN HOST GROUP', } STORAGE_POOL = { 'label': 'DDP', 'volumeGroupRef': 'fakevolgroupref', 'raidLevel': 'raidDiskPool', } VOLUME = { 'extremeProtection': False, 'pitBaseVolume': True, 'dssMaxSegmentSize': 131072, 'totalSizeInBytes': '1073741824', 'raidLevel': 'raid6', 'volumeRef': '0200000060080E500023BB34000003FB515C2293', 'listOfMappings': [], 'sectorOffset': '15', 'id': '0200000060080E500023BB34000003FB515C2293', 'wwn': '60080E500023BB3400001FC352D14CB2', 'capacity': '2147483648', 'mgmtClientAttribute': 0, 'label': 'CFDXJ67BLJH25DXCZFZD4NSF54', 'volumeFull': False, 'blkSize': 512, 'volumeCopyTarget': False, 'volumeGroupRef': '0400000060080E500023BB3400001F9F52CECC3F', 'preferredControllerId': '070000000000000000000001', 'currentManager': '070000000000000000000001', 'applicationTagOwned': False, 'status': 'optimal', 'segmentSize': 131072, 'volumeUse': 'standardVolume', 'action': 'none', 'preferredManager': '070000000000000000000001', 'volumeHandle': 15, 'offline': False, 'preReadRedundancyCheckEnabled': False, 'dssPreallocEnabled': False, 'name': 'bdm-vc-test-1', 'worldWideName': '60080E500023BB3400001FC352D14CB2', 'currentControllerId': '070000000000000000000001', 'protectionInformationCapable': False, 'mapped': False, 'reconPriority': 1, 'protectionType': 'type1Protection' } INITIATOR_NAME = 'iqn.1998-01.com.vmware:localhost-28a58148' INITIATOR_NAME_2 = 'iqn.1998-01.com.vmware:localhost-28a58149' INITIATOR_NAME_3 = 'iqn.1998-01.com.vmware:localhost-28a58150' WWPN = '20130080E5322230' WWPN_2 = '20230080E5322230' FC_TARGET_WWPNS = [ '500a098280feeba5', '500a098290feeba5', '500a098190feeba5', '500a098180feeba5' ] FC_I_T_MAP = { '20230080E5322230': [ '500a098280feeba5', '500a098290feeba5' ], '20130080E5322230': [ '500a098190feeba5', '500a098180feeba5' ] } FC_FABRIC_MAP = { 'fabricB': { 'target_port_wwn_list': [ '500a098190feeba5', '500a098180feeba5' ], 'initiator_port_wwn_list': [ '20130080E5322230' ] }, 'fabricA': { 'target_port_wwn_list': [ '500a098290feeba5', '500a098280feeba5' ], 'initiator_port_wwn_list': [ '20230080E5322230' ] } } HOST = { 'isSAControlled': False, 'confirmLUNMappingCreation': False, 'label': 'stlrx300s7-55', 'isLargeBlockFormatHost': False, 'clusterRef': '8500000060080E500023C7340036035F515B78FC', 'protectionInformationCapableAccessMethod': False, 'ports': [], 'hostRef': '8400000060080E500023C73400300381515BFBA3', 'hostTypeIndex': 6, 'hostSidePorts': [{ 'label': 'NewStore', 'type': 'iscsi', 'address': INITIATOR_NAME}] } HOST_2 = { 'isSAControlled': False, 'confirmLUNMappingCreation': False, 'label': 'stlrx300s7-55', 'isLargeBlockFormatHost': False, 'clusterRef': utils.NULL_REF, 'protectionInformationCapableAccessMethod': False, 'ports': [], 'hostRef': '8400000060080E500023C73400300381515BFBA5', 'hostTypeIndex': 6, 'hostSidePorts': [{ 'label': 'NewStore', 'type': 'iscsi', 'address': INITIATOR_NAME_2}] } # HOST_3 has all lun_ids in use. HOST_3 = { 'isSAControlled': False, 'confirmLUNMappingCreation': False, 'label': 'stlrx300s7-55', 'isLargeBlockFormatHost': False, 'clusterRef': '8500000060080E500023C73400360351515B78FC', 'protectionInformationCapableAccessMethod': False, 'ports': [], 'hostRef': '8400000060080E501023C73400800381515BFBA5', 'hostTypeIndex': 6, 'hostSidePorts': [{ 'label': 'NewStore', 'type': 'iscsi', 'address': INITIATOR_NAME_3}], } VOLUME_MAPPING = { 'lunMappingRef': '8800000000000000000000000000000000000000', 'lun': 0, 'ssid': 16384, 'perms': 15, 'volumeRef': VOLUME['volumeRef'], 'type': 'all', 'mapRef': HOST['hostRef'] } # VOLUME_MAPPING_3 corresponding to HOST_3 has all lun_ids in use. VOLUME_MAPPING_3 = { 'lunMappingRef': '8800000000000000000000000000000000000000', 'lun': range(255), 'ssid': 16384, 'perms': 15, 'volumeRef': VOLUME['volumeRef'], 'type': 'all', 'mapRef': HOST_3['hostRef'], } VOLUME_MAPPING_TO_MULTIATTACH_GROUP = copy.deepcopy(VOLUME_MAPPING) VOLUME_MAPPING_TO_MULTIATTACH_GROUP.update( {'mapRef': MULTIATTACH_HOST_GROUP['clusterRef']} ) STORAGE_SYSTEM = { 'freePoolSpace': 11142431623168, 'driveCount': 24, 'hostSparesUsed': 0, 'id': '1fa6efb5-f07b-4de4-9f0e-52e5f7ff5d1b', 'hotSpareSizeAsString': '0', 'wwn': '60080E500023C73400000000515AF323', 'parameters': { 'minVolSize': 1048576, 'maxSnapshotsPerBase': 16, 'maxDrives': 192, 'maxVolumes': 512, 'maxVolumesPerGroup': 256, 'maxMirrors': 0, 'maxMappingsPerVolume': 1, 'maxMappableLuns': 256, 'maxVolCopys': 511, 'maxSnapshots': 256 }, 'hotSpareCount': 0, 'hostSpareCountInStandby': 0, 'status': 'needsattn', 'trayCount': 1, 'usedPoolSpaceAsString': '5313000380416', 'ip2': '10.63.165.216', 'ip1': '10.63.165.215', 'freePoolSpaceAsString': '11142431623168', 'types': 'SAS', 'name': 'stle2600-7_8', 'hotSpareSize': 0, 'usedPoolSpace': 5313000380416, 'driveTypes': ['sas'], 'unconfiguredSpaceByDriveType': {}, 'unconfiguredSpaceAsStrings': '0', 'model': '2650', 'unconfiguredSpace': 0 } SNAPSHOT_GROUP = { 'status': 'optimal', 'autoDeleteLimit': 0, 'maxRepositoryCapacity': '-65536', 'rollbackStatus': 'none', 'unusableRepositoryCapacity': '0', 'pitGroupRef': '3300000060080E500023C7340000098D5294AC9A', 'clusterSize': 65536, 'label': 'C6JICISVHNG2TFZX4XB5ZWL7O', 'maxBaseCapacity': '476187142128128', 'repositoryVolume': '3600000060080E500023BB3400001FA952CEF12C', 'fullWarnThreshold': 99, 'repFullPolicy': 'purgepit', 'action': 'none', 'rollbackPriority': 'medium', 'creationPendingStatus': 'none', 'consistencyGroupRef': '0000000000000000000000000000000000000000', 'volumeHandle': 49153, 'consistencyGroup': False, 'baseVolume': '0200000060080E500023C734000009825294A534' } SNAPSHOT_IMAGE = { 'status': 'optimal', 'pitCapacity': '2147483648', 'pitTimestamp': '1389315375', 'pitGroupRef': '3300000060080E500023C7340000098D5294AC9A', 'creationMethod': 'user', 'repositoryCapacityUtilization': '2818048', 'activeCOW': True, 'isRollbackSource': False, 'pitRef': '3400000060080E500023BB3400631F335294A5A8', 'pitSequenceNumber': '19' } HARDWARE_INVENTORY = { 'iscsiPorts': [ { 'controllerId': '070000000000000000000002', 'ipv4Enabled': True, 'ipv4Data': { 'ipv4Address': '0.0.0.0', 'ipv4AddressConfigMethod': 'configStatic', 'ipv4VlanId': { 'isEnabled': False, 'value': 0 }, 'ipv4AddressData': { 'ipv4Address': '172.20.123.66', 'ipv4SubnetMask': '255.255.255.0', 'configState': 'configured', 'ipv4GatewayAddress': '0.0.0.0' } }, 'tcpListenPort': 3260, 'interfaceRef': '2202040000000000000000000000000000000000', 'iqn': 'iqn.1992-01.com.lsi:2365.60080e500023c73400000000515af323' } ], 'fibrePorts': [ { "channel": 1, "loopID": 126, "speed": 800, "hardAddress": 6, "nodeName": "20020080E5322230", "portName": "20130080E5322230", "portId": "011700", "topology": "fabric", "part": "PM8032 ", "revision": 8, "chanMiswire": False, "esmMiswire": False, "linkStatus": "up", "isDegraded": False, "speedControl": "auto", "maxSpeed": 800, "speedNegError": False, "reserved1": "000000000000000000000000", "reserved2": "", "ddsChannelState": 0, "ddsStateReason": 0, "ddsStateWho": 0, "isLocal": True, "channelPorts": [], "currentInterfaceSpeed": "speed8gig", "maximumInterfaceSpeed": "speed8gig", "interfaceRef": "2202020000000000000000000000000000000000", "physicalLocation": { "trayRef": "0000000000000000000000000000000000000000", "slot": 0, "locationParent": { "refType": "generic", "controllerRef": None, "symbolRef": "0000000000000000000000000000000000000000", "typedReference": None }, "locationPosition": 0 }, "isTrunkCapable": False, "trunkMiswire": False, "protectionInformationCapable": True, "controllerId": "070000000000000000000002", "interfaceId": "2202020000000000000000000000000000000000", "addressId": "20130080E5322230", "niceAddressId": "20:13:00:80:E5:32:22:30" }, { "channel": 2, "loopID": 126, "speed": 800, "hardAddress": 7, "nodeName": "20020080E5322230", "portName": "20230080E5322230", "portId": "011700", "topology": "fabric", "part": "PM8032 ", "revision": 8, "chanMiswire": False, "esmMiswire": False, "linkStatus": "up", "isDegraded": False, "speedControl": "auto", "maxSpeed": 800, "speedNegError": False, "reserved1": "000000000000000000000000", "reserved2": "", "ddsChannelState": 0, "ddsStateReason": 0, "ddsStateWho": 0, "isLocal": True, "channelPorts": [], "currentInterfaceSpeed": "speed8gig", "maximumInterfaceSpeed": "speed8gig", "interfaceRef": "2202030000000000000000000000000000000000", "physicalLocation": { "trayRef": "0000000000000000000000000000000000000000", "slot": 0, "locationParent": { "refType": "generic", "controllerRef": None, "symbolRef": "0000000000000000000000000000000000000000", "typedReference": None }, "locationPosition": 0 }, "isTrunkCapable": False, "trunkMiswire": False, "protectionInformationCapable": True, "controllerId": "070000000000000000000002", "interfaceId": "2202030000000000000000000000000000000000", "addressId": "20230080E5322230", "niceAddressId": "20:23:00:80:E5:32:22:30" }, ] } VOLUME_COPY_JOB = { "status": "complete", "cloneCopy": True, "pgRef": "3300000060080E500023C73400000ACA52D29454", "volcopyHandle": 49160, "idleTargetWriteProt": True, "copyPriority": "priority2", "volcopyRef": "1800000060080E500023C73400000ACF52D29466", "worldWideName": "60080E500023C73400000ACF52D29466", "copyCompleteTime": "0", "sourceVolume": "3500000060080E500023C73400000ACE52D29462", "currentManager": "070000000000000000000002", "copyStartTime": "1389551671", "reserved1": "00000000", "targetVolume": "0200000060080E500023C73400000A8C52D10675", } def create_configuration_eseries(): config = conf.Configuration(None) config.append_config_values(na_opts.netapp_connection_opts) config.append_config_values(na_opts.netapp_transport_opts) config.append_config_values(na_opts.netapp_basicauth_opts) config.append_config_values(na_opts.netapp_provisioning_opts) config.append_config_values(na_opts.netapp_eseries_opts) config.netapp_storage_protocol = 'iscsi' config.netapp_login = 'rw' config.netapp_password = 'rw' config.netapp_server_hostname = '127.0.0.1' config.netapp_transport_type = 'http' config.netapp_server_port = '8080' config.netapp_storage_pools = 'DDP' config.netapp_storage_family = 'eseries' config.netapp_sa_password = 'saPass' config.netapp_controller_ips = '10.11.12.13,10.11.12.14' config.netapp_webservice_path = '/devmgr/v2' config.netapp_enable_multiattach = False return config def deepcopy_return_value_method_decorator(fn): """Returns a deepcopy of the returned value of the wrapped function.""" def decorator(*args, **kwargs): return copy.deepcopy(fn(*args, **kwargs)) return decorator def deepcopy_return_value_class_decorator(cls): """Wraps 'non-protected' methods of a class with decorator. Wraps all 'non-protected' methods of a class with the deepcopy_return_value_method_decorator decorator. """ class NewClass(cls): def __getattribute__(self, attr_name): obj = super(NewClass, self).__getattribute__(attr_name) if (hasattr(obj, '__call__') and not attr_name.startswith('_') and not isinstance(obj, mock.Mock)): return deepcopy_return_value_method_decorator(obj) return obj return NewClass @deepcopy_return_value_class_decorator class FakeEseriesClient(object): def __init__(self, *args, **kwargs): pass def list_storage_pools(self): return [STORAGE_POOL] def register_storage_system(self, *args, **kwargs): return { 'freePoolSpace': '17055871480319', 'driveCount': 24, 'wwn': '60080E500023C73400000000515AF323', 'id': '1', 'hotSpareSizeAsString': '0', 'hostSparesUsed': 0, 'types': '', 'hostSpareCountInStandby': 0, 'status': 'optimal', 'trayCount': 1, 'usedPoolSpaceAsString': '37452115456', 'ip2': '10.63.165.216', 'ip1': '10.63.165.215', 'freePoolSpaceAsString': '17055871480319', 'hotSpareCount': 0, 'hotSpareSize': '0', 'name': 'stle2600-7_8', 'usedPoolSpace': '37452115456', 'driveTypes': ['sas'], 'unconfiguredSpaceByDriveType': {}, 'unconfiguredSpaceAsStrings': '0', 'model': '2650', 'unconfiguredSpace': '0' } def list_volumes(self): return [VOLUME] def delete_volume(self, vol): pass def create_host_group(self, name): return MULTIATTACH_HOST_GROUP def get_host_group(self, ref): return MULTIATTACH_HOST_GROUP def list_host_groups(self): return [MULTIATTACH_HOST_GROUP] def get_host_group_by_name(self, name, *args, **kwargs): host_groups = self.list_host_groups() return [host_group for host_group in host_groups if host_group['label'] == name][0] def set_host_group_for_host(self, *args, **kwargs): pass def create_host_with_ports(self, *args, **kwargs): return HOST def list_hosts(self): return [HOST, HOST_2] def get_host(self, *args, **kwargs): return HOST def create_volume_mapping(self, *args, **kwargs): return VOLUME_MAPPING def get_volume_mappings(self): return [VOLUME_MAPPING] def get_volume_mappings_for_volume(self, volume): return [VOLUME_MAPPING] def get_volume_mappings_for_host(self, host_ref): return [VOLUME_MAPPING] def get_volume_mappings_for_host_group(self, hg_ref): return [VOLUME_MAPPING] def delete_volume_mapping(self): return def move_volume_mapping_via_symbol(self, map_ref, to_ref, lun_id): return {'lun': lun_id} def list_storage_system(self): return STORAGE_SYSTEM def list_storage_systems(self): return [STORAGE_SYSTEM] def list_snapshot_groups(self): return [SNAPSHOT_GROUP] def list_snapshot_images(self): return [SNAPSHOT_IMAGE] def list_host_types(self): return [ { 'id': '4', 'code': 'AIX', 'name': 'AIX', 'index': 4 }, { 'id': '5', 'code': 'IRX', 'name': 'IRX', 'index': 5 }, { 'id': '6', 'code': 'LnxALUA', 'name': 'LnxALUA', 'index': 6 } ] def list_hardware_inventory(self): return HARDWARE_INVENTORY def create_volume_copy_job(self, *args, **kwargs): return VOLUME_COPY_JOB def list_vol_copy_job(self, *args, **kwargs): return VOLUME_COPY_JOB def delete_vol_copy_job(self, *args, **kwargs): pass def delete_snapshot_volume(self, *args, **kwargs): pass def list_target_wwpns(self, *args, **kwargs): return [WWPN_2]