From 01ece9dbcbbb209135b832803c3c8160d96ec2c8 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Fri, 23 Sep 2022 20:05:06 +0200 Subject: [PATCH] aruba: WIP: restore config using REST API --- action_plugins/aruba_cfg_restore.py | 83 ++++++++++++++++++++++++ ansible.cfg | 1 + playbooks/aruba.yml | 22 ++++--- roles/aruba/tasks/main.yml | 99 +++++++++++++++++++++++++++-- roles/aruba/templates/config.j2 | 40 ++++++------ 5 files changed, 211 insertions(+), 34 deletions(-) create mode 100644 action_plugins/aruba_cfg_restore.py diff --git a/action_plugins/aruba_cfg_restore.py b/action_plugins/aruba_cfg_restore.py new file mode 100644 index 0000000..de17d5e --- /dev/null +++ b/action_plugins/aruba_cfg_restore.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +import base64 +import time +import functools +import urllib.parse + +import requests +from ansible.errors import AnsibleActionFail +from ansible.plugins.action import ActionBase + + +class Aruba: + def __init__(self, base_url): + self._session = requests.session() + self._base_url = base_url + + def _url(self, url): + return urllib.parse.urljoin(self._base_url, url) + + def login(self, username, password): + response = self._session.post( + self._url("/rest/v4/login-sessions"), + json={"userName": username, "password": password}, + ) + if response.status_code != requests.codes.created: + raise AnsibleActionFail("Login failed") + + def logout(self): + response = self._session.delete(self._url("/rest/v4/login-sessions")) + if response.status_code != requests.codes.no_content: + raise AnsibleActionFail("Logout failed") + + def restore(self, config): + response = self._session.post( + self._url( + "/rest/v4/system/config/cfg_restore/payload" + ), + json={ + "config_base64_encoded": base64.b64encode(config.encode()), + "is_forced_reboot_enabled": True, + }, + ) + if response.status_code != requests.codes.accepted: + raise AnsibleActionFail("Restore failed") + + response = self._session.get( + self._url( + "/rest/v4/system/config/cfg_restore/payload/status" + ) + ) + print(response.text) + + +class ActionModule(ActionBase): + + _VALID_ARGS = frozenset(("username", "password", "config", "url")) + + def _require_arg(self, name): + try: + return self._task.args[name] + except KeyError: + raise AnsibleActionFail("Missing argument: {}".format(name)) + + def run(self, tmp=None, task_vars=None): + task_vars = task_vars or {} + result = super().run(tmp, task_vars) + + base_url = self._task.args.get("url") + username = self._require_arg("username") + password = self._require_arg("password") + config = self._require_arg("config") + + aruba = Aruba(base_url) + aruba.login(username, password) + + try: + aruba.restore(config) + except: + raise + else: + aruba.logout() + + return result diff --git a/ansible.cfg b/ansible.cfg index b04e116..9b2d21c 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -4,6 +4,7 @@ roles_path = ./roles retry_files_enabled = False inventory = ./hosts filter_plugins = ./filter_plugins +action_plugins = ./action_plugins ansible_managed = Ansible managed, modified on %Y-%m-%d %H:%M:%S nocows = 1 forks = 15 diff --git a/playbooks/aruba.yml b/playbooks/aruba.yml index 59d3eaf..d58582b 100755 --- a/playbooks/aruba.yml +++ b/playbooks/aruba.yml @@ -4,7 +4,12 @@ - test-1.switch.infra.auro.re gather_facts: false vars: - aruba__hostname: "{{ inventory_hostname }}" + aruba__api_url: "http://{{ inventory_hostname }}" + aruba__api_username: "manager" + aruba__api_password: "manager" + aruba__model: J9773A + aruba__release: YA.16.11.002 + aruba__hostname: "{{ inventory_hostname | hostname }}" aruba__rest_enabled: true aruba__ssh_enabled: true aruba__ntp_servers: @@ -17,18 +22,20 @@ aruba__dns_domain_names: - switch.infra.auro.re - infra.auro.re - - auro.re - aruba__manager_password: "P@ssw0rd!" - aruba__operator_password: "P@ssw0rd!" + - toto.auro.re + aruba__manager_password: "manager" + aruba__operator_password: "operator" aruba__default_gateways: - 10.131.0.1 - 2a09:6840:131:0:1::1 aruba__vlans: + 1: + name: Default 131: name: Switchs addresses: - - 10.131.0.1/16 - - 2a09:6840:131:0:1::1/56 + - 10.131.1.1/16 + - 2a09:6840:131:1:1::1/56 1000: name: "Client 0" 1001: @@ -62,8 +69,7 @@ 4: name: "Client 2" untagged: 1002 - speed: 1000 - duplex: half + speed_duplex: 100-full loop_protect: true 5: name: "Client 3" diff --git a/roles/aruba/tasks/main.yml b/roles/aruba/tasks/main.yml index 0cfb423..fc8d718 100644 --- a/roles/aruba/tasks/main.yml +++ b/roles/aruba/tasks/main.yml @@ -1,12 +1,97 @@ --- - -- name: Generation configuration +- name: Generate configuration set_fact: aruba__config: "{{ lookup('template', './config.j2') }}" + when: "aruba__config is not defined" + +- name: Restore configuration + aruba_cfg_restore: + url: "http://{{ inventory_hostname }}/" + username: "{{ aruba__api_username }}" + password: "{{ aruba__api_password }}" + config: "{{ aruba__config }}" + +#- name: Login to switch +# delegate_to: localhost +# uri: +# url: "{{ aruba__api_base_url }}/rest/v4/login-sessions" +# method: POST +# status_code: 201 +# body_format: json +# body: +# userName: "{{ aruba__api_username }}" +# password: "{{ aruba__api_password }}" +# register: login + +#- name: Get diff +# delegate_to: localhost +# uri: +# url: "{{ aruba__api_base_url }}/rest/v4/system/config/cfg_restore/payload/latest_diff" +# method: POST +# body_format: json +# status_code: 202 +# body: +# config_base64_encoded: "{{ aruba__config | b64encode }}" +# headers: +# Cookie: "{{ login.json.cookie }}" +# register: diff + +#- name: Diff +# debug: +# msg: "{{ diff }}" + +#- name: Get diff +# delegate_to: localhost +# uri: +# url: "{{ aruba__api_base_url }}/rest/v4/system/config/cfg_restore/payload/latest_diff/status" +# method: GET +# status_code: 200 +# headers: +# Cookie: "{{ login.json.cookie }}" +# register: diff + +#- name: Diff +# debug: +# msg: "{{ diff }}" + +#- name: Restore configuration +# delegate_to: localhost +# uri: +# url: "{{ aruba__api_base_url }}/rest/v4/system/config/cfg_restore/payload" +# method: POST +# body_format: json +# status_code: 202 +# body: +# config_base64_encoded: "{{ aruba__config | b64encode }}" +# is_forced_reboot_enabled: true +# headers: +# Cookie: "{{ login.json.cookie }}" +# register: status +# +#- name: XX +# debug: +# msg: "{{ status }}" +# +#- name: Get diff +# delegate_to: localhost +# uri: +# url: "{{ aruba__api_base_url }}/rest/v4/system/config/cfg_restore/payload/status" +# method: GET +# status_code: 200 +# headers: +# Cookie: "{{ login.json.cookie }}" +# register: diff + +#- name: Diff +# debug: +# msg: "{{ diff }}" -- name: Write configuration - delegate_to: localhost - copy: - content: "{{ aruba__config }}" - dest: /tmp/aruba.config +#- name: Logout +# delegate_to: localhost +# uri: +# url: "{{ aruba__api_base_url }}/rest/v4/login-sessions" +# method: DELETE +# status_code: 204 +# headers: +# Cookie: "{{ login.json.cookie }}" ... diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 index 5d67987..44541b7 100644 --- a/roles/aruba/templates/config.j2 +++ b/roles/aruba/templates/config.j2 @@ -1,4 +1,4 @@ -{{ ansible_managed | comment(decoration="; ") }} +; {{ aruba__model }} Configuration Editor; Created on release #{{ aruba__release }} hostname {{ aruba__hostname | hostname | truncate(32) | enquote }} @@ -24,13 +24,15 @@ ip dns server-address priority {{ loop.index }} {{ addr | ipaddr }} ip dns domain-name {{ domain | enquote }} {% endfor %} -; TODO +activate provision disable +activate software-update disable + {% if False %} snmpv3 enable snmpv3 only snmpv3 user "re2o" -;snmpv3 group ManagerPriv user "re2o" sec-model ver3 -;snmp-server community "public" Operator +snmpv3 group ManagerPriv user "re2o" sec-model ver3 +snmp-server community "public" Operator {% endif %} no cdp run @@ -50,25 +52,27 @@ set lldp_disabled = lldp admin-status {{ lldp_disabled | aruba_ints }} disable {% endif %} -password manager plaintext {{ aruba__manager_password | enquote }} +password manager sha1 {{ aruba__manager_password | hash("sha1") }} {% if aruba__operator_password is defined %} -password operator plaintext {{ aruba__operator_password | enquote }} +password operator sha1 {{ aruba__operator_password | hash("sha1") }} {% endif %} +#} {% if aruba__ssh_enabled %} ip ssh -ip ssh ciphertype aes256–ctr -ip ssh kex ecdh-sha2-nistp521 -ip ssh mac hmac-sha2-256 +{# ip ssh cipher aes256–ctr #} +{# ip ssh kex ecdh-sha2-nistp521 #} +{# ip ssh mac hmac-sha2-256 #} ip ssh filetransfer {% else %} no ip ssh {% endif %} no telnet-server +no tftp {% if aruba__rest_enabled %} -; FIXME: ssl +{# FIXME: ssl #} web-management plaintext rest-interface {% endif %} @@ -92,7 +96,7 @@ loop-protect {{ loop_protect | aruba_ints }} ip default-gateway {{ aruba__default_gateways | ipv4 | first }} {% endif %} {% if aruba__default_gateways | ipv6 %} -ipv6 default-gateway {{ aruba__default_gateways | ipv6 | first }} +{# ipv6 default-gateway {{ aruba__default_gateways | ipv6 | first }} #} {% endif %} {% for id, vlan in aruba__vlans.items() %} @@ -126,15 +130,14 @@ set tagged = {% endif %} {% if vlan.addresses | default([]) %} {% for addr in vlan.addresses | ipv4 %} - ip address {{ addr | ipaddr("address") }} {{ addr | ipaddr("netmask") }} + ip address {{ addr | ipaddr("host") }} {% endfor %} {% for addr in vlan.addresses | ipv6 %} - ipv6 address {{ addr | ipaddr("address") }} {{ addr | ipaddr("netmask") }} + ipv6 address {{ addr | ipaddr("host") }} {% endfor %} {% else %} no ip address {% endif %} - no flow-control exit {% endfor %} @@ -149,12 +152,11 @@ interface {{ id | int }} {% else %} no enable {% endif %} -{% if iface.speed is defined %} - speed {{ iface.speed | int | choices([10, 100, 1000]) }} -{% endif %} -{% if iface.duplex is defined %} - duplex {{ iface.duplex | choices(["full", "half", "auto"]) }} +{# TODO: split and check speed/duplex #} +{% if iface.speed_duplex is defined %} + speed-duplex {{ iface.speed_duplex }} {% endif %} + no flow-control exit {% endfor %}