| #    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': '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) | 
 |         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) |