How to define a service catalog using an ansible playbook?

In the previous example, we used a python script to implement a software catalog. In this example, we use an ansible playbook to create a mongodb.

Step 1. Import TOSCA types

imports:
- tosca-normative-types:1.0.0-ALIEN20

Step 2. Implement the interfaces

Implement the node MongoDB with the create interfaces and provide an ansible script for the interface (e.g., mongodb_install.yaml):

node_types:
myservice.nodes.SoftwareComponent.MongoDB:
derived_from: tosca.nodes.SoftwareComponent
...
interfaces:
Standard:
create:
inputs:
# HOST is the keyword to get more information about the hosted compute node at runtime
IP_ADDRESS: { get_attribute: [HOST, private_address] }
MONGODB_PORT: { get_property: [SELF, port] }
# ansible script
implementation: playbooks/mongodb_install.yaml
note
  • Use the keyword HOST to get information of the hosted compute node.
  • Use the function get_attribute to get the runtime attribute private_address of the hosted compute node. The compute node also has the following attributes available at runtime that you can use by default: private_address, public_address:

Runtime attributes of a compute node

Step 3a. Implement an ansible playbook for the interface

Write an ansible script (e.g., mongodb_install.yaml), which imports a third party ansible role undergreen.ansible-role-mongodb and uses it to install mongodb:

# playbooks/mongodb_install.yaml
- name: Install MongoDB
# "hosts" must be set to all. Otherwise the deployment will fail.
hosts: all
strategy: free
become: true
become_method: sudo
tasks:
- name: Install MongoDB using a 3rd party role
import_role:
name: undergreen.ansible-role-mongodb
vars:
# The environment variables IP_ADDRESS and MONGODB_PORT are the
# input parameters of the create interface.
mongodb_net_bindip: "{{ IP_ADDRESS }}"
mongodb_net_port: "{{ MONGODB_PORT }}"
...

In the same folder of the ansible script, we have an ansible role available at playbooks/roles/undergreen.ansible-role-mongodb

Expected result

The orchestration engine will apply the ansible playbook mongodb_install.yaml on the remote compute node, which in turn imports the ansible role undergreen.ansible-role-mongodb to create the mongodb.

Step 3b. (alternative) Zip the whole playbook folder

Define ansible roles

  • In this scenario, we create a folder (e.g., playbook) contaning more than one roles (e.g., create, configure, delete, start, and stop).
  • Each roles has an entry script (e.g., the entry script create.yml imports the role create).

Zip the playbook folder

Zip the content of the playbook folder into the file playbook/playbook.ansible

cd playbook && rm -f playbook.ansible && zip -r playbook.ansible *

Implement the interface

In the create interfaces, we specify the zip file as an implementation and set the PLAYBOOK_ENTRY to the entry script create.yml:

interfaces:
Standard:
create:
inputs:
# the entry script in the playbook folder
PLAYBOOK_ENTRY: create.yml
# the zip file of the playbook folder content
implementation: playbook/playbook.ansible

Step 4. Define the outputs (optional)

  • In the ansible script, use set_fact to define an output:
- name: my tasks
hosts: all
tasks:
...
- name: Set an output
set_fact:
OUTPUT: "A result of my tasks"
note

See known limitations of outputs belows

Expected result

The orchestration engine will apply the entry script create.yml, which in turn imports the role create (in the zip file) to create the component on the remote compute.

Full example

Known limitations

1. Output a comma character

  • The output is a string but cannot contain a comma character ,. For a workaround, use encode:
set_fact:
# ca.cert may contain a comma, so we encode it
CA_CERT: "{{ lookup('file', '/tmp/ca.cert') | b64encode }}"

2. Output with become true

  • The orchestrator cannot read the output if set_fact is used together with become: true:
- name: my tasks
hosts: all
# cannot get output
become: true
tasks:
...
- name: Set an output
set_fact:
OUTPUT: "A result of my tasks"
  • For a workaround, use become: true in a task where you need it:
- name: my tasks
hosts: all
tasks:
- name: A task with become true
become: true
...
- name: Set an output without become true
set_fact:
OUTPUT: "A result of my tasks"