# Cloudify properties and intrinsic functions

POSTED IN   cloudify | devops | python

Passing data between nodes in Cloudify blueprints is one of the basic things blueprint author might want to do. In this post, we will have a look at how can we exchange and manipulate data in Cloudify blueprints, with emphasis on using intrinsic functions and their limitations.

## Seting up our sandbox

If you would like to play along with our examples, you will need Cloudify’s command line tool cfy installed. In order to keep our system clean, we will install cfy in a virtual environment by running

$cd /tmp$ virtualenv -p python2 venv
(venv) $. venv/bin/activate (venv)$ pip install 'cloudify<4'

Make sure you create a virtual environment for python 2, since Cloudify does not support python 3. Also, we will be using latest Cloudify in 3.x release. All of this can be done using Cloudify 4, but commands need to be changed a bit. Those modifications are left to the reader as an exercise.

Another thing that we will need is blueprint, that is available in this repo. We will clone it into /tmp/blueprint by running

(venv) $git clone --branch init \ https://github.com/xlab-si/cloudify-intrinsic-functions blueprint (venv)$ cd blueprint

And with this being done, we are ready to start running some cfy commands.

## Initial blueprint incarnation

At the end of this post, we would like to have a blueprint that deploys node_a and node_b components, where node_b needs node_a to be running. Additionally, node_b also needs some information about node_a at installation time. But for starters, we will write down the blueprint that simply creates both components.

tosca_definitions_version: cloudify_dsl_1_3

imports:
- http://www.getcloudify.org/spec/cloudify/3.4.2/types.yaml

node_types:

type_a:
derived_from: cloudify.nodes.Root
properties:
property_a: { default: property_a_value }
interfaces:
cloudify.interfaces.lifecycle:
create:
implementation: scripts/create_a.py
executor: central_deployment_agent

type_b:
derived_from: cloudify.nodes.Root
properties:
property_b1: { required: true }
property_b2: { required: true }
interfaces:
cloudify.interfaces.lifecycle:
create:
implementation: scripts/create_b.py
executor: central_deployment_agent

node_templates:

node_a:
type: type_a

node_b:
type: type_b
properties:
property_b1: some data from node_a
property_b2: more data from node_a

This is a fairly simple blueprint that we can install locally by running

(venv) $cfy local install -p blueprint.yaml ... ... [node_b_t0mdbd] Creating node ... [node_b_t0mdbd.create] Sending task 'script_runner.tasks.run' ... [node_b_t0mdbd.create] Task started 'script_runner.tasks.run' ... [node_b_t0mdbd.create] INFO: Creating node of type B ... [node_b_t0mdbd.create] INFO: property_b1 = property_a_value ... [node_b_t0mdbd.create] INFO: input_b2 = attribute_a_value ... [node_b_t0mdbd.create] Task succeeded 'script_runner.tasks.run' ... [node_b_t0mdbd] Configuring node ... [node_b_t0mdbd] Starting node ... 'install' workflow execution succeeded But at what cost! We now need to configure operation inputs on each node template instead of hiding this mess inside the node type. Sure we can do better, right? ## Digging a hole inside the rabbit hole By the fundamental theorem of software engineering, there must be another level of indirection that will resolve the conundrum that we got ourselves into. And indeed there is. Our initial output contained a line: INFO: property_b2 = {u'get_attribute': [u'node_a', u'attribute_a']} So the function call is still there, we just need to trick Cloudify into evaluating this for us without resorting to duplication. And we can do this by resetting node_b template back to previous state: node_b: type: type_b properties: property_b1: { get_property: [ node_a, property_a ] } property_b2: { get_attribute: [ node_a, attribute_a ] } relationships: - type: cloudify.relationships.connected_to target: node_a Next, we employ another level of indirection inside the type_b definition: type_b: derived_from: cloudify.nodes.Root properties: property_b1: { required: true } property_b2: { required: true } interfaces: cloudify.interfaces.lifecycle: create: implementation: scripts/create_b.py executor: central_deployment_agent inputs: input_b2: { default: { get_property: [ SELF, property_b2 ] } } The trick is in the last line. Since we know that get_attribute function call is evaluated before the operation is executed, we use the get_property function to retrieve the “stored” get_attributes call and evaluate it. And just to verify our hypothesis, here is the installation log: (venv)$ git checkout master
(venv) \$ cfy local install -p blueprint.yaml
...
... [node_b_h8yzi9] Creating node
... [node_b_h8yzi9.create] INFO: Creating node of type B
... [node_b_h8yzi9.create] INFO: property_b1 = property_a_value
... [node_b_h8yzi9.create] INFO: input_b2 = attribute_a_value