Update project maturity status
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / adapters / context_adapter.py
1 #
2 # Copyright (c) 2017 GigaSpaces Technologies Ltd. All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # 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, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15 #
16
17 import os
18 import tempfile
19
20 from aria.orchestrator.context import operation
21
22
23 DEPLOYMENT = 'deployment'
24 NODE_INSTANCE = 'node-instance'
25 RELATIONSHIP_INSTANCE = 'relationship-instance'
26
27
28 class CloudifyContextAdapter(object):
29
30     def __init__(self, ctx):
31         self._ctx = ctx
32         self._blueprint = BlueprintAdapter(ctx)
33         self._deployment = DeploymentAdapter(ctx)
34         self._operation = OperationAdapter(ctx)
35         self._bootstrap_context = BootstrapAdapter(ctx)
36         self._plugin = PluginAdapter(ctx)
37         self._agent = CloudifyAgentAdapter()
38         self._node = None
39         self._node_instance = None
40         self._source = None
41         self._target = None
42         if isinstance(ctx, operation.NodeOperationContext):
43             self._node = NodeAdapter(ctx, ctx.node_template, ctx.node)
44             self._instance = NodeInstanceAdapter(ctx, ctx.node, True)
45         elif isinstance(ctx, operation.RelationshipOperationContext):
46             self._source = RelationshipTargetAdapter(
47                 ctx,
48                 ctx.source_node_template,
49                 ctx.source_node,
50                 True
51             )
52             self._target = RelationshipTargetAdapter(
53                 ctx,
54                 ctx.target_node_template,
55                 ctx.target_node,
56                 True
57             )
58
59     def __getattr__(self, item):
60         try:
61             return getattr(self._ctx, item)
62         except AttributeError:
63             return super(CloudifyContextAdapter, self).__getattribute__(item)
64
65     @property
66     def blueprint(self):
67         return self._blueprint
68
69     @property
70     def deployment(self):
71         return self._deployment
72
73     @property
74     def operation(self):
75         return self._operation
76
77     @property
78     def bootstrap_context(self):
79         return self._bootstrap_context
80
81     @property
82     def plugin(self):
83         return self._plugin
84
85     @property
86     def agent(self):
87         return self._agent
88
89     @property
90     def type(self):
91         if self._source:
92             return RELATIONSHIP_INSTANCE
93         elif self._instance:
94             return NODE_INSTANCE
95         else:
96             return DEPLOYMENT
97
98     @property
99     def instance(self):
100         self._verify_in_node_operation()
101         return self._instance
102
103     @property
104     def node(self):
105         self._verify_in_node_operation()
106         return self._node
107
108     @property
109     def source(self):
110         self._verify_in_relationship_operation()
111         return self._source
112
113     @property
114     def target(self):
115         self._verify_in_relationship_operation()
116         return self._target
117
118     @property
119     def execution_id(self):
120         return self._ctx.task.execution.id
121
122     @property
123     def workflow_id(self):
124         return self._ctx.task.execution.workflow_name
125
126     @property
127     def rest_token(self):
128         return None
129
130     @property
131     def task_id(self):
132         return self._ctx.task.id
133
134     @property
135     def task_name(self):
136         return self._ctx.task.function
137
138     @property
139     def task_target(self):
140         return None
141
142     @property
143     def task_queue(self):
144         return None
145
146     @property
147     def logger(self):
148         
149
150         def getChild( self, name ):
151             loggertype = type(self)
152
153             childlogger =  self._logger.getChild(name)
154             finallogger = loggertype(childlogger, self._task_id)
155             return finallogger
156         
157     
158         loggertype = type(self._ctx.logger)
159        
160         childloggertype = type(self._ctx.logger.getChild("test"))
161         if loggertype != childloggertype:
162             loggertype.getChild = getChild
163       
164         return self._ctx.logger
165
166     def send_event(self, event):
167         self.logger.info(event)
168
169     @property
170     def provider_context(self):
171         return {}
172
173     def get_resource(self, resource_path):
174         return self._ctx.get_resource(resource_path)
175
176     def get_resource_and_render(self, resource_path, template_variables=None):
177         return self._ctx.get_resource_and_render(resource_path, variables=template_variables)
178
179     def download_resource(self, resource_path, target_path=None):
180         target_path = self._get_target_path(target_path, resource_path)
181         self._ctx.download_resource(
182             destination=target_path,
183             path=resource_path
184         )
185         return target_path
186
187     def download_resource_and_render(self,
188                                      resource_path,
189                                      target_path=None,
190                                      template_variables=None):
191         target_path = self._get_target_path(target_path, resource_path)
192         self._ctx.download_resource_and_render(
193             destination=target_path,
194             path=resource_path,
195             variables=template_variables
196         )
197         return target_path
198
199     @staticmethod
200     def _get_target_path(target_path, resource_path):
201         if target_path:
202             return target_path
203         fd, target_path = tempfile.mkstemp(suffix=os.path.basename(resource_path))
204         os.close(fd)
205         return target_path
206
207     def _verify_in_node_operation(self):
208         if self.type != NODE_INSTANCE:
209             self._ctx.task.abort(
210                 'ctx.node/ctx.instance can only be used in a {0} context but '
211                 'used in a {1} context.'.format(NODE_INSTANCE, self.type)
212             )
213
214     def _verify_in_relationship_operation(self):
215         if self.type != RELATIONSHIP_INSTANCE:
216             self._ctx.task.abort(
217                 'ctx.source/ctx.target can only be used in a {0} context but '
218                 'used in a {1} context.'.format(RELATIONSHIP_INSTANCE,
219                                                 self.type)
220             )
221
222
223 class BlueprintAdapter(object):
224
225     def __init__(self, ctx):
226         self._ctx = ctx
227
228     @property
229     def id(self):
230         return self._ctx.service_template.id
231
232
233 class DeploymentAdapter(object):
234
235     def __init__(self, ctx):
236         self._ctx = ctx
237
238     @property
239     def id(self):
240         return self._ctx.service.id
241
242
243 class NodeAdapter(object):
244
245     def __init__(self, ctx, node_template, node):
246         self._ctx = ctx
247         self._node_template = node_template
248         self._node = node
249
250     @property
251     def id(self):
252         return self._node_template.id
253
254     @property
255     def name(self):
256         return self._node_template.name
257
258     @property
259     def properties(self):
260         # Cloudify Azure plugin will request the resource_config and merge it with new configurations.
261         # This creates an problem when the resource_config is None. Fix this by replacing an empty
262         # resource_config with an empth dict.
263         if 'resource_config' in self._node.properties and self._node.properties.get('resource_config') == None:
264             self._node.properties['resource_config']={}
265         return self._node.properties
266
267     @property
268     def type(self):
269         return self._node_template.type.name
270
271     @property
272     def type_hierarchy(self):
273         # We needed to modify the type hierarchy to be a list of strings that include the word
274         # 'cloudify' in each one of them instead of 'aria', since in the Cloudify AWS plugin, that
275         # we currently wish to support, if we want to attach an ElasticIP to a node, this node's
276         # type_hierarchy property must be a list of strings only, and it must contain either the
277         # string 'cloudify.aws.nodes.Instance', or the string 'cloudify.aws.nodes.Interface'.
278         # In any other case, we won't be able to attach an ElasticIP to a node using the Cloudify
279         # AWS plugin.
280         type_hierarchy_names = [type_.name for type_ in self._node_template.type.hierarchy
281                                 if type_.name is not None]
282         return [type_name.replace('aria', 'cloudify') for type_name in type_hierarchy_names]
283
284
285 class NodeInstanceAdapter(object):
286
287     def __init__(self, ctx, node, modifiable):
288         self._ctx = ctx
289         self._node = node
290         self._modifiable = modifiable
291
292     @property
293     def id(self):
294         return self._node.id
295
296     @property
297     def runtime_properties(self):
298         return self._node.attributes
299
300     @runtime_properties.setter
301     def runtime_properties(self, value):
302         self._node.attributes = value
303
304     def update(self, on_conflict=None):
305         self._ctx.model.node.update(self._node)
306
307     def refresh(self, force=False):
308         self._ctx.model.node.refresh(self._node)
309
310     @property
311     def host_ip(self):
312         return self._node.host_address
313
314     @property
315     def relationships(self):
316         return [RelationshipAdapter(self._ctx, relationship=relationship) for
317                 relationship in self._node.outbound_relationships]
318
319     #def __getattr__(self, item):
320     #    print "requsting "
321     #    print self._node.attributes
322     #    print dir(self._ctx)
323     #    return getattr(self._ctx.instance, item)
324
325
326 class RelationshipAdapter(object):
327
328     def __init__(self, ctx, relationship):
329         self._ctx = ctx
330         self._relationship = relationship
331         node = relationship.target_node
332         node_template = node.node_template
333         self.target = RelationshipTargetAdapter(ctx, node_template, node, False)
334
335     @property
336     def type(self):
337         return self._relationship.type.name
338
339     #@property
340     #def type_hierarchy(self):
341     #    return self._relationship.type.hierarchy
342
343
344
345     @property
346     def type_hierarchy(self):
347         # We needed to modify the type hierarchy to be a list of strings that include the word
348         # 'cloudify' in each one of them instead of 'aria', since in the Cloudify AWS plugin, that
349         # we currently wish to support, if we want to attach an ElasticIP to a node, this node's
350         # type_hierarchy property must be a list of strings only, and it must contain either the
351         # string 'cloudify.aws.nodes.Instance', or the string 'cloudify.aws.nodes.Interface'.
352         # In any other case, we won't be able to attach an ElasticIP to a node using the Cloudify
353         # AWS plugin.
354         type_hierarchy_names = [type_.name for type_ in self._relationship.type.hierarchy
355                                 if type_.name is not None]
356         return [type_name.replace('aria', 'cloudify') for type_name in type_hierarchy_names]
357
358
359
360
361
362
363
364
365
366
367
368 class RelationshipTargetAdapter(object):
369
370     def __init__(self, ctx, node_template, node, modifiable):
371         self._ctx = ctx
372         self.node = NodeAdapter(ctx, node_template=node_template, node=node)
373         self.instance = NodeInstanceAdapter(ctx, node=node, modifiable=modifiable)
374
375
376 class OperationAdapter(object):
377
378     def __init__(self, ctx):
379         self._ctx = ctx
380
381     @property
382     def name(self):
383         # We needed to modify the operation's 'name' property in order to support the Cloudify AWS
384         # plugin. It can't use ARIA's operation naming convention, as any operation we want to run
385         # using the Cloudify AWS plugin must have its name in the format:
386         # '<something>.<operation_name>'.
387         aria_name = self._ctx.task.name
388         return aria_name.split('@')[0].replace(':', '.')
389
390     @property
391     def retry_number(self):
392         return self._ctx.task.attempts_count - 1
393
394     @property
395     def max_retries(self):
396         task = self._ctx.task
397         if task.max_attempts == task.INFINITE_RETRIES:
398             return task.INFINITE_RETRIES
399         else:
400             return task.max_attempts - 1 if task.max_attempts > 0 else 0
401
402     def retry(self, message=None, retry_after=None):
403         self._ctx.task.retry(message, retry_after)
404
405
406 class BootstrapAdapter(object):
407
408     def __init__(self, ctx):
409         self._ctx = ctx
410         self.cloudify_agent = _Stub()
411         self.resources_prefix = ''
412
413     def broker_config(self, *args, **kwargs):
414         return {}
415
416
417 class CloudifyAgentAdapter(object):
418
419     def init_script(self, *args, **kwargs):
420         return None
421
422
423 class PluginAdapter(object):
424
425     def __init__(self, ctx):
426         self._ctx = ctx
427         self._plugin = None
428
429     @property
430     def name(self):
431         return self._ctx.task.plugin.name
432
433     @property
434     def package_name(self):
435         return self._plugin_attr('package_name')
436
437     @property
438     def package_version(self):
439         return self._plugin_attr('package_version')
440
441     @property
442     def prefix(self):
443         # TODO
444         return self._plugin_attr('prefix')
445
446     @property
447     def workdir(self):
448         return self._ctx.plugin_workdir
449
450     def _plugin_attr(self, attr):
451         if not self._plugin:
452             self._plugin = self._ctx.task.plugin
453         if not self._plugin:
454             return None
455         return getattr(self._plugin, attr, None)
456
457
458
459 class _Stub(object):
460     def __getattr__(self, _):
461         return None