http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/test/container/test_rancher.py ---------------------------------------------------------------------- diff --cc libcloud/test/container/test_rancher.py index 4793c67,a2997e4..13d0e37 --- a/libcloud/test/container/test_rancher.py +++ b/libcloud/test/container/test_rancher.py @@@ -26,6 -26,148 +26,146 @@@ from libcloud.test.file_fixtures impor from libcloud.test import MockHttp + # --------------------------------------------------------------------------- # + # Mock Classes + + class RancherMockHttp(MockHttp): + fixtures = ContainerFileFixtures('rancher') + + def _v1_environments(self, method, url, body, headers): + if method == 'GET': + return (httplib.OK, self.fixtures.load('ex_list_stacks.json'), {}, + httplib.responses[httplib.OK]) + else: + return (httplib.OK, self.fixtures.load('ex_deploy_stack.json'), {}, + httplib.responses[httplib.OK]) + + def _v1_environments_1e9(self, method, url, body, headers): + return (httplib.OK, self.fixtures.load('ex_deploy_stack.json'), {}, + httplib.responses[httplib.OK]) + + def _v1_environments_1e10(self, method, url, body, headers): + return (httplib.OK, self.fixtures.load('ex_destroy_stack.json'), {}, + httplib.responses[httplib.OK]) + + def _v1_environments_1e1(self, method, url, body, headers): + return (httplib.OK, self.fixtures.load('ex_activate_stack.json'), {}, + httplib.responses[httplib.OK]) + + def _v1_services(self, method, url, body, headers): + if '?healthState=healthy' in url: + return (httplib.OK, self.fixtures.load('ex_search_services.json'), + {}, httplib.responses[httplib.OK]) + elif method == 'GET': + return (httplib.OK, self.fixtures.load('ex_list_services.json'), + {}, httplib.responses[httplib.OK]) + else: + return (httplib.OK, self.fixtures.load('ex_deploy_service.json'), + {}, httplib.responses[httplib.OK]) + + def _v1_services_1s13(self, method, url, body, headers): + if method == 'GET': + return (httplib.OK, self.fixtures.load('ex_deploy_service.json'), + {}, httplib.responses[httplib.OK]) + elif method == 'DELETE': + return (httplib.OK, self.fixtures.load('ex_destroy_service.json'), + {}, httplib.responses[httplib.OK]) + + def _v1_services_1s6(self, method, url, body, headers): + return (httplib.OK, self.fixtures.load('ex_activate_service.json'), {}, + httplib.responses[httplib.OK]) + + def _v1_containers(self, method, url, body, headers): + if '?state=running' in url: + return (httplib.OK, + self.fixtures.load('ex_search_containers.json'), {}, + httplib.responses[httplib.OK]) + elif method == 'POST': + return (httplib.OK, self.fixtures.load('deploy_container.json'), + {}, httplib.responses[httplib.OK]) + return (httplib.OK, self.fixtures.load('list_containers.json'), {}, + httplib.responses[httplib.OK]) + + def _v1_containers_1i31(self, method, url, body, headers): + if method == 'GET': + return (httplib.OK, self.fixtures.load('deploy_container.json'), + {}, httplib.responses[httplib.OK]) + elif method == 'DELETE' or '?action=stop' in url: + return (httplib.OK, self.fixtures.load('stop_container.json'), {}, + httplib.responses[httplib.OK]) + elif '?action=start' in url: + return (httplib.OK, self.fixtures.load('start_container.json'), {}, + httplib.responses[httplib.OK]) + else: + return (httplib.OK, self.fixtures.load('deploy_container.json'), + {}, httplib.responses[httplib.OK]) + + -RancherContainerDriver.connectionCls.conn_classes = ( - RancherMockHttp, RancherMockHttp -) ++RancherContainerDriver.connectionCls.conn_class = RancherMockHttp + RancherMockHttp.type = None + RancherMockHttp.use_param = 'a' + + + # --------------------------------------------------------------------------- # + # Test Cases + + + class RancherContainerDriverInitTestCase(unittest.TestCase): + """ + Tests for testing the different permutations of the driver initialization + string. + """ + + def test_full_url_string(self): + """ + Test a 'full' URL string, which contains a scheme, port, and base path. + """ + path = "http://myhostname:1234/base" + driver = RancherContainerDriver(*CONTAINER_PARAMS_RANCHER, host=path) + + self.assertEqual(driver.secure, False) + self.assertEqual(driver.connection.host, "myhostname") + self.assertEqual(driver.connection.port, 1234) + self.assertEqual(driver.baseuri, "/base") + + def test_url_string_no_port(self): + """ + Test a partial URL string, which contains a scheme, and base path. + """ + path = "http://myhostname/base" + driver = RancherContainerDriver(*CONTAINER_PARAMS_RANCHER, host=path, + port=1234) + + self.assertEqual(driver.secure, False) + self.assertEqual(driver.connection.host, "myhostname") + self.assertEqual(driver.connection.port, 1234) + self.assertEqual(driver.baseuri, "/base") + + def test_url_string_no_scheme(self): + """ + Test a partial URL string, which contains a port, and base path. + """ + path = "myhostname:1234/base" + driver = RancherContainerDriver(*CONTAINER_PARAMS_RANCHER, host=path) + + self.assertEqual(driver.secure, True) + self.assertEqual(driver.connection.host, "myhostname") + self.assertEqual(driver.connection.port, 1234) + self.assertEqual(driver.baseuri, "/base") + + def test_url_string_no_base_path(self): + """ + Test a partial URL string, which contains a scheme, and a port. + """ + path = "http://myhostname:1234" + driver = RancherContainerDriver(*CONTAINER_PARAMS_RANCHER, host=path) + + self.assertEqual(driver.secure, False) + self.assertEqual(driver.connection.host, "myhostname") + self.assertEqual(driver.connection.port, 1234) + self.assertEqual(driver.baseuri, "/v%s" % driver.version) + + class RancherContainerDriverTestCase(unittest.TestCase): def setUp(self):
http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/test/loadbalancer/test_alb.py ---------------------------------------------------------------------- diff --cc libcloud/test/loadbalancer/test_alb.py index 0f1d293,0f1d293..01813eb --- a/libcloud/test/loadbalancer/test_alb.py +++ b/libcloud/test/loadbalancer/test_alb.py @@@ -28,7 -28,7 +28,7 @@@ from libcloud.test.file_fixtures impor class ApplicationLBTests(unittest.TestCase): def setUp(self): ApplicationLBMockHttp.test = self -- ApplicationLBDriver.connectionCls.conn_classes = (None, ApplicationLBMockHttp) ++ ApplicationLBDriver.connectionCls.conn_class = ApplicationLBMockHttp ApplicationLBMockHttp.type = None ApplicationLBMockHttp.use_param = 'Action' self.driver = ApplicationLBDriver(*LB_ALB_PARAMS) http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/test/loadbalancer/test_dimensiondata_v2_3.py ---------------------------------------------------------------------- diff --cc libcloud/test/loadbalancer/test_dimensiondata_v2_3.py index 0000000,9a406cb..2467373 mode 000000,100644..100644 --- a/libcloud/test/loadbalancer/test_dimensiondata_v2_3.py +++ b/libcloud/test/loadbalancer/test_dimensiondata_v2_3.py @@@ -1,0 -1,620 +1,620 @@@ + # Licensed to the Apache Software Foundation (ASF) under one or more + # contributor license agreements. See the NOTICE file distributed with + # this work for additional information regarding copyright ownership. + # The ASF licenses this file to You under the Apache License, Version 2.0 + # (the "License"); you may not use this file except in compliance with + # the License. You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + import sys + from libcloud.utils.py3 import httplib + + from libcloud.common.types import InvalidCredsError + from libcloud.common.dimensiondata import DimensionDataVIPNode, DimensionDataPool + from libcloud.common.dimensiondata import DimensionDataPoolMember + from libcloud.loadbalancer.base import LoadBalancer, Member, Algorithm + from libcloud.loadbalancer.drivers.dimensiondata \ + import DimensionDataLBDriver as DimensionData + from libcloud.loadbalancer.types import State + + from libcloud.test import MockHttp, unittest + from libcloud.test.file_fixtures import LoadBalancerFileFixtures + + from libcloud.test.secrets import DIMENSIONDATA_PARAMS + + + class DimensionData_v2_3_Tests(unittest.TestCase): + + def setUp(self): + DimensionData.connectionCls.active_api_version = '2.3' - DimensionData.connectionCls.conn_classes = (None, DimensionDataMockHttp) ++ DimensionData.connectionCls.conn_class = DimensionDataMockHttp + DimensionDataMockHttp.type = None + self.driver = DimensionData(*DIMENSIONDATA_PARAMS) + + def test_invalid_region(self): + with self.assertRaises(ValueError): + self.driver = DimensionData(*DIMENSIONDATA_PARAMS, region='blah') + + def test_invalid_creds(self): + DimensionDataMockHttp.type = 'UNAUTHORIZED' + with self.assertRaises(InvalidCredsError): + self.driver.list_balancers() + + def test_create_balancer(self): + self.driver.ex_set_current_network_domain('1234') + members = [] + members.append(Member( + id=None, + ip='1.2.3.4', + port=80)) + + balancer = self.driver.create_balancer( + name='test', + port=80, + protocol='http', + algorithm=Algorithm.ROUND_ROBIN, + members=members, + ex_listener_ip_address='5.6.7.8') + self.assertEqual(balancer.name, 'test') + self.assertEqual(balancer.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(balancer.ip, '165.180.12.22') + self.assertEqual(balancer.port, 80) + self.assertEqual(balancer.extra['pool_id'], '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(balancer.extra['network_domain_id'], '1234') + self.assertEqual(balancer.extra['listener_ip_address'], '5.6.7.8') + + def test_create_balancer_with_defaults(self): + self.driver.ex_set_current_network_domain('1234') + + balancer = self.driver.create_balancer( + name='test', + port=None, + protocol=None, + algorithm=None, + members=None) + self.assertEqual(balancer.name, 'test') + self.assertEqual(balancer.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(balancer.ip, '165.180.12.22') + self.assertEqual(balancer.port, None) + self.assertEqual(balancer.extra['pool_id'], '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(balancer.extra['network_domain_id'], '1234') + + def test_create_balancer_no_members(self): + self.driver.ex_set_current_network_domain('1234') + members = None + + balancer = self.driver.create_balancer( + name='test', + port=80, + protocol='http', + algorithm=Algorithm.ROUND_ROBIN, + members=members) + self.assertEqual(balancer.name, 'test') + self.assertEqual(balancer.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(balancer.ip, '165.180.12.22') + self.assertEqual(balancer.port, 80) + self.assertEqual(balancer.extra['pool_id'], '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(balancer.extra['network_domain_id'], '1234') + + def test_create_balancer_empty_members(self): + self.driver.ex_set_current_network_domain('1234') + members = [] + + balancer = self.driver.create_balancer( + name='test', + port=80, + protocol='http', + algorithm=Algorithm.ROUND_ROBIN, + members=members) + self.assertEqual(balancer.name, 'test') + self.assertEqual(balancer.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(balancer.ip, '165.180.12.22') + self.assertEqual(balancer.port, 80) + self.assertEqual(balancer.extra['pool_id'], '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(balancer.extra['network_domain_id'], '1234') + + def test_list_balancers(self): + bal = self.driver.list_balancers() + self.assertEqual(bal[0].name, 'myProduction.Virtual.Listener') + self.assertEqual(bal[0].id, '6115469d-a8bb-445b-bb23-d23b5283f2b9') + self.assertEqual(bal[0].port, '8899') + self.assertEqual(bal[0].ip, '165.180.12.22') + self.assertEqual(bal[0].state, State.RUNNING) + + def test_balancer_list_members(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + members = self.driver.balancer_list_members(balancer) + self.assertEqual(2, len(members)) + self.assertEqual(members[0].ip, '10.0.3.13') + self.assertEqual(members[0].id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(members[0].port, 9889) + + def test_balancer_attach_member(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + member = Member( + id=None, + ip='112.12.2.2', + port=80, + balancer=balancer, + extra=None) + member = self.driver.balancer_attach_member(balancer, member) + self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + + def test_balancer_attach_member_without_port(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + member = Member( + id=None, + ip='112.12.2.2', + port=None, + balancer=balancer, + extra=None) + member = self.driver.balancer_attach_member(balancer, member) + self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(member.port, None) + + def test_balancer_detach_member(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + member = Member( + id='3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0', + ip='112.12.2.2', + port=80, + balancer=balancer, + extra=None) + result = self.driver.balancer_detach_member(balancer, member) + self.assertEqual(result, True) + + def test_destroy_balancer(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + response = self.driver.destroy_balancer(balancer) + self.assertEqual(response, True) + + def test_set_get_network_domain_id(self): + self.driver.ex_set_current_network_domain('1234') + nwd = self.driver.ex_get_current_network_domain() + self.assertEqual(nwd, '1234') + + def test_ex_create_pool_member(self): + pool = DimensionDataPool( + id='4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None + ) + node = DimensionDataVIPNode( + id='2344', + name='test', + status=State.RUNNING, + ip='123.23.3.2' + ) + member = self.driver.ex_create_pool_member( + pool=pool, + node=node, + port=80 + ) + self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(member.name, '10.0.3.13') + self.assertEqual(member.ip, '123.23.3.2') + + def test_ex_create_node(self): + node = self.driver.ex_create_node( + network_domain_id='12345', + name='test', + ip='123.12.32.2', + ex_description='', + connection_limit=25000, + connection_rate_limit=2000) + self.assertEqual(node.name, 'myProductionNode.1') + self.assertEqual(node.id, '9e6b496d-5261-4542-91aa-b50c7f569c54') + + def test_ex_create_pool(self, ): + pool = self.driver.ex_create_pool( + network_domain_id='1234', + name='test', + balancer_method='ROUND_ROBIN', + ex_description='test', + service_down_action='NONE', + slow_ramp_time=30) + self.assertEqual(pool.id, '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(pool.name, 'test') + self.assertEqual(pool.status, State.RUNNING) + + def test_ex_create_virtual_listener(self): + listener = self.driver.ex_create_virtual_listener( + network_domain_id='12345', + name='test', + ex_description='test', + port=80, + pool=DimensionDataPool( + id='1234', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None + )) + self.assertEqual(listener.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(listener.name, 'test') + + def test_ex_create_virtual_listener_unusual_port(self): + listener = self.driver.ex_create_virtual_listener( + network_domain_id='12345', + name='test', + ex_description='test', + port=8900, + pool=DimensionDataPool( + id='1234', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None + )) + self.assertEqual(listener.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(listener.name, 'test') + + def test_ex_create_virtual_listener_without_port(self): + listener = self.driver.ex_create_virtual_listener( + network_domain_id='12345', + name='test', + ex_description='test', + pool=DimensionDataPool( + id='1234', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None + )) + self.assertEqual(listener.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(listener.name, 'test') + + def test_ex_create_virtual_listener_without_pool(self): + listener = self.driver.ex_create_virtual_listener( + network_domain_id='12345', + name='test', + ex_description='test') + self.assertEqual(listener.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(listener.name, 'test') + + def test_get_balancer(self): + bal = self.driver.get_balancer('6115469d-a8bb-445b-bb23-d23b5283f2b9') + self.assertEqual(bal.name, 'myProduction.Virtual.Listener') + self.assertEqual(bal.id, '6115469d-a8bb-445b-bb23-d23b5283f2b9') + self.assertEqual(bal.port, '8899') + self.assertEqual(bal.ip, '165.180.12.22') + self.assertEqual(bal.state, State.RUNNING) + + def test_list_protocols(self): + protocols = self.driver.list_protocols() + self.assertNotEqual(0, len(protocols)) + + def test_ex_get_nodes(self): + nodes = self.driver.ex_get_nodes() + self.assertEqual(2, len(nodes)) + self.assertEqual(nodes[0].name, 'ProductionNode.1') + self.assertEqual(nodes[0].id, '34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + self.assertEqual(nodes[0].ip, '10.10.10.101') + + def test_ex_get_node(self): + node = self.driver.ex_get_node('34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + self.assertEqual(node.name, 'ProductionNode.2') + self.assertEqual(node.id, '34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + self.assertEqual(node.ip, '10.10.10.101') + + def test_ex_update_node(self): + node = self.driver.ex_get_node('34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + node.connection_limit = '100' + result = self.driver.ex_update_node(node) + self.assertEqual(result.connection_limit, '100') + + def test_ex_destroy_node(self): + result = self.driver.ex_destroy_node('34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + self.assertTrue(result) + + def test_ex_set_node_state(self): + node = self.driver.ex_get_node('34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + result = self.driver.ex_set_node_state(node, False) + self.assertEqual(result.connection_limit, '10000') + + def test_ex_get_pools(self): + pools = self.driver.ex_get_pools() + self.assertNotEqual(0, len(pools)) + self.assertEqual(pools[0].name, 'myDevelopmentPool.1') + self.assertEqual(pools[0].id, '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + + def test_ex_get_pool(self): + pool = self.driver.ex_get_pool('4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + self.assertEqual(pool.name, 'myDevelopmentPool.1') + self.assertEqual(pool.id, '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + + def test_ex_update_pool(self): + pool = self.driver.ex_get_pool('4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + pool.slow_ramp_time = '120' + result = self.driver.ex_update_pool(pool) + self.assertTrue(result) + + def test_ex_destroy_pool(self): + response = self.driver.ex_destroy_pool( + pool=DimensionDataPool( + id='4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None)) + self.assertTrue(response) + + def test_get_pool_members(self): + members = self.driver.ex_get_pool_members('4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + self.assertEqual(2, len(members)) + self.assertEqual(members[0].id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(members[0].name, '10.0.3.13') + self.assertEqual(members[0].status, 'NORMAL') + self.assertEqual(members[0].ip, '10.0.3.13') + self.assertEqual(members[0].port, 9889) + self.assertEqual(members[0].node_id, '3c207269-e75e-11e4-811f-005056806999') + + def test_get_pool_member(self): + member = self.driver.ex_get_pool_member('3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(member.name, '10.0.3.13') + self.assertEqual(member.status, 'NORMAL') + self.assertEqual(member.ip, '10.0.3.13') + self.assertEqual(member.port, 9889) + + def test_set_pool_member_state(self): + member = self.driver.ex_get_pool_member('3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + result = self.driver.ex_set_pool_member_state(member, True) + self.assertTrue(result) + + def test_ex_destroy_pool_member(self): + response = self.driver.ex_destroy_pool_member( + member=DimensionDataPoolMember( + id='', + name='test', + status=State.RUNNING, + ip='1.2.3.4', + port=80, + node_id='3c207269-e75e-11e4-811f-005056806999'), + destroy_node=False) + self.assertTrue(response) + + def test_ex_destroy_pool_member_with_node(self): + response = self.driver.ex_destroy_pool_member( + member=DimensionDataPoolMember( + id='', + name='test', + status=State.RUNNING, + ip='1.2.3.4', + port=80, + node_id='34de6ed6-46a4-4dae-a753-2f8d3840c6f9'), + destroy_node=True) + self.assertTrue(response) + + def test_ex_get_default_health_monitors(self): + monitors = self.driver.ex_get_default_health_monitors( + '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7' + ) + self.assertEqual(len(monitors), 6) + self.assertEqual(monitors[0].id, '01683574-d487-11e4-811f-005056806999') + self.assertEqual(monitors[0].name, 'CCDEFAULT.Http') + self.assertFalse(monitors[0].node_compatible) + self.assertTrue(monitors[0].pool_compatible) + + def test_ex_get_default_persistence_profiles(self): + profiles = self.driver.ex_get_default_persistence_profiles( + '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7' + ) + self.assertEqual(len(profiles), 4) + self.assertEqual(profiles[0].id, 'a34ca024-f3db-11e4-b010-005056806999') + self.assertEqual(profiles[0].name, 'CCDEFAULT.Cookie') + self.assertEqual(profiles[0].fallback_compatible, False) + self.assertEqual(len(profiles[0].compatible_listeners), 1) + self.assertEqual(profiles[0].compatible_listeners[0].type, 'PERFORMANCE_LAYER_4') + + def test_ex_get_default_irules(self): + irules = self.driver.ex_get_default_irules( + '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7' + ) + self.assertEqual(len(irules), 4) + self.assertEqual(irules[0].id, '2b20cb2c-ffdc-11e4-b010-005056806999') + self.assertEqual(irules[0].name, 'CCDEFAULT.HttpsRedirect') + self.assertEqual(len(irules[0].compatible_listeners), 1) + self.assertEqual(irules[0].compatible_listeners[0].type, 'PERFORMANCE_LAYER_4') + + + class DimensionDataMockHttp(MockHttp): + + fixtures = LoadBalancerFileFixtures('dimensiondata') + + def _oec_0_9_myaccount_UNAUTHORIZED(self, method, url, body, headers): + return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED]) + + def _oec_0_9_myaccount(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_myaccount.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_myaccount_INPROGRESS(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_myaccount.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_virtualListener.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_pool.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_poolMember.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_createPool(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_createPool.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_createNode(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_createNode.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_addPoolMember(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_addPoolMember.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_createVirtualListener(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_createVirtualListener.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_removePoolMember(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_removePoolMember.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_deleteVirtualListener(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_deleteVirtualListener.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_deletePool(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_deletePool.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_deleteNode(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_deleteNode.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_node(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_node.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_node_34de6ed6_46a4_4dae_a753_2f8d3840c6f9(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_node_34de6ed6_46a4_4dae_a753_2f8d3840c6f9.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_editNode(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_editNode.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_editPool(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_editPool.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_editPoolMember(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_editPoolMember.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_defaultHealthMonitor(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_defaultHealthMonitor.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_defaultPersistenceProfile(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_defaultPersistenceProfile.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_defaultIrule(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_defaultIrule.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + if __name__ == '__main__': + sys.exit(unittest.main()) http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/test/loadbalancer/test_dimensiondata_v2_4.py ---------------------------------------------------------------------- diff --cc libcloud/test/loadbalancer/test_dimensiondata_v2_4.py index 0000000,07f472c..b45ecc6 mode 000000,100644..100644 --- a/libcloud/test/loadbalancer/test_dimensiondata_v2_4.py +++ b/libcloud/test/loadbalancer/test_dimensiondata_v2_4.py @@@ -1,0 -1,620 +1,620 @@@ + # Licensed to the Apache Software Foundation (ASF) under one or more + # contributor license agreements. See the NOTICE file distributed with + # this work for additional information regarding copyright ownership. + # The ASF licenses this file to You under the Apache License, Version 2.0 + # (the "License"); you may not use this file except in compliance with + # the License. You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + import sys + from libcloud.utils.py3 import httplib + + from libcloud.common.types import InvalidCredsError + from libcloud.common.dimensiondata import DimensionDataVIPNode, DimensionDataPool + from libcloud.common.dimensiondata import DimensionDataPoolMember + from libcloud.loadbalancer.base import LoadBalancer, Member, Algorithm + from libcloud.loadbalancer.drivers.dimensiondata \ + import DimensionDataLBDriver as DimensionData + from libcloud.loadbalancer.types import State + + from libcloud.test import MockHttp, unittest + from libcloud.test.file_fixtures import LoadBalancerFileFixtures + + from libcloud.test.secrets import DIMENSIONDATA_PARAMS + + + class DimensionData_v2_4_Tests(unittest.TestCase): + + def setUp(self): + DimensionData.connectionCls.active_api_version = '2.4' - DimensionData.connectionCls.conn_classes = (None, DimensionDataMockHttp) ++ DimensionData.connectionCls.conn_class = DimensionDataMockHttp + DimensionDataMockHttp.type = None + self.driver = DimensionData(*DIMENSIONDATA_PARAMS) + + def test_invalid_region(self): + with self.assertRaises(ValueError): + self.driver = DimensionData(*DIMENSIONDATA_PARAMS, region='blah') + + def test_invalid_creds(self): + DimensionDataMockHttp.type = 'UNAUTHORIZED' + with self.assertRaises(InvalidCredsError): + self.driver.list_balancers() + + def test_create_balancer(self): + self.driver.ex_set_current_network_domain('1234') + members = [] + members.append(Member( + id=None, + ip='1.2.3.4', + port=80)) + + balancer = self.driver.create_balancer( + name='test', + port=80, + protocol='http', + algorithm=Algorithm.ROUND_ROBIN, + members=members, + ex_listener_ip_address='5.6.7.8') + self.assertEqual(balancer.name, 'test') + self.assertEqual(balancer.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(balancer.ip, '165.180.12.22') + self.assertEqual(balancer.port, 80) + self.assertEqual(balancer.extra['pool_id'], '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(balancer.extra['network_domain_id'], '1234') + self.assertEqual(balancer.extra['listener_ip_address'], '5.6.7.8') + + def test_create_balancer_with_defaults(self): + self.driver.ex_set_current_network_domain('1234') + + balancer = self.driver.create_balancer( + name='test', + port=None, + protocol=None, + algorithm=None, + members=None) + self.assertEqual(balancer.name, 'test') + self.assertEqual(balancer.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(balancer.ip, '165.180.12.22') + self.assertEqual(balancer.port, None) + self.assertEqual(balancer.extra['pool_id'], '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(balancer.extra['network_domain_id'], '1234') + + def test_create_balancer_no_members(self): + self.driver.ex_set_current_network_domain('1234') + members = None + + balancer = self.driver.create_balancer( + name='test', + port=80, + protocol='http', + algorithm=Algorithm.ROUND_ROBIN, + members=members) + self.assertEqual(balancer.name, 'test') + self.assertEqual(balancer.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(balancer.ip, '165.180.12.22') + self.assertEqual(balancer.port, 80) + self.assertEqual(balancer.extra['pool_id'], '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(balancer.extra['network_domain_id'], '1234') + + def test_create_balancer_empty_members(self): + self.driver.ex_set_current_network_domain('1234') + members = [] + + balancer = self.driver.create_balancer( + name='test', + port=80, + protocol='http', + algorithm=Algorithm.ROUND_ROBIN, + members=members) + self.assertEqual(balancer.name, 'test') + self.assertEqual(balancer.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(balancer.ip, '165.180.12.22') + self.assertEqual(balancer.port, 80) + self.assertEqual(balancer.extra['pool_id'], '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(balancer.extra['network_domain_id'], '1234') + + def test_list_balancers(self): + bal = self.driver.list_balancers() + self.assertEqual(bal[0].name, 'myProduction.Virtual.Listener') + self.assertEqual(bal[0].id, '6115469d-a8bb-445b-bb23-d23b5283f2b9') + self.assertEqual(bal[0].port, '8899') + self.assertEqual(bal[0].ip, '165.180.12.22') + self.assertEqual(bal[0].state, State.RUNNING) + + def test_balancer_list_members(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + members = self.driver.balancer_list_members(balancer) + self.assertEqual(2, len(members)) + self.assertEqual(members[0].ip, '10.0.3.13') + self.assertEqual(members[0].id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(members[0].port, 9889) + + def test_balancer_attach_member(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + member = Member( + id=None, + ip='112.12.2.2', + port=80, + balancer=balancer, + extra=None) + member = self.driver.balancer_attach_member(balancer, member) + self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + + def test_balancer_attach_member_without_port(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + member = Member( + id=None, + ip='112.12.2.2', + port=None, + balancer=balancer, + extra=None) + member = self.driver.balancer_attach_member(balancer, member) + self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(member.port, None) + + def test_balancer_detach_member(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + member = Member( + id='3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0', + ip='112.12.2.2', + port=80, + balancer=balancer, + extra=None) + result = self.driver.balancer_detach_member(balancer, member) + self.assertEqual(result, True) + + def test_destroy_balancer(self): + extra = {'pool_id': '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + 'network_domain_id': '1234'} + balancer = LoadBalancer( + id='234', + name='test', + state=State.RUNNING, + ip='1.2.3.4', + port=1234, + driver=self.driver, + extra=extra + ) + response = self.driver.destroy_balancer(balancer) + self.assertEqual(response, True) + + def test_set_get_network_domain_id(self): + self.driver.ex_set_current_network_domain('1234') + nwd = self.driver.ex_get_current_network_domain() + self.assertEqual(nwd, '1234') + + def test_ex_create_pool_member(self): + pool = DimensionDataPool( + id='4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None + ) + node = DimensionDataVIPNode( + id='2344', + name='test', + status=State.RUNNING, + ip='123.23.3.2' + ) + member = self.driver.ex_create_pool_member( + pool=pool, + node=node, + port=80 + ) + self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(member.name, '10.0.3.13') + self.assertEqual(member.ip, '123.23.3.2') + + def test_ex_create_node(self): + node = self.driver.ex_create_node( + network_domain_id='12345', + name='test', + ip='123.12.32.2', + ex_description='', + connection_limit=25000, + connection_rate_limit=2000) + self.assertEqual(node.name, 'myProductionNode.1') + self.assertEqual(node.id, '9e6b496d-5261-4542-91aa-b50c7f569c54') + + def test_ex_create_pool(self, ): + pool = self.driver.ex_create_pool( + network_domain_id='1234', + name='test', + balancer_method='ROUND_ROBIN', + ex_description='test', + service_down_action='NONE', + slow_ramp_time=30) + self.assertEqual(pool.id, '9e6b496d-5261-4542-91aa-b50c7f569c54') + self.assertEqual(pool.name, 'test') + self.assertEqual(pool.status, State.RUNNING) + + def test_ex_create_virtual_listener(self): + listener = self.driver.ex_create_virtual_listener( + network_domain_id='12345', + name='test', + ex_description='test', + port=80, + pool=DimensionDataPool( + id='1234', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None + )) + self.assertEqual(listener.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(listener.name, 'test') + + def test_ex_create_virtual_listener_unusual_port(self): + listener = self.driver.ex_create_virtual_listener( + network_domain_id='12345', + name='test', + ex_description='test', + port=8900, + pool=DimensionDataPool( + id='1234', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None + )) + self.assertEqual(listener.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(listener.name, 'test') + + def test_ex_create_virtual_listener_without_port(self): + listener = self.driver.ex_create_virtual_listener( + network_domain_id='12345', + name='test', + ex_description='test', + pool=DimensionDataPool( + id='1234', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None + )) + self.assertEqual(listener.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(listener.name, 'test') + + def test_ex_create_virtual_listener_without_pool(self): + listener = self.driver.ex_create_virtual_listener( + network_domain_id='12345', + name='test', + ex_description='test') + self.assertEqual(listener.id, '8334f461-0df0-42d5-97eb-f4678eb26bea') + self.assertEqual(listener.name, 'test') + + def test_get_balancer(self): + bal = self.driver.get_balancer('6115469d-a8bb-445b-bb23-d23b5283f2b9') + self.assertEqual(bal.name, 'myProduction.Virtual.Listener') + self.assertEqual(bal.id, '6115469d-a8bb-445b-bb23-d23b5283f2b9') + self.assertEqual(bal.port, '8899') + self.assertEqual(bal.ip, '165.180.12.22') + self.assertEqual(bal.state, State.RUNNING) + + def test_list_protocols(self): + protocols = self.driver.list_protocols() + self.assertNotEqual(0, len(protocols)) + + def test_ex_get_nodes(self): + nodes = self.driver.ex_get_nodes() + self.assertEqual(2, len(nodes)) + self.assertEqual(nodes[0].name, 'ProductionNode.1') + self.assertEqual(nodes[0].id, '34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + self.assertEqual(nodes[0].ip, '10.10.10.101') + + def test_ex_get_node(self): + node = self.driver.ex_get_node('34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + self.assertEqual(node.name, 'ProductionNode.2') + self.assertEqual(node.id, '34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + self.assertEqual(node.ip, '10.10.10.101') + + def test_ex_update_node(self): + node = self.driver.ex_get_node('34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + node.connection_limit = '100' + result = self.driver.ex_update_node(node) + self.assertEqual(result.connection_limit, '100') + + def test_ex_destroy_node(self): + result = self.driver.ex_destroy_node('34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + self.assertTrue(result) + + def test_ex_set_node_state(self): + node = self.driver.ex_get_node('34de6ed6-46a4-4dae-a753-2f8d3840c6f9') + result = self.driver.ex_set_node_state(node, False) + self.assertEqual(result.connection_limit, '10000') + + def test_ex_get_pools(self): + pools = self.driver.ex_get_pools() + self.assertNotEqual(0, len(pools)) + self.assertEqual(pools[0].name, 'myDevelopmentPool.1') + self.assertEqual(pools[0].id, '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + + def test_ex_get_pool(self): + pool = self.driver.ex_get_pool('4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + self.assertEqual(pool.name, 'myDevelopmentPool.1') + self.assertEqual(pool.id, '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + + def test_ex_update_pool(self): + pool = self.driver.ex_get_pool('4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + pool.slow_ramp_time = '120' + result = self.driver.ex_update_pool(pool) + self.assertTrue(result) + + def test_ex_destroy_pool(self): + response = self.driver.ex_destroy_pool( + pool=DimensionDataPool( + id='4d360b1f-bc2c-4ab7-9884-1f03ba2768f7', + name='test', + description='test', + status=State.RUNNING, + health_monitor_id=None, + load_balance_method=None, + service_down_action=None, + slow_ramp_time=None)) + self.assertTrue(response) + + def test_get_pool_members(self): + members = self.driver.ex_get_pool_members('4d360b1f-bc2c-4ab7-9884-1f03ba2768f7') + self.assertEqual(2, len(members)) + self.assertEqual(members[0].id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(members[0].name, '10.0.3.13') + self.assertEqual(members[0].status, 'NORMAL') + self.assertEqual(members[0].ip, '10.0.3.13') + self.assertEqual(members[0].port, 9889) + self.assertEqual(members[0].node_id, '3c207269-e75e-11e4-811f-005056806999') + + def test_get_pool_member(self): + member = self.driver.ex_get_pool_member('3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(member.id, '3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + self.assertEqual(member.name, '10.0.3.13') + self.assertEqual(member.status, 'NORMAL') + self.assertEqual(member.ip, '10.0.3.13') + self.assertEqual(member.port, 9889) + + def test_set_pool_member_state(self): + member = self.driver.ex_get_pool_member('3dd806a2-c2c8-4c0c-9a4f-5219ea9266c0') + result = self.driver.ex_set_pool_member_state(member, True) + self.assertTrue(result) + + def test_ex_destroy_pool_member(self): + response = self.driver.ex_destroy_pool_member( + member=DimensionDataPoolMember( + id='', + name='test', + status=State.RUNNING, + ip='1.2.3.4', + port=80, + node_id='3c207269-e75e-11e4-811f-005056806999'), + destroy_node=False) + self.assertTrue(response) + + def test_ex_destroy_pool_member_with_node(self): + response = self.driver.ex_destroy_pool_member( + member=DimensionDataPoolMember( + id='', + name='test', + status=State.RUNNING, + ip='1.2.3.4', + port=80, + node_id='34de6ed6-46a4-4dae-a753-2f8d3840c6f9'), + destroy_node=True) + self.assertTrue(response) + + def test_ex_get_default_health_monitors(self): + monitors = self.driver.ex_get_default_health_monitors( + '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7' + ) + self.assertEqual(len(monitors), 6) + self.assertEqual(monitors[0].id, '01683574-d487-11e4-811f-005056806999') + self.assertEqual(monitors[0].name, 'CCDEFAULT.Http') + self.assertFalse(monitors[0].node_compatible) + self.assertTrue(monitors[0].pool_compatible) + + def test_ex_get_default_persistence_profiles(self): + profiles = self.driver.ex_get_default_persistence_profiles( + '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7' + ) + self.assertEqual(len(profiles), 4) + self.assertEqual(profiles[0].id, 'a34ca024-f3db-11e4-b010-005056806999') + self.assertEqual(profiles[0].name, 'CCDEFAULT.Cookie') + self.assertEqual(profiles[0].fallback_compatible, False) + self.assertEqual(len(profiles[0].compatible_listeners), 1) + self.assertEqual(profiles[0].compatible_listeners[0].type, 'PERFORMANCE_LAYER_4') + + def test_ex_get_default_irules(self): + irules = self.driver.ex_get_default_irules( + '4d360b1f-bc2c-4ab7-9884-1f03ba2768f7' + ) + self.assertEqual(len(irules), 4) + self.assertEqual(irules[0].id, '2b20cb2c-ffdc-11e4-b010-005056806999') + self.assertEqual(irules[0].name, 'CCDEFAULT.HttpsRedirect') + self.assertEqual(len(irules[0].compatible_listeners), 1) + self.assertEqual(irules[0].compatible_listeners[0].type, 'PERFORMANCE_LAYER_4') + + + class DimensionDataMockHttp(MockHttp): + + fixtures = LoadBalancerFileFixtures('dimensiondata') + + def _oec_0_9_myaccount_UNAUTHORIZED(self, method, url, body, headers): + return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED]) + + def _oec_0_9_myaccount(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_myaccount.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_myaccount_INPROGRESS(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_myaccount.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_virtualListener.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_virtualListener_6115469d_a8bb_445b_bb23_d23b5283f2b9.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_pool.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_pool_4d360b1f_bc2c_4ab7_9884_1f03ba2768f7.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_poolMember.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_poolMember_3dd806a2_c2c8_4c0c_9a4f_5219ea9266c0.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_createPool(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_createPool.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_createNode(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_createNode.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_addPoolMember(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_addPoolMember.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_createVirtualListener(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_createVirtualListener.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_removePoolMember(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_removePoolMember.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_deleteVirtualListener(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_deleteVirtualListener.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_deletePool(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_deletePool.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_deleteNode(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_deleteNode.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_node(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_node.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_node_34de6ed6_46a4_4dae_a753_2f8d3840c6f9(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_node_34de6ed6_46a4_4dae_a753_2f8d3840c6f9.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_editNode(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_editNode.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_editPool(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_editPool.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_editPoolMember(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_editPoolMember.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_defaultHealthMonitor(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_defaultHealthMonitor.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_defaultPersistenceProfile(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_defaultPersistenceProfile.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkDomainVip_defaultIrule(self, method, url, body, headers): + body = self.fixtures.load( + 'networkDomainVip_defaultIrule.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + if __name__ == '__main__': + sys.exit(unittest.main()) http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/test/loadbalancer/test_gce.py ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/test/test_httplib_ssl.py ---------------------------------------------------------------------- diff --cc libcloud/test/test_httplib_ssl.py index 073cce1,f94f737..1ed89ff --- a/libcloud/test/test_httplib_ssl.py +++ b/libcloud/test/test_httplib_ssl.py @@@ -16,8 -16,9 +16,6 @@@ import os import sys import os.path --import ssl --import socket - import mock from mock import patch @@@ -86,67 -101,67 +84,6 @@@ class TestHttpLibSSLTests(unittest.Test self.assertTrue(self.httplib_object.ca_cert is not None) -- # verify = True, no CA certs are available, exception should be thrown -- libcloud.security.CA_CERTS_PATH = [] -- -- expected_msg = libcloud.security.CA_CERTS_UNAVAILABLE_ERROR_MSG -- self.assertRaisesRegexp(RuntimeError, expected_msg, -- self.httplib_object._setup_ca_cert) -- -- @mock.patch('socket.create_connection', mock.MagicMock()) -- @mock.patch('socket.socket', mock.MagicMock()) -- def test_connect_throws_friendly_error_message_on_ssl_wrap_connection_reset_by_peer(self): -- -- mock_wrap_socket = None -- -- if getattr(ssl, 'HAS_SNI', False): -- ssl.SSLContext.wrap_socket = mock.MagicMock() -- mock_wrap_socket = ssl.SSLContext.wrap_socket -- else: -- ssl.wrap_socket = mock.MagicMock() -- mock_wrap_socket = ssl.wrap_socket -- -- # Test that we re-throw a more friendly error message in case -- # "connection reset by peer" error occurs when trying to establish a -- # SSL connection -- libcloud.security.VERIFY_SSL_CERT = True -- self.httplib_object.verify = True -- self.httplib_object.http_proxy_used = False -- -- # No connection reset by peer, original exception should be thrown -- mock_wrap_socket.side_effect = Exception('foo bar fail') -- -- expected_msg = 'foo bar fail' -- self.assertRaisesRegexp(Exception, expected_msg, -- self.httplib_object.connect) -- -- # Connection reset by peer, wrapped exception with friendly error -- # message should be thrown -- mock_wrap_socket.side_effect = socket.error('Connection reset by peer') -- -- expected_msg = 'Failed to establish SSL / TLS connection' -- self.assertRaisesRegexp(socket.error, expected_msg, -- self.httplib_object.connect) -- -- # Same error but including errno -- with self.assertRaises(socket.error) as cm: -- mock_wrap_socket.side_effect = socket.error(104, 'Connection reset by peer') -- self.httplib_object.connect() -- -- e = cm.exception -- self.assertEqual(e.errno, 104) -- self.assertTrue(expected_msg in str(e)) -- -- # Test original exception is propagated correctly on non reset by peer -- # error -- with self.assertRaises(socket.error) as cm: -- mock_wrap_socket.side_effect = socket.error(105, 'Some random error') -- self.httplib_object.connect() -- -- e = cm.exception -- self.assertEqual(e.errno, 105) -- self.assertTrue('Some random error' in str(e)) -- def test_certifi_ca_bundle_in_search_path(self): mock_certifi_ca_bundle_path = '/certifi/bundle/path' http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/utils/loggingconnection.py ---------------------------------------------------------------------- diff --cc libcloud/utils/loggingconnection.py index 71931b0,0000000..8577da4 mode 100644,000000..100644 --- a/libcloud/utils/loggingconnection.py +++ b/libcloud/utils/loggingconnection.py @@@ -1,196 -1,0 +1,196 @@@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +from __future__ import absolute_import + +try: + import simplejson as json +except: + import json + +from pipes import quote as pquote +from xml.dom.minidom import parseString + +import sys +import os + +from libcloud.common.base import (LibcloudConnection, + HTTPResponse) +from libcloud.utils.py3 import httplib +from libcloud.utils.py3 import PY3 +from libcloud.utils.py3 import StringIO +from libcloud.utils.py3 import u +from libcloud.utils.py3 import b + + +from libcloud.utils.misc import lowercase_keys +from libcloud.utils.compression import decompress_data + + - class LoggingBaseConnection(): ++class LoggingBaseConnection(LibcloudConnection): + """ + Debug class to log all HTTP(s) requests as they could be made + with the curl command. + + :cvar log: file-like object that logs entries are written to. + """ + + log = None + http_proxy_used = False + + def _log_response(self, r): + rv = "# -------- begin %d:%d response ----------\n" % (id(self), id(r)) + ht = "" + v = r.version + if r.version == 10: + v = "HTTP/1.0" + if r.version == 11: + v = "HTTP/1.1" + ht += "%s %s %s\r\n" % (v, r.status, r.reason) + body = r.read() + for h in r.getheaders(): + ht += "%s: %s\r\n" % (h[0].title(), h[1]) + ht += "\r\n" + + # this is evil. laugh with me. ha arharhrhahahaha + class fakesock(object): + def __init__(self, s): + self.s = s + + def makefile(self, *args, **kwargs): + if PY3: + from io import BytesIO + cls = BytesIO + else: + cls = StringIO + + return cls(b(self.s)) + rr = r + headers = lowercase_keys(dict(r.getheaders())) + + encoding = headers.get('content-encoding', None) + content_type = headers.get('content-type', None) + + if encoding in ['zlib', 'deflate']: + body = decompress_data('zlib', body) + elif encoding in ['gzip', 'x-gzip']: + body = decompress_data('gzip', body) + + pretty_print = os.environ.get('LIBCLOUD_DEBUG_PRETTY_PRINT_RESPONSE', + False) + + if r.chunked: + ht += "%x\r\n" % (len(body)) + ht += body.decode('utf-8') + ht += "\r\n0\r\n" + else: + if pretty_print and content_type == 'application/json': + try: + body = json.loads(body.decode('utf-8')) + body = json.dumps(body, sort_keys=True, indent=4) + except: + # Invalid JSON or server is lying about content-type + pass + elif pretty_print and content_type == 'text/xml': + try: + elem = parseString(body.decode('utf-8')) + body = elem.toprettyxml() + except Exception: + # Invalid XML + pass + + ht += u(body) + + if sys.version_info >= (2, 6) and sys.version_info < (2, 7): + cls = HTTPResponse + else: + cls = httplib.HTTPResponse + + rr = cls(sock=fakesock(ht), method=r._method, + debuglevel=r.debuglevel) + rr.begin() + rv += ht + rv += ("\n# -------- end %d:%d response ----------\n" + % (id(self), id(r))) + + rr._original_data = body + return (rr, rv) + + def _log_curl(self, method, url, body, headers): + cmd = ["curl"] + + if self.http_proxy_used: + if self.proxy_username and self.proxy_password: + proxy_url = 'http://%s:%s@%s:%s' % (self.proxy_username, + self.proxy_password, + self.proxy_host, + self.proxy_port) + else: + proxy_url = 'http://%s:%s' % (self.proxy_host, + self.proxy_port) + proxy_url = pquote(proxy_url) + cmd.extend(['--proxy', proxy_url]) + + cmd.extend(['-i']) + + if method.lower() == 'head': + # HEAD method need special handling + cmd.extend(["--head"]) + else: + cmd.extend(["-X", pquote(method)]) + + for h in headers: + cmd.extend(["-H", pquote("%s: %s" % (h, headers[h]))]) + + cert_file = getattr(self, 'cert_file', None) + + if cert_file: + cmd.extend(["--cert", pquote(cert_file)]) + + # TODO: in python 2.6, body can be a file-like object. + if body is not None and len(body) > 0: + cmd.extend(["--data-binary", pquote(body)]) + + cmd.extend(["--compress"]) + cmd.extend([pquote("%s://%s:%d%s" % (self.protocol, self.host, + self.port, url))]) + return " ".join(cmd) + + - class LoggingConnection(LoggingBaseConnection, LibcloudConnection): ++class LoggingConnection(LoggingBaseConnection): + """ + Utility Class for logging HTTPS connections + """ + + protocol = 'https' + port = None + + def getresponse(self): + r = LibcloudConnection.getresponse(self) + if self.log is not None: + r, rv = self._log_response(r) + self.log.write(rv + "\n") + self.log.flush() + return r + + def request(self, method, url, body=None, headers=None): + headers.update({'X-LC-Request-ID': str(id(self))}) + if self.log is not None: + pre = "# -------- begin %d request ----------\n" % id(self) + self.log.write(pre + + self._log_curl(method, url, body, headers) + "\n") + self.log.flush() + return LibcloudConnection.request(self, method, url, body, + headers) http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/requirements-tests.txt ---------------------------------------------------------------------- diff --cc requirements-tests.txt index 35db592,b409664..0fb1567 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@@ -4,6 -4,4 +4,6 @@@ astroid>=1.4.5,<1. pylint>=1.5.5,<1.6 mock>=1.0.1,<1.1 coveralls -coverage<4.0 +coverage<4.0 - requests ++requests<=1.12.4 +requests_mock http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/setup.py ---------------------------------------------------------------------- diff --cc setup.py index 85b750f,c778cce..1e05352 --- a/setup.py +++ b/setup.py @@@ -57,11 -57,10 +57,11 @@@ DOC_TEST_MODULES = ['libcloud.compute.d 'libcloud.container.drivers.dummy', 'libcloud.backup.drivers.dummy'] --SUPPORTED_VERSIONS = ['2.5', '2.6', '2.7', 'PyPy', '3.x'] ++SUPPORTED_VERSIONS = ['2.6', '2.7', 'PyPy', '3.x'] TEST_REQUIREMENTS = [ - 'mock' + 'mock', + 'requests' ] if PY2_pre_279 or PY3_pre_32: @@@ -293,13 -292,13 +293,9 @@@ setup 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Software Development :: Libraries :: Python Modules', -- 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', -- 'Programming Language :: Python :: 3.0', -- 'Programming Language :: Python :: 3.1', -- 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/tox.ini ---------------------------------------------------------------------- diff --cc tox.ini index 248b102,e41a912..85aa177 --- a/tox.ini +++ b/tox.ini @@@ -20,7 -20,7 +20,6 @@@ basepython {py2.7,lint,pylint,docs}: python2.7 pypypy: pypy pypypy3: pypy3 -- py3.2: python3.2 py3.3: python3.3 py3.4: python3.4 py3.5: python3.5 @@@ -48,9 -49,9 +48,6 @@@ deps = -r{toxinidir}/requirements-tests [testenv:pypypy3-lxml] deps = -r{toxinidir}/requirements-tests.txt lxml --[testenv:py3.2-lxml] --deps = -r{toxinidir}/requirements-tests.txt -- lxml [testenv:py3.3-lxml] deps = -r{toxinidir}/requirements-tests.txt lxml