source: fedd/fedd_client.py @ e19b75c

axis_examplecompt_changesinfo-opsversion-2.00version-3.01version-3.02
Last change on this file since e19b75c was 7c9a0a4, checked in by Ted Faber <faber@…>, 15 years ago

missed an error message

  • Property mode set to 100755
File size: 56.2 KB
RevLine 
[6ff0b91]1#!/usr/local/bin/python
2
3import sys
4import os
5import pwd
[63f3746]6import tempfile
7import subprocess
[cc58813]8import re
9import xml.parsers.expat
[281c0ca]10import time
[6ff0b91]11
[5d3f239]12from federation import fedid, service_error
13from federation.util import fedd_ssl_context, pack_id, unpack_id
14from federation.remote_service import service_caller
[df783c1]15from federation import topdl
[6ff0b91]16
17from optparse import OptionParser, OptionValueError
18
[bb3769a]19
[6ff0b91]20class IDFormatException(RuntimeError): pass
21
22class access_method:
23    """Encapsulates an access method generically."""
24    (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey')
25    default_type = type_ssh
26    def __init__(self, buf=None, type=None, file=None):
[16a23a6]27        self.buf = buf
[6ff0b91]28
[16a23a6]29        if type != None: self.type = type
30        else: self.type = access_method.default_type
[6ff0b91]31
[16a23a6]32        if file != None:
33            self.readfile(file)
[6ff0b91]34   
35    def readfile(self, file, type=None):
[16a23a6]36        f = open(file, "r")
37        self.buf = f.read();
38        f.close()
39        if type == None:
40            if self.type == None:
41                self.type = access_method.default_type
42        else:
43            self.type = type;
[6ff0b91]44   
45class node_desc:
46    def __init__(self, image, hardware, count=1):
[16a23a6]47        if getattr(image, "__iter__", None) == None:
48            if image == None: self.image = [ ]
49            else: self.image = [ image ]
50        else:
51            self.image = image
52
53        if getattr(hardware, "__iter__", None) == None: 
54            if hardware == None: self.hardware = [ ]
55            else: self.hardware = [ hardware ]
56        else:
57            self.hardware = hardware
58        if count != None: self.count = int(count)
59        else: self.count = 1
[6ff0b91]60
61class fedd_client_opts(OptionParser):
62    """Encapsulate option processing in this class, rather than in main"""
63    def __init__(self):
[16a23a6]64        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
65                version="0.1")
66
67        self.add_option("-c","--cert", action="store", dest="cert",
68                type="string", help="my certificate file")
69        self.add_option("-d", "--debug", action="count", dest="debug", 
70                default=0, help="Set debug.  Repeat for more information")
71        self.add_option("-s", "--serializeOnly", action="store_true", 
72                dest="serialize_only", default=False,
73                help="Print the SOAP request that would be sent and exit")
74        self.add_option("-T","--trusted", action="store", dest="trusted",
75                type="string", help="Trusted certificates (required)")
76        self.add_option("-u", "--url", action="store", dest="url",
77                type="string",default="https://localhost:23235", 
78                help="URL to connect to (default %default)")
79        self.add_option("-x","--transport", action="store", type="choice",
80                choices=("xmlrpc", "soap"), default="soap",
81                help="Transport for request (xmlrpc|soap) (Default: %default)")
82        self.add_option("--trace", action="store_const", dest="tracefile", 
83                const=sys.stderr, help="Print SOAP exchange to stderr")
[6ff0b91]84
[03e0290]85class fedd_create_opts(fedd_client_opts):
86    def __init__(self, access_keys, add_key_callback=None, 
[16a23a6]87            add_cert_callback=None):
88        fedd_client_opts.__init__(self)
89        self.add_option("-e", "--experiment_cert", dest="out_certfile",
90                type="string", help="output certificate file")
91        self.add_option("-E", "--experiment_name", dest="exp_name",
92                type="string", help="Suggested experiment name")
93        self.add_option("-F","--useFedid", action="store_true",
94                dest="use_fedid", default=False,
95                help="Use a fedid derived from my certificate as user identity")
96        self.add_option("-f", "--file", dest="file", 
97                help="experiment description file")
98        self.add_option("-p", "--project", action="store", dest="project", 
99                type="string",
100                help="Project to export from master")
101        if add_key_callback:
102            self.add_option("-k", "--ssh_key", action="callback",
103                    type="string", callback=add_key_callback,
104                    callback_args=(access_keys,),
105                    help="ssh key for access (can be supplied more than once")
106        if add_cert_callback:
107            self.add_option("-K", "--x509Key", action="callback",
108                    type="string", callback=add_cert_callback,
109                    callback_args=(access_keys,),
110                    help="X509 certificate for access " + \
111                        "(can be supplied more than once")
112        self.add_option("-m", "--master", dest="master",
113                help="Master testbed in the federation")
114        self.add_option("-U", "--username", action="store", dest="user",
115                type="string", help="Use this username instead of the uid")
[6ff0b91]116
[2c6128f]117class fedd_split_opts(fedd_create_opts):
118    def __init__(self, access_keys, add_key_callback=None, 
[16a23a6]119            add_cert_callback=None):
120        fedd_create_opts.__init__(self, access_keys, add_key_callback,
121                add_cert_callback)
122        self.add_option('-t','--fedkit', action='store_true', dest='fedkit',
123                default=False,
124                help="get output suitable for federation kit install")
125        self.add_option('-G','--gatewaykit', action='store_true',
126                dest='gatewaykit', default=False,
127                help="get output suitable for federation gateway kit install")
[2c6128f]128
129
[03e0290]130class fedd_access_opts(fedd_create_opts):
131    def __init__(self, access_keys, node_descs, add_key_callback=None, 
[16a23a6]132            add_cert_callback=None, add_node_callback=None):
133        fedd_create_opts.__init__(self, access_keys, add_key_callback,
134                add_cert_callback)
135        self.add_option("-a","--anonymous", action="store_true",
136                dest="anonymous", default=False,
137                help="Do not include a user in the request")
138        self.add_option("-l","--label", action="store", dest="label",
139                type="string", help="Label for output")
140        if add_node_callback:
141            self.add_option("-n", "--node", action="callback", type="string", 
142                    callback=add_node_callback, callback_args=(node_descs,),
143                    help="Node description: image:hardware[:count]")
144        self.add_option("-t", "--testbed", action="store", dest="testbed",
145                type="string",
146                help="Testbed identifier (URI) to contact (required)")
[6ff0b91]147
[e40c7ee]148class fedd_exp_data_opts(fedd_client_opts):
149    def __init__(self):
[16a23a6]150        fedd_client_opts.__init__(self)
151        self.add_option("-e", "--experiment_cert", dest="exp_certfile",
152                type="string", help="experiment certificate file")
153        self.add_option("-E", "--experiment_name", dest="exp_name",
154                type="string", help="human readable experiment name")
155        self.add_option("--data", dest="data", default=[],
156                action="append", type="choice",
157                choices=("id", "federant", "vtopo", "vis", "log", "status"),
158                help="data to extract")
[281c0ca]159
[ca489e8]160class fedd_terminate_opts(fedd_exp_data_opts):
161    def __init__(self):
[16a23a6]162        fedd_exp_data_opts.__init__(self)
163        self.add_option("--force", dest="force",
164                action="store_true", default=False,
165                help="Force termination if experiment is in strange state")
166        self.add_option("--logfile", dest="logfile", default=None,
167                help="File to write log to")
168        self.add_option("--print_log", dest="print_log", default=False,
169                action="store_true",
170                help="Print deallocation log to standard output")
[ca489e8]171
[65f3f29]172class fedd_multi_exp_data_opts(fedd_client_opts):
173    def __init__(self):
[16a23a6]174        fedd_client_opts.__init__(self)
175        self.add_option("--data", dest="data", default=[],
176                action="append", type="choice",
177                choices=("id", "federant", "vtopo", "vis", "log", "status"),
178                help="data to extract")
[65f3f29]179
[281c0ca]180class fedd_spew_opts(fedd_client_opts):
181    def __init__(self):
[16a23a6]182        fedd_client_opts.__init__(self)
183        self.add_option("-e", "--experiment_cert", dest="exp_certfile",
184                type="string", help="experiment name certificate file")
185        self.add_option("-E", "--experiment_name", dest="exp_name",
186                type="string", help="human readable experiment name")
187        self.add_option("--logfile", dest="logfile", default=None,
188                help="File to write log to")
189        self.add_option('--update_time', dest='update', type='int', default=10,
190                help='how often to update the printed log')
[e40c7ee]191
[63f3746]192class fedd_image_opts(fedd_exp_data_opts):
193    def __init__(self):
[16a23a6]194        fedd_exp_data_opts.__init__(self)
195        self.add_option("-o", "--output", dest="outfile", type="string",
196                help="output image file")
197        self.add_option("-F", "--format", dest="format", type="choice", 
198                choices=("jpg", "png", "dot", "svg"),
199                help="Output file format override")
200        self.add_option("-P", "--program", dest="neato", default=None,
201                type="string",
202                help="Program compatible with dot (from graphviz) used to " + \
203                        "render image")
204        self.add_option('-L', "--labels", dest='labels', action='store_true',
205                default=True, help='Label nodes and edges')
206        self.add_option('-A', "--no_labels", dest='labels',
207                default=True, action='store_false',
208                help='Label nodes and edges')
209        self.add_option('-j','--pixels', dest="pixels", default=None,
210                type="int",
211                help="Size of output in pixels (diagrams are square")
[cc58813]212
213class fedd_ns_image_opts(fedd_split_opts):
214    def __init__(self, access_keys, add_key_callback=None, 
[16a23a6]215            add_cert_callback=None):
216        fedd_split_opts.__init__(self, access_keys, add_key_callback, 
217                add_cert_callback)
218        self.add_option("-o", "--output", dest="outfile", type="string",
219                help="output image file")
220        self.add_option("-Q", "--format", dest="format", type="choice", 
221                choices=("jpg", "png", "dot", "svg"),
222                help="Output file format override")
223        self.add_option("-P", "--program", dest="neato", default=None,
224                type="string",
225                help="Program compatible with dot (from graphviz) used to " + \
226                        "render image")
227        self.add_option('-L', "--labels", dest='labels', action='store_true',
228                default=True, help='Label nodes and edges')
229        self.add_option('-A', "--no_labels", dest='labels',
230                default=True, action='store_false',
231                help='Label nodes and edges')
232        self.add_option('-j','--pixels', dest="pixels", default=None,
233                type="int",
234                help="Size of output in pixels (diagrams are square")
[63f3746]235
[0c0b13c]236def exit_with_fault(dict, out=sys.stderr):
237    """ Print an error message and exit.
238
[2d5c8b6]239    The dictionary contains the FeddFaultBody elements."""
[0c0b13c]240    codestr = ""
241
242    if dict.has_key('errstr'):
[16a23a6]243        codestr = "Error: %s" % dict['errstr']
[0c0b13c]244
245    if dict.has_key('code'):
[16a23a6]246        if len(codestr) > 0 : 
247            codestr += " (%d)" % dict['code']
248        else:
249            codestr = "Error Code: %d" % dict['code']
[0c0b13c]250
251    print>>out, codestr
252    print>>out, "Description: %s" % dict['desc']
253    sys.exit(dict.get('code', 20))
[03e0290]254# Base class that will do a the SOAP/XMLRPC exchange for a request.
255class fedd_rpc:
256    class RPCException:
[16a23a6]257        def __init__(self, fb):
258            self.desc = fb.get('desc', None)
259            self.code = fb.get('code', -1)
260            self.errstr = fb.get('errstr', None)
[03e0290]261
262    def __init__(self, pre): 
[16a23a6]263        """
264        Specialize the class for the pre method
265        """
266        self.RequestBody="%sRequestBody" % pre
267        self.ResponseBody="%sResponseBody" % pre
268        self.method = pre
[058f58e]269
[16a23a6]270        self.caller = service_caller(self.method)
271        self.RPCException = fedd_rpc.RPCException
[03e0290]272
273
274    def add_ssh_key(self, option, opt_str, value, parser, access_keys):
[16a23a6]275        try:
276            access_keys.append(access_method(file=value,
277                type=access_method.type_ssh))
278        except IOError, (errno, strerror):
279            raise OptionValueError("Cannot generate sshPubkey from %s: "\
280                    "%s (%d)" % (value,strerror,errno))
[03e0290]281
282    def add_x509_cert(self, option, opt_str, value, parser, access_keys):
[16a23a6]283        try:
284            access_keys.append(access_method(file=value,
285                type=access_method.type_x509))
286        except IOError, (errno, strerror):
287            raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
288                    (value,strerror,errno))
[03e0290]289    def add_node_desc(self, option, opt_str, value, parser, node_descs):
[16a23a6]290        def none_if_zero(x):
291            if len(x) > 0: return x
292            else: return None
[03e0290]293
[16a23a6]294        params = map(none_if_zero, value.split(":"));
295       
296        if len(params) < 4 and len(params) > 1:
297            node_descs.append(node_desc(*params))
298        else:
299            raise OptionValueError("Bad node description: %s" % value)
[03e0290]300
301    def get_user_info(self, access_keys):
[16a23a6]302        pw = pwd.getpwuid(os.getuid());
303        try_cert=None
304        user = None
305
306        if pw != None:
307            user = pw[0]
308            try_cert = "%s/.ssl/emulab.pem" % pw[5];
309            if not os.access(try_cert, os.R_OK):
310                try_cert = None
311            if len(access_keys) == 0:
312                for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub", 
313                        "%s/.ssh/identity.pub"]:
314                    try_key = k % pw[5];
315                    if os.access(try_key, os.R_OK):
316                        access_keys.append(access_method(file=try_key,
317                            type=access_method.type_ssh))
318                        break
319        return (user, try_cert)
[03e0290]320
321    def do_rpc(self, req_dict, url, transport, cert, trusted, tracefile=None,
[16a23a6]322            serialize_only=False):
323        """
324        The work of sending and parsing the RPC as either XMLRPC or SOAP
325        """
326
327        context = None
328        while context == None:
329            try:
330                context = fedd_ssl_context(cert, trusted)
331            except Exception, e:
332                # Yes, doing this on message type is not ideal.  The string
333                # comes from OpenSSL, so check there is this stops working.
334                if str(e) == "bad decrypt": 
335                    print >>sys.stderr, "Bad Passphrase given."
336                else: raise
337
338        if transport == "soap":
339            if serialize_only:
340                print self.caller.serialize_soap(req_dict) 
341                sys.exit(0)
342            else:
343                try:
344                    resp = self.caller.call_soap_service(url, req_dict, 
345                            context=context, tracefile=tracefile)
346                except service_error, e:
347                    raise self.RPCException( {\
348                            'code': e.code, 
349                            'desc': e.desc, 
350                            'errstr': e.code_string()\
351                        })
352        elif transport == "xmlrpc":
353            if serialize_only:
354                ser = dumps((req_dict,))
355                print ser
356                sys.exit(0)
357            else:
358                try:
359                    resp = self.caller.call_xmlrpc_service(url, req_dict, 
360                            context=context, tracefile=tracefile)
361                except service_error, e:
362                    raise self.RPCException( {\
363                            'code': e.code, 
364                            'desc': e.desc, 
365                            'errstr': e.code_string()\
366                        })
367
368        else:
369            raise RuntimeError("Unknown RPC transport: %s" % transport)
370
371        if resp.has_key(self.ResponseBody):
372            return resp[self.ResponseBody]
373        else:
374            raise RuntimeError("No body in response??")
[058f58e]375
[65f3f29]376class exp_data_base(fedd_rpc):
377    def __init__(self, op='Info'):
[16a23a6]378        """
379        Init the various conversions
380        """
381
382        fedd_rpc.__init__(self, op)
383        # List of things one could ask for and what formatting routine is
384        # called.
385        self.params = {
386                'vis': ('vis', self.print_xml('vis')),
387                'vtopo': ('vtopo', self.print_xml('vtopo')),
388                'federant': ('federant', self.print_xml),
389                'id': ('experimentID', self.print_id),
390                'status': ('experimentStatus', self.print_string),
391                'log': ('allocationLog', self.print_string),
392                'access': ('experimentAccess', self.print_string),
393            }
[281c0ca]394
[65f3f29]395    # Utility functions
[281c0ca]396    def print_string(self, d, out=sys.stdout):
[16a23a6]397        print >>out, d
[281c0ca]398
399    def print_id(self, d, out=sys.stdout):
[16a23a6]400        if d:
401            for id in d:
402                for k in id.keys():
403                    print >>out, "%s: %s" % (k, id[k])
[03e0290]404
[281c0ca]405    class print_xml:
[16a23a6]406        """
407        Print the retrieved data is a simple xml representation of the dict.
408        """
409        def __init__(self, top):
410            self.xml = top
411
412        def __call__(self, d, out=sys.stdout):
413            str = "<%s>\n" % self.xml
414            for t in ('node', 'lan'):
415                if d.has_key(t): 
416                    for x in d[t]:
417                        str += "<%s>" % t
418                        for k in x.keys():
419                            str += "<%s>%s</%s>" % (k, x[k],k)
420                        str += "</%s>\n" % t
421            str+= "</%s>" % self.xml
422            print >>out, str
423
424       
[65f3f29]425
426# Querying experiment data follows the same control flow regardless of the
427# specific data retrieved.  This class encapsulates that control flow.
428class exp_data(exp_data_base):
429    def __init__(self): 
[16a23a6]430        exp_data_base.__init__(self, 'Info')
[65f3f29]431
[03e0290]432    def __call__(self):
[16a23a6]433        """
434        The control flow.  Compose the request and print the response.
435        """
436        # Process the options using the customized option parser defined above
437        parser = fedd_exp_data_opts()
[03e0290]438
[16a23a6]439        (opts, args) = parser.parse_args()
[03e0290]440
[16a23a6]441        if opts.trusted:
442            if ( not os.access(opts.trusted, os.R_OK) ) :
443                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
[6ff0b91]444
[16a23a6]445        if opts.debug > 0: opts.tracefile=sys.stderr
[6ff0b91]446
[16a23a6]447        (user, cert) = self.get_user_info([])
[f3d72f7]448
[16a23a6]449        if opts.cert != None: cert = opts.cert
[6ff0b91]450
[16a23a6]451        if cert == None:
452            sys.exit("No certificate given (--cert) or found")
[6ff0b91]453
[16a23a6]454        if os.access(cert, os.R_OK):
455            fid = fedid(file=cert)
456        else:
457            sys.exit("Cannot read certificate (%s)" % cert)
[03e0290]458
[16a23a6]459        if opts.exp_name and opts.exp_certfile:
460            sys.exit("Only one of --experiment_cert and " +\
461                    "--experiment_name permitted");
[e40c7ee]462
[7c9a0a4]463        exp_id = None
[16a23a6]464        if opts.exp_certfile:
465            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
[e40c7ee]466
[16a23a6]467        if opts.exp_name:
468            exp_id = { 'localname' : opts.exp_name }
[e40c7ee]469
[7c9a0a4]470        if not exp_id:
471            sys.exit("specify one of --experiment_cert and --experiment_name")
472
473
[16a23a6]474        req = { 'experiment': exp_id }
[03e0290]475
[16a23a6]476        try:
477            resp_dict = self.do_rpc(req,
478                    opts.url, opts.transport, cert, opts.trusted, 
479                    serialize_only=opts.serialize_only,
480                    tracefile=opts.tracefile)
481        except self.RPCException, e:
482            exit_with_fault(\
483                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
484        except RuntimeError, e:
485            sys.exit("Error processing RPC: %s" % e)
[03e0290]486
[16a23a6]487        for d in opts.data:
488            key, output = self.params[d]
489            try:
490                if resp_dict.has_key(key):
491                    output(resp_dict[key])
492            except RuntimeError, e:
493                sys.exit("Bad response. %s" % e.message)
[f76d3d7]494
[281c0ca]495class vtopo(exp_data):
496    """
497    vtopo is just an info --data=vtopo request, so this adds that parameter to
498    the arguments and executes exp_info when called.
499    """
500    def __init__(self):
[16a23a6]501        exp_data.__init__(self)
[f76d3d7]502    def __call__(self):
[16a23a6]503        sys.argv.append('--data=vtopo')
504        exp_data.__call__(self)
[f76d3d7]505
506
[281c0ca]507class vis(exp_data):
508    """
509    vis is just an info --data=vis request, so this adds that parameter to
510    the arguments and executes exp_info when called.
511    """
512    def __init__(self):
[16a23a6]513        exp_data.__init__(self)
[281c0ca]514    def __call__(self):
[16a23a6]515        sys.argv.append('--data=vis')
516        exp_data.__call__(self)
[281c0ca]517
518class status(exp_data):
519    """
520    status is just an info --data=status request, so this adds that parameter
521    to the arguments and executes exp_info when called.
522    """
523    def __init__(self):
[16a23a6]524        exp_data.__init__(self)
[281c0ca]525    def __call__(self):
[16a23a6]526        sys.argv.append('--data=status')
527        exp_data.__call__(self)
[f76d3d7]528
[65f3f29]529class multi_exp_data(exp_data_base):
530    def __init__(self): 
[16a23a6]531        exp_data_base.__init__(self, 'MultiInfo')
[65f3f29]532
533
534    def __call__(self):
[16a23a6]535        """
536        The control flow.  Compose the request and print the response.
537        """
538        # Process the options using the customized option parser defined above
539        parser = fedd_multi_exp_data_opts()
540
541        (opts, args) = parser.parse_args()
542
543        if opts.trusted:
544            if ( not os.access(opts.trusted, os.R_OK) ) :
545                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
546
547        if opts.debug > 0: opts.tracefile=sys.stderr
548
549        (user, cert) = self.get_user_info([])
550
551        if opts.cert != None: cert = opts.cert
552
553        if cert == None:
554            sys.exit("No certificate given (--cert) or found")
555
556        if os.access(cert, os.R_OK):
557            fid = fedid(file=cert)
558        else:
559            sys.exit("Cannot read certificate (%s)" % cert)
560
561        req = { }
562
563        try:
564            resp_dict = self.do_rpc(req,
565                    opts.url, opts.transport, cert, opts.trusted, 
566                    serialize_only=opts.serialize_only,
567                    tracefile=opts.tracefile)
568        except self.RPCException, e:
569            exit_with_fault(\
570                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
571        except RuntimeError, e:
572            sys.exit("Error processing RPC: %s" % e)
573
574        exps = resp_dict.get('info', [])
575        if exps:
576            print '---'
577            for exp in exps:
578                for d in opts.data:
579                    key, output = self.params[d]
580                    try:
581                        if exp.has_key(key):
582                            output(exp[key])
583                    except RuntimeError, e:
584                        sys.exit("Bad response. %s" % e.message)
585                print '---'
[65f3f29]586
587
588class multi_status(exp_data_base):
589    def __init__(self): 
[16a23a6]590        exp_data_base.__init__(self, 'MultiInfo')
[65f3f29]591
592
593    def __call__(self):
[16a23a6]594        """
595        The control flow.  Compose the request and print the response.
596        """
597        # Process the options using the customized option parser defined above
598        parser = fedd_client_opts()
599
600        (opts, args) = parser.parse_args()
601
602        if opts.trusted:
603            if ( not os.access(opts.trusted, os.R_OK) ) :
604                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
605
606        if opts.debug > 0: opts.tracefile=sys.stderr
607
608        (user, cert) = self.get_user_info([])
609
610        if opts.cert != None: cert = opts.cert
611
612        if cert == None:
613            sys.exit("No certificate given (--cert) or found")
614
615        if os.access(cert, os.R_OK):
616            fid = fedid(file=cert)
617        else:
618            sys.exit("Cannot read certificate (%s)" % cert)
619
620        req = { }
621
622        try:
623            resp_dict = self.do_rpc(req,
624                    opts.url, opts.transport, cert, opts.trusted, 
625                    serialize_only=opts.serialize_only,
626                    tracefile=opts.tracefile)
627        except self.RPCException, e:
628            exit_with_fault(\
629                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
630        except RuntimeError, e:
631            sys.exit("Error processing RPC: %s" % e)
632
633        for exp in resp_dict.get('info', []):
634            out = []
635            for eid in exp.get('experimentID', []):
636                if eid.has_key('localname'):
637                    out.append(eid['localname'])
638                    break
639            else:
640                out.append("")
641            for eid in exp.get('experimentID', []):
642                if eid.has_key('fedid'):
643                    out.append("%s" % eid['fedid'])
644                    break
645            else:
646                out.append("")
647
648            out.append(exp.get('experimentStatus', ""))
649
650            for f in exp.get('federant', []):
651                if f.get('master', False):
652                    em = f.get('emulab', None)
653                    if em:
654                        project = em.get('project', None)
655                        if project:
656                            tb = project.get('testbed', None)
657                            if tb and tb.has_key('localname'):
658                                out.append(tb['localname'])
659                            else:
660                                out.append("")
661                            pn = project.get('name', None)
662                            if pn and pn.has_key('localname'):
663                                out.append(pn['localname'])
664                            else:
665                                out.append("")
666                        else:
667                            out.extend(("", ""))
668                    else:
669                        out.extend(("", ""))
670                    break
671            else:
672                out.extend(("",""))
673
674            print ":".join(out)
[65f3f29]675
[63f3746]676class image(fedd_rpc):
[281c0ca]677    def __init__(self, op='Vtopo'): 
[16a23a6]678        """
679        Null constructor
680        """
[63f3746]681
[16a23a6]682        fedd_rpc.__init__(self, op)
[63f3746]683
[df783c1]684    @staticmethod
685    def gen_dot_topo(d, labels, dotfile):
[16a23a6]686        lans = { }
687        links = { }
688
689        for n in d.get('node', []):
690            print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n['vname']
691            print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]'
692
693        # Collect lan members (we have to draw extra stuff for these)
694        for n in d.get('lan', []):
695            v = n['vname']
696            m = n['member']
697            i = n['ip']
698            if m.find(':') != -1:
699                m = m[0:m.find(':')]
700            if lans.has_key(v):
701                lans[v].append((m, i))
702            elif links.has_key(v):
703                links[v].append((m, i))
704                if len(links[v]) > 2:
705                    lans[v] = links[v]
706                    del links[v]
707            else:
708                links[v] = [(m, i)]
709
710        # Encode the lans and the links
711        for l in lans.keys():
712            print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l
713            print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]'
714            for n in lans[l]:
715                if labels:
716                    print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \
717                            (l, n[0], n[1])
718                else:
719                    print >>dotfile, '\t"%s" -- "%s"' % (l, n[0])
720
721        for k, l in links.items():
722            if len(l) == 2:
723                if labels:
724                    print >>dotfile, \
725                            ('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \
726                            'headlabel="%s"]') % \
727                            (l[0][0], l[1][0], k, l[0][1], l[1][1])
728                else:
729                    print >>dotfile, '\t"%s" -- "%s" ' % (l[0][0], l[1][0])
[df783c1]730
731
732    def gen_image(self, d, nodes, file, fmt, neato, labels, pix=None):
[63f3746]733
[16a23a6]734        # Open up a temporary file for dot to turn into a visualization
735        try:
736            df, dotname = tempfile.mkstemp(prefix='fedd_client', suffix=".dot")
737            dotfile = os.fdopen(df, 'w')
738        except IOError:
739            raise service_error(service_error.internal,
740                    "Failed to open file in genviz")
741
742        if not neato:
743            for f in ['/usr/bin/neato', '/usr/local/bin/neato', 
744                    '/usr/bin/dot', '/usr/local/bin/dot']:
745                if os.access(f, os.X_OK):
746                    neato = f
747                    break
748            else:
749                sys.exit("Cannot find graph rendering program")
750
751        cmd = [neato, '-Gsplines=true']
752        if fmt != 'dot': cmd.append('-T%s' % fmt)
753        if file:
754            cmd.append('-o')
755            cmd.append(file)
756        cmd.append(dotname)
757
758        #nodes = d.get('node',[])
759
760        if nodes < 10: size = 5
761        elif nodes < 50: size = 10
762        else: size = 18
763
764        if pix:
765            dpi = pix / size
766        else:
767            dpi = None
768
769
770        print >>dotfile, "graph G {"
771        if dpi:
772            print >>dotfile, '\tgraph [size="%i,%i", dpi="%i", ratio=fill];' \
773                    % (size, size, dpi)
774        else:
775            print >>dotfile, '\tgraph [size="%i,%i", ratio=fill];' \
776                    % (size, size)
777
778        if labels:
779            print >>dotfile, '\tnode [fontname=arial,fontsize=9,label="\N"];'
780            print >>dotfile, '\tedge [fontname=arial,fontsize=9];\n'
781        else:
782            print >>dotfile, '\tnode [label=""];'
783
784
785        self.gen_dot_topo(d, labels, dotfile)
786        print >>dotfile, "}"
787        dotfile.close()
788
789        # Redirect the drawing program stderr
790        dev_null = open("/dev/null", "w")
791        rv = subprocess.call(cmd, stderr=dev_null)
792        os.remove(dotname)
793        dev_null.close()
794        if rv != 0:
795            sys.exit("Error creating graph")
[63f3746]796
797
798
799    def __call__(self):
[16a23a6]800        """
801        The control flow.  Compose the request and print the response.
802        """
803        # Process the options using the customized option parser defined above
804        parser = fedd_image_opts()
805
806        (opts, args) = parser.parse_args()
807
808        if opts.trusted:
809            if ( not os.access(opts.trusted, os.R_OK) ) :
810                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
811
812        if opts.debug > 0: opts.tracefile=sys.stderr
813
814        (user, cert) = self.get_user_info([])
815
816        if opts.cert != None: cert = opts.cert
817
818        if cert == None:
819            sys.exit("No certificate given (--cert) or found")
820
821        if os.access(cert, os.R_OK):
822            fid = fedid(file=cert)
823        else:
824            sys.exit("Cannot read certificate (%s)" % cert)
825
826        if opts.exp_name and opts.exp_certfile:
827            sys.exit("Only one of --experiment_cert and " +\
828                    "--experiment_name permitted");
829
830        if opts.exp_certfile:
831            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
832
833        if opts.exp_name:
834            exp_id = { 'localname' : opts.exp_name }
835
836        if opts.format and opts.outfile:
837            fmt = opts.format
838            file = opts.outfile
839        elif not opts.format and opts.outfile:
840            fmt = opts.outfile[-3:]
841            if fmt not in ("png", "jpg", "dot", "svg"):
842                sys.exit("Unexpected file type and no format specified")
843            file = opts.outfile
844        elif opts.format and not opts.outfile:
845            fmt = opts.format
846            file = None
847        else:
848            fmt="dot"
849            file = None
850
851
852        req = { 'experiment': exp_id }
853
854        try:
855            resp_dict = self.do_rpc(req,
856                    opts.url, opts.transport, cert, opts.trusted, 
857                    serialize_only=opts.serialize_only,
858                    tracefile=opts.tracefile)
859        except self.RPCException, e:
860            exit_with_fault(\
861                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
862        except RuntimeError, e:
863            sys.exit("Error processing RPC: %s" % e)
864
865
866        if resp_dict.has_key('vtopo'):
867            self.gen_image(resp_dict['vtopo'], 
868                    len(resp_dict['vtopo'].get('node', [])),
869                    file, fmt, opts.neato, opts.labels, opts.pixels)
870        else:
871            sys.exit("Bad response. %s" % e.message)
[63f3746]872
[cc58813]873class ns_image(image):
[281c0ca]874    def __init__(self, op='Ns2Split'): 
[16a23a6]875        """
876        Null constructor
877        """
[cc58813]878
[16a23a6]879        image.__init__(self, 'Ns2Split')
[cc58813]880
881    def generate_topo_dict(self, splitout):
[16a23a6]882        class topo_parse:
883            """
884            Parse the topology XML and create the dats structure.  This class
885            is copied from federation.experiment_control.
886            """
887            def __init__(self):
888                # Typing of the subelements for data conversion
889                self.str_subelements = ('vname', 'vnode', 'ips', 'ip', 'member')
890                self.int_subelements = ( 'bandwidth',)
891                self.float_subelements = ( 'delay',)
892                # The final data structure
893                self.nodes = [ ]
894                self.lans =  [ ]
895                self.topo = { \
896                        'node': self.nodes,\
897                        'lan' : self.lans,\
898                    }
899                self.element = { }  # Current element being created
900                self.chars = ""     # Last text seen
901
902            def end_element(self, name):
903                # After each sub element the contents is added to the current
904                # element or to the appropriate list.
905                if name == 'node':
906                    self.nodes.append(self.element)
907                    self.element = { }
908                elif name == 'lan':
909                    self.lans.append(self.element)
910                    self.element = { }
911                elif name in self.str_subelements:
912                    self.element[name] = self.chars
913                    self.chars = ""
914                elif name in self.int_subelements:
915                    self.element[name] = int(self.chars)
916                    self.chars = ""
917                elif name in self.float_subelements:
918                    self.element[name] = float(self.chars)
919                    self.chars = ""
920
921            def found_chars(self, data):
922                self.chars += data.rstrip()
923
924
925        tp = topo_parse();
926        parser = xml.parsers.expat.ParserCreate()
927        parser.EndElementHandler = tp.end_element
928        parser.CharacterDataHandler = tp.found_chars
929
930        m = re.search('^#\s+Begin\s+Vtopo\s*$(.*)^#\s+End\s+Vtopo', splitout, 
931                re.MULTILINE | re.DOTALL)
932        if m:
933            str = m.group(1)
934        else:
935            sys.exit("Badly formatted split")
936
937        parser.Parse(str)
938
939        return tp.topo
[cc58813]940
941    def __call__(self):
[16a23a6]942        """
943        The control flow.  Compose the request and print the response.
944        """
945        access_keys = []
946        # Process the options using the customized option parser defined above
947        parser = fedd_ns_image_opts(access_keys, self.add_ssh_key,
948                self.add_x509_cert)
949
950        (opts, args) = parser.parse_args()
951
952        if opts.trusted:
953            if ( not os.access(opts.trusted, os.R_OK) ) :
954                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
955
956        if opts.debug > 0: opts.tracefile=sys.stderr
957
958        (user, cert) = self.get_user_info([])
959
960        if opts.cert != None: cert = opts.cert
961
962        if cert == None:
963            sys.exit("No certificate given (--cert) or found")
964
965        if os.access(cert, os.R_OK):
966            fid = fedid(file=cert)
967        else:
968            sys.exit("Cannot read certificate (%s)" % cert)
969
970        if opts.file:
971            exp_desc = ""
972            try:
973                f = open(opts.file, 'r')
974                for line in f:
975                    exp_desc += line
976                f.close()
977            except IOError:
978                sys.exit("Cannot read description file (%s)" %opts.file)
979        else:
980            sys.exit("Must specify an experiment description (--file)")
981
982        if not opts.master:
983            opts.master="dummy"
984
985
986        req = {
987                'description': { 'ns2description': exp_desc },
988                'master': opts.master,
989                'include_fedkit': opts.fedkit,
990                'include_gatewaykit': opts.gatewaykit,
991                }
992
993
994        if opts.format and opts.outfile:
995            fmt = opts.format
996            file = opts.outfile
997        elif not opts.format and opts.outfile:
998            fmt = opts.outfile[-3:]
999            if fmt not in ("png", "jpg", "dot", "svg"):
1000                sys.exit("Unexpected file type and no format specified")
1001            file = opts.outfile
1002        elif opts.format and not opts.outfile:
1003            fmt = opts.format
1004            file = None
1005        else:
1006            fmt="dot"
1007            file = None
1008
1009        try:
1010            resp_dict = self.do_rpc(req,
1011                    opts.url, opts.transport, cert, opts.trusted, 
1012                    serialize_only=opts.serialize_only,
1013                    tracefile=opts.tracefile)
1014        except self.RPCException, e:
1015            exit_with_fault(\
1016                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1017        except RuntimeError, e:
1018            sys.exit("Error processing RPC: %s" % e)
1019
1020
1021        if resp_dict.has_key('output'):
1022            if len(resp_dict['output']) < 1:
1023                sys.exit("Bad response: could not split")
1024            topo = self.generate_topo_dict(resp_dict['output'])
1025            self.gen_image(topo, len(topo.get('node', [])), file, fmt,
1026                    opts.neato, opts.labels, opts.pixels)
1027        else:
1028            sys.exit("Bad response. %s" % e.message)
[cc58813]1029
[df783c1]1030class topdl_image(image):
1031    def __init__(self, op='Ns2Split'): 
[16a23a6]1032        """
1033        Null constructor
1034        """
[df783c1]1035
[16a23a6]1036        image.__init__(self, 'Ns2Split')
[df783c1]1037
1038    @staticmethod
1039    def gen_dot_topo(t, labels, dotfile):
[16a23a6]1040        lans = [ s for s in t.substrates if len(s.interfaces) != 2]
1041        links = [ s for s in t.substrates if len(s.interfaces) == 2]
1042
1043        i = 0
1044        for n in t.elements:
1045            if n.name:
1046                print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n.name[0]
1047            else:
1048                print >>dotfile, \
1049                        '\t"unnamed_node%d" [shape=box,style=filled,\\' % i
1050                i += 1
1051            print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]'
1052
1053        # Encode the lans and the links
1054        for l in lans:
1055            print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l.name
1056            print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]'
1057            for i in l.interfaces:
1058                ip = i.get_attribute('ip4_address')
1059                if labels and ip:
1060                    print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \
1061                            (l.name, i.element.name[0], ip)
1062                else:
1063                    print >>dotfile, '\t"%s" -- "%s"' % \
1064                            (l.name, i.element.name[0])
1065
1066        for l in links:
1067            s, d = l.interfaces[0:2] 
1068            sip = s.get_attribute('ip4_address')
1069            dip = d.get_attribute('ip4_address')
1070            if labels and sip and dip:
1071                print >>dotfile, \
1072                        ('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \
1073                        'headlabel="%s"]') % \
1074                        (s.element.name[0], d.element.name[0], l.name,
1075                            sip, dip)
1076            else:
1077                print >>dotfile, '\t"%s" -- "%s" ' % \
1078                        (s.element.name[0], d.element.name[0])
[df783c1]1079    def __call__(self):
[16a23a6]1080        """
1081        The control flow.  Compose the request and print the response.
1082        """
1083        access_keys = []
1084        # Process the options using the customized option parser defined above
1085        parser = fedd_ns_image_opts(access_keys, self.add_ssh_key,
1086                self.add_x509_cert)
1087
1088        (opts, args) = parser.parse_args()
1089
1090        if opts.trusted:
1091            if ( not os.access(opts.trusted, os.R_OK) ) :
1092                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1093
1094        if opts.debug > 0: opts.tracefile=sys.stderr
1095
1096        (user, cert) = self.get_user_info([])
1097
1098        if opts.cert != None: cert = opts.cert
1099
1100        if cert == None:
1101            sys.exit("No certificate given (--cert) or found")
1102
1103        if os.access(cert, os.R_OK):
1104            fid = fedid(file=cert)
1105        else:
1106            sys.exit("Cannot read certificate (%s)" % cert)
1107
1108        if opts.file:
1109            exp_desc = ""
1110            try:
1111                top = topdl.topology_from_xml(filename=opts.file, 
1112                        top="experiment")
1113            except IOError:
1114                sys.exit("Cannot read description file (%s)" % opts.file)
1115        else:
1116            sys.exit("Must specify an experiment description (--file)")
1117
1118        if not opts.master:
1119            opts.master="dummy"
1120
1121
1122        if opts.format and opts.outfile:
1123            fmt = opts.format
1124            file = opts.outfile
1125        elif not opts.format and opts.outfile:
1126            fmt = opts.outfile[-3:]
1127            if fmt not in ("png", "jpg", "dot", "svg"):
1128                sys.exit("Unexpected file type and no format specified")
1129            file = opts.outfile
1130        elif opts.format and not opts.outfile:
1131            fmt = opts.format
1132            file = None
1133        else:
1134            fmt="dot"
1135            file = None
1136
1137        self.gen_image(top, len(top.elements), file, fmt, opts.neato, 
1138                opts.labels, opts.pixels)
[df783c1]1139
[7a8d667]1140class terminate(fedd_rpc):
1141    def __init__(self): 
[16a23a6]1142        """
1143        Termination request
1144        """
[7a8d667]1145
[16a23a6]1146        fedd_rpc.__init__(self, "Terminate")
[7a8d667]1147
1148    def __call__(self):
[16a23a6]1149        """
1150        The control flow.  Compose the request and print the response.
1151        """
1152        # Process the options using the customized option parser defined above
1153        parser = fedd_terminate_opts()
1154
1155        (opts, args) = parser.parse_args()
1156
1157        (user, cert) = self.get_user_info([])
1158        if opts.trusted:
1159            if ( not os.access(opts.trusted, os.R_OK) ) :
1160                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1161
1162        if opts.debug > 0: opts.tracefile=sys.stderr
1163
1164        if opts.cert != None: cert = opts.cert
1165
1166        if cert == None:
1167            sys.exit("No certificate given (--cert) or found")
1168
1169        if os.access(cert, os.R_OK):
1170            fid = fedid(file=cert)
1171        else:
1172            sys.exit("Cannot read certificate (%s)" % cert)
1173
1174        if opts.exp_name and opts.exp_certfile:
1175            sys.exit("Only one of --experiment_cert and " +\
1176                    "--experiment_name permitted")
1177
1178        if opts.print_log and opts.logfile:
1179            sys.exit("Only one of --logfile and --print_log is permitted")
1180        elif opts.print_log:
1181            out = sys.stdout
1182        elif opts.logfile:
1183            try:
1184                out = open(opts.logfile, "w")
1185            except IOError,e:
1186                sys.exit("Cannot open logfile: %s" %e)
1187        else:
1188            out = None
1189
1190        exp_id = None
1191
1192        if opts.exp_certfile:
1193            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
1194
1195        if opts.exp_name:
1196            exp_id = { 'localname' : opts.exp_name }
1197
1198        if not exp_id:
1199            sys.exit("Must give one of --experiment_cert and " +\
1200                    "--experiment_name");
1201
1202        req = { 'experiment': exp_id, 'force': opts.force }
1203
1204        try:
1205            resp_dict = self.do_rpc(req,
1206                    opts.url, opts.transport, cert, opts.trusted, 
1207                    serialize_only=opts.serialize_only,
1208                    tracefile=opts.tracefile)
1209        except self.RPCException, e:
1210            exit_with_fault(\
1211                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1212        except RuntimeError, e:
1213            print e
1214            sys.exit("Error processing RPC: %s" % e)
1215
1216        if out:
1217            log = resp_dict.get('deallocationLog', None)
1218            if log:
1219                print >>out, log
1220                out.close()
1221            else:
1222                out.close()
1223                sys.exit("No log returned")
[7a8d667]1224
[03e0290]1225class create(fedd_rpc):
1226    def __init__(self): 
[16a23a6]1227        fedd_rpc.__init__(self, "Create")
[03e0290]1228    def __call__(self):
[16a23a6]1229        access_keys = []
1230        # Process the options using the customized option parser defined above
1231        parser = fedd_create_opts(access_keys, self.add_ssh_key,
1232                self.add_x509_cert)
1233
1234        (opts, args) = parser.parse_args()
1235
1236        if opts.trusted:
1237            if ( not os.access(opts.trusted, os.R_OK) ) :
1238                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1239
1240        if not opts.project :
1241            parser.error('--project is required')
1242
1243        if opts.debug > 0: opts.tracefile=sys.stderr
1244
1245        (user, cert) = self.get_user_info(access_keys)
1246
1247        if opts.user: user = opts.user
1248
1249        if opts.cert != None: cert = opts.cert
1250
1251        if cert == None:
1252            sys.exit("No certificate given (--cert) or found")
1253
1254        if os.access(cert, os.R_OK):
1255            fid = fedid(file=cert)
1256            if opts.use_fedid == True:
1257                user = fid
1258        else:
1259            sys.exit("Cannot read certificate (%s)" % cert)
1260
1261        if opts.file:
1262            exp_desc = ""
1263            try:
1264                f = open(opts.file, 'r')
1265                for line in f:
1266                    exp_desc += line
1267                f.close()
1268            except IOError:
1269                sys.exit("Cannot read description file (%s)" %opts.file)
1270        else:
1271            sys.exit("Must specify an experiment description (--file)")
1272
1273        if not opts.master:
1274            sys.exit("Must specify a master testbed (--master)")
1275
1276        out_certfile = opts.out_certfile
1277
1278        msg = {
1279                'experimentdescription': { 'ns2description': exp_desc },
1280                'master': opts.master,
1281                'exportProject': { 'localname': opts.project },
1282                'user' : [ {\
1283                        'userID': pack_id(user), \
1284                        'access': [ { a.type: a.buf } for a in access_keys]\
1285                        } ]
1286                }
1287
1288        if opts.exp_name:
1289            msg['experimentID'] = { 'localname': opts.exp_name }
1290
1291        if opts.debug > 1: print >>sys.stderr, msg
1292
1293        try:
1294            resp_dict = self.do_rpc(msg, 
1295                    opts.url, opts.transport, cert, opts.trusted, 
1296                    serialize_only=opts.serialize_only,
1297                    tracefile=opts.tracefile)
1298        except self.RPCException, e:
1299            exit_with_fault(\
1300                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1301        except RuntimeError, e:
1302            sys.exit("Error processing RPC: %s" % e)
1303
1304        if opts.debug > 1: print >>sys.stderr, resp_dict
1305
1306        ea = resp_dict.get('experimentAccess', None)
1307        if out_certfile and ea and ea.has_key('X509'):
1308            try:
1309                f = open(out_certfile, "w")
1310                print >>f, ea['X509']
1311                f.close()
1312            except IOError:
1313                sys.exit('Could not write to %s' %  out_certfile)
1314        eid = resp_dict.get('experimentID', None)
1315        if eid:
1316            for id in eid:
1317                for k in id.keys():
1318                    print "%s: %s" % (k, id[k])
1319        st = resp_dict.get('experimentStatus', None)
1320        if st:
1321            print "status: %s" % st
[03e0290]1322
[f4f4117]1323class split(fedd_rpc):
1324    def __init__(self): 
[16a23a6]1325        fedd_rpc.__init__(self, "Ns2Split")
[f4f4117]1326    def __call__(self):
[16a23a6]1327        access_keys = []
1328        # Process the options using the customized option parser defined above
1329        parser = fedd_split_opts(access_keys, self.add_ssh_key,
1330                self.add_x509_cert)
[f4f4117]1331
[16a23a6]1332        (opts, args) = parser.parse_args()
[f4f4117]1333
[16a23a6]1334        if opts.trusted:
1335            if ( not os.access(opts.trusted, os.R_OK) ) :
1336                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
[f4f4117]1337
[16a23a6]1338        if opts.debug > 0: opts.tracefile=sys.stderr
[f4f4117]1339
[16a23a6]1340        (user, cert) = self.get_user_info(access_keys)
[cc58813]1341
[16a23a6]1342        if opts.cert != None: cert = opts.cert
[f4f4117]1343
[16a23a6]1344        if cert == None:
1345            sys.exit("No certificate given (--cert) or found")
[f4f4117]1346
[16a23a6]1347        if os.access(cert, os.R_OK):
1348            fid = fedid(file=cert)
1349            if opts.use_fedid == True:
1350                user = fid
1351        else:
1352            sys.exit("Cannot read certificate (%s)" % cert)
[f4f4117]1353
[16a23a6]1354        if opts.file:
1355            exp_desc = ""
1356            try:
1357                f = open(opts.file, 'r')
1358                for line in f:
1359                    exp_desc += line
1360                f.close()
1361            except IOError:
1362                sys.exit("Cannot read description file (%s)" %opts.file)
1363        else:
1364            sys.exit("Must specify an experiment description (--file)")
[f4f4117]1365
[16a23a6]1366        if not opts.master:
1367            sys.exit("Must specify a master testbed (--master)")
[f4f4117]1368
[16a23a6]1369        out_certfile = opts.out_certfile
[f4f4117]1370
[16a23a6]1371        msg = {
1372                'description': { 'ns2description': exp_desc },
1373                'master': opts.master,
1374                'include_fedkit': opts.fedkit,
1375                'include_gatewaykit': opts.gatewaykit,
1376                }
[f4f4117]1377
[16a23a6]1378        if opts.debug > 1: print >>sys.stderr, msg
[f4f4117]1379
[16a23a6]1380        try:
1381            resp_dict = self.do_rpc(msg, 
1382                    opts.url, opts.transport, cert, opts.trusted, 
1383                    serialize_only=opts.serialize_only,
1384                    tracefile=opts.tracefile)
1385        except self.RPCException, e:
1386            exit_with_fault(\
1387                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1388        except RuntimeError, e:
1389            sys.exit("Error processing RPC: %s" % e)
[f4f4117]1390
[16a23a6]1391        if opts.debug > 1: print >>sys.stderr, resp_dict
[f4f4117]1392
[16a23a6]1393        out = resp_dict.get('output', None)
[f4f4117]1394
[16a23a6]1395        for line in out.splitlines():
1396            print "%s" % line
[f4f4117]1397
[03e0290]1398class access(fedd_rpc):
1399    def __init__(self):
[16a23a6]1400        fedd_rpc.__init__(self, "RequestAccess")
[03e0290]1401
1402    def print_response_as_testbed(self, resp, label, out=sys.stdout):
[16a23a6]1403        """Print the response as input to the splitter script"""
[03e0290]1404
[16a23a6]1405        e = resp['emulab']
1406        p = e['project']
1407        fields = { 
1408                "Boss": e['boss'],
1409                "OpsNode": e['ops'],
1410                "Domain": e['domain'],
1411                "FileServer": e['fileServer'],
1412                "EventServer": e['eventServer'],
1413                "Project": unpack_id(p['name'])
1414                }
1415        if (label != None): print >> out, "[%s]" % label
[03e0290]1416
[16a23a6]1417        for l, v in fields.iteritems():
1418            print >>out, "%s: %s" % (l, v)
[03e0290]1419
[16a23a6]1420        for u in p['user']:
1421            print >>out, "User: %s" % unpack_id(u['userID'])
[03e0290]1422
[16a23a6]1423        for a in e['fedAttr']:
1424            print >>out, "%s: %s" % (a['attribute'], a['value'])
[03e0290]1425
1426    def __call__(self):
[16a23a6]1427        access_keys = []
1428        node_descs = []
1429        proj = None
1430
1431        # Process the options using the customized option parser defined above
1432        parser = fedd_access_opts(access_keys, node_descs, self.add_ssh_key,
1433                self.add_x509_cert, self.add_node_desc)
1434
1435        (opts, args) = parser.parse_args()
1436
1437        if opts.testbed == None:
1438            parser.error("--testbed is required")
1439
1440        if opts.trusted:
1441            if ( not os.access(opts.trusted, os.R_OK) ) :
1442                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1443
1444        if opts.debug > 0: opts.tracefile=sys.stderr
1445
1446        (user, cert) = self.get_user_info(access_keys)
1447
1448        if opts.user: user = opts.user
1449
1450        if opts.cert != None: cert = opts.cert
1451
1452        if cert == None:
1453            sys.exit("No certificate given (--cert) or found")
1454
1455        if os.access(cert, os.R_OK):
1456            fid = fedid(file=cert)
1457            if opts.use_fedid == True:
1458                user = fid
1459        else:
1460            sys.exit("Cannot read certificate (%s)" % cert)
1461
1462        msg = {
1463                'allocID': pack_id('test alloc'),
1464                'destinationTestbed': pack_id(opts.testbed),
1465                'serviceAccess' : [ { a.type: a.buf } for a in access_keys ],
1466                'createAccess' : [ { a.type: a.buf } for a in access_keys ],
1467                }
1468
1469        if len(node_descs) > 0:
1470            msg['resources'] = { 
1471                    'node': [ 
1472                        { 
1473                            'image':  n.image ,
1474                            'hardware':  n.hardware,
1475                            'count': n.count,
1476                        } for n in node_descs],
1477                    }
1478
1479        if opts.project != None:
1480            if not opts.anonymous and user != None:
1481                msg['project'] = {
1482                        'name': pack_id(opts.project),
1483                        'user': [ { 'userID': pack_id(user) } ],
1484                        }
1485            else:
1486                msg['project'] = { 'name': pack_id(opts.project) }
1487        else:
1488            if not opts.anonymous and user != None:
1489                msg['user'] = [ { 'userID': pack_id(user) } ]
1490            else:
1491                msg['user'] = [];
1492
1493        if opts.debug > 1: print >>sys.stderr, msg
1494
1495        try:
1496            resp_dict = self.do_rpc(msg, 
1497                    opts.url, opts.transport, cert, opts.trusted, 
1498                    serialize_only=opts.serialize_only,
1499                    tracefile=opts.tracefile)
1500        except self.RPCException, e:
1501            exit_with_fault(\
1502                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1503        except RuntimeError, e:
1504            sys.exit("Error processing RPC: %s" % e.message)
1505
1506        if opts.debug > 1: print >>sys.stderr, resp_dict
1507        self.print_response_as_testbed(resp_dict, opts.label)
[03e0290]1508
[281c0ca]1509# Keep requesting experiment status and printing updates to the log until the
1510# experiment is done being created.
1511class spew_log(fedd_rpc):
1512    def __init__(self): 
[16a23a6]1513        """
1514        Init the superclass
1515        """
[281c0ca]1516
[16a23a6]1517        fedd_rpc.__init__(self, 'Info')
[281c0ca]1518
1519    def __call__(self):
[16a23a6]1520        """
1521        The control flow.  Compose the request and print the response.
1522        """
1523        # Process the options using the customized option parser defined above
1524        parser = fedd_spew_opts()
1525
1526        (opts, args) = parser.parse_args()
1527
1528        if opts.trusted:
1529            if ( not os.access(opts.trusted, os.R_OK) ) :
1530                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1531
1532        if opts.debug > 0: opts.tracefile=sys.stderr
1533
1534        (user, cert) = self.get_user_info([])
1535
1536        if opts.cert != None: cert = opts.cert
1537
1538        if cert == None:
1539            sys.exit("No certificate given (--cert) or found")
1540
1541        if os.access(cert, os.R_OK):
1542            fid = fedid(file=cert)
1543        else:
1544            sys.exit("Cannot read certificate (%s)" % cert)
1545
1546        if opts.exp_name and opts.exp_certfile:
1547            sys.exit("Only one of --experiment_cert and " +\
1548                    "--experiment_name permitted");
1549
1550        if opts.exp_certfile:
1551            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
1552
1553        if opts.exp_name:
1554            exp_id = { 'localname' : opts.exp_name }
1555
1556        if opts.logfile:
1557            try:
1558                out = open(opts.logfile, "w")
1559            except IOError,e:
1560                sys.exit("Cannot open logfile: %s" %e)
1561        else:
1562            out = sys.stdout
1563
1564        req = { 'experiment': exp_id }
1565
1566        status = "starting"
1567        log = ""
1568        log_offset = 0
1569        update = opts.update
1570        while status == 'starting':
1571            try:
1572                resp_dict = self.do_rpc(req,
1573                        opts.url, opts.transport, cert, opts.trusted, 
1574                        serialize_only=opts.serialize_only,
1575                        tracefile=opts.tracefile)
1576            except self.RPCException, e:
1577                exit_with_fault(\
1578                        {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1579            except RuntimeError, e:
1580                sys.exit("Error processing RPC: %s" % e)
1581
1582            status = resp_dict.get('experimentStatus', None)
1583            log = resp_dict.get('allocationLog', None)
1584            if not status:
1585                sys.exit("No status in Info response??")
1586            if log:
1587                if len(log) > log_offset:
1588                    print >>out, log[log_offset:],
1589                    out.flush()
1590                    log_offset = len(log)
1591            if status == 'starting': 
1592                time.sleep(update)
1593
1594        print >>out
1595        print >>out, status
1596        out.close()
[281c0ca]1597
1598
[03e0290]1599cmds = {\
[16a23a6]1600        'create': create(),\
1601        'split': split(),\
1602        'access': access(),\
1603        'vtopo': vtopo(),\
1604        'vis': vis(),\
1605        'info': exp_data(),\
1606        'multiinfo': multi_exp_data(),\
1607        'multistatus': multi_status(),\
1608        'image': image(),\
1609        'ns_image': ns_image(),\
1610        'status': status(),\
1611        'terminate': terminate(),\
1612        'spewlog': spew_log(),\
1613        'topdl_image': topdl_image(),\
[03e0290]1614    }
1615
1616operation = cmds.get(sys.argv[1], None)
1617if operation:
1618    del sys.argv[1]
1619    operation()
1620else:
[d15522f]1621    if sys.argv[1] == '--help':
[16a23a6]1622        sys.exit(\
[d15522f]1623'''Only context sensitive help is available.  For one of the commands:
1624
1625%s
1626
1627type
1628  %s command --help
1629
1630to get help, e.g., %s create --help
1631''' % (", ".join(cmds.keys()), sys.argv[0], sys.argv[0]))
1632    else:
[16a23a6]1633        sys.exit("Bad command: %s.  Valid ones are: %s" % \
1634                (sys.argv[1], ", ".join(cmds.keys())))
[03e0290]1635
Note: See TracBrowser for help on using the repository browser.