diff --git a/translator/shell.py b/translator/shell.py index b26814a0..27ae1ff5 100644 --- a/translator/shell.py +++ b/translator/shell.py @@ -105,6 +105,12 @@ class TranslatorShell(object): help=_('Whether to deploy the generated template ' 'or not.')) + parser.add_argument('--stack-name', + metavar='', + required=False, + help=_('The name to use for the Heat stack when ' + 'deploy the generated template.')) + self._append_global_identity_args(parser, argv) return parser @@ -131,6 +137,7 @@ class TranslatorShell(object): output_file = args.output_file validate_only = args.validate_only deploy = args.deploy + stack_name = args.stack_name parsed_params = {} if args.parameters: @@ -172,8 +179,11 @@ class TranslatorShell(object): 'Keystone to deploy on Heat, ' 'please check your credentials')) + file_name = os.path.basename( + os.path.splitext(template_file)[0]) self.deploy_on_heat(keystone_session, keystone_auth, - hot, parsed_params) + hot, stack_name, file_name, + parsed_params) self._write_output(hot, output_file) else: @@ -183,19 +193,31 @@ class TranslatorShell(object): log.error(msg) raise ValueError(msg) - def deploy_on_heat(self, session, auth, template, parameters): + def deploy_on_heat(self, session, auth, template, + stack_name, file_name, parameters): endpoint = auth.get_endpoint(session, service_type="orchestration") - client = heatclient.client.Client('1', - session=session, - auth=auth, - endpoint=endpoint) + heat_client = heatclient.client.Client('1', + session=session, + auth=auth, + endpoint=endpoint) - stack_name = "heat_" + str(uuid.uuid4()).split("-")[0] + heat_stack_name = stack_name if stack_name else \ + 'heat_' + file_name + '_' + str(uuid.uuid4()).split("-")[0] + msg = _('Deploy the generated template, the stack name is %(name)s.')\ + % {'name': heat_stack_name} + log.debug(msg) tpl = yaml.load(template) tpl['heat_template_version'] = str(tpl['heat_template_version']) - client.stacks.create(stack_name=stack_name, - template=tpl, - parameters=parameters) + self._create_stack(heat_client=heat_client, + stack_name=heat_stack_name, + template=tpl, + parameters=parameters) + + def _create_stack(self, heat_client, stack_name, template, parameters): + if heat_client: + self.heat_client.stacks.create(stack_name=stack_name, + template=template, + parameters=parameters) def _parse_parameters(self, parameter_list): parsed_inputs = {} diff --git a/translator/tests/test_shell.py b/translator/tests/test_shell.py index 7f8e11ed..c4570cac 100644 --- a/translator/tests/test_shell.py +++ b/translator/tests/test_shell.py @@ -10,6 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. + +import json +import mock import os import shutil import tempfile @@ -94,3 +97,71 @@ class ShellTest(TestCase): shutil.rmtree(temp_dir) self.assertTrue(temp_dir is None or not os.path.exists(temp_dir)) + + @mock.patch('uuid.uuid4') + @mock.patch.object(shell.TranslatorShell, '_create_stack') + @mock.patch('keystoneauth1.loading.load_auth_from_argparse_arguments') + @mock.patch('keystoneauth1.loading.load_session_from_argparse_arguments') + @mock.patch('translator.common.flavors.get_flavors') + @mock.patch('translator.common.images.get_images') + def test_template_deploy(self, mock_populate_image_dict, + mock_flavor_dict, + mock_keystone_session, + mock_keystone_auth, + mock_client, + mock_uuid): + mock_uuid.return_value = 'abcXXX-abcXXX' + mock_flavor_dict.return_value = { + 'm1.medium': {'mem_size': 4096, 'disk_size': 40, 'num_cpus': 2} + } + mock_populate_image_dict.return_value = { + "rhel-6.5-test-image": { + "version": "6.5", + "architecture": "x86_64", + "distribution": "RHEL", + "type": "Linux" + } + } + + try: + data = { + 'outputs': {}, + 'heat_template_version': '2013-05-23', + 'description': 'Template for deploying a single server ' + 'with predefined properties.\n', + 'parameters': {}, + 'resources': { + 'my_server': { + 'type': 'OS::Nova::Server', + 'properties': { + 'flavor': 'm1.medium', + 'user_data_format': 'SOFTWARE_CONFIG', + 'image': 'rhel-6.5-test-image' + } + } + } + } + + mock_heat_res = { + "stacks": [ + { + "id": "d648ad27-fb9c-44d1-b293-646ea6c4f8da", + "stack_status": "CREATE_IN_PROGRESS", + } + ] + } + + class mock_response(object): + def __init__(self, status_code, _content): + self.status_code = status_code + self._content = _content + + mock_response_obj = mock_response(201, json.dumps(mock_heat_res)) + mock_client.return_value = mock_response_obj + shell.main([self.template_file, self.template_type, "--deploy"]) + args, kwargs = mock_client.call_args + self.assertEqual(kwargs["stack_name"], + 'heat_tosca_helloworld_abcXXX') + self.assertEqual(kwargs["template"], data) + except Exception as e: + self.fail(e)