2035564b1f7533edf3430b847318fa34721a3677
[sfa.git] / geni / plc / sfa-import-plc.py
1 #!/usr/bin/python
2 #
3 ### $Id$
4 ### $URL$
5 #
6 ##
7 # Import PLC records into the Geni database. It is indended that this tool be
8 # run once to create Geni records that reflect the current state of the
9 # planetlab database.
10 #
11 # The import tool assumes that the existing PLC hierarchy should all be part
12 # of "planetlab.us" (see the root_auth and level1_auth variables below).
13 #
14 # Public keys are extracted from the users' SSH keys automatically and used to
15 # create GIDs. This is relatively experimental as a custom tool had to be
16 # written to perform conversion from SSH to OpenSSL format. It only supports
17 # RSA keys at this time, not DSA keys.
18 ##
19
20 import getopt
21 import sys
22 import tempfile
23
24 from geni.trust.certificate import *
25 from geni.trust.trustedroot import *
26 from geni.util.hierarchy import *
27 from geni.util.record import *
28 from geni.util.genitable import *
29 from geni.util.misc import *
30 from geni.util.config import *
31
32 # get PL account settings from config module
33 pl_auth = get_pl_auth()
34
35 def connect_shell():
36     global pl_auth, shell
37
38     # get PL account settings from config module
39     pl_auth = get_pl_auth()
40
41     # connect to planetlab
42     if "Url" in pl_auth:
43         from geni.util import remoteshell
44         shell = remoteshell.RemoteShell()
45     else:
46         import PLC.Shell
47         shell = PLC.Shell.Shell(globals = globals())
48
49     return shell
50
51 # connect to planetlab
52 shell = connect_shell()
53
54 ##
55 # Two authorities are specified: the root authority and the level1 authority.
56
57 #root_auth = "plc"
58 #level1_auth = None
59
60 #root_auth = "planetlab"
61 #level1_auth = "planetlab.us"
62 config = Config()
63
64 root_auth = config.GENI_REGISTRY_ROOT_AUTH
65 level1_auth = config.GENI_REGISTRY_LEVEL1_AUTH
66 if not level1_auth or level1_auth in ['']:
67     level1_auth = None
68
69 def un_unicode(str):
70    if isinstance(str, unicode):
71        return str.encode("ascii", "ignore")
72    else:
73        return str
74
75 def cleanup_string(str):
76     # pgsql has a fit with strings that have high ascii in them, so filter it
77     # out when generating the hrns.
78     tmp = ""
79     for c in str:
80         if ord(c) < 128:
81             tmp = tmp + c
82     str = tmp
83
84     str = un_unicode(str)
85     str = str.replace(" ", "_")
86     str = str.replace(".", "_")
87     str = str.replace("(", "_")
88     str = str.replace("'", "_")
89     str = str.replace(")", "_")
90     str = str.replace('"', "_")
91     return str
92
93 def process_options():
94    global hrn
95
96    (options, args) = getopt.getopt(sys.argv[1:], '', [])
97    for opt in options:
98        name = opt[0]
99        val = opt[1]
100
101 def get_auth_table(auth_name):
102     AuthHierarchy = Hierarchy()
103     auth_info = AuthHierarchy.get_auth_info(auth_name)
104
105     table = GeniTable(hrn=auth_name,
106                       cninfo=auth_info.get_dbinfo())
107
108     # if the table doesn't exist, then it means we haven't put any records
109     # into this authority yet.
110
111     if not table.exists():
112         report.trace("Import: creating table for authority " + auth_name)
113         table.create()
114
115     return table
116
117 def person_to_hrn(parent_hrn, person):
118     # the old way - Lastname_Firstname
119     #personname = person['last_name'] + "_" + person['first_name']
120
121     # the new way - use email address up to the "@" 
122     personname = person['email'].split("@")[0]
123
124     personname = cleanup_string(personname)
125
126     hrn = parent_hrn + "." + personname
127     return hrn
128
129 def import_person(parent_hrn, person):
130     AuthHierarchy = Hierarchy()
131     hrn = person_to_hrn(parent_hrn, person)
132
133     # ASN.1 will have problems with hrn's longer than 64 characters
134     if len(hrn) > 64:
135         hrn = hrn[:64]
136
137     report.trace("Import: importing person " + hrn)
138
139     table = get_auth_table(parent_hrn)
140
141     key_ids = []
142     if 'key_ids' in person:    
143         key_ids = person["key_ids"]
144         
145         # get the user's private key from the SSH keys they have uploaded
146         # to planetlab
147         keys = shell.GetKeys(pl_auth, key_ids)
148         key = keys[0]['key']
149         pkey =convert_public_key(key)
150     else:
151         # the user has no keys
152         report.trace("   person " + hrn + " does not have a PL public key")
153
154         # if a key is unavailable, then we still need to put something in the
155         # user's GID. So make one up.
156         pkey = Keypair(create=True)
157
158     # create the gid 
159     person_gid = AuthHierarchy.create_gid(hrn, create_uuid(), pkey)
160     person_record = table.resolve("user", hrn)
161     if not person_record:
162         report.trace("  inserting user record for " + hrn)
163         person_record = GeniRecord(name=hrn, gid=person_gid, type="user", pointer=person['person_id'])
164         table.insert(person_record)
165     else:
166         report.trace("  updating user record for " + hrn)
167         person_record = GeniRecord(name=hrn, gid=person_gid, type="user", pointer=person['person_id'])
168         table.update(person_record)
169             
170 def import_slice(parent_hrn, slice):
171     AuthHierarchy = Hierarchy()
172     slicename = slice['name'].split("_",1)[-1]
173     slicename = cleanup_string(slicename)
174
175     if not slicename:
176         report.error("Import_Slice: failed to parse slice name " + slice['name'])
177         return
178
179     hrn = parent_hrn + "." + slicename
180     report.trace("Import: importing slice " + hrn)
181
182     table = get_auth_table(parent_hrn)
183
184     slice_record = table.resolve("slice", hrn)
185     if not slice_record:
186         pkey = Keypair(create=True)
187         slice_gid = AuthHierarchy.create_gid(hrn, create_uuid(), pkey)
188         slice_record = GeniRecord(name=hrn, gid=slice_gid, type="slice", pointer=slice['slice_id'])
189         report.trace("  inserting slice record for " + hrn)
190         table.insert(slice_record)
191
192 def import_node(parent_hrn, node):
193     AuthHierarchy = Hierarchy()
194     nodename = node['hostname'].split(".")[0]
195     nodename = cleanup_string(nodename)
196
197     if not nodename:
198         report.error("Import_node: failed to parse node name " + node['hostname'])
199         return
200
201     hrn = parent_hrn + "." + nodename
202
203     # ASN.1 will have problems with hrn's longer than 64 characters
204     if len(hrn) > 64:
205         hrn = hrn[:64]
206
207     report.trace("Import: importing node " + hrn)
208
209     table = get_auth_table(parent_hrn)
210
211     node_record = table.resolve("node", hrn)
212     if not node_record:
213         pkey = Keypair(create=True)
214         node_gid = AuthHierarchy.create_gid(hrn, create_uuid(), pkey)
215         node_record = GeniRecord(name=hrn, gid=node_gid, type="node", pointer=node['node_id'])
216         report.trace("  inserting node record for " + hrn)
217         table.insert(node_record)
218
219 def import_site(parent_hrn, site):
220     AuthHierarchy = Hierarchy()
221     sitename = site['login_base']
222     sitename = cleanup_string(sitename)
223     
224     hrn = parent_hrn + "." + sitename
225     
226     # Hardcode 'internet2' into the hrn for sites hosting 
227     # internet2 nodes. This is a special operation for some vini
228     # sites only
229     if ".vini" in parent_hrn and parent_hrn.endswith('vini'):
230         if sitename.startswith("ii"): 
231             sitename = sitename.replace("ii", "")
232             hrn = ".".join([parent_hrn, "internet2", sitename]) 
233         elif sitename.startswith("nlr"): 
234             hrn = ".".join([parent_hrn, "internet2", sitename]) 
235             sitename = sitename.replace("nlr", "")
236          
237     report.trace("Import_Site: importing site " + hrn)
238
239     # create the authority
240     if not AuthHierarchy.auth_exists(hrn):
241         AuthHierarchy.create_auth(hrn)
242
243     auth_info = AuthHierarchy.get_auth_info(hrn)
244
245     table = get_auth_table(parent_hrn)
246
247     auth_record = table.resolve("authority", hrn)
248     if not auth_record:
249         auth_record = GeniRecord(name=hrn, gid=auth_info.get_gid_object(), type="authority", pointer=site['site_id'])
250         report.trace("  inserting authority record for " + hrn)
251         table.insert(auth_record)
252
253     if 'person_ids' in site: 
254         for person_id in site['person_ids']:
255             persons = shell.GetPersons(pl_auth, [person_id])
256             if persons:
257                 try: 
258                     import_person(hrn, persons[0])
259                 except:
260                     report.trace("Failed to import: %s" % persons[0])
261     if 'slice_ids' in site:
262         for slice_id in site['slice_ids']:
263             slices = shell.GetSlices(pl_auth, [slice_id])
264             if slices:
265                 try:
266                     import_slice(hrn, slices[0])
267                 except:
268                     report.trace("Failed to import: %s" % slices[0])
269     if 'node_ids' in site:
270         for node_id in site['node_ids']:
271             nodes = shell.GetNodes(pl_auth, [node_id])
272             if nodes:
273                 try:
274                     import_node(hrn, nodes[0])
275                 except:
276                     report.trace("Failed to import: %s" % nodes[0])
277
278 def create_top_level_auth_records(hrn):
279     parent_hrn = get_authority(hrn)
280     print hrn, ":", parent_hrn
281     if not parent_hrn:
282         parent_hrn = hrn    
283     auth_info = AuthHierarchy.get_auth_info(parent_hrn)
284     table = get_auth_table(parent_hrn)
285
286     auth_record = table.resolve("authority", hrn)
287     if not auth_record:
288         auth_record = GeniRecord(name=hrn, gid=auth_info.get_gid_object(), type="authority", pointer=-1)
289         report.trace("  inserting authority record for " + hrn)
290         table.insert(auth_record)
291
292 def main():
293     global AuthHierarchy
294     global TrustedRoots
295
296     process_options()
297
298     AuthHierarchy = Hierarchy()
299     TrustedRoots = TrustedRootList()
300
301     print "Import: creating top level authorities"
302
303     if not AuthHierarchy.auth_exists(root_auth):
304         AuthHierarchy.create_auth(root_auth)
305
306     create_top_level_auth_records(root_auth)
307     if level1_auth:
308         if not AuthHierarchy.auth_exists(level1_auth):
309             AuthHierarchy.create_auth(level1_auth)
310         create_top_level_auth_records(level1_auth)
311         import_auth = level1_auth
312     else:
313         import_auth = root_auth
314
315     print "Import: adding", root_auth, "to trusted list"
316     root = AuthHierarchy.get_auth_info(root_auth)
317     TrustedRoots.add_gid(root.get_gid_object())
318
319     connect_shell()
320
321     sites = shell.GetSites(pl_auth, {'peer_id': None})
322     # create a fake internet2 site first
323     i2site = {'name': 'Internet2', 'abbreviated_name': 'I2',
324                     'login_base': 'internet2', 'site_id': -1}
325     import_site(import_auth, i2site)
326     
327     for site in sites:
328         import_site(import_auth, site)
329
330 if __name__ == "__main__":
331     main()