source: fedd/fedd_client.py @ 8bbc504

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

proper quoting on host and link names

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