source: fedd/fedd_client.py @ 4adc697

version-1.30
Last change on this file since 4adc697 was d15522f, checked in by Ted Faber <faber@…>, 15 years ago

recognize a single --help

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