2 # Copyright (c) 2017 GigaSpaces Technologies Ltd. All rights reserved.
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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
20 from aria.orchestrator.context import operation
23 DEPLOYMENT = 'deployment'
24 NODE_INSTANCE = 'node-instance'
25 RELATIONSHIP_INSTANCE = 'relationship-instance'
28 class CloudifyContextAdapter(object):
30 def __init__(self, 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()
39 self._node_instance = 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(
48 ctx.source_node_template,
52 self._target = RelationshipTargetAdapter(
54 ctx.target_node_template,
59 def __getattr__(self, item):
61 return getattr(self._ctx, item)
62 except AttributeError:
63 return super(CloudifyContextAdapter, self).__getattribute__(item)
67 return self._blueprint
71 return self._deployment
75 return self._operation
78 def bootstrap_context(self):
79 return self._bootstrap_context
92 return RELATIONSHIP_INSTANCE
100 self._verify_in_node_operation()
101 return self._instance
105 self._verify_in_node_operation()
110 self._verify_in_relationship_operation()
115 self._verify_in_relationship_operation()
119 def execution_id(self):
120 return self._ctx.task.execution.id
123 def workflow_id(self):
124 return self._ctx.task.execution.workflow_name
127 def rest_token(self):
132 return self._ctx.task.id
136 return self._ctx.task.function
139 def task_target(self):
143 def task_queue(self):
150 def getChild( self, name ):
151 loggertype = type(self)
153 childlogger = self._logger.getChild(name)
154 finallogger = loggertype(childlogger, self._task_id)
158 loggertype = type(self._ctx.logger)
160 childloggertype = type(self._ctx.logger.getChild("test"))
161 if loggertype != childloggertype:
162 loggertype.getChild = getChild
164 return self._ctx.logger
166 def send_event(self, event):
167 self.logger.info(event)
170 def provider_context(self):
173 def get_resource(self, resource_path):
174 return self._ctx.get_resource(resource_path)
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)
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,
187 def download_resource_and_render(self,
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,
195 variables=template_variables
200 def _get_target_path(target_path, resource_path):
203 fd, target_path = tempfile.mkstemp(suffix=os.path.basename(resource_path))
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)
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,
223 class BlueprintAdapter(object):
225 def __init__(self, ctx):
230 return self._ctx.service_template.id
233 class DeploymentAdapter(object):
235 def __init__(self, ctx):
240 return self._ctx.service.id
243 class NodeAdapter(object):
245 def __init__(self, ctx, node_template, node):
247 self._node_template = node_template
252 return self._node_template.id
256 return self._node_template.name
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
269 return self._node_template.type.name
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
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]
285 class NodeInstanceAdapter(object):
287 def __init__(self, ctx, node, modifiable):
290 self._modifiable = modifiable
297 def runtime_properties(self):
298 return self._node.attributes
300 @runtime_properties.setter
301 def runtime_properties(self, value):
302 self._node.attributes = value
304 def update(self, on_conflict=None):
305 self._ctx.model.node.update(self._node)
307 def refresh(self, force=False):
308 self._ctx.model.node.refresh(self._node)
312 return self._node.host_address
315 def relationships(self):
316 return [RelationshipAdapter(self._ctx, relationship=relationship) for
317 relationship in self._node.outbound_relationships]
319 #def __getattr__(self, item):
321 # print self._node.attributes
322 # print dir(self._ctx)
323 # return getattr(self._ctx.instance, item)
326 class RelationshipAdapter(object):
328 def __init__(self, ctx, relationship):
330 self._relationship = relationship
331 node = relationship.target_node
332 node_template = node.node_template
333 self.target = RelationshipTargetAdapter(ctx, node_template, node, False)
337 return self._relationship.type.name
340 #def type_hierarchy(self):
341 # return self._relationship.type.hierarchy
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
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]
368 class RelationshipTargetAdapter(object):
370 def __init__(self, ctx, node_template, node, modifiable):
372 self.node = NodeAdapter(ctx, node_template=node_template, node=node)
373 self.instance = NodeInstanceAdapter(ctx, node=node, modifiable=modifiable)
376 class OperationAdapter(object):
378 def __init__(self, ctx):
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(':', '.')
391 def retry_number(self):
392 return self._ctx.task.attempts_count - 1
395 def max_retries(self):
396 task = self._ctx.task
397 if task.max_attempts == task.INFINITE_RETRIES:
398 return task.INFINITE_RETRIES
400 return task.max_attempts - 1 if task.max_attempts > 0 else 0
402 def retry(self, message=None, retry_after=None):
403 self._ctx.task.retry(message, retry_after)
406 class BootstrapAdapter(object):
408 def __init__(self, ctx):
410 self.cloudify_agent = _Stub()
411 self.resources_prefix = ''
413 def broker_config(self, *args, **kwargs):
417 class CloudifyAgentAdapter(object):
419 def init_script(self, *args, **kwargs):
423 class PluginAdapter(object):
425 def __init__(self, ctx):
431 return self._ctx.task.plugin.name
434 def package_name(self):
435 return self._plugin_attr('package_name')
438 def package_version(self):
439 return self._plugin_attr('package_version')
444 return self._plugin_attr('prefix')
448 return self._ctx.plugin_workdir
450 def _plugin_attr(self, attr):
452 self._plugin = self._ctx.task.plugin
455 return getattr(self._plugin, attr, None)
460 def __getattr__(self, _):