source: fedd/fedd_client.py @ 1e7f268

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

Add an explicit way to say "export this to everyone" (importall service)

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