1 from sfa.util.faults import *
2 from sfa.util.misc import *
3 from sfa.util.rspec import Rspec
4 from sfa.server.registry import Registries
5 from sfa.plc.nodes import *
10 SFA_VINI_DEFAULT_RSPEC = '/etc/sfa/vini.rspec'
11 SFA_VINI_WHITELIST = '/etc/sfa/vini.whitelist'
13 # Taken from bwlimit.py
15 # See tc_util.c and http://physics.nist.gov/cuu/Units/binary.html. Be
16 # warned that older versions of tc interpret "kbps", "mbps", "mbit",
17 # and "kbit" to mean (in this system) "kibps", "mibps", "mibit", and
18 # "kibit" and that if an older version is installed, all rates will
19 # be off by a small fraction.
27 "gibit": 1024*1024*1024,
29 "tibit": 1024*1024*1024*1024,
30 "tbit": 1000000000000,
36 "gibps": 8*1024*1024*1024,
38 "tibps": 8*1024*1024*1024*1024,
45 Parses an integer or a tc rate string (e.g., 1.5mbit) into bits/second
50 m = re.match(r"([0-9.]+)(\D*)", s)
53 suffix = m.group(2).lower()
54 if suffixes.has_key(suffix):
55 return int(float(m.group(1)) * suffixes[suffix])
61 def __init__(self, node):
62 self.id = node['node_id']
63 self.hostname = node['hostname']
64 self.shortname = self.hostname.replace('.vini-veritas.net', '')
65 self.site_id = node['site_id']
66 self.ipaddr = socket.gethostbyname(self.hostname)
69 def get_link_id(self, remote):
70 if self.id < remote.id:
71 link = (self.id<<7) + remote.id
73 link = (remote.id<<7) + self.id
76 def get_iface_id(self, remote):
77 if self.id < remote.id:
83 def get_virt_ip(self, remote):
84 link = self.get_link_id(remote)
85 iface = self.get_iface_id(remote)
87 second = ((link & 0x3f)<<2) + iface
88 return "192.168.%d.%d" % (first, second)
90 def get_virt_net(self, remote):
91 link = self.get_link_id(remote)
93 second = (link & 0x3f)<<2
94 return "192.168.%d.%d/30" % (first, second)
96 def get_site(self, sites):
97 return sites[self.site_id]
99 def adjacent_nodes(self, sites, nodes, node_ids):
100 mysite = self.get_site(sites)
101 adj_ids = mysite.adj_node_ids.intersection(node_ids)
104 adj_nodes.append(nodes[id])
107 def init_links(self):
110 def add_link(self, remote, bw):
111 my_ip = self.get_virt_ip(remote)
112 remote_ip = remote.get_virt_ip(self)
113 net = self.get_virt_net(remote)
114 link = remote.id, remote.ipaddr, bw, my_ip, remote_ip, net
115 self.links.append(link)
119 def __init__(self, site):
120 self.id = site['site_id']
121 self.node_ids = site['node_ids']
122 self.adj_site_ids = set()
123 self.adj_node_ids = set()
125 def get_sitenodes(self, nodes):
127 for i in self.node_ids:
131 def add_adjacency(self, site):
132 self.adj_site_ids.add(site.id)
133 for n in site.node_ids:
134 self.adj_node_ids.add(n)
138 def __init__(self, slice):
139 self.id = slice['slice_id']
140 self.name = slice['name']
141 self.node_ids = set(slice['node_ids'])
142 self.slice_tag_ids = slice['slice_tag_ids']
144 def get_tag(self, tagname, slicetags, node = None):
145 for i in self.slice_tag_ids:
147 if tag.tagname == tagname:
148 if (not node) or (node.id == tag.node_id):
153 def get_nodes(self, nodes):
155 for id in self.node_ids:
160 # Add a new slice tag
161 def add_tag(self, tagname, value, slicetags, node = None):
162 record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value}
164 record['node_id'] = node.id
166 record['node_id'] = None
167 tag = Slicetag(record)
168 slicetags[tag.id] = tag
169 self.slice_tag_ids.append(tag.id)
174 # Update a slice tag if it exists, else add it
175 def update_tag(self, tagname, value, slicetags, node = None):
176 tag = self.get_tag(tagname, slicetags, node)
177 if tag and tag.value == value:
183 tag = self.add_tag(tagname, value, slicetags, node)
186 def assign_egre_key(self, slicetags):
187 if not self.get_tag('egre_key', slicetags):
189 key = free_egre_key(slicetags)
190 self.update_tag('egre_key', key, slicetags)
192 # Should handle this case...
196 def turn_on_netns(self, slicetags):
197 tag = self.get_tag('netns', slicetags)
198 if (not tag) or (tag.value != '1'):
199 self.update_tag('netns', '1', slicetags)
202 def turn_off_netns(self, slicetags):
203 tag = self.get_tag('netns', slicetags)
204 if tag and (tag.value != '0'):
208 def add_cap_net_admin(self, slicetags):
209 tag = self.get_tag('capabilities', slicetags)
211 caps = tag.value.split(',')
213 if cap == "CAP_NET_ADMIN":
216 newcaps = "CAP_NET_ADMIN," + tag.value
217 self.update_tag('capabilities', newcaps, slicetags)
219 self.add_tag('capabilities', 'CAP_NET_ADMIN', slicetags)
222 def remove_cap_net_admin(self, slicetags):
223 tag = self.get_tag('capabilities', slicetags)
225 caps = tag.value.split(',')
228 if cap != "CAP_NET_ADMIN":
231 value = ','.join(newcaps)
232 self.update_tag('capabilities', value, slicetags)
237 # Update the vsys/setup-link and vsys/setup-nat slice tags.
238 def add_vsys_tags(self, slicetags):
240 for i in self.slice_tag_ids:
242 if tag.tagname == 'vsys':
243 if tag.value == 'setup-link':
245 elif tag.value == 'setup-nat':
248 self.add_tag('vsys', 'setup-link', slicetags)
250 self.add_tag('vsys', 'setup-nat', slicetags)
256 def __init__(self, tag):
257 self.id = tag['slice_tag_id']
259 # Make one up for the time being...
260 self.id = Slicetag.newid
262 self.slice_id = tag['slice_id']
263 self.tagname = tag['tagname']
264 self.value = tag['value']
265 self.node_id = tag['node_id']
270 # Mark a tag as deleted
275 def write(self, api):
278 api.plshell.UpdateSliceTag(api.plauth, self.id, self.value)
280 api.plshell.AddSliceTag(api.plauth, self.slice_id,
281 self.tagname, self.value, self.node_id)
282 elif self.deleted and int(self.id) > 0:
283 api.plshell.DeleteSliceTag(api.plauth, self.id)
287 Create a dictionary of site objects keyed by site ID
291 for site in GetSites():
292 t = site['site_id'], Site(site)
298 Create a dictionary of node objects keyed by node ID
302 for node in api.plshell.GetNodes(api.plauth):
303 t = node['node_id'], Node(node)
308 Create a dictionary of slice objects keyed by slice ID
310 def get_slice(api, slicename):
311 slice = api.plshell.GetSlices(api.plauth, [slicename])
313 return Slice(slice[0])
318 Create a dictionary of slicetag objects keyed by slice tag ID
320 def get_slice_tags(api):
322 for tag in api.plshell.GetSliceTags(api.plauth):
323 t = tag['slice_tag_id'], Slicetag(tag)
330 def free_egre_key(slicetags):
334 if tag.tagname == 'egre_key':
335 used.add(int(tag.value))
337 for i in range(1, 256):
342 raise KeyError("No more EGRE keys available")
348 Copied from create_slice_aggregate() in sfa.plc.slices
350 def create_slice_vini_aggregate(api, hrn, nodes):
351 # Get the slice record from geni
353 registries = Registries(api)
354 registry = registries[api.hrn]
355 credential = api.getCredential()
356 records = registry.resolve(credential, hrn)
357 for record in records:
358 if record.get_type() in ['slice']:
359 slice = record.as_dict()
361 raise RecordNotFound(hrn)
363 # Make sure slice exists at plc, if it doesnt add it
364 slicename = hrn_to_pl_slicename(hrn)
365 slices = api.plshell.GetSlices(api.plauth, [slicename], ['node_ids'])
367 parts = slicename.split("_")
368 login_base = parts[0]
369 # if site doesnt exist add it
370 sites = api.plshell.GetSites(api.plauth, [login_base])
372 authority = get_authority(hrn)
373 site_records = registry.resolve(credential, authority)
376 raise RecordNotFound(authority)
377 site_record = site_records[0]
378 site = site_record.as_dict()
382 site_id = api.plshell.AddSite(api.plauth, site)
387 slice_keys = ['name', 'url', 'description']
388 for key in slice_keys:
389 if key in slice and slice[key]:
390 slice_fields[key] = slice[key]
391 api.plshell.AddSlice(api.plauth, slice_fields)
393 slice['node_ids'] = 0
397 # get the list of valid slice users from the registry and make
398 # they are added to the slice
399 researchers = record.get('researcher', [])
400 for researcher in researchers:
402 person_records = registry.resolve(credential, researcher)
403 for record in person_records:
404 if record.get_type() in ['user']:
405 person_record = record
406 if not person_record:
408 person_dict = person_record.as_dict()
409 persons = api.plshell.GetPersons(api.plauth, [person_dict['email']],
410 ['person_id', 'key_ids'])
412 # Create the person record
414 person_id=api.plshell.AddPerson(api.plauth, person_dict)
416 # The line below enables the user account on the remote aggregate
417 # soon after it is created.
418 # without this the user key is not transfered to the slice
419 # (as GetSlivers returns key of only enabled users),
420 # which prevents the user from login to the slice.
421 # We may do additional checks before enabling the user.
423 api.plshell.UpdatePerson(api.plauth, person_id, {'enabled' : True})
426 key_ids = persons[0]['key_ids']
428 api.plshell.AddPersonToSlice(api.plauth, person_dict['email'],
431 # Get this users local keys
432 keylist = api.plshell.GetKeys(api.plauth, key_ids, ['key'])
433 keys = [key['key'] for key in keylist]
435 # add keys that arent already there
436 for personkey in person_dict['keys']:
437 if personkey not in keys:
438 key = {'key_type': 'ssh', 'key': personkey}
439 api.plshell.AddPersonKey(api.plauth, person_dict['email'], key)
441 # find out where this slice is currently running
442 nodelist = api.plshell.GetNodes(api.plauth, slice['node_ids'],
444 hostnames = [node['hostname'] for node in nodelist]
446 # remove nodes not in rspec
447 deleted_nodes = list(set(hostnames).difference(nodes))
448 # add nodes from rspec
449 added_nodes = list(set(nodes).difference(hostnames))
452 print >> sys.stderr, "Slice on nodes:"
454 print >> sys.stderr, n
455 print >> sys.stderr, "Wants nodes:"
457 print >> sys.stderr, n
458 print >> sys.stderr, "Deleting nodes:"
459 for n in deleted_nodes:
460 print >> sys.stderr, n
461 print >> sys.stderr, "Adding nodes:"
462 for n in added_nodes:
463 print >> sys.stderr, n
466 api.plshell.AddSliceToNodes(api.plauth, slicename, added_nodes)
467 api.plshell.DeleteSliceFromNodes(api.plauth, slicename, deleted_nodes)
471 def get_rspec(api, hrn):
474 default.parseFile(SFA_VINI_DEFAULT_RSPEC)
477 slicename = hrn_to_pl_slicename(hrn)
478 defaultrspec = default.toDict()
479 nodedict = get_nodedict(defaultrspec)
481 # call the default sfa.plc.nodes.get_rspec() method
483 rspec = nodes.get_rspec(hrn)
485 # Grab all the PLC info we'll need at once
486 slice = get_slice(api, slicename)
488 nodes = get_nodes(api)
489 tags = get_slice_tags(api)
491 # Add the node tags from the Capacity statement to Node objects
492 for (k, v) in nodedict.iteritems():
494 if v == nodes[id].hostname:
498 for node in slice.get_nodes(nodes):
499 linktag = slice.get_tag('topo_rspec', tags, node)
501 l = eval(linktag.value)
502 for (id, realip, bw, lvip, rvip, vnet) in l:
503 endpoints.append((node.id, id, bw))
507 for (l, r, bw) in endpoints:
508 if (r, l, bw) in endpoints:
511 edict['endpoint'] = [nodes[l].tag, nodes[r].tag]
513 linkspecs.append(edict)
516 d['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] = linkspecs
517 d['Rspec']['Request'][0]['NetSpec'][0]['name'] = hrn
522 # Return canned response for now...
523 rspec = default.toxml()
528 def create_slice(api, hrn, xml):
532 ### Check the whitelist
533 ### It consists of lines of the form: <slice hrn> <bw>
535 f = open(SFA_VINI_WHITELIST)
536 for line in f.readlines():
537 (slice, maxbw) = line.split()
538 whitelist[slice] = maxbw
541 maxbps = get_tc_rate(whitelist[hrn])
543 raise PermissionError("%s not in VINI whitelist" % hrn)
545 ### Check to make sure that the slice isn't requesting more
546 ### than its maximum bandwidth.
547 linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
551 bps = get_tc_rate(bw)
553 raise GeniInvalidArgument(bw, "BW")
555 raise PermissionError(" %s requested %s but max BW is %s" % (hrn, bw, whitelist[hrn]))
557 # Check request against current allocations
560 nodes = rspec_to_nodeset(rspec)
561 create_slice_vini_aggregate(api, hrn, nodes)
563 # Add VINI-specific topology attributes to slice here
565 linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
567 slicename = hrn_to_pl_slicename(hrn)
569 # Grab all the PLC info we'll need at once
570 slice = get_slice(api, slicename)
572 nodes = get_nodes(api)
573 tags = get_slice_tags(api)
575 slice.update_tag('vini_topo', 'manual', tags)
576 slice.assign_egre_key(tags)
577 slice.turn_on_netns(tags)
578 slice.add_cap_net_admin(tags)
581 for (k, v) in get_nodedict(rspec).iteritems():
583 if v == nodes[id].hostname:
584 nodedict[k] = nodes[id]
587 n1 = nodedict[l['endpoint'][0]]
588 n2 = nodedict[l['endpoint'][1]]
593 for node in slice.get_nodes(nodes):
595 topo_str = "%s" % node.links
596 slice.update_tag('topo_rspec', topo_str, tags, node)
598 # Update slice tags in database
601 if tag.slice_id == slice.id:
602 if tag.tagname == 'topo_rspec' and not tag.updated:
612 def get_nodedict(rspec):
615 sitespecs = rspec['Rspec']['Capacity'][0]['NetSpec'][0]['SiteSpec']
617 for node in s['NodeSpec']:
618 nodedict[node['name']] = node['hostname'][0]
625 def rspec_to_nodeset(rspec):
628 nodedict = get_nodedict(rspec)
629 linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
631 for e in l['endpoint']:
632 nodes.add(nodedict[e])
641 r.parseFile(sys.argv[1])
643 create_slice(None,'plc',rspec)
645 if __name__ == "__main__":