| # 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 json |
| import os |
| import requests |
| import subprocess |
| import sys |
| import tempfile |
| import time |
| import yaml |
| |
| from oslo_utils import timeutils |
| |
| from heat_integrationtests.common import exceptions |
| from heat_integrationtests.common import test |
| from heat_integrationtests.functional import functional_base |
| |
| |
| class ParallelDeploymentsTest(functional_base.FunctionalTestsBase): |
| server_template = ''' |
| heat_template_version: "2013-05-23" |
| parameters: |
| flavor: |
| type: string |
| image: |
| type: string |
| network: |
| type: string |
| resources: |
| server: |
| type: OS::Nova::Server |
| properties: |
| image: {get_param: image} |
| flavor: {get_param: flavor} |
| user_data_format: SOFTWARE_CONFIG |
| networks: [{network: {get_param: network}}] |
| outputs: |
| server: |
| value: {get_resource: server} |
| ''' |
| |
| config_template = ''' |
| heat_template_version: "2013-05-23" |
| parameters: |
| server: |
| type: string |
| resources: |
| config: |
| type: OS::Heat::SoftwareConfig |
| properties: |
| ''' |
| |
| deployment_snippet = ''' |
| type: OS::Heat::SoftwareDeployments |
| properties: |
| config: {get_resource: config} |
| servers: {'0': {get_param: server}} |
| ''' |
| |
| enable_cleanup = True |
| |
| def test_deployments_metadata(self): |
| parms = {'flavor': self.conf.minimal_instance_type, |
| 'network': self.conf.fixed_network_name, |
| 'image': self.conf.minimal_image_ref} |
| stack_identifier = self.stack_create( |
| parameters=parms, |
| template=self.server_template, |
| enable_cleanup=self.enable_cleanup) |
| server_stack = self.client.stacks.get(stack_identifier) |
| server = server_stack.outputs[0]['output_value'] |
| |
| config_stacks = [] |
| # add up to 3 stacks each with up to 3 deployments |
| deploy_count = 0 |
| deploy_count = self.deploy_many_configs( |
| stack_identifier, |
| server, |
| config_stacks, |
| 2, |
| 5, |
| deploy_count) |
| self.deploy_many_configs( |
| stack_identifier, |
| server, |
| config_stacks, |
| 3, |
| 3, |
| deploy_count) |
| |
| self.signal_deployments(stack_identifier) |
| for config_stack in config_stacks: |
| self._wait_for_stack_status(config_stack, 'CREATE_COMPLETE') |
| |
| def test_deployments_timeout_failed(self): |
| parms = {'flavor': self.conf.minimal_instance_type, |
| 'network': self.conf.fixed_network_name, |
| 'image': self.conf.minimal_image_ref} |
| stack_identifier = self.stack_create( |
| parameters=parms, |
| template=self.server_template, |
| enable_cleanup=self.enable_cleanup) |
| server_stack = self.client.stacks.get(stack_identifier) |
| server = server_stack.outputs[0]['output_value'] |
| config_stack = self.deploy_config(server, 3, 1) |
| self._wait_for_stack_status(config_stack, 'CREATE_FAILED') |
| kwargs = {'server_id': server} |
| |
| def check_deployment_status(): |
| sd_list = self.client.software_deployments.list(**kwargs) |
| for sd in sd_list: |
| if sd.status != 'FAILED': |
| return False |
| return True |
| |
| self.assertTrue(test.call_until_true( |
| 20, 0, check_deployment_status)) |
| |
| def deploy_many_configs(self, stack, server, config_stacks, |
| stack_count, deploys_per_stack, |
| deploy_count_start): |
| for a in range(stack_count): |
| config_stacks.append( |
| self.deploy_config(server, deploys_per_stack)) |
| |
| new_count = deploy_count_start + stack_count * deploys_per_stack |
| self.wait_for_deploy_metadata_set(stack, new_count) |
| return new_count |
| |
| def deploy_config(self, server, deploy_count, timeout=None): |
| parms = {'server': server} |
| template = yaml.safe_load(self.config_template) |
| resources = template['resources'] |
| resources['config']['properties'] = {'config': 'x' * 10000} |
| for a in range(deploy_count): |
| resources['dep_%s' % a] = yaml.safe_load(self.deployment_snippet) |
| return self.stack_create( |
| parameters=parms, |
| template=template, |
| enable_cleanup=self.enable_cleanup, |
| expected_status=None, |
| timeout=timeout) |
| |
| def wait_for_deploy_metadata_set(self, stack, deploy_count): |
| build_timeout = self.conf.build_timeout |
| build_interval = self.conf.build_interval |
| |
| start = timeutils.utcnow() |
| while timeutils.delta_seconds(start, |
| timeutils.utcnow()) < build_timeout: |
| server_metadata = self.client.resources.metadata( |
| stack, 'server') |
| if len(server_metadata['deployments']) == deploy_count: |
| return |
| time.sleep(build_interval) |
| |
| message = ('Deployment resources failed to be created within ' |
| 'the required time (%s s).' % |
| (build_timeout)) |
| raise exceptions.TimeoutException(message) |
| |
| def signal_deployments(self, stack_identifier): |
| server_metadata = self.client.resources.metadata( |
| stack_identifier, 'server') |
| for dep in server_metadata['deployments']: |
| iv = dict((i['name'], i['value']) for i in dep['inputs']) |
| sigurl = iv.get('deploy_signal_id') |
| requests.post(sigurl, data='{}', |
| headers={'content-type': 'application/json'}, |
| verify=self.verify_cert) |
| |
| |
| class ZaqarSignalTransportTest(functional_base.FunctionalTestsBase): |
| server_template = ''' |
| heat_template_version: "2013-05-23" |
| |
| parameters: |
| flavor: |
| type: string |
| image: |
| type: string |
| network: |
| type: string |
| |
| resources: |
| server: |
| type: OS::Nova::Server |
| properties: |
| image: {get_param: image} |
| flavor: {get_param: flavor} |
| user_data_format: SOFTWARE_CONFIG |
| software_config_transport: ZAQAR_MESSAGE |
| networks: [{network: {get_param: network}}] |
| config: |
| type: OS::Heat::SoftwareConfig |
| properties: |
| config: echo 'foo' |
| deployment: |
| type: OS::Heat::SoftwareDeployment |
| properties: |
| config: {get_resource: config} |
| server: {get_resource: server} |
| signal_transport: ZAQAR_SIGNAL |
| |
| outputs: |
| data: |
| value: {get_attr: [deployment, deploy_stdout]} |
| ''' |
| |
| conf_template = ''' |
| [zaqar] |
| user_id = %(user_id)s |
| password = %(password)s |
| project_id = %(project_id)s |
| auth_url = %(auth_url)s |
| queue_id = %(queue_id)s |
| ''' |
| |
| def test_signal_queues(self): |
| parms = {'flavor': self.conf.minimal_instance_type, |
| 'network': self.conf.fixed_network_name, |
| 'image': self.conf.minimal_image_ref} |
| stack_identifier = self.stack_create( |
| parameters=parms, |
| template=self.server_template, |
| expected_status=None) |
| metadata = self.wait_for_deploy_metadata_set(stack_identifier) |
| config = metadata['os-collect-config']['zaqar'] |
| conf_content = self.conf_template % config |
| fd, temp_path = tempfile.mkstemp() |
| os.write(fd, conf_content.encode('utf-8')) |
| os.close(fd) |
| cmd = ['os-collect-config', '--one-time', |
| '--config-file=%s' % temp_path, 'zaqar'] |
| proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
| stdout_value = proc.communicate()[0] |
| data = json.loads(stdout_value.decode('utf-8')) |
| self.assertEqual(config, data['zaqar']['os-collect-config']['zaqar']) |
| proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
| stdout_value = proc.communicate()[0] |
| data = json.loads(stdout_value.decode('utf-8')) |
| |
| fd, temp_path = tempfile.mkstemp() |
| os.write(fd, |
| json.dumps(data['zaqar']['deployments'][0]).encode('utf-8')) |
| os.close(fd) |
| cmd = [sys.executable, self.conf.heat_config_notify_script, temp_path] |
| proc = subprocess.Popen(cmd, |
| stderr=subprocess.PIPE, |
| stdin=subprocess.PIPE) |
| proc.communicate( |
| json.dumps({'deploy_stdout': 'here!'}).encode('utf-8')) |
| self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE') |
| stack = self.client.stacks.get(stack_identifier) |
| self.assertEqual('here!', stack.outputs[0]['output_value']) |
| |
| def wait_for_deploy_metadata_set(self, stack): |
| build_timeout = self.conf.build_timeout |
| build_interval = self.conf.build_interval |
| |
| start = timeutils.utcnow() |
| while timeutils.delta_seconds(start, |
| timeutils.utcnow()) < build_timeout: |
| server_metadata = self.client.resources.metadata( |
| stack, 'server') |
| if server_metadata.get('deployments'): |
| return server_metadata |
| time.sleep(build_interval) |
| |
| message = ('Deployment resources failed to be created within ' |
| 'the required time (%s s).' % |
| (build_timeout)) |
| raise exceptions.TimeoutException(message) |