add junos device management

This commit is contained in:
Franzi 2022-12-22 15:45:50 +01:00
parent 6ae90733c3
commit f1a775b5c9
Signed by: kunsi
GPG key ID: 12E3D2136B818350
5 changed files with 304 additions and 0 deletions

141
configs/junos-template.conf Normal file
View file

@ -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;
}

View file

@ -88,3 +88,10 @@ groups['debian-bullseye'] = {
groups['debian-sid'] = { groups['debian-sid'] = {
'os_version': (99,) 'os_version': (99,)
} }
groups['junos'] = {
'dummy': True,
'cmd_wrapper_outer': '{}',
'cmd_wrapper_inner': '{}',
'os': 'freebsd',
}

View file

@ -1190,6 +1190,20 @@
"untagged": null "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": { "xe-0/1/0": {
"description": "nas", "description": "nas",
"enabled": true, "enabled": true,

View file

@ -0,0 +1,5 @@
hostname = "172.19.138.4"
groups = ["junos"]
[metadata.junos]
version = ["15", "1R5", "5"]

137
scripts/junos-update-config Executable file
View file

@ -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]} <node name>')
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()