(no commit message)
[sfa.git] / sfa / rspecs / aggregates / rspec_manager_max.py
1 #!/usr/bin/python
2
3 from sfa.util.rspec import Rspec
4 import sys
5 import pdb
6 from sfa.util.misc import *
7 from sfa.util.rspec import *
8 from sfa.util.specdict import *
9 from sfa.util.faults import *
10 from sfa.util.storage import *
11 from sfa.util.policy import Policy
12 from sfa.util.debug import log
13 from sfa.server.aggregate import Aggregates
14 from sfa.server.registry import Registries
15
16 SFA_MAX_CONF_FILE = '/etc/sfa/max_allocations'
17 SFA_MAX_DEFAULT_RSPEC = '/etc/sfa/max_physical.xml'
18
19 # Topology 
20
21 topology = {'pl23':('planetlab2.dragon.maxgigapop.net','planetlab3.dragon.maxgigapop.net'),
22             'pl24':('planetlab2.dragon.maxgigapop.net','planetlab4.dragon.maxgigapop.net'),
23             'pl25':('planetlab2.dragon.maxgigapop.net','planetlab5.dragon.maxgigapop.net'),
24             'pl34':('planetlab3.dragon.maxgigapop.net','planetlab4.dragon.maxgigapop.net'),
25             'pl35':('planetlab3.dragon.maxgigapop.net','planetlab5.dragon.maxgigapop.net'),
26             'pl45':('planetlab4.dragon.maxgigapop.net','planetlab5.dragon.maxgigapop.net')
27             }
28
29 def link_endpoints(links):
30     nodes=[]
31     for l in links:
32         nodes.extend(topology[l])
33     return nodes
34
35 def lock_state_file():
36     # Noop for demo
37     return True
38
39 def unlock_state_file():
40     return True
41     # Noop for demo
42
43 def read_alloc_dict():
44     alloc_dict={}
45     rows = open(SFA_MAX_CONF_FILE).read().split('\n')
46     for r in rows:
47         columns = r.split(' ')
48         if (len(columns)>2):
49             hrn = columns[0]
50             allocs = columns[1].split(',')
51             alloc_dict[hrn]=allocs
52     return alloc_dict
53
54 def commit_alloc_dict(d):
55     f = open(SFA_MAX_CONF_FILE, 'w')
56     for hrn in d.keys():
57         columns = d[hrn]
58         row = hrn+' '+','.join(columns)+'\n'
59         f.write(row)
60     f.close()
61
62 def collapse_alloc_dict(d):
63     ret = []
64     for k in d.keys():
65         ret.extend(d[k])
66     return ret
67
68
69 def alloc_links(api, links_to_add, links_to_drop, foo):
70     pdb.set_trace()
71     for l in links_to_add:
72         (node1,ip1,node2,ip2) = l
73         api.plshell.AddSliceTag(api.plauth, [slicename], ['node_ids'])
74     return True
75
76 def alloc_nodes(api,hrn, requested_links):
77     
78     requested_nodes = link_endpoints(requested_links)
79
80     create_slice_max_aggregate(api, hrn, requested_nodes)
81
82 # Taken from slices.py
83
84 def create_slice_max_aggregate(api, hrn, nodes):
85     # Get the slice record from geni
86     slice = {}
87     registries = Registries(api)
88     registry = registries[api.hrn]
89     credential = api.getCredential()
90     records = registry.resolve(credential, hrn)
91     for record in records:
92         if record.get_type() in ['slice']:
93             slice = record.as_dict()
94     if not slice:
95         raise RecordNotFound(hrn)   
96
97     # Make sure slice exists at plc, if it doesnt add it
98     slicename = hrn_to_pl_slicename(hrn)
99     slices = api.plshell.GetSlices(api.plauth, [slicename], ['node_ids'])
100     if not slices:
101         parts = slicename.split("_")
102         login_base = parts[0]
103         # if site doesnt exist add it
104         sites = api.plshell.GetSites(api.plauth, [login_base])
105         if not sites:
106             authority = get_authority(hrn)
107             site_records = registry.resolve(credential, authority)
108             site_record = {}
109             if not site_records:
110                 raise RecordNotFound(authority)
111             site_record = site_records[0]
112             site = site_record.as_dict()
113                 
114             # add the site
115             site.pop('site_id')
116             site_id = api.plshell.AddSite(api.plauth, site)
117         else:
118             site = sites[0]
119             
120         slice_fields = {}
121         slice_keys = ['name', 'url', 'description']
122         for key in slice_keys:
123             if key in slice and slice[key]:
124                 slice_fields[key] = slice[key]  
125         api.plshell.AddSlice(api.plauth, slice_fields)
126         slice = slice_fields
127         slice['node_ids'] = 0
128     else:
129         slice = slices[0]    
130
131     # get the list of valid slice users from the registry and make 
132     # they are added to the slice 
133     researchers = record.get('researcher', [])
134     for researcher in researchers:
135         person_record = {}
136         person_records = registry.resolve(credential, researcher)
137         for record in person_records:
138             if record.get_type() in ['user']:
139                 person_record = record
140         if not person_record:
141             pass
142         person_dict = person_record.as_dict()
143         persons = api.plshell.GetPersons(api.plauth, [person_dict['email']],
144                                          ['person_id', 'key_ids'])
145
146         # Create the person record 
147         if not persons:
148             person_id=api.plshell.AddPerson(api.plauth, person_dict)
149
150             # The line below enables the user account on the remote aggregate
151             # soon after it is created.
152             # without this the user key is not transfered to the slice
153             # (as GetSlivers returns key of only enabled users),
154             # which prevents the user from login to the slice.
155             # We may do additional checks before enabling the user.
156
157             api.plshell.UpdatePerson(api.plauth, person_id, {'enabled' : True})
158             key_ids = []
159         else:
160             key_ids = persons[0]['key_ids']
161
162         api.plshell.AddPersonToSlice(api.plauth, person_dict['email'],
163                                      slicename)        
164
165         # Get this users local keys
166         keylist = api.plshell.GetKeys(api.plauth, key_ids, ['key'])
167         keys = [key['key'] for key in keylist]
168
169         # add keys that arent already there 
170         for personkey in person_dict['keys']:
171             if personkey not in keys:
172                 key = {'key_type': 'ssh', 'key': personkey}
173                 api.plshell.AddPersonKey(api.plauth, person_dict['email'], key)
174
175     # find out where this slice is currently running
176     nodelist = api.plshell.GetNodes(api.plauth, slice['node_ids'],
177                                     ['hostname'])
178     hostnames = [node['hostname'] for node in nodelist]
179
180     # remove nodes not in rspec
181     deleted_nodes = list(set(hostnames).difference(nodes))
182     # add nodes from rspec
183     added_nodes = list(set(nodes).difference(hostnames))
184
185     api.plshell.AddSliceToNodes(api.plauth, slicename, added_nodes) 
186     api.plshell.DeleteSliceFromNodes(api.plauth, slicename, deleted_nodes)
187
188     return 1
189
190
191 def get_rspec(api, hrn):
192     # Eg. config line:
193     # plc.princeton.sapan vlan23,vlan45
194
195     allocations = read_alloc_dict()
196     if (hrn):
197         ret_rspec = rspec(allocations[hrn])
198     else:
199         ret_rspec = open(SFA_MAX_DEFAULT_RSPEC).read()
200
201     return (ret_rspec)
202
203
204 def create_slice(api, hrn, rspec_xml):
205     # Check if everything in rspec is either allocated by hrn
206     # or not allocated at all.
207
208     r = Rspec()
209     r.parseString(rspec_xml)
210     rspec = r.toDict()
211
212     lock_state_file()
213
214     allocations = read_alloc_dict()
215     requested_allocations = rspec_to_allocations (rspec)
216     current_allocations = collapse_alloc_dict(allocations)
217     try:
218         current_hrn_allocations=allocations[hrn]
219     except KeyError:
220         current_hrn_allocations=[]
221
222     # Check request against current allocations
223     for a in requested_allocations:
224         if (a not in current_hrn_allocations and a in current_allocations):
225             return False
226     # Request OK
227
228     # Allocations to delete
229     allocations_to_delete = []
230     for a in current_hrn_allocations:
231         if (a not in requested_allocations):
232             allocations_to_delete.extend([a])
233
234     # Ok, let's do our thing
235     alloc_nodes(api, hrn, requested_allocations)
236     alloc_links(api, hrn, requested_allocations, allocations_to_delete)
237     allocations[hrn] = requested_allocations
238     commit_alloc_dict(allocations)
239
240     unlock_state_file()
241
242     return True
243
244 def rspec_to_allocations(rspec):
245     links = []
246     try:
247         linkspecs = rspec['rspec']['request'][0]['netspec'][0]['linkspec']
248         for l in linkspecs:
249             links.extend([l['name'].replace('tns:','')])
250         
251     except KeyError:
252         # Bad Rspec
253         pass
254     return links
255
256 def main():
257     r = Rspec()
258     rspec_xml = open(sys.argv[1]).read()
259     r.parseString(rspec_xml)
260     rspec = r.toDict()
261     create_slice(None,'plc',rspec)
262     
263 if __name__ == "__main__":
264     main()