Add the gateway for static networks.
[pyplnet.git] / plnet.py
1 #!/usr/bin/python /usr/bin/plcsh
2 # $Id$
3
4 import os
5 import socket
6 import time
7 import tempfile
8
9 import sioc
10 import modprobe
11
12 def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeManager"):
13     sysconfig = "%s/etc/sysconfig/network-scripts" % root
14
15     # query running network interfaces
16     devs = sioc.gifconf()
17     ips = dict(zip(devs.values(), devs.keys()))
18     macs = {}
19     for dev in devs:
20         macs[sioc.gifhwaddr(dev).lower()] = dev
21
22     # assume data['networks'] contains this node's NodeNetworks
23     interfaces = {}
24     interface = 1
25     hostname = data.get('hostname',socket.gethostname())
26     gateway = None
27     networks = data['networks']
28     failedToGetSettings = False
29     for network in networks:
30         logger.verbose('net:InitInterfaces interface %d: %s'%(interface,network))
31         logger.verbose('net:InitInterfaces macs = %s' % macs)
32         logger.verbose('net:InitInterfaces ips = %s' % ips)
33         # Get interface name preferably from MAC address, falling back
34         # on IP address.
35         hwaddr=network['mac']
36         if hwaddr <> None: hwaddr=hwaddr.lower()
37         if hwaddr in macs:
38             orig_ifname = macs[hwaddr]
39         elif network['ip'] in ips:
40             orig_ifname = ips[network['ip']]
41         else:
42             orig_ifname = None
43
44         if orig_ifname:
45                 logger.verbose('net:InitInterfaces orig_ifname = %s' % orig_ifname)
46         
47         inter = {}
48         inter['ONBOOT']='yes'
49         inter['USERCTL']='no'
50         if network['mac']:
51             inter['HWADDR'] = network['mac']
52         if network['is_primary']:
53             inter['PRIMARY']='yes'
54
55         if network['method'] == "static":
56             inter['BOOTPROTO'] = "static"
57             inter['IPADDR'] = network['ip']
58             inter['NETMASK'] = network['netmask']
59             if network['is_primary']:
60                 gateway = network['gateway']
61
62         elif network['method'] == "dhcp":
63             inter['BOOTPROTO'] = "dhcp"
64             inter['PERSISTENT_DHCLIENT'] = "yes"
65             if network['hostname']:
66                 inter['DHCP_HOSTNAME'] = network['hostname']
67             else:
68                 inter['DHCP_HOSTNAME'] = hostname 
69             if not network['is_primary']:
70                 inter['DHCLIENTARGS'] = "-R subnet-mask"
71
72         if len(network['interface_tag_ids']) > 0:
73             try:
74                 settings = plc.GetInterfaceTags({'interface_tag_id':
75                                                  network['interface_tag_ids']})
76             except:
77                 logger.log("net:InitInterfaces FATAL: failed call GetInterfaceTags({'interface_tag_id':{%s})"% \
78                            network['interface_tag_ids'])
79                 failedToGetSettings = True
80                 continue # on to the next network
81
82             for setting in settings:
83                 # to explicitly set interface name
84                 settingname = setting['name'].upper()
85                 if settingname in ('IFNAME','ALIAS','CFGOPTIONS','DRIVER'):
86                     inter[settingname]=setting['value']
87                 else:
88                     logger.log("net:InitInterfaces WARNING: ignored setting named %s"%setting['name'])
89
90         # support aliases to interfaces either by name or HWADDR
91         if 'ALIAS' in inter:
92             if 'HWADDR' in inter:
93                 hwaddr = inter['HWADDR'].lower()
94                 del inter['HWADDR']
95                 if hwaddr in macs:
96                     hwifname = macs[hwaddr]
97                     if ('IFNAME' in inter) and inter['IFNAME'] <> hwifname:
98                         logger.log("net:InitInterfaces WARNING: alias ifname (%s) and hwaddr ifname (%s) do not match"%\
99                                        (inter['IFNAME'],hwifname))
100                         inter['IFNAME'] = hwifname
101                 else:
102                     logger.log('net:InitInterfaces WARNING: mac addr %s for alias not found' %(hwaddr,alias))
103
104             if 'IFNAME' in inter:
105                 # stupid RH /etc/sysconfig/network-scripts/ifup-aliases:new_interface()
106                 # checks if the "$DEVNUM" only consists of '^[0-9A-Za-z_]*$'. Need to make
107                 # our aliases compliant.
108                 parts = inter['ALIAS'].split('_')
109                 isValid=True
110                 for part in parts:
111                     isValid=isValid and part.isalnum()
112
113                 if isValid:
114                     interfaces["%s:%s" % (inter['IFNAME'],inter['ALIAS'])] = inter 
115                 else:
116                     logger.log("net:InitInterfaces WARNING: interface alias (%s) not a valid string for RH ifup-aliases"% inter['ALIAS'])
117             else:
118                 logger.log("net:InitInterfaces WARNING: interface alias (%s) not matched to an interface"% inter['ALIAS'])
119             interface -= 1
120         else:
121             if ('IFNAME' not in inter) and not orig_ifname:
122                 ifname="eth%d" % (interface-1)
123                 # should check if $ifname is an eth already defines
124                 if os.path.exists("%s/ifcfg-%s"%(sysconfig,ifname)):
125                     logger.log("net:InitInterfaces WARNING: possibly blowing away %s configuration"%ifname)
126             else:
127                 if ('IFNAME' not in inter) and orig_ifname:
128                     ifname = orig_ifname
129                 else:
130                     ifname = inter['IFNAME']
131                 interface -= 1
132             interfaces[ifname] = inter
133                 
134     m = modprobe.Modprobe()
135     try:
136         m.input("%s/etc/modprobe.conf" % root, program)
137     except:
138         pass
139     for (dev, inter) in interfaces.iteritems():
140         # get the driver string "moduleName option1=a option2=b"
141         driver=inter.get('DRIVER','')
142         if driver <> '':
143             driver=driver.split()
144             kernelmodule=driver[0]
145             m.aliasset(dev,kernelmodule)
146             options=" ".join(driver[1:])
147             if options <> '':
148                 m.optionsset(dev,options)
149     m.output("%s/etc/modprobe.conf" % root)
150
151     # clean up after any ifcfg-$dev script that's no longer listed as
152     # part of the NodeNetworks associated with this node
153
154     # list all network-scripts
155     files = os.listdir(sysconfig)
156
157     # filter out the ifcfg-* files
158     ifcfgs=[]
159     for f in files:
160         if f.find("ifcfg-") == 0:
161             ifcfgs.append(f)
162
163     # remove loopback (lo) from ifcfgs list
164     lo = "ifcfg-lo"
165     if lo in ifcfgs: ifcfgs.remove(lo)
166
167     # remove known devices from icfgs list
168     for (dev, inter) in interfaces.iteritems():
169         ifcfg = 'ifcfg-'+dev
170         if ifcfg in ifcfgs: ifcfgs.remove(ifcfg)
171
172     # delete the remaining ifcfgs from 
173     deletedSomething = False
174
175     if not failedToGetSettings:
176         for ifcfg in ifcfgs:
177             dev = ifcfg[len('ifcfg-'):]
178             path = "%s/ifcfg-%s" % (sysconfig,dev)
179             if not files_only:
180                 logger.verbose("net:InitInterfaces removing %s %s"%(dev,path))
181                 os.system("/sbin/ifdown %s" % dev)
182             deletedSomething=True
183             os.unlink(path)
184
185     # wait a bit for the one or more ifdowns to have taken effect
186     if deletedSomething:
187         time.sleep(2)
188
189     # Write network configuration file
190     networkconf = file("%s/etc/sysconfig/network", "w")
191     networkconf.write("NETWORKING=yes\nHOSTNAME=%s\n" % hostname)
192     if gateway is not None:
193         networkconf.write("GATEWAY=%s\n" % gateway)
194     networkconf.close()
195
196     # Process ifcfg-$dev changes / additions
197     newdevs = []
198     for (dev, inter) in interfaces.iteritems():
199         (fd, tmpnam) = tempfile.mkstemp(dir=sysconfig)
200         f = os.fdopen(fd, "w")
201         f.write("# Autogenerated by pyplnet... do not edit!\n")
202         if 'DRIVER' in inter:
203             f.write("# using %s driver for device %s\n" % (inter['DRIVER'],dev))
204         f.write('DEVICE="%s"\n' % dev)
205         
206         # print the configuration values
207         for (key, val) in inter.iteritems():
208             if key not in ('IFNAME','ALIAS','CFGOPTIONS','DRIVER'):
209                 f.write('%s="%s"\n' % (key, val))
210
211         # print the configuration specific option values (if any)
212         if 'CFGOPTIONS' in inter:
213             cfgoptions = inter['CFGOPTIONS']
214             f.write('#CFGOPTIONS are %s\n' % cfgoptions)
215             for cfgoption in cfgoptions.split():
216                 key,val = cfgoption.split('=')
217                 key=key.strip()
218                 key=key.upper()
219                 val=val.strip()
220                 f.write('%s="%s"\n' % (key,val))
221         f.close()
222
223         # compare whether two files are the same
224         def comparefiles(a,b):
225             try:
226                 logger.verbose("net:InitInterfaces comparing %s with %s" % (a,b))
227                 if not os.path.exists(a): return False
228                 fb = open(a)
229                 buf_a = fb.read()
230                 fb.close()
231
232                 if not os.path.exists(b): return False
233                 fb = open(b)
234                 buf_b = fb.read()
235                 fb.close()
236
237                 return buf_a == buf_b
238             except IOError, e:
239                 return False
240
241         path = "%s/ifcfg-%s" % (sysconfig,dev)
242         if not os.path.exists(path):
243             logger.verbose('net:InitInterfaces adding configuration for %s' % dev)
244             # add ifcfg-$dev configuration file
245             os.rename(tmpnam,path)
246             os.chmod(path,0644)
247             newdevs.append(dev)
248             
249         elif not comparefiles(tmpnam,path):
250             logger.verbose('net:InitInterfaces Configuration change for %s' % dev)
251             if not files_only:
252                 logger.verbose('net:InitInterfaces ifdown %s' % dev)
253                 # invoke ifdown for the old configuration
254                 os.system("/sbin/ifdown %s" % dev)
255                 # wait a few secs for ifdown to complete
256                 time.sleep(2)
257
258             logger.log('replacing configuration for %s' % dev)
259             # replace ifcfg-$dev configuration file
260             os.rename(tmpnam,path)
261             os.chmod(path,0644)
262             newdevs.append(dev)
263         else:
264             # tmpnam & path are identical
265             os.unlink(tmpnam)
266
267     for dev in newdevs:
268         cfgvariables = {}
269         fb = file("%s/ifcfg-%s"%(sysconfig,dev),"r")
270         for line in fb.readlines():
271             parts = line.split()
272             if parts[0][0]=="#":continue
273             if parts[0].find('='):
274                 name,value = parts[0].split('=')
275                 # clean up name & value
276                 name = name.strip()
277                 value = value.strip()
278                 value = value.strip("'")
279                 value = value.strip('"')
280                 cfgvariables[name]=value
281         fb.close()
282
283         def getvar(name):
284             if name in cfgvariables:
285                 value=cfgvariables[name]
286                 value = value.lower()
287                 return value
288             return ''
289
290         # skip over device configs with ONBOOT=no
291         if getvar("ONBOOT") == 'no': continue
292
293         # don't bring up slave devices, the network scripts will
294         # handle those correctly
295         if getvar("SLAVE") == 'yes': continue
296
297         if not files_only:
298             logger.verbose('net:InitInterfaces bringing up %s' % dev)
299             os.system("/sbin/ifup %s" % dev)
300
301 if __name__ == "__main__":
302     import optparse
303     import sys
304
305     parser = optparse.OptionParser(usage="plnet [-v] [-f] [-p <program>] -r root node_id")
306     parser.add_option("-v", "--verbose", action="store_true", dest="verbose")
307     parser.add_option("-r", "--root", action="store", type="string",
308                       dest="root", default=None)
309     parser.add_option("-f", "--files-only", action="store_true",
310                       dest="files_only")
311     parser.add_option("-p", "--program", action="store", type="string",
312                       dest="program", default="plnet")
313     (options, args) = parser.parse_args()
314     if len(args) != 1 or options.root is None:
315         print >>sys.stderr, "Missing root or node_id"
316         parser.print_help()
317         sys.exit(1)
318
319     node = shell.GetNodes({'node_id': [int(args[0])]})
320     networks = shell.GetInterfaces({'interface_id': node[0]['interface_ids']})
321
322     data = {'hostname': node[0]['hostname'], 'networks': networks}
323     class logger:
324         def __init__(self, verbose):
325             self.verbosity = verbose
326         def log(self, msg, loglevel=2):
327             if self.verbosity:
328                 print msg
329         def verbose(self, msg):
330             self.log(msg, 1)
331     l = logger(options.verbose)
332     InitInterfaces(l, shell, data, options.root, options.files_only)