diff --git a/.editorconfig b/.editorconfig index e09c9dd..b632cc1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -22,3 +22,6 @@ indent_size = unset [*.vault] end_of_line = unset insert_final_newline = unset + +[*.json] +insert_final_newline = unset diff --git a/configs/junos-template.conf b/configs/junos-template.conf new file mode 100644 index 0000000..0f4012e --- /dev/null +++ b/configs/junos-template.conf @@ -0,0 +1,141 @@ +version ${'.'.join(node.metadata.get('junos/version'))}; +system { + host-name ${node.name.split('.')[-1]}; + time-zone GMT; + root-authentication { + encrypted-password "$5$1hGrR8Kk$lx3CIdxqvesBrZUtDftROEoyXQuMENEu62JVtHw6WGD"; ## SECRET-DATA + } + name-server { +% for srv in repo.libs.defaults.nameservers_ipv4: + ${srv}; +% endfor + } + login { +% for uid, (uname, uconfig) in enumerate(sorted(users.items())): + user ${uname} { + full-name ${uname}; + uid ${1000+uid}; + class super-user; + authentication { +% for pubkey in sorted(uconfig['ssh_pubkey']): + ${pubkey.split(' ', 1)[0]} "${pubkey}"; +% endfor + } + } +% endfor + } + services { + ssh { + protocol-version v2; + } + netconf { + ssh; + } +# web-management { +# http; +# } + } + syslog { + user * { + any emergency; + } + file messages { + any notice; + authorization info; + } + file interactive-commands { + interactive-commands any; + } + } + ntp { +% for srv in sorted(ntp_servers): + server ${srv}; +% endfor; + } +} +interfaces { +% for iface, config in sorted(interfaces.items()): + ${iface} { + unit 0 { +% if not config['enabled']: + disable; +% endif +% if config['mode'] == 'trunk': + family ethernet-switching { + port-mode trunk; + vlan { + members [ ${' '.join(sorted(config['tagged_vlans']))} ]; + } +% if config['untagged_vlan']: + native-vlan-id ${config['untagged_vlan']}; +% endif + } +% else: + family ethernet-switching; +% endif + } + } +% endfor + vlan { +% for idx, (vlan, vconfig) in enumerate(sorted(vlans.items())): +% if vconfig['ip_address']: + unit ${idx} { + family inet { + address ${vconfig['ip_address']}; + } + } +% endif +% endfor + } +} +snmp { + contact "${repo.libs.defaults.hostmaster_email}"; + community public { + authorization read-only; + } +} +routing-options { + static { + route 0.0.0.0/0 next-hop ${gateway}; + } +} +protocols { + igmp-snooping { + vlan all; + } + rstp; + lldp { + interface all; + } + lldp-med { + interface all; + } +} +ethernet-switching-options { + voip; + storm-control { + interface all; + } +} +vlans { +% for idx, (vlan, vconfig) in enumerate(sorted(vlans.items())): + ${vlan} { +% if vconfig['id']: + vlan-id ${vconfig['id']}; +% endif + interface { +% for iface, iconfig in sorted(interfaces.items()): +% if iconfig['untagged_vlan'] == vlan: + ${iface}.0; +% endif +% endfor + } +% if vconfig['ip_address']: + l3-interface vlan.${idx}; +% endif + } +% endfor +} +poe { + interface all; +} diff --git a/groups/os.py b/groups/os.py index 4fa97f7..21d4a60 100644 --- a/groups/os.py +++ b/groups/os.py @@ -88,3 +88,10 @@ groups['debian-bullseye'] = { groups['debian-sid'] = { 'os_version': (99,) } + +groups['junos'] = { + 'dummy': True, + 'cmd_wrapper_outer': '{}', + 'cmd_wrapper_inner': '{}', + 'os': 'freebsd', +} diff --git a/libs/juniper.py b/libs/juniper.py new file mode 100644 index 0000000..9549a77 --- /dev/null +++ b/libs/juniper.py @@ -0,0 +1,149 @@ +import random + +# copied from https://github.com/peering-manager/peering-manager/blob/main/devices/crypto/juniper.py + + +# This code is the result of the attempt at converting a Perl module, the expected +# result might not actually be what we really want it to be ¯\_(ツ)_/¯ +# +# https://metacpan.org/pod/Crypt::Juniper + + +MAGIC = "$9$" + +FAMILY = [ + "QzF3n6/9CAtpu0O", + "B1IREhcSyrleKvMW8LXx", + "7N-dVbwsY2g4oaJZGUDj", + "iHkq.mPf5T", +] +EXTRA = {} +for counter, value in enumerate(FAMILY): + for character in value: + EXTRA[character] = 3 - counter + +NUM_ALPHA = [x for x in "".join(FAMILY)] +ALPHA_NUM = {NUM_ALPHA[x]: x for x in range(0, len(NUM_ALPHA))} + +ENCODING = [ + [1, 4, 32], + [1, 16, 32], + [1, 8, 32], + [1, 64], + [1, 32], + [1, 4, 16, 128], + [1, 32, 64], +] + + +def __nibble(cref, length): + nib = cref[0:length] + rest = cref[length:] + + if len(nib) != length: + raise Exception(f"Ran out of characters: hit '{nib}', expecting {length} chars") + + return nib, rest + + +def __gap(c1, c2): + return (ALPHA_NUM[str(c2)] - ALPHA_NUM[str(c1)]) % (len(NUM_ALPHA)) - 1 + + +def __gap_decode(gaps, dec): + num = 0 + + if len(gaps) != len(dec): + raise Exception("Nibble and decode size not the same.") + + for x in range(0, len(gaps)): + num += gaps[x] * dec[x] + + return chr(num % 256) + + +def __reverse(current): + reversed = list(current) + reversed.reverse() + return reversed + + +def __gap_encode(pc, prev, encode): + __ord = ord(pc) + + crypt = "" + gaps = [] + for mod in __reverse(encode): + gaps.insert(0, int(__ord / mod)) + __ord %= mod + + for gap in gaps: + gap += ALPHA_NUM[prev] + 1 + prev = NUM_ALPHA[gap % len(NUM_ALPHA)] + crypt += prev + + return crypt + + +def __randc(counter=0): + return_value = "" + for _ in range(counter): + return_value += NUM_ALPHA[random.randrange(len(NUM_ALPHA))] + return return_value + + +def is_encrypted(value): + return value.startswith(MAGIC) + + +def decrypt(value): + if not value: + return "" + + if not is_encrypted(value): + return value + + chars = value.split("$9$", 1)[1] + first, chars = __nibble(chars, 1) + toss, chars = __nibble(chars, EXTRA[first]) + previous = first + decrypted = "" + + while chars: + decode = ENCODING[len(decrypted) % len(ENCODING)] + nibble, chars = __nibble(chars, len(decode)) + gaps = [] + for i in nibble: + g = __gap(previous, i) + previous = i + gaps += [g] + decrypted += __gap_decode(gaps, decode) + + return decrypted + + +def encrypt(value, salt=None): + if not value: + return "" + + if not isinstance(value, str): + value = str(value) + + if is_encrypted(value): + return value + + if not salt: + salt = __randc(1) + rand = __randc(EXTRA[salt]) + + position = 0 + previous = salt + crypted = MAGIC + salt + rand + + for x in value: + encode = ENCODING[position % len(ENCODING)] + crypted += __gap_encode(x, previous, encode) + previous = crypted[-1] + position += 1 + + return crypted diff --git a/netbox_dump.json b/netbox_dump.json new file mode 100644 index 0000000..c013f00 --- /dev/null +++ b/netbox_dump.json @@ -0,0 +1,1322 @@ +{ + "home": { + "devices": { + "home.nas": { + "bond0": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "lag", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "br0": { + "description": "", + "enabled": true, + "ip_addresses": [ + "172.19.138.20/24" + ], + "lag": null, + "mode": "tagged-all", + "type": "bridge", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "br42": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "bridge", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "eno1": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "enp8s0f0": { + "description": "home.sw01 (41)", + "enabled": true, + "ip_addresses": [], + "lag": "bond0", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "enp8s0f1": { + "description": "home.sw01 (43)", + "enabled": true, + "ip_addresses": [], + "lag": "bond0", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "enp9s0f0": { + "description": "home.sw01 (45)", + "enabled": true, + "ip_addresses": [], + "lag": "bond0", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "enp9s0f1": { + "description": "home.sw01 (47)", + "enabled": true, + "ip_addresses": [], + "lag": "bond0", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + } + }, + "home.router": { + "enp1s0": { + "description": "home.sw01 (2)", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "enp1s0.100": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "virtual", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "enp1s0.23": { + "description": "", + "enabled": true, + "ip_addresses": [ + "172.19.139.1/24" + ], + "lag": null, + "mode": null, + "type": "virtual", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "enp1s0.42": { + "description": "", + "enabled": true, + "ip_addresses": [ + "172.19.138.1/24" + ], + "lag": null, + "mode": null, + "type": "virtual", + "vlans": { + "tagged": [], + "untagged": null + } + } + }, + "home.sw01": { + "1": { + "description": "Isanet", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.dmz" + } + }, + "10": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "11": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "12": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "13": { + "description": "home.ejgwdesk", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "14": { + "description": "Schreibtisch Franzi", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "15": { + "description": "Schreibtisch Sophie", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "16": { + "description": "home.snom-wohnzimmer", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "17": { + "description": "home.drucker-sophie", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "18": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "19": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "2": { + "description": "home.router (enp1s0)", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "tagged", + "type": "1000base-t", + "vlans": { + "tagged": [ + "home.clients", + "home.dmz", + "home.wan" + ], + "untagged": null + } + }, + "20": { + "description": "RIPE-Probe #28280", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.dmz" + } + }, + "21": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "22": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "23": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "24": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "25": { + "description": "home.kodi-wohnzimmer", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "26": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "27": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "28": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "29": { + "description": "Sofa-Kabel", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "3": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "30": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "31": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "32": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "33": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": "LAG3", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "34": { + "description": "Patchpanel unten (17)", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "35": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": "LAG3", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "36": { + "description": "Patchpanel unten (18)", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "37": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": "LAG1", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "38": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "39": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": "LAG1", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "4": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "40": { + "description": "info-beamer 12199 (LAN)", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "41": { + "description": "home.nas (enp8s0f0)", + "enabled": true, + "ip_addresses": [], + "lag": "LAG2", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "42": { + "description": "Patchpanel unten (21)", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.wan" + } + }, + "43": { + "description": "home.nas (enp8s0f1)", + "enabled": true, + "ip_addresses": [], + "lag": "LAG2", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "44": { + "description": "home.bubble01", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "45": { + "description": "home.nas (enp9s0f0)", + "enabled": true, + "ip_addresses": [], + "lag": "LAG2", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "46": { + "description": "home.winkeeinhorn-1", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "tagged", + "type": "1000base-t", + "vlans": { + "tagged": [ + "ffwi.mesh" + ], + "untagged": "home.clients" + } + }, + "47": { + "description": "home.nas (enp9s0f1)", + "enabled": true, + "ip_addresses": [], + "lag": "LAG2", + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "48": { + "description": "home.winkeeinhorn-2", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "tagged", + "type": "1000base-t", + "vlans": { + "tagged": [ + "ffwi.mesh" + ], + "untagged": "home.clients" + } + }, + "49": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-x-sfp", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "5": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "50": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-x-sfp", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "51": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-x-sfp", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "52": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-x-sfp", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "6": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "7": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "8": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "9": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "ffwi.client" + } + }, + "LAG1": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "lag", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "LAG2": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "tagged", + "type": "lag", + "vlans": { + "tagged": [ + "ffwi.client", + "ffwi.mesh", + "home.clients", + "home.dmz", + "home.wan" + ], + "untagged": null + } + }, + "LAG3": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "lag", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "VLAN42": { + "description": "", + "enabled": true, + "ip_addresses": [ + "172.19.138.2/24" + ], + "lag": null, + "mode": "access", + "type": "virtual", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + } + }, + "home.sw02": { + "ge-0/0/0": { + "description": "snom", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/1": { + "description": "bubble", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/10": { + "description": "sophie", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/11": { + "description": "sophie", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/12": { + "description": "franzi", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/13": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/14": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/15": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/16": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/17": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/18": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/19": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/2": { + "description": "freifunk", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "tagged", + "type": "1000base-t", + "vlans": { + "tagged": [ + "ffwi.mesh" + ], + "untagged": "home.clients" + } + }, + "ge-0/0/20": { + "description": "kodi", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/21": { + "description": "infobeamer", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/22": { + "description": "fritzbox", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.wan" + } + }, + "ge-0/0/23": { + "description": "router", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "tagged", + "type": "1000base-t", + "vlans": { + "tagged": [ + "home.clients", + "home.dmz", + "home.wan" + ], + "untagged": null + } + }, + "ge-0/0/3": { + "description": "freifunk", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "tagged", + "type": "1000base-t", + "vlans": { + "tagged": [ + "ffwi.mesh" + ], + "untagged": "home.clients" + } + }, + "ge-0/0/4": { + "description": "wohnzimmer", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/5": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/6": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/7": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/0/8": { + "description": "drucker", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "ge-0/0/9": { + "description": "ripe-probe", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": "home.dmz" + } + }, + "ge-0/1/0": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-x-sfp", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/1/1": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-x-sfp", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/1/2": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-x-sfp", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "ge-0/1/3": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-x-sfp", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "home.clients": { + "description": "", + "enabled": true, + "ip_addresses": [ + "172.19.138.4/24" + ], + "lag": null, + "mode": null, + "type": "virtual", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "xe-0/1/0": { + "description": "nas", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "tagged", + "type": "10gbase-x-sfpp", + "vlans": { + "tagged": [ + "ffwi.client", + "ffwi.mesh", + "home.clients", + "home.dmz" + ], + "untagged": null + } + }, + "xe-0/1/1": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "10gbase-x-sfpp", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + }, + "xe-0/1/2": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": "access", + "type": "10gbase-x-sfpp", + "vlans": { + "tagged": [], + "untagged": "home.clients" + } + } + }, + "home.usv01": { + "LAN": { + "description": "", + "enabled": true, + "ip_addresses": [ + "172.19.138.3/24" + ], + "lag": null, + "mode": null, + "type": "100base-tx", + "vlans": { + "tagged": [], + "untagged": null + } + } + } + }, + "vlans": { + "ffwi.client": 8, + "ffwi.mesh": 7, + "home.clients": 42, + "home.dmz": 23, + "home.wan": 100 + } + }, + "meerfarbig gmbh & co. kg": { + "devices": { + "rx300": { + "IPMI": { + "description": "", + "enabled": true, + "ip_addresses": [ + "10.250.179.2/32" + ], + "lag": null, + "mode": null, + "type": "100base-tx", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "LAN1": { + "description": "", + "enabled": true, + "ip_addresses": [ + "2a00:f820:528::2/64", + "31.47.232.106/29" + ], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + }, + "LAN2": { + "description": "", + "enabled": true, + "ip_addresses": [], + "lag": null, + "mode": null, + "type": "1000base-t", + "vlans": { + "tagged": [], + "untagged": null + } + } + } + }, + "vlans": {} + } +} \ No newline at end of file diff --git a/nodes/home/home.sw02.toml b/nodes/home/home.sw02.toml new file mode 100644 index 0000000..8e4520a --- /dev/null +++ b/nodes/home/home.sw02.toml @@ -0,0 +1,5 @@ +hostname = "172.19.138.4" +groups = ["junos"] + +[metadata.junos] +version = ["15", "1R5", "5"] diff --git a/requirements.txt b/requirements.txt index eaec252..8c04545 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ bundlewrap~=4.16.0 PyNaCl bundlewrap-pass +pynetbox==7.0.0 diff --git a/scripts/junos-update-config b/scripts/junos-update-config new file mode 100755 index 0000000..74191cf --- /dev/null +++ b/scripts/junos-update-config @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +from json import load +from os import environ +from os.path import join +from sys import argv, exit +from tempfile import gettempdir + +from mako.template import Template + +from bundlewrap.repo import Repository +from bundlewrap.utils.text import bold +from bundlewrap.utils.ui import io + +NTP_SERVERS = { +# pool.ntp.org + '148.251.54.81', + '162.159.200.123', + '213.209.109.44', + '54.36.110.36', +} + +try: + node_name = argv[1] +except Exception: + print(f'Usage: {argv[0]} ') + exit(1) + +path = environ.get('BW_REPO_PATH', '.') +repo = Repository(path) +node = repo.get_node(node_name) + +try: + io.activate() + + interfaces = {} + users = {} + vlans = { + 'default': { + 'id': None, + 'ip_address': '169.254.254.254/24', + }, + } + + tmpfile = join(gettempdir(), f'{node.name}.conf') + + gw_split = node.hostname.split('.') + gw_split[3] = '1' + gateway = '.'.join(gw_split) + + with io.job('reading netbox_dump.json'): + with open(join(repo.path, 'netbox_dump.json'), 'r') as f: + json = load(f)[node.metadata.get('location')] + + for vlan, vid in json['vlans'].items(): + vlans[vlan] = { + 'id': vid, + 'ip_address': None, + } + + for iface, iconfig in json['devices'][node.name].items(): + if iface in vlans: + # If the interface name is the same as a vlan name, this + # means the ip assigned to this interface should get + # assigned to that vlan. + vlans[iface]['ip_address'] = iconfig['ip_addresses'][0] + else: + interfaces[iface] = { + 'enabled': bool( + iconfig['enabled'] + and iconfig['mode'] + and ( + iconfig['vlans']['tagged'] + or iconfig['vlans']['untagged'] + ) + ), + 'description': iconfig['description'], + 'untagged_vlan': iconfig['vlans']['untagged'], + } + + if iconfig['mode'] and iconfig['mode'].startswith('tagged'): + interfaces[iface]['mode'] = 'trunk' + else: + interfaces[iface]['mode'] = 'access' + + tagged_vlans = set() + for vlan in iconfig['vlans']['tagged']: + tagged_vlans.add(str(vlans[vlan]['id'])) + interfaces[iface]['tagged_vlans'] = tagged_vlans + + with io.job('reading users.json'): + with open(join(repo.path, 'users.json'), 'r') as f: + json = load(f) + + users = {} + for uname, config in json.items(): + if config.get('is_admin', False): + users[uname] = { + 'password': repo.vault.password_for(f'{node.name} {uname} login'), + 'ssh_pubkey': set(config['ssh_pubkey']), + } + + + with io.job(f'{bold(node.name)} rendering config template to {tmpfile}'): + with open(join(repo.path, 'configs', 'junos-template.conf')) as f: + template = Template( + f.read().encode('utf-8'), + input_encoding='utf-8', + output_encoding='utf-8', + ) + content = template.render( + gateway=gateway, + interfaces=interfaces, + node=node, + ntp_servers=NTP_SERVERS, + repo=repo, + users=users, + vlans=vlans, + ) + with open(tmpfile, 'w+') as f: + f.write(content.decode('utf-8')) + + with io.job(f'{bold(node.name)} updating configuration on device'): + node.upload(tmpfile, '/tmp/bundlewrap.conf') + + result = node.run( + 'configure exclusive ; load override /tmp/bundlewrap.conf ; commit', + log_output=True, + ) + + if 'commit complete' in result.stdout.decode(): + node.run( + 'request system configuration rescue save', + log_output=True, + ) +finally: + io.deactivate() diff --git a/scripts/netbox-dump b/scripts/netbox-dump new file mode 100755 index 0000000..779b143 --- /dev/null +++ b/scripts/netbox-dump @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +from json import dump +from os import environ +from os.path import join +from sys import exit + +from bundlewrap.utils.ui import QUIT_EVENT, io +from bundlewrap.utils.text import bold, yellow, validate_name +from pynetbox import api as netbox_api + + +BW_REPO_PATH = environ.get('BW_REPO_PATH', '.') +netbox = netbox_api( + environ.get('NETBOX_HOST', 'https://netbox.franzi.business'), + token=environ.get('NETBOX_TOKEN', None), +) + +result = { +# 'my_site_name': { +# 'vlans': { +# 'my_vlan_name': 10, +# 'other_vlan_name': 11, +# 'yet_another_vlan_name': 12, +# }, +# 'devices': { +# 'my_switch': { +# 'port1': { +# 'description': 'foo', +# 'type': '1000base-t', # or 'lag' +# 'mode': None, # or 'access', 'tagged', 'tagged-all' +# 'lag': 'none', # or 'LAG1' +# 'vlan': { +# 'untagged': 'my_vlan_name', +# 'tagged': [ +# 'other_vlan_name', +# 'yet_another_vlan_name', +# ], +# }, +# }, +# }, +# }, +# }, +} + +errors = False +try: + io.activate() + + for site in netbox.dcim.sites.all(): + site_name = site.name.lower() + + result[site_name] = { + 'vlans': {}, + 'devices': {}, + } + + with io.job(f'{bold(site_name)} getting vlans'): + for vlan in netbox.ipam.vlans.filter(site_id=site.id): + if vlan.name in result[site_name]['vlans'].keys() and result[site_name]['vlans'][vlan.name] != vlan.id: + raise Exception(f"vlan {result[site_name]['vlans'][vlan.name]} and {vlan.id} both have the name {vlan.name}") + + result[site_name]['vlans'][vlan.name] = vlan.vid + + for interface in netbox.dcim.interfaces.filter(site_id=site.id): + if QUIT_EVENT.is_set(): + exit(0) + + with io.job(f'{bold(site_name)} {bold(interface.device.name)} interface {yellow(interface.name)}'): + if not interface.device.name: + # Unnamed device. Probably not managed by bw. + continue + elif not validate_name(interface.device.name): + # bundlewrap does not consider this device name to be a valid + # node name. Ignore it, we don't manage it + continue + + has_valid_description = False + if interface.description: + description = interface.description + has_valid_description = True + elif interface.connected_endpoints: + description = f'{sorted(interface.connected_endpoints)[0].device.display} ({sorted(interface.connected_endpoints)[0].display})' + has_valid_description = True + elif interface.link_peers: + description = f'{sorted(interface.link_peers)[0].device.display} ({sorted(interface.link_peers)[0].display})' + else: + description = '' + + if not description.isascii(): + errors = True + io.stderr(f'{bold(interface.device.name)} {bold(interface.name)} description "{description}" contains non-ascii characters, this isn\'t supported') + + result[site_name]['devices'].setdefault(interface.device.name, {})[interface.name] = { + 'description': description, + 'enabled': interface.enabled, + 'ip_addresses': sorted(set() if interface.count_ipaddresses == 0 else { + ip.address for ip in + netbox.ipam.ip_addresses.filter(interface_id=interface.id) + }), + 'mode': interface.mode.value if interface.mode else None, + 'type': interface.type.value, + 'lag': interface.lag.name if interface.lag else None, + 'vlans': { + 'untagged': interface.untagged_vlan.name if interface.untagged_vlan else None, + 'tagged': sorted(vlan.name for vlan in interface.tagged_vlans), + }, + } + + if errors: + exit(1) + + with io.job('dumping result to netbox_dump.json'): + with open(join(BW_REPO_PATH, 'netbox_dump.json'), 'w') as f: + dump(result, f, indent=4, sort_keys=True) +finally: + io.deactivate()