Alcatel-Lucent Enterprise regularly reminds us: “the objective is to eventually only have Ethernet switches running AOS8”. Indeed, all the models that came out and were added to the catalog, sometimes replacing existing models, run version 8 of the AOS. In this context, it is interesting to see the possibilities offered in terms of external automation in a homogeneous environment.
As a reminder, when you are in a mixed AOS6 and AOS8 context, the possibilities are as follows:
It is the Web Services function that allows you to customize and extend the management interface on AOS8 devices. In our case, it supports the use of a REST-based web interface that interacts with AOS management variables (MIBs) and CLI commands. So, it provides two configuration methods via direct management of MIB variables or using CLI commands and supports both XML and JSON response formats.
Do not confuse REST with the RESTCONF protocol which uses the YANG data model. REST is a set of guidelines for the software architecture of distributed systems and AOS8 supports the following verbs:
Security is maintained through the use of sessions in the backend and cookies in the frontend, which is equivalent to current HTTP security for thin clients.
The following are used to create the REST URL:
The output format can be encoded using XML or JSON. The “Accept” header can be used to specify an output type:
Due to the volatile nature of the returned content, the Web Service (producer) will instruct any system positioned between it and the “consumer” (included) not to cache its output. The following headers are returned by the “producer”:
The first two headers indicate that caching should not take place. The last header is for proxy servers, informing them that the Content-Type header is a variable not to be cached. If a proxy server decided not to respect this directive, it would be possible to have unexpected behaviors such as retrieving the JSON data format after specifically requesting the XML format.
For implementing REST on AOS8, I don’t find the official documentation very helpful. Indeed, the proposal is based on the consumer .py library which is not officially maintained and whose evolution is not guaranteed.
To make Python scripts, I prefer to use the requests library which is my favorite for HTTP support. Its use is simple to manage authentication, just create a session and generate all the requests within the framework of this session.
Before being able to use the MIB, it must be analyzed by downloading it and browsing the files using a suitable tool.
---
- host: 192.168.199.193
username: admin
password: switch #!/usr/bin/env python
"""
Restconf python3 example by Gilbert MOÏSIO
Installing python dependencies:
> pip install requests pyyaml
"""
import requests
import yaml
import json
from pprint import pprint
requests.packages.urllib3.disable_warnings()
headers = {'Accept': 'application/vnd.alcatellucentaos+json'}
if __name__ == '__main__':
# get parameters from yaml file
with open('hosts.yaml') as f:
hosts = yaml.load(f, Loader=yaml.FullLoader)
# iterate onto the list
for host in hosts:
# execute requests
try:
# create a session for persistence
session = requests.Session()
# login into the switch
payload = {'username': host['username'],
'password': host['password']}
uri = f"https://{host['host']}/auth/"
session.get(uri, params=payload, verify=False)
# get MIB structure using info domain
uri = f"https://{host['host']}/info/vlanTable?"
response = session.get(uri, headers=headers, verify=False)
print('{0}{1} vlanTable MIB structure {1}{0}'.format('\n', '='*10))
pprint(json.loads(response.text)['result']['data'])
# add a new vlan using mib domain
payload = {'mibObject0': 'vlanNumber:666',
'mibObject1': 'vlanDescription:go_to_hell'}
uri = f"https://{host['host']}/mib/vlanTable"
response = session.put(
uri, data=payload, headers=headers, verify=False)
print('{0}{1} add vlan 666 {1}{0}'.format('\n', '='*10))
print(json.loads(response.text)['result']['error'])
# modify vlan description using mib domain
payload = {'mibObject0': 'vlanNumber:666',
'mibObject1': 'vlanDescription:this_one_is_better'}
uri = f"https://{host['host']}/mib/vlanTable"
response = session.post(
uri, data=payload, headers=headers, verify=False)
print('{0}{1} change vlan 666 description {1}{0}'.format('\n', '='*10))
print(json.loads(response.text)['result']['error'])
# get vlan informations with CLI command
payload_str = '&cmd=show+vlan+666'
uri = f"https://{host['host']}/cli/aos"
response = session.get(uri, headers=headers,
params=payload_str, verify=False)
print('{0}{1} vlan 666 informations {1}{0}'.format('\n', '='*10))
print(json.loads(response.text)['result']['output'])
# delete vlan using mib domain
payload = {'mibObject0': 'vlanNumber:666'}
uri = f"https://{host['host']}/mib/vlanTable"
response = session.delete(
uri, data=payload, headers=headers, verify=False)
print('{0}{1} delete vlan 666 {1}{0}'.format('\n', '='*10))
print(json.loads(response.text)['result']['error'])
# logout from the switch
uri = f"https://{host['host']}/auth/?"
session.get(uri, verify=False)
except requests.exceptions.RequestException as e:
print(e._raw)Whether in XML or JSON format, the response contains the following elements:
[ale]
6465T ansible_host=192.168.199.193
[ale:vars]
username=admin
password=switch [defaults]
inventory = ./hosts
host_key_checking = False
roles_path = roles.galaxy:roles
stdout_callback = yaml ---
- name: This is a test to request AOS8 API
hosts: ale
vars:
ansible_python_interpreter: "python"
connection: local
gather_facts: no
tasks:
- name: Login into the switch
uri:
url: "https://{{ ansible_host }}/auth/?username={{ username }}&password={{ password }}"
validate_certs: no
method: GET
register: login
- name: get MIB structure using info domain
uri:
url: "https://{{ ansible_host }}/info/vlanTable?"
validate_certs: no
method: GET
return_content: yes
headers:
Accept: "application/vnd.alcatellucentaos+json"
Cookie: "{{ login.cookies_string }}"
register: result
- name: Print body of the response
debug:
msg:
- "{{ result.json.result.data }}"
when: result.json.result.diag == 200
- name: Add a new vlan using mib domain
uri:
url: "https://{{ ansible_host }}/mib/vlanTable"
validate_certs: no
method: PUT
headers:
Accept: "application/vnd.alcatellucentaos+json"
Cookie: "{{ login.cookies_string }}"
body_format: form-urlencoded
body:
mibObject0: "vlanNumber:666"
mibObject1: "vlanDescription:go_to_hell"
register: result
- name: Print body of the response
debug:
msg:
- "{{ result.json.result.error }}"
when: result.json.result.diag == 200
- name: Modify vlan description using mib domain
uri:
url: "https://{{ ansible_host }}/mib/vlanTable"
validate_certs: no
method: POST
headers:
Accept: "application/vnd.alcatellucentaos+json"
Cookie: "{{ login.cookies_string }}"
body_format: form-urlencoded
body:
mibObject0: "vlanNumber:666"
mibObject1: "vlanDescription:this_one_is_better"
register: result
- name: Print body of the response
debug:
msg:
- "{{ result.json.result.error }}"
when: result.json.result.diag == 200
- name: Get vlan informations with CLI command
uri:
url: "https://{{ ansible_host }}/cli/aos?cmd=show+vlan+666"
validate_certs: no
method: GET
return_content: yes
headers:
Accept: "application/vnd.alcatellucentaos+json"
Cookie: "{{ login.cookies_string }}"
register: result
- name: Print body of the response
debug:
msg:
- "{{ result.json.result.output }}"
when: result.json.result.diag == 200
- name: Delete vlan using mib domain
uri:
url: "https://{{ ansible_host }}/mib/vlanTable"
validate_certs: no
method: DELETE
headers:
Accept: "application/vnd.alcatellucentaos+json"
Cookie: "{{ login.cookies_string }}"
body_format: form-urlencoded
body:
mibObject0: "vlanNumber:666"
register: result
- name: Print body of the response
debug:
msg:
- "{{ result.json.result.error }}"
when: result.json.result.diag == 200
- name: Logout from the switch
uri:
url: "https://{{ ansible_host }}/auth/?"
validate_certs: no
method: GET