289 lines
7.7 KiB
Plaintext
289 lines
7.7 KiB
Plaintext
|
#!/usr/bin/python3
|
||
|
#
|
||
|
# hv_set_ifconfig <config> -- take the hv_kvp_daemon generated configuration
|
||
|
# file and apply it to the Ubuntu configuration.
|
||
|
#
|
||
|
|
||
|
# CONFIG example:
|
||
|
# HWADDR=11:22:33:44:55:66
|
||
|
# DEVICE=foo1
|
||
|
# DHCP=yes
|
||
|
|
||
|
# CONFIG example:
|
||
|
# HWADDR=11:22:33:44:55:66
|
||
|
# DEVICE=foo1
|
||
|
# IPADDR=192.168.99.10
|
||
|
# GATEWAY=192.168.99.1
|
||
|
# DNS1=192.168.88.250
|
||
|
# IPADDR2=192.168.99.11
|
||
|
# IPV6ADDR=2001:DB8:99::10
|
||
|
# IPV6NETMASK=64
|
||
|
# IPV6_DEFAULTGW=2001:DB8:99::10
|
||
|
|
||
|
# set interfaces in hv_kvp_daemon style
|
||
|
import fileinput
|
||
|
import sys
|
||
|
import errno
|
||
|
import os
|
||
|
import shutil
|
||
|
import tempfile
|
||
|
import subprocess
|
||
|
|
||
|
if_filename="/etc/network/interfaces"
|
||
|
|
||
|
# Drop our output (XXX?)
|
||
|
sys.stdout = open(os.devnull, 'w')
|
||
|
sys.stderr = open(os.devnull, 'w')
|
||
|
|
||
|
# Confirm we can open the network configuration.
|
||
|
try:
|
||
|
if_file=open(if_filename,"r+")
|
||
|
except IOError as e:
|
||
|
exit(e.errno)
|
||
|
else:
|
||
|
if_file.close()
|
||
|
|
||
|
# Usage: hv_set_ifconfig <config>
|
||
|
if len(sys.argv) != 2 :
|
||
|
exit(errno.EINVAL)
|
||
|
|
||
|
#
|
||
|
# Here is the format of the ip configuration file:
|
||
|
#
|
||
|
# HWADDR=macaddr
|
||
|
# DEVICE=interface name
|
||
|
# BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured
|
||
|
# or "none" if no boot-time protocol should be used)
|
||
|
#
|
||
|
# IPADDR0=ipaddr1
|
||
|
# IPADDR1=ipaddr2
|
||
|
# IPADDRx=ipaddry (where y = x + 1)
|
||
|
#
|
||
|
# NETMASK0=netmask1
|
||
|
# NETMASKx=netmasky (where y = x + 1)
|
||
|
#
|
||
|
# GATEWAY=ipaddr1
|
||
|
# GATEWAYx=ipaddry (where y = x + 1)
|
||
|
#
|
||
|
# DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
|
||
|
#
|
||
|
# IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
|
||
|
# tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
|
||
|
# IPV6NETMASK.
|
||
|
#
|
||
|
|
||
|
kvp=dict(line.strip().split("=") for line in fileinput.input())
|
||
|
|
||
|
# Setting the hwaddress to something azure is not expecting is fatal
|
||
|
# to networking.
|
||
|
if not "HWADDR" in kvp :
|
||
|
exit(errno.EPROTO)
|
||
|
|
||
|
# Confirm we have a device specified.
|
||
|
if not "DEVICE" in kvp :
|
||
|
exit(1)
|
||
|
|
||
|
autolist = []
|
||
|
output=[]
|
||
|
basename=kvp["DEVICE"]
|
||
|
|
||
|
# DNS entries will go with the first interface and there can be a max
|
||
|
# of three. These will be emitted with the first interface.
|
||
|
dns = []
|
||
|
for count in (1, 2, 3):
|
||
|
key = "DNS" + str(count)
|
||
|
if key in kvp:
|
||
|
dns += [kvp[key]]
|
||
|
dns_emitted = False
|
||
|
|
||
|
# IPV4 may either be dhcp or static.
|
||
|
if ("DHCP" in kvp and kvp["DHCP"] == "yes") or \
|
||
|
("BOOTPROTO" in kvp and kvp["BOOTPROTO"] == "dhcp"):
|
||
|
autolist.append(basename)
|
||
|
output += ["iface " + basename + " inet dhcp"]
|
||
|
output += [""]
|
||
|
else:
|
||
|
# Matchup the interface specific lines
|
||
|
|
||
|
# No real max for the number of interface + aliases ...
|
||
|
# only required is the address (but mate everything up that comes in.
|
||
|
|
||
|
# IPv4 -- ensure we sort by numeric suffixes.
|
||
|
v4names = [ int(name[6:]) for name in kvp.keys() if name.startswith("IPADDR") ]
|
||
|
v4names.sort()
|
||
|
|
||
|
for if_count in v4names:
|
||
|
ifname = basename
|
||
|
which = str(if_count)
|
||
|
|
||
|
if if_count:
|
||
|
ifname += ":" + str(if_count)
|
||
|
which_gw = which
|
||
|
else:
|
||
|
which_gw = ""
|
||
|
|
||
|
if not ifname in autolist:
|
||
|
autolist += [ifname]
|
||
|
|
||
|
output += [ "iface " + ifname + " inet static" ]
|
||
|
output += [ "\t" + "address " + kvp["IPADDR" + which] ]
|
||
|
if "NETMASK" + which in kvp:
|
||
|
output += [ "\tnetmask " + kvp["NETMASK" + which] ]
|
||
|
if "GATEWAY" + which_gw in kvp:
|
||
|
output += ["\tgateway " + kvp["GATEWAY" + which_gw]]
|
||
|
|
||
|
if not dns_emitted:
|
||
|
dns_emitted = True
|
||
|
output += ["\tdns-nameservers " + ' '.join(dns)]
|
||
|
output += [""]
|
||
|
|
||
|
# IPv6 requires a netmask
|
||
|
# If an ipv6 exists, you'll want to turn off /proc/sys/net/ipv6/conf/all/autoconf with
|
||
|
# echo 0 > /proc/sys/net/ipv6/conf/all/autoconf
|
||
|
v6names = [ int(name[8:]) for name in kvp.keys() if name.startswith("IPV6ADDR") ]
|
||
|
v6names.sort()
|
||
|
|
||
|
for if6_count in v6names:
|
||
|
ifname = basename
|
||
|
which = str(if6_count)
|
||
|
|
||
|
if if6_count:
|
||
|
ifname += ":" + str(if6_count)
|
||
|
which_gw = which
|
||
|
else:
|
||
|
which_gw = ""
|
||
|
|
||
|
if not ifname in autolist:
|
||
|
autolist += [ifname]
|
||
|
|
||
|
if "IPV6NETMASK" + which in kvp:
|
||
|
output += [ "iface " + ifname + " inet6 static"]
|
||
|
output += [ "\taddress " + kvp["IPV6ADDR" + which]]
|
||
|
output += [ "\tnetmask " + kvp["IPV6NETMASK" + which]]
|
||
|
if "IPV6_DEFAULTGW" + which_gw in kvp:
|
||
|
output += [ "\tgateway " + kvp["IPV6_DEFAULTGW" + which_gw] ]
|
||
|
if not dns_emitted:
|
||
|
dns_emitted = True
|
||
|
output += ["\tdns-nameservers " + ' '.join(dns)]
|
||
|
output += [""]
|
||
|
|
||
|
# Mark this new interface for automatic up.
|
||
|
if len(autolist):
|
||
|
output = ["auto "+" ".join(autolist)] + output
|
||
|
|
||
|
print("===================================")
|
||
|
print(output)
|
||
|
print("===================================")
|
||
|
|
||
|
|
||
|
# Time to clean out the existing interface file
|
||
|
|
||
|
# Markers.
|
||
|
start_mark = "# The following stanza(s) added by hv_set_ifconfig"
|
||
|
end_mark = "#End of hv_set_ifconfig stanzas"
|
||
|
|
||
|
f=open(if_filename,"r")
|
||
|
flines=f.readlines()
|
||
|
f.close()
|
||
|
newfile=[]
|
||
|
pitchstanza=0
|
||
|
inastanza=0
|
||
|
stanza=[]
|
||
|
prev_line=None
|
||
|
for line in flines:
|
||
|
if line.startswith("auto"):
|
||
|
if inastanza:
|
||
|
if not pitchstanza:
|
||
|
newfile.extend(stanza)
|
||
|
stanza=[]
|
||
|
inastanza=0
|
||
|
newline=""
|
||
|
autoline=line.strip().split(" ")
|
||
|
for word in autoline:
|
||
|
if (not word == basename) and (not word.startswith(basename+":")):
|
||
|
newline+=word + " "
|
||
|
newline = newline.strip()
|
||
|
if not newline == "auto":
|
||
|
newfile += [newline.strip()]
|
||
|
elif line.startswith(("iface","mapping","source")):
|
||
|
'''Read a stanza'''
|
||
|
'''A Stanza can also start with allow- ie allow-hotplug'''
|
||
|
if inastanza:
|
||
|
if not pitchstanza:
|
||
|
newfile.extend(stanza)
|
||
|
stanza=[]
|
||
|
inastanza=1
|
||
|
pitchstanza=0
|
||
|
autoline=line.strip().split(" ")
|
||
|
for word in autoline:
|
||
|
if (word == basename) or (word.startswith(basename+":")):
|
||
|
pitchstanza=1
|
||
|
if not pitchstanza:
|
||
|
stanza+=[line.strip()]
|
||
|
elif line.strip() in (start_mark, end_mark):
|
||
|
if inastanza:
|
||
|
if not pitchstanza:
|
||
|
newfile.extend(stanza)
|
||
|
stanza=[]
|
||
|
inastanza = 0
|
||
|
pitchstanza = 0
|
||
|
# Deduplicate markers.
|
||
|
if line != prev_line:
|
||
|
newfile += [line.strip()]
|
||
|
else:
|
||
|
if inastanza:
|
||
|
if not pitchstanza:
|
||
|
stanza+=[line.strip()]
|
||
|
else:
|
||
|
if not pitchstanza:
|
||
|
newfile += [line.strip()]
|
||
|
prev_line=line
|
||
|
|
||
|
# Include pending stanza if any.
|
||
|
if inastanza and not pitchstanza:
|
||
|
newfile.extend(stanza)
|
||
|
|
||
|
|
||
|
def emit(line):
|
||
|
print(line)
|
||
|
output = line + "\n"
|
||
|
os.write(fd, output.encode('utf-8'))
|
||
|
|
||
|
# Insert the new output at the end and inside the existing markers if found.
|
||
|
emitted = False
|
||
|
fd, path = tempfile.mkstemp()
|
||
|
for line in newfile:
|
||
|
if line == end_mark:
|
||
|
emit("\n".join(output))
|
||
|
emitted = True
|
||
|
emit(line)
|
||
|
if not emitted:
|
||
|
emit(start_mark)
|
||
|
emit("\n".join(output))
|
||
|
emit(end_mark)
|
||
|
os.close(fd)
|
||
|
|
||
|
shutil.copy(path,if_filename)
|
||
|
os.chmod(if_filename,0o644)
|
||
|
|
||
|
#print("TMPFILE is at: " + path)
|
||
|
#print("Copied file is at: " + if_filename)
|
||
|
|
||
|
try:
|
||
|
retcode = subprocess.call("ifdown "+basename , shell=True)
|
||
|
if retcode < 0:
|
||
|
print("Child was terminated by signal", -retcode, file=sys.stderr)
|
||
|
else:
|
||
|
print("Child returned", retcode, file=sys.stderr)
|
||
|
except OSError as e:
|
||
|
print("Execution failed:", e, file=sys.stderr)
|
||
|
|
||
|
try:
|
||
|
retcode = subprocess.call("ifup "+basename , shell=True)
|
||
|
if retcode < 0:
|
||
|
print("Child was terminated by signal", -retcode, file=sys.stderr)
|
||
|
else:
|
||
|
print("Child returned", retcode, file=sys.stderr)
|
||
|
except OSError as e:
|
||
|
print("Execution failed:", e, file=sys.stderr)
|