source: fedd/fedd_client.py @ a3ad8bd

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

checkpoint, adding new operation - prequel to splitting the create call to allow delegation

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