Merge "Merge Neutron AutoScaling and LoadBalancer tests"
diff --git a/scenario/templates/app_server_neutron.yaml b/scenario/templates/app_server_neutron.yaml
new file mode 100644
index 0000000..9cbf82a
--- /dev/null
+++ b/scenario/templates/app_server_neutron.yaml
@@ -0,0 +1,65 @@
+heat_template_version: 2015-10-15
+
+description: |
+ App server that is a member of Neutron Pool.
+
+parameters:
+
+ image:
+ type: string
+
+ flavor:
+ type: string
+
+ net:
+ type: string
+
+ sec_group:
+ type: string
+
+ pool_id:
+ type: string
+
+ app_port:
+ type: number
+
+ timeout:
+ type: number
+
+resources:
+
+ config:
+ type: OS::Test::WebAppConfig
+ properties:
+ app_port: { get_param: app_port }
+ wc_curl_cli: { get_attr: [ handle, curl_cli ] }
+
+ server:
+ type: OS::Nova::Server
+ properties:
+ image: { get_param: image }
+ flavor: { get_param: flavor }
+ networks:
+ - network: { get_param: net }
+ security_groups:
+ - { get_param: sec_group }
+ user_data_format: RAW
+ user_data: { get_resource: config }
+
+ handle:
+ type: OS::Heat::WaitConditionHandle
+
+ waiter:
+ type: OS::Heat::WaitCondition
+ depends_on: server
+ properties:
+ timeout: { get_param: timeout }
+ handle: { get_resource: handle }
+
+ pool_member:
+ type: OS::Neutron::PoolMember
+ depends_on: waiter
+ properties:
+ address: { get_attr: [ server, networks, { get_param: net }, 0 ] }
+ pool_id: { get_param: pool_id }
+ protocol_port: { get_param: app_port }
diff --git a/scenario/templates/netcat-webapp.yaml b/scenario/templates/netcat-webapp.yaml
new file mode 100644
index 0000000..fdb0335
--- /dev/null
+++ b/scenario/templates/netcat-webapp.yaml
@@ -0,0 +1,35 @@
+heat_template_version: 2015-10-15
+
+description: |
+ Simplest web-app using netcat reporting only hostname.
+ Specifically tailored for minimal Cirros image.
+
+parameters:
+
+ app_port:
+ type: number
+
+ wc_curl_cli:
+ type: string
+
+resources:
+
+ webapp_nc:
+ type: OS::Heat::SoftwareConfig
+ properties:
+ group: ungrouped
+ config:
+ str_replace:
+ template: |
+ #! /bin/sh -v
+ Body=$(hostname)
+ Response="HTTP/1.1 200 OK\r\nContent-Length: ${#Body}\r\n\r\n$Body"
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ while true ; do echo -e $Response | nc -llp PORT; done
+ params:
+ PORT: { get_param: app_port }
+ wc_notify: { get_param: wc_curl_cli }
+
+outputs:
+ OS::stack_id:
+ value: { get_resource: webapp_nc }
diff --git a/scenario/templates/test_autoscaling_lb_neutron.yaml b/scenario/templates/test_autoscaling_lb_neutron.yaml
new file mode 100644
index 0000000..d47e787
--- /dev/null
+++ b/scenario/templates/test_autoscaling_lb_neutron.yaml
@@ -0,0 +1,113 @@
+heat_template_version: 2015-04-30
+
+description: |
+ Template which tests Neutron load balancing requests to members of
+ Heat AutoScalingGroup.
+ Instances must be running some webserver on a given app_port
+ producing HTTP response that is different between servers
+ but stable over time for given server.
+
+parameters:
+ flavor:
+ type: string
+ image:
+ type: string
+ net:
+ type: string
+ subnet:
+ type: string
+ public_net:
+ type: string
+ app_port:
+ type: number
+ default: 8080
+ lb_port:
+ type: number
+ default: 80
+ timeout:
+ type: number
+ default: 600
+
+resources:
+
+ sec_group:
+ type: OS::Neutron::SecurityGroup
+ properties:
+ rules:
+ - remote_ip_prefix: 0.0.0.0/0
+ protocol: tcp
+ port_range_min: { get_param: app_port }
+ port_range_max: { get_param: app_port }
+
+ asg:
+ type: OS::Heat::AutoScalingGroup
+ properties:
+ desired_capacity: 1
+ max_size: 2
+ min_size: 1
+ resource:
+ type: OS::Test::NeutronAppServer
+ properties:
+ image: { get_param: image }
+ flavor: { get_param: flavor }
+ net: { get_param: net}
+ sec_group: { get_resource: sec_group }
+ app_port: { get_param: app_port }
+ pool_id: { get_resource: pool }
+ timeout: { get_param: timeout }
+
+ scale_up:
+ type: OS::Heat::ScalingPolicy
+ properties:
+ adjustment_type: change_in_capacity
+ auto_scaling_group_id: { get_resource: asg }
+ scaling_adjustment: 1
+
+ scale_down:
+ type: OS::Heat::ScalingPolicy
+ properties:
+ adjustment_type: change_in_capacity
+ auto_scaling_group_id: { get_resource: asg }
+ scaling_adjustment: -1
+
+ health_monitor:
+ type: OS::Neutron::HealthMonitor
+ properties:
+ delay: 3
+ type: HTTP
+ timeout: 3
+ max_retries: 3
+
+ pool:
+ type: OS::Neutron::Pool
+ properties:
+ lb_method: ROUND_ROBIN
+ protocol: HTTP
+ subnet: { get_param: subnet }
+ monitors:
+ - { get_resource: health_monitor }
+ vip:
+ protocol_port: { get_param: lb_port }
+
+ floating_ip:
+ type: OS::Neutron::FloatingIP
+ properties:
+ floating_network: { get_param: public_net }
+ port_id:
+ { get_attr: [pool, vip, 'port_id'] }
+
+ loadbalancer:
+ type: OS::Neutron::LoadBalancer
+ properties:
+ pool_id: { get_resource: pool }
+ protocol_port: { get_param: app_port }
+
+outputs:
+ lburl:
+ description: URL of the loadbalanced app
+ value:
+ str_replace:
+ template: http://IP_ADDRESS:PORT
+ params:
+ IP_ADDRESS: { get_attr: [ floating_ip, floating_ip_address ] }
+ PORT: { get_param: lb_port }
diff --git a/scenario/templates/test_neutron_autoscaling.yaml b/scenario/templates/test_neutron_autoscaling.yaml
deleted file mode 100644
index a34ec43..0000000
--- a/scenario/templates/test_neutron_autoscaling.yaml
+++ /dev/null
@@ -1,56 +0,0 @@
-heat_template_version: 2014-10-16
-
-description: Auto-scaling Test
-
-parameters:
- image_id:
- type: string
- label: Image ID
- description: Image ID from configurations
- capacity:
- type: string
- label: Capacity
- description: Auto-scaling group desired capacity
- fixed_subnet:
- type: string
- label: fixed subnetwork ID
- description: subnetwork ID used for autoscaling
- instance_type:
- type: string
- label: instance_type
- description: type of instance to launch
-
-resources:
- test_pool:
- type: OS::Neutron::Pool
- properties:
- description: Test Pool
- lb_method: ROUND_ROBIN
- name: test_pool
- protocol: HTTP
- subnet: { get_param: fixed_subnet }
- vip: {
- "description": "Test VIP",
- "protocol_port": 80,
- "name": "test_vip"
- }
- load_balancer:
- type: OS::Neutron::LoadBalancer
- properties:
- protocol_port: 80
- pool_id: { get_resource: test_pool }
- launch_config:
- type: AWS::AutoScaling::LaunchConfiguration
- properties:
- ImageId: { get_param: image_id }
- InstanceType: { get_param: instance_type }
- server_group:
- type: AWS::AutoScaling::AutoScalingGroup
- properties:
- AvailabilityZones : ["nova"]
- LaunchConfigurationName : { get_resource : launch_config }
- VPCZoneIdentifier: [{ get_param: fixed_subnet }]
- MinSize : 1
- MaxSize : 5
- DesiredCapacity: { get_param: capacity }
- LoadBalancerNames : [ { get_resource : load_balancer } ]
diff --git a/scenario/templates/test_neutron_loadbalancer.yaml b/scenario/templates/test_neutron_loadbalancer.yaml
deleted file mode 100644
index dd659d0..0000000
--- a/scenario/templates/test_neutron_loadbalancer.yaml
+++ /dev/null
@@ -1,133 +0,0 @@
-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
- network:
- type: string
- private_subnet_id:
- type: string
- external_network_id:
- type: string
- port:
- type: string
- default: '80'
- timeout:
- type: number
-
-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
-
- wait_condition:
- type: OS::Heat::WaitCondition
- properties:
- handle: { get_resource: wait_condition_handle }
- count: 2
- timeout: { get_param: timeout }
-
- wait_condition_handle:
- type: OS::Heat::WaitConditionHandle
-
- config:
- type: OS::Heat::SoftwareConfig
- properties:
- group: ungrouped
- config:
- str_replace:
- template: |
- #!/bin/bash -v
- echo $(hostname) > index.html
- python -m SimpleHTTPServer port &
- wc_notify --data-binary '{"status": "SUCCESS"}'
- params:
- wc_notify: { get_attr: ['wait_condition_handle', 'curl_cli'] }
- port: { get_param: port }
-
- server1:
- type: OS::Nova::Server
- properties:
- name: Server1
- image: { get_param: image }
- flavor: { get_param: flavor }
- key_name: { get_param: key_name }
- networks: [{network: {get_param: network} }]
- security_groups: [{ get_resource: sec_group }]
- user_data_format: SOFTWARE_CONFIG
- user_data: { get_resource: config }
-
- server2:
- type: OS::Nova::Server
- properties:
- name: Server2
- image: { get_param: image }
- flavor: { get_param: flavor }
- key_name: { get_param: key_name }
- networks: [{network: {get_param: network} }]
- security_groups: [{ get_resource: sec_group }]
- user_data_format: SOFTWARE_CONFIG
- user_data: { get_resource: config }
-
- 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'] }
- fixed_ip_address:
- { get_attr: [test_pool, vip, 'address'] }
-
- LBaaS:
- type: OS::Neutron::LoadBalancer
- depends_on: wait_condition
- properties:
- pool_id: { get_resource: test_pool }
- protocol_port: { get_param: port }
- members:
- - { get_resource: server1 }
-
-outputs:
- serv1_ip:
- value: {get_attr: [server1, networks, { get_param: network }, 0]}
- serv2_ip:
- value: {get_attr: [server2, networks, { get_param: network }, 0]}
- vip:
- value: {get_attr: [test_pool, vip, address]}
- fip:
- value: {get_attr: [floating_ip, floating_ip_address]}
diff --git a/scenario/test_autoscaling_lb.py b/scenario/test_autoscaling_lb.py
new file mode 100644
index 0000000..21b27dd
--- /dev/null
+++ b/scenario/test_autoscaling_lb.py
@@ -0,0 +1,114 @@
+#
+# 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 requests
+
+from heat_integrationtests.common import test
+from heat_integrationtests.scenario import scenario_base
+
+
+class AutoscalingLoadBalancerTest(scenario_base.ScenarioTestsBase):
+ """
+ The class is responsible for testing ASG + LB scenario.
+
+ The very common use case tested is an autoscaling group
+ of some web application servers behind a loadbalancer.
+ """
+
+ def setUp(self):
+ super(AutoscalingLoadBalancerTest, self).setUp()
+ self.template_name = 'test_autoscaling_lb_neutron.yaml'
+ self.app_server_template_name = 'app_server_neutron.yaml'
+ self.webapp_template_name = 'netcat-webapp.yaml'
+
+ def check_num_responses(self, url, expected_num, retries=10):
+ resp = set()
+ for count in range(retries):
+ time.sleep(1)
+ r = requests.get(url)
+ # skip unsuccessfull requests
+ if r.status_code == 200:
+ resp.add(r.text)
+ self.assertEqual(expected_num, len(resp))
+
+ def autoscale_complete(self, stack_id, expected_num):
+ res_list = self.client.resources.list(stack_id)
+ all_res_complete = all(res.resource_status in ('UPDATE_COMPLETE',
+ 'CREATE_COMPLETE')
+ for res in res_list)
+ all_res = len(res_list) == expected_num
+ return all_res and all_res_complete
+
+ def test_autoscaling_loadbalancer_neutron(self):
+ """
+ Check work of AutoScaing and Neutron LBaaS resource in Heat.
+
+ The scenario is the following:
+ 1. Launch a stack with a load balancer and autoscaling group
+ of one server, wait until stack create is complete.
+ 2. Check that there is only one distinctive response from
+ loadbalanced IP.
+ 3. Signal the scale_up policy, wait until all resources in
+ autoscaling group are complete.
+ 4. Check that now there are two distinctive responses from
+ loadbalanced IP.
+ """
+
+ parameters = {
+ 'flavor': self.conf.minimal_instance_type,
+ 'image': self.conf.minimal_image_ref,
+ 'net': self.conf.fixed_network_name,
+ 'subnet': self.conf.fixed_subnet_name,
+ 'public_net': self.conf.floating_network_name,
+ 'app_port': 8080,
+ 'lb_port': 80,
+ 'timeout': 600
+ }
+
+ app_server_template = self._load_template(
+ __file__, self.app_server_template_name, self.sub_dir
+ )
+ webapp_template = self._load_template(
+ __file__, self.webapp_template_name, self.sub_dir
+ )
+ files = {'appserver.yaml': app_server_template,
+ 'webapp.yaml': webapp_template}
+ env = {'resource_registry':
+ {'OS::Test::NeutronAppServer': 'appserver.yaml',
+ 'OS::Test::WebAppConfig': 'webapp.yaml'}}
+ # Launch stack
+ sid = self.launch_stack(
+ template_name=self.template_name,
+ parameters=parameters,
+ files=files,
+ environment=env
+ )
+ stack = self.client.stacks.get(sid)
+ lb_url = self._stack_output(stack, 'lburl')
+ # Check number of distinctive responces, must be 1
+ self.check_num_responses(lb_url, 1)
+
+ # Signal the scaling hook
+ self.client.resources.signal(sid, 'scale_up')
+
+ # Wait for AutoScalingGroup update to finish
+ asg = self.client.resources.get(sid, 'asg')
+ test.call_until_true(self.conf.build_timeout,
+ self.conf.build_interval,
+ self.autoscale_complete,
+ asg.physical_resource_id, 2)
+
+ # Check number of distinctive responses, must now be 2
+ self.check_num_responses(lb_url, 2)
diff --git a/scenario/test_neutron_autoscaling.py b/scenario/test_neutron_autoscaling.py
deleted file mode 100644
index e7aae19..0000000
--- a/scenario/test_neutron_autoscaling.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# 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.
-
-from heat_integrationtests.scenario import scenario_base
-
-
-class NeutronAutoscalingTest(scenario_base.ScenarioTestsBase):
- """
- The class is responsible for testing of neutron resources autoscaling.
- """
-
- def setUp(self):
- super(NeutronAutoscalingTest, self).setUp()
- if not self.conf.fixed_subnet_name:
- raise self.skipException("No sub-network configured to test")
- self.template_name = 'test_neutron_autoscaling.yaml'
-
- def test_neutron_autoscaling(self):
- """
- Check autoscaling of load balancer members in Heat.
-
- The alternative scenario is the following:
- 1. Launch a stack with a load balancer.
- 2. Check that the load balancer created
- one load balancer member for stack.
- 3. Update stack definition: increase desired capacity of stack.
- 4. Check that number of members in load balancer was increased.
- """
-
- parameters = {
- "image_id": self.conf.minimal_image_ref,
- "capacity": "1",
- "instance_type": self.conf.minimal_instance_type,
- "fixed_subnet": self.net['subnets'][0],
- }
-
- # Launch stack
- stack_id = self.launch_stack(
- template_name=self.template_name,
- parameters=parameters
- )
-
- # Check number of members
- pool_resource = self.client.resources.get(stack_id, 'test_pool')
- pool_members = self.network_client.list_members(
- pool_id=pool_resource.physical_resource_id)['members']
- self.assertEqual(1, len(pool_members))
-
- # Increase desired capacity and update the stack
- template = self._load_template(
- __file__, self.template_name, self.sub_dir
- )
- parameters["capacity"] = "2"
- self.update_stack(
- stack_id,
- template=template,
- parameters=parameters
- )
-
- # Check number of members
- pool_members = self.network_client.list_members(
- pool_id=pool_resource.physical_resource_id)['members']
- self.assertEqual(2, len(pool_members))
diff --git a/scenario/test_neutron_loadbalancer.py b/scenario/test_neutron_loadbalancer.py
deleted file mode 100644
index d8e0197..0000000
--- a/scenario/test_neutron_loadbalancer.py
+++ /dev/null
@@ -1,108 +0,0 @@
-#
-# 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
-
-from six.moves 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(self.conf.floating_network_name)
- 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.request.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.minimal_instance_type,
- 'image': self.conf.image_ref,
- 'network': self.net['name'],
- 'private_subnet_id': self.net['subnets'][0],
- 'external_network_id': self.public_net['id'],
- 'timeout': self.conf.build_timeout
- }
-
- # Launch stack
- sid = self.launch_stack(
- template_name=self.template_name,
- parameters=parameters
- )
-
- stack = self.client.stacks.get(sid)
- floating_ip = self._stack_output(stack, 'fip')
- vip = self._stack_output(stack, 'vip')
- server1_ip = self._stack_output(stack, 'serv1_ip')
- server2_ip = self._stack_output(stack, 'serv2_ip')
- # Check connection and info about received responses
- 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'})