5 VINI/Trellis NodeManager plugin.
6 Create virtual links from the topo_rspec slice attribute.
15 from time import strftime
19 vinidir = "/usr/share/vini/"
20 setup_link_cmd = vinidir + "setup-egre-link"
21 teardown_link_cmd = vinidir + "teardown-egre-link"
22 setup_nat_cmd = vinidir + "setup-nat"
23 teardown_nat_cmd = vinidir + "teardown-nat"
32 return subprocess.call(cmd, shell=True);
35 From old pyplnet, former semantics needed for VINI
39 interfaces = os.listdir("/sys/class/net")
45 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
46 for interface in interfaces:
48 ifreq = fcntl.ioctl(s.fileno(), SIOCGIFADDR,
49 struct.pack("16sH14x", interface, socket.AF_INET))
50 (family, ip) = struct.unpack(SIOCGIFADDR_struct, ifreq)
51 if family == socket.AF_INET:
52 ret[interface] = _format_ip(ip)
56 ret[interface] = "0.0.0.0"
63 Subnet used for virtual interfaces by setup-egre-link script
66 return "192.168.0.0 255.255.0.0"
70 Check for existence of interface d<key>x<nodeid>
72 def virtual_link(key, nodeid):
73 name = "d%sx%s" % (key, nodeid)
80 Create a "virtual link" for slice between here and nodeid.
81 The key is used to create the EGRE tunnel.
83 def setup_virtual_link(slice, key, rate, myid, nodeid, ipaddr, virtip, vnet):
84 logger.log("%s: Set up virtual link to node %s" % (slice, nodeid))
85 run(setup_link_cmd + " %s %s %s %s %s %s %s" % (slice, nodeid, ipaddr,
86 key, rate, virtip, vnet))
91 Tear down the "virtual link" for slice between here and nodeid.
93 def teardown_virtual_link(key, nodeid):
94 logger.log("topo: Tear down virtual link %sx%s" % (key, nodeid))
95 run(teardown_link_cmd + " %s %s" % (nodeid, key))
100 Called for all active virtual link interfaces, so they won't be cleaned up.
102 def refresh_virtual_link(nodeid, key):
103 name = "d%sx%s" % (key, nodeid)
104 if name in old_ifaces:
110 IP address of the NAT interface created inside the slice by the
113 def nat_inner_ip(key):
114 return "10.0.%s.2" % key
118 Check for existence of interface natx<key>
121 name = "natx%s" % key
129 Create a NAT interface inside the sliver.
131 def setup_nat(slice, myid, key):
132 logger.log("%s: Set up NAT" % slice)
133 run(setup_nat_cmd + " %s %s %s" % (slice, myid, key))
138 Tear down the NAT interface identified by key
140 def teardown_nat(key):
141 logger.log("topo: Tear down NAT %s" % key)
142 run(teardown_nat_cmd + " %s" % key)
147 Called for all active NAT interfaces, so they won't be cleaned up.
149 def refresh_nat(key):
150 name = "natx%s" % (key)
151 if name in old_ifaces:
157 Clean up old virtual links (e.g., to nodes that have been deleted
160 def clean_up_old_virtual_links():
161 pattern1 = "d(.*)x(.*)"
162 pattern2 = "natx(.*)"
163 for iface in old_ifaces:
164 m = re.match(pattern1, iface)
168 teardown_virtual_link(key, node)
170 m = re.match(pattern2, iface)
178 Not the safest thing to do, probably should use pickle() or something.
180 def convert_topospec_to_list(rspec):
185 Update virtual links for the slice
187 def update_links(slice, myid, topospec, key, netns):
188 topolist = convert_topospec_to_list(topospec)
189 for (nodeid, ipaddr, rate, myvirtip, remvirtip, virtnet) in topolist:
190 if not virtual_link(key, nodeid):
192 setup_virtual_link(slice, key, rate, myid, nodeid,
193 ipaddr, myvirtip, virtnet)
195 logger.log("%s: virtual link to node %s exists" % (slice, nodeid))
196 refresh_virtual_link(nodeid, key)
200 Update NAT interface for the slice
202 def update_nat(slice, myid, key, netns):
203 if not nat_exists(key):
205 setup_nat(slice, myid, key)
207 logger.log("%s: NAT exists" % slice)
212 Write /etc/vservers/<slicename>/spaces/net.
213 Restart the vserver if there are any changes.
215 def write_spaces_net(slicename, value):
216 SLICEDIR="/etc/vservers/%s/" % slicename
217 SPACESDIR="%s/spaces/" % SLICEDIR
218 FILENAME="%s/net" % SPACESDIR
219 if os.path.exists(SLICEDIR):
220 if not os.path.exists(SPACESDIR):
224 logger.log("topo: could not create %s\n" % SPACESDIR)
227 if os.path.exists(FILENAME) != value:
228 sliver = vserver.VServer(slicename)
234 f = open(FILENAME, "w")
242 logger.log("%s: network namespace %s\n" % (slicename, STATUS))
246 Generate information for each interface in the sliver, in order to configure
249 def get_ifaces(hostname, myid, topospec, key):
251 topolist = convert_topospec_to_list(topospec)
252 for (nodeid, ipaddr, rate, myvirtip, remvirtip, virtnet) in topolist:
253 name = "a%sx%s" % (key, nodeid)
255 ifaces[name]['remote-ip'] = remvirtip
256 ifaces[name]['local-ip'] = myvirtip
257 ifaces[name]['network'] = virtnet
258 ifaces[name]['short-name'] = hostname.replace('.vini-veritas.net', '')
262 def write_header(f, myname, password):
263 f.write ("""! Configuration for %s
269 """ % (myname, strftime("%Y-%m-%d %H:%M:%S"), myname, password))
274 IP address of NAT gateway to outside world
277 return "10.0.%s.1" % key
280 IP address of the NAT interface inside the slice
283 return "10.0.%s.2" % key
287 Write zebra.conf file for Quagga
289 def write_zebra(filename, myname, ifaces, myid, key):
290 f = open(filename, 'w')
292 write_header(f, myname, password)
294 f.write ("enable password %s\n" % password)
303 access-list vty permit 127.0.0.1/32
313 Write ospfd.conf file for Quagga.
315 def write_ospf(filename, myname, ifaces):
316 f = open(filename, 'w')
318 write_header(f, myname, password)
325 ip ospf hello-interval 5
326 ip ospf dead-interval 10
327 ip ospf network non-broadcast
334 """ % ifaces[name]['local-ip'])
337 f.write (" neighbor %s\n" % ifaces[name]['remote-ip'])
340 net = ifaces[name]['network']
341 f.write (" network %s area 0\n" % net)
343 f.write(""" redistribute kernel
345 access-list vty permit 127.0.0.1/32
353 Write config files directly into the slice's file system.
355 def update_quagga_configs(slicename, hostname, myid, topo, key, netns):
356 ifaces = get_ifaces(hostname, myid, topo, key)
358 quagga_dir = "/vservers/%s/etc/quagga/" % slicename
359 if not os.path.exists(quagga_dir):
363 logger.log("topo: could not create %s\n" % quagga_dir)
366 write_zebra(quagga_dir + "zebra.conf.generated", hostname, ifaces,
368 write_ospf(quagga_dir + "ospfd.conf.generated", hostname, ifaces)
374 Write /etc/hosts in the sliver
376 def update_hosts(slicename, hosts):
377 hosts_file = "/vservers/%s/etc/hosts" % slicename
378 f = open(hosts_file, 'w')
384 Write /etc/vini/egre-keys.txt, used by vsys topo scripts
386 def write_egre_keys(slicekeys):
387 vini_dir = "/etc/vini"
388 if not os.path.exists(vini_dir):
392 logger.log("topo: could not create %s\n" % vini_dir)
394 keys_file = "%s/egre-keys.txt" % vini_dir
395 f = open(keys_file, 'w')
396 for slice in slicekeys:
397 f.write("%s %s\n" % (slice, slicekeys[slice]))
403 Executed on NM startup
406 # Should be taken care of by /etc/sysctl.conf, but it doesn't hurt...
407 run ("echo 1 > /proc/sys/net/ipv4/ip_forward")
412 Update the virtual links for a sliver if it has a 'netns' attribute,
413 an 'egre_key' attribute, and a 'topo_rspec' attribute.
415 Creating the virtual link depends on the contents of
416 /etc/vservers/<slice>/spaces/net. Update this first.
418 def GetSlivers(data, config = None, plc = None):
419 global ifaces, old_ifaces
420 ifaces = old_ifaces = gifconf()
423 for sliver in data['slivers']:
425 for tag in sliver['attributes']:
426 attrs[tag['tagname']] = tag['value']
427 if tag['tagname'] == 'egre_key':
428 slicekeys[sliver['name']] = tag['value']
431 if vserver.VServer(sliver['name']).is_running():
433 netns = int(attrs['netns'])
436 write_spaces_net(sliver['name'], netns)
438 if vserver.VServer(sliver['name']).is_running():
439 if 'egre_key' in attrs:
440 logger.log("topo: Update slice %s" % sliver['name'])
441 update_nat(sliver['name'], data['node_id'], attrs['egre_key'],
443 if 'topo_rspec' in attrs:
444 update_links(sliver['name'], data['node_id'],
445 attrs['topo_rspec'], attrs['egre_key'], netns)
446 update_quagga_configs(sliver['name'], data['hostname'],
447 data['node_id'], attrs['topo_rspec'],
448 attrs['egre_key'], netns)
450 update_hosts(sliver['name'], attrs['hosts'])
452 logger.log("topo: sliver %s not running yet. Deferring." % \
455 clean_up_old_virtual_links()
456 write_egre_keys(slicekeys)