]>
Commit | Line | Data |
---|---|---|
0d34a427 LO |
1 | #!/usr/bin/python3 |
2 | # | |
3 | # hv_set_ifconfig <config> -- take the hv_kvp_daemon generated configuration | |
4 | # file and apply it to the Ubuntu configuration. | |
5 | # | |
6 | ||
7 | # CONFIG example: | |
8 | # HWADDR=11:22:33:44:55:66 | |
9 | # DEVICE=foo1 | |
10 | # DHCP=yes | |
11 | ||
12 | # CONFIG example: | |
13 | # HWADDR=11:22:33:44:55:66 | |
14 | # DEVICE=foo1 | |
15 | # IPADDR=192.168.99.10 | |
16 | # GATEWAY=192.168.99.1 | |
17 | # DNS1=192.168.88.250 | |
18 | # IPADDR2=192.168.99.11 | |
19 | # IPV6ADDR=2001:DB8:99::10 | |
20 | # IPV6NETMASK=64 | |
21 | # IPV6_DEFAULTGW=2001:DB8:99::10 | |
22 | ||
23 | # set interfaces in hv_kvp_daemon style | |
24 | import fileinput | |
25 | import sys | |
26 | import errno | |
27 | import os | |
28 | import shutil | |
29 | import tempfile | |
30 | import subprocess | |
31 | ||
32 | if_filename="/etc/network/interfaces" | |
33 | ||
34 | # Drop our output (XXX?) | |
35 | sys.stdout = open(os.devnull, 'w') | |
36 | sys.stderr = open(os.devnull, 'w') | |
37 | ||
38 | # Confirm we can open the network configuration. | |
39 | try: | |
40 | if_file=open(if_filename,"r+") | |
41 | except IOError as e: | |
42 | exit(e.errno) | |
43 | else: | |
44 | if_file.close() | |
45 | ||
46 | # Usage: hv_set_ifconfig <config> | |
47 | if len(sys.argv) != 2 : | |
48 | exit(errno.EINVAL) | |
49 | ||
50 | # | |
51 | # Here is the format of the ip configuration file: | |
52 | # | |
53 | # HWADDR=macaddr | |
54 | # DEVICE=interface name | |
55 | # BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured | |
56 | # or "none" if no boot-time protocol should be used) | |
57 | # | |
58 | # IPADDR0=ipaddr1 | |
59 | # IPADDR1=ipaddr2 | |
60 | # IPADDRx=ipaddry (where y = x + 1) | |
61 | # | |
62 | # NETMASK0=netmask1 | |
63 | # NETMASKx=netmasky (where y = x + 1) | |
64 | # | |
65 | # GATEWAY=ipaddr1 | |
66 | # GATEWAYx=ipaddry (where y = x + 1) | |
67 | # | |
68 | # DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) | |
69 | # | |
70 | # IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be | |
71 | # tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as | |
72 | # IPV6NETMASK. | |
73 | # | |
74 | ||
75 | kvp=dict(line.strip().split("=") for line in fileinput.input()) | |
76 | ||
77 | # Setting the hwaddress to something azure is not expecting is fatal | |
78 | # to networking. | |
79 | if not "HWADDR" in kvp : | |
80 | exit(errno.EPROTO) | |
81 | ||
82 | # Confirm we have a device specified. | |
83 | if not "DEVICE" in kvp : | |
84 | exit(1) | |
85 | ||
86 | autolist = [] | |
87 | output=[] | |
88 | basename=kvp["DEVICE"] | |
89 | ||
90 | # DNS entries will go with the first interface and there can be a max | |
91 | # of three. These will be emitted with the first interface. | |
92 | dns = [] | |
93 | for count in (1, 2, 3): | |
94 | key = "DNS" + str(count) | |
95 | if key in kvp: | |
96 | dns += [kvp[key]] | |
97 | dns_emitted = False | |
98 | ||
99 | # IPV4 may either be dhcp or static. | |
100 | if ("DHCP" in kvp and kvp["DHCP"] == "yes") or \ | |
101 | ("BOOTPROTO" in kvp and kvp["BOOTPROTO"] == "dhcp"): | |
102 | autolist.append(basename) | |
103 | output += ["iface " + basename + " inet dhcp"] | |
104 | output += [""] | |
105 | else: | |
106 | # Matchup the interface specific lines | |
107 | ||
108 | # No real max for the number of interface + aliases ... | |
109 | # only required is the address (but mate everything up that comes in. | |
110 | ||
111 | # IPv4 -- ensure we sort by numeric suffixes. | |
112 | v4names = [ int(name[6:]) for name in kvp.keys() if name.startswith("IPADDR") ] | |
113 | v4names.sort() | |
114 | ||
115 | for if_count in v4names: | |
116 | ifname = basename | |
117 | which = str(if_count) | |
118 | ||
119 | if if_count: | |
120 | ifname += ":" + str(if_count) | |
121 | which_gw = which | |
122 | else: | |
123 | which_gw = "" | |
124 | ||
125 | if not ifname in autolist: | |
126 | autolist += [ifname] | |
127 | ||
128 | output += [ "iface " + ifname + " inet static" ] | |
129 | output += [ "\t" + "address " + kvp["IPADDR" + which] ] | |
130 | if "NETMASK" + which in kvp: | |
131 | output += [ "\tnetmask " + kvp["NETMASK" + which] ] | |
132 | if "GATEWAY" + which_gw in kvp: | |
133 | output += ["\tgateway " + kvp["GATEWAY" + which_gw]] | |
134 | ||
135 | if not dns_emitted: | |
136 | dns_emitted = True | |
137 | output += ["\tdns-nameservers " + ' '.join(dns)] | |
138 | output += [""] | |
139 | ||
140 | # IPv6 requires a netmask | |
141 | # If an ipv6 exists, you'll want to turn off /proc/sys/net/ipv6/conf/all/autoconf with | |
142 | # echo 0 > /proc/sys/net/ipv6/conf/all/autoconf | |
143 | v6names = [ int(name[8:]) for name in kvp.keys() if name.startswith("IPV6ADDR") ] | |
144 | v6names.sort() | |
145 | ||
146 | for if6_count in v6names: | |
147 | ifname = basename | |
148 | which = str(if6_count) | |
149 | ||
150 | if if6_count: | |
151 | ifname += ":" + str(if6_count) | |
152 | which_gw = which | |
153 | else: | |
154 | which_gw = "" | |
155 | ||
156 | if not ifname in autolist: | |
157 | autolist += [ifname] | |
158 | ||
159 | if "IPV6NETMASK" + which in kvp: | |
160 | output += [ "iface " + ifname + " inet6 static"] | |
161 | output += [ "\taddress " + kvp["IPV6ADDR" + which]] | |
162 | output += [ "\tnetmask " + kvp["IPV6NETMASK" + which]] | |
163 | if "IPV6_DEFAULTGW" + which_gw in kvp: | |
164 | output += [ "\tgateway " + kvp["IPV6_DEFAULTGW" + which_gw] ] | |
165 | if not dns_emitted: | |
166 | dns_emitted = True | |
167 | output += ["\tdns-nameservers " + ' '.join(dns)] | |
168 | output += [""] | |
169 | ||
170 | # Mark this new interface for automatic up. | |
171 | if len(autolist): | |
172 | output = ["auto "+" ".join(autolist)] + output | |
173 | ||
174 | print("===================================") | |
175 | print(output) | |
176 | print("===================================") | |
177 | ||
178 | ||
179 | # Time to clean out the existing interface file | |
180 | ||
181 | # Markers. | |
182 | start_mark = "# The following stanza(s) added by hv_set_ifconfig" | |
183 | end_mark = "#End of hv_set_ifconfig stanzas" | |
184 | ||
185 | f=open(if_filename,"r") | |
186 | flines=f.readlines() | |
187 | f.close() | |
188 | newfile=[] | |
189 | pitchstanza=0 | |
190 | inastanza=0 | |
191 | stanza=[] | |
192 | prev_line=None | |
193 | for line in flines: | |
194 | if line.startswith("auto"): | |
195 | if inastanza: | |
196 | if not pitchstanza: | |
197 | newfile.extend(stanza) | |
198 | stanza=[] | |
199 | inastanza=0 | |
200 | newline="" | |
201 | autoline=line.strip().split(" ") | |
202 | for word in autoline: | |
203 | if (not word == basename) and (not word.startswith(basename+":")): | |
204 | newline+=word + " " | |
205 | newline = newline.strip() | |
206 | if not newline == "auto": | |
207 | newfile += [newline.strip()] | |
208 | elif line.startswith(("iface","mapping","source")): | |
209 | '''Read a stanza''' | |
210 | '''A Stanza can also start with allow- ie allow-hotplug''' | |
211 | if inastanza: | |
212 | if not pitchstanza: | |
213 | newfile.extend(stanza) | |
214 | stanza=[] | |
215 | inastanza=1 | |
216 | pitchstanza=0 | |
217 | autoline=line.strip().split(" ") | |
218 | for word in autoline: | |
219 | if (word == basename) or (word.startswith(basename+":")): | |
220 | pitchstanza=1 | |
221 | if not pitchstanza: | |
222 | stanza+=[line.strip()] | |
223 | elif line.strip() in (start_mark, end_mark): | |
224 | if inastanza: | |
225 | if not pitchstanza: | |
226 | newfile.extend(stanza) | |
227 | stanza=[] | |
228 | inastanza = 0 | |
229 | pitchstanza = 0 | |
230 | # Deduplicate markers. | |
231 | if line != prev_line: | |
232 | newfile += [line.strip()] | |
233 | else: | |
234 | if inastanza: | |
235 | if not pitchstanza: | |
236 | stanza+=[line.strip()] | |
237 | else: | |
238 | if not pitchstanza: | |
239 | newfile += [line.strip()] | |
240 | prev_line=line | |
241 | ||
242 | # Include pending stanza if any. | |
243 | if inastanza and not pitchstanza: | |
244 | newfile.extend(stanza) | |
245 | ||
246 | ||
247 | def emit(line): | |
248 | print(line) | |
249 | output = line + "\n" | |
250 | os.write(fd, output.encode('utf-8')) | |
251 | ||
252 | # Insert the new output at the end and inside the existing markers if found. | |
253 | emitted = False | |
254 | fd, path = tempfile.mkstemp() | |
255 | for line in newfile: | |
256 | if line == end_mark: | |
257 | emit("\n".join(output)) | |
258 | emitted = True | |
259 | emit(line) | |
260 | if not emitted: | |
261 | emit(start_mark) | |
262 | emit("\n".join(output)) | |
263 | emit(end_mark) | |
264 | os.close(fd) | |
265 | ||
266 | shutil.copy(path,if_filename) | |
267 | os.chmod(if_filename,0o644) | |
268 | ||
269 | #print("TMPFILE is at: " + path) | |
270 | #print("Copied file is at: " + if_filename) | |
271 | ||
272 | try: | |
273 | retcode = subprocess.call("ifdown "+basename , shell=True) | |
274 | if retcode < 0: | |
275 | print("Child was terminated by signal", -retcode, file=sys.stderr) | |
276 | else: | |
277 | print("Child returned", retcode, file=sys.stderr) | |
278 | except OSError as e: | |
279 | print("Execution failed:", e, file=sys.stderr) | |
280 | ||
281 | try: | |
282 | retcode = subprocess.call("ifup "+basename , shell=True) | |
283 | if retcode < 0: | |
284 | print("Child was terminated by signal", -retcode, file=sys.stderr) | |
285 | else: | |
286 | print("Child returned", retcode, file=sys.stderr) | |
287 | except OSError as e: | |
288 | print("Execution failed:", e, file=sys.stderr) |