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