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'] = {
|
groups['debian-sid'] = {
|
||||||
'os_version': (99,)
|
'os_version': (99,)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groups['junos'] = {
|
||||||
|
'dummy': True,
|
||||||
|
'cmd_wrapper_outer': '{}',
|
||||||
|
'cmd_wrapper_inner': '{}',
|
||||||
|
'os': 'freebsd',
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
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