source: fedd/fedd_client.py @ d27fd76

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

When the loop is to exit in spewlog, don't sleep

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