source: fedd/fedd_client.py @ 7b26c39

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

Cleanup and split creation into two operations.

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