Ansible Plugin

The Ansible plugin enables you to configure Cloudify resources with Ansible and provides an agentless method for executing operations on hosts.

Node Types

The primary method to include an Ansible playbook in your Cloudify blueprint is to use the cloudify.nodes.ansible.Executor node type.

cloudify.nodes.ansible.Executor

Execute playbook lifecycle as stand-alone node template.

Node Operations

Node Properties:

Simple example of cloudify.nodes.ansible.Executor node type:

If you have a playbook, playbook.yml that only uses localhost, then you can install that playbook by including it in the same directory as your blueprint.yaml like this:

  example:
    type: cloudify.nodes.ansible.Executor
    properties:
      sources:
        inventory:
          hosts:
      playbook_path: playbook.yml
      ansible_env_vars:
        ANSIBLE_INVALID_TASK_ATTRIBUTE_FAILED: "False"
        ANSIBLE_HOST_KEY_CHECKING: "False"
        ANSIBLE_STDOUT_CALLBACK: dense

cloudify.nodes.ansible.Playbook

Stores Ansible inputs in runtime properties. Does not call ansible-playbook command. This node type is designed to be used with the cloudify.ansible.relationships.run_on_host relationship type.

Simple example of cloudify.nodes.ansible.Playbook node type:

  example:
    type: cloudify.nodes.ansible.Playbook
    properties:
      playbook_source_path: { get_input: playbook_source_path }
      playbook_path: { get_input: playbook_path }
    relationships:
      - type: cloudify.relationships.ansible.run_on_host
        target: vm
        source_interfaces:
          cloudify.interfaces.relationship_lifecycle:
            establish:
              inputs:
                sources:
                  vms:
                    hosts:
                      vm:
                        ansible_host: { get_attribute: [ vm, ip ] }
                        ansible_user: { get_input: agent_user }
                        ansible_ssh_private_key_file: { get_secret: agent_key_private }
                        ansible_become: True
                        ansible_ssh_common_args: -o StrictHostKeyChecking=no

cloudify.nodes.ansible.Ansible

Used for sharing ansible installation with extra packages and galaxy collections. While using cloudify.nodes.ansible.Ansible node and setting property ansbile_external_venv to { get_attribute: [ansible_node, playbook_venv] } where ansible_node is of type cloudify.nodes.ansible.Ansible python virtualenv would not be created for the source node and cloudify.nodes.ansible.Ansible node’s python virtualenv will be used instead.

Node Operations

Node Properties:

Example:

  ansible:
    type: cloudify.nodes.ansible.Ansible
    properties:
      extra_packages:
        - whatever
      galaxy_collections:
        - community.general

  shared_venv_collection:
    type: cloudify.nodes.ansible.Executor
    properties:
      ansible_external_venv: { get_attribute: [ansible, playbook_venv] }
      playbook_path: local/filesize.yml
      galaxy_collections:
        - community.general
      sources: *sources
    relationships:
      - type: cloudify.relationships.depends_on
        target: ansible

Workflows

Using the above cloudify.nodes.ansible.Playbook example, you can change the parameters for playbook_source_path or playbook_path:

cfy executions start reload_ansible_playbook -d {deployment_id} -p '{"playbook_source_path": "https://github.com/cloudify-community/blueprint-examples/releases/download/latest/hello-world-example.zip","playbook_path":"apache2/playbook.yaml","node_ids": ["hello-world"]}'

Playbook Run Operation

The node type cloudify.nodes.ansible.Executor and the relationship type cloudify.relationships.ansible.run_on_host both call the same operation ansible.cloudify_ansible.tasks.run.

Similar to the Script Plugin and the Fabric Plugin, you do not have to use any specify node type or relationship type. Instead, you may create new node types or modify existing node types to perform one or more of their lifecycle operations using the Ansible plugin and any additional inputs that you provide.

Operations

In addition, you can provide additional key-word args parameters to the AnsiblePlaybookFromFile class, such as options_config.

Ansible Module for Cloudify

You may use the ansible Cloudify module to store runtime properties inside of your Ansible playbooks.

requirements

Using the module:

Assuming you are using the above example for cloudify.nodes.ansible.Executor node type, your playbook.yml could be this simple:

- hosts: localhost
  tasks:
    - name: simple runtime property
      cloudify_runtime_property:
        path: hello
        value: world

This will create a property hello with the value world.

For nesting information in new or preexisting dicts, you could use . notation to indicate the dict level and key name.

- hosts: localhost
  tasks:
    - name: complex runtime property part I
      cloudify_runtime_property:
        path: foo.bar
        value: baz

    - name: complex runtime property part II
      cloudify_runtime_property:
        path: foo.qux
        value: quux

This will create a dict foo wth the value: {'bar': 'baz', 'qux': 'quux'}.

An example providing credentials instead of letting Cloudify attempt to determine them from the environment:

- hosts: localhost
  tasks:
    - name: simple runtime property
      cloudify_runtime_property:
        path: hello
        value: world
        client_kwargs:
          username: cooluser
          password: supersecret123
          tenant: nicetenant
          protocol: http

Further information:

NOTE there is a special handling for “ANSIBLE_FACT_PATH” environment variable that you can pass to ansible_env_vars property, where you could add custom .fact files -which could be executable that Ansible expect JSON on stdout. If you include files that are not executable and simply contain raw JSON then Ansible will just read them and use the data inside - when gather facts is triggered they will be part of runtime_properties.facts.ansible_local.{fact_file_name}

For example you could do something like this inside your playbook:

- hosts: all
  connection: local
  tasks:
    - name: "Set fact: output dictionary"
      set_fact:
        output_dict:
          just_a_test: "my value from ansible gathered fact !!"
    - name: "Creates facts directory if it doesn't exist"
      file:
        path: "{{ lookup('ansible.builtin.env', 'ANSIBLE_FACT_PATH') }}"
        state: directory
    - name: "Insert custom fact file"
      copy:
        content: "{{ output_dict | to_nice_json }}"
        dest: "{{ lookup('ansible.builtin.env', 'ANSIBLE_FACT_PATH') }}/custom.fact"
        mode: 0644

Inventory Sources

** There are also two methods for generating the sources parameter automatically, see using compute nodes and Relationships.

For all inventory sources, we require these parameters:

In addition, we handle these parameters if provided (and highly recommend them):

For more information on the sources format in YAML, see Ansible Inventory YAML.

Environment Management

Python virtualenv: - not installed if using provided external ansible binary path - not installed if external virtualenv provided by property, relationship or runtime property - installed in deployment’s node directory

Collections: - if collections available in a node that has local virtual environment collections will be installed in local venv site-packages - if collections available in a node that has no local virtual environment will be installed in work directory

Roles: - always installed in local working directory for the node instance

Using Compute Nodes

If your operation is mapped on the lifecycle operation of a node template derived from cloudify.nodes.Compute, we will attempt to generate the sources parameter from the node properties.

Example Compute Node

Provision some component on a VM.

  compute_and_component:
    type: cloudify.nodes.Compute
    properties:
      ip: { get_input: ip }
      agent_config:
        install_method: none
        key: { get_input: private_key_path }
        user: { get_input: username }
    interfaces:
      cloudify.interfaces.lifecycle:
        start:
          implementation: ansible.cloudify_ansible.tasks.run
          inputs:
            site_yaml_path: resources/component/site.yaml

Using Relationships

Use the cloudify.ansible.relationships.connected_to_host relationship defined in the plugin to populate the sources parameter, if the target node is derived from cloudify.nodes.Compute.

Example Relationship Usage

  component:
    type: cloudify.nodes.Root
    interfaces:
      cloudify.interfaces.lifecycle:
        start:
          implementation: ansible.cloudify_ansible.tasks.run
          inputs:
            site_yaml_path: resources/component/site.yaml
            sources: { get_attribute: [ SELF, sources ] }
    relationships:
      - type: cloudify.ansible.relationships.connected_to_host
        target: compute

  compute:
    type: cloudify.nodes.Compute
    properties:
      ip: { get_input: ip }
      agent_config:
        install_method: none
        key: { get_input: private_key_path }
        user: { get_input: username }

More Examples

Basic usage with no special node or relationship type behavior.

  my_node:
    type: cloudify.nodes.Root
    interfaces:
      cloudify.interfaces.lifecycle:
        create:
          implementation: ansible.cloudify_ansible.tasks.run
          inputs:
            site_yaml_path: resources/my_ansible_playbook/site.yaml
            sources:
              webservers:
                hosts:
                  web:
                    ansible_host: { get_input: ip }
                    ansible_user: { get_input: username }
                    ansible_ssh_private_key_file: { get_input: private_key_path }
                    ansible_become: true
                    ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

Passing run_data at runtime:

  component:
    type: cloudify.nodes.Root
    interfaces:
      cloudify.interfaces.lifecycle:
        create:
          implementation: ansible.cloudify_ansible.tasks.run
          inputs:
            site_yaml_path: resources/my_ansible_playbook/site.yaml
            sources:
              foo_group:
                hosts:
                  foo_host:
                    ansible_host: { get_input: ip }
                    ansible_user: { get_input: username }
                    ansible_ssh_private_key_file: { get_input: private_key_path }
                    ansible_become: true
                    ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
            run_data:
              foo: bar