 27b683e6a1
			
		
	
	27b683e6a1
	
	
	
		
			
			If they're at the end of a function/method and don't return a value, then it's superfluous Change-Id: Ibd8d43a15ee83cf51c5b7cea0b1cb6ea13d1357d
		
			
				
	
	
		
			928 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			928 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # vim: tabstop=4 shiftwidth=4 softtabstop=4
 | |
| 
 | |
| # Copyright (c) 2012 NetApp, Inc.
 | |
| # 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 volume driver
 | |
| 
 | |
| """
 | |
| 
 | |
| import BaseHTTPServer
 | |
| import httplib
 | |
| import StringIO
 | |
| 
 | |
| from lxml import etree
 | |
| 
 | |
| from nova import log as logging
 | |
| from nova import test
 | |
| from nova.volume import netapp
 | |
| 
 | |
| LOG = logging.getLogger("nova.volume.driver")
 | |
| 
 | |
| 
 | |
| WSDL_HEADER = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | |
| <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
 | |
|     xmlns:na="http://www.netapp.com/management/v1"
 | |
|     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 | |
|     xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="NetAppDfm"
 | |
|     targetNamespace="http://www.netapp.com/management/v1">"""
 | |
| 
 | |
| WSDL_TYPES = """<types>
 | |
| <xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
 | |
|     targetNamespace="http://www.netapp.com/management/v1">
 | |
| <xsd:element name="ApiProxy">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Request" type="na:Request"/>
 | |
|             <xsd:element name="Target" type="xsd:string"/>
 | |
|             <xsd:element minOccurs="0" name="Timeout" type="xsd:integer"/>
 | |
|             <xsd:element minOccurs="0" name="Username" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="ApiProxyResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Response" type="na:Response"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetEditBegin">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="DatasetNameOrId" type="na:ObjNameOrId"/>
 | |
|             <xsd:element minOccurs="0" name="Force" type="xsd:boolean"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetEditBeginResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="EditLockId" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetEditCommit">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="AssumeConfirmation"
 | |
|                 type="xsd:boolean"/>
 | |
|             <xsd:element name="EditLockId" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetEditCommitResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="IsProvisioningFailure"
 | |
|                 type="xsd:boolean"/>
 | |
|             <xsd:element minOccurs="0" name="JobIds" type="na:ArrayOfJobInfo"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetEditRollback">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="EditLockId" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetEditRollbackResult">
 | |
|     <xsd:complexType/>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetListInfoIterEnd">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetListInfoIterEndResult">
 | |
|     <xsd:complexType/>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetListInfoIterNext">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Maximum" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetListInfoIterNextResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Datasets" type="na:ArrayOfDatasetInfo"/>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetListInfoIterStart">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="ObjectNameOrId"
 | |
|                 type="na:ObjNameOrId"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetListInfoIterStartResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetMemberListInfoIterEnd">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetMemberListInfoIterEndResult">
 | |
|     <xsd:complexType/>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetMemberListInfoIterNext">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Maximum" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetMemberListInfoIterNextResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="DatasetMembers"
 | |
|                 type="na:ArrayOfDatasetMemberInfo"/>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetMemberListInfoIterStart">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="DatasetNameOrId" type="na:ObjNameOrId"/>
 | |
|             <xsd:element minOccurs="0" name="IncludeExportsInfo"
 | |
|                 type="xsd:boolean"/>
 | |
|             <xsd:element minOccurs="0" name="IncludeIndirect"
 | |
|                 type="xsd:boolean"/>
 | |
|             <xsd:element minOccurs="0" name="MemberType" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetMemberListInfoIterStartResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetProvisionMember">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="EditLockId" type="xsd:integer"/>
 | |
|             <xsd:element name="ProvisionMemberRequestInfo"
 | |
|                 type="na:ProvisionMemberRequestInfo"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetProvisionMemberResult">
 | |
|     <xsd:complexType/>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetRemoveMember">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="DatasetMemberParameters"
 | |
|                 type="na:ArrayOfDatasetMemberParameter"/>
 | |
|             <xsd:element minOccurs="0" name="Destroy" type="xsd:boolean"/>
 | |
|             <xsd:element name="EditLockId" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DatasetRemoveMemberResult">
 | |
|     <xsd:complexType/>
 | |
| </xsd:element>
 | |
| <xsd:element name="DpJobProgressEventListIterEnd">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DpJobProgressEventListIterEndResult">
 | |
|     <xsd:complexType/>
 | |
| </xsd:element>
 | |
| <xsd:element name="DpJobProgressEventListIterNext">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Maximum" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DpJobProgressEventListIterNextResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="ProgressEvents"
 | |
|                 type="na:ArrayOfDpJobProgressEventInfo"/>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DpJobProgressEventListIterStart">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="JobId" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DpJobProgressEventListIterStartResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DfmAbout">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="IncludeDirectorySizeInfo"
 | |
|                 type="xsd:boolean"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="DfmAboutResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all/>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="HostListInfoIterEnd">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="HostListInfoIterEndResult">
 | |
|     <xsd:complexType/>
 | |
| </xsd:element>
 | |
| <xsd:element name="HostListInfoIterNext">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Maximum" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="HostListInfoIterNextResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Hosts" type="na:ArrayOfHostInfo"/>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="HostListInfoIterStart">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="ObjectNameOrId"
 | |
|                 type="na:ObjNameOrId"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="HostListInfoIterStartResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="LunListInfoIterEnd">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="LunListInfoIterEndResult">
 | |
|     <xsd:complexType/>
 | |
| </xsd:element>
 | |
| <xsd:element name="LunListInfoIterNext">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Maximum" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="LunListInfoIterNextResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Luns" type="na:ArrayOfLunInfo"/>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="LunListInfoIterStart">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="ObjectNameOrId"
 | |
|                 type="na:ObjNameOrId"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="LunListInfoIterStartResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element name="Records" type="xsd:integer"/>
 | |
|             <xsd:element name="Tag" type="xsd:string"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="StorageServiceDatasetProvision">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="AssumeConfirmation"
 | |
|                 type="xsd:boolean"/>
 | |
|             <xsd:element name="DatasetName" type="na:ObjName"/>
 | |
|             <xsd:element name="StorageServiceNameOrId" type="na:ObjNameOrId"/>
 | |
|             <xsd:element minOccurs="0" name="StorageSetDetails"
 | |
|                 type="na:ArrayOfStorageSetInfo"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:element name="StorageServiceDatasetProvisionResult">
 | |
|     <xsd:complexType>
 | |
|         <xsd:all>
 | |
|             <xsd:element minOccurs="0" name="ConformanceAlerts"
 | |
|                 type="na:ArrayOfConformanceAlert"/>
 | |
|             <xsd:element name="DatasetId" type="na:ObjId"/>
 | |
|             <xsd:element minOccurs="0" name="DryRunResults"
 | |
|                 type="na:ArrayOfDryRunResult"/>
 | |
|         </xsd:all>
 | |
|     </xsd:complexType>
 | |
| </xsd:element>
 | |
| <xsd:complexType name="ArrayOfDatasetInfo">
 | |
|     <xsd:sequence>
 | |
|         <xsd:element maxOccurs="unbounded" name="DatasetInfo"
 | |
|             type="na:DatasetInfo"/>
 | |
|     </xsd:sequence>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="ArrayOfDatasetMemberInfo">
 | |
|     <xsd:sequence>
 | |
|         <xsd:element maxOccurs="unbounded" name="DatasetMemberInfo"
 | |
|             type="na:DatasetMemberInfo"/>
 | |
|     </xsd:sequence>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="ArrayOfDatasetMemberParameter">
 | |
|     <xsd:sequence>
 | |
|         <xsd:element maxOccurs="unbounded" name="DatasetMemberParameter"
 | |
|             type="na:DatasetMemberParameter"/>
 | |
|     </xsd:sequence>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="ArrayOfDpJobProgressEventInfo">
 | |
|     <xsd:sequence>
 | |
|         <xsd:element maxOccurs="unbounded" name="DpJobProgressEventInfo"
 | |
|             type="na:DpJobProgressEventInfo"/>
 | |
|     </xsd:sequence>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="ArrayOfHostInfo">
 | |
|     <xsd:sequence>
 | |
|         <xsd:element maxOccurs="unbounded" name="HostInfo" type="na:HostInfo"/>
 | |
|     </xsd:sequence>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="ArrayOfJobInfo">
 | |
|     <xsd:sequence>
 | |
|         <xsd:element maxOccurs="unbounded" name="JobInfo" type="na:JobInfo"/>
 | |
|     </xsd:sequence>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="ArrayOfLunInfo">
 | |
|     <xsd:sequence>
 | |
|         <xsd:element maxOccurs="unbounded" name="LunInfo" type="na:LunInfo"/>
 | |
|     </xsd:sequence>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="ArrayOfStorageSetInfo">
 | |
|     <xsd:sequence>
 | |
|         <xsd:element maxOccurs="unbounded" name="StorageSetInfo"
 | |
|             type="na:StorageSetInfo"/>
 | |
|     </xsd:sequence>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="DatasetExportInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element minOccurs="0" name="DatasetExportProtocol"
 | |
|             type="na:DatasetExportProtocol"/>
 | |
|         <xsd:element minOccurs="0" name="DatasetLunMappingInfo"
 | |
|             type="na:DatasetLunMappingInfo"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:simpleType name="DatasetExportProtocol">
 | |
|     <xsd:restriction base="xsd:string"/>
 | |
| </xsd:simpleType>
 | |
| <xsd:complexType name="DatasetInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="DatasetId" type="na:ObjId"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="DatasetLunMappingInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="IgroupOsType" type="xsd:string"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="DatasetMemberInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="MemberId" type="na:ObjId"/>
 | |
|         <xsd:element name="MemberName" type="na:ObjName"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="DatasetMemberParameter">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="ObjectNameOrId" type="na:ObjNameOrId"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="DpJobProgressEventInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="EventStatus" type="na:ObjStatus"/>
 | |
|         <xsd:element name="EventType" type="xsd:string"/>
 | |
|         <xsd:element minOccurs="0" name="ProgressLunInfo"
 | |
|             type="na:ProgressLunInfo"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:simpleType name="DpPolicyNodeName">
 | |
|     <xsd:restriction base="xsd:string"/>
 | |
| </xsd:simpleType>
 | |
| <xsd:simpleType name="HostId">
 | |
|     <xsd:restriction base="xsd:integer"/>
 | |
| </xsd:simpleType>
 | |
| <xsd:complexType name="HostInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="HostAddress" type="xsd:string"/>
 | |
|         <xsd:element name="HostId" type="na:HostId"/>
 | |
|         <xsd:element name="HostName" type="xsd:string"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="JobInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="JobId" type="xsd:integer"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="LunInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="HostId" type="na:ObjId"/>
 | |
|         <xsd:element name="LunPath" type="na:ObjName"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:simpleType name="ObjId">
 | |
|     <xsd:restriction base="xsd:integer"/>
 | |
| </xsd:simpleType>
 | |
| <xsd:simpleType name="ObjName">
 | |
|     <xsd:restriction base="xsd:string"/>
 | |
| </xsd:simpleType>
 | |
| <xsd:simpleType name="ObjNameOrId">
 | |
|     <xsd:restriction base="xsd:string"/>
 | |
| </xsd:simpleType>
 | |
| <xsd:simpleType name="ObjStatus">
 | |
|     <xsd:restriction base="xsd:string"/>
 | |
| </xsd:simpleType>
 | |
| <xsd:complexType name="ProgressLunInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element name="LunPathId" type="na:ObjId"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="ProvisionMemberRequestInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element minOccurs="0" name="Description" type="xsd:string"/>
 | |
|         <xsd:element minOccurs="0" name="MaximumSnapshotSpace"
 | |
|             type="xsd:integer"/>
 | |
|         <xsd:element name="Name" type="xsd:string"/>
 | |
|         <xsd:element name="Size" type="xsd:integer"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="Request">
 | |
|     <xsd:all>
 | |
|         <xsd:element minOccurs="0" name="Args">
 | |
|             <xsd:complexType>
 | |
|                 <xsd:sequence>
 | |
|                     <xsd:any maxOccurs="unbounded" minOccurs="0"/>
 | |
|                 </xsd:sequence>
 | |
|             </xsd:complexType>
 | |
|         </xsd:element>
 | |
|         <xsd:element name="Name" type="xsd:string">
 | |
|         </xsd:element>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="Response">
 | |
|     <xsd:all>
 | |
|         <xsd:element minOccurs="0" name="Errno" type="xsd:integer"/>
 | |
|         <xsd:element minOccurs="0" name="Reason" type="xsd:string"/>
 | |
|         <xsd:element minOccurs="0" name="Results">
 | |
|             <xsd:complexType>
 | |
|                 <xsd:sequence>
 | |
|                     <xsd:any maxOccurs="unbounded" minOccurs="0"/>
 | |
|                 </xsd:sequence>
 | |
|             </xsd:complexType>
 | |
|         </xsd:element>
 | |
|         <xsd:element name="Status" type="xsd:string"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| <xsd:complexType name="StorageSetInfo">
 | |
|     <xsd:all>
 | |
|         <xsd:element minOccurs="0" name="DatasetExportInfo"
 | |
|             type="na:DatasetExportInfo"/>
 | |
|         <xsd:element minOccurs="0" name="DpNodeName"
 | |
|             type="na:DpPolicyNodeName"/>
 | |
|         <xsd:element minOccurs="0" name="ServerNameOrId"
 | |
|             type="na:ObjNameOrId"/>
 | |
|     </xsd:all>
 | |
| </xsd:complexType>
 | |
| </xsd:schema></types>"""
 | |
| 
 | |
| WSDL_TRAILER = """<service name="DfmService">
 | |
| <port binding="na:DfmBinding" name="DfmPort">
 | |
| <soap:address location="https://HOST_NAME:8488/apis/soap/v1"/>
 | |
| </port></service></definitions>"""
 | |
| 
 | |
| RESPONSE_PREFIX = """<?xml version="1.0" encoding="UTF-8"?>
 | |
| <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
 | |
|     xmlns:na="http://www.netapp.com/management/v1"><env:Header/><env:Body>"""
 | |
| 
 | |
| RESPONSE_SUFFIX = """</env:Body></env:Envelope>"""
 | |
| 
 | |
| APIS = ['ApiProxy', 'DatasetListInfoIterStart', 'DatasetListInfoIterNext',
 | |
|     'DatasetListInfoIterEnd', 'DatasetEditBegin', 'DatasetEditCommit',
 | |
|     'DatasetProvisionMember', 'DatasetRemoveMember', 'DfmAbout',
 | |
|     'DpJobProgressEventListIterStart', 'DpJobProgressEventListIterNext',
 | |
|     'DpJobProgressEventListIterEnd', 'DatasetMemberListInfoIterStart',
 | |
|     'DatasetMemberListInfoIterNext', 'DatasetMemberListInfoIterEnd',
 | |
|     'HostListInfoIterStart', 'HostListInfoIterNext', 'HostListInfoIterEnd',
 | |
|     'LunListInfoIterStart', 'LunListInfoIterNext', 'LunListInfoIterEnd',
 | |
|     'StorageServiceDatasetProvision']
 | |
| 
 | |
| iter_count = 0
 | |
| iter_table = {}
 | |
| 
 | |
| 
 | |
| class FakeDfmServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
 | |
|     """HTTP handler that fakes enough stuff to allow the driver to run"""
 | |
| 
 | |
|     def do_GET(s):
 | |
|         """Respond to a GET request."""
 | |
|         if '/dfm.wsdl' != s.path:
 | |
|             s.send_response(404)
 | |
|             s.end_headers
 | |
|             return
 | |
|         s.send_response(200)
 | |
|         s.send_header("Content-Type", "application/wsdl+xml")
 | |
|         s.end_headers()
 | |
|         out = s.wfile
 | |
|         out.write(WSDL_HEADER)
 | |
|         out.write(WSDL_TYPES)
 | |
|         for api in APIS:
 | |
|             out.write('<message name="%sRequest">' % api)
 | |
|             out.write('<part element="na:%s" name="parameters"/>' % api)
 | |
|             out.write('</message>')
 | |
|             out.write('<message name="%sResponse">' % api)
 | |
|             out.write('<part element="na:%sResult" name="results"/>' % api)
 | |
|             out.write('</message>')
 | |
|         out.write('<portType name="DfmInterface">')
 | |
|         for api in APIS:
 | |
|             out.write('<operation name="%s">' % api)
 | |
|             out.write('<input message="na:%sRequest"/>' % api)
 | |
|             out.write('<output message="na:%sResponse"/>' % api)
 | |
|             out.write('</operation>')
 | |
|         out.write('</portType>')
 | |
|         out.write('<binding name="DfmBinding" type="na:DfmInterface">')
 | |
|         out.write('<soap:binding style="document" ' +
 | |
|             'transport="http://schemas.xmlsoap.org/soap/http"/>')
 | |
|         for api in APIS:
 | |
|             out.write('<operation name="%s">' % api)
 | |
|             out.write('<soap:operation soapAction="urn:%s"/>' % api)
 | |
|             out.write('<input><soap:body use="literal"/></input>')
 | |
|             out.write('<output><soap:body use="literal"/></output>')
 | |
|             out.write('</operation>')
 | |
|         out.write('</binding>')
 | |
|         out.write(WSDL_TRAILER)
 | |
| 
 | |
|     def do_POST(s):
 | |
|         """Respond to a POST request."""
 | |
|         if '/apis/soap/v1' != s.path:
 | |
|             s.send_response(404)
 | |
|             s.end_headers
 | |
|             return
 | |
|         request_xml = s.rfile.read(int(s.headers['Content-Length']))
 | |
|         ntap_ns = 'http://www.netapp.com/management/v1'
 | |
|         nsmap = {'env': 'http://schemas.xmlsoap.org/soap/envelope/',
 | |
|             'na': ntap_ns}
 | |
|         root = etree.fromstring(request_xml)
 | |
| 
 | |
|         body = root.xpath('/env:Envelope/env:Body', namespaces=nsmap)[0]
 | |
|         request = body.getchildren()[0]
 | |
|         tag = request.tag
 | |
|         if not tag.startswith('{' + ntap_ns + '}'):
 | |
|             s.send_response(500)
 | |
|             s.end_headers
 | |
|             return
 | |
|         api = tag[(2 + len(ntap_ns)):]
 | |
|         global iter_count
 | |
|         global iter_table
 | |
|         if 'DatasetListInfoIterStart' == api:
 | |
|             body = """<na:DatasetListInfoIterStartResult>
 | |
|                     <na:Records>1</na:Records>
 | |
|                     <na:Tag>dataset</na:Tag>
 | |
|                 </na:DatasetListInfoIterStartResult>"""
 | |
|         elif 'DatasetListInfoIterNext' == api:
 | |
|             body = """<na:DatasetListInfoIterNextResult>
 | |
|                     <na:Datasets>
 | |
|                         <na:DatasetInfo>
 | |
|                             <na:DatasetId>0</na:DatasetId>
 | |
|                         </na:DatasetInfo>
 | |
|                     </na:Datasets>
 | |
|                     <na:Records>1</na:Records>
 | |
|                 </na:DatasetListInfoIterNextResult>"""
 | |
|         elif 'DatasetListInfoIterEnd' == api:
 | |
|             body = """<na:DatasetListInfoIterEndResult/>"""
 | |
|         elif 'DatasetEditBegin' == api:
 | |
|             body = """<na:DatasetEditBeginResult>
 | |
|                     <na:EditLockId>0</na:EditLockId>
 | |
|                 </na:DatasetEditBeginResult>"""
 | |
|         elif 'DatasetEditCommit' == api:
 | |
|             body = """<na:DatasetEditCommitResult>
 | |
|                     <na:IsProvisioningFailure>false</na:IsProvisioningFailure>
 | |
|                     <na:JobIds>
 | |
|                         <na:JobInfo>
 | |
|                             <na:JobId>0</na:JobId>
 | |
|                         </na:JobInfo>
 | |
|                     </na:JobIds>
 | |
|                 </na:DatasetEditCommitResult>"""
 | |
|         elif 'DatasetProvisionMember' == api:
 | |
|             body = """<na:DatasetProvisionMemberResult/>"""
 | |
|         elif 'DatasetRemoveMember' == api:
 | |
|             body = """<na:DatasetRemoveMemberResult/>"""
 | |
|         elif 'DfmAbout' == api:
 | |
|             body = """<na:DfmAboutResult/>"""
 | |
|         elif 'DpJobProgressEventListIterStart' == api:
 | |
|             iter_name = 'dpjobprogress_%s' % iter_count
 | |
|             iter_count = iter_count + 1
 | |
|             iter_table[iter_name] = 0
 | |
|             body = """<na:DpJobProgressEventListIterStartResult>
 | |
|                     <na:Records>2</na:Records>
 | |
|                     <na:Tag>%s</na:Tag>
 | |
|                 </na:DpJobProgressEventListIterStartResult>""" % iter_name
 | |
|         elif 'DpJobProgressEventListIterNext' == api:
 | |
|             tags = body.xpath('na:DpJobProgressEventListIterNext/na:Tag',
 | |
|                               namespaces=nsmap)
 | |
|             iter_name = tags[0].text
 | |
|             if iter_table[iter_name]:
 | |
|                 body = """<na:DpJobProgressEventListIterNextResult/>"""
 | |
|             else:
 | |
|                 iter_table[iter_name] = 1
 | |
|                 body = """<na:DpJobProgressEventListIterNextResult>
 | |
|                         <na:ProgressEvents>
 | |
|                             <na:DpJobProgressEventInfo>
 | |
|                                 <na:EventStatus>normal</na:EventStatus>
 | |
|                                 <na:EventType>lun-create</na:EventType>
 | |
|                                 <na:ProgressLunInfo>
 | |
|                                     <na:LunPathId>0</na:LunPathId>
 | |
|                                  </na:ProgressLunInfo>
 | |
|                             </na:DpJobProgressEventInfo>
 | |
|                             <na:DpJobProgressEventInfo>
 | |
|                                 <na:EventStatus>normal</na:EventStatus>
 | |
|                                 <na:EventType>job-end</na:EventType>
 | |
|                             </na:DpJobProgressEventInfo>
 | |
|                         </na:ProgressEvents>
 | |
|                         <na:Records>2</na:Records>
 | |
|                     </na:DpJobProgressEventListIterNextResult>"""
 | |
|         elif 'DpJobProgressEventListIterEnd' == api:
 | |
|             body = """<na:DpJobProgressEventListIterEndResult/>"""
 | |
|         elif 'DatasetMemberListInfoIterStart' == api:
 | |
|             body = """<na:DatasetMemberListInfoIterStartResult>
 | |
|                     <na:Records>1</na:Records>
 | |
|                     <na:Tag>dataset-member</na:Tag>
 | |
|                 </na:DatasetMemberListInfoIterStartResult>"""
 | |
|         elif 'DatasetMemberListInfoIterNext' == api:
 | |
|             name = 'filer:/OpenStack_testproj/volume-00000001/volume-00000001'
 | |
|             body = """<na:DatasetMemberListInfoIterNextResult>
 | |
|                     <na:DatasetMembers>
 | |
|                         <na:DatasetMemberInfo>
 | |
|                             <na:MemberId>0</na:MemberId>
 | |
|                             <na:MemberName>%s</na:MemberName>
 | |
|                         </na:DatasetMemberInfo>
 | |
|                     </na:DatasetMembers>
 | |
|                     <na:Records>1</na:Records>
 | |
|                 </na:DatasetMemberListInfoIterNextResult>""" % name
 | |
|         elif 'DatasetMemberListInfoIterEnd' == api:
 | |
|             body = """<na:DatasetMemberListInfoIterEndResult/>"""
 | |
|         elif 'HostListInfoIterStart' == api:
 | |
|             body = """<na:HostListInfoIterStartResult>
 | |
|                     <na:Records>1</na:Records>
 | |
|                     <na:Tag>host</na:Tag>
 | |
|                 </na:HostListInfoIterStartResult>"""
 | |
|         elif 'HostListInfoIterNext' == api:
 | |
|             body = """<na:HostListInfoIterNextResult>
 | |
|                     <na:Hosts>
 | |
|                         <na:HostInfo>
 | |
|                             <na:HostAddress>1.2.3.4</na:HostAddress>
 | |
|                             <na:HostId>0</na:HostId>
 | |
|                             <na:HostName>filer</na:HostName>
 | |
|                         </na:HostInfo>
 | |
|                     </na:Hosts>
 | |
|                     <na:Records>1</na:Records>
 | |
|                 </na:HostListInfoIterNextResult>"""
 | |
|         elif 'HostListInfoIterEnd' == api:
 | |
|             body = """<na:HostListInfoIterEndResult/>"""
 | |
|         elif 'LunListInfoIterStart' == api:
 | |
|             body = """<na:LunListInfoIterStartResult>
 | |
|                     <na:Records>1</na:Records>
 | |
|                     <na:Tag>lun</na:Tag>
 | |
|                 </na:LunListInfoIterStartResult>"""
 | |
|         elif 'LunListInfoIterNext' == api:
 | |
|             path = 'OpenStack_testproj/volume-00000001/volume-00000001'
 | |
|             body = """<na:LunListInfoIterNextResult>
 | |
|                     <na:Luns>
 | |
|                         <na:LunInfo>
 | |
|                             <na:HostId>0</na:HostId>
 | |
|                             <na:LunPath>%s</na:LunPath>
 | |
|                         </na:LunInfo>
 | |
|                     </na:Luns>
 | |
|                     <na:Records>1</na:Records>
 | |
|                 </na:LunListInfoIterNextResult>""" % path
 | |
|         elif 'LunListInfoIterEnd' == api:
 | |
|             body = """<na:LunListInfoIterEndResult/>"""
 | |
|         elif 'ApiProxy' == api:
 | |
|             names = body.xpath('na:ApiProxy/na:Request/na:Name',
 | |
|                                namespaces=nsmap)
 | |
|             proxy = names[0].text
 | |
|             if 'igroup-list-info' == proxy:
 | |
|                 igroup = 'openstack-iqn.1993-08.org.debian:01:23456789'
 | |
|                 initiator = 'iqn.1993-08.org.debian:01:23456789'
 | |
|                 proxy_body = """<initiator-groups>
 | |
|                         <initiator-group-info>
 | |
|                             <initiator-group-name>%s</initiator-group-name>
 | |
|                             <initiator-group-type>iscsi</initiator-group-type>
 | |
|                        <initiator-group-os-type>linux</initiator-group-os-type>
 | |
|                             <initiators>
 | |
|                                 <initiator-info>
 | |
|                                     <initiator-name>%s</initiator-name>
 | |
|                                 </initiator-info>
 | |
|                             </initiators>
 | |
|                         </initiator-group-info>
 | |
|                     </initiator-groups>""" % (igroup, initiator)
 | |
|             elif 'igroup-create' == proxy:
 | |
|                 proxy_body = ''
 | |
|             elif 'igroup-add' == proxy:
 | |
|                 proxy_body = ''
 | |
|             elif 'lun-map-list-info' == proxy:
 | |
|                 proxy_body = '<initiator-groups/>'
 | |
|             elif 'lun-map' == proxy:
 | |
|                 proxy_body = '<lun-id-assigned>0</lun-id-assigned>'
 | |
|             elif 'lun-unmap' == proxy:
 | |
|                 proxy_body = ''
 | |
|             elif 'iscsi-portal-list-info' == proxy:
 | |
|                 proxy_body = """<iscsi-portal-list-entries>
 | |
|                         <iscsi-portal-list-entry-info>
 | |
|                             <ip-address>1.2.3.4</ip-address>
 | |
|                             <ip-port>3260</ip-port>
 | |
|                             <tpgroup-tag>1000</tpgroup-tag>
 | |
|                         </iscsi-portal-list-entry-info>
 | |
|                     </iscsi-portal-list-entries>"""
 | |
|             elif 'iscsi-node-get-name' == proxy:
 | |
|                 target = 'iqn.1992-08.com.netapp:sn.111111111'
 | |
|                 proxy_body = '<node-name>%s</node-name>' % target
 | |
|             else:
 | |
|                 # Unknown proxy API
 | |
|                 s.send_response(500)
 | |
|                 s.end_headers
 | |
|                 return
 | |
|             api = api + ':' + proxy
 | |
|             proxy_header = '<na:ApiProxyResult><na:Response><na:Results>'
 | |
|             proxy_trailer = """</na:Results><na:Status>passed</na:Status>
 | |
|                 </na:Response></na:ApiProxyResult>"""
 | |
|             body = proxy_header + proxy_body + proxy_trailer
 | |
|         else:
 | |
|             # Unknown API
 | |
|             s.send_response(500)
 | |
|             s.end_headers
 | |
|             return
 | |
|         s.send_response(200)
 | |
|         s.send_header("Content-Type", "text/xml; charset=utf-8")
 | |
|         s.end_headers()
 | |
|         s.wfile.write(RESPONSE_PREFIX)
 | |
|         s.wfile.write(body)
 | |
|         s.wfile.write(RESPONSE_SUFFIX)
 | |
| 
 | |
| 
 | |
| class FakeHttplibSocket(object):
 | |
|     """A fake socket implementation for httplib.HTTPResponse"""
 | |
|     def __init__(self, value):
 | |
|         self._rbuffer = StringIO.StringIO(value)
 | |
|         self._wbuffer = StringIO.StringIO('')
 | |
|         oldclose = self._wbuffer.close
 | |
| 
 | |
|         def newclose():
 | |
|             self.result = self._wbuffer.getvalue()
 | |
|             oldclose()
 | |
|         self._wbuffer.close = newclose
 | |
| 
 | |
|     def makefile(self, mode, _other):
 | |
|         """Returns the socket's internal buffer"""
 | |
|         if mode == 'r' or mode == 'rb':
 | |
|             return self._rbuffer
 | |
|         if mode == 'w' or mode == 'wb':
 | |
|             return self._wbuffer
 | |
| 
 | |
| 
 | |
| class FakeHTTPConnection(object):
 | |
|     """A fake httplib.HTTPConnection for netapp tests
 | |
| 
 | |
|     Requests made via this connection actually get translated and routed into
 | |
|     the fake Dfm handler above, we then turn the response into
 | |
|     the httplib.HTTPResponse that the caller expects.
 | |
|     """
 | |
|     def __init__(self, host, timeout=None):
 | |
|         self.host = host
 | |
| 
 | |
|     def request(self, method, path, data=None, headers=None):
 | |
|         if not headers:
 | |
|             headers = {}
 | |
|         req_str = '%s %s HTTP/1.1\r\n' % (method, path)
 | |
|         for key, value in headers.iteritems():
 | |
|             req_str += "%s: %s\r\n" % (key, value)
 | |
|         if data:
 | |
|             req_str += '\r\n%s' % data
 | |
| 
 | |
|         # NOTE(vish): normally the http transport normailizes from unicode
 | |
|         sock = FakeHttplibSocket(req_str.decode("latin-1").encode("utf-8"))
 | |
|         # NOTE(vish): stop the server from trying to look up address from
 | |
|         #             the fake socket
 | |
|         FakeDfmServerHandler.address_string = lambda x: '127.0.0.1'
 | |
|         self.app = FakeDfmServerHandler(sock, '127.0.0.1:8088', None)
 | |
| 
 | |
|         self.sock = FakeHttplibSocket(sock.result)
 | |
|         self.http_response = httplib.HTTPResponse(self.sock)
 | |
| 
 | |
|     def set_debuglevel(self, level):
 | |
|         pass
 | |
| 
 | |
|     def getresponse(self):
 | |
|         self.http_response.begin()
 | |
|         return self.http_response
 | |
| 
 | |
|     def getresponsebody(self):
 | |
|         return self.sock.result
 | |
| 
 | |
| 
 | |
| class NetAppDriverTestCase(test.TestCase):
 | |
|     """Test case for NetAppISCSIDriver"""
 | |
|     STORAGE_SERVICE = 'Thin Provisioned Space for VMFS Datastores'
 | |
|     PROJECT_ID = 'testproj'
 | |
|     VOLUME_NAME = 'volume-00000001'
 | |
|     VOLUME_SIZE = 2147483648L  # 2 GB
 | |
|     INITIATOR = 'iqn.1993-08.org.debian:01:23456789'
 | |
| 
 | |
|     def setUp(self):
 | |
|         super(NetAppDriverTestCase, self).setUp()
 | |
|         driver = netapp.NetAppISCSIDriver()
 | |
|         self.stubs.Set(httplib, 'HTTPConnection', FakeHTTPConnection)
 | |
|         driver._create_client('http://localhost:8088/dfm.wsdl',
 | |
|                               'root', 'password', 'localhost', 8088)
 | |
|         driver._set_storage_service(self.STORAGE_SERVICE)
 | |
|         self.driver = driver
 | |
| 
 | |
|     def test_connect(self):
 | |
|         self.driver.check_for_setup_error()
 | |
| 
 | |
|     def test_create_destroy(self):
 | |
|         self.driver._provision(self.VOLUME_NAME, None, self.PROJECT_ID,
 | |
|                                self.VOLUME_SIZE)
 | |
|         self.driver._remove_destroy(self.VOLUME_NAME, self.PROJECT_ID)
 | |
| 
 | |
|     def test_map_unmap(self):
 | |
|         self.driver._provision(self.VOLUME_NAME, None, self.PROJECT_ID,
 | |
|                                self.VOLUME_SIZE)
 | |
|         volume = {'name': self.VOLUME_NAME, 'project_id': self.PROJECT_ID,
 | |
|             'id': 0, 'provider_auth': None}
 | |
|         updates = self.driver._get_export(volume)
 | |
|         self.assertTrue(updates['provider_location'])
 | |
|         volume['provider_location'] = updates['provider_location']
 | |
|         connector = {'initiator': self.INITIATOR}
 | |
|         connection_info = self.driver.initialize_connection(volume, connector)
 | |
|         self.assertEqual(connection_info['driver_volume_type'], 'iscsi')
 | |
|         properties = connection_info['data']
 | |
|         self.driver.terminate_connection(volume, connector)
 | |
|         self.driver._remove_destroy(self.VOLUME_NAME, self.PROJECT_ID)
 |