Generate Capacity in VINI Rspec
[sfa.git] / sfa / rspecs / aggregates / vini / utils.py
1 import re
2 import socket
3 from sfa.rspecs.aggregates.vini.topology import *
4
5 # Taken from bwlimit.py
6 #
7 # See tc_util.c and http://physics.nist.gov/cuu/Units/binary.html. Be
8 # warned that older versions of tc interpret "kbps", "mbps", "mbit",
9 # and "kbit" to mean (in this system) "kibps", "mibps", "mibit", and
10 # "kibit" and that if an older version is installed, all rates will
11 # be off by a small fraction.
12 suffixes = {
13     "":         1,
14     "bit":      1,
15     "kibit":    1024,
16     "kbit":     1000,
17     "mibit":    1024*1024,
18     "mbit":     1000000,
19     "gibit":    1024*1024*1024,
20     "gbit":     1000000000,
21     "tibit":    1024*1024*1024*1024,
22     "tbit":     1000000000000,
23     "bps":      8,
24     "kibps":    8*1024,
25     "kbps":     8000,
26     "mibps":    8*1024*1024,
27     "mbps":     8000000,
28     "gibps":    8*1024*1024*1024,
29     "gbps":     8000000000,
30     "tibps":    8*1024*1024*1024*1024,
31     "tbps":     8000000000000
32 }
33
34
35 def get_tc_rate(s):
36     """
37     Parses an integer or a tc rate string (e.g., 1.5mbit) into bits/second
38     """
39
40     if type(s) == int:
41         return s
42     m = re.match(r"([0-9.]+)(\D*)", s)
43     if m is None:
44         return -1
45     suffix = m.group(2).lower()
46     if suffixes.has_key(suffix):
47         return int(float(m.group(1)) * suffixes[suffix])
48     else:
49         return -1
50
51
52 class Node:
53     def __init__(self, node):
54         self.id = node['node_id']
55         self.hostname = node['hostname']
56         self.shortname = self.hostname.replace('.vini-veritas.net', '')
57         self.site_id = node['site_id']
58         self.ipaddr = socket.gethostbyname(self.hostname)
59         self.links = []
60
61     def get_link_id(self, remote):
62         if self.id < remote.id:
63             link = (self.id<<7) + remote.id
64         else:
65             link = (remote.id<<7) + self.id
66         return link
67         
68     def get_iface_id(self, remote):
69         if self.id < remote.id:
70             iface = 1
71         else:
72             iface = 2
73         return iface
74     
75     def get_virt_ip(self, remote):
76         link = self.get_link_id(remote)
77         iface = self.get_iface_id(remote)
78         first = link >> 6
79         second = ((link & 0x3f)<<2) + iface
80         return "192.168.%d.%d" % (first, second)
81
82     def get_virt_net(self, remote):
83         link = self.get_link_id(remote)
84         first = link >> 6
85         second = (link & 0x3f)<<2
86         return "192.168.%d.%d/30" % (first, second)
87         
88     def get_site(self, sites):
89         return sites[self.site_id]
90     
91     def init_links(self):
92         self.links = []
93         
94     def add_link(self, remote, bw):
95         my_ip = self.get_virt_ip(remote)
96         remote_ip = remote.get_virt_ip(self)
97         net = self.get_virt_net(remote)
98         link = remote.id, remote.ipaddr, bw, my_ip, remote_ip, net
99         self.links.append(link)
100         
101     def add_tag(self, sites):
102         s = self.get_site(sites)
103         words = self.hostname.split(".")
104         index = words[0].replace("node", "")
105         if index.isdigit():
106             self.tag = s.tag + index
107         else:
108             self.tag = None
109         
110
111 class SiteLink:
112     def __init__(self, site1, site2, mbps = 1000):
113         self.site1 = site1
114         self.site2 = site2
115         self.totalMbps = mbps
116         self.availMbps = mbps
117         
118         site1.add_sitelink(self)
119         site2.add_sitelink(self)
120         
121         
122 class Site:
123     def __init__(self, site):
124         self.id = site['site_id']
125         self.node_ids = site['node_ids']
126         self.name = site['abbreviated_name']
127         self.tag = site['login_base']
128         self.public = site['is_public']
129         self.sitelinks = []
130
131     def get_sitenodes(self, nodes):
132         n = []
133         for i in self.node_ids:
134             n.append(nodes[i])
135         return n
136     
137     def add_sitelink(self, link):
138         self.sitelinks.append(link)
139     
140     
141 class Slice:
142     def __init__(self, slice):
143         self.id = slice['slice_id']
144         self.name = slice['name']
145         self.node_ids = set(slice['node_ids'])
146         self.slice_tag_ids = slice['slice_tag_ids']
147     
148     def get_tag(self, tagname, slicetags, node = None):
149         for i in self.slice_tag_ids:
150             tag = slicetags[i]
151             if tag.tagname == tagname:
152                 if (not node) or (node.id == tag.node_id):
153                     return tag
154         else:
155             return None
156         
157     def get_nodes(self, nodes):
158         n = []
159         for id in self.node_ids:
160             n.append(nodes[id])
161         return n
162              
163     
164     # Add a new slice tag   
165     def add_tag(self, tagname, value, slicetags, node = None):
166         record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value}
167         if node:
168             record['node_id'] = node.id
169         else:
170             record['node_id'] = None
171         tag = Slicetag(record)
172         slicetags[tag.id] = tag
173         self.slice_tag_ids.append(tag.id)
174         tag.changed = True       
175         tag.updated = True
176         return tag
177     
178     # Update a slice tag if it exists, else add it             
179     def update_tag(self, tagname, value, slicetags, node = None):
180         tag = self.get_tag(tagname, slicetags, node)
181         if tag and tag.value == value:
182             value = "no change"
183         elif tag:
184             tag.value = value
185             tag.changed = True
186         else:
187             tag = self.add_tag(tagname, value, slicetags, node)
188         tag.updated = True
189             
190     def assign_egre_key(self, slicetags):
191         if not self.get_tag('egre_key', slicetags):
192             try:
193                 key = free_egre_key(slicetags)
194                 self.update_tag('egre_key', key, slicetags)
195             except:
196                 # Should handle this case...
197                 pass
198         return
199             
200     def turn_on_netns(self, slicetags):
201         tag = self.get_tag('netns', slicetags)
202         if (not tag) or (tag.value != '1'):
203             self.update_tag('netns', '1', slicetags)
204         return
205    
206     def turn_off_netns(self, slicetags):
207         tag = self.get_tag('netns', slicetags)
208         if tag and (tag.value != '0'):
209             tag.delete()
210         return
211     
212     def add_cap_net_admin(self, slicetags):
213         tag = self.get_tag('capabilities', slicetags)
214         if tag:
215             caps = tag.value.split(',')
216             for cap in caps:
217                 if cap == "CAP_NET_ADMIN":
218                     return
219             else:
220                 newcaps = "CAP_NET_ADMIN," + tag.value
221                 self.update_tag('capabilities', newcaps, slicetags)
222         else:
223             self.add_tag('capabilities', 'CAP_NET_ADMIN', slicetags)
224         return
225     
226     def remove_cap_net_admin(self, slicetags):
227         tag = self.get_tag('capabilities', slicetags)
228         if tag:
229             caps = tag.value.split(',')
230             newcaps = []
231             for cap in caps:
232                 if cap != "CAP_NET_ADMIN":
233                     newcaps.append(cap)
234             if newcaps:
235                 value = ','.join(newcaps)
236                 self.update_tag('capabilities', value, slicetags)
237             else:
238                 tag.delete()
239         return
240
241     # Update the vsys/setup-link and vsys/setup-nat slice tags.
242     def add_vsys_tags(self, slicetags):
243         link = nat = False
244         for i in self.slice_tag_ids:
245             tag = slicetags[i]
246             if tag.tagname == 'vsys':
247                 if tag.value == 'setup-link':
248                     link = True
249                 elif tag.value == 'setup-nat':
250                     nat = True
251         if not link:
252             self.add_tag('vsys', 'setup-link', slicetags)
253         if not nat:
254             self.add_tag('vsys', 'setup-nat', slicetags)
255         return
256
257
258 class Slicetag:
259     newid = -1 
260     def __init__(self, tag):
261         self.id = tag['slice_tag_id']
262         if not self.id:
263             # Make one up for the time being...
264             self.id = Slicetag.newid
265             Slicetag.newid -= 1
266         self.slice_id = tag['slice_id']
267         self.tagname = tag['tagname']
268         self.value = tag['value']
269         self.node_id = tag['node_id']
270         self.updated = False
271         self.changed = False
272         self.deleted = False
273     
274     # Mark a tag as deleted
275     def delete(self):
276         self.deleted = True
277         self.updated = True
278     
279     def write(self, api):
280         if self.changed:
281             if int(self.id) > 0:
282                 api.plshell.UpdateSliceTag(api.plauth, self.id, self.value)
283             else:
284                 api.plshell.AddSliceTag(api.plauth, self.slice_id, 
285                                         self.tagname, self.value, self.node_id)
286         elif self.deleted and int(self.id) > 0:
287             api.plshell.DeleteSliceTag(api.plauth, self.id)
288
289
290 """
291 Create a dictionary of site objects keyed by site ID
292 """
293 def get_sites(api):
294     tmp = []
295     for site in api.plshell.GetSites(api.plauth):
296         t = site['site_id'], Site(site)
297         tmp.append(t)
298     return dict(tmp)
299
300
301 """
302 Create a dictionary of node objects keyed by node ID
303 """
304 def get_nodes(api):
305     tmp = []
306     for node in api.plshell.GetNodes(api.plauth):
307         t = node['node_id'], Node(node)
308         tmp.append(t)
309     return dict(tmp)
310
311 """
312 Create a dictionary of slice objects keyed by slice ID
313 """
314 def get_slice(api, slicename):
315     slice = api.plshell.GetSlices(api.plauth, [slicename])
316     if slice:
317         return Slice(slice[0])
318     else:
319         return None
320
321 """
322 Create a dictionary of slicetag objects keyed by slice tag ID
323 """
324 def get_slice_tags(api):
325     tmp = []
326     for tag in api.plshell.GetSliceTags(api.plauth):
327         t = tag['slice_tag_id'], Slicetag(tag)
328         tmp.append(t)
329     return dict(tmp)
330     
331 """
332 Find a free EGRE key
333 """
334 def free_egre_key(slicetags):
335     used = set()
336     for i in slicetags:
337         tag = slicetags[i]
338         if tag.tagname == 'egre_key':
339             used.add(int(tag.value))
340                 
341     for i in range(1, 256):
342         if i not in used:
343             key = i
344             break
345     else:
346         raise KeyError("No more EGRE keys available")
347         
348     return "%s" % key
349    
350 """
351 Return the network topology.
352 The topology consists of:
353 * a dictionary mapping site IDs to Site objects
354 * a dictionary mapping node IDs to Node objects
355 * the Site objects are connected via SiteLink objects representing
356   the physical topology and available bandwidth
357 """
358 def get_topology(api):
359     sites = get_sites(api)
360     nodes = get_nodes(api)
361     tags = get_slice_tags(api)
362     
363     for (s1, s2) in PhysicalLinks:
364         SiteLink(sites[s1], sites[s2])
365
366     for id in nodes:
367         nodes[id].add_tag(sites)
368         
369     return (sites, nodes, tags)