+++ /dev/null
-# Copyright (c) 2014 NetApp, Inc.
-# Copyright (c) 2015 Alex Meade. All Rights Reserved.
-# Copyright (c) 2015 Rushil Chugh. All Rights Reserved.
-# Copyright (c) 2015 Navneet Singh. All Rights Reserved.
-# Copyright (c) 2015 Michael Price. 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.
-"""Tests for NetApp e-series iscsi volume driver."""
-
-import copy
-import ddt
-import json
-import re
-import socket
-
-import mock
-import requests
-
-from cinder import exception
-from cinder import test
-from cinder.tests.unit.volume.drivers.netapp.eseries import fakes as \
- fakes
-from cinder.volume import configuration as conf
-from cinder.volume.drivers.netapp import common
-from cinder.volume.drivers.netapp.eseries import client
-from cinder.volume.drivers.netapp.eseries import library
-from cinder.volume.drivers.netapp.eseries import utils
-from cinder.volume.drivers.netapp import options
-import cinder.volume.drivers.netapp.utils as na_utils
-
-
-def create_configuration():
- configuration = conf.Configuration(None)
- configuration.append_config_values(options.netapp_basicauth_opts)
- configuration.append_config_values(options.netapp_eseries_opts)
- configuration.append_config_values(options.netapp_san_opts)
- return configuration
-
-
-class FakeEseriesResponse(object):
- """Fake response to requests."""
-
- def __init__(self, code=None, text=None):
- self.status_code = code
- self.text = text
-
- def json(self):
- return json.loads(self.text)
-
-
-class FakeEseriesServerHandler(object):
- """HTTP handler that fakes enough stuff to allow the driver to run."""
-
- def do_GET(self, path, params, data, headers):
- """Respond to a GET request."""
-
- response = FakeEseriesResponse()
- if "/devmgr/vn" not in path:
- response.status_code = 404
-
- (__, ___, path) = path.partition("/devmgr/vn")
- if re.match("^/storage-systems/[0-9a-zA-Z]+/volumes$", path):
- response.status_code = 200
- response.text = """[{"extremeProtection": false,
- "pitBaseVolume": false,
- "dssMaxSegmentSize": 131072,
- "totalSizeInBytes": "2126008832", "raidLevel": "raid6",
- "volumeRef": "0200000060080E500023C73400000AAA52D11677",
- "listOfMappings": [], "sectorOffset": "6",
- "id": "0200000060080E500023C73400000AAA52D11677",
- "wwn": "60080E500023C73400000AAA52D11677",
- "capacity": "2126008832", "mgmtClientAttribute": 0,
- "label": "repos_0006", "volumeFull": false,
- "blkSize": 512, "volumeCopyTarget": false,
- "volumeGroupRef":
- "0400000060080E500023BB3400001F9F52CECC3F",
- "preferredControllerId": "070000000000000000000002",
- "currentManager": "070000000000000000000002",
- "applicationTagOwned": true, "status": "optimal",
- "segmentSize": 131072, "volumeUse":
- "freeRepositoryVolume", "action": "none",
- "name": "repos_0006", "worldWideName":
- "60080E500023C73400000AAA52D11677", "currentControllerId"
- : "070000000000000000000002",
- "protectionInformationCapable": false, "mapped": false,
- "reconPriority": 1, "protectionType": "type0Protection"}
- ,
- {"extremeProtection": false, "pitBaseVolume": true,
- "dssMaxSegmentSize": 131072,
- "totalSizeInBytes": "2147483648", "raidLevel": "raid6",
- "volumeRef": "0200000060080E500023BB3400001FC352D14CB2",
- "listOfMappings": [], "sectorOffset": "15",
- "id": "0200000060080E500023BB3400001FC352D14CB2",
- "wwn": "60080E500023BB3400001FC352D14CB2",
- "capacity": "2147483648", "mgmtClientAttribute": 0,
- "label": "bdm-vc-test-1", "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"},
- {"extremeProtection": false, "pitBaseVolume": true,
- "dssMaxSegmentSize": 131072,
- "totalSizeInBytes": "1073741824", "raidLevel": "raid6",
- "volumeRef": "0200000060080E500023BB34000003FB515C2293",
- "listOfMappings": [{
- "lunMappingRef":"8800000000000000000000000000000000000000",
- "lun": 0,
- "ssid": 16384,
- "perms": 15,
- "volumeRef": "0200000060080E500023BB34000003FB515C2293",
- "type": "all",
- "mapRef": "8400000060080E500023C73400300381515BFBA3"
- }], "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"}]"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volumes/[0-9A-Za-z]+$",
- path):
- response.status_code = 200
- response.text = """{"extremeProtection": false,
- "pitBaseVolume": true,
- "dssMaxSegmentSize": 131072,
- "totalSizeInBytes": "2147483648", "raidLevel": "raid6",
- "volumeRef": "0200000060080E500023BB3400001FC352D14CB2",
- "listOfMappings": [], "sectorOffset": "15",
- "id": "0200000060080E500023BB3400001FC352D14CB2",
- "wwn": "60080E500023BB3400001FC352D14CB2",
- "capacity": "2147483648", "mgmtClientAttribute": 0,
- "label": "bdm-vc-test-1", "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"}"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/hardware-inventory$",
- path):
- response.status_code = 200
- response.text = """
- {"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"
- }]}"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/hosts$", path):
- response.status_code = 200
- response.text = """[{"isSAControlled": false,
- "confirmLUNMappingCreation"
- : false, "label": "stlrx300s7-55", "isLargeBlockFormatHost":
- false, "clusterRef": "8500000060080E500023C7340036035F515B78FC",
- "protectionInformationCapableAccessMethod": false,
- "ports": [], "hostRef":
- "8400000060080E500023C73400300381515BFBA3", "hostTypeIndex": 6,
- "hostSidePorts": [{"label": "NewStore", "type": "iscsi",
- "address": "iqn.1998-01.com.vmware:localhost-28a58148"}]}]"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/host-types$", path):
- response.status_code = 200
- response.text = """[{
- "id" : "4",
- "code" : "AIX",
- "name" : "AIX",
- "index" : 4
- }, {
- "id" : "5",
- "code" : "IRX",
- "name" : "IRX",
- "index" : 5
- }, {
- "id" : "6",
- "code" : "LnxALUA",
- "name" : "LnxALUA",
- "index" : 6
- }]"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-groups$", path):
- response.status_code = 200
- response.text = """[]"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-images$", path):
- response.status_code = 200
- response.text = """[]"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/storage-pools$", path):
- response.status_code = 200
- response.text = """[ {"protectionInformationCapabilities":
- {"protectionInformationCapable": true, "protectionType":
- "type2Protection"}, "raidLevel": "raidDiskPool", "reserved1":
- "000000000000000000000000", "reserved2": "", "isInaccessible":
- false, "label": "DDP", "state": "complete", "usage":
- "standard", "offline": false, "drawerLossProtection": false,
- "trayLossProtection": false, "securityType": "capable",
- "volumeGroupRef": "0400000060080E500023BB3400001F9F52CECC3F",
- "driveBlockFormat": "__UNDEFINED", "usedSpace": "81604378624",
- "volumeGroupData": {"type": "diskPool", "diskPoolData":
- {"criticalReconstructPriority": "highest",
- "poolUtilizationState": "utilizationOptimal",
- "reconstructionReservedDriveCountCurrent": 3, "allocGranularity":
- "4294967296", "degradedReconstructPriority": "high",
- "backgroundOperationPriority": "low",
- "reconstructionReservedAmt": "897111293952", "unusableCapacity":
- "0", "reconstructionReservedDriveCount": 1,
- "poolUtilizationWarningThreshold": 50,
- "poolUtilizationCriticalThreshold": 85}}, "spindleSpeed": 10000,
- "worldWideName": "60080E500023BB3400001F9F52CECC3F",
- "spindleSpeedMatch": true, "totalRaidedSpace": "17273253317836",
- "sequenceNum": 2, "protectionInformationCapable": false}]"""
- elif re.match("^/storage-systems$", path):
- response.status_code = 200
- response.text = """[ {"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}]"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+$", path):
- response.status_code = 200
- response.text = """{"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}"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volume-copy-jobs"
- "/[0-9a-zA-Z]+$", path):
- response.status_code = 200
- response.text = """{"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"}"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volume-mappings$", path):
- response.status_code = 200
- response.text = """[
- {
- "lunMappingRef":"8800000000000000000000000000000000000000",
- "lun": 0,
- "ssid": 16384,
- "perms": 15,
- "volumeRef": "0200000060080E500023BB34000003FB515C2293",
- "type": "all",
- "mapRef": "8400000060080E500023C73400300381515BFBA3"
- }]
- """
- else:
- # Unknown API
- response.status_code = 500
-
- return response
-
- def do_POST(self, path, params, data, headers):
- """Respond to a POST request."""
-
- response = FakeEseriesResponse()
- if "/devmgr/vn" not in path:
- response.status_code = 404
- data = json.loads(data) if data else None
- (__, ___, path) = path.partition("/devmgr/vn")
- if re.match("^/storage-systems/[0-9a-zA-Z]+/volumes$", path):
- response.status_code = 200
- text_json = json.loads("""
- {"extremeProtection": false, "pitBaseVolume": true,
- "dssMaxSegmentSize": 131072,
- "totalSizeInBytes": "1073741824", "raidLevel": "raid6",
- "volumeRef": "0200000060080E500023BB34000003FB515C2293",
- "listOfMappings": [{
- "lunMappingRef":"8800000000000000000000000000000000000000",
- "lun": 0,
- "ssid": 16384,
- "perms": 15,
- "volumeRef": "0200000060080E500023BB34000003FB515C2293",
- "type": "all",
- "mapRef": "8400000060080E500023C73400300381515BFBA3"
- }], "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"}""")
- text_json['label'] = data['name']
- text_json['name'] = data['name']
- text_json['volumeRef'] = data['name']
- text_json['id'] = data['name']
- response.text = json.dumps(text_json)
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volume-mappings$", path):
- response.status_code = 200
- text_json = json.loads("""
- {
- "lunMappingRef":"8800000000000000000000000000000000000000",
- "lun": 0,
- "ssid": 16384,
- "perms": 15,
- "volumeRef": "0200000060080E500023BB34000003FB515C2293",
- "type": "all",
- "mapRef": "8400000060080E500023C73400300381515BFBA3"
- }
- """)
- text_json['volumeRef'] = data['mappableObjectId']
- text_json['mapRef'] = data['targetId']
- response.text = json.dumps(text_json)
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/hosts$", path):
- response.status_code = 200
- response.text = """{"isSAControlled": false,
- "confirmLUNMappingCreation"
- : false, "label": "stlrx300s7-55", "isLargeBlockFormatHost":
- false, "clusterRef": "8500000060080E500023C7340036035F515B78FC",
- "protectionInformationCapableAccessMethod": false,
- "ports": [], "hostRef":
- "8400000060080E500023C73400300381515BFBA3", "hostTypeIndex": 10,
- "hostSidePorts": [{"label": "NewStore", "type": "iscsi",
- "address": "iqn.1998-01.com.vmware:localhost-28a58148"}]}"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-groups$", path):
- response.status_code = 200
- text_json = json.loads("""{"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"}""")
- text_json['label'] = data['name']
- text_json['name'] = data['name']
- text_json['pitGroupRef'] = data['name']
- text_json['id'] = data['name']
- text_json['baseVolume'] = data['baseMappableObjectId']
- response.text = json.dumps(text_json)
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-images$", path):
- response.status_code = 200
- text_json = json.loads("""{"status": "optimal",
- "pitCapacity": "2147483648",
- "pitTimestamp": "1389315375", "pitGroupRef":
- "3300000060080E500023C7340000098D5294AC9A", "creationMethod":
- "user", "repositoryCapacityUtilization": "2818048",
- "activeCOW": true, "isRollbackSource": false, "pitRef":
- "3400000060080E500023BB3400631F335294A5A8",
- "pitSequenceNumber": "19"}""")
- text_json['label'] = data['groupId']
- text_json['name'] = data['groupId']
- text_json['id'] = data['groupId']
- text_json['pitGroupRef'] = data['groupId']
- response.text = json.dumps(text_json)
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-volumes$",
- path):
- response.status_code = 200
- text_json = json.loads("""{"unusableRepositoryCapacity": "0",
- "totalSizeInBytes":
- "-1", "worldWideName": "60080E500023BB3400001FAD52CEF2F5",
- "boundToPIT": true, "wwn":
- "60080E500023BB3400001FAD52CEF2F5", "id":
- "3500000060080E500023BB3400001FAD52CEF2F5",
- "baseVol": "0200000060080E500023BB3400001FA352CECCAE",
- "label": "bdm-pv-1", "volumeFull": false,
- "preferredControllerId": "070000000000000000000001", "offline":
- false, "viewSequenceNumber": "10", "status": "optimal",
- "viewRef": "3500000060080E500023BB3400001FAD52CEF2F5",
- "mapped": false, "accessMode": "readOnly", "viewTime":
- "1389315613", "repositoryVolume":
- "0000000000000000000000000000000000000000", "preferredManager":
- "070000000000000000000001", "volumeHandle": 16385,
- "currentManager": "070000000000000000000001",
- "maxRepositoryCapacity": "0", "name": "bdm-pv-1",
- "fullWarnThreshold": 0, "currentControllerId":
- "070000000000000000000001", "basePIT":
- "3400000060080E500023BB3400631F335294A5A8", "clusterSize":
- 0, "mgmtClientAttribute": 0}""")
- text_json['label'] = data['name']
- text_json['name'] = data['name']
- text_json['id'] = data['name']
- text_json['basePIT'] = data['snapshotImageId']
- text_json['baseVol'] = data['baseMappableObjectId']
- response.text = json.dumps(text_json)
- elif re.match("^/storage-systems$", path):
- response.status_code = 200
- response.text = """{"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"}"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+$",
- path):
- response.status_code = 200
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volume-copy-jobs$",
- path):
- response.status_code = 200
- response.text = """{"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"}"""
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volumes/[0-9A-Za-z]+$",
- path):
- response.status_code = 200
- response.text = """{"extremeProtection": false,
- "pitBaseVolume": true,
- "dssMaxSegmentSize": 131072,
- "totalSizeInBytes": "1073741824", "raidLevel": "raid6",
- "volumeRef": "0200000060080E500023BB34000003FB515C2293",
- "listOfMappings": [{
- "lunMappingRef":"8800000000000000000000000000000000000000",
- "lun": 0,
- "ssid": 16384,
- "perms": 15,
- "volumeRef": "0200000060080E500023BB34000003FB515C2293",
- "type": "all",
- "mapRef": "8400000060080E500023C73400300381515BFBA3"
- }], "sectorOffset": "15",
- "id": "0200000060080E500023BB34000003FB515C2293",
- "wwn": "60080E500023BB3400001FC352D14CB2",
- "capacity": "2147483648", "mgmtClientAttribute": 0,
- "label": "rename",
- "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"}"""
- else:
- # Unknown API
- response.status_code = 500
-
- return response
-
- def do_DELETE(self, path, params, data, headers):
- """Respond to a DELETE request."""
-
- response = FakeEseriesResponse()
- if "/devmgr/vn" not in path:
- response.status_code = 500
-
- (__, ___, path) = path.partition("/devmgr/vn")
- if re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-images"
- "/[0-9A-Za-z]+$", path):
- code = 204
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-groups"
- "/[0-9A-Za-z]+$", path):
- code = 204
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-volumes"
- "/[0-9A-Za-z]+$", path):
- code = 204
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volume-copy-jobs"
- "/[0-9A-Za-z]+$", path):
- code = 204
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volumes"
- "/[0-9A-Za-z]+$", path):
- code = 204
- elif re.match("^/storage-systems/[0-9a-zA-Z]+/volume-mappings/"
- "[0-9a-zA-Z]+$", path):
- code = 204
- else:
- code = 500
-
- response.status_code = code
- return response
-
-
-class FakeEseriesHTTPSession(object):
- """A fake requests.Session for netapp tests."""
- def __init__(self):
- self.handler = FakeEseriesServerHandler()
-
- def request(self, method, url, params, data, headers, timeout, verify):
- address = '127.0.0.1:80'
- (__, ___, path) = url.partition(address)
- if method.upper() == 'GET':
- return self.handler.do_GET(path, params, data, headers)
- elif method.upper() == 'POST':
- return self.handler.do_POST(path, params, data, headers)
- elif method.upper() == 'DELETE':
- return self.handler.do_DELETE(path, params, data, headers)
- else:
- raise exception.Invalid()
-
-
-@ddt.ddt
-class NetAppEseriesISCSIDriverTestCase(test.TestCase):
- """Test case for NetApp e-series iscsi driver."""
-
- volume = {'id': '114774fb-e15a-4fae-8ee2-c9723e3645ef', 'size': 1,
- 'volume_name': 'lun1', 'host': 'hostname@backend#DDP',
- 'os_type': 'linux', 'provider_location': 'lun1',
- 'name_id': '114774fb-e15a-4fae-8ee2-c9723e3645ef',
- 'provider_auth': 'provider a b', 'project_id': 'project',
- 'display_name': None, 'display_description': 'lun1',
- 'volume_type_id': None}
- snapshot = {'id': '17928122-553b-4da9-9737-e5c3dcd97f75',
- 'volume_id': '114774fb-e15a-4fae-8ee2-c9723e3645ef',
- 'size': 2, 'volume_name': 'lun1',
- 'volume_size': 2, 'project_id': 'project',
- 'display_name': None, 'display_description': 'lun1',
- 'volume_type_id': None}
- volume_sec = {'id': 'b6c01641-8955-4917-a5e3-077147478575',
- 'size': 2, 'volume_name': 'lun1',
- 'os_type': 'linux', 'provider_location': 'lun1',
- 'name_id': 'b6c01641-8955-4917-a5e3-077147478575',
- 'provider_auth': None, 'project_id': 'project',
- 'display_name': None, 'display_description': 'lun1',
- 'volume_type_id': None}
- volume_clone = {'id': 'b4b24b27-c716-4647-b66d-8b93ead770a5', 'size': 3,
- 'volume_name': 'lun1',
- 'os_type': 'linux', 'provider_location': 'cl_sm',
- 'name_id': 'b4b24b27-c716-4647-b66d-8b93ead770a5',
- 'provider_auth': None,
- 'project_id': 'project', 'display_name': None,
- 'display_description': 'lun1',
- 'volume_type_id': None}
- volume_clone_large = {'id': 'f6ef5bf5-e24f-4cbb-b4c4-11d631d6e553',
- 'size': 6, 'volume_name': 'lun1',
- 'os_type': 'linux', 'provider_location': 'cl_lg',
- 'name_id': 'f6ef5bf5-e24f-4cbb-b4c4-11d631d6e553',
- 'provider_auth': None,
- 'project_id': 'project', 'display_name': None,
- 'display_description': 'lun1',
- 'volume_type_id': None}
- fake_eseries_volume_label = utils.convert_uuid_to_es_fmt(volume['id'])
- connector = {'initiator': 'iqn.1998-01.com.vmware:localhost-28a58148'}
- fake_size_gb = volume['size']
- fake_eseries_pool_label = 'DDP'
- fake_ref = {'source-name': 'CFDGJSLS'}
- fake_ret_vol = {'id': 'vol_id', 'label': 'label',
- 'worldWideName': 'wwn', 'capacity': '2147583648'}
-
- def setUp(self):
- super(NetAppEseriesISCSIDriverTestCase, self).setUp()
- self._custom_setup()
-
- def _custom_setup(self):
- self.mock_object(na_utils, 'OpenStackInfo')
-
- configuration = self._set_config(create_configuration())
- self.driver = common.NetAppDriver(configuration=configuration)
- self.library = self.driver.library
- self.mock_object(requests, 'Session', FakeEseriesHTTPSession)
- self.mock_object(self.library,
- '_check_mode_get_or_register_storage_system')
- self.mock_object(self.driver.library, '_check_storage_system')
- self.driver.do_setup(context='context')
- self.driver.library._client._endpoint = fakes.FAKE_ENDPOINT_HTTP
-
- def _set_config(self, configuration):
- configuration.netapp_storage_family = 'eseries'
- configuration.netapp_storage_protocol = 'iscsi'
- configuration.netapp_transport_type = 'http'
- configuration.netapp_server_hostname = '127.0.0.1'
- configuration.netapp_server_port = None
- configuration.netapp_webservice_path = '/devmgr/vn'
- configuration.netapp_controller_ips = '127.0.0.2,127.0.0.3'
- configuration.netapp_sa_password = 'pass1234'
- configuration.netapp_login = 'rw'
- configuration.netapp_password = 'rw'
- configuration.netapp_storage_pools = 'DDP'
- configuration.netapp_enable_multiattach = False
- return configuration
-
- def test_embedded_mode(self):
- self.mock_object(self.driver.library,
- '_check_mode_get_or_register_storage_system')
- self.mock_object(client.RestClient, '_init_features')
- configuration = self._set_config(create_configuration())
- configuration.netapp_controller_ips = '127.0.0.1,127.0.0.3'
-
- driver = common.NetAppDriver(configuration=configuration)
- self.mock_object(client.RestClient, 'list_storage_systems', mock.Mock(
- return_value=[fakes.STORAGE_SYSTEM]))
- driver.do_setup(context='context')
-
- self.assertEqual('1fa6efb5-f07b-4de4-9f0e-52e5f7ff5d1b',
- driver.library._client.get_system_id())
-
- def test_check_system_pwd_not_sync(self):
- def list_system():
- if getattr(self, 'test_count', None):
- self.test_count = 1
- return {'status': 'passwordoutofsync'}
- return {'status': 'needsAttention'}
-
- self.library._client.list_storage_system = mock.Mock(wraps=list_system)
- result = self.library._check_storage_system()
- self.assertTrue(result)
-
- def test_create_destroy(self):
- self.mock_object(client.RestClient, 'delete_volume',
- mock.Mock(return_value='None'))
- self.mock_object(self.driver.library, 'create_volume',
- mock.Mock(return_value=self.volume))
- self.mock_object(self.library._client, 'list_volume', mock.Mock(
- return_value=fakes.VOLUME))
-
- self.driver.create_volume(self.volume)
- self.driver.delete_volume(self.volume)
-
- def test_vol_stats(self):
- self.driver.get_volume_stats(refresh=False)
-
- def test_get_pool(self):
- self.mock_object(self.library, '_get_volume',
- mock.Mock(return_value={
- 'volumeGroupRef': 'fake_ref'}))
- self.mock_object(self.library._client, "get_storage_pool",
- mock.Mock(return_value={'volumeGroupRef': 'fake_ref',
- 'label': 'ddp1'}))
-
- pool = self.driver.get_pool({'name_id': 'fake-uuid'})
-
- self.assertEqual('ddp1', pool)
-
- def test_get_pool_no_pools(self):
- self.mock_object(self.library, '_get_volume',
- mock.Mock(return_value={
- 'volumeGroupRef': 'fake_ref'}))
- self.mock_object(self.library._client, "get_storage_pool",
- mock.Mock(return_value=None))
-
- pool = self.driver.get_pool({'name_id': 'fake-uuid'})
-
- self.assertIsNone(pool)
-
- @mock.patch.object(library.NetAppESeriesLibrary, '_create_volume',
- mock.Mock())
- def test_create_volume(self):
-
- self.driver.create_volume(self.volume)
-
- self.library._create_volume.assert_called_with(
- 'DDP', self.fake_eseries_volume_label, self.volume['size'], {})
-
- def test_create_volume_no_pool_provided_by_scheduler(self):
- volume = copy.deepcopy(self.volume)
- volume['host'] = "host@backend" # missing pool
- self.assertRaises(exception.InvalidHost, self.driver.create_volume,
- volume)
-
- @mock.patch.object(client.RestClient, 'list_storage_pools')
- def test_helper_create_volume_fail(self, fake_list_pools):
- fake_pool = {}
- fake_pool['label'] = self.fake_eseries_pool_label
- fake_pool['volumeGroupRef'] = 'foo'
- fake_pool['raidLevel'] = 'raidDiskPool'
- fake_pools = [fake_pool]
- fake_list_pools.return_value = fake_pools
- wrong_eseries_pool_label = 'hostname@backend'
- self.assertRaises(exception.NetAppDriverException,
- self.library._create_volume,
- wrong_eseries_pool_label,
- self.fake_eseries_volume_label,
- self.fake_size_gb)
-
- @mock.patch.object(library.LOG, 'info')
- @mock.patch.object(client.RestClient, 'list_storage_pools')
- @mock.patch.object(client.RestClient, 'create_volume',
- mock.MagicMock(return_value='CorrectVolume'))
- def test_helper_create_volume(self, storage_pools, log_info):
- fake_pool = {}
- fake_pool['label'] = self.fake_eseries_pool_label
- fake_pool['volumeGroupRef'] = 'foo'
- fake_pool['raidLevel'] = 'raidDiskPool'
- fake_pools = [fake_pool]
- storage_pools.return_value = fake_pools
- storage_vol = self.library._create_volume(
- self.fake_eseries_pool_label,
- self.fake_eseries_volume_label,
- self.fake_size_gb)
- log_info.assert_called_once_with("Created volume with label %s.",
- self.fake_eseries_volume_label)
- self.assertEqual('CorrectVolume', storage_vol)
-
- @mock.patch.object(client.RestClient, 'list_storage_pools')
- @mock.patch.object(client.RestClient, 'create_volume',
- mock.MagicMock(
- side_effect=exception.NetAppDriverException))
- @mock.patch.object(library.LOG, 'info', mock.Mock())
- def test_create_volume_check_exception(self, fake_list_pools):
- fake_pool = {}
- fake_pool['label'] = self.fake_eseries_pool_label
- fake_pool['volumeGroupRef'] = 'foo'
- fake_pool['raidLevel'] = 'raidDiskPool'
- fake_pools = [fake_pool]
- fake_list_pools.return_value = fake_pools
- self.assertRaises(exception.NetAppDriverException,
- self.library._create_volume,
- self.fake_eseries_pool_label,
- self.fake_eseries_volume_label, self.fake_size_gb)
-
- def test_portal_for_vol_controller(self):
- volume = {'id': 'vol_id', 'currentManager': 'ctrl1'}
- vol_nomatch = {'id': 'vol_id', 'currentManager': 'ctrl3'}
- portals = [{'controller': 'ctrl2', 'iqn': 'iqn2'},
- {'controller': 'ctrl1', 'iqn': 'iqn1'}]
- portal = self.library._get_iscsi_portal_for_vol(volume, portals)
- self.assertEqual({'controller': 'ctrl1', 'iqn': 'iqn1'}, portal)
- portal = self.library._get_iscsi_portal_for_vol(vol_nomatch, portals)
- self.assertEqual({'controller': 'ctrl2', 'iqn': 'iqn2'}, portal)
-
- def test_portal_for_vol_any_false(self):
- vol_nomatch = {'id': 'vol_id', 'currentManager': 'ctrl3'}
- portals = [{'controller': 'ctrl2', 'iqn': 'iqn2'},
- {'controller': 'ctrl1', 'iqn': 'iqn1'}]
- self.assertRaises(exception.NetAppDriverException,
- self.library._get_iscsi_portal_for_vol,
- vol_nomatch, portals, False)
-
- def test_setup_error_unsupported_host_type(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_host_type = 'garbage'
- driver = common.NetAppDriver(configuration=configuration)
- self.assertRaises(exception.NetAppDriverException,
- driver.library.check_for_setup_error)
-
- def test_check_host_type_default(self):
- configuration = self._set_config(create_configuration())
- driver = common.NetAppDriver(configuration=configuration)
- driver.library._check_host_type()
- self.assertEqual('LnxALUA', driver.library.host_type)
-
- def test_do_setup_all_default(self):
- configuration = self._set_config(create_configuration())
- driver = common.NetAppDriver(configuration=configuration)
- driver.library._check_mode_get_or_register_storage_system = mock.Mock()
- mock_invoke = self.mock_object(client, 'RestClient')
- driver.do_setup(context='context')
- mock_invoke.assert_called_with(**fakes.FAKE_CLIENT_PARAMS)
-
- def test_do_setup_http_default_port(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_transport_type = 'http'
- driver = common.NetAppDriver(configuration=configuration)
- driver.library._check_mode_get_or_register_storage_system = mock.Mock()
- mock_invoke = self.mock_object(client, 'RestClient')
- driver.do_setup(context='context')
- mock_invoke.assert_called_with(**fakes.FAKE_CLIENT_PARAMS)
-
- def test_do_setup_https_default_port(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_transport_type = 'https'
- driver = common.NetAppDriver(configuration=configuration)
- driver.library._check_mode_get_or_register_storage_system = mock.Mock()
- mock_invoke = self.mock_object(client, 'RestClient')
- driver.do_setup(context='context')
- FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=8443,
- scheme='https')
- mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
-
- def test_do_setup_http_non_default_port(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_server_port = 81
- driver = common.NetAppDriver(configuration=configuration)
- driver.library._check_mode_get_or_register_storage_system = mock.Mock()
- mock_invoke = self.mock_object(client, 'RestClient')
- driver.do_setup(context='context')
- FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=81)
- mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
-
- def test_do_setup_https_non_default_port(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_transport_type = 'https'
- configuration.netapp_server_port = 446
- driver = common.NetAppDriver(configuration=configuration)
- driver.library._check_mode_get_or_register_storage_system = mock.Mock()
- mock_invoke = self.mock_object(client, 'RestClient')
- driver.do_setup(context='context')
- FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=446,
- scheme='https')
- mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
-
- def test_setup_good_controller_ip(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_controller_ips = '127.0.0.1'
- driver = common.NetAppDriver(configuration=configuration)
- driver.library._check_mode_get_or_register_storage_system
-
- def test_setup_good_controller_ips(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_controller_ips = '127.0.0.2,127.0.0.1'
- driver = common.NetAppDriver(configuration=configuration)
- driver.library._check_mode_get_or_register_storage_system
-
- def test_setup_missing_controller_ip(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_controller_ips = None
- driver = common.NetAppDriver(configuration=configuration)
- self.assertRaises(exception.InvalidInput,
- driver.do_setup, context='context')
-
- def test_setup_error_invalid_controller_ip(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_controller_ips = '987.65.43.21'
- driver = common.NetAppDriver(configuration=configuration)
- self.mock_object(na_utils, 'resolve_hostname',
- mock.Mock(side_effect=socket.gaierror))
-
- self.assertRaises(
- exception.NoValidHost,
- driver.library._check_mode_get_or_register_storage_system)
-
- def test_setup_error_invalid_first_controller_ip(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_controller_ips = '987.65.43.21,127.0.0.1'
- driver = common.NetAppDriver(configuration=configuration)
- self.mock_object(na_utils, 'resolve_hostname',
- mock.Mock(side_effect=socket.gaierror))
-
- self.assertRaises(
- exception.NoValidHost,
- driver.library._check_mode_get_or_register_storage_system)
-
- def test_setup_error_invalid_second_controller_ip(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_controller_ips = '127.0.0.1,987.65.43.21'
- driver = common.NetAppDriver(configuration=configuration)
- self.mock_object(na_utils, 'resolve_hostname',
- mock.Mock(side_effect=socket.gaierror))
-
- self.assertRaises(
- exception.NoValidHost,
- driver.library._check_mode_get_or_register_storage_system)
-
- def test_setup_error_invalid_both_controller_ips(self):
- configuration = self._set_config(create_configuration())
- configuration.netapp_controller_ips = '564.124.1231.1,987.65.43.21'
- driver = common.NetAppDriver(configuration=configuration)
- self.mock_object(na_utils, 'resolve_hostname',
- mock.Mock(side_effect=socket.gaierror))
-
- self.assertRaises(
- exception.NoValidHost,
- driver.library._check_mode_get_or_register_storage_system)
-
- def test_manage_existing_get_size(self):
- self.library._get_existing_vol_with_manage_ref = mock.Mock(
- return_value=self.fake_ret_vol)
- size = self.driver.manage_existing_get_size(self.volume, self.fake_ref)
- self.assertEqual(3, size)
- self.library._get_existing_vol_with_manage_ref.assert_called_once_with(
- self.fake_ref)
-
- def test_get_exist_vol_source_name_missing(self):
- self.library._client.list_volume = mock.Mock(
- side_effect=exception.InvalidInput(message=""))
- self.assertRaises(exception.ManageExistingInvalidReference,
- self.library._get_existing_vol_with_manage_ref,
- {'id': '1234'})
-
- @ddt.data('source-id', 'source-name')
- def test_get_exist_vol_source_not_found(self, attr_name):
- self.library._client.list_volume = mock.Mock(
- side_effect=exception.VolumeNotFound(message='Not Found'))
- self.assertRaises(exception.ManageExistingInvalidReference,
- self.library._get_existing_vol_with_manage_ref,
- {attr_name: 'name2'})
-
- self.library._client.list_volume.assert_called_once_with(
- 'name2')
-
- def test_get_exist_vol_with_manage_ref(self):
- fake_ret_vol = {'id': 'right'}
- self.library._client.list_volume = mock.Mock(return_value=fake_ret_vol)
-
- actual_vol = self.library._get_existing_vol_with_manage_ref(
- {'source-name': 'name2'})
-
- self.library._client.list_volume.assert_called_once_with('name2')
- self.assertEqual(fake_ret_vol, actual_vol)
-
- @mock.patch.object(utils, 'convert_uuid_to_es_fmt')
- def test_manage_existing_same_label(self, mock_convert_es_fmt):
- self.library._get_existing_vol_with_manage_ref = mock.Mock(
- return_value=self.fake_ret_vol)
- mock_convert_es_fmt.return_value = 'label'
- self.driver.manage_existing(self.volume, self.fake_ref)
- self.library._get_existing_vol_with_manage_ref.assert_called_once_with(
- self.fake_ref)
- mock_convert_es_fmt.assert_called_once_with(
- '114774fb-e15a-4fae-8ee2-c9723e3645ef')
-
- @mock.patch.object(utils, 'convert_uuid_to_es_fmt')
- def test_manage_existing_new(self, mock_convert_es_fmt):
- self.library._get_existing_vol_with_manage_ref = mock.Mock(
- return_value=self.fake_ret_vol)
- mock_convert_es_fmt.return_value = 'vol_label'
- self.library._client.update_volume = mock.Mock(
- return_value={'id': 'update', 'worldWideName': 'wwn'})
- self.driver.manage_existing(self.volume, self.fake_ref)
- self.library._get_existing_vol_with_manage_ref.assert_called_once_with(
- self.fake_ref)
- mock_convert_es_fmt.assert_called_once_with(
- '114774fb-e15a-4fae-8ee2-c9723e3645ef')
- self.library._client.update_volume.assert_called_once_with(
- 'vol_id', 'vol_label')
-
- @mock.patch.object(library.LOG, 'info')
- def test_unmanage(self, log_info):
- self.library._get_volume = mock.Mock(return_value=self.fake_ret_vol)
- self.driver.unmanage(self.volume)
- self.library._get_volume.assert_called_once_with(
- '114774fb-e15a-4fae-8ee2-c9723e3645ef')
- self.assertEqual(1, log_info.call_count)
--- /dev/null
+# Copyright (c) 2015 Alex Meade. All rights reserved.
+# Copyright (c) 2015 Michael Price. All rights reserved.
+# 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 abc
+import copy
+import ddt
+import mock
+import socket
+
+from cinder import exception
+from cinder.volume import configuration as conf
+
+from cinder.tests.unit.volume.drivers.netapp.eseries import fakes as \
+ fakes
+from cinder.volume.drivers.netapp import common
+from cinder.volume.drivers.netapp.eseries import client
+from cinder.volume.drivers.netapp.eseries import library
+from cinder.volume.drivers.netapp.eseries import utils
+from cinder.volume.drivers.netapp import options
+import cinder.volume.drivers.netapp.utils as na_utils
+
+
+@ddt.ddt
+class NetAppESeriesDriverTestCase(object):
+ """Test case for NetApp e-series iscsi driver."""
+
+ volume = {'id': '114774fb-e15a-4fae-8ee2-c9723e3645ef', 'size': 1,
+ 'volume_name': 'lun1', 'host': 'hostname@backend#DDP',
+ 'os_type': 'linux', 'provider_location': 'lun1',
+ 'name_id': '114774fb-e15a-4fae-8ee2-c9723e3645ef',
+ 'provider_auth': 'provider a b', 'project_id': 'project',
+ 'display_name': None, 'display_description': 'lun1',
+ 'volume_type_id': None}
+ snapshot = {'id': '17928122-553b-4da9-9737-e5c3dcd97f75',
+ 'volume_id': '114774fb-e15a-4fae-8ee2-c9723e3645ef',
+ 'size': 2, 'volume_name': 'lun1',
+ 'volume_size': 2, 'project_id': 'project',
+ 'display_name': None, 'display_description': 'lun1',
+ 'volume_type_id': None}
+ volume_sec = {'id': 'b6c01641-8955-4917-a5e3-077147478575',
+ 'size': 2, 'volume_name': 'lun1',
+ 'os_type': 'linux', 'provider_location': 'lun1',
+ 'name_id': 'b6c01641-8955-4917-a5e3-077147478575',
+ 'provider_auth': None, 'project_id': 'project',
+ 'display_name': None, 'display_description': 'lun1',
+ 'volume_type_id': None}
+ volume_clone = {'id': 'b4b24b27-c716-4647-b66d-8b93ead770a5', 'size': 3,
+ 'volume_name': 'lun1',
+ 'os_type': 'linux', 'provider_location': 'cl_sm',
+ 'name_id': 'b4b24b27-c716-4647-b66d-8b93ead770a5',
+ 'provider_auth': None,
+ 'project_id': 'project', 'display_name': None,
+ 'display_description': 'lun1',
+ 'volume_type_id': None}
+ volume_clone_large = {'id': 'f6ef5bf5-e24f-4cbb-b4c4-11d631d6e553',
+ 'size': 6, 'volume_name': 'lun1',
+ 'os_type': 'linux', 'provider_location': 'cl_lg',
+ 'name_id': 'f6ef5bf5-e24f-4cbb-b4c4-11d631d6e553',
+ 'provider_auth': None,
+ 'project_id': 'project', 'display_name': None,
+ 'display_description': 'lun1',
+ 'volume_type_id': None}
+ fake_eseries_volume_label = utils.convert_uuid_to_es_fmt(volume['id'])
+ fake_size_gb = volume['size']
+ fake_eseries_pool_label = 'DDP'
+ fake_ref = {'source-name': 'CFDGJSLS'}
+ fake_ret_vol = {'id': 'vol_id', 'label': 'label',
+ 'worldWideName': 'wwn', 'capacity': '2147583648'}
+ PROTOCOL = 'iscsi'
+
+ def setUp(self):
+ super(NetAppESeriesDriverTestCase, self).setUp()
+ self._custom_setup()
+
+ def _custom_setup(self):
+ self.mock_object(na_utils, 'OpenStackInfo')
+
+ configuration = self._set_config(self.create_configuration())
+ self.driver = common.NetAppDriver(configuration=configuration)
+ self.library = self.driver.library
+ self.mock_object(self.library,
+ '_check_mode_get_or_register_storage_system')
+ self.mock_object(self.driver.library, '_check_storage_system')
+ self.driver.do_setup(context='context')
+ self.driver.library._client._endpoint = fakes.FAKE_ENDPOINT_HTTP
+
+ def _set_config(self, configuration):
+ configuration.netapp_storage_family = 'eseries'
+ configuration.netapp_storage_protocol = self.PROTOCOL
+ configuration.netapp_transport_type = 'http'
+ configuration.netapp_server_hostname = '127.0.0.1'
+ configuration.netapp_server_port = None
+ configuration.netapp_webservice_path = '/devmgr/vn'
+ configuration.netapp_controller_ips = '127.0.0.2,127.0.0.3'
+ configuration.netapp_sa_password = 'pass1234'
+ configuration.netapp_login = 'rw'
+ configuration.netapp_password = 'rw'
+ configuration.netapp_storage_pools = 'DDP'
+ configuration.netapp_enable_multiattach = False
+ return configuration
+
+ @staticmethod
+ def create_configuration():
+ configuration = conf.Configuration(None)
+ configuration.append_config_values(options.netapp_basicauth_opts)
+ configuration.append_config_values(options.netapp_eseries_opts)
+ configuration.append_config_values(options.netapp_san_opts)
+ return configuration
+
+ @abc.abstractmethod
+ @mock.patch.object(na_utils, 'validate_instantiation')
+ def test_instantiation(self, mock_validate_instantiation):
+ pass
+
+ def test_embedded_mode(self):
+ self.mock_object(self.driver.library,
+ '_check_mode_get_or_register_storage_system')
+ self.mock_object(client.RestClient, '_init_features')
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_controller_ips = '127.0.0.1,127.0.0.3'
+
+ driver = common.NetAppDriver(configuration=configuration)
+ self.mock_object(client.RestClient, 'list_storage_systems', mock.Mock(
+ return_value=[fakes.STORAGE_SYSTEM]))
+ driver.do_setup(context='context')
+
+ self.assertEqual('1fa6efb5-f07b-4de4-9f0e-52e5f7ff5d1b',
+ driver.library._client.get_system_id())
+
+ def test_check_system_pwd_not_sync(self):
+ def list_system():
+ if getattr(self, 'test_count', None):
+ self.test_count = 1
+ return {'status': 'passwordoutofsync'}
+ return {'status': 'needsAttention'}
+
+ self.library._client.list_storage_system = mock.Mock(wraps=list_system)
+ result = self.library._check_storage_system()
+ self.assertTrue(result)
+
+ def test_create_destroy(self):
+ self.mock_object(client.RestClient, 'delete_volume',
+ mock.Mock(return_value='None'))
+ self.mock_object(self.driver.library, 'create_volume',
+ mock.Mock(return_value=self.volume))
+ self.mock_object(self.library._client, 'list_volume', mock.Mock(
+ return_value=fakes.VOLUME))
+
+ self.driver.create_volume(self.volume)
+ self.driver.delete_volume(self.volume)
+
+ def test_vol_stats(self):
+ self.driver.get_volume_stats(refresh=False)
+
+ def test_get_pool(self):
+ self.mock_object(self.library, '_get_volume',
+ mock.Mock(return_value={
+ 'volumeGroupRef': 'fake_ref'}))
+ self.mock_object(self.library._client, "get_storage_pool",
+ mock.Mock(return_value={'volumeGroupRef': 'fake_ref',
+ 'label': 'ddp1'}))
+
+ pool = self.driver.get_pool({'name_id': 'fake-uuid'})
+
+ self.assertEqual('ddp1', pool)
+
+ def test_get_pool_no_pools(self):
+ self.mock_object(self.library, '_get_volume',
+ mock.Mock(return_value={
+ 'volumeGroupRef': 'fake_ref'}))
+ self.mock_object(self.library._client, "get_storage_pool",
+ mock.Mock(return_value=None))
+
+ pool = self.driver.get_pool({'name_id': 'fake-uuid'})
+
+ self.assertIsNone(pool)
+
+ @mock.patch.object(library.NetAppESeriesLibrary, '_create_volume',
+ mock.Mock())
+ def test_create_volume(self):
+
+ self.driver.create_volume(self.volume)
+
+ self.library._create_volume.assert_called_with(
+ 'DDP', self.fake_eseries_volume_label, self.volume['size'], {})
+
+ def test_create_volume_no_pool_provided_by_scheduler(self):
+ volume = copy.deepcopy(self.volume)
+ volume['host'] = "host@backend" # missing pool
+ self.assertRaises(exception.InvalidHost, self.driver.create_volume,
+ volume)
+
+ @mock.patch.object(client.RestClient, 'list_storage_pools')
+ def test_helper_create_volume_fail(self, fake_list_pools):
+ fake_pool = {}
+ fake_pool['label'] = self.fake_eseries_pool_label
+ fake_pool['volumeGroupRef'] = 'foo'
+ fake_pool['raidLevel'] = 'raidDiskPool'
+ fake_pools = [fake_pool]
+ fake_list_pools.return_value = fake_pools
+ wrong_eseries_pool_label = 'hostname@backend'
+ self.assertRaises(exception.NetAppDriverException,
+ self.library._create_volume,
+ wrong_eseries_pool_label,
+ self.fake_eseries_volume_label,
+ self.fake_size_gb)
+
+ @mock.patch.object(library.LOG, 'info')
+ @mock.patch.object(client.RestClient, 'list_storage_pools')
+ @mock.patch.object(client.RestClient, 'create_volume',
+ mock.MagicMock(return_value='CorrectVolume'))
+ def test_helper_create_volume(self, storage_pools, log_info):
+ fake_pool = {}
+ fake_pool['label'] = self.fake_eseries_pool_label
+ fake_pool['volumeGroupRef'] = 'foo'
+ fake_pool['raidLevel'] = 'raidDiskPool'
+ fake_pools = [fake_pool]
+ storage_pools.return_value = fake_pools
+ storage_vol = self.library._create_volume(
+ self.fake_eseries_pool_label,
+ self.fake_eseries_volume_label,
+ self.fake_size_gb)
+ log_info.assert_called_once_with("Created volume with label %s.",
+ self.fake_eseries_volume_label)
+ self.assertEqual('CorrectVolume', storage_vol)
+
+ @mock.patch.object(client.RestClient, 'list_storage_pools')
+ @mock.patch.object(client.RestClient, 'create_volume',
+ mock.MagicMock(
+ side_effect=exception.NetAppDriverException))
+ @mock.patch.object(library.LOG, 'info', mock.Mock())
+ def test_create_volume_check_exception(self, fake_list_pools):
+ fake_pool = {}
+ fake_pool['label'] = self.fake_eseries_pool_label
+ fake_pool['volumeGroupRef'] = 'foo'
+ fake_pool['raidLevel'] = 'raidDiskPool'
+ fake_pools = [fake_pool]
+ fake_list_pools.return_value = fake_pools
+ self.assertRaises(exception.NetAppDriverException,
+ self.library._create_volume,
+ self.fake_eseries_pool_label,
+ self.fake_eseries_volume_label, self.fake_size_gb)
+
+ def test_portal_for_vol_controller(self):
+ volume = {'id': 'vol_id', 'currentManager': 'ctrl1'}
+ vol_nomatch = {'id': 'vol_id', 'currentManager': 'ctrl3'}
+ portals = [{'controller': 'ctrl2', 'iqn': 'iqn2'},
+ {'controller': 'ctrl1', 'iqn': 'iqn1'}]
+ portal = self.library._get_iscsi_portal_for_vol(volume, portals)
+ self.assertEqual({'controller': 'ctrl1', 'iqn': 'iqn1'}, portal)
+ portal = self.library._get_iscsi_portal_for_vol(vol_nomatch, portals)
+ self.assertEqual({'controller': 'ctrl2', 'iqn': 'iqn2'}, portal)
+
+ def test_portal_for_vol_any_false(self):
+ vol_nomatch = {'id': 'vol_id', 'currentManager': 'ctrl3'}
+ portals = [{'controller': 'ctrl2', 'iqn': 'iqn2'},
+ {'controller': 'ctrl1', 'iqn': 'iqn1'}]
+ self.assertRaises(exception.NetAppDriverException,
+ self.library._get_iscsi_portal_for_vol,
+ vol_nomatch, portals, False)
+
+ def test_setup_error_unsupported_host_type(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_host_type = 'garbage'
+ driver = common.NetAppDriver(configuration=configuration)
+ self.assertRaises(exception.NetAppDriverException,
+ driver.library.check_for_setup_error)
+
+ def test_check_host_type_default(self):
+ configuration = self._set_config(self.create_configuration())
+ driver = common.NetAppDriver(configuration=configuration)
+ driver.library._check_host_type()
+ self.assertEqual('LnxALUA', driver.library.host_type)
+
+ def test_do_setup_all_default(self):
+ configuration = self._set_config(self.create_configuration())
+ driver = common.NetAppDriver(configuration=configuration)
+ driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
+ driver.do_setup(context='context')
+ mock_invoke.assert_called_with(**fakes.FAKE_CLIENT_PARAMS)
+
+ def test_do_setup_http_default_port(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_transport_type = 'http'
+ driver = common.NetAppDriver(configuration=configuration)
+ driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
+ driver.do_setup(context='context')
+ mock_invoke.assert_called_with(**fakes.FAKE_CLIENT_PARAMS)
+
+ def test_do_setup_https_default_port(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_transport_type = 'https'
+ driver = common.NetAppDriver(configuration=configuration)
+ driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
+ driver.do_setup(context='context')
+ FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=8443,
+ scheme='https')
+ mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
+
+ def test_do_setup_http_non_default_port(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_server_port = 81
+ driver = common.NetAppDriver(configuration=configuration)
+ driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
+ driver.do_setup(context='context')
+ FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=81)
+ mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
+
+ def test_do_setup_https_non_default_port(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_transport_type = 'https'
+ configuration.netapp_server_port = 446
+ driver = common.NetAppDriver(configuration=configuration)
+ driver.library._check_mode_get_or_register_storage_system = mock.Mock()
+ mock_invoke = self.mock_object(client, 'RestClient')
+ driver.do_setup(context='context')
+ FAKE_EXPECTED_PARAMS = dict(fakes.FAKE_CLIENT_PARAMS, port=446,
+ scheme='https')
+ mock_invoke.assert_called_with(**FAKE_EXPECTED_PARAMS)
+
+ def test_setup_good_controller_ip(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_controller_ips = '127.0.0.1'
+ driver = common.NetAppDriver(configuration=configuration)
+ driver.library._check_mode_get_or_register_storage_system
+
+ def test_setup_good_controller_ips(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_controller_ips = '127.0.0.2,127.0.0.1'
+ driver = common.NetAppDriver(configuration=configuration)
+ driver.library._check_mode_get_or_register_storage_system
+
+ def test_setup_missing_controller_ip(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_controller_ips = None
+ driver = common.NetAppDriver(configuration=configuration)
+ self.assertRaises(exception.InvalidInput,
+ driver.do_setup, context='context')
+
+ def test_setup_error_invalid_controller_ip(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_controller_ips = '987.65.43.21'
+ driver = common.NetAppDriver(configuration=configuration)
+ self.mock_object(na_utils, 'resolve_hostname',
+ mock.Mock(side_effect=socket.gaierror))
+
+ self.assertRaises(
+ exception.NoValidHost,
+ driver.library._check_mode_get_or_register_storage_system)
+
+ def test_setup_error_invalid_first_controller_ip(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_controller_ips = '987.65.43.21,127.0.0.1'
+ driver = common.NetAppDriver(configuration=configuration)
+ self.mock_object(na_utils, 'resolve_hostname',
+ mock.Mock(side_effect=socket.gaierror))
+
+ self.assertRaises(
+ exception.NoValidHost,
+ driver.library._check_mode_get_or_register_storage_system)
+
+ def test_setup_error_invalid_second_controller_ip(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_controller_ips = '127.0.0.1,987.65.43.21'
+ driver = common.NetAppDriver(configuration=configuration)
+ self.mock_object(na_utils, 'resolve_hostname',
+ mock.Mock(side_effect=socket.gaierror))
+
+ self.assertRaises(
+ exception.NoValidHost,
+ driver.library._check_mode_get_or_register_storage_system)
+
+ def test_setup_error_invalid_both_controller_ips(self):
+ configuration = self._set_config(self.create_configuration())
+ configuration.netapp_controller_ips = '564.124.1231.1,987.65.43.21'
+ driver = common.NetAppDriver(configuration=configuration)
+ self.mock_object(na_utils, 'resolve_hostname',
+ mock.Mock(side_effect=socket.gaierror))
+
+ self.assertRaises(
+ exception.NoValidHost,
+ driver.library._check_mode_get_or_register_storage_system)
+
+ def test_manage_existing_get_size(self):
+ self.library._get_existing_vol_with_manage_ref = mock.Mock(
+ return_value=self.fake_ret_vol)
+ size = self.driver.manage_existing_get_size(self.volume, self.fake_ref)
+ self.assertEqual(3, size)
+ self.library._get_existing_vol_with_manage_ref.assert_called_once_with(
+ self.fake_ref)
+
+ def test_get_exist_vol_source_name_missing(self):
+ self.library._client.list_volume = mock.Mock(
+ side_effect=exception.InvalidInput)
+ self.assertRaises(exception.ManageExistingInvalidReference,
+ self.library._get_existing_vol_with_manage_ref,
+ {'id': '1234'})
+
+ @ddt.data('source-id', 'source-name')
+ def test_get_exist_vol_source_not_found(self, attr_name):
+ def _get_volume(v_id):
+ d = {'id': '1', 'name': 'volume1', 'worldWideName': '0'}
+ if v_id in d:
+ return d[v_id]
+ else:
+ raise exception.VolumeNotFound(message=v_id)
+
+ self.library._client.list_volume = mock.Mock(wraps=_get_volume)
+ self.assertRaises(exception.ManageExistingInvalidReference,
+ self.library._get_existing_vol_with_manage_ref,
+ {attr_name: 'name2'})
+
+ self.library._client.list_volume.assert_called_once_with(
+ 'name2')
+
+ def test_get_exist_vol_with_manage_ref(self):
+ fake_ret_vol = {'id': 'right'}
+ self.library._client.list_volume = mock.Mock(return_value=fake_ret_vol)
+
+ actual_vol = self.library._get_existing_vol_with_manage_ref(
+ {'source-name': 'name2'})
+
+ self.library._client.list_volume.assert_called_once_with('name2')
+ self.assertEqual(fake_ret_vol, actual_vol)
+
+ @mock.patch.object(utils, 'convert_uuid_to_es_fmt')
+ def test_manage_existing_same_label(self, mock_convert_es_fmt):
+ self.library._get_existing_vol_with_manage_ref = mock.Mock(
+ return_value=self.fake_ret_vol)
+ mock_convert_es_fmt.return_value = 'label'
+ self.driver.manage_existing(self.volume, self.fake_ref)
+ self.library._get_existing_vol_with_manage_ref.assert_called_once_with(
+ self.fake_ref)
+ mock_convert_es_fmt.assert_called_once_with(
+ '114774fb-e15a-4fae-8ee2-c9723e3645ef')
+
+ @mock.patch.object(utils, 'convert_uuid_to_es_fmt')
+ def test_manage_existing_new(self, mock_convert_es_fmt):
+ self.library._get_existing_vol_with_manage_ref = mock.Mock(
+ return_value=self.fake_ret_vol)
+ mock_convert_es_fmt.return_value = 'vol_label'
+ self.library._client.update_volume = mock.Mock(
+ return_value={'id': 'update', 'worldWideName': 'wwn'})
+ self.driver.manage_existing(self.volume, self.fake_ref)
+ self.library._get_existing_vol_with_manage_ref.assert_called_once_with(
+ self.fake_ref)
+ mock_convert_es_fmt.assert_called_once_with(
+ '114774fb-e15a-4fae-8ee2-c9723e3645ef')
+ self.library._client.update_volume.assert_called_once_with(
+ 'vol_id', 'vol_label')
+
+ @mock.patch.object(library.LOG, 'info')
+ def test_unmanage(self, log_info):
+ self.library._get_volume = mock.Mock(return_value=self.fake_ret_vol)
+ self.driver.unmanage(self.volume)
+ self.library._get_volume.assert_called_once_with(
+ '114774fb-e15a-4fae-8ee2-c9723e3645ef')
+ self.assertEqual(1, log_info.call_count)
+
+ @mock.patch.object(library.NetAppESeriesLibrary, 'ensure_export',
+ mock.Mock())
+ def test_ensure_export(self):
+ self.driver.ensure_export('context', self.fake_ret_vol)
+ self.assertTrue(self.library.ensure_export.called)
+
+ @mock.patch.object(library.NetAppESeriesLibrary, 'extend_volume',
+ mock.Mock())
+ def test_extend_volume(self):
+ capacity = 10
+ self.driver.extend_volume(self.fake_ret_vol, capacity)
+ self.library.extend_volume.assert_called_with(self.fake_ret_vol,
+ capacity)