source: fedd/fedd_client.py @ eab6ae1

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

Fix ns_image, which still believed that there was an Ns2Split interface. Now
it calls Ns2Topdl and draws the image from that. Internally, this means that
the ns_image class has become a subclass of topdl_image.

  • Property mode set to 100755
File size: 61.4 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4import os
5import pwd
6import tempfile
7import subprocess
8import re
9import xml.parsers.expat
10import time
11
12from federation import fedid, service_error
13from federation.util import fedd_ssl_context, pack_id, unpack_id
14from federation.remote_service import service_caller
15from federation import topdl
16
17from optparse import OptionParser, OptionValueError
18
19
20class IDFormatException(RuntimeError): pass
21
22class access_method:
23    """Encapsulates an access method generically."""
24    (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey')
25    default_type = type_ssh
26    def __init__(self, buf=None, type=None, file=None):
27        self.buf = buf
28
29        if type != None: self.type = type
30        else: self.type = access_method.default_type
31
32        if file != None:
33            self.readfile(file)
34   
35    def readfile(self, file, type=None):
36        f = open(file, "r")
37        self.buf = f.read();
38        f.close()
39        if type == None:
40            if self.type == None:
41                self.type = access_method.default_type
42        else:
43            self.type = type;
44   
45class node_desc:
46    def __init__(self, image, hardware, count=1):
47        if getattr(image, "__iter__", None) == None:
48            if image == None: self.image = [ ]
49            else: self.image = [ image ]
50        else:
51            self.image = image
52
53        if getattr(hardware, "__iter__", None) == None: 
54            if hardware == None: self.hardware = [ ]
55            else: self.hardware = [ hardware ]
56        else:
57            self.hardware = hardware
58        if count != None: self.count = int(count)
59        else: self.count = 1
60
61class fedd_client_opts(OptionParser):
62    """Encapsulate option processing in this class, rather than in main"""
63    def __init__(self):
64        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
65                version="0.1")
66
67        self.add_option("--cert", action="store", dest="cert",
68                type="string", help="my certificate file")
69        self.add_option( "--debug", action="count", dest="debug", 
70                default=0, help="Set debug.  Repeat for more information")
71        self.add_option("--serialize_only", action="store_true", 
72                dest="serialize_only", default=False,
73                help="Print the SOAP request that would be sent and exit")
74        self.add_option("--trusted", action="store", dest="trusted",
75                type="string", help="Trusted certificates (required)")
76        self.add_option("--url", action="store", dest="url",
77                type="string",default="https://localhost:23235", 
78                help="URL to connect to (default %default)")
79        self.add_option("--transport", action="store", type="choice",
80                choices=("xmlrpc", "soap"), default="soap",
81                help="Transport for request (xmlrpc|soap) (Default: %default)")
82        self.add_option("--trace", action="store_const", dest="tracefile", 
83                const=sys.stderr, help="Print SOAP exchange to stderr")
84class fedd_new_opts(fedd_client_opts):
85    def __init__(self):
86        fedd_client_opts.__init__(self)
87        self.add_option("--experiment_cert", dest="out_certfile",
88                type="string", help="output certificate file")
89        self.add_option("--experiment_name", dest="exp_name",
90                type="string", help="Suggested experiment name")
91
92class fedd_create_opts(fedd_new_opts):
93    def __init__(self):
94        fedd_new_opts.__init__(self)
95        self.add_option("--file", dest="file", 
96                help="experiment description file")
97        self.add_option("--project", action="store", dest="project", 
98                type="string",
99                help="Project to export from master")
100        self.add_option("--master", dest="master",
101                help="Master testbed in the federation (pseudo project export)")
102        self.add_option("--service", dest="service", action="append",
103                type="string", default=[],
104                help="Service description name:exporters:importers:attrs")
105
106class fedd_split_opts(fedd_create_opts):
107    def __init__(self ):
108        fedd_create_opts.__init__(self)
109        self.add_option('--fedkit', action='store_true', dest='fedkit',
110                default=False,
111                help="get output suitable for federation kit install")
112        self.add_option('--gatewaykit', action='store_true',
113                dest='gatewaykit', default=False,
114                help="get output suitable for federation gateway kit install")
115
116
117class fedd_access_opts(fedd_create_opts):
118    def __init__(self):
119        fedd_create_opts.__init__(self)
120        self.add_option("--label", action="store", dest="label",
121                type="string", help="Label for output")
122        if add_node_callback:
123            self.add_option("--node", action="callback", type="string", 
124                    callback=add_node_callback, callback_args=(node_descs,),
125                    help="Node description: image:hardware[:count]")
126        self.add_option("--testbed", action="store", dest="testbed",
127                type="string",
128                help="Testbed identifier (URI) to contact (required)")
129
130class fedd_exp_data_opts(fedd_client_opts):
131    def __init__(self):
132        fedd_client_opts.__init__(self)
133        self.add_option("--experiment_cert", dest="exp_certfile",
134                type="string", help="experiment certificate file")
135        self.add_option("--experiment_name", dest="exp_name",
136                type="string", help="human readable experiment name")
137        self.add_option("--data", dest="data", default=[],
138                action="append", type="choice",
139                choices=("id", "experimentdescription", "federant", "vtopo", 
140                    "vis", "log", "status"),
141                help="data to extract")
142
143class fedd_terminate_opts(fedd_exp_data_opts):
144    def __init__(self):
145        fedd_exp_data_opts.__init__(self)
146        self.add_option("--force", dest="force",
147                action="store_true", default=False,
148                help="Force termination if experiment is in strange state")
149        self.add_option("--logfile", dest="logfile", default=None,
150                help="File to write log to")
151        self.add_option("--print_log", dest="print_log", default=False,
152                action="store_true",
153                help="Print deallocation log to standard output")
154
155class fedd_multi_exp_data_opts(fedd_client_opts):
156    def __init__(self):
157        fedd_client_opts.__init__(self)
158        self.add_option("--data", dest="data", default=[],
159                action="append", type="choice",
160                choices=("id", "federant", "vtopo", "vis", "log", "status"),
161                help="data to extract")
162
163class fedd_spew_opts(fedd_client_opts):
164    def __init__(self):
165        fedd_client_opts.__init__(self)
166        self.add_option("--experiment_cert", dest="exp_certfile",
167                type="string", help="experiment name certificate file")
168        self.add_option("--experiment_name", dest="exp_name",
169                type="string", help="human readable experiment name")
170        self.add_option("--logfile", dest="logfile", default=None,
171                help="File to write log to")
172        self.add_option('--update_time', dest='update', type='int', default=10,
173                help='how often to update the printed log')
174
175class fedd_image_opts(fedd_exp_data_opts):
176    def __init__(self):
177        fedd_exp_data_opts.__init__(self)
178        self.add_option("--output", dest="outfile", type="string",
179                help="output image file")
180        self.add_option("--format", dest="format", type="choice", 
181                choices=("jpg", "png", "dot", "svg"),
182                help="Output file format override")
183        self.add_option("--program", dest="neato", default=None,
184                type="string",
185                help="Program compatible with dot (from graphviz) used to " + \
186                        "render image")
187        self.add_option("--labels", dest='labels', action='store_true',
188                default=True, help='Label nodes and edges')
189        self.add_option("--no_labels", dest='labels',
190                default=True, action='store_false',
191                help='Label nodes and edges')
192        self.add_option('--pixels', dest="pixels", default=None,
193                type="int",
194                help="Size of output in pixels (diagrams are square")
195
196class fedd_ns_image_opts(fedd_split_opts):
197    def __init__(self):
198        fedd_split_opts.__init__(self)
199        self.add_option("--output", dest="outfile", type="string",
200                help="output image file")
201        self.add_option("--format", dest="format", type="choice", 
202                choices=("jpg", "png", "dot", "svg"),
203                help="Output file format override")
204        self.add_option("--program", dest="neato", default=None,
205                type="string",
206                help="Program compatible with dot (from graphviz) used to " + \
207                        "render image")
208        self.add_option("--labels", dest='labels', action='store_true',
209                default=True, help='Label nodes and edges')
210        self.add_option("--no_labels", dest='labels',
211                default=True, action='store_false',
212                help='Label nodes and edges')
213        self.add_option('--pixels', dest="pixels", default=None,
214                type="int",
215                help="Size of output in pixels (diagrams are square")
216
217class fedd_start_opts(fedd_client_opts):
218    def __init__(self):
219        fedd_client_opts.__init__(self)
220        self.add_option("--file", dest="file", 
221                help="experiment description file")
222
223def exit_with_fault(dict, out=sys.stderr):
224    """ Print an error message and exit.
225
226    The dictionary contains the FeddFaultBody elements."""
227    codestr = ""
228
229    if dict.has_key('errstr'):
230        codestr = "Error: %s" % dict['errstr']
231
232    if dict.has_key('code'):
233        if len(codestr) > 0 : 
234            codestr += " (%d)" % dict['code']
235        else:
236            codestr = "Error Code: %d" % dict['code']
237
238    print>>out, codestr
239    print>>out, "Description: %s" % dict['desc']
240    sys.exit(dict.get('code', 20))
241# Base class for the various client operations.  It includes some commonly used
242# functions and classes the most important of which are the exception classes
243# and the service calling classes.
244class fedd_rpc:
245
246    class RPCException:
247        """
248        An error during the RPC exchange.  It unifies errors from both SOAP and
249        XMLPRC calls.
250        """
251        def __init__(self, fb):
252            self.desc = fb.get('desc', None)
253            self.code = fb.get('code', -1)
254            self.errstr = fb.get('errstr', None)
255
256    class caller(service_caller):
257        """
258        The caller is used by fedd_rpc.do_rpc to make the rpc call.  The extra
259        stashed information is used to parse responses a little.
260        """
261        def __init__(self, pre):
262            self.ResponseBody="%sResponseBody" % pre
263            self.method = pre
264            service_caller.__init__(self, self.method)
265
266    def __init__(self): 
267        """
268        Specialize the class for the pre method
269        """
270        self.caller = fedd_rpc.caller
271        self.RPCException = fedd_rpc.RPCException
272
273
274    def add_node_desc(self, option, opt_str, value, parser, node_descs):
275        def none_if_zero(x):
276            if len(x) > 0: return x
277            else: return None
278
279        params = map(none_if_zero, value.split(":"));
280       
281        if len(params) < 4 and len(params) > 1:
282            node_descs.append(node_desc(*params))
283        else:
284            raise OptionValueError("Bad node description: %s" % value)
285
286    def get_user_info(self):
287        pw = pwd.getpwuid(os.getuid());
288        try_cert=None
289        user = None
290
291        if pw != None:
292            user = pw[0]
293            try_cert = "%s/.ssl/emulab.pem" % pw[5];
294            if not os.access(try_cert, os.R_OK):
295                try_cert = None
296        return (user, try_cert)
297
298    def do_rpc(self, req_dict, url, transport, cert, trusted, tracefile=None,
299            serialize_only=False, caller=None):
300        """
301        The work of sending and parsing the RPC as either XMLRPC or SOAP
302        """
303
304        if caller is None: 
305            raise RuntimeError("Must provide caller to do_rpc")
306
307        context = None
308        while context == None:
309            try:
310                context = fedd_ssl_context(cert, trusted)
311            except Exception, e:
312                # Yes, doing this on message type is not ideal.  The string
313                # comes from OpenSSL, so check there is this stops working.
314                if str(e) == "bad decrypt": 
315                    print >>sys.stderr, "Bad Passphrase given."
316                else: raise
317
318        if transport == "soap":
319            if serialize_only:
320                print caller.serialize_soap(req_dict) 
321                return { }
322            else:
323                try:
324                    resp = caller.call_soap_service(url, req_dict, 
325                            context=context, tracefile=tracefile)
326                except service_error, e:
327                    raise self.RPCException( {\
328                            'code': e.code, 
329                            'desc': e.desc, 
330                            'errstr': e.code_string()\
331                        })
332        elif transport == "xmlrpc":
333            if serialize_only:
334                ser = dumps((req_dict,))
335                print ser
336                return { }
337            else:
338                try:
339                    resp = caller.call_xmlrpc_service(url, req_dict, 
340                            context=context, tracefile=tracefile)
341                except service_error, e:
342                    raise self.RPCException( {\
343                            'code': e.code, 
344                            'desc': e.desc, 
345                            'errstr': e.code_string()\
346                        })
347
348        else:
349            raise RuntimeError("Unknown RPC transport: %s" % transport)
350
351        if resp.has_key(caller.ResponseBody):
352            return resp[caller.ResponseBody]
353        else:
354            raise RuntimeError("No body in response??")
355
356class exp_data_base(fedd_rpc):
357    def __init__(self):
358        """
359        Init the various conversions
360        """
361
362        fedd_rpc.__init__(self)
363        # List of things one could ask for and what formatting routine is
364        # called.
365        self.params = {
366                'vis': ('vis', self.print_vis_or_vtopo('vis')),
367                'vtopo': ('vtopo', self.print_vis_or_vtopo('vtopo')),
368                'federant': ('federant', self.print_xml),
369                'experimentdescription': \
370                        ('experimentdescription', self.print_xml),
371                'id': ('experimentID', self.print_id),
372                'status': ('experimentStatus', self.print_string),
373                'log': ('allocationLog', self.print_string),
374                'access': ('experimentAccess', self.print_string),
375            }
376
377    # Utility functions
378    def print_string(self, d, out=sys.stdout):
379        print >>out, d
380
381    def print_id(self, d, out=sys.stdout):
382        if d:
383            for id in d:
384                for k in id.keys():
385                    print >>out, "%s: %s" % (k, id[k])
386
387    def print_xml(self, d, out=sys.stdout):
388        """
389        Very simple ugly xml formatter of the kinds of dicts that come back
390        from services.
391        """
392        if isinstance(d, dict):
393            for k, v in d.items():
394                print >>out, "<%s>" % k
395                self.print_xml(v, out)
396                print >>out, "</%s>" % k
397        elif isinstance(d, list):
398            for x in d:
399                self.print_xml(x, out)
400        else:
401            print >>out, d
402
403
404    class print_vis_or_vtopo:
405        """
406        Print the retrieved data is a simple xml representation of the dict.
407        """
408        def __init__(self, top):
409            self.xml = top
410
411        def __call__(self, d, out=sys.stdout):
412            str = "<%s>\n" % self.xml
413            for t in ('node', 'lan'):
414                if d.has_key(t): 
415                    for x in d[t]:
416                        str += "<%s>" % t
417                        for k in x.keys():
418                            str += "<%s>%s</%s>" % (k, x[k],k)
419                        str += "</%s>\n" % t
420            str+= "</%s>" % self.xml
421            print >>out, str
422
423       
424    def __call__(self):
425        """
426        The control flow.  Compose the request and print the response.
427        """
428        # Process the options using the customized option parser defined above
429        parser = fedd_exp_data_opts()
430
431        (opts, args) = parser.parse_args()
432
433        if opts.trusted:
434            if ( not os.access(opts.trusted, os.R_OK) ) :
435                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
436
437        if opts.debug > 0: opts.tracefile=sys.stderr
438
439        (user, cert) = self.get_user_info()
440
441        if opts.cert != None: cert = opts.cert
442
443        if cert == None:
444            sys.exit("No certificate given (--cert) or found")
445
446        if os.access(cert, os.R_OK):
447            fid = fedid(file=cert)
448        else:
449            sys.exit("Cannot read certificate (%s)" % cert)
450
451        if opts.exp_name and opts.exp_certfile:
452            sys.exit("Only one of --experiment_cert and " +\
453                    "--experiment_name permitted");
454
455        exp_id = None
456        if opts.exp_certfile:
457            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
458
459        if opts.exp_name:
460            exp_id = { 'localname' : opts.exp_name }
461
462        if not exp_id:
463            sys.exit("specify one of --experiment_cert and --experiment_name")
464
465
466        req = { 'experiment': exp_id }
467
468        try:
469            resp_dict = self.do_rpc(req,
470                    opts.url, opts.transport, cert, opts.trusted, 
471                    serialize_only=opts.serialize_only,
472                    tracefile=opts.tracefile,
473                    caller=self.caller('Info'))
474        except self.RPCException, e:
475            exit_with_fault(\
476                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
477        except RuntimeError, e:
478            sys.exit("Error processing RPC: %s" % e)
479
480        return (resp_dict, opts)
481
482
483# Querying experiment data follows the same control flow regardless of the
484# specific data retrieved.  This class encapsulates that control flow.
485class exp_data(exp_data_base):
486    def __init__(self): 
487        exp_data_base.__init__(self)
488
489    def __call__(self):
490        """
491        The control flow.  Compose the request and print the response.
492        """
493
494        resp_dict, opts = exp_data_base.__call__(self)
495
496        for d in opts.data:
497            key, output = self.params[d]
498            try:
499                if resp_dict.has_key(key):
500                    output(resp_dict[key])
501            except RuntimeError, e:
502                sys.exit("Bad response. %s" % e.message)
503
504class ftopo(exp_data_base):
505    """
506    ftopo returns the mapping from logical name to local hostname
507    """
508    def __init__(self):
509        exp_data_base.__init__(self)
510    def __call__(self):
511        sys.argv.append('--data=experimentdescription')
512        resp, opts = exp_data_base.__call__(self)
513        if 'experimentdescription' in resp and \
514                'topdldescription' in resp['experimentdescription']:
515            top = \
516                topdl.Topology(\
517                **resp['experimentdescription']['topdldescription'])
518
519            for e in [ e for e in top.elements \
520                    if isinstance(e, topdl.Computer)]:
521                hn = e.get_attribute('hostname')
522                tb = e.get_attribute('testbed')
523                if hn and tb:
524                    print ":".join([",".join(e.name), hn, tb])
525
526
527
528
529class vtopo(exp_data_base):
530    """
531    vtopo is just an info --data=vtopo request, so this adds that parameter to
532    the arguments and executes exp_info when called.
533    """
534    def __init__(self):
535        exp_data_base.__init__(self)
536    def __call__(self):
537        sys.argv.append('--data=vtopo')
538        resp, opts = exp_data_base.__call__(self)
539        if 'vtopo' in resp:
540            self.print_vis_or_vtopo('vtopo')(resp['vtopo'])
541
542
543class vis(exp_data_base):
544    """
545    vis is just an info --data=vis request, so this adds that parameter to
546    the arguments and executes exp_info when called.
547    """
548    def __init__(self):
549        exp_data_base.__init__(self)
550    def __call__(self):
551        sys.argv.append('--data=vis')
552        resp, opts = exp_data_base.__call__(self)
553        if 'vis' in resp:
554            self.print_vis_or_vtopo('vis')(resp['vis'])
555
556class status(exp_data_base):
557    """
558    status is just an info --data=status request, so this adds that parameter
559    to the arguments and executes exp_info when called.
560    """
561    def __init__(self):
562        exp_data_base.__init__(self)
563    def __call__(self):
564        sys.argv.append('--data=status')
565        resp, opts = exp_data_base.__call__(self)
566        if 'experimentStatus' in resp:
567            self.print_string(resp['experimentStatus'])
568
569class multi_exp_data(exp_data_base):
570    def __init__(self): 
571        exp_data_base.__init__(self)
572
573
574    def __call__(self):
575        """
576        The control flow.  Compose the request and print the response.
577        """
578        # Process the options using the customized option parser defined above
579        parser = fedd_multi_exp_data_opts()
580
581        (opts, args) = parser.parse_args()
582
583        if opts.trusted:
584            if ( not os.access(opts.trusted, os.R_OK) ) :
585                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
586
587        if opts.debug > 0: opts.tracefile=sys.stderr
588
589        (user, cert) = self.get_user_info()
590
591        if opts.cert != None: cert = opts.cert
592
593        if cert == None:
594            sys.exit("No certificate given (--cert) or found")
595
596        if os.access(cert, os.R_OK):
597            fid = fedid(file=cert)
598        else:
599            sys.exit("Cannot read certificate (%s)" % cert)
600
601        req = { }
602
603        try:
604            resp_dict = self.do_rpc(req,
605                    opts.url, opts.transport, cert, opts.trusted, 
606                    serialize_only=opts.serialize_only,
607                    tracefile=opts.tracefile,
608                    caller=self.caller('MultiInfo'))
609        except self.RPCException, e:
610            exit_with_fault(\
611                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
612        except RuntimeError, e:
613            sys.exit("Error processing RPC: %s" % e)
614
615        exps = resp_dict.get('info', [])
616        if exps:
617            print '---'
618            for exp in exps:
619                for d in opts.data:
620                    key, output = self.params[d]
621                    try:
622                        if exp.has_key(key):
623                            output(exp[key])
624                    except RuntimeError, e:
625                        sys.exit("Bad response. %s" % e.message)
626                print '---'
627
628
629class multi_status(exp_data_base):
630    def __init__(self): 
631        exp_data_base.__init__(self)
632
633
634    def __call__(self):
635        """
636        The control flow.  Compose the request and print the response.
637        """
638        # Process the options using the customized option parser defined above
639        parser = fedd_client_opts()
640
641        (opts, args) = parser.parse_args()
642
643        if opts.trusted:
644            if ( not os.access(opts.trusted, os.R_OK) ) :
645                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
646
647        if opts.debug > 0: opts.tracefile=sys.stderr
648
649        (user, cert) = self.get_user_info()
650
651        if opts.cert != None: cert = opts.cert
652
653        if cert == None:
654            sys.exit("No certificate given (--cert) or found")
655
656        if os.access(cert, os.R_OK):
657            fid = fedid(file=cert)
658        else:
659            sys.exit("Cannot read certificate (%s)" % cert)
660
661        req = { }
662
663        try:
664            resp_dict = self.do_rpc(req,
665                    opts.url, opts.transport, cert, opts.trusted, 
666                    serialize_only=opts.serialize_only,
667                    tracefile=opts.tracefile,
668                    caller=self.caller('MultiInfo'))
669        except self.RPCException, e:
670            exit_with_fault(\
671                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
672        except RuntimeError, e:
673            sys.exit("Error processing RPC: %s" % e)
674
675        for exp in resp_dict.get('info', []):
676            out = []
677            for eid in exp.get('experimentID', []):
678                if eid.has_key('localname'):
679                    out.append(eid['localname'])
680                    break
681            else:
682                out.append("")
683            for eid in exp.get('experimentID', []):
684                if eid.has_key('fedid'):
685                    out.append("%s" % eid['fedid'])
686                    break
687            else:
688                out.append("")
689
690            out.append(exp.get('experimentStatus', ""))
691
692            for f in exp.get('federant', []):
693                if f.get('master', False):
694                    em = f.get('emulab', None)
695                    if em:
696                        project = em.get('project', None)
697                        if project:
698                            tb = project.get('testbed', None)
699                            if tb and tb.has_key('localname'):
700                                out.append(tb['localname'])
701                            else:
702                                out.append("")
703                            pn = project.get('name', None)
704                            if pn and pn.has_key('localname'):
705                                out.append(pn['localname'])
706                            else:
707                                out.append("")
708                        else:
709                            out.extend(("", ""))
710                    else:
711                        out.extend(("", ""))
712                    break
713            else:
714                out.extend(("",""))
715
716            print ":".join(out)
717
718class image(fedd_rpc):
719    def __init__(self): 
720        """
721        Null constructor
722        """
723
724        fedd_rpc.__init__(self)
725
726    @staticmethod
727    def gen_dot_topo(d, labels, dotfile):
728        lans = { }
729        links = { }
730
731        for n in d.get('node', []):
732            print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n['vname']
733            print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]'
734
735        # Collect lan members (we have to draw extra stuff for these)
736        for n in d.get('lan', []):
737            v = n['vname']
738            m = n['member']
739            i = n['ip']
740            if m.find(':') != -1:
741                m = m[0:m.find(':')]
742            if lans.has_key(v):
743                lans[v].append((m, i))
744            elif links.has_key(v):
745                links[v].append((m, i))
746                if len(links[v]) > 2:
747                    lans[v] = links[v]
748                    del links[v]
749            else:
750                links[v] = [(m, i)]
751
752        # Encode the lans and the links
753        for l in lans.keys():
754            print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l
755            print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]'
756            for n in lans[l]:
757                if labels:
758                    print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \
759                            (l, n[0], n[1])
760                else:
761                    print >>dotfile, '\t"%s" -- "%s"' % (l, n[0])
762
763        for k, l in links.items():
764            if len(l) == 2:
765                if labels:
766                    print >>dotfile, \
767                            ('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \
768                            'headlabel="%s"]') % \
769                            (l[0][0], l[1][0], k, l[0][1], l[1][1])
770                else:
771                    print >>dotfile, '\t"%s" -- "%s" ' % (l[0][0], l[1][0])
772
773
774    def gen_image(self, d, nodes, file, fmt, neato, labels, pix=None):
775
776        # Open up a temporary file for dot to turn into a visualization
777        try:
778            df, dotname = tempfile.mkstemp(prefix='fedd_client', suffix=".dot")
779            dotfile = os.fdopen(df, 'w')
780        except IOError:
781            raise service_error(service_error.internal,
782                    "Failed to open file in genviz")
783
784        if not neato:
785            for f in ['/usr/bin/neato', '/usr/local/bin/neato', 
786                    '/usr/bin/dot', '/usr/local/bin/dot']:
787                if os.access(f, os.X_OK):
788                    neato = f
789                    break
790            else:
791                sys.exit("Cannot find graph rendering program")
792
793        cmd = [neato, '-Gsplines=true']
794        if fmt != 'dot': cmd.append('-T%s' % fmt)
795        if file:
796            cmd.append('-o')
797            cmd.append(file)
798        cmd.append(dotname)
799
800        #nodes = d.get('node',[])
801
802        if nodes < 10: size = 5
803        elif nodes < 50: size = 10
804        else: size = 18
805
806        if pix:
807            dpi = pix / size
808        else:
809            dpi = None
810
811
812        print >>dotfile, "graph G {"
813        if dpi:
814            print >>dotfile, '\tgraph [size="%i,%i", dpi="%i", ratio=fill];' \
815                    % (size, size, dpi)
816        else:
817            print >>dotfile, '\tgraph [size="%i,%i", ratio=fill];' \
818                    % (size, size)
819
820        if labels:
821            print >>dotfile, '\tnode [fontname=arial,fontsize=9,label="\N"];'
822            print >>dotfile, '\tedge [fontname=arial,fontsize=9];\n'
823        else:
824            print >>dotfile, '\tnode [label=""];'
825
826
827        self.gen_dot_topo(d, labels, dotfile)
828        print >>dotfile, "}"
829        dotfile.close()
830
831        # Redirect the drawing program stderr
832        dev_null = open("/dev/null", "w")
833        rv = subprocess.call(cmd, stderr=dev_null)
834        os.remove(dotname)
835        dev_null.close()
836        if rv != 0:
837            sys.exit("Error creating graph")
838
839
840
841    def __call__(self):
842        """
843        The control flow.  Compose the request and print the response.
844        """
845        # Process the options using the customized option parser defined above
846        parser = fedd_image_opts()
847
848        (opts, args) = parser.parse_args()
849
850        if opts.trusted:
851            if ( not os.access(opts.trusted, os.R_OK) ) :
852                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
853
854        if opts.debug > 0: opts.tracefile=sys.stderr
855
856        (user, cert) = self.get_user_info()
857
858        if opts.cert != None: cert = opts.cert
859
860        if cert == None:
861            sys.exit("No certificate given (--cert) or found")
862
863        if os.access(cert, os.R_OK):
864            fid = fedid(file=cert)
865        else:
866            sys.exit("Cannot read certificate (%s)" % cert)
867
868        if opts.exp_name and opts.exp_certfile:
869            sys.exit("Only one of --experiment_cert and " +\
870                    "--experiment_name permitted");
871
872        if opts.exp_certfile:
873            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
874
875        if opts.exp_name:
876            exp_id = { 'localname' : opts.exp_name }
877
878        if opts.format and opts.outfile:
879            fmt = opts.format
880            file = opts.outfile
881        elif not opts.format and opts.outfile:
882            fmt = opts.outfile[-3:]
883            if fmt not in ("png", "jpg", "dot", "svg"):
884                sys.exit("Unexpected file type and no format specified")
885            file = opts.outfile
886        elif opts.format and not opts.outfile:
887            fmt = opts.format
888            file = None
889        else:
890            fmt="dot"
891            file = None
892
893
894        req = { 'experiment': exp_id }
895
896        try:
897            resp_dict = self.do_rpc(req,
898                    opts.url, opts.transport, cert, opts.trusted, 
899                    serialize_only=opts.serialize_only,
900                    tracefile=opts.tracefile,
901                    caller=self.caller('Vtopo'))
902        except self.RPCException, e:
903            exit_with_fault(\
904                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
905        except RuntimeError, e:
906            sys.exit("Error processing RPC: %s" % e)
907
908
909        if resp_dict.has_key('vtopo'):
910            self.gen_image(resp_dict['vtopo'], 
911                    len(resp_dict['vtopo'].get('node', [])),
912                    file, fmt, opts.neato, opts.labels, opts.pixels)
913        elif opts.serialze_only:
914            sys.exit(0)
915        else:
916            sys.exit("Bad response. %s" % e.message)
917
918class topdl_image(image):
919    def __init__(self): 
920        """
921        Null constructor
922        """
923
924        image.__init__(self)
925
926    @staticmethod
927    def gen_dot_topo(t, labels, dotfile):
928        lans = [ s for s in t.substrates if len(s.interfaces) != 2]
929        links = [ s for s in t.substrates if len(s.interfaces) == 2]
930
931        i = 0
932        for n in t.elements:
933            if n.name:
934                print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n.name
935            else:
936                print >>dotfile, \
937                        '\t"unnamed_node%d" [shape=box,style=filled,\\' % i
938                i += 1
939            print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]'
940
941        # Encode the lans and the links
942        for l in lans:
943            print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l.name
944            print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]'
945            for i in l.interfaces:
946                ip = i.get_attribute('ip4_address')
947                if labels and ip:
948                    print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \
949                            (l.name, i.element.name, ip)
950                else:
951                    print >>dotfile, '\t"%s" -- "%s"' % \
952                            (l.name, i.element.name)
953
954        for l in links:
955            s, d = l.interfaces[0:2] 
956            sip = s.get_attribute('ip4_address')
957            dip = d.get_attribute('ip4_address')
958            if labels and sip and dip:
959                print >>dotfile, \
960                        ('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \
961                        'headlabel="%s"]') % \
962                        (s.element.name, d.element.name, l.name,
963                            sip, dip)
964            else:
965                print >>dotfile, '\t"%s" -- "%s" ' % \
966                        (s.element.name, d.element.name)
967    def __call__(self):
968        """
969        The control flow.  Compose the request and print the response.
970        """
971        # Process the options using the customized option parser defined above
972        parser = fedd_ns_image_opts()
973
974        (opts, args) = parser.parse_args()
975
976        if opts.trusted:
977            if ( not os.access(opts.trusted, os.R_OK) ) :
978                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
979
980        if opts.debug > 0: opts.tracefile=sys.stderr
981
982        (user, cert) = self.get_user_info()
983
984        if opts.cert != None: cert = opts.cert
985
986        if cert == None:
987            sys.exit("No certificate given (--cert) or found")
988
989        if os.access(cert, os.R_OK):
990            fid = fedid(file=cert)
991        else:
992            sys.exit("Cannot read certificate (%s)" % cert)
993
994        if opts.file:
995            exp_desc = ""
996            try:
997                top = topdl.topology_from_xml(filename=opts.file, 
998                        top="experiment")
999            except IOError:
1000                sys.exit("Cannot read description file (%s)" % opts.file)
1001        else:
1002            sys.exit("Must specify an experiment description (--file)")
1003
1004        if not opts.master:
1005            opts.master="dummy"
1006
1007
1008        if opts.format and opts.outfile:
1009            fmt = opts.format
1010            file = opts.outfile
1011        elif not opts.format and opts.outfile:
1012            fmt = opts.outfile[-3:]
1013            if fmt not in ("png", "jpg", "dot", "svg"):
1014                sys.exit("Unexpected file type and no format specified")
1015            file = opts.outfile
1016        elif opts.format and not opts.outfile:
1017            fmt = opts.format
1018            file = None
1019        else:
1020            fmt="dot"
1021            file = None
1022
1023        self.gen_image(top, len(top.elements), file, fmt, opts.neato, 
1024                opts.labels, opts.pixels)
1025
1026class ns_image(topdl_image):
1027    def __init__(self): 
1028        """
1029        Null constructor
1030        """
1031
1032        topdl_image.__init__(self)
1033
1034    def __call__(self):
1035        """
1036        The control flow.  Compose the request and print the response.
1037        """
1038        # Process the options using the customized option parser defined above
1039        parser = fedd_ns_image_opts()
1040
1041        (opts, args) = parser.parse_args()
1042
1043        if opts.trusted:
1044            if ( not os.access(opts.trusted, os.R_OK) ) :
1045                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1046
1047        if opts.debug > 0: opts.tracefile=sys.stderr
1048
1049        (user, cert) = self.get_user_info()
1050
1051        if opts.cert != None: cert = opts.cert
1052
1053        if cert == None:
1054            sys.exit("No certificate given (--cert) or found")
1055
1056        if os.access(cert, os.R_OK):
1057            fid = fedid(file=cert)
1058        else:
1059            sys.exit("Cannot read certificate (%s)" % cert)
1060
1061        if opts.file:
1062            exp_desc = ""
1063            try:
1064                f = open(opts.file, 'r')
1065                for line in f:
1066                    exp_desc += line
1067                f.close()
1068            except IOError:
1069                sys.exit("Cannot read description file (%s)" %opts.file)
1070        else:
1071            sys.exit("Must specify an experiment description (--file)")
1072
1073        if not opts.master:
1074            opts.master="dummy"
1075
1076        req = { 'description': { 'ns2description': exp_desc }, }
1077
1078        if opts.format and opts.outfile:
1079            fmt = opts.format
1080            file = opts.outfile
1081        elif not opts.format and opts.outfile:
1082            fmt = opts.outfile[-3:]
1083            if fmt not in ("png", "jpg", "dot", "svg"):
1084                sys.exit("Unexpected file type and no format specified")
1085            file = opts.outfile
1086        elif opts.format and not opts.outfile:
1087            fmt = opts.format
1088            file = None
1089        else:
1090            fmt="dot"
1091            file = None
1092
1093        try:
1094            resp_dict = self.do_rpc(req,
1095                    opts.url, opts.transport, cert, opts.trusted, 
1096                    serialize_only=opts.serialize_only,
1097                    tracefile=opts.tracefile,
1098                    caller=self.caller('Ns2Topdl'))
1099        except self.RPCException, e:
1100            exit_with_fault(\
1101                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1102        except RuntimeError, e:
1103            sys.exit("Error processing RPC: %s" % e)
1104
1105
1106        if 'experimentdescription' in resp_dict:
1107            if 'topdldescription' in resp_dict['experimentdescription']:
1108                exp = resp_dict['experimentdescription']['topdldescription']
1109                top = topdl.Topology(**exp)
1110                self.gen_image(top, len(top.elements), file, fmt, opts.neato, 
1111                        opts.labels, opts.pixels)
1112            else:
1113                sys.exit("Bad response: could not translate")
1114        elif opts.serialze_only:
1115            sys.exit(0)
1116        else:
1117            sys.exit("Bad response. %s" % e.message)
1118
1119class terminate(fedd_rpc):
1120    def __init__(self): 
1121        """
1122        Termination request
1123        """
1124
1125        fedd_rpc.__init__(self)
1126
1127    def __call__(self):
1128        """
1129        The control flow.  Compose the request and print the response.
1130        """
1131        # Process the options using the customized option parser defined above
1132        parser = fedd_terminate_opts()
1133
1134        (opts, args) = parser.parse_args()
1135
1136        (user, cert) = self.get_user_info()
1137        if opts.trusted:
1138            if ( not os.access(opts.trusted, os.R_OK) ) :
1139                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1140
1141        if opts.debug > 0: opts.tracefile=sys.stderr
1142
1143        if opts.cert != None: cert = opts.cert
1144
1145        if cert == None:
1146            sys.exit("No certificate given (--cert) or found")
1147
1148        if os.access(cert, os.R_OK):
1149            fid = fedid(file=cert)
1150        else:
1151            sys.exit("Cannot read certificate (%s)" % cert)
1152
1153        if opts.exp_name and opts.exp_certfile:
1154            sys.exit("Only one of --experiment_cert and " +\
1155                    "--experiment_name permitted")
1156
1157        if opts.print_log and opts.logfile:
1158            sys.exit("Only one of --logfile and --print_log is permitted")
1159        elif opts.print_log:
1160            out = sys.stdout
1161        elif opts.logfile:
1162            try:
1163                out = open(opts.logfile, "w")
1164            except IOError,e:
1165                sys.exit("Cannot open logfile: %s" %e)
1166        else:
1167            out = None
1168
1169        exp_id = None
1170
1171        if opts.exp_certfile:
1172            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
1173
1174        if opts.exp_name:
1175            exp_id = { 'localname' : opts.exp_name }
1176
1177        if not exp_id:
1178            sys.exit("Must give one of --experiment_cert and " +\
1179                    "--experiment_name");
1180
1181        req = { 'experiment': exp_id, 'force': opts.force }
1182
1183        try:
1184            resp_dict = self.do_rpc(req,
1185                    opts.url, opts.transport, cert, opts.trusted, 
1186                    serialize_only=opts.serialize_only,
1187                    tracefile=opts.tracefile,
1188                    caller=self.caller('Terminate'))
1189        except self.RPCException, e:
1190            exit_with_fault(\
1191                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1192        except RuntimeError, e:
1193            print e
1194            sys.exit("Error processing RPC: %s" % e)
1195
1196        if out:
1197            log = resp_dict.get('deallocationLog', None)
1198            if log:
1199                print >>out, log
1200                out.close()
1201            else:
1202                out.close()
1203                sys.exit("No log returned")
1204
1205class terminate_segment(fedd_rpc):
1206    def __init__(self): 
1207        """
1208        Termination request
1209        """
1210
1211        fedd_rpc.__init__(self)
1212
1213    def __call__(self):
1214        """
1215        The control flow.  Compose the request and print the response.
1216        """
1217        # Process the options using the customized option parser defined above
1218        parser = fedd_terminate_opts()
1219
1220        (opts, args) = parser.parse_args()
1221
1222        (user, cert) = self.get_user_info()
1223        if opts.trusted:
1224            if ( not os.access(opts.trusted, os.R_OK) ) :
1225                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1226
1227        if opts.debug > 0: opts.tracefile=sys.stderr
1228
1229        if opts.cert != None: cert = opts.cert
1230
1231        if cert == None:
1232            sys.exit("No certificate given (--cert) or found")
1233
1234        if os.access(cert, os.R_OK):
1235            fid = fedid(file=cert)
1236        else:
1237            sys.exit("Cannot read certificate (%s)" % cert)
1238
1239        if opts.exp_name and opts.exp_certfile:
1240            sys.exit("Only one of --experiment_cert and " +\
1241                    "--experiment_name permitted")
1242
1243        if opts.print_log and opts.logfile:
1244            sys.exit("Only one of --logfile and --print_log is permitted")
1245        elif opts.print_log:
1246            out = sys.stdout
1247        elif opts.logfile:
1248            try:
1249                out = open(opts.logfile, "w")
1250            except IOError,e:
1251                sys.exit("Cannot open logfile: %s" %e)
1252        else:
1253            out = None
1254
1255        exp_id = None
1256
1257        if opts.exp_certfile:
1258            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
1259
1260        if opts.exp_name:
1261            exp_id = { 'localname' : opts.exp_name }
1262
1263        if not exp_id:
1264            sys.exit("Must give one of --experiment_cert and " +\
1265                    "--experiment_name");
1266
1267        req = { 'allocID': exp_id, 'force': opts.force }
1268
1269        try:
1270            resp_dict = self.do_rpc(req,
1271                    opts.url, opts.transport, cert, opts.trusted, 
1272                    serialize_only=opts.serialize_only,
1273                    tracefile=opts.tracefile,
1274                    caller=self.caller('TerminateSegment'))
1275        except self.RPCException, e:
1276            exit_with_fault(\
1277                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1278        except RuntimeError, e:
1279            print e
1280            sys.exit("Error processing RPC: %s" % e)
1281
1282        if out:
1283            log = resp_dict.get('deallocationLog', None)
1284            if log:
1285                print >>out, log
1286                out.close()
1287            else:
1288                out.close()
1289                sys.exit("No log returned")
1290
1291class new(fedd_rpc):
1292    def __init__(self): 
1293        fedd_rpc.__init__(self)
1294    def __call__(self):
1295        # Process the options using the customized option parser defined above
1296        parser = fedd_new_opts()
1297
1298        (opts, args) = parser.parse_args()
1299
1300        if opts.trusted:
1301            if ( not os.access(opts.trusted, os.R_OK) ) :
1302                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1303
1304        if opts.debug > 0: opts.tracefile=sys.stderr
1305
1306        (user, cert) = self.get_user_info()
1307
1308        if opts.cert != None: cert = opts.cert
1309
1310        if cert == None:
1311            sys.exit("No certificate given (--cert) or found")
1312
1313        if os.access(cert, os.R_OK):
1314            fid = fedid(file=cert)
1315        else:
1316            sys.exit("Cannot read certificate (%s)" % cert)
1317
1318        out_certfile = opts.out_certfile
1319
1320        msg = { }
1321
1322        if opts.exp_name:
1323            msg['experimentID'] = { 'localname': opts.exp_name }
1324
1325        if opts.debug > 1: print >>sys.stderr, msg
1326
1327        try:
1328            resp_dict = self.do_rpc(msg, 
1329                    opts.url, opts.transport, cert, opts.trusted, 
1330                    serialize_only=opts.serialize_only,
1331                    tracefile=opts.tracefile, 
1332                    caller=self.caller("New"))
1333        except self.RPCException, e:
1334            exit_with_fault(\
1335                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1336        except RuntimeError, e:
1337            sys.exit("Error processing RPC: %s" % e)
1338
1339        if opts.debug > 1: print >>sys.stderr, resp_dict
1340
1341        ea = resp_dict.get('experimentAccess', None)
1342        if out_certfile and ea and ea.has_key('X509'):
1343            try:
1344                f = open(out_certfile, "w")
1345                print >>f, ea['X509']
1346                f.close()
1347            except IOError:
1348                sys.exit('Could not write to %s' %  out_certfile)
1349        eid = resp_dict.get('experimentID', None)
1350        if eid:
1351            for id in eid:
1352                for k in id.keys():
1353                    print "%s: %s" % (k, id[k])
1354        st = resp_dict.get('experimentStatus', None)
1355        if st:
1356            print "status: %s" % st
1357
1358
1359class create(fedd_rpc):
1360    def __init__(self): 
1361        fedd_rpc.__init__(self)
1362
1363    @staticmethod
1364    def parse_service(svc):
1365        terms = svc.split(':')
1366        svcd = { }
1367        if len(terms) < 2 or len(terms[0]) == 0 or len(terms[1]) == 0:
1368            sys.exit("Bad service description '%s': Not enough terms" % svc)
1369       
1370        svcd['name'] = terms[0]
1371        svcd['export'] = terms[1].split(",")
1372        if len(terms) > 2 and len(terms[2]) > 0:
1373            svcd['import'] = terms[2].split(",")
1374        if len(terms) > 3 and len(terms[3]) > 0:
1375            svcd['fedAttr'] = [ ]
1376            for t in terms[3].split(","):
1377                i = t.find("=")
1378                if i != -1 :
1379                    svcd['fedAttr'].append(
1380                            {'attribute': t[0:i], 'value': t[i+1:]})
1381                else:
1382                    sys.exit("Bad service attribute '%s': no equals sign" % t)
1383        return svcd
1384
1385    def __call__(self):
1386        parser = fedd_create_opts()
1387
1388        (opts, args) = parser.parse_args()
1389
1390        svcs = []
1391
1392        if opts.trusted:
1393            if ( not os.access(opts.trusted, os.R_OK) ) :
1394                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1395
1396        if not (opts.project and opts.master) and not opts.service:
1397            print >>sys.stderr, "Neither master/project nor services requested"
1398
1399        if opts.debug > 0: opts.tracefile=sys.stderr
1400
1401        (user, cert) = self.get_user_info()
1402
1403        if opts.cert != None: cert = opts.cert
1404
1405        if cert == None:
1406            sys.exit("No certificate given (--cert) or found")
1407
1408        if os.access(cert, os.R_OK):
1409            fid = fedid(file=cert)
1410        else:
1411            sys.exit("Cannot read certificate (%s)" % cert)
1412
1413        if opts.file:
1414            exp_desc = ""
1415            try:
1416                f = open(opts.file, 'r')
1417                for line in f:
1418                    exp_desc += line
1419                f.close()
1420            except IOError:
1421                sys.exit("Cannot read description file (%s)" %opts.file)
1422        else:
1423            sys.exit("Must specify an experiment description (--file)")
1424
1425        out_certfile = opts.out_certfile
1426       
1427        # Fill in svcs here so errors in service specification come before the
1428        # New call is made
1429        if opts.master and opts.project:
1430            svcs.append({
1431                    'name': 'project_export', 
1432                    'export': [opts.master], 
1433                    'importall': True,
1434                    'fedAttr': [ 
1435                        { 'attribute': 'project', 'value': opts.project },
1436                        ],
1437                    })
1438
1439        svcs.extend([ self.parse_service(s) for s in opts.service])
1440        msg = { }
1441
1442        if opts.exp_name:
1443            msg['experimentID'] = { 'localname': opts.exp_name }
1444
1445        if opts.debug > 1: print >>sys.stderr, msg
1446
1447        try:
1448            resp_dict = self.do_rpc(msg, 
1449                    opts.url, opts.transport, cert, opts.trusted, 
1450                    serialize_only=opts.serialize_only,
1451                    tracefile=opts.tracefile,
1452                    caller=self.caller('New'))
1453        except self.RPCException, e:
1454            exit_with_fault(\
1455                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1456        except RuntimeError, e:
1457            sys.exit("Error processing RPC: %s" % e)
1458
1459        if opts.debug > 1: print >>sys.stderr, resp_dict
1460
1461        ea = resp_dict.get('experimentAccess', None)
1462        if out_certfile and ea and ea.has_key('X509'):
1463            try:
1464                f = open(out_certfile, "w")
1465                print >>f, ea['X509']
1466                f.close()
1467            except IOError:
1468                sys.exit('Could not write to %s' %  out_certfile)
1469        eid = resp_dict.get('experimentID', None)
1470        e_fedid = None
1471        e_local = None
1472        if eid:
1473            for id in eid:
1474                for k in id.keys():
1475                    if k =='fedid':
1476                        e_fedid = id[k]
1477                    elif k =='localname':
1478                        e_local = id[k]
1479        elif opts.serialize_only:
1480            e_local = "serialize"
1481        msg = { 'experimentdescription': { 'ns2description': exp_desc }, }
1482
1483        if svcs:
1484            msg['service'] = svcs
1485
1486        if e_fedid:
1487            msg['experimentID'] = { 'fedid': e_fedid }
1488        elif e_local:
1489            msg['experimentID'] = { 'localname': e_local }
1490        else:
1491            sys.exit("New did not return an experiment ID??")
1492
1493        if opts.debug > 1: print >>sys.stderr, msg
1494
1495        try:
1496            resp_dict = self.do_rpc(msg, 
1497                    opts.url, opts.transport, cert, opts.trusted, 
1498                    serialize_only=opts.serialize_only,
1499                    tracefile=opts.tracefile,
1500                    caller=self.caller('Create'))
1501        except self.RPCException, e:
1502            exit_with_fault(\
1503                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1504        except RuntimeError, e:
1505            sys.exit("Error processing RPC: %s" % e)
1506
1507        if opts.debug > 1: print >>sys.stderr, resp_dict
1508
1509        ea = resp_dict.get('experimentAccess', None)
1510        if out_certfile and ea and ea.has_key('X509'):
1511            try:
1512                f = open(out_certfile, "w")
1513                print >>f, ea['X509']
1514                f.close()
1515            except IOError:
1516                sys.exit('Could not write to %s' %  out_certfile)
1517        eid = resp_dict.get('experimentID', None)
1518        if eid:
1519            for id in eid:
1520                for k in id.keys():
1521                    print "%s: %s" % (k, id[k])
1522        st = resp_dict.get('experimentStatus', None)
1523        if st:
1524            print "status: %s" % st
1525
1526class split(fedd_rpc):
1527    def __init__(self): 
1528        fedd_rpc.__init__(self)
1529    def __call__(self):
1530        # Process the options using the customized option parser defined above
1531        parser = fedd_split_opts()
1532
1533        (opts, args) = parser.parse_args()
1534
1535        if opts.trusted:
1536            if ( not os.access(opts.trusted, os.R_OK) ) :
1537                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1538
1539        if opts.debug > 0: opts.tracefile=sys.stderr
1540
1541        (user, cert) = self.get_user_info()
1542
1543        if opts.cert != None: cert = opts.cert
1544
1545        if cert == None:
1546            sys.exit("No certificate given (--cert) or found")
1547
1548        if os.access(cert, os.R_OK):
1549            fid = fedid(file=cert)
1550        else:
1551            sys.exit("Cannot read certificate (%s)" % cert)
1552
1553        if opts.file:
1554            exp_desc = ""
1555            try:
1556                f = open(opts.file, 'r')
1557                for line in f:
1558                    exp_desc += line
1559                f.close()
1560            except IOError:
1561                sys.exit("Cannot read description file (%s)" %opts.file)
1562        else:
1563            sys.exit("Must specify an experiment description (--file)")
1564
1565        if not opts.master:
1566            sys.exit("Must specify a master testbed (--master)")
1567
1568        out_certfile = opts.out_certfile
1569
1570        msg = {
1571                'description': { 'ns2description': exp_desc },
1572                'master': opts.master,
1573                'include_fedkit': opts.fedkit,
1574                'include_gatewaykit': opts.gatewaykit,
1575                }
1576
1577        if opts.debug > 1: print >>sys.stderr, msg
1578
1579        try:
1580            resp_dict = self.do_rpc(msg, 
1581                    opts.url, opts.transport, cert, opts.trusted, 
1582                    serialize_only=opts.serialize_only,
1583                    tracefile=opts.tracefile,
1584                    caller=self.caller('Ns2Split'))
1585        except self.RPCException, e:
1586            exit_with_fault(\
1587                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1588        except RuntimeError, e:
1589            sys.exit("Error processing RPC: %s" % e)
1590
1591        if opts.debug > 1: print >>sys.stderr, resp_dict
1592
1593        ed = resp_dict.get('experimentdescription', None)
1594        if ed:
1595            if ed.has_key('topdldescription'):
1596                topo = topdl.Topology(**ed['topdldescription'])
1597                print topdl.topology_to_xml(topo, 'experiment')
1598
1599class access(fedd_rpc):
1600    def __init__(self):
1601        fedd_rpc.__init__(self)
1602
1603    def print_response_as_testbed(self, resp, label, out=sys.stdout):
1604        """Print the response as input to the splitter script"""
1605
1606        e = resp.get('emulab', None)
1607        if e:
1608            p = e['project']
1609            fields = { 
1610                    "Boss": e['boss'],
1611                    "OpsNode": e['ops'],
1612                    "Domain": e['domain'],
1613                    "FileServer": e['fileServer'],
1614                    "EventServer": e['eventServer'],
1615                    "Project": unpack_id(p['name'])
1616                    }
1617            if (label != None): print >> out, "[%s]" % label
1618
1619            for l, v in fields.iteritems():
1620                print >>out, "%s: %s" % (l, v)
1621
1622            for u in p['user']:
1623                print >>out, "User: %s" % unpack_id(u['userID'])
1624
1625            for a in e['fedAttr']:
1626                print >>out, "%s: %s" % (a['attribute'], a['value'])
1627
1628    def __call__(self):
1629        node_descs = []
1630        proj = None
1631
1632        # Process the options using the customized option parser defined above
1633        parser = fedd_access_opts()
1634
1635        (opts, args) = parser.parse_args()
1636
1637        if opts.testbed == None:
1638            parser.error("--testbed is required")
1639
1640        if opts.trusted:
1641            if ( not os.access(opts.trusted, os.R_OK) ) :
1642                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1643
1644        if opts.debug > 0: opts.tracefile=sys.stderr
1645
1646        (user, cert) = self.get_user_info()
1647
1648        if opts.cert != None: cert = opts.cert
1649
1650        if cert == None:
1651            sys.exit("No certificate given (--cert) or found")
1652
1653        if os.access(cert, os.R_OK):
1654            fid = fedid(file=cert)
1655            if opts.use_fedid == True:
1656                user = fid
1657        else:
1658            sys.exit("Cannot read certificate (%s)" % cert)
1659
1660        msg = {
1661                'allocID': pack_id('test alloc'),
1662                'destinationTestbed': pack_id(opts.testbed),
1663                }
1664
1665        if len(node_descs) > 0:
1666            msg['resources'] = { 
1667                    'node': [ 
1668                        { 
1669                            'image':  n.image ,
1670                            'hardware':  n.hardware,
1671                            'count': n.count,
1672                        } for n in node_descs],
1673                    }
1674
1675        if opts.debug > 1: print >>sys.stderr, msg
1676
1677        try:
1678            resp_dict = self.do_rpc(msg, 
1679                    opts.url, opts.transport, cert, opts.trusted, 
1680                    serialize_only=opts.serialize_only,
1681                    tracefile=opts.tracefile,
1682                    caller=self.caller('RequestAccess'))
1683        except self.RPCException, e:
1684            exit_with_fault(\
1685                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1686        except RuntimeError, e:
1687            sys.exit("Error processing RPC: %s" % e.message)
1688
1689        if opts.debug > 1: print >>sys.stderr, resp_dict
1690        if not opts.serialize_only:
1691            self.print_response_as_testbed(resp_dict, opts.label)
1692
1693# Keep requesting experiment status and printing updates to the log until the
1694# experiment is done being created.
1695class spew_log(fedd_rpc):
1696    def __init__(self): 
1697        """
1698        Init the superclass
1699        """
1700
1701        fedd_rpc.__init__(self)
1702
1703    def __call__(self):
1704        """
1705        The control flow.  Compose the request and print the response.
1706        """
1707        # Process the options using the customized option parser defined above
1708        parser = fedd_spew_opts()
1709
1710        (opts, args) = parser.parse_args()
1711
1712        if opts.trusted:
1713            if ( not os.access(opts.trusted, os.R_OK) ) :
1714                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1715
1716        if opts.debug > 0: opts.tracefile=sys.stderr
1717
1718        (user, cert) = self.get_user_info()
1719
1720        if opts.cert != None: cert = opts.cert
1721
1722        if cert == None:
1723            sys.exit("No certificate given (--cert) or found")
1724
1725        if os.access(cert, os.R_OK):
1726            fid = fedid(file=cert)
1727        else:
1728            sys.exit("Cannot read certificate (%s)" % cert)
1729
1730        if opts.exp_name and opts.exp_certfile:
1731            sys.exit("Only one of --experiment_cert and " +\
1732                    "--experiment_name permitted");
1733
1734        if opts.exp_certfile:
1735            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
1736
1737        if opts.exp_name:
1738            exp_id = { 'localname' : opts.exp_name }
1739
1740        if opts.logfile:
1741            try:
1742                out = open(opts.logfile, "w")
1743            except IOError,e:
1744                sys.exit("Cannot open logfile: %s" %e)
1745        else:
1746            out = sys.stdout
1747
1748        req = { 'experiment': exp_id }
1749
1750        status = "starting"
1751        log = ""
1752        log_offset = 0
1753        update = opts.update
1754        while status == 'starting':
1755            try:
1756                resp_dict = self.do_rpc(req,
1757                        opts.url, opts.transport, cert, opts.trusted, 
1758                        serialize_only=opts.serialize_only,
1759                        tracefile=opts.tracefile,
1760                        caller=self.caller('Info'))
1761            except self.RPCException, e:
1762                exit_with_fault(\
1763                        {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1764            except RuntimeError, e:
1765                sys.exit("Error processing RPC: %s" % e)
1766
1767            if not opts.serialize_only:
1768                status = resp_dict.get('experimentStatus', None)
1769            else:
1770                status = "active"
1771            log = resp_dict.get('allocationLog', None)
1772            if not status:
1773                sys.exit("No status in Info response??")
1774            if log:
1775                if len(log) > log_offset:
1776                    print >>out, log[log_offset:],
1777                    out.flush()
1778                    log_offset = len(log)
1779            if status == 'starting': 
1780                time.sleep(update)
1781
1782        print >>out
1783        print >>out, status
1784        out.close()
1785
1786class start_segment(fedd_rpc):
1787    def __init__(self): 
1788        fedd_rpc.__init__(self)
1789    def __call__(self):
1790        # Process the options using the customized option parser defined above
1791        parser = fedd_start_opts()
1792
1793        (opts, args) = parser.parse_args()
1794
1795        if opts.trusted:
1796            if ( not os.access(opts.trusted, os.R_OK) ) :
1797                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1798
1799        if opts.debug > 0: opts.tracefile=sys.stderr
1800
1801        (user, cert) = self.get_user_info()
1802
1803        if opts.cert != None: cert = opts.cert
1804
1805        if cert == None:
1806            sys.exit("No certificate given (--cert) or found")
1807
1808        if os.access(cert, os.R_OK):
1809            fid = fedid(file=cert)
1810        else:
1811            sys.exit("Cannot read certificate (%s)" % cert)
1812
1813        if opts.file:
1814            try:
1815                top = topdl.topology_from_xml(filename=opts.file, 
1816                        top='experiment')
1817            except IOError:
1818                sys.exit("Cannot read description file (%s)" %opts.file)
1819        else:
1820            sys.exit("Must specify an experiment description (--file)")
1821
1822        msg = {
1823                'segmentdescription': { 'topdldescription': top.to_dict() },
1824                'allocID': pack_id(fid),
1825                'master': False,
1826                }
1827
1828        if opts.debug > 1: print >>sys.stderr, msg
1829
1830        try:
1831            resp_dict = self.do_rpc(msg, 
1832                    opts.url, opts.transport, cert, opts.trusted, 
1833                    serialize_only=opts.serialize_only,
1834                    tracefile=opts.tracefile,
1835                    caller=self.caller('StartSegment'))
1836        except self.RPCException, e:
1837            exit_with_fault(\
1838                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1839        except RuntimeError, e:
1840            sys.exit("Error processing RPC: %s" % e)
1841
1842        if opts.debug > 1: print >>sys.stderr, resp_dict
1843        if not opts.serialize_only:
1844            print resp_dict
1845
1846cmds = {\
1847        'new': new(),\
1848        'create': create(),\
1849        'split': split(),\
1850        'access': access(),\
1851        'ftopo': ftopo(),\
1852        'vtopo': vtopo(),\
1853        'vis': vis(),\
1854        'info': exp_data(),\
1855        'multiinfo': multi_exp_data(),\
1856        'multistatus': multi_status(),\
1857        'image': image(),\
1858        'ns_image': ns_image(),\
1859        'status': status(),\
1860        'terminate': terminate(),\
1861        'spewlog': spew_log(),\
1862        'topdl_image': topdl_image(),\
1863        'start_segment': start_segment(),\
1864        'terminate_segment': terminate_segment(),\
1865    }
1866if len(sys.argv) > 1:
1867    operation = cmds.get(sys.argv[1], None)
1868else:
1869    sys.exit("first argument must be one of " + ",".join(cmds.keys()))
1870
1871if operation:
1872    del sys.argv[1]
1873    operation()
1874else:
1875    if sys.argv[1] == '--help':
1876        sys.exit(\
1877'''Only context sensitive help is available.  For one of the commands:
1878
1879%s
1880
1881type
1882  %s command --help
1883
1884to get help, e.g., %s create --help
1885''' % (", ".join(cmds.keys()), sys.argv[0], sys.argv[0]))
1886    else:
1887        sys.exit("Bad command: %s.  Valid ones are: %s" % \
1888                (sys.argv[1], ", ".join(cmds.keys())))
1889
Note: See TracBrowser for help on using the repository browser.