docs: fix issues
[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 ast
15 import json
16 import logging
17 import logging.config
18 import os
19 import prettytable
20 import requests
21 import sys
22 import uuid
23 import yaml
24
25 from toscaparser.tosca_template import ToscaTemplate
26 from toscaparser.utils.gettextutils import _
27 from toscaparser.utils.urlutils import UrlUtils
28 from translator.common import utils
29 from translator.hot.tosca_translator import TOSCATranslator
30
31 """
32 Test the heat-translator translation from command line as:
33 #heat-translator
34   --template-file=<path to the YAML template>
35   --template-type=<type of template e.g. tosca>
36   --parameters="purpose=test"
37 Takes three user arguments,
38 1. type of translation (e.g. tosca) (required)
39 2. Path to the file that needs to be translated (required)
40 3. Input parameters (optional)
41
42 In order to use heat-translator to only validate template,
43 without actual translation, pass --validate-only=true along with
44 other required arguments.
45
46 """
47
48 logging.config.fileConfig('heat_translator_logging.conf')
49 log = logging.getLogger("heat-translator")
50
51
52 class TranslatorShell(object):
53
54     SUPPORTED_TYPES = ['tosca']
55
56     def _validate(self, args):
57         if len(args) < 2:
58             msg = _("The program requires minimum two arguments. "
59                     "Please refer to the usage documentation.")
60             log.error(msg)
61             raise ValueError(msg)
62         if "--template-file=" not in args[0]:
63             msg = _("The program expects --template-file as first argument. "
64                     "Please refer to the usage documentation.")
65             log.error(msg)
66             raise ValueError(msg)
67         if "--template-type=" not in args[1]:
68             msg = _("The program expects --template-type as second argument. "
69                     "Please refer to the usage documentation.")
70             log.error(msg)
71             raise ValueError(msg)
72
73     def main(self, args):
74         # TODO(spzala): set self.deploy based on passed args once support for
75         # --deploy argument is enabled.
76         self.deploy = False
77         self._validate(args)
78         path = args[0].split('--template-file=')[1]
79         # e.g. --template_file=translator/tests/data/tosca_helloworld.yaml
80         template_type = args[1].split('--template-type=')[1]
81         # e.g. --template_type=tosca
82         if not template_type:
83             msg = _("Template type is needed. For example, 'tosca'")
84             log.error(msg)
85             raise ValueError(msg)
86         elif template_type not in self.SUPPORTED_TYPES:
87             msg = _("%(value)s is not a valid template type.") % {
88                 'value': template_type}
89             log.error(msg)
90             raise ValueError(msg)
91         parsed_params = {}
92         validate_only = None
93         output_file = None
94         if len(args) > 2:
95             parameters = None
96             for arg in args:
97                 if "--validate-only=" in arg:
98                     validate_only = arg
99                 if "--parameters=" in arg:
100                     parameters = arg
101                 if "--output-file=" in arg:
102                     output = arg
103                     output_file = output.split('--output-file=')[1]
104                 if "--deploy" in arg:
105                     self.deploy = True
106             if parameters:
107                 parsed_params = self._parse_parameters(parameters)
108         a_file = os.path.isfile(path)
109         a_url = UrlUtils.validate_url(path) if not a_file else False
110         if a_file or a_url:
111             run_only_validation = False
112             if validate_only:
113                 value = validate_only.split('-validate-only=')[1].lower()
114                 if template_type == 'tosca' and value == 'true':
115                     run_only_validation = True
116             if run_only_validation:
117                 ToscaTemplate(path, parsed_params, a_file)
118                 msg = (_('The input "%(path)s" successfully passed '
119                          'validation.') % {'path': path})
120                 print(msg)
121             else:
122                 log.info(
123                     _('Checked whether template path is a file or url path.'))
124                 heat_tpl = self._translate(template_type, path, parsed_params,
125                                            a_file)
126                 if heat_tpl:
127                     if utils.check_for_env_variables() and self.deploy:
128                         try:
129                             heatclient(heat_tpl, parsed_params)
130                         except Exception:
131                             log.error(_("Unable to launch the heat stack"))
132
133                     self._write_output(heat_tpl, output_file)
134         else:
135             msg = _("The path %(path)s is not a valid file or URL.") % {
136                 'path': path}
137             log.error(msg)
138             raise ValueError(msg)
139
140     def _parse_parameters(self, parameter_list):
141         parsed_inputs = {}
142         if parameter_list.startswith('--parameters'):
143             # Parameters are semi-colon separated
144             inputs = parameter_list.split('--parameters=')[1].\
145                 replace('"', '').split(';')
146             # Each parameter should be an assignment
147             for param in inputs:
148                 keyvalue = param.split('=')
149                 # Validate the parameter has both a name and value
150                 msg = _("'%(param)s' is not a well-formed parameter.") % {
151                     'param': param}
152                 if keyvalue.__len__() is 2:
153                     # Assure parameter name is not zero-length or whitespace
154                     stripped_name = keyvalue[0].strip()
155                     if not stripped_name:
156                         log.error(msg)
157                         raise ValueError(msg)
158                     # Add the valid parameter to the dictionary
159                     parsed_inputs[keyvalue[0]] = keyvalue[1]
160                 else:
161                     log.error(msg)
162                     raise ValueError(msg)
163         else:
164             msg = _("'%(list)s' is not a valid parameter list.") % {
165                 'list': parameter_list}
166             log.error(msg)
167             raise ValueError(msg)
168         return parsed_inputs
169
170     def _translate(self, sourcetype, path, parsed_params, a_file):
171         output = None
172         if sourcetype == "tosca":
173             log.debug(_('Loading the tosca template.'))
174             tosca = ToscaTemplate(path, parsed_params, a_file)
175             translator = TOSCATranslator(tosca, parsed_params, self.deploy)
176             log.debug(_('Translating the tosca template.'))
177             output = translator.translate()
178         return output
179
180     def _write_output(self, output, output_file=None):
181         if output:
182             if output_file:
183                 with open(output_file, 'w+') as f:
184                     f.write(output)
185             else:
186                 print(output)
187
188
189 def heatclient(output, params):
190     try:
191         access_dict = utils.get_ks_access_dict()
192         endpoint = utils.get_url_for(access_dict, 'orchestration')
193         token = utils.get_token_id(access_dict)
194     except Exception as e:
195         log.error(e)
196     headers = {
197         'Content-Type': 'application/json',
198         'X-Auth-Token': token
199     }
200     heat_stack_name = "heat_" + str(uuid.uuid4()).split("-")[0]
201     output = yaml.load(output)
202     output['heat_template_version'] = str(output['heat_template_version'])
203     data = {
204         'stack_name': heat_stack_name,
205         'template': output,
206         'parameters': params
207     }
208     response = requests.post(endpoint + '/stacks',
209                              data=json.dumps(data),
210                              headers=headers)
211     content = ast.literal_eval(response._content)
212     if response.status_code == 201:
213         stack_id = content["stack"]["id"]
214         get_url = endpoint + '/stacks/' + heat_stack_name + '/' + stack_id
215         get_stack_response = requests.get(get_url,
216                                           headers=headers)
217         stack_details = json.loads(get_stack_response.content)["stack"]
218         col_names = ["id", "stack_name", "stack_status", "creation_time",
219                      "updated_time"]
220         pt = prettytable.PrettyTable(col_names)
221         stack_list = []
222         for col in col_names:
223             stack_list.append(stack_details[col])
224         pt.add_row(stack_list)
225         print(pt)
226     else:
227         err_msg = content["error"]["message"]
228         log(_("Unable to deploy to Heat\n%s\n") % err_msg)
229
230
231 def main(args=None):
232     if args is None:
233         args = sys.argv[1:]
234     TranslatorShell().main(args)
235
236
237 if __name__ == '__main__':
238     main()