diff --git a/heat_integrationtests/common/config.py b/heat_integrationtests/common/config.py index 878cd33c2..158d08788 100644 --- a/heat_integrationtests/common/config.py +++ b/heat_integrationtests/common/config.py @@ -103,6 +103,10 @@ IntegrationTestGroup = [ cfg.BoolOpt('skip_stack_abandon_tests', default=False, help="Skip Stack Abandon Integration tests"), + cfg.IntOpt('connectivity_timeout', + default=120, + help="Timeout in seconds to wait for connectivity to " + "server."), ] diff --git a/heat_integrationtests/common/test.py b/heat_integrationtests/common/test.py index 26abe6b68..b2ce6d6c8 100644 --- a/heat_integrationtests/common/test.py +++ b/heat_integrationtests/common/test.py @@ -15,6 +15,7 @@ import random import re import subprocess import time +import urllib import fixtures from heatclient import exc as heat_exceptions @@ -107,6 +108,22 @@ class HeatIntegrationTest(testscenarios.WithScenarios, return linux_client + def check_connectivity(self, check_ip): + def try_connect(ip): + try: + urllib.urlopen('http://%s/' % ip) + return True + except IOError: + return False + + timeout = self.conf.connectivity_timeout + elapsed_time = 0 + while not try_connect(check_ip): + time.sleep(10) + elapsed_time += 10 + if elapsed_time > timeout: + raise exceptions.TimeoutException() + def _log_console_output(self, servers=None): if not servers: servers = self.compute_client.servers.list() diff --git a/heat_integrationtests/heat_integrationtests.conf.sample b/heat_integrationtests/heat_integrationtests.conf.sample index d887779dc..66fdd52cb 100644 --- a/heat_integrationtests/heat_integrationtests.conf.sample +++ b/heat_integrationtests/heat_integrationtests.conf.sample @@ -78,3 +78,6 @@ # Skip Stack Abandon Integration tests (boolean value) #skip_stack_abandon_tests = false + +# Timeout in seconds to wait for connectivity to server +#connectivity_timeout = 120 diff --git a/heat_integrationtests/scenario/templates/test_neutron_loadbalancer.yaml b/heat_integrationtests/scenario/templates/test_neutron_loadbalancer.yaml new file mode 100644 index 000000000..fad7db88b --- /dev/null +++ b/heat_integrationtests/scenario/templates/test_neutron_loadbalancer.yaml @@ -0,0 +1,108 @@ +heat_template_version: 2014-10-16 + +description: | + Template which tests neutron load balancing resources + +parameters: + key_name: + type: string + flavor: + type: string + image: + type: string + private_subnet_id: + type: string + external_network_id: + type: string + port: + type: string + default: '80' + +resources: + sec_group: + type: OS::Neutron::SecurityGroup + properties: + description: Add security group rules for servers + name: security-group + rules: + - remote_ip_prefix: 0.0.0.0/0 + protocol: tcp + port_range_min: { get_param: port } + port_range_max: { get_param: port } + - remote_ip_prefix: 0.0.0.0/0 + protocol: icmp + + server1 : + type: OS::Nova::Server + properties: + name: Server1 + image: { get_param: image } + flavor: { get_param: flavor } + key_name: { get_param: key_name } + security_groups: [{ get_resource: sec_group }] + user_data: + list_join: + - '' + - - '#!/bin/bash -v + + ' + - 'echo $(hostname) > index.html + + ' + - 'python -m SimpleHTTPServer ' + - { get_param: port } + + server2 : + type: OS::Nova::Server + properties: + name: Server2 + image: { get_param: image } + flavor: { get_param: flavor } + key_name: { get_param: key_name } + security_groups: [{ get_resource: sec_group }] + user_data: + list_join: + - '' + - - '#!/bin/bash -v + + ' + - 'echo $(hostname) > index.html + + ' + - 'python -m SimpleHTTPServer ' + - { get_param: port } + + health_monitor: + type: OS::Neutron::HealthMonitor + properties: + delay: 3 + type: HTTP + timeout: 3 + max_retries: 3 + + test_pool: + type: OS::Neutron::Pool + properties: + lb_method: ROUND_ROBIN + protocol: HTTP + subnet: { get_param: private_subnet_id } + monitors: + - { get_resource: health_monitor } + vip: + protocol_port: { get_param: port } + + floating_ip: + type: OS::Neutron::FloatingIP + properties: + floating_network: { get_param: external_network_id } + port_id: + { get_attr: [test_pool, vip, 'port_id'] } + + LBaaS: + type: OS::Neutron::LoadBalancer + properties: + pool_id: { get_resource: test_pool } + protocol_port: { get_param: port } + members: + - { get_resource: server1 } + diff --git a/heat_integrationtests/scenario/test_neutron_loadbalancer.py b/heat_integrationtests/scenario/test_neutron_loadbalancer.py new file mode 100644 index 000000000..bc2d77c61 --- /dev/null +++ b/heat_integrationtests/scenario/test_neutron_loadbalancer.py @@ -0,0 +1,118 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import time +import urllib + +from heat_integrationtests.scenario import scenario_base + + +class NeutronLoadBalancerTest(scenario_base.ScenarioTestsBase): + """ + The class is responsible for testing of neutron resources balancer. + """ + + def setUp(self): + super(NeutronLoadBalancerTest, self).setUp() + self.public_net = self._get_network('public') + self.template_name = 'test_neutron_loadbalancer.yaml' + + def collect_responses(self, ip, expected_resp): + resp = set() + for count in range(10): + time.sleep(1) + resp.add(urllib.urlopen('http://%s/' % ip).read()) + + self.assertEqual(expected_resp, resp) + + def test_neutron_loadbalancer(self): + """ + Check work of Neutron LBaaS resource in Heat. + + The alternative scenario is the following: + 1. Launch a stack with a load balancer, two servers, + but use only one as a LB member. + 2. Check connection to the servers and LB. + 3. Collect info about responces, which were received by LB from + its members (responces have to be received only from 'server1'). + 4. Update stack definition: include 'server2' into LBaaS. + 5. Check that number of members in LB was increased and + responces were received from 'server1' and 'server2'. + """ + + parameters = { + 'key_name': self.keypair_name, + 'flavor': self.conf.instance_type, + 'image': self.conf.image_ref, + 'private_subnet_id': self.net['subnets'][0], + 'external_network_id': self.public_net['id'] + } + + # Launch stack + sid = self.launch_stack( + template_name=self.template_name, + parameters=parameters + ) + + server1_id = self.client.resources.get( + sid, 'server1').physical_resource_id + server2_id = self.client.resources.get( + sid, 'server2').physical_resource_id + floating_ip_id = self.client.resources.get( + sid, 'floating_ip').physical_resource_id + floating_ip = self.network_client.show_floatingip( + floating_ip_id)['floatingip']['floating_ip_address'] + pool_id = self.client.resources.get( + sid, 'test_pool').physical_resource_id + vip_id = self.network_client.show_pool(pool_id)['pool']['vip_id'] + + vip = self.network_client.show_vip(vip_id)['vip']['address'] + server1_ip = self.compute_client.servers.get( + server1_id).networks['private'][0] + server2_ip = self.compute_client.servers.get( + server2_id).networks['private'][0] + + # Check connection and info about received responces + self.check_connectivity(server1_ip) + self.collect_responses(server1_ip, {'server1\n'}) + + self.check_connectivity(server2_ip) + self.collect_responses(server2_ip, {'server2\n'}) + + self.check_connectivity(vip) + self.collect_responses(vip, {'server1\n'}) + + self.check_connectivity(floating_ip) + self.collect_responses(floating_ip, {'server1\n'}) + + # Include 'server2' to LB and update the stack + template = self._load_template( + __file__, self.template_name, self.sub_dir + ) + + template = template.replace( + '- { get_resource: server1 }', + '- { get_resource: server1 }\n - { get_resource: server2 }\n' + ) + + self.update_stack( + sid, + template=template, + parameters=parameters + ) + + self.check_connectivity(vip) + self.collect_responses(vip, {'server1\n', 'server2\n'}) + + self.check_connectivity(floating_ip) + self.collect_responses(floating_ip, {'server1\n', 'server2\n'})