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 *
9 SFA_VINI_DEFAULT_RSPEC = '/etc/sfa/vini.rspec'
10 SFA_VINI_WHITELIST = '/etc/sfa/vini.whitelist'
13 def __init__(self, node):
14 self.id = node['node_id']
15 self.hostname = node['hostname']
16 self.shortname = self.hostname.replace('.vini-veritas.net', '')
17 self.site_id = node['site_id']
18 self.ipaddr = socket.gethostbyname(self.hostname)
21 def get_link_id(self, remote):
22 if self.id < remote.id:
23 link = (self.id<<7) + remote.id
25 link = (remote.id<<7) + self.id
28 def get_iface_id(self, remote):
29 if self.id < remote.id:
35 def get_virt_ip(self, remote):
36 link = self.get_link_id(remote)
37 iface = self.get_iface_id(remote)
39 second = ((link & 0x3f)<<2) + iface
40 return "192.168.%d.%d" % (first, second)
42 def get_virt_net(self, remote):
43 link = self.get_link_id(remote)
45 second = (link & 0x3f)<<2
46 return "192.168.%d.%d/30" % (first, second)
48 def get_site(self, sites):
49 return sites[self.site_id]
51 def adjacent_nodes(self, sites, nodes, node_ids):
52 mysite = self.get_site(sites)
53 adj_ids = mysite.adj_node_ids.intersection(node_ids)
56 adj_nodes.append(nodes[id])
62 def add_link(self, remote, bw):
63 my_ip = self.get_virt_ip(remote)
64 remote_ip = remote.get_virt_ip(self)
65 net = self.get_virt_net(remote)
66 link = remote.id, remote.ipaddr, bw, my_ip, remote_ip, net
67 self.links.append(link)
71 def __init__(self, site):
72 self.id = site['site_id']
73 self.node_ids = site['node_ids']
74 self.adj_site_ids = set()
75 self.adj_node_ids = set()
77 def get_sitenodes(self, nodes):
79 for i in self.node_ids:
83 def add_adjacency(self, site):
84 self.adj_site_ids.add(site.id)
85 for n in site.node_ids:
86 self.adj_node_ids.add(n)
90 def __init__(self, slice):
91 self.id = slice['slice_id']
92 self.name = slice['name']
93 self.node_ids = set(slice['node_ids'])
94 self.slice_tag_ids = slice['slice_tag_ids']
96 def get_tag(self, tagname, slicetags, node = None):
97 for i in self.slice_tag_ids:
99 if tag.tagname == tagname:
100 if (not node) or (node.id == tag.node_id):
105 def get_nodes(self, nodes):
107 for id in self.node_ids:
112 # Add a new slice tag
113 def add_tag(self, tagname, value, slicetags, node = None):
114 record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value}
116 record['node_id'] = node.id
118 record['node_id'] = None
119 tag = Slicetag(record)
120 slicetags[tag.id] = tag
121 self.slice_tag_ids.append(tag.id)
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:
135 tag = self.add_tag(tagname, value, slicetags, node)
138 def assign_egre_key(self, slicetags):
139 if not self.get_tag('egre_key', slicetags):
141 key = free_egre_key(slicetags)
142 self.update_tag('egre_key', key, slicetags)
144 # Should handle this case...
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)
154 def turn_off_netns(self, slicetags):
155 tag = self.get_tag('netns', slicetags)
156 if tag and (tag.value != '0'):
160 def add_cap_net_admin(self, slicetags):
161 tag = self.get_tag('capabilities', slicetags)
163 caps = tag.value.split(',')
165 if cap == "CAP_NET_ADMIN":
168 newcaps = "CAP_NET_ADMIN," + tag.value
169 self.update_tag('capabilities', newcaps, slicetags)
171 self.add_tag('capabilities', 'CAP_NET_ADMIN', slicetags)
174 def remove_cap_net_admin(self, slicetags):
175 tag = self.get_tag('capabilities', slicetags)
177 caps = tag.value.split(',')
180 if cap != "CAP_NET_ADMIN":
183 value = ','.join(newcaps)
184 self.update_tag('capabilities', value, slicetags)
189 # Update the vsys/setup-link and vsys/setup-nat slice tags.
190 def add_vsys_tags(self, slicetags):
192 for i in self.slice_tag_ids:
194 if tag.tagname == 'vsys':
195 if tag.value == 'setup-link':
197 elif tag.value == 'setup-nat':
200 self.add_tag('vsys', 'setup-link', slicetags)
202 self.add_tag('vsys', 'setup-nat', slicetags)
208 def __init__(self, tag):
209 self.id = tag['slice_tag_id']
211 # Make one up for the time being...
212 self.id = Slicetag.newid
214 self.slice_id = tag['slice_id']
215 self.tagname = tag['tagname']
216 self.value = tag['value']
217 self.node_id = tag['node_id']
222 # Mark a tag as deleted
227 def write(self, api):
230 api.plshell.UpdateSliceTag(api.plauth, self.id, self.value)
232 api.plshell.AddSliceTag(api.plauth, self.slice_id,
233 self.tagname, self.value, self.node_id)
234 elif self.deleted and int(self.id) > 0:
235 api.plshell.DeleteSliceTag(api.plauth, self.id)
239 Create a dictionary of site objects keyed by site ID
243 for site in GetSites():
244 t = site['site_id'], Site(site)
250 Create a dictionary of node objects keyed by node ID
254 for node in api.plshell.GetNodes(api.plauth):
255 t = node['node_id'], Node(node)
260 Create a dictionary of slice objects keyed by slice ID
262 def get_slice(api, slicename):
263 slice = api.plshell.GetSlices(api.plauth, [slicename])
265 return Slice(slice[0])
270 Create a dictionary of slicetag objects keyed by slice tag ID
272 def get_slice_tags(api):
274 for tag in api.plshell.GetSliceTags(api.plauth):
275 t = tag['slice_tag_id'], Slicetag(tag)
282 def free_egre_key(slicetags):
286 if tag.tagname == 'egre_key':
287 used.add(int(tag.value))
289 for i in range(1, 256):
294 raise KeyError("No more EGRE keys available")
300 Copied from create_slice_aggregate() in sfa.plc.slices
302 def create_slice_vini_aggregate(api, hrn, nodes):
303 # Get the slice record from geni
305 registries = Registries(api)
306 registry = registries[api.hrn]
307 credential = api.getCredential()
308 records = registry.resolve(credential, hrn)
309 for record in records:
310 if record.get_type() in ['slice']:
311 slice = record.as_dict()
313 raise RecordNotFound(hrn)
315 # Make sure slice exists at plc, if it doesnt add it
316 slicename = hrn_to_pl_slicename(hrn)
317 slices = api.plshell.GetSlices(api.plauth, [slicename], ['node_ids'])
319 parts = slicename.split("_")
320 login_base = parts[0]
321 # if site doesnt exist add it
322 sites = api.plshell.GetSites(api.plauth, [login_base])
324 authority = get_authority(hrn)
325 site_records = registry.resolve(credential, authority)
328 raise RecordNotFound(authority)
329 site_record = site_records[0]
330 site = site_record.as_dict()
334 site_id = api.plshell.AddSite(api.plauth, site)
339 slice_keys = ['name', 'url', 'description']
340 for key in slice_keys:
341 if key in slice and slice[key]:
342 slice_fields[key] = slice[key]
343 api.plshell.AddSlice(api.plauth, slice_fields)
345 slice['node_ids'] = 0
349 # get the list of valid slice users from the registry and make
350 # they are added to the slice
351 researchers = record.get('researcher', [])
352 for researcher in researchers:
354 person_records = registry.resolve(credential, researcher)
355 for record in person_records:
356 if record.get_type() in ['user']:
357 person_record = record
358 if not person_record:
360 person_dict = person_record.as_dict()
361 persons = api.plshell.GetPersons(api.plauth, [person_dict['email']],
362 ['person_id', 'key_ids'])
364 # Create the person record
366 person_id=api.plshell.AddPerson(api.plauth, person_dict)
368 # The line below enables the user account on the remote aggregate
369 # soon after it is created.
370 # without this the user key is not transfered to the slice
371 # (as GetSlivers returns key of only enabled users),
372 # which prevents the user from login to the slice.
373 # We may do additional checks before enabling the user.
375 api.plshell.UpdatePerson(api.plauth, person_id, {'enabled' : True})
378 key_ids = persons[0]['key_ids']
380 api.plshell.AddPersonToSlice(api.plauth, person_dict['email'],
383 # Get this users local keys
384 keylist = api.plshell.GetKeys(api.plauth, key_ids, ['key'])
385 keys = [key['key'] for key in keylist]
387 # add keys that arent already there
388 for personkey in person_dict['keys']:
389 if personkey not in keys:
390 key = {'key_type': 'ssh', 'key': personkey}
391 api.plshell.AddPersonKey(api.plauth, person_dict['email'], key)
393 # find out where this slice is currently running
394 nodelist = api.plshell.GetNodes(api.plauth, slice['node_ids'],
396 hostnames = [node['hostname'] for node in nodelist]
398 # remove nodes not in rspec
399 deleted_nodes = list(set(hostnames).difference(nodes))
400 # add nodes from rspec
401 added_nodes = list(set(nodes).difference(hostnames))
404 print >> sys.stderr, "Slice on nodes:"
406 print >> sys.stderr, n
407 print >> sys.stderr, "Wants nodes:"
409 print >> sys.stderr, n
410 print >> sys.stderr, "Deleting nodes:"
411 for n in deleted_nodes:
412 print >> sys.stderr, n
413 print >> sys.stderr, "Adding nodes:"
414 for n in added_nodes:
415 print >> sys.stderr, n
418 api.plshell.AddSliceToNodes(api.plauth, slicename, added_nodes)
419 api.plshell.DeleteSliceFromNodes(api.plauth, slicename, deleted_nodes)
423 def get_rspec(api, hrn):
426 default.parseFile(SFA_VINI_DEFAULT_RSPEC)
429 slicename = hrn_to_pl_slicename(hrn)
430 defaultrspec = default.toDict()
431 nodedict = get_nodedict(defaultrspec)
433 # call the default sfa.plc.nodes.get_rspec() method
435 rspec = nodes.get_rspec(hrn)
437 # Grab all the PLC info we'll need at once
438 slice = get_slice(api, slicename)
440 nodes = get_nodes(api)
441 tags = get_slice_tags(api)
443 # Add the node tags from the Capacity statement to Node objects
444 for (k, v) in nodedict.iteritems():
446 if v == nodes[id].hostname:
450 for node in slice.get_nodes(nodes):
451 linktag = slice.get_tag('topo_rspec', tags, node)
453 l = eval(linktag.value)
454 for (id, realip, bw, lvip, rvip, vnet) in l:
455 endpoints.append((node.id, id, bw))
459 for (l, r, bw) in endpoints:
460 if (r, l, bw) in endpoints:
463 edict['endpoint'] = [nodes[l].tag, nodes[r].tag]
465 linkspecs.append(edict)
468 d['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] = linkspecs
469 d['Rspec']['Request'][0]['NetSpec'][0]['name'] = hrn
474 # Return canned response for now...
475 rspec = default.toxml()
480 def create_slice(api, hrn, xml):
484 ### Check the whitelist
485 ### It consists of lines of the form: <slice hrn> <bw>
487 f = open(SFA_VINI_WHITELIST)
488 for line in f.readlines():
489 (slice, bw) = line.split()
490 whitelist[slice] = bw
491 if not hrn in whitelist:
492 raise PermissionError("%s not in VINI whitelist" % hrn)
494 # Check request against current allocations
497 nodes = rspec_to_nodeset(rspec)
498 create_slice_vini_aggregate(api, hrn, nodes)
500 # Add VINI-specific topology attributes to slice here
502 linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
504 slicename = hrn_to_pl_slicename(hrn)
506 # Grab all the PLC info we'll need at once
507 slice = get_slice(api, slicename)
509 nodes = get_nodes(api)
510 tags = get_slice_tags(api)
512 slice.update_tag('vini_topo', 'manual', tags)
513 slice.assign_egre_key(tags)
514 slice.turn_on_netns(tags)
515 slice.add_cap_net_admin(tags)
518 for (k, v) in get_nodedict(rspec).iteritems():
520 if v == nodes[id].hostname:
521 nodedict[k] = nodes[id]
524 n1 = nodedict[l['endpoint'][0]]
525 n2 = nodedict[l['endpoint'][1]]
530 for node in slice.get_nodes(nodes):
532 topo_str = "%s" % node.links
533 slice.update_tag('topo_rspec', topo_str, tags, node)
535 # Update slice tags in database
538 if tag.slice_id == slice.id:
539 if tag.tagname == 'topo_rspec' and not tag.updated:
549 def get_nodedict(rspec):
552 sitespecs = rspec['Rspec']['Capacity'][0]['NetSpec'][0]['SiteSpec']
554 for node in s['NodeSpec']:
555 nodedict[node['name']] = node['hostname'][0]
562 def rspec_to_nodeset(rspec):
565 nodedict = get_nodedict(rspec)
566 linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
568 for e in l['endpoint']:
569 nodes.add(nodedict[e])
578 r.parseFile(sys.argv[1])
580 create_slice(None,'plc',rspec)
582 if __name__ == "__main__":