source: fedd/fedd_client.py @ 67c0e15

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

Add --pixels to specify the image size in pixels (they're square)

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