check in missing files
[sfa.git] / util / tree.py
1 import os, sys
2 import uuid
3 sys.path.append('./sec')
4 from util import *
5 from excep import *
6 from sec import *
7 from db import *
8 from pl_to_geni import *
9
10 ROOT_AUTH = 'planetlab' #need to know the higher most node
11
12 class TreeNodeInfo:
13     def __init__(self):
14         self.name = '' # hrn
15         self.login_base = None
16         self.node_data = None
17     def serial(self):
18         dic = {'name':self.name, 'login_base':self.login_base, 'node_data':None}
19         if self.node_data:
20             dic['node_data'] = {}
21             if self.node_data.has_key('key_info'):
22                 dic['node_data']['key_info'] = self.node_data['key_info'].serial()
23             if self.node_data.has_key('db_info'):
24                 dic['node_data']['db_info'] = self.node_data['db_info'].serial()
25         return dic
26         
27 class KeyInfo:
28     def __init__(self):
29         self.folder = ''
30         self.id_file = ''
31         self.id_key_file = ''
32         self.acc_file = ''
33         self.cred_file = ''
34         self.last_update = ''
35     def serial(self):
36         return {'folder': self.folder, 'id_file': self.id_file, 'id_key_file': self.id_key_file, 'acc_file': self.acc_file, 'cred_file': self.cred_file, 'last_update': self.last_update}
37     
38 class DbInfo:
39     def __init__(self):
40         self.table_name = ''  #ex: planetlab.br_sr
41         self.db_name = '' #ex: plDB 
42         self.address = ''
43         self.port = 0
44         self.user = ''
45         self.password = ''
46     def serial(self):
47         return {'table_name':self.table_name, 'db_name':self.db_name, 'address':self.address, 'port':self.port, 'user':self.user, 'password':self.password}
48
49 class TreeNode:
50     def __init__(self):
51         self.info = None
52         self.children = []
53     def serial(self):
54         children = []
55         if len(self.children)>0:
56             for c in self.children:
57                 children.append(c.serial())
58         if self.info:
59             return {'children':children, 'info':self.info.serial()}
60         else:
61             return {'children':children, 'info':None}
62         
63 class InterfaceTree:
64     def __init__(self, type, filename, auth_addr):
65         #the tree for keeping the site names managed by this interface instance
66         self.my_tree = None
67         self.type = type
68         self.filename = filename
69         self.auth_addr = auth_addr
70         self.tree_init()
71         
72     #check a hrn if exists in the tree, return the info of it, if not found return None
73     #hrn:string
74     def tree_lookup(self, hrn) :
75         tree= self.my_tree
76         if not tree: #tree empty
77             return None
78         found = True
79         hrn_arr = geni_to_arr(hrn)
80         tmp_tree = tree
81         if tmp_tree.info.name != hrn_arr[0] :
82             found = False
83         else : 
84             cur_name = hrn_arr[0] + ''
85             for i in range(1, len(hrn_arr)):
86                 cur_name = cur_name + '.' + hrn_arr[i]
87                 child_found = False
88                 child = {}
89                 for j in range(len(tmp_tree.children)) : 
90                     if tmp_tree.children[j].info.name == cur_name :
91                         child_found = True
92                         child = tmp_tree.children[j]
93                         break
94                 if child_found == False:
95                     found = False
96                     break
97                 else:
98                     tmp_tree = child
99         if found == True:
100             return tmp_tree.info
101         else:
102             return  None
103
104     def is_leaf(self, hrn):
105         tree= self.my_tree
106         if not tree: #tree empty
107             return None
108         found = True
109         hrn_arr = geni_to_arr(hrn)
110         tmp_tree = tree
111         if tmp_tree.info.name != hrn_arr[0] :
112             found = False
113         else : 
114             cur_name = hrn_arr[0] + ''
115             for i in range(1, len(hrn_arr)):
116                 cur_name = cur_name + '.' + hrn_arr[i]
117                 child_found = False
118                 child = {}
119                 for j in range(len(tmp_tree.children)) : 
120                     if tmp_tree.children[j].info.name == cur_name :
121                         child_found = True
122                         child = tmp_tree.children[j]
123                         break
124                 if child_found == False:
125                     found = False
126                     break
127                 else:
128                     tmp_tree = child
129         if found == True:
130             return tmp_tree.children == []
131         else:
132             return  None
133     
134     #add a new branch into the tree, branch is indicated by info
135     #info: TreeNodeInfo
136     def tree_add(self, info):
137         tree= self.my_tree
138         inserted = False
139         hrn_arr = geni_to_arr(info.name)
140         tmp_tree = tree
141         
142         if hrn_arr == []:
143             raise TreeException("Wrong input hrn: "+info.name)
144         else:
145             if tree == None:
146                 if len(hrn_arr) == 1:
147                     #insert the node
148                     tree = TreeNode()
149                     tree.info = info
150                     inserted = True
151             else:
152                 cur_name = hrn_arr[0] + ''
153                 if tmp_tree.info.name == cur_name:
154                     for i in range(1, len(hrn_arr)):
155                         cur_name = cur_name + '.' + hrn_arr[i]
156                         child_found = False
157                         child = {}
158                         for j in range(len(tmp_tree.children)) : 
159                             if tmp_tree.children[j].info.name == cur_name :
160                                 child_found = True
161                                 child = tmp_tree.children[j]
162                                 break
163                         if child_found == False:
164                             if len(hrn_arr) - i == 1:
165                                 #insert the remaining of hrn
166                                 new_child = TreeNode()
167                                 new_child.info = info
168                                 tmp_tree.children.append(new_child)
169                                 inserted = True
170                             break
171                         else:
172                             tmp_tree = child
173                     if inserted == False:
174                         print "The hrn '"+info.name+"' should be at leaf position to be inserted.\n"
175             return inserted
176                 
177     #remove the branch indicated by hrn from the tree
178     #hrn: string
179     def tree_remove(self, hrn):
180         tree= self.my_tree
181         removed = False
182         hrn_arr = geni_to_arr(hrn)
183         tmp_tree = tree
184         if tmp_tree.info.name == hrn_arr[0] :
185             cur_name = hrn_arr[0] + ''
186             for i in range(1, len(hrn_arr)):
187                 cur_name = cur_name + '.' + hrn_arr[i]
188                 child_found = False
189                 child = {}
190                 for j in range(len(tmp_tree.children)) : 
191                     if tmp_tree.children[j].info.name == cur_name :
192                         child_found = True
193                         child = tmp_tree.children[j]
194                         break
195                 if child_found == False:
196                     break
197                 else:
198                     if i == len(hrn_arr)-1:
199                         tmp_tree.children.remove(child)
200                         removed = True
201                     else:
202                         tmp_tree = child
203         return removed
204                 
205     #initialize the tree with the file data
206     def tree_init(self):
207         filename = self.filename
208         type = self.type
209         try:
210             #check if dict file exists 
211             if not os.path.exists(filename+'_dict'):
212                 if not os.path.exists(filename):
213                     print 'Error: File not found.\n'
214                     raise NonExistingFile(filename)
215                 else:
216                     self.__file_to_treedict(filename)
217                     self.my_tree = self.__deserialize_tree(eval(open(filename+'_dict').read()))
218             else:
219                 self.my_tree = self.__deserialize_tree(eval(open(filename+'_dict').read()))
220             #create the tables and key folders if they do not exist already
221             self.__sync_with_db_rec(type, self.my_tree)
222         except:
223             print 'Error in initialzing the tree.\n'
224             os._exit(1)
225             
226     def save_tree(self):
227         open(self.filename+'_dict', 'w').write(str(self.my_tree.serial()))
228
229     def __sync_with_db_rec(self, type, treenode):
230         self.__sync_with_db(type, treenode)
231         for child in treenode.children:
232             self.__sync_with_db_rec(type, child)
233         
234     #if do not exist, creates the (key folder, private key and self signed cert) and the db table for the node, fills the table with the children info
235     #type: 'slice' or 'component' indicating the registry type
236     #hierarchy:hrn so far
237     #treenode: node to be synched
238     def __sync_with_db(self, type, treenode):
239         curdir = os.getcwd()
240         key_req = False
241         table_req = False
242         if not treenode.info.node_data:
243             treenode.info.node_data = {}
244             #create key folder, private key and self signed cert
245             hrn = treenode.info.name
246             dirname = type+'/'+(hrn).replace('.','/')
247             hrn_suffix = get_leaf(hrn)
248             if os.path.exists(dirname):
249                 os.system('rm -rf '+dirname)
250             os.makedirs(dirname)
251             os.chdir(dirname)
252             create_self_cert(hrn_suffix)
253             k = KeyInfo()
254             k.folder = dirname
255             k.id_file = hrn_suffix+'.cert'
256             k.id_key_file = hrn_suffix +'.pkey'
257             #for the top authority create the acc and cred files
258             if obtain_authority(hrn) == '':
259                 id_cert = crypto.load_certificate(crypto.FILETYPE_PEM, open(k.id_file).read())
260                 id_pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, open(k.id_key_file).read())
261                 uu_id = str(uuid.uuid4().int)
262                 regtype = ''
263                 if type == 'slice':
264                     regtype = 'slc'
265                 else:
266                     regtype = 'comp'
267                 rights = '(0-0)(1-0)(2-0)(3-0)(4-0)(5-0)(6-0)(7-0)(8-0)(9-0)'
268                 rights = rights + '#0:reg:'+regtype+':'+hrn
269                 k.acc_file = 'acc_file'
270                 k.cred_file = 'cred_file'
271                 acc = create_acc(id_cert, id_pkey, id_cert.get_pubkey(), hrn, uu_id)
272                 cred = create_cred(id_cert, id_pkey, id_cert.get_pubkey(), 'Registry credentials', rights)
273                 open(k.acc_file, 'w').write(crypto.dump_certificate(crypto.FILETYPE_PEM, acc))
274                 open(k.cred_file, 'w').write(crypto.dump_certificate(crypto.FILETYPE_PEM, cred))
275             treenode.info.node_data['key_info'] = k
276             os.chdir(curdir)
277             #create the table and set in the treenode data structure
278             suffix = ''
279             if type == 'slice':
280                 suffix = SR_SUFFIX
281             else:
282                 suffix = CR_SUFFIX
283             tablename = hrn.replace('.','$') + suffix
284             cnx = get_plDB_conn()
285             querystr = "CREATE TABLE "+tablename+" ( \
286                     hrn text, \
287                     type text, \
288                     uuid text, \
289                     userlist text, \
290                     rights text, \
291                     description text, \
292                     pubkey text, \
293                     wrapperURL text, \
294                     disabled text, \
295                     pointer integer);"
296
297             cnx.query('DROP TABLE IF EXISTS '+tablename) #drop the table if it exists
298             cnx.query(querystr)
299             d = DbInfo()
300             d.table_name = tablename
301             get_plDB_info(d)
302             treenode.info.node_data['db_info'] = d
303             #if the authority resides in PL as site, then fill it with the PL data
304             if treenode.children == []:
305                 populate_pl_data(hrn_suffix, tablename, type)
306                 treenode.info.login_base = hrn_suffix
307             #insert the record into parent's table
308             if obtain_authority(hrn) != '':
309                 rectype = ''
310                 regtype = ''
311                 if type == 'slice':
312                     rectype = 'SA'
313                     regtype = 'slc'
314                 else:
315                     rectype = 'MA'
316                     regtype = 'comp'
317                 uu_id = ''
318                 if treenode.children != []:
319                     uu_id = str(uuid.uuid4().int)
320                 rights = '(2-0)(4-0)(6-0)(7-0)(8-0)(9-0)(0-1)(1-1)(2-1)(3-1)(4-1)(5-1)(6-1)(7-1)(8-1)(9-1)'
321                 rights = rights + '#0:reg:'+regtype+':'+obtain_authority(hrn)+'#1:reg:'+regtype+':'+hrn
322                 id_file = treenode.info.node_data['key_info'].folder+'/'+ treenode.info.node_data['key_info'].id_file
323                 pubkey = X509.load_cert(id_file).get_pubkey().as_pem(cipher=None)
324                 wrapper = 'local'
325                 pointer = -1
326                 if treenode.children == []: #get pointer if authority resides in PL as site
327                     pointer = cnx.query("SELECT site_id FROM sites WHERE login_base='"+hrn_suffix+"'").dictresult()[0]['site_id']
328                 disabled = 'f'
329                 record = {'hrn':get_leaf(hrn), 'type':rectype, 'uuid':uu_id, 'rights':rights, 'pubkey':pubkey, 'wrapperURL':wrapper, 'pointer':pointer, 'disabled':disabled}
330                 querystr = generate_querystr('INSERT', obtain_authority(hrn).replace('.','$')+suffix, record)
331                 cnx.query(querystr)
332     
333     #gets a tree in the form of a dictionary, 
334     #                ex:  {'info':{'name':'planetlab', 'node_data':{'key_info':{...}, 'db_info':{...}}}, 'children':[x1, x2, ..]}
335     #returns the tree in the TreeNode class format
336     def __deserialize_tree(self, treenode_dict):
337         node = TreeNode()        
338         node.info = TreeNodeInfo()
339         node.info.name = treenode_dict['info']['name']
340         node.info.login_base = treenode_dict['info']['login_base']
341         if treenode_dict['info']['node_data']:
342             node.info.node_data = {}
343             if treenode_dict['info']['node_data'].has_key('key_info'):
344                 node.info.node_data['key_info'] = KeyInfo()
345                 keyinf = treenode_dict['info']['node_data']['key_info']
346                 node.info.node_data['key_info'].folder = keyinf['folder']
347                 node.info.node_data['key_info'].id_file = keyinf['id_file']
348                 node.info.node_data['key_info'].id_key_file = keyinf['id_key_file']
349                 node.info.node_data['key_info'].acc_file = keyinf['acc_file']
350                 node.info.node_data['key_info'].cred_file = keyinf['cred_file']
351                 node.info.node_data['key_info'].last_update = keyinf['last_update']
352             if treenode_dict['info']['node_data'].has_key('db_info'):
353                 node.info.node_data['db_info'] = DbInfo()
354                 dbinf = treenode_dict['info']['node_data']['db_info']
355                 node.info.node_data['db_info'].table_name = dbinf['table_name']
356                 node.info.node_data['db_info'].db_name = dbinf['db_name']
357                 node.info.node_data['db_info'].address = dbinf['address']
358                 node.info.node_data['db_info'].port = dbinf['port']
359                 node.info.node_data['db_info'].user = dbinf['user']
360                 node.info.node_data['db_info'].password = dbinf['password']
361         for child in treenode_dict['children']:
362             node.children.append(self.__deserialize_tree(child))
363         return node 
364
365     #gets a file name for tree and constructs an output file which contains the tree in dictionary format
366     #write to file:  ex:  {'info':{'name':'planetlab', 'node_data':{'key_info':{...}, 'db_info':{...}}}, 'children':[x1, x2, ..]}
367     def __file_to_treedict(self, filename):
368         lines = open(filename).readlines()
369         hierarchy = ''
370         (node, line) = self.__file_to_treedict_rec(lines, 0, hierarchy, 0)
371         open(filename+'_dict', 'w').write(str(node))
372         
373     #helper function
374     #return: (node, line)
375     def __file_to_treedict_rec(self, lines, indent, hierarchy, line):
376         node = {'info':{'name':'', 'login_base':'', 'node_data':None}, 'children':[]}
377         #count #tabs
378         j = 0
379         while lines[line][j] == '\t':
380             j = j+1
381         if indent != j:
382             print 'Error in file to dictionary conversion.\n'
383             return None
384         name = lines[line].split(' ')[0]
385         name = name[j:len(name)]
386         if hierarchy == '':
387             node['info']['name'] = name
388         else:
389             node['info']['name'] = hierarchy + '.' + name
390         node['info']['login_base'] = name
391         #next indent count
392         j = 0
393         if line+1 != len(lines):        
394             while lines[line+1][j] == '\t':
395                 j = j+1
396         if j > indent+1:
397             print 'Error in file to dictionary conversion.\n'
398             return None
399         indent2 = j
400         line = line+1
401         while indent2 > indent: 
402             (child, line) = self.__file_to_treedict_rec(lines, indent2, node['info']['name'], line)
403             node['children'].append(child)
404             j = 0
405             if line != len(lines):    
406                 while lines[line][j] == '\t':
407                     j = j+1
408             indent2 = j
409         return (node, line)
410
411
412     #determines the key info of a given hrn within the tree, performs calls back to 'server' to obtain updated certs
413     #if the hrn does not exist in the tree hierarchy None is returned
414     #server: the server instance is passed for calls to getCredential/getAccounting if necessary
415     #type is credential or accounting; one of them at a time is determined by the function
416     #returns an array containing the self-signed certificate, private key, and acc or cred chain in string
417     def determine_keyinfo(self, hrn, server, type):
418         tree = self
419         reg_type = self.type
420         info = tree.tree_lookup(hrn)
421         if info == None:
422             return None
423         else:
424             keyinfo = info.node_data['key_info']
425             folder = keyinfo.folder
426             id_file = folder+'/'+keyinfo.id_file
427             id_key_file = folder+'/'+keyinfo.id_key_file
428             #check the id file
429             if not os.path.exists(folder) or not os.path.exists(id_file):
430                 print 'Id file for '+hrn+' does not exist.\n'
431                 return None
432             else:
433                 #check acc or cred
434                 acc_str = None
435                 cred_str = None
436                 renew_res = renew_cert(type, folder, reg_type, hrn, server, tree, tree.auth_addr, server.sec)
437                 if renew_res == None:
438                     return None
439                 if type == 'accounting':
440                     keyinfo.acc_file = 'acc_file'
441                     acc_str = open(keyinfo.folder+'/'+keyinfo.acc_file).read()
442                 else:
443                     keyinfo.cred_file = 'cred_file'
444                     cred_str = open(keyinfo.folder+'/'+keyinfo.cred_file).read()
445                 id = crypto.load_certificate(crypto.FILETYPE_PEM, open(id_file).read())
446                 id_key = crypto.load_privatekey(crypto.FILETYPE_PEM, open(id_key_file).read())
447                 return [id, id_key, acc_str, cred_str]