From 8d596082b3d7969c3f0c033d76a0d1d5d9af6eb1 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 07:53:23 +0200 Subject: [PATCH 01/10] aruba: add minimal (untested) role --- filter_plugins/enquote.py | 16 ++++ filter_plugins/list_utils.py | 9 ++ roles/aruba/defaults/main.yml | 9 ++ roles/aruba/tasks/main.yml | 12 +++ roles/aruba/templates/config.j2 | 154 ++++++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 filter_plugins/enquote.py create mode 100644 filter_plugins/list_utils.py create mode 100644 roles/aruba/defaults/main.yml create mode 100644 roles/aruba/tasks/main.yml create mode 100644 roles/aruba/templates/config.j2 diff --git a/filter_plugins/enquote.py b/filter_plugins/enquote.py new file mode 100644 index 0000000..576bf3f --- /dev/null +++ b/filter_plugins/enquote.py @@ -0,0 +1,16 @@ +class FilterModule: + def filters(self): + return { + "enquote": enquote, + } + + +def enquote(string, delimiter='"', escape="\\"): + translation = str.maketrans( + { + delimiter: f"{escape}{delimiter}", + escape: f"{escape}{escape}", + } + ) + escaped = string.translate(translation) + return f"{delimiter}{escaped}{delimiter}" diff --git a/filter_plugins/list_utils.py b/filter_plugins/list_utils.py new file mode 100644 index 0000000..264a6f1 --- /dev/null +++ b/filter_plugins/list_utils.py @@ -0,0 +1,9 @@ +class FilterModule: + def filters(self): + return { + "contains": contains, + } + + +def contains(a, b): + return b in a diff --git a/roles/aruba/defaults/main.yml b/roles/aruba/defaults/main.yml new file mode 100644 index 0000000..0b4071d --- /dev/null +++ b/roles/aruba/defaults/main.yml @@ -0,0 +1,9 @@ +--- +aruba__ntp_servers: [] +aruba__vlans: {} +aruba__interfaces: {} +aruba__default_gateways: [] +aruba__ssh_enabled: False +aruba__loop_protect_disable_timer: 30 +aruba__loop_protect_tx_interval: 3 +... diff --git a/roles/aruba/tasks/main.yml b/roles/aruba/tasks/main.yml new file mode 100644 index 0000000..0cfb423 --- /dev/null +++ b/roles/aruba/tasks/main.yml @@ -0,0 +1,12 @@ +--- + +- name: Generation configuration + set_fact: + aruba__config: "{{ lookup('template', './config.j2') }}" + +- name: Write configuration + delegate_to: localhost + copy: + content: "{{ aruba__config }}" + dest: /tmp/aruba.config +... diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 new file mode 100644 index 0000000..3519f92 --- /dev/null +++ b/roles/aruba/templates/config.j2 @@ -0,0 +1,154 @@ +{{ ansible_managed | comment(decoration="; ") }} + +hostname {{ aruba__hostname | enquote }} + +include-credentials + +{% if aruba__ntp_servers %} +timesync ntp +ntp unicast +{% for address in aruba__ntp_servers %} +ntp server {{ address | ipaddr }} iburst +{% endfor %} +{% if aruba__timezone == "Europe/Paris" %} +time daylight-time-rule western-europe +time timezone 60 +{% endif %} +{% endif %} + +{% for address in aruba__dns_servers[:2] %} +ip dns server-address priority {{ loop.index }} {{ address | ipaddr }} +{% endfor %} + +{% if aruba__dns_domain_name is defined %} +ip dns domain-name {{ aruba__dns_domain_name | enquote }} +{% endif %} + +; TODO +{% if False %} +snmpv3 enable +snmpv3 only +snmpv3 user "re2o" +;snmpv3 group ManagerPriv user "re2o" sec-model ver3 +;snmp-server community "public" Operator +{% endif %} + +no cdp run +lldp run + +{% +set lldp_enabled = + aruba__interfaces + | dict2items + | selectattr("value.lldp", "defined") + | selectattr("value.lldp", "==", True) + | map(attribute="key") +%} +{% +set lldp_disabled = + aruba__interfaces.keys() + | difference(lldp_enabled) + | map("int") + | list +%} +{% if lldp_disabled %} +lldp admin-status {{ lldp_disabled | join(",") }} disable +{% endif %} + +password manager plaintext {{ aruba__manager_password | enquote }} +{% if aruba__operator_password is defined %} +password operator plaintext {{ aruba__operator_password | enquote }} +{% 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 filetransfer +{% else %} +no ip ssh +{% endif %} + +no telnet-server +; FIXME: ssl +web-management plaintext +rest-interface + +{% +set loop_protect = + aruba__interfaces + | dict2items + | selectattr("value.loop_protect", "defined") + | selectattr("value.loop_protect") + | map(attribute="key") + | map("int") + | list +%} +{% if loop_protect %} +loop-protect disable-timer {{ aruba__loop_protect_disable_timer | int }} +loop-protect transmit-interval {{ aruba__loop_protect_tx_interval | int }} +loop-protect {{ loop_protect | join(",") }} +{% endif %} + +{% if aruba__default_gateways | ipv4 %} +ip default-gateway {{ aruba__default_gateways | ipv4 | first }} +{% endif %} +{% if aruba__default_gateways | ipv6 %} +ipv6 default-gateway {{ aruba__default_gateways | ipv6 | first }} +{% endif %} + +{% for id, vlan in aruba__vlans.items() %} +vlan {{ id | int }} +{% if vlan.name is defined %} + name {{ vlan.name | enquote }} +{% endif %} +{% +set untagged = + aruba__interfaces + | dict2items + | selectattr("value.untagged", "defined") + | selectattr("value.untagged", "==", id) + | map(attribute="key") + | map("int") + | list +%} +{% if untagged %} + untagged {{ untagged | join(",") }} +{% endif %} +{% +set tagged = + aruba__interfaces + | dict2items + | selectattr("value.tagged", "defined") + | selectattr("value.tagged", "contains", id) + | map(attribute="key") + | map("int") + | list +%} +{% if tagged %} + tagged {{ tagged | join(",") }} +{% endif %} +{% if vlan.addresses | default([]) %} +{% for address in vlan.addresses | ipv4 %} + ip address {{ address }} +{% endfor %} +{% for address in vlan.addresses | ipv6 %} + ipv6 address {{ address }} +{% endfor %} +{% else %} + no ip address +{% endif %} + no flow-control + exit + +{% endfor %} + +{% for id, iface in aruba__interfaces.items() %} +interface {{ id | int }} +{% if iface.name is defined %} + name {{ iface.name | enquote }} +{% endif %} + exit + +{% endfor %} -- 2.38.5 From fa9646611697e779cd092f87fbdb75a36857590e Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 09:10:42 +0200 Subject: [PATCH 02/10] aruba: add aruba_ints filter --- filter_plugins/aruba.py | 17 +++++++++++++++++ roles/aruba/templates/config.j2 | 17 +++++++++-------- 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 filter_plugins/aruba.py diff --git a/filter_plugins/aruba.py b/filter_plugins/aruba.py new file mode 100644 index 0000000..c733394 --- /dev/null +++ b/filter_plugins/aruba.py @@ -0,0 +1,17 @@ +class FilterModule: + def filters(self): + return { + "aruba_ints": aruba_ints, + } + + +def aruba_ints(seq, sep=",", hyphen="-"): + ranges = [] + for value in sorted(seq): + if not ranges or ranges[-1][1] + 1 != value: + ranges.append((value, value)) + else: + ranges[-1] = (ranges[-1][0], value) + return sep.join( + (f"{a}" if a == b else f"{a}{hyphen}{b}" for a, b in ranges) + ) diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 index 3519f92..86a2e97 100644 --- a/roles/aruba/templates/config.j2 +++ b/roles/aruba/templates/config.j2 @@ -48,11 +48,10 @@ set lldp_enabled = set lldp_disabled = aruba__interfaces.keys() | difference(lldp_enabled) - | map("int") | list %} {% if lldp_disabled %} -lldp admin-status {{ lldp_disabled | join(",") }} disable +lldp admin-status {{ lldp_disabled | aruba_ints }} disable {% endif %} password manager plaintext {{ aruba__manager_password | enquote }} @@ -82,13 +81,12 @@ set loop_protect = | selectattr("value.loop_protect", "defined") | selectattr("value.loop_protect") | map(attribute="key") - | map("int") | list %} {% if loop_protect %} loop-protect disable-timer {{ aruba__loop_protect_disable_timer | int }} loop-protect transmit-interval {{ aruba__loop_protect_tx_interval | int }} -loop-protect {{ loop_protect | join(",") }} +loop-protect {{ loop_protect | aruba_ints }} {% endif %} {% if aruba__default_gateways | ipv4 %} @@ -110,11 +108,10 @@ set untagged = | selectattr("value.untagged", "defined") | selectattr("value.untagged", "==", id) | map(attribute="key") - | map("int") | list %} {% if untagged %} - untagged {{ untagged | join(",") }} + untagged {{ untagged | aruba_ints }} {% endif %} {% set tagged = @@ -123,11 +120,10 @@ set tagged = | selectattr("value.tagged", "defined") | selectattr("value.tagged", "contains", id) | map(attribute="key") - | map("int") | list %} {% if tagged %} - tagged {{ tagged | join(",") }} + tagged {{ tagged | aruba_ints }} {% endif %} {% if vlan.addresses | default([]) %} {% for address in vlan.addresses | ipv4 %} @@ -148,6 +144,11 @@ set tagged = interface {{ id | int }} {% if iface.name is defined %} name {{ iface.name | enquote }} +{% endif %} +{% if iface.enabled | default(True) %} + enable +{% else %} + no enable {% endif %} exit -- 2.38.5 From c070990b0c225b0f3024aa996aee0ae38a988502 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 09:17:53 +0200 Subject: [PATCH 03/10] aruba: remove unused lldp_enabled variable --- roles/aruba/templates/config.j2 | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 index 86a2e97..b273757 100644 --- a/roles/aruba/templates/config.j2 +++ b/roles/aruba/templates/config.j2 @@ -36,18 +36,14 @@ snmpv3 user "re2o" no cdp run lldp run -{% -set lldp_enabled = - aruba__interfaces - | dict2items - | selectattr("value.lldp", "defined") - | selectattr("value.lldp", "==", True) - | map(attribute="key") -%} {% set lldp_disabled = aruba__interfaces.keys() - | difference(lldp_enabled) + | difference(aruba__interfaces + | dict2items + | selectattr("value.lldp", "defined") + | selectattr("value.lldp", "==", True) + | map(attribute="key")) | list %} {% if lldp_disabled %} -- 2.38.5 From eee4ec26b2e1a66d23b5655a440e68cdeb982585 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 09:25:30 +0200 Subject: [PATCH 04/10] aruba: add netmask to 'ip address' commands --- roles/aruba/templates/config.j2 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 index b273757..be62f83 100644 --- a/roles/aruba/templates/config.j2 +++ b/roles/aruba/templates/config.j2 @@ -7,8 +7,8 @@ include-credentials {% if aruba__ntp_servers %} timesync ntp ntp unicast -{% for address in aruba__ntp_servers %} -ntp server {{ address | ipaddr }} iburst +{% for addr in aruba__ntp_servers %} +ntp server {{ addr | ipaddr }} iburst {% endfor %} {% if aruba__timezone == "Europe/Paris" %} time daylight-time-rule western-europe @@ -16,8 +16,8 @@ time timezone 60 {% endif %} {% endif %} -{% for address in aruba__dns_servers[:2] %} -ip dns server-address priority {{ loop.index }} {{ address | ipaddr }} +{% for addr in aruba__dns_servers[:2] %} +ip dns server-address priority {{ loop.index }} {{ addr | ipaddr }} {% endfor %} {% if aruba__dns_domain_name is defined %} @@ -122,11 +122,11 @@ set tagged = tagged {{ tagged | aruba_ints }} {% endif %} {% if vlan.addresses | default([]) %} -{% for address in vlan.addresses | ipv4 %} - ip address {{ address }} +{% for addr in vlan.addresses | ipv4 %} + ip address {{ addr | ipaddr("address") }} {{ addr | ipaddr("netmask") }} {% endfor %} -{% for address in vlan.addresses | ipv6 %} - ipv6 address {{ address }} +{% for addr in vlan.addresses | ipv6 %} + ipv6 address {{ addr | ipaddr("address") }} {{ addr | ipaddr("netmask") }} {% endfor %} {% else %} no ip address -- 2.38.5 From 4c8e296d5a8f5504e76e87a4db490cec2dd8a2df Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 09:37:15 +0200 Subject: [PATCH 05/10] aruba: add aruba__rest_enabled --- roles/aruba/defaults/main.yml | 1 + roles/aruba/templates/config.j2 | 3 +++ 2 files changed, 4 insertions(+) diff --git a/roles/aruba/defaults/main.yml b/roles/aruba/defaults/main.yml index 0b4071d..8533a55 100644 --- a/roles/aruba/defaults/main.yml +++ b/roles/aruba/defaults/main.yml @@ -4,6 +4,7 @@ aruba__vlans: {} aruba__interfaces: {} aruba__default_gateways: [] aruba__ssh_enabled: False +aruba__rest_enabled: True aruba__loop_protect_disable_timer: 30 aruba__loop_protect_tx_interval: 3 ... diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 index be62f83..642e644 100644 --- a/roles/aruba/templates/config.j2 +++ b/roles/aruba/templates/config.j2 @@ -66,9 +66,12 @@ no ip ssh {% endif %} no telnet-server + +{% if aruba__rest_enabled %} ; FIXME: ssl web-management plaintext rest-interface +{% endif %} {% set loop_protect = -- 2.38.5 From c7e6a7bd504a8cb60b3af774ad45b6067ad5c860 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 09:44:03 +0200 Subject: [PATCH 06/10] aruba: add sample playbook --- hosts | 1 + playbooks/aruba.yml | 74 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100755 playbooks/aruba.yml diff --git a/hosts b/hosts index 52f5078..7c3c07d 100644 --- a/hosts +++ b/hosts @@ -96,6 +96,7 @@ radius-fleming.adm.auro.re dns-1.int.infra.auro.re isp-1.rtr.infra.auro.re isp-2.rtr.infra.auro.re +test-1.switch.infra.auro.re dhcp-1.isp.auro.re dhcp-2.isp.auro.re radius-fleming-backup.adm.auro.re diff --git a/playbooks/aruba.yml b/playbooks/aruba.yml new file mode 100755 index 0000000..972cfa1 --- /dev/null +++ b/playbooks/aruba.yml @@ -0,0 +1,74 @@ +#!/usr/bin/env ansible-playbook +--- +- hosts: + - test-1.switch.infra.auro.re + gather_facts: false + vars: + aruba__hostname: "{{ inventory_hostname }}" + aruba__rest_enabled: true + aruba__ssh_enabled: true + aruba__ntp_servers: + - 10.128.0.1 + - 2a09:6840:128:0:1::1 + aruba__timezone: Europe/Paris + aruba__dns_servers: + - 10.128.0.1 + - 2a09:6840:128:0:1::1 + aruba__dns_domain_name: switch.infra.auro.re + aruba__manager_password: "P@ssw0rd!" + aruba__operator_password: "P@ssw0rd!" + aruba__default_gateways: + - 10.131.0.1 + - 2a09:6840:131:0:1::1 + aruba__vlans: + 131: + name: Switchs + addresses: + - 10.131.0.1/16 + - 2a09:6840:131:0:1::1/56 + 1000: + name: "Client 0" + 1001: + name: "Client 1" + 1002: + name: "Client 2" + 1003: + name: "Client 3" + 1004: + name: "Client 4" + aruba__interfaces: + 1: + name: Uplink + untagged: 131 + tagged: + - 1000 + - 1001 + - 1002 + - 1003 + - 1004 + loop_protect: true + lldp: true + 2: + name: "Client 0" + untagged: 1000 + loop_protect: true + 3: + name: "Client 1" + untagged: 1001 + loop_protect: true + 4: + name: "Client 2" + untagged: 1002 + speed: 100fdx + loop_protect: true + 5: + name: "Client 3" + untagged: 1003 + loop_protect: true + 6: + name: "Client 4" + untagged: 1004 + loop_protect: true + roles: + - aruba +... -- 2.38.5 From b55d81d6cc817042aaa4bb9856f8a767388b1f01 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 10:18:43 +0200 Subject: [PATCH 07/10] aruba: add support for speed + duplex --- filter_plugins/validators.py | 11 +++++++++++ playbooks/aruba.yml | 3 ++- roles/aruba/templates/config.j2 | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 filter_plugins/validators.py diff --git a/filter_plugins/validators.py b/filter_plugins/validators.py new file mode 100644 index 0000000..b435511 --- /dev/null +++ b/filter_plugins/validators.py @@ -0,0 +1,11 @@ +class FilterModule: + def filters(self): + return { + "choices": choices, + } + + +def choices(value, choices): + if value not in choices: + raise ValueError(f"{value} not in {choices}") + return value diff --git a/playbooks/aruba.yml b/playbooks/aruba.yml index 972cfa1..01984dc 100755 --- a/playbooks/aruba.yml +++ b/playbooks/aruba.yml @@ -59,7 +59,8 @@ 4: name: "Client 2" untagged: 1002 - speed: 100fdx + speed: 1000 + duplex: half loop_protect: true 5: name: "Client 3" diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 index 642e644..db451cc 100644 --- a/roles/aruba/templates/config.j2 +++ b/roles/aruba/templates/config.j2 @@ -148,6 +148,12 @@ interface {{ id | int }} enable {% 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"]) }} {% endif %} exit -- 2.38.5 From 1d4534f19ce9fe9579e1dffa742b57f6f2bc631b Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 11:49:19 +0200 Subject: [PATCH 08/10] aruba: truncate long strings --- playbooks/aruba.yml | 5 ++++- roles/aruba/defaults/main.yml | 1 + roles/aruba/templates/config.j2 | 12 ++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/playbooks/aruba.yml b/playbooks/aruba.yml index 01984dc..59d3eaf 100755 --- a/playbooks/aruba.yml +++ b/playbooks/aruba.yml @@ -14,7 +14,10 @@ aruba__dns_servers: - 10.128.0.1 - 2a09:6840:128:0:1::1 - aruba__dns_domain_name: switch.infra.auro.re + aruba__dns_domain_names: + - switch.infra.auro.re + - infra.auro.re + - auro.re aruba__manager_password: "P@ssw0rd!" aruba__operator_password: "P@ssw0rd!" aruba__default_gateways: diff --git a/roles/aruba/defaults/main.yml b/roles/aruba/defaults/main.yml index 8533a55..408044a 100644 --- a/roles/aruba/defaults/main.yml +++ b/roles/aruba/defaults/main.yml @@ -5,6 +5,7 @@ aruba__interfaces: {} aruba__default_gateways: [] aruba__ssh_enabled: False aruba__rest_enabled: True +aruba__dns_domain_names: [] aruba__loop_protect_disable_timer: 30 aruba__loop_protect_tx_interval: 3 ... diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 index db451cc..da146f0 100644 --- a/roles/aruba/templates/config.j2 +++ b/roles/aruba/templates/config.j2 @@ -1,6 +1,6 @@ {{ ansible_managed | comment(decoration="; ") }} -hostname {{ aruba__hostname | enquote }} +hostname {{ aruba__hostname | truncate(32) | enquote }} include-credentials @@ -20,9 +20,9 @@ time timezone 60 ip dns server-address priority {{ loop.index }} {{ addr | ipaddr }} {% endfor %} -{% if aruba__dns_domain_name is defined %} -ip dns domain-name {{ aruba__dns_domain_name | enquote }} -{% endif %} +{% for domain in aruba__dns_domain_names[:5] %} +ip dns domain-name {{ domain | enquote }} +{% endfor %} ; TODO {% if False %} @@ -98,7 +98,7 @@ ipv6 default-gateway {{ aruba__default_gateways | ipv6 | first }} {% for id, vlan in aruba__vlans.items() %} vlan {{ id | int }} {% if vlan.name is defined %} - name {{ vlan.name | enquote }} + name {{ vlan.name | truncate(32) | enquote }} {% endif %} {% set untagged = @@ -142,7 +142,7 @@ set tagged = {% for id, iface in aruba__interfaces.items() %} interface {{ id | int }} {% if iface.name is defined %} - name {{ iface.name | enquote }} + name {{ iface.name | truncate(32) | enquote }} {% endif %} {% if iface.enabled | default(True) %} enable -- 2.38.5 From 89d22feba1719ce3d7231ad97be0573b21d8f0cb Mon Sep 17 00:00:00 2001 From: Jeltz Date: Mon, 5 Sep 2022 11:52:12 +0200 Subject: [PATCH 09/10] aruba: use the hostname, not the FQDN --- filter_plugins/net_utils.py | 6 ++++++ roles/aruba/templates/config.j2 | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/filter_plugins/net_utils.py b/filter_plugins/net_utils.py index 5eecace..cbde396 100644 --- a/filter_plugins/net_utils.py +++ b/filter_plugins/net_utils.py @@ -8,6 +8,7 @@ class FilterModule: def filters(self): return { "remove_domain_suffix": remove_domain_suffix, + "hostname": hostname, "ipaddr_sort": ipaddr_sort, } @@ -17,6 +18,11 @@ def remove_domain_suffix(name): return parent.to_text() +def hostname(fqdn): + name = dns.name.from_text(fqdn) + return name.relativize(name.parent()).to_text() + + def ipaddr_sort(addrs, types, unknown_after=True): check_types = { "global": attrgetter("is_global"), diff --git a/roles/aruba/templates/config.j2 b/roles/aruba/templates/config.j2 index da146f0..5d67987 100644 --- a/roles/aruba/templates/config.j2 +++ b/roles/aruba/templates/config.j2 @@ -1,6 +1,6 @@ {{ ansible_managed | comment(decoration="; ") }} -hostname {{ aruba__hostname | truncate(32) | enquote }} +hostname {{ aruba__hostname | hostname | truncate(32) | enquote }} include-credentials -- 2.38.5 From 01ece9dbcbbb209135b832803c3c8160d96ec2c8 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Fri, 23 Sep 2022 20:05:06 +0200 Subject: [PATCH 10/10] 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 %} -- 2.38.5