Merge "vFW and vDNS support added to azure-plugin"
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / orchestrator / execution_plugin / instantiation.py
1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements.  See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License.  You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """
17 Instantiation of :class:`~aria.modeling.models.Operation` models.
18 """
19
20 # TODO: this module will eventually be moved to a new "aria.instantiation" package
21 from ...modeling.functions import Function
22 from ... import utils
23
24
25 def configure_operation(operation, reporter):
26     host = None
27     interface = operation.interface
28     if interface.node is not None:
29         host = interface.node.host
30     elif interface.relationship is not None:
31         if operation.relationship_edge is True:
32             host = interface.relationship.target_node.host
33         else: # either False or None (None meaning that edge was not specified)
34             host = interface.relationship.source_node.host
35
36     _configure_common(operation, reporter)
37     if host is None:
38         _configure_local(operation)
39     else:
40         _configure_remote(operation, reporter)
41
42     # Any remaining un-handled configuration parameters will become extra arguments, available as
43     # kwargs in either "run_script_locally" or "run_script_with_ssh"
44     for key, value in operation.configurations.iteritems():
45         if key not in ('process', 'ssh'):
46             operation.arguments[key] = value.instantiate(None)
47
48
49 def _configure_common(operation, reporter):
50     """
51     Local and remote operations.
52     """
53
54     from ...modeling.models import Argument
55     operation.arguments['script_path'] = Argument.wrap('script_path', operation.implementation,
56                                                        'Relative path to the executable file.')
57     operation.arguments['process'] = Argument.wrap('process', _get_process(operation, reporter),
58                                                    'Sub-process configuration.')
59
60
61 def _configure_local(operation):
62     """
63     Local operation.
64     """
65
66     from . import operations
67     operation.function = '{0}.{1}'.format(operations.__name__,
68                                           operations.run_script_locally.__name__)
69
70
71 def _configure_remote(operation, reporter):
72     """
73     Remote SSH operation via Fabric.
74     """
75
76     from ...modeling.models import Argument
77     from . import operations
78
79     ssh = _get_ssh(operation, reporter)
80
81     # Defaults
82     # TODO: find a way to configure these generally in the service template
83     default_user = ''
84     default_password = ''
85     if 'user' not in ssh:
86         ssh['user'] = default_user
87     if ('password' not in ssh) and ('key' not in ssh) and ('key_filename' not in ssh):
88         ssh['password'] = default_password
89
90     operation.arguments['use_sudo'] = Argument.wrap('use_sudo', ssh.get('use_sudo', False),
91                                                     'Whether to execute with sudo.')
92
93     operation.arguments['hide_output'] = Argument.wrap('hide_output', ssh.get('hide_output', []),
94                                                        'Hide output of these Fabric groups.')
95
96     fabric_env = {}
97     if 'warn_only' in ssh:
98         fabric_env['warn_only'] = ssh['warn_only']
99     fabric_env['user'] = ssh.get('user')
100     fabric_env['password'] = ssh.get('password')
101     fabric_env['key'] = ssh.get('key')
102     fabric_env['key_filename'] = ssh.get('key_filename')
103     if 'address' in ssh:
104         fabric_env['host_string'] = ssh['address']
105
106     # Make sure we have a user
107     if fabric_env.get('user') is None:
108         reporter.report('must configure "ssh.user" for "{0}"'.format(operation.implementation),
109                         level=reporter.Issue.BETWEEN_TYPES)
110
111     # Make sure we have an authentication value
112     if (fabric_env.get('password') is None) and \
113         (fabric_env.get('key') is None) and \
114         (fabric_env.get('key_filename') is None):
115         reporter.report(
116             'must configure "ssh.password", "ssh.key", or "ssh.key_filename" for "{0}"'
117             .format(operation.implementation),
118             level=reporter.Issue.BETWEEN_TYPES)
119
120     operation.arguments['fabric_env'] = Argument.wrap('fabric_env', fabric_env,
121                                                       'Fabric configuration.')
122
123     operation.function = '{0}.{1}'.format(operations.__name__,
124                                           operations.run_script_with_ssh.__name__)
125
126
127 def _get_process(operation, reporter):
128     value = (operation.configurations.get('process')._value
129              if 'process' in operation.configurations
130              else None)
131     if value is None:
132         return {}
133     _validate_type(value, dict, 'process', reporter)
134     value = utils.collections.OrderedDict(value)
135     for k, v in value.iteritems():
136         if k == 'eval_python':
137             value[k] = _coerce_bool(v, 'process.eval_python', reporter)
138         elif k == 'cwd':
139             _validate_type(v, basestring, 'process.cwd', reporter)
140         elif k == 'command_prefix':
141             _validate_type(v, basestring, 'process.command_prefix', reporter)
142         elif k == 'args':
143             value[k] = _dict_to_list_of_strings(v, 'process.args', reporter)
144         elif k == 'env':
145             _validate_type(v, dict, 'process.env', reporter)
146         else:
147             reporter.report('unsupported configuration parameter: "process.{0}"'.format(k),
148                             level=reporter.Issue.BETWEEN_TYPES)
149     return value
150
151
152 def _get_ssh(operation, reporter):
153     value = (operation.configurations.get('ssh')._value
154              if 'ssh' in operation.configurations
155              else None)
156     if value is None:
157         return {}
158     _validate_type(value, dict, 'ssh', reporter)
159     value = utils.collections.OrderedDict(value)
160     for k, v in value.iteritems():
161         if k == 'use_sudo':
162             value[k] = _coerce_bool(v, 'ssh.use_sudo', reporter)
163         elif k == 'hide_output':
164             value[k] = _dict_to_list_of_strings(v, 'ssh.hide_output', reporter)
165         elif k == 'warn_only':
166             value[k] = _coerce_bool(v, 'ssh.warn_only', reporter)
167         elif k == 'user':
168             _validate_type(v, basestring, 'ssh.user', reporter)
169         elif k == 'password':
170             _validate_type(v, basestring, 'ssh.password', reporter)
171         elif k == 'key':
172             _validate_type(v, basestring, 'ssh.key', reporter)
173         elif k == 'key_filename':
174             _validate_type(v, basestring, 'ssh.key_filename', reporter)
175         elif k == 'address':
176             _validate_type(v, basestring, 'ssh.address', reporter)
177         else:
178             reporter.report('unsupported configuration parameter: "ssh.{0}"'.format(k),
179                             level=reporter.Issue.BETWEEN_TYPES)
180     return value
181
182
183 def _validate_type(value, the_type, name, reporter):
184     if isinstance(value, Function):
185         return
186     if not isinstance(value, the_type):
187         reporter.report(
188             '"{0}" configuration is not a {1}: {2}'.format(
189                 name, utils.type.full_type_name(the_type), utils.formatting.safe_repr(value)),
190             level=reporter.Issue.BETWEEN_TYPES)
191
192
193 def _coerce_bool(value, name, reporter):
194     if value is None:
195         return None
196     if isinstance(value, bool):
197         return value
198     _validate_type(value, basestring, name, reporter)
199     if value == 'true':
200         return True
201     elif value == 'false':
202         return False
203     else:
204         reporter.report(
205             '"{0}" configuration is not "true" or "false": {1}'.format(
206                 name, utils.formatting.safe_repr(value)),
207             level=reporter.Issue.BETWEEN_TYPES)
208
209
210 def _dict_to_list_of_strings(the_dict, name, reporter):
211     _validate_type(the_dict, dict, name, reporter)
212     value = []
213     for k in sorted(the_dict):
214         v = the_dict[k]
215         _validate_type(v, basestring, '{0}.{1}'.format(name, k), reporter)
216         value.append(v)
217     return value