Setup and teardown NAT interfaces automatically
[nodemanager-topo.git] / topo.py
1 # $Id$
2 # $URL$
3
4 """ 
5 VINI/Trellis NodeManager plugin.
6 Create virtual links from the topo_rspec slice attribute. 
7 """
8
9 import logger
10 import subprocess
11 import sioc
12 import re
13 import vserver
14 import os
15
16 dryrun = 0
17 vinidir = "/usr/share/vini/"
18 setup_link_cmd = vinidir + "setup-egre-link"
19 teardown_link_cmd = vinidir + "teardown-egre-link"
20 setup_nat_cmd = vinidir + "setup-nat"
21 teardown_nat_cmd = vinidir + "teardown-nat"
22 ifaces = {}
23 old_ifaces = {}
24
25 def run(cmd):
26     if dryrun:
27         logger.log(cmd)
28         return -1
29     else:
30         return subprocess.call(cmd, shell=True);
31
32
33 """
34 Check for existence of interface d<key>x<nodeid>
35 """
36 def virtual_link(key, nodeid):
37     name = "d%sx%s" % (key, nodeid)
38     if name in ifaces:
39         return True
40     else:
41         return False
42
43
44 """
45 Create a "virtual link" for slice between here and nodeid.
46 The key is used to create the EGRE tunnel.
47 """
48 def setup_virtual_link(slice, key, rate, myid, nodeid, ipaddr):
49     logger.log("%s: Set up virtual link to node %d" % (slice, nodeid))
50     if myid < nodeid:
51         virtip = "10.%d.%d.2" % (myid, nodeid)
52     else:
53         virtip = "10.%d.%d.3" % (nodeid, myid)
54         
55     run(setup_link_cmd + " %s %s %s %s %s %s" % (slice, nodeid, ipaddr, 
56                                                  key, rate, virtip))
57     return
58
59
60 """
61 Tear down the "virtual link" for slice between here and nodeid.
62 """
63 def teardown_virtual_link(key, nodeid):
64     logger.log("topo: Tear down virtual link %sx%s" % (key, nodeid))
65     run(teardown_link_cmd + " %s %s" % (nodeid, key))
66     return
67
68
69 """
70 Called for all active virtual link interfaces, so they won't be cleaned up.
71 """
72 def refresh_virtual_link(nodeid, key):
73     name = "d%sx%s" % (key, nodeid)
74     if name in old_ifaces:
75         del old_ifaces[name]
76     return
77
78
79 """
80 Check for existence of interface natx<key>
81 """
82 def nat_exists(key):
83     name = "natx%s" % key
84     if name in ifaces:
85         return True
86     else:
87         return False
88
89
90 """
91 Create a NAT interface inside the sliver.  
92 """
93 def setup_nat(slice, myid, key):
94     logger.log("%s: Set up NAT" % slice)
95     run(setup_nat_cmd + " %s %s %s" % (slice, myid, key))
96     return
97
98
99 """
100 Tear down the NAT interface identified by key
101 """
102 def teardown_nat(key):
103     logger.log("topo: Tear down NAT %s" % key)
104     run(teardown_nat_cmd + " %s" % key)
105     return
106
107
108 """
109 Called for all active NAT interfaces, so they won't be cleaned up.
110 """
111 def refresh_nat(key):
112     name = "natx%s" % (key)
113     if name in old_ifaces:
114         del old_ifaces[name]
115     return
116
117
118 """
119 Clean up old virtual links (e.g., to nodes that have been deleted 
120 from the slice).
121 """
122 def clean_up_old_virtual_links():
123     pattern1 = "d(.*)x(.*)"
124     pattern2 = "natx(.*)"
125     for iface in old_ifaces:
126         m = re.match(pattern1, iface)
127         if m:
128             key = int(m.group(1))
129             node = int(m.group(2))
130             teardown_virtual_link(key, node)
131
132         m = re.match(pattern2, iface)
133         if m:
134             key = int(m.group(1))
135             teardown_nat(key)
136     return
137
138
139 """
140 Not the safest thing to do, probably should use pickle() or something.
141 """
142 def convert_topospec_to_list(rspec):
143     return eval(rspec)
144
145
146 """
147 Update virtual links for the slice
148 """
149 def update(slice, myid, topospec, key, netns):
150     topolist = convert_topospec_to_list(topospec)
151     for (nodeid,ipaddr,rate) in topolist:
152         if not virtual_link(key, nodeid):
153             if netns:
154                 setup_virtual_link(slice, key, rate, myid, nodeid, ipaddr)
155         else:
156             logger.log("%s: virtual link to node %s exists" % (slice, nodeid))
157             refresh_virtual_link(nodeid, key)
158
159     if not nat_exists(key):
160         setup_nat(slice, myid, key)
161     else:
162         logger.log("%s: NAT exists" % slice)
163         refresh_nat(key)
164
165
166 """
167 Write /etc/vservers/<slicename>/spaces/net
168 """
169 def writeConf(slicename, value):
170     SLICEDIR="/etc/vservers/%s/" % slicename
171     SPACESDIR="%s/spaces/" % SLICEDIR
172     if os.path.exists(SLICEDIR):
173         if not os.path.exists(SPACESDIR):
174             try:
175                 os.mkdir(SPACESDIR)
176             except os.error:
177                 logger.log("netns: could not create %s\n" % SPACESDIR)
178                 return
179         f = open("%s/net" % SPACESDIR, "w")
180         f.write("%s\n" % value)
181         f.close()
182         STATUS="OFF"
183         if value:
184             STATUS="ON"
185         logger.log("%s: network namespace %s\n" % (slicename, STATUS))
186
187
188 def start(options, config):
189     pass
190
191
192 """
193 Update the virtual links for a sliver if it has a 'netns' attribute,
194 an 'egre_key' attribute, and a 'topo_rspec' attribute.
195
196 Creating the virtual link depends on the contents of 
197 /etc/vservers/<slice>/spaces/net.  Update this first.
198 """
199 def GetSlivers(data):
200     global ifaces, old_ifaces
201     ifaces = old_ifaces = sioc.gifconf()
202
203     for sliver in data['slivers']:
204         attrs = {}
205         for attribute in sliver['attributes']:
206             attrs[attribute['name']] = attribute['value']
207         if 'netns' in attrs:
208             netns = int(attrs['netns'])
209             writeConf(sliver['name'], netns)
210         else:
211             netns = 0
212
213         try:
214             if vserver.VServer(sliver['name']).is_running():
215                 if 'egre_key' in attrs and 'topo_rspec' in attrs:
216                     logger.log("topo: Update topology for slice %s" % \
217                                    sliver['name'])
218                     update(sliver['name'], data['node_id'], 
219                            attrs['topo_rspec'], attrs['egre_key'], netns)
220         except:
221             logger.log("topo: sliver %s not running yet. Deferring." % \
222                            sliver['name'])
223
224     clean_up_old_virtual_links()
225
226