# 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 tempfile import time import yaml from oslo_utils import timeutils from heat_integrationtests.common import exceptions 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 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): 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) 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': None}, 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) 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) 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) fd, temp_path = tempfile.mkstemp() os.write(fd, json.dumps(data['zaqar']['deployments'][0])) os.close(fd) cmd = ['python', 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!'})) 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)