Changes for 2.6.32 kernel
[nodemanager-topo.git] / create-topo-attributes.py
1 # $Id$
2 # $URL$
3
4 """
5 Scan the VINI Central database and create topology "rspec" tags for
6 slices that have an EGRE key.  This script to be run from a cron job.
7 """
8
9 import string
10 import socket
11 from topology import links, bwlimits
12
13 class Node:
14     def __init__(self, node):
15         self.id = node['node_id']
16         self.hostname = node['hostname']
17         self.shortname = self.hostname.replace('.vini-veritas.net', '')
18         self.site_id = node['site_id']
19         self.ipaddr = socket.gethostbyname(self.hostname)
20
21     def get_link_id(self, remote):
22         if self.id < remote.id:
23             link = (self.id<<7) + remote.id
24         else:
25             link = (remote.id<<7) + self.id
26         return link
27         
28     def get_iface_id(self, remote):
29         if self.id < remote.id:
30             iface = 1
31         else:
32             iface = 2
33         return iface
34     
35     def get_virt_ip(self, remote):
36         link = self.get_link_id(remote)
37         iface = self.get_iface_id(remote)
38         first = link >> 6
39         second = ((link & 0x3f)<<2) + iface
40         return "192.168.%d.%d" % (first, second)
41
42     def get_virt_net(self, remote):
43         link = self.get_link_id(remote)
44         first = link >> 6
45         second = (link & 0x3f)<<2
46         return "192.168.%d.%d/30" % (first, second)
47         
48     def get_site(self, sites):
49         return sites[self.site_id]
50             
51     
52     # What nodes in the set of node_ids are adjacent to this one?
53     def adjacent_nodes(self, sites, nodes, node_ids):
54         mysite = self.get_site(sites)
55         adj_ids = mysite.adj_node_ids.intersection(node_ids)
56         adj_nodes = []
57         for id in adj_ids:
58             adj_nodes.append(nodes[id])
59         return adj_nodes
60     
61     def init_rspecs(self):
62         self.rspecs = []
63         
64     def add_rspec(self, remote, bw):
65         my_ip = self.get_virt_ip(remote)
66         remote_ip = remote.get_virt_ip(self)
67         net = self.get_virt_net(remote)
68         rspec = remote.id, remote.ipaddr, bw, my_ip, remote_ip, net
69         self.rspecs.append(rspec)
70
71         
72 class Site:
73     def __init__(self, site):
74         self.id = site['site_id']
75         self.node_ids = site['node_ids']
76         self.adj_site_ids = set()
77         self.adj_node_ids = set()
78
79     def get_sitenodes(self, nodes):
80         n = []
81         for i in self.node_ids:
82             n.append(nodes[i])
83         return n
84     
85     def add_adjacency(self, site):
86         self.adj_site_ids.add(site.id)
87         for n in site.node_ids:
88             self.adj_node_ids.add(n)
89         
90     
91 class Slice:
92     def __init__(self, slice):
93         self.id = slice['slice_id']
94         self.name = slice['name']
95         self.node_ids = set(slice['node_ids'])
96         self.slice_tag_ids = slice['slice_tag_ids']
97     
98     def get_tag(self, tagname, slicetags, node = None):
99         for i in self.slice_tag_ids:
100             tag = slicetags[i]
101             if tag.tagname == tagname:
102                 if (not node) or (node.id == tag.node_id):
103                     return tag
104         else:
105             return None
106         
107     def get_nodes(self, nodes):
108         n = []
109         for id in self.node_ids:
110             n.append(nodes[id])
111         return n
112              
113     
114     # Add a new slice tag   
115     def add_tag(self, tagname, value, slicetags, node = None):
116         record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value}
117         if node:
118             record['node_id'] = node.id
119         else:
120             record['node_id'] = None
121         tag = Slicetag(record)
122         slicetags[tag.id] = tag
123         self.slice_tag_ids.append(tag.id)
124         tag.changed = True       
125         tag.updated = True
126         return tag
127     
128     # Update a slice tag if it exists, else add it             
129     def update_tag(self, tagname, value, slicetags, node = None):
130         tag = self.get_tag(tagname, slicetags, node)
131         if tag and tag.value == value:
132             value = "no change"
133         elif tag:
134             tag.value = value
135             tag.changed = True
136         else:
137             tag = self.add_tag(tagname, value, slicetags, node)
138         tag.updated = True
139             
140     def assign_egre_key(self, slicetags):
141         if not self.get_tag('egre_key', slicetags):
142             try:
143                 key = free_egre_key(slicetags)
144                 self.update_tag('egre_key', key, slicetags)
145             except:
146                 # Should handle this case...
147                 pass
148         return
149             
150     def turn_on_netns(self, slicetags):
151         tag = self.get_tag('netns', slicetags)
152         if (not tag) or (tag.value != '1'):
153             self.update_tag('netns', '1', slicetags)
154         return
155    
156     def turn_off_netns(self, slicetags):
157         tag = self.get_tag('netns', slicetags)
158         if tag and (tag.value != '0'):
159             tag.delete()
160         return
161     
162     def add_cap_net_admin(self, slicetags):
163         tag = self.get_tag('capabilities', slicetags)
164         if tag:
165             caps = tag.value.split(',')
166             for cap in caps:
167                 if cap == "CAP_NET_ADMIN":
168                     return
169             else:
170                 newcaps = "CAP_NET_ADMIN," + tag.value
171                 self.update_tag('capabilities', newcaps, slicetags)
172         else:
173             self.add_tag('capabilities', 'CAP_NET_ADMIN', slicetags)
174         return
175     
176     def remove_cap_net_admin(self, slicetags):
177         tag = self.get_tag('capabilities', slicetags)
178         if tag:
179             caps = tag.value.split(',')
180             newcaps = []
181             for cap in caps:
182                 if cap != "CAP_NET_ADMIN":
183                     newcaps.append(cap)
184             if newcaps:
185                 value = ','.join(newcaps)
186                 self.update_tag('capabilities', value, slicetags)
187             else:
188                 tag.delete()
189         return
190
191     # Update the vsys/setup-link and vsys/setup-nat slice tags.
192     def add_vsys_tags(self, slicetags):
193         link = nat = False
194         for i in self.slice_tag_ids:
195             tag = slicetags[i]
196             if tag.tagname == 'vsys':
197                 if tag.value == 'setup-link':
198                     link = True
199                 elif tag.value == 'setup-nat':
200                     nat = True
201         if not link:
202             self.add_tag('vsys', 'setup-link', slicetags)
203         if not nat:
204             self.add_tag('vsys', 'setup-nat', slicetags)
205         return
206
207
208 class Slicetag:
209     newid = -1 
210     def __init__(self, tag):
211         self.id = tag['slice_tag_id']
212         if not self.id:
213             # Make one up for the time being...
214             self.id = Slicetag.newid
215             Slicetag.newid -= 1
216         self.slice_id = tag['slice_id']
217         self.tagname = tag['tagname']
218         self.value = tag['value']
219         self.node_id = tag['node_id']
220         self.updated = False
221         self.changed = False
222         self.deleted = False
223     
224     # Mark a tag as deleted
225     def delete(self):
226         self.deleted = True
227         self.updated = True
228     
229     def write(self, slices, nodes, dryrun):
230         if not dryrun:
231             if self.changed:
232                 if int(self.id) > 0:
233                     UpdateSliceTag(self.id, self.value)
234                 else:
235                     AddSliceTag(self.slice_id, self.tagname, self.value, self.node_id)
236             elif self.deleted and int(self.id) > 0:
237                 try:
238                     DeleteSliceTag(self.id)
239                 except:
240                     print "[%s] %s: could not delete" % (self.id, self.tagname)
241         else:
242             try:
243                 slice = slices[self.slice_id].name
244             except:
245                 return
246             if self.node_id:
247                 node = nodes[tag.node_id].hostname
248             if self.updated:
249                 if self.deleted:
250                     self.value = "deleted"
251                 elif not self.changed:
252                     self.value = "no change"
253                 if int(self.id) < 0:
254                     self.id = "new"
255                 if self.node_id:
256                     print "[%s] %s: %s (%s, %s)" % (self.id, self.tagname, self.value, slice, node)
257                 else:
258                     print "[%s] %s: %s (%s)" % (self.id, self.tagname, self.value, slice)
259
260
261 """
262 Create a dictionary of site objects keyed by site ID
263 """
264 def get_sites():
265     tmp = []
266     for site in GetSites():
267         t = site['site_id'], Site(site)
268         tmp.append(t)
269     return dict(tmp)
270
271
272 """
273 Create a dictionary of node objects keyed by node ID
274 """
275 def get_nodes():
276     tmp = []
277     for node in GetNodes():
278         t = node['node_id'], Node(node)
279         tmp.append(t)
280     return dict(tmp)
281
282 """
283 Create a dictionary of slice objects keyed by slice ID
284 """
285 def get_slices():
286     tmp = []
287     for slice in GetSlices():
288         t = slice['slice_id'], Slice(slice)
289         tmp.append(t)
290     return dict(tmp)
291
292 """
293 Create a dictionary of slicetag objects keyed by slice tag ID
294 """
295 def get_slice_tags():
296     tmp = []
297     for tag in GetSliceTags():
298         t = tag['slice_tag_id'], Slicetag(tag)
299         tmp.append(t)
300     return dict(tmp)
301     
302 """
303 Find a free EGRE key
304 """
305 def free_egre_key(slicetags):
306     used = set()
307     for i in slicetags:
308         tag = slicetags[i]
309         if tag.tagname == 'egre_key':
310             used.add(int(tag.value))
311                 
312     for i in range(1, 256):
313         if i not in used:
314             key = i
315             break
316     else:
317         raise KeyError("No more EGRE keys available")
318         
319     return "%s" % key
320    
321 # For debugging
322 dryrun = 0
323
324 sites = get_sites()
325 nodes = get_nodes()
326 slices = get_slices()
327 slicetags = get_slice_tags()
328
329 # Add adjacencies
330 for (a, b) in links:
331     sites[a].add_adjacency(sites[b])
332     sites[b].add_adjacency(sites[a])  
333
334 for i in slices:
335     slice = slices[i]
336     tag = slice.get_tag('vini_topo', slicetags)
337     if tag:
338         topo_type = tag.value
339     else:
340         topo_type = None
341     
342     """
343     Valid values of topo_type: 
344     'vsys':   Use vsys topology scripts to set up virtual links
345     'iias':   Automatically create a virtual topology mirroring the physical one
346     'manual': Don't modify the topo_rspec tags if present
347     None:     No virtual topology
348     """
349     if topo_type in ['vsys', 'iias', 'manual']:
350         slice.assign_egre_key(slicetags)
351         slice.turn_on_netns(slicetags)
352         slice.add_cap_net_admin(slicetags)
353     else:
354         # Let them keep EGRE key for now...
355         slice.turn_off_netns(slicetags)
356         slice.remove_cap_net_admin(slicetags)
357             
358     # Add vsys/setup-link and vsys/setup-nat
359     if topo_type == 'vsys' and slice.get_tag('egre_key', slicetags):
360         slice.add_vsys_tags(slicetags)
361         
362     if topo_type == 'iias' and slice.get_tag('egre_key', slicetags):
363         if dryrun:
364             print "Building virtual topology for %s" % slice.name
365             
366         if slice.name in bwlimits:
367             bw = bwlimits[slice.name]
368         else:
369             bw = "1Mbit"
370
371         hosts = "127.0.0.1\t\tlocalhost\n"
372         """
373         For each node in the slice, check whether the slice is running on any
374         adjacent nodes.  For each pair of adjacent nodes, add to nodes' rspecs.
375         """
376         for node in slice.get_nodes(nodes):
377             node.init_rspecs()
378             adj_nodes = node.adjacent_nodes(sites, nodes, slice.node_ids)
379             for adj in adj_nodes:
380                 node.add_rspec(adj, bw)
381                 hosts += "%s\t\t%s\n" % (node.get_virt_ip(adj), node.shortname)
382             if node.rspecs:
383                 topo_str = "%s" % node.rspecs
384                 slice.update_tag('topo_rspec', topo_str, slicetags, node)
385                     
386         slice.update_tag('hosts', hosts, slicetags)
387     else:
388         if dryrun:
389             print "Slice %s not using IIAS" % slice.name
390
391     if topo_type == 'manual' and slice.get_tag('egre_key', slicetags):
392         for node in slice.get_nodes(nodes):
393             topo_tag = slice.get_tag('topo_rspec', slicetags, node)
394             if topo_tag:
395                 topo_tag.updated = True
396             
397 # Update the tag values in the database
398 for i in slicetags:
399     tag = slicetags[i]
400     if (tag.tagname == 'topo_rspec' or tag.tagname == 'hosts') and not tag.updated:
401         tag.delete()
402     tag.write(slices, nodes, dryrun)
403
404