add junos device management
This commit is contained in:
parent
6ae90733c3
commit
f1a775b5c9
5 changed files with 304 additions and 0 deletions
141
configs/junos-template.conf
Normal file
141
configs/junos-template.conf
Normal 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;
|
||||
}
|
|
@ -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',
|
||||
}
|
||||
|
|
|
@ -1190,6 +1190,20 @@
|
|||
"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,
|
||||
|
|
5
nodes/home/home.sw02.toml
Normal file
5
nodes/home/home.sw02.toml
Normal 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
137
scripts/junos-update-config
Executable 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()
|
Loading…
Reference in a new issue