1# Copyright (c) 2017 DataCore Software Corp. All Rights Reserved.
2#
3#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4#    not use this file except in compliance with the License. You may obtain
5#    a copy of the License at
6#
7#         http://www.apache.org/licenses/LICENSE-2.0
8#
9#    Unless required by applicable law or agreed to in writing, software
10#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12#    License for the specific language governing permissions and limitations
13#    under the License.
14
15"""Unit tests for classes that are used to invoke DataCore SANsymphony API."""
16
17import mock
18from oslo_utils import units
19import six
20import suds
21from suds.sax import parser
22from suds import wsdl
23
24from cinder import test
25from cinder.volume.drivers.datacore import api
26from cinder.volume.drivers.datacore import exception
27
28
29class FakeWebSocketException(Exception):
30    pass
31
32
33class DataCoreClientTestCase(test.TestCase):
34    """Tests for the DataCore SANsymphony client."""
35
36    def setUp(self):
37        super(DataCoreClientTestCase, self).setUp()
38        self.mock_storage_services = mock.MagicMock()
39        self.mock_executive_service = mock.MagicMock()
40
41        self.mock_suds_client = mock.MagicMock()
42        self.mock_object(
43            api.suds_client, 'Client', return_value=self.mock_suds_client)
44
45        self.mock_channel = mock.MagicMock()
46        mock_websocket = self.mock_object(api, 'websocket')
47        mock_websocket.WebSocketException = FakeWebSocketException
48        mock_websocket.create_connection.return_value = self.mock_channel
49
50        setattr(self.mock_suds_client.service.__getitem__,
51                'side_effect',
52                self._get_service_side_effect)
53
54        self.client = api.DataCoreClient('hostname', 'username', 'password', 1)
55        self.client.API_RETRY_INTERVAL = 0
56
57    def _get_service_side_effect(self, service_name):
58        self.assertIn(service_name,
59                      [
60                          api.DataCoreClient.STORAGE_SERVICES_BINDING,
61                          api.DataCoreClient.EXECUTIVE_SERVICE_BINDING
62                      ])
63
64        if service_name is api.DataCoreClient.STORAGE_SERVICES_BINDING:
65            return self.mock_storage_services
66        else:
67            return self.mock_executive_service
68
69    def _assert_storage_services_method_called(self, method_name):
70        return self.mock_storage_services.__getitem__.assert_called_with(
71            method_name)
72
73    @property
74    def mock_storage_service_context(self):
75        return self.mock_storage_services.__getitem__()()
76
77    @property
78    def mock_executive_service_context(self):
79        return self.mock_executive_service.__getitem__()()
80
81    def test_process_request_failed(self):
82        def fail_with_socket_error():
83            raise FakeWebSocketException()
84
85        def fail_with_web_fault(message):
86            fault = mock.Mock()
87            fault.faultstring = "General error."
88            document = mock.Mock()
89            raise suds.WebFault(fault, document)
90
91        self.mock_channel.recv.side_effect = fail_with_socket_error
92        self.assertRaises(exception.DataCoreConnectionException,
93                          self.client.get_server_groups)
94        self.mock_channel.recv.side_effect = None
95
96        (self.mock_storage_service_context.process_reply
97         .side_effect) = fail_with_web_fault
98        self.assertRaises(exception.DataCoreFaultException,
99                          self.client.get_server_groups)
100
101    def test_channel_closing_failed(self):
102        def fail_with_socket_error():
103            raise FakeWebSocketException()
104
105        def fail_with_web_fault(message):
106            fault = mock.Mock()
107            fault.faultstring = "General error."
108            document = mock.Mock()
109            raise suds.WebFault(fault, document)
110
111        self.mock_channel.close.side_effect = fail_with_socket_error
112        (self.mock_storage_service_context.process_reply
113         .side_effect) = fail_with_web_fault
114        self.assertRaises(exception.DataCoreFaultException,
115                          self.client.get_server_groups)
116
117    def test_update_api_endpoints(self):
118        def fail_with_socket_error():
119            try:
120                raise FakeWebSocketException()
121            finally:
122                self.mock_channel.recv.side_effect = None
123
124        self.mock_channel.recv.side_effect = fail_with_socket_error
125
126        mock_executive_endpoints = [{
127            'network_address': '127.0.0.1:3794',
128            'http_endpoint': 'http://127.0.0.1:3794/',
129            'ws_endpoint': 'ws://127.0.0.1:3794/',
130        }]
131        self.mock_object(self.client,
132                         '_executive_service_endpoints',
133                         mock_executive_endpoints)
134
135        mock_storage_endpoint = {
136            'network_address': '127.0.0.1:3794',
137            'http_endpoint': 'http://127.0.0.1:3794/',
138            'ws_endpoint': 'ws://127.0.0.1:3794/',
139        }
140        self.mock_object(self.client,
141                         '_storage_services_endpoint',
142                         mock_storage_endpoint)
143
144        node = mock.Mock()
145        node.HostAddress = '127.0.0.1:3794'
146        reply = mock.MagicMock()
147        reply.RegionNodeData = [node]
148        self.mock_storage_service_context.process_reply.return_value = reply
149
150        result = self.client.get_server_groups()
151        self.assertIsNotNone(result)
152
153    def test_update_api_endpoints_failed(self):
154        def fail_with_socket_error():
155            try:
156                raise FakeWebSocketException()
157            finally:
158                self.mock_channel.recv.side_effect = None
159
160        self.mock_channel.recv.side_effect = fail_with_socket_error
161
162        mock_executive_endpoints = [{
163            'network_address': '127.0.0.1:3794',
164            'http_endpoint': 'http://127.0.0.1:3794/',
165            'ws_endpoint': 'ws://127.0.0.1:3794/',
166        }]
167        self.mock_object(self.client,
168                         '_executive_service_endpoints',
169                         mock_executive_endpoints)
170
171        reply = mock.MagicMock()
172        reply.RegionNodeData = []
173        self.mock_storage_service_context.process_reply.return_value = reply
174
175        self.mock_executive_service_context.process_reply.return_value = None
176
177        result = self.client.get_server_groups()
178        self.assertIsNotNone(result)
179
180    def test_get_server_groups(self):
181        self.client.get_server_groups()
182        self._assert_storage_services_method_called('GetServerGroups')
183
184    def test_get_servers(self):
185        self.client.get_servers()
186        self._assert_storage_services_method_called('GetServers')
187
188    def test_get_disk_pools(self):
189        self.client.get_disk_pools()
190        self._assert_storage_services_method_called('GetDiskPools')
191
192    def test_get_logical_disks(self):
193        self.client.get_logical_disks()
194        self._assert_storage_services_method_called('GetLogicalDisks')
195
196    def test_create_pool_logical_disk(self):
197        pool_id = 'pool_id'
198        pool_volume_type = 'Striped'
199        size = 1 * units.Gi
200        min_quota = 1
201        max_quota = 1 * units.Gi
202        self.client.create_pool_logical_disk(
203            pool_id, pool_volume_type, size, min_quota, max_quota)
204        self._assert_storage_services_method_called('CreatePoolLogicalDisk')
205
206    def test_delete_logical_disk(self):
207        logical_disk_id = 'disk_id'
208        self.client.delete_logical_disk(logical_disk_id)
209        self._assert_storage_services_method_called('DeleteLogicalDisk')
210
211    def test_get_logical_disk_chunk_allocation_map(self):
212        logical_disk_id = 'disk_id'
213        self.client.get_logical_disk_chunk_allocation_map(logical_disk_id)
214        self._assert_storage_services_method_called(
215            'GetLogicalDiskChunkAllocationMap')
216
217    def test_get_next_virtual_disk_alias(self):
218        base_alias = 'volume'
219        self.client.get_next_virtual_disk_alias(base_alias)
220        self._assert_storage_services_method_called('GetNextVirtualDiskAlias')
221
222    def test_get_virtual_disks(self):
223        self.client.get_virtual_disks()
224        self._assert_storage_services_method_called('GetVirtualDisks')
225
226    def test_build_virtual_disk_data(self):
227        disk_alias = 'alias'
228        disk_type = 'Mirrored'
229        size = 1 * units.Gi
230        description = 'description'
231        storage_profile_id = 'storage_profile_id'
232
233        vd_data = self.client.build_virtual_disk_data(
234            disk_alias, disk_type, size, description, storage_profile_id)
235
236        self.assertEqual(disk_alias, vd_data.Alias)
237        self.assertEqual(size, vd_data.Size.Value)
238        self.assertEqual(description, vd_data.Description)
239        self.assertEqual(storage_profile_id, vd_data.StorageProfileId)
240        self.assertTrue(hasattr(vd_data, 'Type'))
241        self.assertTrue(hasattr(vd_data, 'SubType'))
242        self.assertTrue(hasattr(vd_data, 'DiskStatus'))
243        self.assertTrue(hasattr(vd_data, 'RecoveryPriority'))
244
245    def test_create_virtual_disk_ex2(self):
246        disk_alias = 'alias'
247        disk_type = 'Mirrored'
248        size = 1 * units.Gi
249        description = 'description'
250        storage_profile_id = 'storage_profile_id'
251        first_disk_id = 'disk_id'
252        second_disk_id = 'disk_id'
253        add_redundancy = True
254        vd_data = self.client.build_virtual_disk_data(
255            disk_alias, disk_type, size, description, storage_profile_id)
256        self.client.create_virtual_disk_ex2(
257            vd_data, first_disk_id, second_disk_id, add_redundancy)
258        self._assert_storage_services_method_called('CreateVirtualDiskEx2')
259
260    def test_set_virtual_disk_size(self):
261        disk_id = 'disk_id'
262        size = 1 * units.Gi
263        self.client.set_virtual_disk_size(disk_id, size)
264        self._assert_storage_services_method_called('SetVirtualDiskSize')
265
266    def test_delete_virtual_disk(self):
267        virtual_disk_id = 'disk_id'
268        delete_logical_disks = True
269        self.client.delete_virtual_disk(virtual_disk_id, delete_logical_disks)
270        self._assert_storage_services_method_called('DeleteVirtualDisk')
271
272    def test_serve_virtual_disks_to_host(self):
273        host_id = 'host_id'
274        disks = ['disk_id']
275        self.client.serve_virtual_disks_to_host(host_id, disks)
276        self._assert_storage_services_method_called('ServeVirtualDisksToHost')
277
278    def test_unserve_virtual_disks_from_host(self):
279        host_id = 'host_id'
280        disks = ['disk_id']
281        self.client.unserve_virtual_disks_from_host(host_id, disks)
282        self._assert_storage_services_method_called(
283            'UnserveVirtualDisksFromHost')
284
285    def test_unserve_virtual_disks_from_port(self):
286        port_id = 'port_id'
287        disks = ['disk_id']
288        self.client.unserve_virtual_disks_from_port(port_id, disks)
289        self._assert_storage_services_method_called(
290            'UnserveVirtualDisksFromPort')
291
292    def test_bind_logical_disk(self):
293        disk_id = 'disk_id'
294        logical_disk_id = 'disk_id'
295        role = 'Second'
296        create_mirror_mappings = True
297        create_client_mappings = False
298        add_redundancy = True
299        self.client.bind_logical_disk(
300            disk_id, logical_disk_id, role, create_mirror_mappings,
301            create_client_mappings, add_redundancy)
302        self._assert_storage_services_method_called(
303            'BindLogicalDisk')
304
305    def test_get_snapshots(self):
306        self.client.get_snapshots()
307        self._assert_storage_services_method_called('GetSnapshots')
308
309    def test_create_snapshot(self):
310        disk_id = 'disk_id'
311        name = 'name'
312        description = 'description'
313        pool_id = 'pool_id'
314        snapshot_type = 'Full'
315        duplicate_disk_id = False
316        storage_profile_id = 'profile_id'
317        self.client.create_snapshot(
318            disk_id, name, description, pool_id, snapshot_type,
319            duplicate_disk_id, storage_profile_id)
320        self._assert_storage_services_method_called('CreateSnapshot')
321
322    def test_delete_snapshot(self):
323        snapshot_id = "snapshot_id"
324        self.client.delete_snapshot(snapshot_id)
325        self._assert_storage_services_method_called('DeleteSnapshot')
326
327    def test_get_storage_profiles(self):
328        self.client.get_storage_profiles()
329        self._assert_storage_services_method_called('GetStorageProfiles')
330
331    def test_designate_map_store(self):
332        pool_id = 'pool_id'
333        self.client.designate_map_store(pool_id)
334        self._assert_storage_services_method_called('DesignateMapStore')
335
336    def test_get_performance_by_type(self):
337        types = ['DiskPoolPerformance']
338        self.client.get_performance_by_type(types)
339        self._assert_storage_services_method_called('GetPerformanceByType')
340
341    def test_get_ports(self):
342        self.client.get_ports()
343        self._assert_storage_services_method_called('GetPorts')
344
345    def test_build_scsi_port_data(self):
346        host_id = 'host_id'
347        port_name = 'port_name'
348        port_mode = 'Initiator'
349        port_type = 'iSCSI'
350
351        port_data = self.client.build_scsi_port_data(
352            host_id, port_name, port_mode, port_type)
353
354        self.assertEqual(host_id, port_data.HostId)
355        self.assertEqual(port_name, port_data.PortName)
356        self.assertTrue(hasattr(port_data, 'PortMode'))
357        self.assertTrue(hasattr(port_data, 'PortType'))
358
359    def test_register_port(self):
360        port_data = self.client.build_scsi_port_data(
361            'host_id', 'port_name', 'initiator', 'iSCSI')
362        self.client.register_port(port_data)
363        self._assert_storage_services_method_called('RegisterPort')
364
365    def test_assign_port(self):
366        client_id = 'client_id'
367        port_id = 'port_id'
368        self.client.assign_port(client_id, port_id)
369        self._assert_storage_services_method_called('AssignPort')
370
371    def test_set_server_port_properties(self):
372        port_id = 'port_id'
373        port_properties = mock.MagicMock()
374        self.client.set_server_port_properties(port_id, port_properties)
375        self._assert_storage_services_method_called('SetServerPortProperties')
376
377    def test_build_access_token(self):
378        initiator_node_name = 'initiator'
379        initiator_username = 'initiator_username'
380        initiator_password = 'initiator_password'
381        mutual_authentication = True
382        target_username = 'target_username'
383        target_password = 'target_password'
384
385        access_token = self.client.build_access_token(
386            initiator_node_name, initiator_username, initiator_password,
387            mutual_authentication, target_username, target_password)
388
389        self.assertEqual(initiator_node_name, access_token.InitiatorNodeName)
390        self.assertEqual(initiator_username, access_token.InitiatorUsername)
391        self.assertEqual(initiator_password, access_token.InitiatorPassword)
392        self.assertEqual(mutual_authentication,
393                         access_token.MutualAuthentication)
394        self.assertEqual(target_username, access_token.TargetUsername)
395        self.assertEqual(target_password, access_token.TargetPassword)
396
397    def test_set_access_token(self):
398        port_id = 'port_id'
399        access_token = self.client.build_access_token(
400            'initiator_name', None, None, False, 'initiator_name', 'password')
401        self.client.set_access_token(port_id, access_token)
402        self._assert_storage_services_method_called('SetAccessToken')
403
404    def test_get_clients(self):
405        self.client.get_clients()
406        self._assert_storage_services_method_called('GetClients')
407
408    def test_register_client(self):
409        host_name = 'name'
410        description = 'description'
411        machine_type = 'Other'
412        mode = 'PreferredServer'
413        preferred_server_ids = None
414        self.client.register_client(
415            host_name, description, machine_type, mode, preferred_server_ids)
416        self._assert_storage_services_method_called('RegisterClient')
417
418    def test_set_client_capabilities(self):
419        client_id = 'client_id'
420        mpio = True
421        alua = True
422        self.client.set_client_capabilities(client_id, mpio, alua)
423        self._assert_storage_services_method_called('SetClientCapabilities')
424
425    def test_get_target_domains(self):
426        self.client.get_target_domains()
427        self._assert_storage_services_method_called('GetTargetDomains')
428
429    def test_create_target_domain(self):
430        initiator_host_id = 'host_id'
431        target_host_id = 'host_id'
432        self.client.create_target_domain(initiator_host_id, target_host_id)
433        self._assert_storage_services_method_called('CreateTargetDomain')
434
435    def test_delete_target_domain(self):
436        domain_id = 'domain_id'
437        self.client.delete_target_domain(domain_id)
438        self._assert_storage_services_method_called('DeleteTargetDomain')
439
440    def test_get_target_devices(self):
441        self.client.get_target_devices()
442        self._assert_storage_services_method_called('GetTargetDevices')
443
444    def test_build_scsi_port_nexus_data(self):
445        initiator_id = 'initiator_id'
446        target_id = 'target_id'
447
448        nexus = self.client.build_scsi_port_nexus_data(initiator_id, target_id)
449
450        self.assertEqual(initiator_id, nexus.InitiatorPortId)
451        self.assertEqual(target_id, nexus.TargetPortId)
452
453    def test_create_target_device(self):
454        domain_id = 'domain_id'
455        nexus = self.client.build_scsi_port_nexus_data('initiator_id',
456                                                       'target_id')
457        self.client.create_target_device(domain_id, nexus)
458        self._assert_storage_services_method_called('CreateTargetDevice')
459
460    def test_delete_target_device(self):
461        device_id = 'device_id'
462        self.client.delete_target_device(device_id)
463        self._assert_storage_services_method_called('DeleteTargetDevice')
464
465    def test_get_next_free_lun(self):
466        device_id = 'device_id'
467        self.client.get_next_free_lun(device_id)
468        self._assert_storage_services_method_called('GetNextFreeLun')
469
470    def test_get_logical_units(self):
471        self.client.get_logical_units()
472        self._assert_storage_services_method_called('GetLogicalUnits')
473
474    def test_map_logical_disk(self):
475        disk_id = 'disk_id'
476        lun = 0
477        host_id = 'host_id'
478        mapping_type = 'Client'
479        initiator_id = 'initiator_id'
480        target_id = 'target_id'
481        nexus = self.client.build_scsi_port_nexus_data(initiator_id, target_id)
482        self.client.map_logical_disk(
483            disk_id, nexus, lun, host_id, mapping_type)
484        self._assert_storage_services_method_called('MapLogicalDisk')
485
486    def test_unmap_logical_disk(self):
487        logical_disk_id = 'disk_id'
488        nexus = self.client.build_scsi_port_nexus_data('initiator_id',
489                                                       'target_id')
490        self.client.unmap_logical_disk(logical_disk_id, nexus)
491        self._assert_storage_services_method_called('UnmapLogicalDisk')
492
493
494FAKE_WSDL_DOCUMENT = """<?xml version="1.0" encoding="utf-8"?>
495<wsdl:definitions name="ExecutiveServices"
496                  targetNamespace="http://tempuri.org/"
497                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
498                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
499                  xmlns:tns="http://tempuri.org/"
500                  xmlns:wsa10="http://www.w3.org/2005/08/addressing"
501                  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl">
502    <wsdl:types>
503        <xs:schema elementFormDefault="qualified"
504                   targetNamespace="http://tempuri.org/"
505                   xmlns:xs="http://www.w3.org/2001/XMLSchema">
506            <xs:import
507namespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
508            <xs:import
509namespace="http://schemas.datacontract.org/2004/07/DataCore.Executive"/>
510            <xs:element name="StartExecutive">
511                <xs:complexType>
512                    <xs:sequence/>
513                </xs:complexType>
514            </xs:element>
515            <xs:element name="StartExecutiveResponse">
516                <xs:complexType>
517                    <xs:sequence/>
518                </xs:complexType>
519            </xs:element>
520            <xs:element name="StopExecutive">
521                <xs:complexType>
522                    <xs:sequence/>
523                </xs:complexType>
524            </xs:element>
525            <xs:element name="StopExecutiveResponse">
526                <xs:complexType>
527                    <xs:sequence/>
528                </xs:complexType>
529            </xs:element>
530            <xs:element name="ExecutiveStarted">
531                <xs:complexType>
532                    <xs:sequence/>
533                </xs:complexType>
534            </xs:element>
535            <xs:element name="ExecutiveStopped">
536                <xs:complexType>
537                    <xs:sequence/>
538                </xs:complexType>
539            </xs:element>
540        </xs:schema>
541    </wsdl:types>
542    <wsdl:message name="IExecutiveServiceEx_StartExecutive_InputMessage">
543        <wsdl:part name="parameters" element="tns:StartExecutive"/>
544    </wsdl:message>
545    <wsdl:message name="IExecutiveServiceEx_StartExecutive_OutputMessage">
546        <wsdl:part name="parameters" element="tns:StartExecutiveResponse"/>
547    </wsdl:message>
548    <wsdl:message
549name="IExecutiveServiceEx_StartExecutive_ExecutiveError_FaultMessage">
550        <wsdl:part name="detail" element="ExecutiveError"/>
551    </wsdl:message>
552    <wsdl:message name="IExecutiveServiceEx_StopExecutive_InputMessage">
553        <wsdl:part name="parameters" element="tns:StopExecutive"/>
554    </wsdl:message>
555    <wsdl:message name="IExecutiveServiceEx_StopExecutive_OutputMessage">
556        <wsdl:part name="parameters" element="tns:StopExecutiveResponse"/>
557    </wsdl:message>
558    <wsdl:message
559name="IExecutiveServiceEx_StopExecutive_ExecutiveError_FaultMessage">
560        <wsdl:part name="detail" element="ExecutiveError"/>
561    </wsdl:message>
562    <wsdl:message
563            name="IExecutiveServiceEx_ExecutiveStarted_OutputCallbackMessage">
564        <wsdl:part name="parameters" element="tns:ExecutiveStarted"/>
565    </wsdl:message>
566    <wsdl:message
567name="IExecutiveServiceEx_ExecutiveStopped_OutputCallbackMessage">
568        <wsdl:part name="parameters" element="tns:ExecutiveStopped"/>
569    </wsdl:message>
570    <wsdl:portType name="IExecutiveServiceEx">
571        <wsdl:operation name="StartExecutive">
572            <wsdl:input
573wsaw:Action="http://tempuri.org/IExecutiveService/StartExecutive"
574message="tns:IExecutiveServiceEx_StartExecutive_InputMessage"/>
575            <wsdl:output
576wsaw:Action="http://tempuri.org/IExecutiveService/StartExecutiveResponse"
577message="tns:IExecutiveServiceEx_StartExecutive_OutputMessage"/>
578            <wsdl:fault wsaw:Action="ExecutiveError" name="ExecutiveError"
579message="tns:IExecutiveServiceEx_StartExecutive_ExecutiveError_FaultMessage"/>
580        </wsdl:operation>
581        <wsdl:operation name="StopExecutive">
582            <wsdl:input
583wsaw:Action="http://tempuri.org/IExecutiveService/StopExecutive"
584message="tns:IExecutiveServiceEx_StopExecutive_InputMessage"/>
585            <wsdl:output
586wsaw:Action="http://tempuri.org/IExecutiveService/StopExecutiveResponse"
587message="tns:IExecutiveServiceEx_StopExecutive_OutputMessage"/>
588            <wsdl:fault wsaw:Action="ExecutiveError" name="ExecutiveError"
589message="tns:IExecutiveServiceEx_StopExecutive_ExecutiveError_FaultMessage"/>
590        </wsdl:operation>
591        <wsdl:operation name="ExecutiveStarted">
592            <wsdl:output
593wsaw:Action="http://tempuri.org/IExecutiveService/ExecutiveStarted"
594message="tns:IExecutiveServiceEx_ExecutiveStarted_OutputCallbackMessage"/>
595            <wsdl:fault wsaw:Action="ExecutiveError" name="ExecutiveError"
596                        message="tns:"/>
597        </wsdl:operation>
598        <wsdl:operation name="ExecutiveStopped">
599            <wsdl:output
600wsaw:Action="http://tempuri.org/IExecutiveService/ExecutiveStopped"
601message="tns:IExecutiveServiceEx_ExecutiveStopped_OutputCallbackMessage"/>
602            <wsdl:fault wsaw:Action="ExecutiveError" name="ExecutiveError"
603                        message="tns:"/>
604        </wsdl:operation>
605    </wsdl:portType>
606    <wsdl:binding name="CustomBinding_IExecutiveServiceEx"
607                  type="tns:IExecutiveServiceEx">
608        <soap:binding transport="http://schemas.microsoft.com/soap/websocket"/>
609        <wsdl:operation name="StartExecutive">
610            <soap:operation
611soapAction="http://tempuri.org/IExecutiveService/StartExecutive"
612                    style="document"/>
613            <wsdl:input>
614                <soap:body use="literal"/>
615            </wsdl:input>
616            <wsdl:output>
617                <soap:body use="literal"/>
618            </wsdl:output>
619            <wsdl:fault name="ExecutiveError">
620                <soap:fault use="literal" name="ExecutiveError" namespace=""/>
621            </wsdl:fault>
622        </wsdl:operation>
623        <wsdl:operation name="StopExecutive">
624            <soap:operation
625soapAction="http://tempuri.org/IExecutiveService/StopExecutive"
626                    style="document"/>
627            <wsdl:input>
628                <soap:body use="literal"/>
629            </wsdl:input>
630            <wsdl:output>
631                <soap:body use="literal"/>
632            </wsdl:output>
633            <wsdl:fault name="ExecutiveError">
634                <soap:fault use="literal" name="ExecutiveError" namespace=""/>
635            </wsdl:fault>
636        </wsdl:operation>
637        <wsdl:operation name="ExecutiveStarted">
638            <soap:operation
639soapAction="http://tempuri.org/IExecutiveService/ExecutiveStarted"
640                    style="document"/>
641            <wsdl:output>
642                <soap:body use="literal"/>
643            </wsdl:output>
644            <wsdl:fault name="ExecutiveError">
645                <soap:fault use="literal" name="ExecutiveError" namespace=""/>
646            </wsdl:fault>
647        </wsdl:operation>
648        <wsdl:operation name="ExecutiveStopped">
649            <soap:operation
650soapAction="http://tempuri.org/IExecutiveService/ExecutiveStopped"
651                    style="document"/>
652            <wsdl:output>
653                <soap:body use="literal"/>
654            </wsdl:output>
655            <wsdl:fault name="ExecutiveError">
656                <soap:fault use="literal" name="ExecutiveError" namespace=""/>
657            </wsdl:fault>
658        </wsdl:operation>
659    </wsdl:binding>
660    <wsdl:service name="ExecutiveServices">
661        <wsdl:port name="CustomBinding_IExecutiveServiceEx"
662                   binding="tns:CustomBinding_IExecutiveServiceEx">
663            <soap:address
664                    location="ws://mns-vsp-001:3794/IExecutiveServiceEx"/>
665            <wsa10:EndpointReference>
666                <wsa10:Address>ws://mns-vsp-001:3794/IExecutiveServiceEx
667                </wsa10:Address>
668            </wsa10:EndpointReference>
669        </wsdl:port>
670    </wsdl:service>
671</wsdl:definitions>"""
672
673
674class FaultDefinitionsFilterTestCase(test.TestCase):
675    """Tests for the plugin to process the DataCore API WSDL document."""
676
677    @staticmethod
678    def _binding_operation_has_fault(document, operation_name):
679        for binding in document.getChildren('binding', wsdl.wsdlns):
680            for operation in binding.getChildren('operation', wsdl.wsdlns):
681                if operation.get('name') == operation_name:
682                    fault = operation.getChildren('fault', wsdl.wsdlns)
683                    if fault:
684                        return True
685        return False
686
687    @staticmethod
688    def _port_type_operation_has_fault(document, operation_name):
689        for port_type in document.getChildren('portType', wsdl.wsdlns):
690            for operation in port_type.getChildren('operation', wsdl.wsdlns):
691                if operation.get('name') == operation_name:
692                    fault = operation.getChildren('fault', wsdl.wsdlns)
693                    if fault:
694                        return True
695        return False
696
697    def _operation_has_fault(self, document, operation_name):
698        _binding_has_fault = self._binding_operation_has_fault(
699            document, operation_name)
700        _port_type_has_fault = self._port_type_operation_has_fault(
701            document, operation_name)
702        self.assertEqual(_binding_has_fault, _port_type_has_fault)
703        return _binding_has_fault
704
705    def test_parsed(self):
706        context = mock.Mock()
707        sax = parser.Parser()
708        wsdl_document = FAKE_WSDL_DOCUMENT
709        if isinstance(wsdl_document, six.text_type):
710            wsdl_document = wsdl_document.encode('utf-8')
711        context.document = sax.parse(string=wsdl_document).root()
712        self.assertTrue(self._operation_has_fault(context.document,
713                                                  'StartExecutive'))
714        self.assertTrue(self._operation_has_fault(context.document,
715                                                  'StopExecutive'))
716        self.assertTrue(self._operation_has_fault(context.document,
717                                                  'ExecutiveStarted'))
718        self.assertTrue(self._operation_has_fault(context.document,
719                                                  'ExecutiveStopped'))
720        plugin = api.FaultDefinitionsFilter()
721        plugin.parsed(context)
722        self.assertTrue(self._operation_has_fault(context.document,
723                                                  'StartExecutive'))
724        self.assertTrue(self._operation_has_fault(context.document,
725                                                  'StopExecutive'))
726        self.assertFalse(self._operation_has_fault(context.document,
727                                                   'ExecutiveStarted'))
728        self.assertFalse(self._operation_has_fault(context.document,
729                                                   'ExecutiveStopped'))
730