vrandom yet another random IT blog

Managing NSX with Ansible

Overview

In this article we will cover how to manage NSX Manager and NSX Edge settings using Ansible Playbooks.

Background

You may wonder why you would ever want to do this. However when managing large numbers of environments being able to integrate NSX with your existing automation (Ansible in this example) can be very helpful.

Preparation

All you need to give this a whirl is a working NSX environment and a Linux installation with Ansible installed. If you need a jump start on this then the Getting Started page is a really good place to start. A very quick way to validate you have installed Ansible correctly is to execute the below code:

[alex@web01 ~]$ ansible localhost -m ping
localhost | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

Basic configuration

Now lets create a basic Ansible playbook. I will create the struction from scratch, however I would recommend you simply clone the repository GitHub Repo and play with the files from there.

[alex@web01 ~]$ mkdir nsx-ansible-syslog
[alex@web01 ~]$ cd nsx-ansible-syslog/
[alex@web01 nsx-ansible-syslog]$ ls
[alex@web01 nsx-ansible-syslog]$ mkdir -p ./group_vars/all ./roles/nsx/templates ./roles/nsx/tasks
[alex@web01 nsx-ansible-syslog]$ touch inventory site.yml ./roles/nsx/tasks/main.yml
[alex@web01 nsx-ansible-syslog]$ find
.
./group_vars
./group_vars/all
./roles
./roles/nsx
./roles/nsx/templates
./roles/nsx/tasks
./roles/nsx/tasks/main.yml
./inventory
./site.yml

Lets start by populating the inventory file, this contains a list of devices that Ansible is managing.

[nsx_managers:vars]
ansible_connection=local

[nsx_managers]
nsx01.lab.vrandom.com user=admin password=XXXXXX

This defines a group called nsx_managers which contains nsx01.lab.vrandom.com, and has some variables set which we will use for authentication later. The group also has a variable ansible_connection=local. This overrides the Ansible default of connecting to a target via SSH to manage it, instead all the commands will be executed locally. The reason for this will become clear shortly.

We will also create some variables to store our syslog settings in:

[alex@web01 nsx-ansible-syslog]$ cat group_vars/all/syslog.yml
---
syslog_host: vrli01.lab.vrandom.com
syslog_port: 1514
syslog_protocol: udp

Now lets create some templates we will use later on. I am deliberately using JSON and XML in the two templates to demonstrate the different formats we can use. Most NSX examples you see will be using XML data.

[alex@web01 nsx-ansible-syslog]$ cat roles/nsx/templates/manager-syslog.j2 
{ 
  "syslogServer":
    "{{ syslog_host }}",
    "port":"{{ syslog_port }}",
    "protocol":"{{ syslog_protocol }}"
}
[alex@web01 nsx-ansible-syslog]$ cat roles/nsx/templates/edge-syslog.j2
<syslog>
   <protocol>tcp</protocol>
   <serverAddresses>
      <ipAddress>{{ syslog_host }}</ipAddress>
      <ipAddress></ipAddress>
   </serverAddresses>
</syslog>

These templates will be used as payloads later in the process.

Now lets create the main workflow:

[alex@web01 nsx-ansible-syslog]$ cat roles/nsx/tasks/main.yml 
---
- name: Retrieve syslog settings
  uri:
    url: "https://{{ inventory_hostname }}/api/1.0/appliance-management/system/syslogserver"
    HEADER_Accept: "application/json"
    method: GET
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: json
  register: result

- name: Apply syslog settings
  uri:
    url: "https://{{ inventory_hostname }}/api/1.0/appliance-management/system/syslogserver"
    HEADER_Accept: "application/json"
    method: PUT
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: json
    body: "{{ lookup('template', 'manager-syslog.j2') | to_json }}"
  when: (result.json is not defined) or 
        (result.json.syslogServer != "{{ syslog_host }}") or
        (result.json.port != "{{ syslog_port }}") or
        (result.json.protocol != "{{ syslog_protocol|upper }}")

- name: Get Edge Devices
  uri:
    url: "https://{{ inventory_hostname }}/api/4.0/edges"
    HEADER_Accept: "application/json"
    method: GET
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: json
  register: edges

- name: Check Edge Syslog Settings
  uri:
    url: "https://{{ inventory_hostname }}/api/4.0/edges/{{ item.id }}/syslog/config"
    HEADER_Accept: "application/json"
    method: GET
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: json
  with_items: "{{ edges.json.edgePage.data }}"
  register: edge_syslog
  no_log: True

- name: Apply Edge syslog settings
  uri:
    url: "https://{{ inventory_hostname }}/api/4.0/edges/{{ item.item.id }}/syslog/config"
    HEADER_Content-Type: "application/xml"
    method: PUT
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: raw
    body: "{{ lookup('template', 'edge-syslog.j2') }}"
    status_code: 204
  with_items: "{{ edge_syslog.results }}"
  when: 
        (item.json.enabled == false) or
        (not (item.json.serverAddresses.ipAddress is defined and "{{ syslog_host }}" in item.json.serverAddresses.ipAddress) or
        not (item.json.protocol is defined and item.json.protocol == "tcp"))
  no_log: True

#- name: Debug request
  #debug: var=edge_syslog

There is quite a lot happening here, so lets break it down:

  1. Retrieving current NSX Manager syslog settings
  2. Setting NSX Manager syslog settings (if required)
  3. Get all NSX Edge devices
  4. Check NSX Edge syslog settings
  5. Set NSX Edge syslog settings (if required)

1. Retrieve current settings

- name: Retrieve syslog settings
  uri:
    url: "https://{{ inventory_hostname }}/api/1.0/appliance-management/system/syslogserver"
    HEADER_Accept: "application/json"
    method: GET
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: json
  register: result

This retrieves the current syslog configuration from the NSX Manager and stores the result in ‘result’ (cunning I know!)

2. Set NSX Manager Settings

- name: Apply syslog settings
  uri:
    url: "https://{{ inventory_hostname }}/api/1.0/appliance-management/system/syslogserver"
    HEADER_Accept: "application/json"
    method: PUT
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: json
    body: "{{ lookup('template', 'manager-syslog.j2') | to_json }}"
  when: (result.json is not defined) or 
        (result.json.syslogServer != "{{ syslog_host }}") or
        (result.json.port != "{{ syslog_port }}") or
        (result.json.protocol != "{{ syslog_protocol }}")

There are a number of things occuring in this block:

  1. We define a HTTP(S) PUT request which will use the template payload previously defined to set the syslog settings of the NSX Manager.
  2. There are multiple ‘when’ clauses which ensure that this block is only executed if:
    • There is no result returned result.json is not defined
    • The syslog server is not already correct result.json.syslogServer != ""
    • The syslog port is not already coorect result.json.port != ""
    • Or finally, if the syslog protocol is not correct result.json.protocol != ""

3. Retrieve all NSX Edge devices

This block of code simply retrieves a list of all NSX Edge devices managed by the NSX Manager

- name: Get Edge Devices
  uri:
    url: "https://{{ inventory_hostname }}/api/4.0/edges"
    HEADER_Accept: "application/json"
    method: GET
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: json
  register: edges

4. Check NSX Edge syslog settings

As with the NSX Manager previously we will now retrieve the current syslog configuration so we are able to validate it is correct. This block makes no changes to the environment.

- name: Check Edge Syslog Settings
  uri:
    url: "https://{{ inventory_hostname }}/api/4.0/edges/{{ item.id }}/syslog/config"
    HEADER_Accept: "application/json"
    method: GET
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: json
  with_items: "{{ edges.json.edgePage.data }}"
  register: edge_syslog
  no_log: True

5. Set NSX Edge Syslog Settings

This section is again very similar to step 2, however the validation needs to be different as the data returned from the API is not the same.

- name: Apply Edge syslog settings
  uri:
    url: "https://{{ inventory_hostname }}/api/4.0/edges/{{ item.item.id }}/syslog/config"
    HEADER_Content-Type: "application/xml"
    method: PUT
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    body_format: raw
    body: "{{ lookup('template', 'edge-syslog.j2') }}"
    status_code: 204
  with_items: "{{ edge_syslog.results }}"
  when:
        (item.json.enabled == false) or
        (not (item.json.serverAddresses.ipAddress is defined and "{{ syslog_host }}" in item.json.serverAddresses.ipAddress) or
        not (item.json.protocol is defined and item.json.protocol == "{{ syslog_protocol|lower }}"))
  no_log: True

In this example I am also using an XML template instead of the JSON template previously used. This step iterates over each NSX Edge device, and if the settings are missing, or incorrect, it will apply the syslog configuration using the supplied template. The NSX edges do not allow you to set the syslog port (which is frustrating).

You will see in the Edge sections I also add the option no_log: True. This is simply to reduce the verbosity when executing the steps, you can remove this especially during testing.

Results

So what does this give you? Lets execute the above playbook and see how it interacts with the environment ### First Run

[alex@web01 nsx-ansible-syslog]$ ansible-playbook -i inventory site.yml 

PLAY [NSX Configuration] *******************************************************

TASK [setup] *******************************************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Retrieve syslog settings] ******************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Apply syslog settings] *********************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Get Edge Devices] **************************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Check Edge Syslog Settings] ****************************************
ok: [nsx01.lab.vrandom.com] => (item=(censored due to no_log))
ok: [nsx01.lab.vrandom.com] => (item=(censored due to no_log))

TASK [nsx : Apply Edge syslog settings] ****************************************
ok: [nsx01.lab.vrandom.com] => (item=(censored due to no_log))
ok: [nsx01.lab.vrandom.com] => (item=(censored due to no_log))

PLAY RECAP *********************************************************************
nsx01.lab.vrandom.com      : ok=6    changed=0    unreachable=0    failed=0

Looking at the NSX Manager you can see the settings have been changed: manager configuration As as the NSX Edge configuration: nsx edge configuration

Second Run

Where automation gets interesting is when you run it against an environment you have already configured:

[alex@web01 nsx-ansible-syslog]$ ansible-playbook -i inventory site.yml 

PLAY [NSX Configuration] *******************************************************

TASK [setup] *******************************************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Retrieve syslog settings] ******************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Apply syslog settings] *********************************************
skipping: [nsx01.lab.vrandom.com]

TASK [nsx : Get Edge Devices] **************************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Check Edge Syslog Settings] ****************************************
ok: [nsx01.lab.vrandom.com] => (item=(censored due to no_log))
ok: [nsx01.lab.vrandom.com] => (item=(censored due to no_log))

TASK [nsx : Apply Edge syslog settings] ****************************************
skipping: [nsx01.lab.vrandom.com] => (item=(censored due to no_log)) 
skipping: [nsx01.lab.vrandom.com] => (item=(censored due to no_log)) 

PLAY RECAP *********************************************************************
nsx01.lab.vrandom.com      : ok=4    changed=0    unreachable=0    failed=0

You can see no changes have been made to the environment (note the ‘skipping’ references). This is really important as it means we can safely execute this script regularly without it making un-necessary calls or continually applying the same settings over and over again.

I am now going to change my settings so that syslog goes to a different port:

[alex@web01 nsx-ansible-syslog]$ cat ./group_vars/all/syslog.yml 
---
syslog_host: vrli01.lab.vrandom.com
syslog_port: 514
syslog_protocol: udp

Lets look at what happens when we re-run it:

alex@web01 nsx-ansible-syslog]$ ansible-playbook -i inventory site.yml 

PLAY [NSX Configuration] *******************************************************

TASK [setup] *******************************************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Retrieve syslog settings] ******************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Apply syslog settings] *********************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Get Edge Devices] **************************************************
ok: [nsx01.lab.vrandom.com]

TASK [nsx : Check Edge Syslog Settings] ****************************************
ok: [nsx01.lab.vrandom.com] => (item=(censored due to no_log))
ok: [nsx01.lab.vrandom.com] => (item=(censored due to no_log))

TASK [nsx : Apply Edge syslog settings] ****************************************
skipping: [nsx01.lab.vrandom.com] => (item=(censored due to no_log)) 
skipping: [nsx01.lab.vrandom.com] => (item=(censored due to no_log)) 

PLAY RECAP *********************************************************************
nsx01.lab.vrandom.com      : ok=5    changed=0    unreachable=0    failed=0

As desired only the NSX Manager changes, as we are not comparing the port when evaluating the NSX Edges (as they dont’t support customising it). If we were to change the syslog address then all the devices would have fresh configurations applied.

Summary

This is a very rough example of how you can consume the NSX API’s to integrate the management of the NSX environment using other automation tools.

FIN