source: fedd/fedd_client.py @ 9b8e269

axis_examplecompt_changesinfo-opsversion-3.01version-3.02
Last change on this file since 9b8e269 was 43197eb, checked in by Ted Faber <faber@…>, 15 years ago

better service handling including project_export psuedo service done more or less right- tested on dry runs

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