Update all slice tags in one shot
[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
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):
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, "1Mbit", 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         tag = Slicetag(record)
120         slicetags[id] = tag
121         self.slice_tag_ids.append(id)
122         tag.changed = True       
123         tag.updated = True
124         return tag
125     
126     # Update a slice tag if it exists, else add it             
127     def update_tag(self, tagname, value, slicetags, node = None):
128         tag = self.get_tag(tagname, slicetags, node)
129         if tag and tag.value == value:
130             value = "no change"
131         elif tag:
132             tag.value = value
133             tag.changed = True
134         else:
135             tag = self.add_tag(tagname, value, slicetags, node)
136         tag.updated = True
137             
138     def assign_egre_key(self, slicetags):
139         if not self.get_tag('egre_key', slicetags):
140             try:
141                 key = free_egre_key(slicetags)
142                 self.update_tag('egre_key', key, slicetags)
143             except:
144                 # Should handle this case...
145                 pass
146         return
147             
148     def turn_on_netns(self, slicetags):
149         tag = self.get_tag('netns', slicetags)
150         if (not tag) or (tag.value != '1'):
151             self.update_tag('netns', '1', slicetags)
152         return
153    
154     def turn_off_netns(self, slicetags):
155         tag = self.get_tag('netns', slicetags)
156         if tag and (tag.value != '0'):
157             tag.delete()
158         return
159     
160     def add_cap_net_admin(self, slicetags):
161         tag = self.get_tag('capabilities', slicetags)
162         if tag:
163             caps = tag.value.split(',')
164             for cap in caps:
165                 if cap == "CAP_NET_ADMIN":
166                     return
167             else:
168                 newcaps = "CAP_NET_ADMIN," + caps
169                 self.update_tag('capabilities', newcaps, slicetags)
170         else:
171             self.add_tag('capabilities', 'CAP_NET_ADMIN', slicetags)
172         return
173     
174     def remove_cap_net_admin(self, slicetags):
175         tag = self.get_tag('capabilities', slicetags)
176         if tag:
177             caps = tag.value.split(',')
178             newcaps = []
179             for cap in caps:
180                 if cap != "CAP_NET_ADMIN":
181                     newcaps.append(cap)
182             if newcaps:
183                 value = ','.join(newcaps)
184                 self.update_tag('capabilities', value, slicetags)
185             else:
186                 tag.delete()
187         return
188
189     # Update the vsys/setup-link and vsys/setup-nat slice tags.
190     def add_vsys_tags(self, slicetags):
191         link = nat = False
192         for i in self.slice_tag_ids:
193             tag = slicetags[i]
194             if tag.tagname == 'vsys':
195                 if tag.value == 'setup-link':
196                     link = True
197                 elif tag.value == 'setup-nat':
198                     nat = True
199         if not link:
200             self.add_tag('vsys', 'setup-link', slicetags)
201         if not nat:
202             self.add_tag('vsys', 'setup-nat', slicetags)
203         return
204
205
206 class Slicetag:
207     newid = -1 
208     def __init__(self, tag):
209         self.id = tag['slice_tag_id']
210         if not self.id:
211             # Make one up for the time being...
212             self.id = Slicetag.newid
213             Slicetag.newid -= 1
214         self.slice_id = tag['slice_id']
215         self.tagname = tag['tagname']
216         self.value = tag['value']
217         self.node_id = tag['node_id']
218         self.updated = False
219         self.changed = False
220         self.deleted = False
221     
222     # Mark a tag as deleted
223     def delete(self):
224         self.deleted = True
225         self.updated = True
226     
227     def write(self, slices, nodes, dryrun):
228         if not dryrun:
229             if self.changed:
230                 if int(self.id) > 0:
231                     UpdateSliceTag(self.id, self.value)
232                 else:
233                     AddSliceTag(self.slice_id, self.tagname, self.value, self.node_id)
234             elif self.deleted and int(self.id) > 0:
235                 DeleteSliceTag(self.id)
236         else:
237             slice = slices[self.slice_id].name
238             if self.node_id:
239                 node = nodes[tag.node_id].hostname
240             if self.updated:
241                 if self.deleted:
242                     self.value = "deleted"
243                 elif not self.changed:
244                     self.value = "no change"
245                 if int(self.id) < 0:
246                     self.id = "new"
247                 if self.node_id:
248                     print "[%s] %s: %s (%s, %s)" % (self.id, self.tagname, self.value, slice, node)
249                 else:
250                     print "[%s] %s: %s (%s)" % (self.id, self.tagname, self.value, slice)
251
252
253 """
254 Create a dictionary of site objects keyed by site ID
255 """
256 def get_sites():
257     tmp = []
258     for site in GetSites():
259         t = site['site_id'], Site(site)
260         tmp.append(t)
261     return dict(tmp)
262
263
264 """
265 Create a dictionary of node objects keyed by node ID
266 """
267 def get_nodes():
268     tmp = []
269     for node in GetNodes():
270         t = node['node_id'], Node(node)
271         tmp.append(t)
272     return dict(tmp)
273
274 """
275 Create a dictionary of slice objects keyed by slice ID
276 """
277 def get_slices():
278     tmp = []
279     for slice in GetSlices():
280         t = slice['slice_id'], Slice(slice)
281         tmp.append(t)
282     return dict(tmp)
283
284 """
285 Create a dictionary of slicetag objects keyed by slice tag ID
286 """
287 def get_slice_tags():
288     tmp = []
289     for tag in GetSliceTags():
290         t = tag['slice_tag_id'], Slicetag(tag)
291         tmp.append(t)
292     return dict(tmp)
293     
294 """
295 Find a free EGRE key
296 """
297 def free_egre_key(slicetags):
298     for i in slicetags:
299         used = set()
300         tag = slicetags[i]
301         if tag.tagname == 'egre_key':
302             used.add(int(tag.value))
303                 
304     for i in range(1, 256):
305         if i not in used:
306             key = i
307             break
308     else:
309         raise KeyError("No more EGRE keys available")
310         
311     return "%s" % key
312    
313 # For debugging
314 dryrun = 0
315
316 sites = get_sites()
317 nodes = get_nodes()
318 slices = get_slices()
319 slicetags = get_slice_tags()
320
321 # Add adjacencies
322 for (a, b) in links:
323     sites[a].add_adjacency(sites[b])
324     sites[b].add_adjacency(sites[a])  
325
326 for i in slices:
327     slice = slices[i]
328     tag = slice.get_tag('vini_topo', slicetags)
329     if tag:
330         topo_type = tag.value
331     else:
332         topo_type = None
333     
334     if topo_type == 'vsys' or topo_type == 'iias':
335         slice.assign_egre_key(slicetags)
336         slice.turn_on_netns(slicetags)
337         slice.add_cap_net_admin(slicetags)
338     else:
339         # Let them keep EGRE key for now...
340         slice.turn_off_netns(slicetags)
341         slice.remove_cap_net_admin(slicetags)
342             
343     # Add vsys/setup-link and vsys/setup-nat
344     if topo_type == 'vsys' and slice.get_tag('egre_key', slicetags):
345         slice.add_vsys_tags(slicetags)
346         
347     if topo_type == 'iias' and slice.get_tag('egre_key', slicetags):
348         if dryrun:
349             print "Building virtual topology for %s" % slice.name
350             
351         hosts = "127.0.0.1\t\tlocalhost\n"
352         """
353         For each node in the slice, check whether the slice is running on any
354         adjacent nodes.  For each pair of adjacent nodes, add to nodes' rspecs.
355         """
356         for node in slice.get_nodes(nodes):
357             node.init_rspecs()
358             adj_nodes = node.adjacent_nodes(sites, nodes, slice.node_ids)
359             for adj in adj_nodes:
360                 node.add_rspec(adj)
361                 hosts += "%s\t\t%s\n" % (node.get_virt_ip(adj), node.shortname)
362             if node.rspecs:
363                 topo_str = "%s" % node.rspecs
364                 slice.update_tag('topo_rspec', topo_str, slicetags, node)
365                     
366         slice.update_tag('hosts', hosts, slicetags)
367     else:
368         if dryrun:
369             print "Slice %s not using IIAS" % slice.name
370
371 # Update the tag values in the database
372 for i in slicetags:
373     tag = slicetags[i]
374     if (tag.tagname == 'topo_rspec' or tag.tagname == 'hosts') and not tag.updated:
375         tag.delete()
376     tag.write(slices, nodes, dryrun)
377
378