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