3 from sfa.rspecs.aggregates.vini.topology import *
5 # Taken from bwlimit.py
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.
19 "gibit": 1024*1024*1024,
21 "tibit": 1024*1024*1024*1024,
22 "tbit": 1000000000000,
28 "gibps": 8*1024*1024*1024,
30 "tibps": 8*1024*1024*1024*1024,
37 Parses an integer or a tc rate string (e.g., 1.5mbit) into bits/second
42 m = re.match(r"([0-9.]+)(\D*)", s)
45 suffix = m.group(2).lower()
46 if suffixes.has_key(suffix):
47 return int(float(m.group(1)) * suffixes[suffix])
51 def format_tc_rate(rate):
53 Formats a bits/second rate into a tc rate string
56 if rate >= 1000000000 and (rate % 1000000000) == 0:
57 return "%.0fgbit" % (rate / 1000000000.)
58 elif rate >= 1000000 and (rate % 1000000) == 0:
59 return "%.0fmbit" % (rate / 1000000.)
61 return "%.0fkbit" % (rate / 1000.)
63 return "%.0fbit" % rate
67 def __init__(self, node, mbps = 1000):
68 self.id = node['node_id']
69 self.hostname = node['hostname']
70 self.shortname = self.hostname.replace('.vini-veritas.net', '')
71 self.site_id = node['site_id']
72 self.ipaddr = socket.gethostbyname(self.hostname)
73 self.bps = mbps * 1000000
76 def get_link_id(self, remote):
77 if self.id < remote.id:
78 link = (self.id<<7) + remote.id
80 link = (remote.id<<7) + self.id
83 def get_iface_id(self, remote):
84 if self.id < remote.id:
90 def get_virt_ip(self, remote):
91 link = self.get_link_id(remote)
92 iface = self.get_iface_id(remote)
94 second = ((link & 0x3f)<<2) + iface
95 return "192.168.%d.%d" % (first, second)
97 def get_virt_net(self, remote):
98 link = self.get_link_id(remote)
100 second = (link & 0x3f)<<2
101 return "192.168.%d.%d/30" % (first, second)
103 def get_site(self, sites):
104 return sites[self.site_id]
106 def init_links(self):
109 def add_link(self, remote, bw):
110 my_ip = self.get_virt_ip(remote)
111 remote_ip = remote.get_virt_ip(self)
112 net = self.get_virt_net(remote)
113 link = remote.id, remote.ipaddr, bw, my_ip, remote_ip, net
114 self.links.append(link)
116 def add_tag(self, sites):
117 s = self.get_site(sites)
118 words = self.hostname.split(".")
119 index = words[0].replace("node", "")
121 self.tag = s.tag + index
125 # Assumes there is at most one SiteLink between two sites
126 def get_sitelink(self, node, sites):
127 site1 = sites[self.site_id]
128 site2 = sites[node.site_id]
129 sl = site1.sitelinks.intersection(site2.sitelinks)
136 def __init__(self, site1, site2, mbps = 1000):
139 self.bps = mbps * 1000000
141 site1.add_sitelink(self)
142 site2.add_sitelink(self)
146 def __init__(self, site):
147 self.id = site['site_id']
148 self.node_ids = site['node_ids']
149 self.name = site['abbreviated_name']
150 self.tag = site['login_base']
151 self.public = site['is_public']
152 self.sitelinks = set()
154 def get_sitenodes(self, nodes):
156 for i in self.node_ids:
160 def add_sitelink(self, link):
161 self.sitelinks.add(link)
165 def __init__(self, slice):
166 self.id = slice['slice_id']
167 self.name = slice['name']
168 self.node_ids = set(slice['node_ids'])
169 self.slice_tag_ids = slice['slice_tag_ids']
171 def get_tag(self, tagname, slicetags, node = None):
172 for i in self.slice_tag_ids:
174 if tag.tagname == tagname:
175 if (not node) or (node.id == tag.node_id):
180 def get_nodes(self, nodes):
182 for id in self.node_ids:
187 # Add a new slice tag
188 def add_tag(self, tagname, value, slicetags, node = None):
189 record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value}
191 record['node_id'] = node.id
193 record['node_id'] = None
194 tag = Slicetag(record)
195 slicetags[tag.id] = tag
196 self.slice_tag_ids.append(tag.id)
201 # Update a slice tag if it exists, else add it
202 def update_tag(self, tagname, value, slicetags, node = None):
203 tag = self.get_tag(tagname, slicetags, node)
204 if tag and tag.value == value:
210 tag = self.add_tag(tagname, value, slicetags, node)
213 def assign_egre_key(self, slicetags):
214 if not self.get_tag('egre_key', slicetags):
216 key = free_egre_key(slicetags)
217 self.update_tag('egre_key', key, slicetags)
219 # Should handle this case...
223 def turn_on_netns(self, slicetags):
224 tag = self.get_tag('netns', slicetags)
225 if (not tag) or (tag.value != '1'):
226 self.update_tag('netns', '1', slicetags)
229 def turn_off_netns(self, slicetags):
230 tag = self.get_tag('netns', slicetags)
231 if tag and (tag.value != '0'):
235 def add_cap_net_admin(self, slicetags):
236 tag = self.get_tag('capabilities', slicetags)
238 caps = tag.value.split(',')
240 if cap == "CAP_NET_ADMIN":
243 newcaps = "CAP_NET_ADMIN," + tag.value
244 self.update_tag('capabilities', newcaps, slicetags)
246 self.add_tag('capabilities', 'CAP_NET_ADMIN', slicetags)
249 def remove_cap_net_admin(self, slicetags):
250 tag = self.get_tag('capabilities', slicetags)
252 caps = tag.value.split(',')
255 if cap != "CAP_NET_ADMIN":
258 value = ','.join(newcaps)
259 self.update_tag('capabilities', value, slicetags)
264 # Update the vsys/setup-link and vsys/setup-nat slice tags.
265 def add_vsys_tags(self, slicetags):
267 for i in self.slice_tag_ids:
269 if tag.tagname == 'vsys':
270 if tag.value == 'setup-link':
272 elif tag.value == 'setup-nat':
275 self.add_tag('vsys', 'setup-link', slicetags)
277 self.add_tag('vsys', 'setup-nat', slicetags)
283 def __init__(self, tag):
284 self.id = tag['slice_tag_id']
286 # Make one up for the time being...
287 self.id = Slicetag.newid
289 self.slice_id = tag['slice_id']
290 self.tagname = tag['tagname']
291 self.value = tag['value']
292 self.node_id = tag['node_id']
297 # Mark a tag as deleted
302 def write(self, api):
305 api.plshell.UpdateSliceTag(api.plauth, self.id, self.value)
307 api.plshell.AddSliceTag(api.plauth, self.slice_id,
308 self.tagname, self.value, self.node_id)
309 elif self.deleted and int(self.id) > 0:
310 api.plshell.DeleteSliceTag(api.plauth, self.id)
314 Create a dictionary of site objects keyed by site ID
318 for site in api.plshell.GetSites(api.plauth):
319 t = site['site_id'], Site(site)
325 Create a dictionary of node objects keyed by node ID
329 for node in api.plshell.GetNodes(api.plauth):
330 t = node['node_id'], Node(node)
335 Create a dictionary of slice objects keyed by slice ID
337 def get_slice(api, slicename):
338 slice = api.plshell.GetSlices(api.plauth, [slicename])
340 return Slice(slice[0])
345 Create a dictionary of slicetag objects keyed by slice tag ID
347 def get_slice_tags(api):
349 for tag in api.plshell.GetSliceTags(api.plauth):
350 t = tag['slice_tag_id'], Slicetag(tag)
357 def free_egre_key(slicetags):
361 if tag.tagname == 'egre_key':
362 used.add(int(tag.value))
364 for i in range(1, 256):
369 raise KeyError("No more EGRE keys available")
374 Return the network topology.
375 The topology consists of:
376 * a dictionary mapping site IDs to Site objects
377 * a dictionary mapping node IDs to Node objects
378 * the Site objects are connected via SiteLink objects representing
379 the physical topology and available bandwidth
381 def get_topology(api):
382 sites = get_sites(api)
383 nodes = get_nodes(api)
384 tags = get_slice_tags(api)
386 for (s1, s2) in PhysicalLinks:
387 SiteLink(sites[s1], sites[s2])
390 nodes[id].add_tag(sites)
394 if tag.tagname == 'topo_rspec':
395 node1 = nodes[tag.node_id]
397 for (id, realip, bw, lvip, rvip, vnet) in l:
398 allocbps = get_tc_rate(bw)
399 node1.bps -= allocbps
402 if node1.id < node2.id:
403 sl = node1.get_sitelink(node2, sites)
408 return (sites, nodes, tags)