5 VINI/Trellis NodeManager plugin.
6 Create virtual links from the topo_rspec slice attribute.
15 from time import strftime
18 vinidir = "/usr/share/vini/"
19 setup_link_cmd = vinidir + "setup-egre-link"
20 teardown_link_cmd = vinidir + "teardown-egre-link"
21 setup_nat_cmd = vinidir + "setup-nat"
22 teardown_nat_cmd = vinidir + "teardown-nat"
31 return subprocess.call(cmd, shell=True);
35 Subnet used for virtual interfaces by setup-egre-link script
38 return "192.168.0.0 255.255.0.0"
42 Check for existence of interface d<key>x<nodeid>
44 def virtual_link(key, nodeid):
45 name = "d%sx%s" % (key, nodeid)
52 Create a "virtual link" for slice between here and nodeid.
53 The key is used to create the EGRE tunnel.
55 def setup_virtual_link(slice, key, rate, myid, nodeid, ipaddr, virtip, vnet):
56 logger.log("%s: Set up virtual link to node %s" % (slice, nodeid))
57 run(setup_link_cmd + " %s %s %s %s %s %s %s" % (slice, nodeid, ipaddr,
58 key, rate, virtip, vnet))
63 Tear down the "virtual link" for slice between here and nodeid.
65 def teardown_virtual_link(key, nodeid):
66 logger.log("topo: Tear down virtual link %sx%s" % (key, nodeid))
67 run(teardown_link_cmd + " %s %s" % (nodeid, key))
72 Called for all active virtual link interfaces, so they won't be cleaned up.
74 def refresh_virtual_link(nodeid, key):
75 name = "d%sx%s" % (key, nodeid)
76 if name in old_ifaces:
82 IP address of the NAT interface created inside the slice by the
85 def nat_inner_ip(key):
86 return "10.0.%s.2" % key
90 Check for existence of interface natx<key>
101 Create a NAT interface inside the sliver.
103 def setup_nat(slice, myid, key):
104 logger.log("%s: Set up NAT" % slice)
105 run(setup_nat_cmd + " %s %s %s" % (slice, myid, key))
110 Tear down the NAT interface identified by key
112 def teardown_nat(key):
113 logger.log("topo: Tear down NAT %s" % key)
114 run(teardown_nat_cmd + " %s" % key)
119 Called for all active NAT interfaces, so they won't be cleaned up.
121 def refresh_nat(key):
122 name = "natx%s" % (key)
123 if name in old_ifaces:
129 Clean up old virtual links (e.g., to nodes that have been deleted
132 def clean_up_old_virtual_links():
133 pattern1 = "d(.*)x(.*)"
134 pattern2 = "natx(.*)"
135 for iface in old_ifaces:
136 m = re.match(pattern1, iface)
138 key = int(m.group(1))
139 node = int(m.group(2))
140 teardown_virtual_link(key, node)
142 m = re.match(pattern2, iface)
144 key = int(m.group(1))
150 Not the safest thing to do, probably should use pickle() or something.
152 def convert_topospec_to_list(rspec):
157 Update virtual links for the slice
159 def update_links(slice, myid, topospec, key, netns):
160 topolist = convert_topospec_to_list(topospec)
161 for (nodeid, ipaddr, rate, myvirtip, remvirtip, virtnet) in topolist:
162 if not virtual_link(key, nodeid):
164 setup_virtual_link(slice, key, rate, myid, nodeid,
165 ipaddr, myvirtip, virtnet)
167 logger.log("%s: virtual link to node %s exists" % (slice, nodeid))
168 refresh_virtual_link(nodeid, key)
170 if not nat_exists(key):
172 setup_nat(slice, myid, key)
174 logger.log("%s: NAT exists" % slice)
179 Write /etc/vservers/<slicename>/spaces/net. If the vserver is running and the spaces/net file is modified, we need to restart it.
181 def write_conf_and_restart(slicename, value):
182 SLICEDIR="/etc/vservers/%s/" % slicename
183 SPACESDIR="%s/spaces/" % SLICEDIR
184 FILENAME="%s/net" % SPACESDIR
185 if os.path.exists(SLICEDIR):
186 if not os.path.exists(SPACESDIR):
190 logger.log("topo: could not create %s\n" % SPACESDIR)
193 if os.path.exists(FILENAME) != value:
194 sliver = vserver.VServer(slicename)
195 restart = sliver.is_running()
202 f = open(FILENAME, "w")
208 logger.log("%s: network namespace %s\n" % (slicename, STATUS))
211 logger.log("topo: restarting sliver %s\n" % slicename)
216 Generate information for each interface in the sliver, in order to configure
219 def get_ifaces(hostname, myid, topospec, key):
221 topolist = convert_topospec_to_list(topospec)
222 for (nodeid, ipaddr, rate, myvirtip, remvirtip, virtnet) in topolist:
223 name = "a%sx%s" % (key, nodeid)
225 ifaces[name]['remote-ip'] = remvirtip
226 ifaces[name]['local-ip'] = myvirtip
227 ifaces[name]['network'] = virtnet
228 ifaces[name]['short-name'] = hostname.replace('.vini-veritas.net', '')
232 def write_header(f, myname, password):
233 f.write ("""! Configuration for %s
239 """ % (myname, strftime("%Y-%m-%d %H:%M:%S"), myname, password))
244 IP address of NAT gateway to outside world
247 return "10.0.%s.1" % key
250 IP address of the NAT interface inside the slice
253 return "10.0.%s.2" % key
257 Write zebra.conf file for Quagga
259 def write_zebra(filename, myname, ifaces, myid, key):
260 f = open(filename, 'w')
262 write_header(f, myname, password)
264 f.write ("enable password %s\n" % password)
273 access-list vty permit 127.0.0.1/32
283 Write ospfd.conf file for Quagga.
285 def write_ospf(filename, myname, ifaces):
286 f = open(filename, 'w')
288 write_header(f, myname, password)
294 ip ospf hello-interval 5
295 ip ospf dead-interval 10
296 ip ospf network non-broadcast
302 """ % ifaces[name]['local-ip'])
305 f.write (" neighbor %s\n" % ifaces[name]['remote-ip'])
308 net = ifaces[name]['network']
309 f.write (" network %s area 0\n" % net)
311 f.write(""" redistribute kernel
313 access-list vty permit 127.0.0.1/32
321 Write config files directly into the slice's file system.
323 def update_quagga_configs(slicename, hostname, myid, topo, key, netns):
324 ifaces = get_ifaces(hostname, myid, topo, key)
326 quagga_dir = "/vservers/%s/etc/quagga/" % slicename
327 if not os.path.exists(quagga_dir):
329 # Quagga not installed. Install it here? Chkconfig, sym links.
332 logger.log("topo: could not create %s\n" % quagga_dir)
335 write_zebra(quagga_dir + "zebra.conf.generated", hostname, ifaces,
337 write_ospf(quagga_dir + "ospfd.conf.generated", hostname, ifaces)
339 # Start up Quagga if we installed it earlier and netns = 1.
345 Write /etc/hosts in the sliver
347 def update_hosts(slicename, hosts):
348 hosts_file = "/vservers/%s/etc/hosts" % slicename
349 f = open(hosts_file, 'w')
355 Write /etc/vini/egre-keys.txt, used by vsys topo scripts
357 def write_egre_keys(slicekeys):
358 vini_dir = "/etc/vini"
359 if not os.path.exists(vini_dir):
363 logger.log("topo: could not create %s\n" % vini_dir)
365 keys_file = "%s/egre-keys.txt" % vini_dir
366 f = open(keys_file, 'w')
367 for slice in slicekeys:
368 f.write("%s %s\n" % (slice, slicekeys[slice]))
374 Executed on NM startup
376 def start(options, config):
377 run ("echo 1 > /proc/sys/net/ipv4/ip_forward")
382 Update the virtual links for a sliver if it has a 'netns' attribute,
383 an 'egre_key' attribute, and a 'topo_rspec' attribute.
385 Creating the virtual link depends on the contents of
386 /etc/vservers/<slice>/spaces/net. Update this first.
388 def GetSlivers(data):
389 global ifaces, old_ifaces
390 ifaces = old_ifaces = sioc.gifconf()
393 for sliver in data['slivers']:
395 for tag in sliver['attributes']:
396 attrs[tag['tagname']] = tag['value']
397 if tag['tagname'] == 'egre_key':
398 slicekeys[sliver['name']] = tag['value']
402 netns = int(attrs['netns'])
405 write_conf_and_restart(sliver['name'], netns)
407 if vserver.VServer(sliver['name']).is_running():
408 if 'egre_key' in attrs and 'topo_rspec' in attrs:
409 logger.log("topo: Update topology for slice %s" % \
411 update_links(sliver['name'], data['node_id'],
412 attrs['topo_rspec'], attrs['egre_key'], netns)
413 update_quagga_configs(sliver['name'], data['hostname'],
414 data['node_id'], attrs['topo_rspec'],
415 attrs['egre_key'], netns)
417 update_hosts(sliver['name'], attrs['hosts'])
419 logger.log("topo: sliver %s not running yet. Deferring." % \
422 clean_up_old_virtual_links()
423 write_egre_keys(slicekeys)