Disable py34 in tox temporary
[parser.git] / tosca2heat / heat-translator / translator / shell.py
1 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
2 #    not use this file except in compliance with the License. You may obtain
3 #    a copy of the License at
4 #
5 #         http://www.apache.org/licenses/LICENSE-2.0
6 #
7 #    Unless required by applicable law or agreed to in writing, software
8 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 #    License for the specific language governing permissions and limitations
11 #    under the License.
12
13
14 import argparse
15 import ast
16 import json
17 import logging
18 import logging.config
19 import os
20 import prettytable
21 import requests
22 import sys
23 import uuid
24 import yaml
25
26 from toscaparser.tosca_template import ToscaTemplate
27 from toscaparser.utils.gettextutils import _
28 from toscaparser.utils.urlutils import UrlUtils
29 from translator.common import utils
30 from translator.conf.config import ConfigProvider
31 from translator.hot.tosca_translator import TOSCATranslator
32
33 """
34 Test the heat-translator translation from command line as:
35 #heat-translator
36   --template-file=<path to the YAML template>
37   --template-type=<type of template e.g. tosca>
38   --parameters="purpose=test"
39 Takes three user arguments,
40 1. type of translation (e.g. tosca) (required)
41 2. Path to the file that needs to be translated (required)
42 3. Input parameters (optional)
43
44 In order to use heat-translator to only validate template,
45 without actual translation, pass --validate-only=true along with
46 other required arguments.
47
48 """
49 conf_file = ConfigProvider.get_translator_logging_file()
50 logging.config.fileConfig(conf_file)
51 log = logging.getLogger("heat-translator")
52
53
54 class TranslatorShell(object):
55
56     SUPPORTED_TYPES = ['tosca']
57
58     def get_parser(self):
59         parser = argparse.ArgumentParser(prog="heat-translator")
60
61         parser.add_argument('--template-file',
62                             metavar='<filename>',
63                             required=True,
64                             help=_('Template file to load.'))
65
66         parser.add_argument('--output-file',
67                             metavar='<filename>',
68                             help=_('Where to store the output file. If not '
69                                    'passed, it will be printed to stdin.'))
70
71         parser.add_argument('--template-type',
72                             metavar='<input-template-type>',
73                             choices=self.SUPPORTED_TYPES,
74                             default='tosca',
75                             help=(_('Template type to parse. Choose between '
76                                     '%s.') % self.SUPPORTED_TYPES))
77
78         parser.add_argument('--parameters',
79                             metavar='<param1=val1;param2=val2;...>',
80                             help=_('Optional input parameters.'))
81
82         parser.add_argument('--validate-only',
83                             action='store_true',
84                             default=False,
85                             help=_('Only validate input template, do not '
86                                    'perform translation.'))
87
88         parser.add_argument('--deploy',
89                             action='store_true',
90                             default=False,
91                             help=_('Whether to deploy the generated template '
92                                    'or not.'))
93
94         return parser
95
96     def main(self, argv):
97
98         parser = self.get_parser()
99         (args, args_list) = parser.parse_known_args(argv)
100
101         template_file = args.template_file
102         template_type = args.template_type
103         output_file = args.output_file
104         validate_only = args.validate_only
105         deploy = args.deploy
106
107         parsed_params = {}
108         if args.parameters:
109             parsed_params = self._parse_parameters(args.parameters)
110
111         a_file = os.path.isfile(template_file)
112         a_url = UrlUtils.validate_url(template_file) if not a_file else False
113         if a_file or a_url:
114             if validate_only:
115                 ToscaTemplate(template_file, parsed_params, a_file)
116                 msg = (_('The input "%(template_file)s" successfully passed '
117                          'validation.') % {'template_file': template_file})
118                 print(msg)
119             else:
120                 heat_tpl = self._translate(template_type, template_file,
121                                            parsed_params, a_file, deploy)
122                 if heat_tpl:
123                     if utils.check_for_env_variables() and deploy:
124                         try:
125                             heatclient(heat_tpl, parsed_params)
126                         except Exception:
127                             log.error(_("Unable to launch the heat stack"))
128
129                     self._write_output(heat_tpl, output_file)
130         else:
131             msg = (_('The path %(template_file)s is not a valid '
132                      'file or URL.') % {'template_file': template_file})
133
134             log.error(msg)
135             raise ValueError(msg)
136
137     def _parse_parameters(self, parameter_list):
138         parsed_inputs = {}
139
140         # Parameters are semi-colon separated
141         inputs = parameter_list.replace('"', '').split(';')
142         # Each parameter should be an assignment
143         for param in inputs:
144             keyvalue = param.split('=')
145             # Validate the parameter has both a name and value
146             msg = _("'%(param)s' is not a well-formed parameter.") % {
147                 'param': param}
148             if keyvalue.__len__() is 2:
149                 # Assure parameter name is not zero-length or whitespace
150                 stripped_name = keyvalue[0].strip()
151                 if not stripped_name:
152                     log.error(msg)
153                     raise ValueError(msg)
154                 # Add the valid parameter to the dictionary
155                 parsed_inputs[keyvalue[0]] = keyvalue[1]
156             else:
157                 log.error(msg)
158                 raise ValueError(msg)
159         return parsed_inputs
160
161     def _translate(self, sourcetype, path, parsed_params, a_file, deploy):
162         output = None
163         if sourcetype == "tosca":
164             log.debug(_('Loading the tosca template.'))
165             tosca = ToscaTemplate(path, parsed_params, a_file)
166             translator = TOSCATranslator(tosca, parsed_params, deploy)
167             log.debug(_('Translating the tosca template.'))
168             output = translator.translate()
169         return output
170
171     def _write_output(self, output, output_file=None):
172         if output:
173             if output_file:
174                 with open(output_file, 'w+') as f:
175                     f.write(output)
176             else:
177                 print(output)
178
179
180 def heatclient(output, params):
181     try:
182         access_dict = utils.get_ks_access_dict()
183         endpoint = utils.get_url_for(access_dict, 'orchestration')
184         token = utils.get_token_id(access_dict)
185     except Exception as e:
186         log.error(e)
187     headers = {
188         'Content-Type': 'application/json',
189         'X-Auth-Token': token
190     }
191     heat_stack_name = "heat_" + str(uuid.uuid4()).split("-")[0]
192     output = yaml.load(output)
193     output['heat_template_version'] = str(output['heat_template_version'])
194     data = {
195         'stack_name': heat_stack_name,
196         'template': output,
197         'parameters': params
198     }
199     response = requests.post(endpoint + '/stacks',
200                              data=json.dumps(data),
201                              headers=headers)
202     content = ast.literal_eval(response._content)
203     if response.status_code == 201:
204         stack_id = content["stack"]["id"]
205         get_url = endpoint + '/stacks/' + heat_stack_name + '/' + stack_id
206         get_stack_response = requests.get(get_url,
207                                           headers=headers)
208         stack_details = json.loads(get_stack_response.content)["stack"]
209         col_names = ["id", "stack_name", "stack_status", "creation_time",
210                      "updated_time"]
211         pt = prettytable.PrettyTable(col_names)
212         stack_list = []
213         for col in col_names:
214             stack_list.append(stack_details[col])
215         pt.add_row(stack_list)
216         print(pt)
217     else:
218         err_msg = content["error"]["message"]
219         log(_("Unable to deploy to Heat\n%s\n") % err_msg)
220
221
222 def main(args=None):
223     if args is None:
224         args = sys.argv[1:]
225     TranslatorShell().main(args)
226
227
228 if __name__ == '__main__':
229     main()