source: fedd/fedd_client.py @ 4c931af

axis_examplecompt_changesinfo-opsversion-3.01version-3.02
Last change on this file since 4c931af was d055eb1, checked in by Ted Faber <faber@…>, 15 years ago

Ftopo function returns the virtual to physical host mapping. Slight refactoring to support this.

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