-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathrhizoma.mix
580 lines (451 loc) · 15.7 KB
/
rhizoma.mix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
"""
# JAC: OBSOLETE!!! NOT PORTED / TESTED
<Author>
<Purpose>
I do all kinds of remote interactions with GENI. I can deploy
scripts, acquire vessels, release vessels, run code, stop code,
and check logs and statuses.
Specificatlly, I may either:
case(command):
status: output the status of your vessels
start: upload and run your daemon to those vessels
stop: kill your daemon on all vessels
log: output the log of all your vessels
acquire: go to GENI and get more resources
release: get rid of resources
Please have ready your:
* keyname.public/.private key pair
* daemon script
I may prompt for your GENI password. Don't worry,
you can trust me.
<Usage>
python rhizoma.py keyname command [args]
"""
# TODO: convert to Repy?
from repyportability import *
# python core
import getpass
import re
import sys # switch to repy
import socket # switch to repy
# from autograder; thanks Alper!
import nm_remote_api
# seattle libs
dy_import_module_symbols('centralizedadvertise.r2py')
dy_import_module_symbols('rsa.r2py')
# it's the bizarrest thing; beraber works but
# satya.cs refuses to accept a socket connection
# So for now I'll override the default in centralizedadvertise.r2py
servername = "beraber.cs.washington.edu"
######################
# nm remote API changes
#######################
# i want to change the way the nm_remote_api module works a little
# without editing the actual file in case I'd mess somebody else up
# but I don't want to duplicate nm_remote_api.
#
# Some of these changes may be temporary or could be integrated in the future.
def infinitely_run_target(longname, filename, filedata, argstring, timeout=240):
"""
<Purpose>
Uploads and starts the filename Repy script running on the given longname node.
<Differences>
While the original nm_remote_api function times out, this function allows the
script to run forever.
"""
# smart argstring check:
if filename.find("/") != -1:
error_msg = "Please pass in the filename without any directory/hierarchy information (passed in '%s')" % filename
return (False, error_msg)
argparts = argstring.partition(" ")
if argparts[0] != filename:
# attempt to fix
if argparts[2] != "":
argstring = filename + " " + argparts[2].strip()
else:
argstring = filename
nm_remote_api.check_is_initialized()
vesselname = nm_remote_api.vesselinfo[longname]['vesselname']
try:
nm_remote_api.nmclient_signedsay(nm_remote_api.vesselinfo[longname]['handle'], "AddFileToVessel",
vesselname, filename, filedata)
except nm_remote_api.NMClientException, e:
return (False, str(e))
#print "Successfully added ", filename, " to vessel"
try:
nm_remote_api.nmclient_signedsay(nm_remote_api.vesselinfo[longname]['handle'], "StartVessel",
vesselname, argstring)
except nm_remote_api.NMClientException, e:
return (False, str(e))
# CHANGE: April 08, 2009 RJ
return (True, "No checking for termination")
def uservessels_add_node_by_hostname(host, port=nm_remote_api.DEFAULT_NODEMANAGER_PORT):
"""
<Purpose>
Attempts to find a running Seattle instance at the host specified,
connect to it, and add meta-information about the instance and its
available vessels to the internal dictionary (vesselinfo).
See documentation in nm_remote_api.mix for more.
<Differences>
Uses uservessels instead of owner vessels.
<Note>
This function does no checking of whether the uservessels
are allocated through GENI. You may get vessels that you don't
have access to. Please lookup with centralizedadvertise.r2py first
to find your vessels.
"""
nm_remote_api.check_is_initialized(check_vesselinfo=False)
# get information about the node's vessels
thishandle = nm_remote_api.nmclient_createhandle(host, port, privatekey = nm_remote_api.key['private'],
publickey = nm_remote_api.key['public'])
ownervessels, uservessels = nm_remote_api.nmclient_listaccessiblevessels(thishandle,
nm_remote_api.key['public'])
new_vessel_list = []
# CHANGE: April 08, 2009 RJ
# we should add anything we can access (we only care about uservessels)
for vesselname in uservessels:
longname = host+":"+str(port)+":"+vesselname
if longname not in nm_remote_api.vesselinfo:
# set the vesselname
# NOTE: we leak handles (no cleanup of thishandle).
# I think we don't care...
newhandle = nm_remote_api.nmclient_duplicatehandle(thishandle)
handleinfo = nm_remote_api.nmclient_get_handle_info(newhandle)
handleinfo['vesselname'] = vesselname
nm_remote_api.nmclient_set_handle_info(newhandle, handleinfo)
nm_remote_api.add_vessel(longname, newhandle)
new_vessel_list.append(longname)
# tell the user what we did...
if len(new_vessel_list) == 0:
print "Could not add any targets."
else:
print "Added targets: "+", ".join(new_vessel_list)
return new_vessel_list
def desperate_initialize(host_list, keyname):
"""
<Purpose>
Initializes the state of the module, attempts to connect to each instance
of Seattle on the given nodes.
See nm_remote_api for more documentation.
<Diffences>
Thinks that any vessels found is a success. Even if we get less than
we asked for, we'll take it.
"""
summary = ""
# check if any state has been initialized
if not nm_remote_api.key == {} or not nm_remote_api.vesselinfo == {}:
summary += "Initialized state still exists, tear_down() first."
return (False, summary)
# attempt to read in authentication keys
nm_remote_api.key['public'] = rsa_file_to_publickey(keyname + ".publickey")
nm_remote_api.key['private'] = rsa_file_to_privatekey(keyname + ".privatekey")
# critical for nmclient to work, attempt to get the current time on port
nm_remote_api.time_updatetime(34933)
# attempt to contact and store state, append vessel longnames to list
acquired_vessels = []
for host in host_list:
try:
new_vessels = nm_remote_api.add_node_by_hostname(host)
except nm_remote_api.NMClientException, e:
summary += " " + str(e)
else:
acquired_vessels.extend(new_vessels)
# CHANGE RJ 4/8/09
# if we get anything, we'll take it
if len(acquired_vessels) == 0:
return (False, summary)
else:
return (True, acquired_vessels)
# override functions
nm_remote_api.run_target = infinitely_run_target # let process run forever
nm_remote_api.add_node_by_hostname = uservessels_add_node_by_hostname # use uservessels instead of ownervessels
nm_remote_api.initialize = desperate_initialize # take any found vessels as success
#############
# Helpers
###############
def usage():
print """Usage: python rhizoma.py keyname command [args]
keyname is your username for GENI
command is either:
acquire [num_vessels <LAN | WAN | Random>] - allocate number, type new vessels through GENI
log - output the logs of all vessels
release - unallocate vessels through GENI
status - show the status of all vessels
start file_to_run.r2py [args] - start a script on all vessels
stop - stop all scripts running on all vessels
Example usage:
python rhizoma.py richard acquire # default is 1 LAN vessel
python rhizoma.py richard start hello.r2py # run the script
python rhizoma.py richard status # check status
python rhizoma.py richard stop # kill it
python rhizoma.py richard release # give up the vessel
Requirements:
Please put your keyname.publickey and keyname.privatekey
in your current directory. All commands require your keys.
If you want to start a script, the script must also be in
your current directory.
"""
sys.exit(1)
def get_uservessels(keyname):
"""Return the given user's GENI vessels"""
# check with advertiser
key_public = rsa_file_to_publickey(keyname + ".publickey")
raw_hosts = centralizedadvertise_lookup(key_public)
# BUG in centralizedadvertise. Should return [].
if raw_hosts == ['']:
raw_hosts = []
hosts = []
# strip off the port
for raw_host in raw_hosts:
host, port = raw_host.split(':')
hosts += [host]
return hosts
def geni_logon(ss, keyname, password):
"""
<Purpose>
Use HTTP to logon to GENI.
<Args>
ss - ssl socket connected to GENI
keyname - username of the GENI accoutn
password - user's GENI password
<Return>
The sessionid of the logged on user.
"""
# GET the test cookie by hitting the home page
message = """GET /geni/accounts/login HTTP/1.1 \r
Host: seattlegeni.cs.washington.edu\r
Accept: */*\r\n\r\n"""
ss.write(message)
# see the result
data = ""
for x in range(0,6):
data += ss.read()
# parse out the cookie
m = re.search(r".*sessionid=([^;]+);.*", data)
ms = m.groups()
sessionid = list(ms).pop()
# POST to the login with test cookie
postdata = "username=" + keyname + "&password=" + password + "&next=&jsenabled=true"
message = """POST /geni/accounts/login HTTP/1.1 \r
Host: seattlegeni.cs.washington.edu\r
Accept: */*\r
Cookie: sessionid=""" + sessionid + """\r
Content-Type: application/x-www-form-urlencoded\r
Content-Length: """ + str(len(postdata)) + "\r\n\r\n" + postdata + "\r\n\r\n"
ss.write(message)
# see the result
data = ""
data += ss.read() # here
# print repr(data)
# parse the real cookie
m = re.search(r".*sessionid=([^;]+);.*", data)
if not m:
print
print "Could not log on to GENI. Was your password right, " + keyname + "?"
print
sys.exit(4)
ms = m.groups()
sessionid = list(ms).pop()
return sessionid
def request_vessels(ss, sessionid, num =1, env = 1):
"""
<Purpose>
Use HTTP to get some vessels.
<Args>
ss - ssl socket
sessionid - geni session for the user
num - number of vessels to acquire
env - the type either LAN, WAN, or Random
<Pre condition>
ss is connected to GENI
session is valid (result of calling geni_logon)
<Return>
None # maybe new vessels? would be nice
"""
# POST to acquire resources
postdata = "num=" + str(num) + "&env=" + str(env)
message = """POST /geni/control/get_resources HTTP/1.1 \r
Host: seattlegeni.cs.washington.edu\r
Accept: */*\r
Cookie: sessionid=""" + sessionid + """\r
Content-Type: application/x-www-form-urlencoded\r
Content-Length: """ + str(len(postdata)) + "\r\n\r\n" + postdata + "\r\n"
ss.write(message)
# print message
# see the result
data = ""
for x in range(0,6):
data += ss.read()
# print repr(data)
def acquire_uservessels(keyname, num=1, env=1):
"""
<Purpose>
The user, keyname, wants some more vessels. We'll
do it for her.
Let's login to GENI and them ask for some.
<Args>
keyname - the username of the GENI account
num - number of vessels to acquire
env - the type either LAN, WAN, or Random
<Side Effects>
Prompts the user for their GENI password.
"""
# the GENI site
HOST = 'seattlegeni.cs.washington.edu'
PORT = 443 # ssl
# set up a secure socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
ss = socket.ssl(s)
# login to GENI
password = getpass.getpass("I'll get them for you, " + keyname + ".\nPlease enter your GENI password: ")
sessionid = geni_logon(ss, keyname, password)
# acquire them
request_vessels(ss, sessionid, num , env)
# clean up
del ss
s.close()
def return_vessels(ss, sessionid):
"""
<Purpose>
Use HTTP to get rid of all my vessels.
<Args>
ss - ssl socket
sessionid - geni session for the user
<Pre condition>
ss is connected to GENI
session is valid (result of calling geni_logon)
<Return>
None
"""
# POST to acquire resources
postdata = ""
message = """POST /geni/control/del_all_resource HTTP/1.1 \r
Host: seattlegeni.cs.washington.edu\r
Accept: */*\r
Cookie: sessionid=""" + sessionid + """\r
Content-Type: application/x-www-form-urlencoded\r
Content-Length: """ + str(len(postdata)) + "\r\n\r\n" + postdata + "\r\n"
ss.write(message)
# print message
# see the result
data = ""
for x in range(0,6):
data += ss.read()
#print repr(data)
def release_uservessels(keyname):
"""
<Purpose>
The user, keyname, wants to get rid of all her vessels.
We'll do it for her.
Let's login to GENI and them hit the del page.
<Args>
keyname - the username of the GENI account
<Side Effects>
Prompts the user for their GENI password.
"""
# the GENI site
HOST = 'seattlegeni.cs.washington.edu'
PORT = 443 # ssl
# set up a secure socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
ss = socket.ssl(s)
# login to GENI
password = getpass.getpass("I'll release the vessels for you, " + keyname + ".\nPlease enter your GENI password: ")
sessionid = geni_logon(ss, keyname, password)
# release them
return_vessels(ss, sessionid)
# clean up
del ss
s.close()
#############
# Main
###########
def main():
# must give exactly one argument
if not len(sys.argv) >= 3:
usage()
my_name = sys.argv.pop(0) # this script's name
keyname = sys.argv.pop(0) # aka username
# Do we have any vessels?
hosts = get_uservessels(keyname)
print "Found: ", hosts
print
# everything is ready to go
# what was the arg?
cmd = sys.argv.pop(0)
if cmd == 'acquire':
# defaults
num = 1
env = 1
# optionals overwrite
if len(sys.argv):
num = sys.argv.pop(0)
if len(sys.argv):
env = sys.argv.pop(0)
if env == "LAN":
env = 1
elif env == "WAN":
env = 2
elif env == "Random":
env = 3
else:
print "Bad Environment: must be one of LAN, WAN, or Random"
print
usage()
# go to GENI and do it
acquire_uservessels(keyname, num ,env)
# Try again the advertise server and show the result
hosts = get_uservessels(keyname)
print "User Vessels: " + str(hosts)
# return early
sys.exit(0)
elif cmd == 'release':
print "Releasing all vessels..."
release_uservessels(keyname)
print "Changes may take a few minutes to propagate"
# return early
sys.exit(0)
else:
# use the vessels we have
print "Initializing vessels..."
success, info = nm_remote_api.initialize(hosts, keyname)
if not success:
print info
print "Could not acquire hosts. Please wait a little and try again. Or acquire some vessels."
sys.exit(2)
print
if cmd == 'status':
print "Checking the status of vessels..."
for lname in info:
nm_remote_api.is_vessel_finished(lname)
elif cmd == 'start' and len(sys.argv) > 0:
# get file name
filename = sys.argv.pop(0)
# any args?
argstring = filename
while len(sys.argv) > 0:
argstring += " " + sys.argv.pop(0)
# read in the file and run it
print "Starting " + str(filename) + " on all vessels..."
FILE = open(filename)
filedata = FILE.read()
FILE.close()
dic = nm_remote_api.run_on_targets(info, filename, filedata, argstring, 10)
# result?
print dic
elif cmd == 'stop':
print "Stopping " + str(filename) + " on all vessels..."
for longname in info:
nm_remote_api.stop_target(longname)
elif cmd == 'log':
print "Showing the logs of vessels..."
for lname in info:
print nm_remote_api.showlog_vessel(lname)
else:
usage()
if __name__ == '__main__':
main()
sys.exit(0)