1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright (c) 2012 NetApp, Inc.
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7
# not use this file except in compliance with the License. You may obtain
8
# a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
# License for the specific language governing permissions and limitations
18
Tests for NetApp volume driver
26
from lxml import etree
28
from nova import log as logging
30
from nova.volume.netapp import NetAppISCSIDriver
32
LOG = logging.getLogger("nova.volume.driver")
35
WSDL_HEADER = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
36
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
37
xmlns:na="http://www.netapp.com/management/v1"
38
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
39
xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="NetAppDfm"
40
targetNamespace="http://www.netapp.com/management/v1">"""
42
WSDL_TYPES = """<types>
43
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
44
targetNamespace="http://www.netapp.com/management/v1">
45
<xsd:element name="ApiProxy">
48
<xsd:element name="Request" type="na:Request"/>
49
<xsd:element name="Target" type="xsd:string"/>
50
<xsd:element minOccurs="0" name="Timeout" type="xsd:integer"/>
51
<xsd:element minOccurs="0" name="Username" type="xsd:string"/>
55
<xsd:element name="ApiProxyResult">
58
<xsd:element name="Response" type="na:Response"/>
62
<xsd:element name="DatasetEditBegin">
65
<xsd:element name="DatasetNameOrId" type="na:ObjNameOrId"/>
66
<xsd:element minOccurs="0" name="Force" type="xsd:boolean"/>
70
<xsd:element name="DatasetEditBeginResult">
73
<xsd:element name="EditLockId" type="xsd:integer"/>
77
<xsd:element name="DatasetEditCommit">
80
<xsd:element minOccurs="0" name="AssumeConfirmation"
82
<xsd:element name="EditLockId" type="xsd:integer"/>
86
<xsd:element name="DatasetEditCommitResult">
89
<xsd:element minOccurs="0" name="IsProvisioningFailure"
91
<xsd:element minOccurs="0" name="JobIds" type="na:ArrayOfJobInfo"/>
95
<xsd:element name="DatasetEditRollback">
98
<xsd:element name="EditLockId" type="xsd:integer"/>
102
<xsd:element name="DatasetEditRollbackResult">
105
<xsd:element name="DatasetListInfoIterEnd">
108
<xsd:element name="Tag" type="xsd:string"/>
112
<xsd:element name="DatasetListInfoIterEndResult">
115
<xsd:element name="DatasetListInfoIterNext">
118
<xsd:element name="Maximum" type="xsd:integer"/>
119
<xsd:element name="Tag" type="xsd:string"/>
123
<xsd:element name="DatasetListInfoIterNextResult">
126
<xsd:element name="Datasets" type="na:ArrayOfDatasetInfo"/>
127
<xsd:element name="Records" type="xsd:integer"/>
131
<xsd:element name="DatasetListInfoIterStart">
134
<xsd:element minOccurs="0" name="ObjectNameOrId"
135
type="na:ObjNameOrId"/>
139
<xsd:element name="DatasetListInfoIterStartResult">
142
<xsd:element name="Records" type="xsd:integer"/>
143
<xsd:element name="Tag" type="xsd:string"/>
147
<xsd:element name="DatasetMemberListInfoIterEnd">
150
<xsd:element name="Tag" type="xsd:string"/>
154
<xsd:element name="DatasetMemberListInfoIterEndResult">
157
<xsd:element name="DatasetMemberListInfoIterNext">
160
<xsd:element name="Maximum" type="xsd:integer"/>
161
<xsd:element name="Tag" type="xsd:string"/>
165
<xsd:element name="DatasetMemberListInfoIterNextResult">
168
<xsd:element name="DatasetMembers"
169
type="na:ArrayOfDatasetMemberInfo"/>
170
<xsd:element name="Records" type="xsd:integer"/>
174
<xsd:element name="DatasetMemberListInfoIterStart">
177
<xsd:element name="DatasetNameOrId" type="na:ObjNameOrId"/>
178
<xsd:element minOccurs="0" name="IncludeExportsInfo"
180
<xsd:element minOccurs="0" name="IncludeIndirect"
182
<xsd:element minOccurs="0" name="MemberType" type="xsd:string"/>
186
<xsd:element name="DatasetMemberListInfoIterStartResult">
189
<xsd:element name="Records" type="xsd:integer"/>
190
<xsd:element name="Tag" type="xsd:string"/>
194
<xsd:element name="DatasetProvisionMember">
197
<xsd:element name="EditLockId" type="xsd:integer"/>
198
<xsd:element name="ProvisionMemberRequestInfo"
199
type="na:ProvisionMemberRequestInfo"/>
203
<xsd:element name="DatasetProvisionMemberResult">
206
<xsd:element name="DatasetRemoveMember">
209
<xsd:element name="DatasetMemberParameters"
210
type="na:ArrayOfDatasetMemberParameter"/>
211
<xsd:element minOccurs="0" name="Destroy" type="xsd:boolean"/>
212
<xsd:element name="EditLockId" type="xsd:integer"/>
216
<xsd:element name="DatasetRemoveMemberResult">
219
<xsd:element name="DpJobProgressEventListIterEnd">
222
<xsd:element name="Tag" type="xsd:string"/>
226
<xsd:element name="DpJobProgressEventListIterEndResult">
229
<xsd:element name="DpJobProgressEventListIterNext">
232
<xsd:element name="Maximum" type="xsd:integer"/>
233
<xsd:element name="Tag" type="xsd:string"/>
237
<xsd:element name="DpJobProgressEventListIterNextResult">
240
<xsd:element name="ProgressEvents"
241
type="na:ArrayOfDpJobProgressEventInfo"/>
242
<xsd:element name="Records" type="xsd:integer"/>
246
<xsd:element name="DpJobProgressEventListIterStart">
249
<xsd:element minOccurs="0" name="JobId" type="xsd:integer"/>
253
<xsd:element name="DpJobProgressEventListIterStartResult">
256
<xsd:element name="Records" type="xsd:integer"/>
257
<xsd:element name="Tag" type="xsd:string"/>
261
<xsd:element name="DfmAbout">
264
<xsd:element minOccurs="0" name="IncludeDirectorySizeInfo"
269
<xsd:element name="DfmAboutResult">
274
<xsd:element name="HostListInfoIterEnd">
277
<xsd:element name="Tag" type="xsd:string"/>
281
<xsd:element name="HostListInfoIterEndResult">
284
<xsd:element name="HostListInfoIterNext">
287
<xsd:element name="Maximum" type="xsd:integer"/>
288
<xsd:element name="Tag" type="xsd:string"/>
292
<xsd:element name="HostListInfoIterNextResult">
295
<xsd:element name="Hosts" type="na:ArrayOfHostInfo"/>
296
<xsd:element name="Records" type="xsd:integer"/>
300
<xsd:element name="HostListInfoIterStart">
303
<xsd:element minOccurs="0" name="ObjectNameOrId"
304
type="na:ObjNameOrId"/>
308
<xsd:element name="HostListInfoIterStartResult">
311
<xsd:element name="Records" type="xsd:integer"/>
312
<xsd:element name="Tag" type="xsd:string"/>
316
<xsd:element name="LunListInfoIterEnd">
319
<xsd:element name="Tag" type="xsd:string"/>
323
<xsd:element name="LunListInfoIterEndResult">
326
<xsd:element name="LunListInfoIterNext">
329
<xsd:element name="Maximum" type="xsd:integer"/>
330
<xsd:element name="Tag" type="xsd:string"/>
334
<xsd:element name="LunListInfoIterNextResult">
337
<xsd:element name="Luns" type="na:ArrayOfLunInfo"/>
338
<xsd:element name="Records" type="xsd:integer"/>
342
<xsd:element name="LunListInfoIterStart">
345
<xsd:element minOccurs="0" name="ObjectNameOrId"
346
type="na:ObjNameOrId"/>
350
<xsd:element name="LunListInfoIterStartResult">
353
<xsd:element name="Records" type="xsd:integer"/>
354
<xsd:element name="Tag" type="xsd:string"/>
358
<xsd:element name="StorageServiceDatasetProvision">
361
<xsd:element minOccurs="0" name="AssumeConfirmation"
363
<xsd:element name="DatasetName" type="na:ObjName"/>
364
<xsd:element name="StorageServiceNameOrId" type="na:ObjNameOrId"/>
365
<xsd:element minOccurs="0" name="StorageSetDetails"
366
type="na:ArrayOfStorageSetInfo"/>
370
<xsd:element name="StorageServiceDatasetProvisionResult">
373
<xsd:element minOccurs="0" name="ConformanceAlerts"
374
type="na:ArrayOfConformanceAlert"/>
375
<xsd:element name="DatasetId" type="na:ObjId"/>
376
<xsd:element minOccurs="0" name="DryRunResults"
377
type="na:ArrayOfDryRunResult"/>
381
<xsd:complexType name="ArrayOfDatasetInfo">
383
<xsd:element maxOccurs="unbounded" name="DatasetInfo"
384
type="na:DatasetInfo"/>
387
<xsd:complexType name="ArrayOfDatasetMemberInfo">
389
<xsd:element maxOccurs="unbounded" name="DatasetMemberInfo"
390
type="na:DatasetMemberInfo"/>
393
<xsd:complexType name="ArrayOfDatasetMemberParameter">
395
<xsd:element maxOccurs="unbounded" name="DatasetMemberParameter"
396
type="na:DatasetMemberParameter"/>
399
<xsd:complexType name="ArrayOfDpJobProgressEventInfo">
401
<xsd:element maxOccurs="unbounded" name="DpJobProgressEventInfo"
402
type="na:DpJobProgressEventInfo"/>
405
<xsd:complexType name="ArrayOfHostInfo">
407
<xsd:element maxOccurs="unbounded" name="HostInfo" type="na:HostInfo"/>
410
<xsd:complexType name="ArrayOfJobInfo">
412
<xsd:element maxOccurs="unbounded" name="JobInfo" type="na:JobInfo"/>
415
<xsd:complexType name="ArrayOfLunInfo">
417
<xsd:element maxOccurs="unbounded" name="LunInfo" type="na:LunInfo"/>
420
<xsd:complexType name="ArrayOfStorageSetInfo">
422
<xsd:element maxOccurs="unbounded" name="StorageSetInfo"
423
type="na:StorageSetInfo"/>
426
<xsd:complexType name="DatasetExportInfo">
428
<xsd:element minOccurs="0" name="DatasetExportProtocol"
429
type="na:DatasetExportProtocol"/>
430
<xsd:element minOccurs="0" name="DatasetLunMappingInfo"
431
type="na:DatasetLunMappingInfo"/>
434
<xsd:simpleType name="DatasetExportProtocol">
435
<xsd:restriction base="xsd:string"/>
437
<xsd:complexType name="DatasetInfo">
439
<xsd:element name="DatasetId" type="na:ObjId"/>
442
<xsd:complexType name="DatasetLunMappingInfo">
444
<xsd:element name="IgroupOsType" type="xsd:string"/>
447
<xsd:complexType name="DatasetMemberInfo">
449
<xsd:element name="MemberId" type="na:ObjId"/>
450
<xsd:element name="MemberName" type="na:ObjName"/>
453
<xsd:complexType name="DatasetMemberParameter">
455
<xsd:element name="ObjectNameOrId" type="na:ObjNameOrId"/>
458
<xsd:complexType name="DpJobProgressEventInfo">
460
<xsd:element name="EventStatus" type="na:ObjStatus"/>
461
<xsd:element name="EventType" type="xsd:string"/>
462
<xsd:element minOccurs="0" name="ProgressLunInfo"
463
type="na:ProgressLunInfo"/>
466
<xsd:simpleType name="DpPolicyNodeName">
467
<xsd:restriction base="xsd:string"/>
469
<xsd:simpleType name="HostId">
470
<xsd:restriction base="xsd:integer"/>
472
<xsd:complexType name="HostInfo">
474
<xsd:element name="HostAddress" type="xsd:string"/>
475
<xsd:element name="HostId" type="na:HostId"/>
476
<xsd:element name="HostName" type="xsd:string"/>
479
<xsd:complexType name="JobInfo">
481
<xsd:element name="JobId" type="xsd:integer"/>
484
<xsd:complexType name="LunInfo">
486
<xsd:element name="HostId" type="na:ObjId"/>
487
<xsd:element name="LunPath" type="na:ObjName"/>
490
<xsd:simpleType name="ObjId">
491
<xsd:restriction base="xsd:integer"/>
493
<xsd:simpleType name="ObjName">
494
<xsd:restriction base="xsd:string"/>
496
<xsd:simpleType name="ObjNameOrId">
497
<xsd:restriction base="xsd:string"/>
499
<xsd:simpleType name="ObjStatus">
500
<xsd:restriction base="xsd:string"/>
502
<xsd:complexType name="ProgressLunInfo">
504
<xsd:element name="LunPathId" type="na:ObjId"/>
507
<xsd:complexType name="ProvisionMemberRequestInfo">
509
<xsd:element minOccurs="0" name="Description" type="xsd:string"/>
510
<xsd:element minOccurs="0" name="MaximumSnapshotSpace"
512
<xsd:element name="Name" type="xsd:string"/>
513
<xsd:element name="Size" type="xsd:integer"/>
516
<xsd:complexType name="Request">
518
<xsd:element minOccurs="0" name="Args">
521
<xsd:any maxOccurs="unbounded" minOccurs="0"/>
525
<xsd:element name="Name" type="xsd:string">
529
<xsd:complexType name="Response">
531
<xsd:element minOccurs="0" name="Errno" type="xsd:integer"/>
532
<xsd:element minOccurs="0" name="Reason" type="xsd:string"/>
533
<xsd:element minOccurs="0" name="Results">
536
<xsd:any maxOccurs="unbounded" minOccurs="0"/>
540
<xsd:element name="Status" type="xsd:string"/>
543
<xsd:complexType name="StorageSetInfo">
545
<xsd:element minOccurs="0" name="DatasetExportInfo"
546
type="na:DatasetExportInfo"/>
547
<xsd:element minOccurs="0" name="DpNodeName"
548
type="na:DpPolicyNodeName"/>
549
<xsd:element minOccurs="0" name="ServerNameOrId"
550
type="na:ObjNameOrId"/>
553
</xsd:schema></types>"""
555
WSDL_TRAILER = """<service name="DfmService">
556
<port binding="na:DfmBinding" name="DfmPort">
557
<soap:address location="https://HOST_NAME:8488/apis/soap/v1"/>
558
</port></service></definitions>"""
560
RESPONSE_PREFIX = """<?xml version="1.0" encoding="UTF-8"?>
561
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
562
xmlns:na="http://www.netapp.com/management/v1"><env:Header/><env:Body>"""
564
RESPONSE_SUFFIX = """</env:Body></env:Envelope>"""
566
APIS = ['ApiProxy', 'DatasetListInfoIterStart', 'DatasetListInfoIterNext',
567
'DatasetListInfoIterEnd', 'DatasetEditBegin', 'DatasetEditCommit',
568
'DatasetProvisionMember', 'DatasetRemoveMember', 'DfmAbout',
569
'DpJobProgressEventListIterStart', 'DpJobProgressEventListIterNext',
570
'DpJobProgressEventListIterEnd', 'DatasetMemberListInfoIterStart',
571
'DatasetMemberListInfoIterNext', 'DatasetMemberListInfoIterEnd',
572
'HostListInfoIterStart', 'HostListInfoIterNext', 'HostListInfoIterEnd',
573
'LunListInfoIterStart', 'LunListInfoIterNext', 'LunListInfoIterEnd',
574
'StorageServiceDatasetProvision']
580
class FakeDfmServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
581
"""HTTP handler that fakes enough stuff to allow the driver to run"""
584
"""Respond to a GET request."""
585
if '/dfm.wsdl' != s.path:
590
s.send_header("Content-Type", "application/wsdl+xml")
593
out.write(WSDL_HEADER)
594
out.write(WSDL_TYPES)
596
out.write('<message name="%sRequest">' % api)
597
out.write('<part element="na:%s" name="parameters"/>' % api)
598
out.write('</message>')
599
out.write('<message name="%sResponse">' % api)
600
out.write('<part element="na:%sResult" name="results"/>' % api)
601
out.write('</message>')
602
out.write('<portType name="DfmInterface">')
604
out.write('<operation name="%s">' % api)
605
out.write('<input message="na:%sRequest"/>' % api)
606
out.write('<output message="na:%sResponse"/>' % api)
607
out.write('</operation>')
608
out.write('</portType>')
609
out.write('<binding name="DfmBinding" type="na:DfmInterface">')
610
out.write('<soap:binding style="document" ' +
611
'transport="http://schemas.xmlsoap.org/soap/http"/>')
613
out.write('<operation name="%s">' % api)
614
out.write('<soap:operation soapAction="urn:%s"/>' % api)
615
out.write('<input><soap:body use="literal"/></input>')
616
out.write('<output><soap:body use="literal"/></output>')
617
out.write('</operation>')
618
out.write('</binding>')
619
out.write(WSDL_TRAILER)
623
"""Respond to a POST request."""
624
if '/apis/soap/v1' != s.path:
628
request_xml = s.rfile.read(int(s.headers['Content-Length']))
629
ntap_ns = 'http://www.netapp.com/management/v1'
630
nsmap = {'env': 'http://schemas.xmlsoap.org/soap/envelope/',
632
root = etree.fromstring(request_xml)
634
body = root.xpath('/env:Envelope/env:Body', namespaces=nsmap)[0]
635
request = body.getchildren()[0]
637
if not tag.startswith('{' + ntap_ns + '}'):
641
api = tag[(2 + len(ntap_ns)):]
644
if 'DatasetListInfoIterStart' == api:
645
body = """<na:DatasetListInfoIterStartResult>
646
<na:Records>1</na:Records>
647
<na:Tag>dataset</na:Tag>
648
</na:DatasetListInfoIterStartResult>"""
649
elif 'DatasetListInfoIterNext' == api:
650
body = """<na:DatasetListInfoIterNextResult>
653
<na:DatasetId>0</na:DatasetId>
656
<na:Records>1</na:Records>
657
</na:DatasetListInfoIterNextResult>"""
658
elif 'DatasetListInfoIterEnd' == api:
659
body = """<na:DatasetListInfoIterEndResult/>"""
660
elif 'DatasetEditBegin' == api:
661
body = """<na:DatasetEditBeginResult>
662
<na:EditLockId>0</na:EditLockId>
663
</na:DatasetEditBeginResult>"""
664
elif 'DatasetEditCommit' == api:
665
body = """<na:DatasetEditCommitResult>
666
<na:IsProvisioningFailure>false</na:IsProvisioningFailure>
669
<na:JobId>0</na:JobId>
672
</na:DatasetEditCommitResult>"""
673
elif 'DatasetProvisionMember' == api:
674
body = """<na:DatasetProvisionMemberResult/>"""
675
elif 'DatasetRemoveMember' == api:
676
body = """<na:DatasetRemoveMemberResult/>"""
677
elif 'DfmAbout' == api:
678
body = """<na:DfmAboutResult/>"""
679
elif 'DpJobProgressEventListIterStart' == api:
680
iter_name = 'dpjobprogress_%s' % iter_count
681
iter_count = iter_count + 1
682
iter_table[iter_name] = 0
683
body = """<na:DpJobProgressEventListIterStartResult>
684
<na:Records>2</na:Records>
686
</na:DpJobProgressEventListIterStartResult>""" % iter_name
687
elif 'DpJobProgressEventListIterNext' == api:
688
tags = body.xpath('na:DpJobProgressEventListIterNext/na:Tag',
690
iter_name = tags[0].text
691
if iter_table[iter_name]:
692
body = """<na:DpJobProgressEventListIterNextResult/>"""
694
iter_table[iter_name] = 1
695
body = """<na:DpJobProgressEventListIterNextResult>
697
<na:DpJobProgressEventInfo>
698
<na:EventStatus>normal</na:EventStatus>
699
<na:EventType>lun-create</na:EventType>
701
<na:LunPathId>0</na:LunPathId>
702
</na:ProgressLunInfo>
703
</na:DpJobProgressEventInfo>
704
<na:DpJobProgressEventInfo>
705
<na:EventStatus>normal</na:EventStatus>
706
<na:EventType>job-end</na:EventType>
707
</na:DpJobProgressEventInfo>
709
<na:Records>2</na:Records>
710
</na:DpJobProgressEventListIterNextResult>"""
711
elif 'DpJobProgressEventListIterEnd' == api:
712
body = """<na:DpJobProgressEventListIterEndResult/>"""
713
elif 'DatasetMemberListInfoIterStart' == api:
714
body = """<na:DatasetMemberListInfoIterStartResult>
715
<na:Records>1</na:Records>
716
<na:Tag>dataset-member</na:Tag>
717
</na:DatasetMemberListInfoIterStartResult>"""
718
elif 'DatasetMemberListInfoIterNext' == api:
719
name = 'filer:/OpenStack_testproj/volume-00000001/volume-00000001'
720
body = """<na:DatasetMemberListInfoIterNextResult>
722
<na:DatasetMemberInfo>
723
<na:MemberId>0</na:MemberId>
724
<na:MemberName>%s</na:MemberName>
725
</na:DatasetMemberInfo>
727
<na:Records>1</na:Records>
728
</na:DatasetMemberListInfoIterNextResult>""" % name
729
elif 'DatasetMemberListInfoIterEnd' == api:
730
body = """<na:DatasetMemberListInfoIterEndResult/>"""
731
elif 'HostListInfoIterStart' == api:
732
body = """<na:HostListInfoIterStartResult>
733
<na:Records>1</na:Records>
734
<na:Tag>host</na:Tag>
735
</na:HostListInfoIterStartResult>"""
736
elif 'HostListInfoIterNext' == api:
737
body = """<na:HostListInfoIterNextResult>
740
<na:HostAddress>1.2.3.4</na:HostAddress>
741
<na:HostId>0</na:HostId>
742
<na:HostName>filer</na:HostName>
745
<na:Records>1</na:Records>
746
</na:HostListInfoIterNextResult>"""
747
elif 'HostListInfoIterEnd' == api:
748
body = """<na:HostListInfoIterEndResult/>"""
749
elif 'LunListInfoIterStart' == api:
750
body = """<na:LunListInfoIterStartResult>
751
<na:Records>1</na:Records>
753
</na:LunListInfoIterStartResult>"""
754
elif 'LunListInfoIterNext' == api:
755
path = 'OpenStack_testproj/volume-00000001/volume-00000001'
756
body = """<na:LunListInfoIterNextResult>
759
<na:HostId>0</na:HostId>
760
<na:LunPath>%s</na:LunPath>
763
<na:Records>1</na:Records>
764
</na:LunListInfoIterNextResult>""" % path
765
elif 'LunListInfoIterEnd' == api:
766
body = """<na:LunListInfoIterEndResult/>"""
767
elif 'ApiProxy' == api:
768
names = body.xpath('na:ApiProxy/na:Request/na:Name',
770
proxy = names[0].text
771
if 'igroup-list-info' == proxy:
772
igroup = 'openstack-iqn.1993-08.org.debian:01:23456789'
773
initiator = 'iqn.1993-08.org.debian:01:23456789'
774
proxy_body = """<initiator-groups>
775
<initiator-group-info>
776
<initiator-group-name>%s</initiator-group-name>
777
<initiator-group-type>iscsi</initiator-group-type>
778
<initiator-group-os-type>linux</initiator-group-os-type>
781
<initiator-name>%s</initiator-name>
784
</initiator-group-info>
785
</initiator-groups>""" % (igroup, initiator)
786
elif 'igroup-create' == proxy:
788
elif 'igroup-add' == proxy:
790
elif 'lun-map-list-info' == proxy:
791
proxy_body = '<initiator-groups/>'
792
elif 'lun-map' == proxy:
793
proxy_body = '<lun-id-assigned>0</lun-id-assigned>'
794
elif 'lun-unmap' == proxy:
796
elif 'iscsi-portal-list-info' == proxy:
797
proxy_body = """<iscsi-portal-list-entries>
798
<iscsi-portal-list-entry-info>
799
<ip-address>1.2.3.4</ip-address>
800
<ip-port>3260</ip-port>
801
<tpgroup-tag>1000</tpgroup-tag>
802
</iscsi-portal-list-entry-info>
803
</iscsi-portal-list-entries>"""
804
elif 'iscsi-node-get-name' == proxy:
805
target = 'iqn.1992-08.com.netapp:sn.111111111'
806
proxy_body = '<node-name>%s</node-name>' % target
812
api = api + ':' + proxy
813
proxy_header = '<na:ApiProxyResult><na:Response><na:Results>'
814
proxy_trailer = """</na:Results><na:Status>passed</na:Status>
815
</na:Response></na:ApiProxyResult>"""
816
body = proxy_header + proxy_body + proxy_trailer
823
s.send_header("Content-Type", "text/xml; charset=utf-8")
825
s.wfile.write(RESPONSE_PREFIX)
827
s.wfile.write(RESPONSE_SUFFIX)
831
class FakeHttplibSocket(object):
832
"""A fake socket implementation for httplib.HTTPResponse"""
833
def __init__(self, value):
834
self._rbuffer = StringIO.StringIO(value)
835
self._wbuffer = StringIO.StringIO('')
836
oldclose = self._wbuffer.close
839
self.result = self._wbuffer.getvalue()
841
self._wbuffer.close = newclose
843
def makefile(self, mode, _other):
844
"""Returns the socket's internal buffer"""
845
if mode == 'r' or mode == 'rb':
847
if mode == 'w' or mode == 'wb':
851
class FakeHTTPConnection(object):
852
"""A fake httplib.HTTPConnection for netapp tests
854
Requests made via this connection actually get translated and routed into
855
the fake Dfm handler above, we then turn the response into
856
the httplib.HTTPResponse that the caller expects.
858
def __init__(self, host, timeout=None):
861
def request(self, method, path, data=None, headers=None):
864
req_str = '%s %s HTTP/1.1\r\n' % (method, path)
865
for key, value in headers.iteritems():
866
req_str += "%s: %s\r\n" % (key, value)
868
req_str += '\r\n%s' % data
870
# NOTE(vish): normally the http transport normailizes from unicode
871
sock = FakeHttplibSocket(req_str.decode("latin-1").encode("utf-8"))
872
# NOTE(vish): stop the server from trying to look up address from
874
FakeDfmServerHandler.address_string = lambda x: '127.0.0.1'
875
self.app = FakeDfmServerHandler(sock, '127.0.0.1:8088', None)
877
self.sock = FakeHttplibSocket(sock.result)
878
self.http_response = httplib.HTTPResponse(self.sock)
880
def set_debuglevel(self, level):
883
def getresponse(self):
884
self.http_response.begin()
885
return self.http_response
887
def getresponsebody(self):
888
return self.sock.result
891
class NetAppDriverTestCase(test.TestCase):
892
"""Test case for NetAppISCSIDriver"""
893
STORAGE_SERVICE = 'Thin Provisioned Space for VMFS Datastores'
894
PROJECT_ID = 'testproj'
895
VOLUME_NAME = 'volume-00000001'
896
VOLUME_SIZE = 2147483648L # 2 GB
897
INITIATOR = 'iqn.1993-08.org.debian:01:23456789'
900
super(NetAppDriverTestCase, self).setUp()
901
driver = NetAppISCSIDriver()
902
self.stubs.Set(httplib, 'HTTPConnection', FakeHTTPConnection)
903
driver._create_client('http://localhost:8088/dfm.wsdl',
904
'root', 'password', 'localhost', 8088)
905
driver._set_storage_service(self.STORAGE_SERVICE)
908
def test_connect(self):
909
self.driver.check_for_setup_error()
911
def test_create_destroy(self):
912
self.driver._provision(self.VOLUME_NAME, None, self.PROJECT_ID,
914
self.driver._remove_destroy(self.VOLUME_NAME, self.PROJECT_ID)
916
def test_map_unmap(self):
917
self.driver._provision(self.VOLUME_NAME, None, self.PROJECT_ID,
919
volume = {'name': self.VOLUME_NAME, 'project_id': self.PROJECT_ID,
920
'id': 0, 'provider_auth': None}
921
updates = self.driver._get_export(volume)
922
self.assertTrue(updates['provider_location'])
923
volume['provider_location'] = updates['provider_location']
924
connector = {'initiator': self.INITIATOR}
925
connection_info = self.driver.initialize_connection(volume, connector)
926
self.assertEqual(connection_info['driver_volume_type'], 'iscsi')
927
properties = connection_info['data']
928
self.driver.terminate_connection(volume, connector)
929
self.driver._remove_destroy(self.VOLUME_NAME, self.PROJECT_ID)