source: fedd/fedd_client.py @ f76d3d7

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

add status command

  • Property mode set to 100755
File size: 33.7 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4import os
5import pwd
6import tempfile
7import subprocess
8import re
9import xml.parsers.expat
10
11from federation import fedid, service_error
12from federation.util import fedd_ssl_context, pack_id, unpack_id
13from federation.remote_service import service_caller
14
15from optparse import OptionParser, OptionValueError
16
17
18class IDFormatException(RuntimeError): pass
19
20class access_method:
21    """Encapsulates an access method generically."""
22    (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey')
23    default_type = type_ssh
24    def __init__(self, buf=None, type=None, file=None):
25        self.buf = buf
26
27        if type != None: self.type = type
28        else: self.type = access_method.default_type
29
30        if file != None:
31            self.readfile(file)
32   
33    def readfile(self, file, type=None):
34        f = open(file, "r")
35        self.buf = f.read();
36        f.close()
37        if type == None:
38            if self.type == None:
39                self.type = access_method.default_type
40        else:
41            self.type = type;
42   
43class node_desc:
44    def __init__(self, image, hardware, count=1):
45        if getattr(image, "__iter__", None) == None:
46            if image == None: self.image = [ ]
47            else: self.image = [ image ]
48        else:
49            self.image = image
50
51        if getattr(hardware, "__iter__", None) == None: 
52            if hardware == None: self.hardware = [ ]
53            else: self.hardware = [ hardware ]
54        else:
55            self.hardware = hardware
56        if count != None: self.count = int(count)
57        else: self.count = 1
58
59class fedd_client_opts(OptionParser):
60    """Encapsulate option processing in this class, rather than in main"""
61    def __init__(self):
62        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
63                version="0.1")
64
65        self.add_option("-c","--cert", action="store", dest="cert",
66                type="string", help="my certificate file")
67        self.add_option("-d", "--debug", action="count", dest="debug", 
68                default=0, help="Set debug.  Repeat for more information")
69        self.add_option("-s", "--serializeOnly", action="store_true", 
70                dest="serialize_only", default=False,
71                help="Print the SOAP request that would be sent and exit")
72        self.add_option("-T","--trusted", action="store", dest="trusted",
73                type="string", help="Trusted certificates (required)")
74        self.add_option("-u", "--url", action="store", dest="url",
75                type="string",default="https://localhost:23235", 
76                help="URL to connect to (default %default)")
77        self.add_option("-x","--transport", action="store", type="choice",
78                choices=("xmlrpc", "soap"), default="soap",
79                help="Transport for request (xmlrpc|soap) (Default: %default)")
80        self.add_option("--trace", action="store_const", dest="tracefile", 
81                const=sys.stderr, help="Print SOAP exchange to stderr")
82
83class fedd_create_opts(fedd_client_opts):
84    def __init__(self, access_keys, add_key_callback=None, 
85            add_cert_callback=None):
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="output certificate file")
91        self.add_option("-F","--useFedid", action="store_true",
92                dest="use_fedid", default=False,
93                help="Use a fedid derived from my certificate as user identity")
94        self.add_option("-f", "--file", dest="file", 
95                help="experiment description file")
96        self.add_option("-p", "--project", action="store", dest="project", 
97                type="string",
98                help="Project to export from master")
99        if add_key_callback:
100            self.add_option("-k", "--ssh_key", action="callback",
101                    type="string", callback=add_key_callback,
102                    callback_args=(access_keys,),
103                    help="ssh key for access (can be supplied more than once")
104        if add_cert_callback:
105            self.add_option("-K", "--x509Key", action="callback",
106                    type="string", callback=add_cert_callback,
107                    callback_args=(access_keys,),
108                    help="X509 certificate for access " + \
109                        "(can be supplied more than once")
110        self.add_option("-m", "--master", dest="master",
111                help="Master testbed in the federation")
112        self.add_option("-U", "--username", action="store", dest="user",
113                type="string", help="Use this username instead of the uid")
114
115class fedd_split_opts(fedd_create_opts):
116    def __init__(self, access_keys, add_key_callback=None, 
117            add_cert_callback=None):
118        fedd_create_opts.__init__(self, access_keys, add_key_callback,
119                add_cert_callback)
120        self.add_option('-t','--fedkit', action='store_true', dest='fedkit',
121                default=False,
122                help="get output suitable for federation kit install")
123        self.add_option('-G','--gatewaykit', action='store_true',
124                dest='gatewaykit', default=False,
125                help="get output suitable for federation gateway kit install")
126
127
128class fedd_access_opts(fedd_create_opts):
129    def __init__(self, access_keys, node_descs, add_key_callback=None, 
130            add_cert_callback=None, add_node_callback=None):
131        fedd_create_opts.__init__(self, access_keys, add_key_callback,
132                add_cert_callback)
133        self.add_option("-a","--anonymous", action="store_true",
134                dest="anonymous", default=False,
135                help="Do not include a user in the request")
136        self.add_option("-l","--label", action="store", dest="label",
137                type="string", help="Label for output")
138        if add_node_callback:
139            self.add_option("-n", "--node", action="callback", type="string", 
140                    callback=add_node_callback, callback_args=(node_descs,),
141                    help="Node description: image:hardware[:count]")
142        self.add_option("-t", "--testbed", action="store", dest="testbed",
143                type="string",
144                help="Testbed identifier (URI) to contact (required)")
145
146class fedd_exp_data_opts(fedd_client_opts):
147    def __init__(self):
148        fedd_client_opts.__init__(self)
149        self.add_option("-e", "--experiment_cert", dest="exp_certfile",
150                type="string", help="output certificate file")
151        self.add_option("-E", "--experiment_name", dest="exp_name",
152                type="string", help="output certificate file")
153
154class fedd_image_opts(fedd_exp_data_opts):
155    def __init__(self):
156        fedd_exp_data_opts.__init__(self)
157        self.add_option("-o", "--output", dest="outfile", type="string",
158                help="output image file")
159        self.add_option("-F", "--format", dest="format", type="choice", 
160                choices=("jpg", "png", "dot", "svg"),
161                help="Output file format override")
162        self.add_option("-P", "--program", dest="neato", default=None,
163                type="string",
164                help="Program compatible with dot (from graphviz) used to " + \
165                        "render image")
166        self.add_option('-L', "--labels", dest='labels', action='store_true',
167                default=True, help='Label nodes and edges')
168        self.add_option('-A', "--no_labels", dest='labels',
169                default=True, action='store_false',
170                help='Label nodes and edges')
171
172class fedd_ns_image_opts(fedd_split_opts):
173    def __init__(self, access_keys, add_key_callback=None, 
174            add_cert_callback=None):
175        fedd_split_opts.__init__(self, access_keys, add_key_callback, 
176                add_cert_callback)
177        self.add_option("-o", "--output", dest="outfile", type="string",
178                help="output image file")
179        self.add_option("-Q", "--format", dest="format", type="choice", 
180                choices=("jpg", "png", "dot", "svg"),
181                help="Output file format override")
182        self.add_option("-P", "--program", dest="neato", default=None,
183                type="string",
184                help="Program compatible with dot (from graphviz) used to " + \
185                        "render image")
186        self.add_option('-L', "--labels", dest='labels', action='store_true',
187                default=True, help='Label nodes and edges')
188        self.add_option('-A', "--no_labels", dest='labels',
189                default=True, action='store_false',
190                help='Label nodes and edges')
191
192def exit_with_fault(dict, out=sys.stderr):
193    """ Print an error message and exit.
194
195    The dictionary contains the FeddFaultBody elements."""
196    codestr = ""
197
198    if dict.has_key('errstr'):
199        codestr = "Error: %s" % dict['errstr']
200
201    if dict.has_key('code'):
202        if len(codestr) > 0 : 
203            codestr += " (%d)" % dict['code']
204        else:
205            codestr = "Error Code: %d" % dict['code']
206
207    print>>out, codestr
208    print>>out, "Description: %s" % dict['desc']
209    sys.exit(dict.get('code', 20))
210# Base class that will do a the SOAP/XMLRPC exchange for a request.
211class fedd_rpc:
212    class RPCException:
213        def __init__(self, fb):
214            self.desc = fb.get('desc', None)
215            self.code = fb.get('code', -1)
216            self.errstr = fb.get('errstr', None)
217
218    def __init__(self, pre): 
219        """
220        Specialize the class for the pre method
221        """
222        self.RequestBody="%sRequestBody" % pre
223        self.ResponseBody="%sResponseBody" % pre
224        self.method = pre
225
226        self.caller = service_caller(self.method)
227        self.RPCException = fedd_rpc.RPCException
228
229
230    def add_ssh_key(self, option, opt_str, value, parser, access_keys):
231        try:
232            access_keys.append(access_method(file=value,
233                type=access_method.type_ssh))
234        except IOError, (errno, strerror):
235            raise OptionValueError("Cannot generate sshPubkey from %s: "\
236                    "%s (%d)" % (value,strerror,errno))
237
238    def add_x509_cert(self, option, opt_str, value, parser, access_keys):
239        try:
240            access_keys.append(access_method(file=value,
241                type=access_method.type_x509))
242        except IOError, (errno, strerror):
243            raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
244                    (value,strerror,errno))
245    def add_node_desc(self, option, opt_str, value, parser, node_descs):
246        def none_if_zero(x):
247            if len(x) > 0: return x
248            else: return None
249
250        params = map(none_if_zero, value.split(":"));
251       
252        if len(params) < 4 and len(params) > 1:
253            node_descs.append(node_desc(*params))
254        else:
255            raise OptionValueError("Bad node description: %s" % value)
256
257    def get_user_info(self, access_keys):
258        pw = pwd.getpwuid(os.getuid());
259        try_cert=None
260        user = None
261
262        if pw != None:
263            user = pw[0]
264            try_cert = "%s/.ssl/emulab.pem" % pw[5];
265            if not os.access(try_cert, os.R_OK):
266                try_cert = None
267            if len(access_keys) == 0:
268                for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub", 
269                        "%s/.ssh/identity.pub"]:
270                    try_key = k % pw[5];
271                    if os.access(try_key, os.R_OK):
272                        access_keys.append(access_method(file=try_key,
273                            type=access_method.type_ssh))
274                        break
275        return (user, try_cert)
276
277    def do_rpc(self, req_dict, url, transport, cert, trusted, tracefile=None,
278            serialize_only=False):
279        """
280        The work of sending and parsing the RPC as either XMLRPC or SOAP
281        """
282
283        context = None
284        while context == None:
285            try:
286                context = fedd_ssl_context(cert, trusted)
287            except Exception, e:
288                # Yes, doing this on message type is not ideal.  The string
289                # comes from OpenSSL, so check there is this stops working.
290                if str(e) == "bad decrypt": 
291                    print >>sys.stderr, "Bad Passphrase given."
292                else: raise
293
294        if transport == "soap":
295            if serialize_only:
296                print self.caller.serialize_soap(req_dict) 
297                sys.exit(0)
298            else:
299                try:
300                    resp = self.caller.call_soap_service(url, req_dict, 
301                            context=context, tracefile=tracefile)
302                except service_error, e:
303                    raise self.RPCException( {\
304                            'code': e.code, 
305                            'desc': e.desc, 
306                            'errstr': e.code_string()\
307                        })
308        elif transport == "xmlrpc":
309            if serialize_only:
310                ser = dumps((req_dict,))
311                print ser
312                sys.exit(0)
313            else:
314                try:
315                    resp = self.caller.call_xmlrpc_service(url, req_dict, 
316                            context=context, tracefile=tracefile)
317                except service_error, e:
318                    raise self.RPCException( {\
319                            'code': e.code, 
320                            'desc': e.desc, 
321                            'errstr': e.code_string()\
322                        })
323
324        else:
325            raise RuntimeError("Unknown RPC transport: %s" % transport)
326
327        if resp.has_key(self.ResponseBody):
328            return resp[self.ResponseBody]
329        else:
330            raise RuntimeError("No body in response??")
331
332# Querying experiment data follows the same control flow regardless of the
333# specific data retrieved.  This class encapsulates that control flow.
334class exp_data(fedd_rpc):
335    def __init__(self, op): 
336        """
337        Specialize the class for the type of data requested (op)
338        """
339
340        fedd_rpc.__init__(self, op)
341        if op =='Vtopo':
342            self.key="vtopo"
343            self.xml='experiment'
344        elif op == 'Vis':
345            self.key="vis"
346            self.xml='vis'
347        elif op == 'Info': pass
348        else:
349            raise TypeError("Bad op: %s" % op)
350
351    def print_xml(self, d, out=sys.stdout):
352        """
353        Print the retrieved data is a simple xml representation of the dict.
354        """
355        str = "<%s>\n" % self.xml
356        for t in ('node', 'lan'):
357            if d.has_key(t): 
358                for x in d[t]:
359                    str += "<%s>" % t
360                    for k in x.keys():
361                        str += "<%s>%s</%s>" % (k, x[k],k)
362                    str += "</%s>\n" % t
363        str+= "</%s>" % self.xml
364        print >>out, str
365
366    def __call__(self):
367        """
368        The control flow.  Compose the request and print the response.
369        """
370        # Process the options using the customized option parser defined above
371        parser = fedd_exp_data_opts()
372
373        (opts, args) = parser.parse_args()
374
375        if opts.trusted:
376            if ( not os.access(opts.trusted, os.R_OK) ) :
377                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
378
379        if opts.debug > 0: opts.tracefile=sys.stderr
380
381        (user, cert) = self.get_user_info([])
382
383        if opts.cert != None: cert = opts.cert
384
385        if cert == None:
386            sys.exit("No certificate given (--cert) or found")
387
388        if os.access(cert, os.R_OK):
389            fid = fedid(file=cert)
390        else:
391            sys.exit("Cannot read certificate (%s)" % cert)
392
393        if opts.exp_name and opts.exp_certfile:
394            sys.exit("Only one of --experiment_cert and " +\
395                    "--experiment_name permitted");
396
397        if opts.exp_certfile:
398            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
399
400        if opts.exp_name:
401            exp_id = { 'localname' : opts.exp_name }
402
403        req = { 'experiment': exp_id }
404
405        try:
406            resp_dict = self.do_rpc(req,
407                    opts.url, opts.transport, cert, opts.trusted, 
408                    serialize_only=opts.serialize_only,
409                    tracefile=opts.tracefile)
410        except self.RPCException, e:
411            exit_with_fault(\
412                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
413        except RuntimeError, e:
414            print e
415            sys.exit("Error processing RPC: %s" % e)
416
417        if getattr(self, 'key', None):
418            try:
419                if resp_dict.has_key(self.key):
420                    self.print_xml(resp_dict[self.key])
421            except RuntimeError, e:
422                sys.exit("Bad response. %s" % e.message)
423        else:
424            print resp_dict
425
426
427# Querying experiment data follows the same control flow regardless of the
428# specific data retrieved.  This class encapsulates that control flow.
429class simple_info(fedd_rpc):
430    def __init__(self): 
431        """
432        Specialize the class for the type of data requested (op)
433        """
434
435        fedd_rpc.__init__(self, 'Info')
436
437    def print_xml(self, d, out=sys.stdout):
438        """
439        Print the retrieved data is a simple xml representation of the dict.
440        """
441        str = "<%s>\n" % self.xml
442        for t in ('node', 'lan'):
443            if d.has_key(t): 
444                for x in d[t]:
445                    str += "<%s>" % t
446                    for k in x.keys():
447                        str += "<%s>%s</%s>" % (k, x[k],k)
448                    str += "</%s>\n" % t
449        str+= "</%s>" % self.xml
450        print >>out, str
451
452    def __call__(self):
453        """
454        The control flow.  Compose the request and print the response.
455        """
456        # Process the options using the customized option parser defined above
457        parser = fedd_exp_data_opts()
458
459        (opts, args) = parser.parse_args()
460
461        if opts.trusted:
462            if ( not os.access(opts.trusted, os.R_OK) ) :
463                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
464
465        if opts.debug > 0: opts.tracefile=sys.stderr
466
467        (user, cert) = self.get_user_info([])
468
469        if opts.cert != None: cert = opts.cert
470
471        if cert == None:
472            sys.exit("No certificate given (--cert) or found")
473
474        if os.access(cert, os.R_OK):
475            fid = fedid(file=cert)
476        else:
477            sys.exit("Cannot read certificate (%s)" % cert)
478
479        if opts.exp_name and opts.exp_certfile:
480            sys.exit("Only one of --experiment_cert and " +\
481                    "--experiment_name permitted");
482
483        if opts.exp_certfile:
484            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
485
486        if opts.exp_name:
487            exp_id = { 'localname' : opts.exp_name }
488
489        req = { 'experiment': exp_id }
490
491        status = "active"
492        try:
493            resp_dict = self.do_rpc(req,
494                    opts.url, opts.transport, cert, opts.trusted, 
495                    serialize_only=opts.serialize_only,
496                    tracefile=opts.tracefile)
497        except self.RPCException, e:
498            if e.code == 5:
499                status = "swapping"
500            elif e.code == 1:
501                status = "inactive"
502            else:
503                status = "unknown"
504        except RuntimeError, e:
505            print e
506            sys.exit("Error processing RPC: %s" % e)
507
508        print status
509
510
511class image(fedd_rpc):
512    def __init__(self, op): 
513        """
514        Null constructor
515        """
516
517        fedd_rpc.__init__(self, op)
518
519    def gen_image(self, d, file, fmt, neato, labels):
520
521        # Open up a temporary file for dot to turn into a visualization
522        try:
523            df, dotname = tempfile.mkstemp(prefix='fedd_client', suffix=".dot")
524            dotfile = os.fdopen(df, 'w')
525        except IOError:
526            raise service_error(service_error.internal,
527                    "Failed to open file in genviz")
528
529        if not neato:
530            for f in ['/usr/bin/neato', '/usr/local/bin/neato', 
531                    '/usr/bin/dot', '/usr/local/bin/dot']:
532                if os.access(f, os.X_OK):
533                    neato = f
534                    break
535            else:
536                sys.exit("Cannot find graph rendering program")
537
538        cmd = [neato, '-Gsplines=true']
539        if fmt != 'dot': cmd.append('-T%s' % fmt)
540        if file:
541            cmd.append('-o')
542            cmd.append(file)
543        cmd.append(dotname)
544
545        nodes = d.get('node',[])
546
547        if len(nodes) < 10: size = 5
548        elif len(nodes) < 50: size = 10
549        else: size = 18
550
551        print >>dotfile, "graph G {"
552        print >>dotfile, '\tgraph [size="%i,%i", ratio=fill];' % (size, size)
553        if labels:
554            print >>dotfile, '\tnode [fontname=arial,fontsize=9,label="\N"];'
555            print >>dotfile, '\tedge [fontname=arial,fontsize=9];\n'
556        else:
557            print >>dotfile, '\tnode [label=""];'
558
559        for n in d.get('node', []):
560            print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n['vname']
561            print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]'
562
563
564        lans = { }
565        links = { }
566
567        # Collect lan members (we have to draw extra stuff for these)
568        for n in d.get('lan', []):
569            v = n['vname']
570            m = n['member']
571            i = n['ip']
572            if m.find(':') != -1:
573                m = m[0:m.find(':')]
574            if lans.has_key(v):
575                lans[v].append((m, i))
576            elif links.has_key(v):
577                links[v].append((m, i))
578                if len(links[v]) > 2:
579                    lans[v] = links[v]
580                    del links[v]
581            else:
582                links[v] = [(m, i)]
583
584        # Encode the lans and the links
585        for l in lans.keys():
586            print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l
587            print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]'
588            for n in lans[l]:
589                if labels:
590                    print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \
591                            (l, n[0], n[1])
592                else:
593                    print >>dotfile, '\t"%s" -- "%s"' % (l, n[0])
594
595        for k, l in links.items():
596            if len(l) == 2:
597                if labels:
598                    print >>dotfile, \
599                            ('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \
600                            'headlabel="%s"]') % \
601                            (l[0][0], l[1][0], k, l[0][1], l[1][1])
602                else:
603                    print >>dotfile, '\t"%s" -- "%s" ' % (l[0][0], l[1][0])
604
605        print >>dotfile, "}"
606        dotfile.close()
607
608        # Redirect the drawing program stderr
609        dev_null = open("/dev/null", "w")
610        rv = subprocess.call(cmd, stderr=dev_null)
611        os.remove(dotname)
612        dev_null.close()
613        if rv != 0:
614            sys.exit("Error creating graph")
615
616
617
618    def __call__(self):
619        """
620        The control flow.  Compose the request and print the response.
621        """
622        # Process the options using the customized option parser defined above
623        parser = fedd_image_opts()
624
625        (opts, args) = parser.parse_args()
626
627        if opts.trusted:
628            if ( not os.access(opts.trusted, os.R_OK) ) :
629                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
630
631        if opts.debug > 0: opts.tracefile=sys.stderr
632
633        (user, cert) = self.get_user_info([])
634
635        if opts.cert != None: cert = opts.cert
636
637        if cert == None:
638            sys.exit("No certificate given (--cert) or found")
639
640        if os.access(cert, os.R_OK):
641            fid = fedid(file=cert)
642        else:
643            sys.exit("Cannot read certificate (%s)" % cert)
644
645        if opts.exp_name and opts.exp_certfile:
646            sys.exit("Only one of --experiment_cert and " +\
647                    "--experiment_name permitted");
648
649        if opts.exp_certfile:
650            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
651
652        if opts.exp_name:
653            exp_id = { 'localname' : opts.exp_name }
654
655        if opts.format and opts.outfile:
656            fmt = opts.format
657            file = opts.outfile
658        elif not opts.format and opts.outfile:
659            fmt = opts.outfile[-3:]
660            if fmt not in ("png", "jpg", "dot", "svg"):
661                sys.exit("Unexpected file type and no format specified")
662            file = opts.outfile
663        elif opts.format and not opts.outfile:
664            fmt = opts.format
665            file = None
666        else:
667            fmt="dot"
668            file = None
669
670
671        req = { 'experiment': exp_id }
672
673        try:
674            resp_dict = self.do_rpc(req,
675                    opts.url, opts.transport, cert, opts.trusted, 
676                    serialize_only=opts.serialize_only,
677                    tracefile=opts.tracefile)
678        except self.RPCException, e:
679            exit_with_fault(\
680                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
681        except RuntimeError, e:
682            print e
683            sys.exit("Error processing RPC: %s" % e)
684
685
686        if resp_dict.has_key('vtopo'):
687            self.gen_image(resp_dict['vtopo'], file, fmt, opts.neato,
688                    opts.labels)
689        else:
690            sys.exit("Bad response. %s" % e.message)
691
692class ns_image(image):
693    def __init__(self, op): 
694        """
695        Null constructor
696        """
697
698        image.__init__(self, op)
699
700    def generate_topo_dict(self, splitout):
701        class topo_parse:
702            """
703            Parse the topology XML and create the dats structure.  This class
704            is copied from federation.experiment_control.
705            """
706            def __init__(self):
707                # Typing of the subelements for data conversion
708                self.str_subelements = ('vname', 'vnode', 'ips', 'ip', 'member')
709                self.int_subelements = ( 'bandwidth',)
710                self.float_subelements = ( 'delay',)
711                # The final data structure
712                self.nodes = [ ]
713                self.lans =  [ ]
714                self.topo = { \
715                        'node': self.nodes,\
716                        'lan' : self.lans,\
717                    }
718                self.element = { }  # Current element being created
719                self.chars = ""     # Last text seen
720
721            def end_element(self, name):
722                # After each sub element the contents is added to the current
723                # element or to the appropriate list.
724                if name == 'node':
725                    self.nodes.append(self.element)
726                    self.element = { }
727                elif name == 'lan':
728                    self.lans.append(self.element)
729                    self.element = { }
730                elif name in self.str_subelements:
731                    self.element[name] = self.chars
732                    self.chars = ""
733                elif name in self.int_subelements:
734                    self.element[name] = int(self.chars)
735                    self.chars = ""
736                elif name in self.float_subelements:
737                    self.element[name] = float(self.chars)
738                    self.chars = ""
739
740            def found_chars(self, data):
741                self.chars += data.rstrip()
742
743
744        tp = topo_parse();
745        parser = xml.parsers.expat.ParserCreate()
746        parser.EndElementHandler = tp.end_element
747        parser.CharacterDataHandler = tp.found_chars
748
749        m = re.search('^#\s+Begin\s+Vtopo\s*$(.*)^#\s+End\s+Vtopo', splitout, 
750                re.MULTILINE | re.DOTALL)
751        if m:
752            str = m.group(1)
753        else:
754            sys.exit("Badly formatted split")
755
756        parser.Parse(str)
757
758        return tp.topo
759
760    def __call__(self):
761        """
762        The control flow.  Compose the request and print the response.
763        """
764        access_keys = []
765        # Process the options using the customized option parser defined above
766        parser = fedd_ns_image_opts(access_keys, self.add_ssh_key,
767                self.add_x509_cert)
768
769        (opts, args) = parser.parse_args()
770
771        if opts.trusted:
772            if ( not os.access(opts.trusted, os.R_OK) ) :
773                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
774
775        if opts.debug > 0: opts.tracefile=sys.stderr
776
777        (user, cert) = self.get_user_info([])
778
779        if opts.cert != None: cert = opts.cert
780
781        if cert == None:
782            sys.exit("No certificate given (--cert) or found")
783
784        if os.access(cert, os.R_OK):
785            fid = fedid(file=cert)
786        else:
787            sys.exit("Cannot read certificate (%s)" % cert)
788
789        if opts.file:
790            exp_desc = ""
791            try:
792                f = open(opts.file, 'r')
793                for line in f:
794                    exp_desc += line
795                f.close()
796            except IOError:
797                sys.exit("Cannot read description file (%s)" %opts.file)
798        else:
799            sys.exit("Must specify an experiment description (--file)")
800
801        if not opts.master:
802            opts.master="dummy"
803
804
805        req = {
806                'description': { 'ns2description': exp_desc },
807                'master': opts.master,
808                'include_fedkit': opts.fedkit,
809                'include_gatewaykit': opts.gatewaykit,
810                }
811
812
813        if opts.format and opts.outfile:
814            fmt = opts.format
815            file = opts.outfile
816        elif not opts.format and opts.outfile:
817            fmt = opts.outfile[-3:]
818            if fmt not in ("png", "jpg", "dot", "svg"):
819                sys.exit("Unexpected file type and no format specified")
820            file = opts.outfile
821        elif opts.format and not opts.outfile:
822            fmt = opts.format
823            file = None
824        else:
825            fmt="dot"
826            file = None
827
828
829        try:
830            resp_dict = self.do_rpc(req,
831                    opts.url, opts.transport, cert, opts.trusted, 
832                    serialize_only=opts.serialize_only,
833                    tracefile=opts.tracefile)
834        except self.RPCException, e:
835            exit_with_fault(\
836                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
837        except RuntimeError, e:
838            print e
839            sys.exit("Error processing RPC: %s" % e)
840
841
842        if resp_dict.has_key('output'):
843            if len(resp_dict['output']) < 1:
844                sys.exit("Bad response: could not split")
845            topo = self.generate_topo_dict(resp_dict['output'])
846            self.gen_image(topo, file, fmt, opts.neato, opts.labels)
847        else:
848            sys.exit("Bad response. %s" % e.message)
849
850class terminate(fedd_rpc):
851    def __init__(self): 
852        """
853        Termination request
854        """
855
856        fedd_rpc.__init__(self, "Terminate")
857
858    def __call__(self):
859        """
860        The control flow.  Compose the request and print the response.
861        """
862        # Process the options using the customized option parser defined above
863        parser = fedd_exp_data_opts()
864
865        (opts, args) = parser.parse_args()
866
867        (user, cert) = self.get_user_info([])
868        if opts.trusted:
869            if ( not os.access(opts.trusted, os.R_OK) ) :
870                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
871
872        if opts.debug > 0: opts.tracefile=sys.stderr
873
874        if opts.cert != None: cert = opts.cert
875
876        if cert == None:
877            sys.exit("No certificate given (--cert) or found")
878
879        if os.access(cert, os.R_OK):
880            fid = fedid(file=cert)
881        else:
882            sys.exit("Cannot read certificate (%s)" % cert)
883
884        if opts.exp_name and opts.exp_certfile:
885            sys.exit("Only one of --experiment_cert and " +\
886                    "--experiment_name permitted");
887
888        exp_id = None
889
890        if opts.exp_certfile:
891            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
892
893        if opts.exp_name:
894            exp_id = { 'localname' : opts.exp_name }
895
896        if not exp_id:
897            sys.exit("Must give one of --experiment_cert and " +\
898                    "--experiment_name");
899
900        req = { 'experiment': exp_id }
901
902        try:
903            resp_dict = self.do_rpc(req,
904                    opts.url, opts.transport, cert, opts.trusted, 
905                    serialize_only=opts.serialize_only,
906                    tracefile=opts.tracefile)
907        except self.RPCException, e:
908            exit_with_fault(\
909                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
910        except RuntimeError, e:
911            print e
912            sys.exit("Error processing RPC: %s" % e)
913
914        eid = resp_dict.get('experimentID', None)
915        if eid:
916            for id in eid:
917                for k in id.keys():
918                    if k == 'fedid': print "%s: %s" % (k,fedid(bits=id[k]))
919                    else: print "%s: %s" % (k, id[k])
920
921class create(fedd_rpc):
922    def __init__(self): 
923        fedd_rpc.__init__(self, "Create")
924    def __call__(self):
925        access_keys = []
926        # Process the options using the customized option parser defined above
927        parser = fedd_create_opts(access_keys, self.add_ssh_key,
928                self.add_x509_cert)
929
930        (opts, args) = parser.parse_args()
931
932        if opts.trusted:
933            if ( not os.access(opts.trusted, os.R_OK) ) :
934                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
935
936        if not opts.project :
937            parser.error('--project is required')
938
939        if opts.debug > 0: opts.tracefile=sys.stderr
940
941        (user, cert) = self.get_user_info(access_keys)
942
943        if opts.user: user = opts.user
944
945        if opts.cert != None: cert = opts.cert
946
947        if cert == None:
948            sys.exit("No certificate given (--cert) or found")
949
950        if os.access(cert, os.R_OK):
951            fid = fedid(file=cert)
952            if opts.use_fedid == True:
953                user = fid
954        else:
955            sys.exit("Cannot read certificate (%s)" % cert)
956
957        if opts.file:
958            exp_desc = ""
959            try:
960                f = open(opts.file, 'r')
961                for line in f:
962                    exp_desc += line
963                f.close()
964            except IOError:
965                sys.exit("Cannot read description file (%s)" %opts.file)
966        else:
967            sys.exit("Must specify an experiment description (--file)")
968
969        if not opts.master:
970            sys.exit("Must specify a master testbed (--master)")
971
972        out_certfile = opts.out_certfile
973
974        msg = {
975                'experimentdescription': { 'ns2description': exp_desc },
976                'master': opts.master,
977                'exportProject': { 'localname': opts.project },
978                'user' : [ {\
979                        'userID': pack_id(user), \
980                        'access': [ { a.type: a.buf } for a in access_keys]\
981                        } ]
982                }
983
984        if opts.exp_name:
985            msg['experimentID'] = { 'localname': opts.exp_name }
986
987        if opts.debug > 1: print >>sys.stderr, msg
988
989        try:
990            resp_dict = self.do_rpc(msg, 
991                    opts.url, opts.transport, cert, opts.trusted, 
992                    serialize_only=opts.serialize_only,
993                    tracefile=opts.tracefile)
994        except self.RPCException, e:
995            exit_with_fault(\
996                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
997        except RuntimeError, e:
998            sys.exit("Error processing RPC: %s" % e)
999
1000        if opts.debug > 1: print >>sys.stderr, resp_dict
1001
1002        ea = resp_dict.get('experimentAccess', None)
1003        if out_certfile and ea and ea.has_key('X509'):
1004            try:
1005                f = open(out_certfile, "w")
1006                print >>f, ea['X509']
1007                f.close()
1008            except IOError:
1009                sys.exit('Could not write to %s' %  out_certfile)
1010        eid = resp_dict.get('experimentID', None)
1011        if eid:
1012            for id in eid:
1013                for k in id.keys():
1014                    print "%s: %s" % (k, id[k])
1015
1016class split(fedd_rpc):
1017    def __init__(self): 
1018        fedd_rpc.__init__(self, "Ns2Split")
1019    def __call__(self):
1020        access_keys = []
1021        # Process the options using the customized option parser defined above
1022        parser = fedd_split_opts(access_keys, self.add_ssh_key,
1023                self.add_x509_cert)
1024
1025        (opts, args) = parser.parse_args()
1026
1027        if opts.trusted:
1028            if ( not os.access(opts.trusted, os.R_OK) ) :
1029                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1030
1031        if opts.debug > 0: opts.tracefile=sys.stderr
1032
1033        (user, cert) = self.get_user_info(access_keys)
1034
1035        if opts.cert != None: cert = opts.cert
1036
1037        if cert == None:
1038            sys.exit("No certificate given (--cert) or found")
1039
1040        if os.access(cert, os.R_OK):
1041            fid = fedid(file=cert)
1042            if opts.use_fedid == True:
1043                user = fid
1044        else:
1045            sys.exit("Cannot read certificate (%s)" % cert)
1046
1047        if opts.file:
1048            exp_desc = ""
1049            try:
1050                f = open(opts.file, 'r')
1051                for line in f:
1052                    exp_desc += line
1053                f.close()
1054            except IOError:
1055                sys.exit("Cannot read description file (%s)" %opts.file)
1056        else:
1057            sys.exit("Must specify an experiment description (--file)")
1058
1059        if not opts.master:
1060            sys.exit("Must specify a master testbed (--master)")
1061
1062        out_certfile = opts.out_certfile
1063
1064        msg = {
1065                'description': { 'ns2description': exp_desc },
1066                'master': opts.master,
1067                'include_fedkit': opts.fedkit,
1068                'include_gatewaykit': opts.gatewaykit,
1069                }
1070
1071        if opts.debug > 1: print >>sys.stderr, msg
1072
1073        try:
1074            resp_dict = self.do_rpc(msg, 
1075                    opts.url, opts.transport, cert, opts.trusted, 
1076                    serialize_only=opts.serialize_only,
1077                    tracefile=opts.tracefile)
1078        except self.RPCException, e:
1079            exit_with_fault(\
1080                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1081        except RuntimeError, e:
1082            sys.exit("Error processing RPC: %s" % e)
1083
1084        if opts.debug > 1: print >>sys.stderr, resp_dict
1085
1086        out = resp_dict.get('output', None)
1087
1088        for line in out.splitlines():
1089            print "%s" % line
1090
1091class access(fedd_rpc):
1092    def __init__(self):
1093        fedd_rpc.__init__(self, "RequestAccess")
1094
1095    def print_response_as_testbed(self, resp, label, out=sys.stdout):
1096        """Print the response as input to the splitter script"""
1097
1098        e = resp['emulab']
1099        p = e['project']
1100        fields = { 
1101                "Boss": e['boss'],
1102                "OpsNode": e['ops'],
1103                "Domain": e['domain'],
1104                "FileServer": e['fileServer'],
1105                "EventServer": e['eventServer'],
1106                "Project": unpack_id(p['name'])
1107                }
1108        if (label != None): print >> out, "[%s]" % label
1109
1110        for l, v in fields.iteritems():
1111            print >>out, "%s: %s" % (l, v)
1112
1113        for u in p['user']:
1114            print >>out, "User: %s" % unpack_id(u['userID'])
1115
1116        for a in e['fedAttr']:
1117            print >>out, "%s: %s" % (a['attribute'], a['value'])
1118
1119    def __call__(self):
1120        access_keys = []
1121        node_descs = []
1122        proj = None
1123
1124        # Process the options using the customized option parser defined above
1125        parser = fedd_access_opts(access_keys, node_descs, self.add_ssh_key,
1126                self.add_x509_cert, self.add_node_desc)
1127
1128        (opts, args) = parser.parse_args()
1129
1130        if opts.testbed == None:
1131            parser.error("--testbed is required")
1132
1133        if opts.trusted:
1134            if ( not os.access(opts.trusted, os.R_OK) ) :
1135                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1136
1137        if opts.debug > 0: opts.tracefile=sys.stderr
1138
1139        (user, cert) = self.get_user_info(access_keys)
1140
1141        if opts.user: user = opts.user
1142
1143        if opts.cert != None: cert = opts.cert
1144
1145        if cert == None:
1146            sys.exit("No certificate given (--cert) or found")
1147
1148        if os.access(cert, os.R_OK):
1149            fid = fedid(file=cert)
1150            if opts.use_fedid == True:
1151                user = fid
1152        else:
1153            sys.exit("Cannot read certificate (%s)" % cert)
1154
1155        msg = {
1156                'allocID': pack_id('test alloc'),
1157                'destinationTestbed': pack_id(opts.testbed),
1158                'serviceAccess' : [ { a.type: a.buf } for a in access_keys ],
1159                'createAccess' : [ { a.type: a.buf } for a in access_keys ],
1160                }
1161
1162        if len(node_descs) > 0:
1163            msg['resources'] = { 
1164                    'node': [ 
1165                        { 
1166                            'image':  n.image ,
1167                            'hardware':  n.hardware,
1168                            'count': n.count,
1169                        } for n in node_descs],
1170                    }
1171
1172        if opts.project != None:
1173            if not opts.anonymous and user != None:
1174                msg['project'] = {
1175                        'name': pack_id(opts.project),
1176                        'user': [ { 'userID': pack_id(user) } ],
1177                        }
1178            else:
1179                msg['project'] = { 'name': pack_id(opts.project) }
1180        else:
1181            if not opts.anonymous and user != None:
1182                msg['user'] = [ { 'userID': pack_id(user) } ]
1183            else:
1184                msg['user'] = [];
1185
1186        if opts.debug > 1: print >>sys.stderr, msg
1187
1188        try:
1189            resp_dict = self.do_rpc(msg, 
1190                    opts.url, opts.transport, cert, opts.trusted, 
1191                    serialize_only=opts.serialize_only,
1192                    tracefile=opts.tracefile)
1193        except self.RPCException, e:
1194            exit_with_fault(\
1195                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1196        except RuntimeError, e:
1197            sys.exit("Error processing RPC: %s" % e.message)
1198
1199        if opts.debug > 1: print >>sys.stderr, resp_dict
1200        self.print_response_as_testbed(resp_dict, opts.label)
1201
1202cmds = {\
1203        'create': create(),\
1204        'split': split(),\
1205        'access': access(),\
1206        'vtopo': exp_data('Vtopo'),\
1207        'vis': exp_data('Vis'),\
1208        'image': image('Vtopo'),\
1209        'ns_image': ns_image('Ns2Split'),\
1210        'info': exp_data('Info'),\
1211        'status': simple_info(),\
1212        'terminate': terminate(),\
1213    }
1214
1215operation = cmds.get(sys.argv[1], None)
1216if operation:
1217    del sys.argv[1]
1218    operation()
1219else:
1220    sys.exit("Bad command: %s.  Valid ones are: %s" % \
1221            (sys.argv[1], ", ".join(cmds.keys())))
1222
Note: See TracBrowser for help on using the repository browser.