Skip to content

Instantly share code, notes, and snippets.

@ramcq
Created August 21, 2018 09:51
Show Gist options
  • Save ramcq/629c51f9ffdfe715079a5ca50b8a2356 to your computer and use it in GitHub Desktop.
Save ramcq/629c51f9ffdfe715079a5ca50b8a2356 to your computer and use it in GitHub Desktop.
Reads Packet.net metadata JSON and writes out RedHat style /etc/sysconfig/network-scripts config
#!/usr/bin/env python3
import argparse
import glob
import ipaddress
import json
import os
import requests
PACKET_BOND_OPTIONS = 'miimon=100 downdelay=200 updelay=200 xmit_hash_policy=1 lacp_rate=1'
PACKET_DNS = [
'147.75.207.207',
'147.75.207.208'
]
PACKET_V4_PRIVATE_NETWORK = '10.0.0.0/8'
def get_metadata(args):
if args.file:
with open(args.file, 'r') as json_file:
metadata = json.load(json_file)
else:
r = requests.get(args.url)
r.raise_for_status()
metadata = r.json()
return metadata
def write_files(args, ifcfgs, routes):
for iface,ifcfg in ifcfgs.items():
fn = 'ifcfg-{}'.format(iface)
if args.debug:
for k in sorted(ifcfg):
print('{}: {}={}'.format(fn, k, ifcfg[k]))
else:
with open(os.path.join(args.outdir, fn), 'w') as iface_file:
for k in sorted(ifcfg):
iface_file.write('{}={}\n'.format(k, ifcfg[k]))
for iface,route in routes.items():
fn = 'route-{}'.format(iface)
if args.debug:
for k in sorted(route):
print('{}: {} via {}'.format(fn, k, route[k]))
else:
with open(os.path.join(args.outdir, fn), 'w') as route_file:
for k in sorted(route):
route_file.write('{} via {}\n'.format(k, route[k]))
def get_iface_macs():
devs = {}
for iface in glob.glob('/sys/class/net/*'):
# skip virtual interfaces
device = os.path.join(iface, 'device')
if not os.path.exists(device):
continue
with open(os.path.join(iface, 'address')) as mac_file:
mac = mac_file.read().strip().lower()
dev = os.path.basename(iface)
devs[mac] = dev
return devs
def add_bond(ifcfgs, bond, bond_mode):
assert (bond not in ifcfgs)
bond_opts = '"mode={} {}"'.format(bond_mode, PACKET_BOND_OPTIONS)
cfg = {
'DEVICE': bond,
'NAME': bond,
'BOOTPROTO': 'none',
'ONBOOT': 'yes',
'USERCTL': 'no',
'TYPE': 'Bond',
'BONDING_OPTS': bond_opts
}
ifcfgs[bond] = cfg
def add_alias(ifcfgs, alias):
assert (alias not in ifcfgs)
cfg = {
'DEVICE': alias,
'NAME': alias,
'ONBOOT': 'yes',
'USERCTL': 'no'
}
ifcfgs[alias] = cfg
def add_route(routes, iface, network, gateway):
route = routes.setdefault(iface, {})
new_route = {
network: gateway
}
route.update(new_route)
def add_interface(ifcfgs, name, mac, bond):
assert (name not in ifcfgs)
cfg = {
'DEVICE': name,
'ONBOOT': 'yes',
'HWADDR': mac,
'MASTER': bond,
'SLAVE': 'yes',
'BOOTPROTO': 'none',
}
ifcfgs[name] = cfg
def add_interfaces(ifcfgs, interfaces):
iface_macs = get_iface_macs()
for iface in interfaces:
name = iface['name']
mac = iface['mac']
bond = iface['bond']
assert (bond in ifcfgs)
if mac in iface_macs:
name = iface_macs[mac]
add_interface(ifcfgs, name, mac, bond)
def incr_alias(iface):
if ':' not in iface:
return iface + ':0'
a,b = iface.rsplit(':', 1)
b = b+1
return iface + ':' + b
def find_free_alias(ifcfgs, iface, key):
while key in ifcfgs[iface]:
iface = incr_alias(iface)
if iface not in ifcfgs:
add_alias(ifcfgs, iface)
return iface
def add_v4_address(ifcfgs, iface, addr):
iface = find_free_alias(ifcfgs, iface, 'IPADDR')
cfg = {
'IPADDR': addr['address'],
'NETMASK': addr['netmask'],
}
if addr['public']:
cfg['GATEWAY'] = addr['gateway']
ifcfgs[iface].update(cfg)
def add_v6_address(ifcfgs, iface, addr):
iface = find_free_alias(ifcfgs, iface, 'IPV6ADDR')
cfg = {
'IPV6INIT': 'yes',
'IPV6ADDR': '{}/{}'.format(addr['address'], addr['cidr']),
}
if addr['public']:
cfg['IPV6_DEFAULTGW'] = addr['gateway']
ifcfgs[iface].update(cfg)
def add_addresses(ifcfgs, routes, iface, addresses):
for addr in addresses:
if not addr['enabled']:
continue
if addr['address_family'] == 4:
add_v4_address(ifcfgs, iface, addr)
if not addr['public']:
add_route(routes, iface, PACKET_V4_PRIVATE_NETWORK, addr['gateway'])
elif addr['address_family'] == 6:
add_v6_address(ifcfgs, iface, addr)
else:
raise Exception('unexpected address family')
def add_dns(ifcfgs, iface, servers):
i = 1
for server in servers:
ifcfgs[iface]['DNS{}'.format(i)] = server
i = i+1
ifcfgs[iface]['PEERDNS'] = 'yes'
cfg['IPV6_DEFAULTGW'] = addr['gateway']
ifcfgs[iface].update(cfg)
def add_addresses(ifcfgs, routes, iface, addresses):
for addr in addresses:
if not addr['enabled']:
continue
if addr['address_family'] == 4:
add_v4_address(ifcfgs, iface, addr)
if not addr['public']:
add_route(routes, iface, PACKET_V4_PRIVATE_NETWORK, addr['gateway'])
elif addr['address_family'] == 6:
add_v6_address(ifcfgs, iface, addr)
else:
raise Exception('unexpected address family')
def add_dns(ifcfgs, iface, servers):
i = 1
for server in servers:
ifcfgs[iface]['DNS{}'.format(i)] = server
i = i+1
ifcfgs[iface]['PEERDNS'] = 'yes'
def main(args):
metadata = get_metadata(args)
network = metadata['network']
if 'bonding' not in network:
raise Exception('not a bonded network, don\'t know how to proceed')
ifcfgs = {}
routes = {}
bond = 'bond0'
bond_mode = network['bonding']['mode']
add_bond(ifcfgs, 'bond0', bond_mode)
interfaces = network['interfaces']
add_interfaces(ifcfgs, interfaces)
addresses = network['addresses']
add_addresses(ifcfgs, routes, bond, addresses)
add_dns(ifcfgs, bond, PACKET_DNS)
write_files(args, ifcfgs, routes)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Fetch Packet.net metadata and write out ifcfg and route files for RH-style network config')
parser.add_argument('-d', '--debug', action='store_true', help='Write to stdout instead of files')
parser.add_argument('-o', '--outdir', default='/etc/sysconfig/network-scripts', help='Folder to write ifcfg-*/route-* files to')
group = parser.add_mutually_exclusive_group()
group.add_argument('-f', '--file', help='Use local input file instead of remote fetch')
group.add_argument('-u', '--url', default='https://metadata.packet.net/metadata', help='Remote metadata URL to fetch')
args = parser.parse_args()
main(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment