Part four of my autonomous cloud management (ACM) tutorial series. In this article we’ll look at an API driven way to apply tags to all of your entities (hosts, processes, process groups / clusters and services).
This tutorial series builds from previous tutorials. I recommend you complete parts 1 through 3 first:
Tutorial Aim
This tutorial will leverage the custom metadata we’ve already applied, we’ll create automatic (dynamic) tag rules so that we can use these values as filters.
Recap
Recall that in the previous tutorials we added the following metadata tags to each host (with unique values for each host):
"metadata": {
"Location": "DE",
"ConsumingRegion": "Global",
"Owner": "Alice",
"ChargeCode": "Central"
}
It would be extremely powerful to have the ability to use these values as filters:
- Show me all hosts with a
ChargeCode
ofCentral
. - Show me all hosts and services for which
Alice
is responsible. - Show me all hosts in
France
.
How?
API Token
The first thing we need is an API Token. Go to Settings > Integration > Dynatrace API and create your token.
Make sure it has at least Write configuration
permissions.
Call API Via Ansible
We’ll use Ansible to call the Dynatrace API. This will create the rule definitions. We’ll create a separate task for each rule.
First, add two variables to the vars
section near the top of your playlist: tenant_url
and api_write_token
. Adjust the values as appropriate for your environment.
vars:
tenant_url: abc123456.live.dynatrace.com
api_write_token: xYz123ABCdef678
Now create one API call. I’ll define it, explain it below, then copy & paste 3 more times, modifying as we go:
- name: Create ChargeCode Auto Tag Rule
run_once: true
uri:
method: PUT
status_code: 201, 204
body_format: json
url: https://{{ tenant_url }}/api/config/v1/autoTags/12345678-1234-1234-1234-123456789012?Api-Token={{api_write_token }}
body: |
{
"name": "ChargeCode",
"rules": [
{
"type": "PROCESS_GROUP",
"enabled": true,
"valueFormat": "{Host:Environment:ChargeCode}",
"propagationTypes": [
"PROCESS_GROUP_TO_HOST",
"PROCESS_GROUP_TO_SERVICE"
],
"conditions": [
{
"key": {
"attribute": "HOST_CUSTOM_METADATA",
"dynamicKey": {
"source": "ENVIRONMENT",
"key": "ChargeCode"
},
"type": "HOST_CUSTOM_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}
]
}
]
}
Task Explanation
We could improve the Ansible code here in numerous ways. If you’re comfortable enough with Ansible, please feel free to do so. I’ve deliberately left the example verbose for clarity.
The task is called Create ChargeCode Auto Tag Rule
. Remember that we have two hosts in our inventory. We only need to execute this API call (aka run this task) once. Hence the run_once command.
We are executing a PUT
call to the API and the URL format is:
{% raw %}
https://TENANT-URL/api/config/v1/autoTags/TAG-UUID?Api-token=API-WRITE-TOKEN
Note that the UUID
is something you define and it must be in the standard 8-4-4-4-12
format. Being a unique ID, it must also be, err, unique, for each tag. Clever huh?
Within the PUT
call, we will be passing in body content which defines our tag rule. The format of the body is JSON. Hence body_format: json
.
The Dynatrace API returns one of two HTTP response codes on a successful call. A 201
response means everything was OK & your tag rule was successfully created. A 204
code means that your tag was updated successfully. This means you can re-execute your Ansible playbook multiple times with no ill effects. The status_code
rule tells Ansible to consider 201
and 204
as successful calls.
body
is obviously the PUT
body content. Every tag is a Key=Value
pair. The Key is represented by the name parameter. This tag rule will be enabled and the ruleset will match against to process groups.
Any tags applied to the process group will be propagated down to the hosts that they run on. The tags will also be propagated up to the services offered by those processes / process groups. This is denoted within the propagationTypes
parameter.
The conditions
and comparisonInfo
work together to denote that this tag rule will match whenever the String value of ChargeCode
exists. If found, the value of this tag will be pulled from the value of the host metadata (see the valueFormat
parameter).
All together, this means that whenever the ChargeCode
host metadata tag is found, Dynatrace will create a tag with the Key=ChargeCode
and a dynamic value.
CREATE OTHER TAG API CALLS
Copy and paste the above Ansible tasks 3 more times. Change the UUID value for each and change ChargeCode to match the other Keys (ConsumingRegion, Location and Owner).
Your final playbook (including previous tutorials) should look something like this:
---
- name: ACM Tutorial Playbook
hosts: apache
vars:
hostList: "{{ lookup('file', 'hostList.json') }}"
oneagent_installer_script_url: "https://{{ tenant_url}}/api/v1/deployment/installer/agent/unix/default/latest?Api-Token=***&arch=x86&flavor=default"
defaultHostGroup: "DefaultGroup"
tenant_url: "***.live.dynatrace.com"
api_write_token: "***"
tasks:
- name: Check if Dynatrace OneAgent is already installed
stat:
path: /opt/dynatrace/oneagent/agent/lib64/liboneagentos.so
register: agent_installed
- name: Set Host Group Facts
set_fact:
hostGroup: "{{ item.hostGroup }}"
loop: "{{ hostList }}"
when: item.hostname == inventory_hostname
- name: Download OneAgent
get_url:
url: "{{ oneagent_installer_script_url }}"
dest: "/tmp/dynatrace-oneagent.sh"
when: agent_installed.stat.exists == False
- name: Install Agent
shell: "sh /tmp/dynatrace-oneagent.sh --set-app-log-content-access=true --set-host-group={{ hostvars[inventory_hostname].hostGroup | default(defaultHostGroup) }}"
become: yes
when: agent_installed.stat.exists == False
- name: Get Current Host Group
shell: "/opt/dynatrace/oneagent/agent/tools/lib64/oneagentutil --get-host-group"
become: yes
when: agent_installed.stat.exists == True
register: currentHostGroup
- name: Update Host Group
shell: "/opt/dynatrace/oneagent/agent/tools/lib64/oneagentutil --set-host-group={{ hostvars[inventory_hostname].hostGroup | default(defaultHostGroup) }} --restart-service"
become: yes
when: agent_installed.stat.exists == True and currentHostGroup.stdout != (hostvars[inventory_hostname].hostGroup | default(defaultHostGroup))
- name: "Set Facts"
set_fact:
"metadata" : "{{ item.metadata }}"
loop: "{{ hostList }}"
when: item.hostname == inventory_hostname
- name: "Remove File if it Exists"
become: yes
file:
path: /var/lib/dynatrace/oneagent/agent/config/hostcustomproperties.conf
state: absent
- name: "Write KVs to File"
become: yes
lineinfile:
path: /var/lib/dynatrace/oneagent/agent/config/hostcustomproperties.conf
line: "{{ item.key }}={{ item.value }}"
create: yes
with_dict: "{{ hostvars[inventory_hostname]['metadata'] }}"
- name: Install HTTPD (Apache)
become: yes
package:
name: httpd
state: present
- name: Start HTTPD (Apache)
become: yes
service:
name: httpd
state: started
- name: Create ChargeCode Auto Tag Rule
run_once: true
uri:
method: PUT
status_code: 201, 204
body_format: json
url: https://{{ tenant_url }}/api/config/v1/autoTags/12345678-1234-1234-1234-123456789012?Api-Token={{ api_write_token }}
body: |
{
"name": "ChargeCode",
"rules": [
{
"type": "PROCESS_GROUP",
"enabled": true,
"valueFormat": "{Host:Environment:ChargeCode}",
"propagationTypes": [
"PROCESS_GROUP_TO_HOST",
"PROCESS_GROUP_TO_SERVICE"
],
"conditions": [
{
"key": {
"attribute": "HOST_CUSTOM_METADATA",
"dynamicKey": {
"source": "ENVIRONMENT",
"key": "ChargeCode"
},
"type": "HOST_CUSTOM_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}
]
}
]
}
- name: Create ConsumingRegion Auto Tag Rule
run_once: true
uri:
method: PUT
status_code: 201, 204
body_format: json
url: https://{{ tenant_url }}/api/config/v1/autoTags/23456789-2345-2345-2345-234567890123?Api-Token={{ api_write_token }}
body: |
{
"name": "ConsumingRegion",
"rules": [
{
"type": "PROCESS_GROUP",
"enabled": true,
"valueFormat": "{Host:Environment:ConsumingRegion}",
"propagationTypes": [
"PROCESS_GROUP_TO_HOST",
"PROCESS_GROUP_TO_SERVICE"
],
"conditions": [
{
"key": {
"attribute": "HOST_CUSTOM_METADATA",
"dynamicKey": {
"source": "ENVIRONMENT",
"key": "ConsumingRegion"
},
"type": "HOST_CUSTOM_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}
]
}
]
}
- name: Create Location Auto Tag Rule
run_once: true
uri:
method: PUT
status_code: 201, 204
body_format: json
url: https://{{ tenant_url }}/api/config/v1/autoTags/34567890-3456-3456-3456-345678901234?Api-Token={{ api_write_token }}
body: |
{
"name": "Location",
"rules": [
{
"type": "PROCESS_GROUP",
"enabled": true,
"valueFormat": "{Host:Environment:Location}",
"propagationTypes": [
"PROCESS_GROUP_TO_HOST",
"PROCESS_GROUP_TO_SERVICE"
],
"conditions": [
{
"key": {
"attribute": "HOST_CUSTOM_METADATA",
"dynamicKey": {
"source": "ENVIRONMENT",
"key": "Location"
},
"type": "HOST_CUSTOM_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}
]
}
]
}
- name: Create Owner Auto Tag Rule
run_once: true
uri:
method: PUT
status_code: 201, 204
body_format: json
url: https://{{ tenant_url }}/api/config/v1/autoTags/45678901-4567-4567-4567-456789012345?Api-Token={{ api_write_token }}
body: |
{
"name": "Owner",
"rules": [
{
"type": "PROCESS_GROUP",
"enabled": true,
"valueFormat": "{Host:Environment:Owner}",
"propagationTypes": [
"PROCESS_GROUP_TO_HOST",
"PROCESS_GROUP_TO_SERVICE"
],
"conditions": [
{
"key": {
"attribute": "HOST_CUSTOM_METADATA",
"dynamicKey": {
"source": "ENVIRONMENT",
"key": "Owner"
},
"type": "HOST_CUSTOM_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}
]
}
]
}
We now have hosts, processes, process groups and services all automatically tagged based on the content of the host metadata. The host metadata is, in itself, dynamic because it’s based on a JSON file.
As always, the latest version of the host list JSON and playbook is available on Github.