source: fedd/fedd_client.py @ d205d97

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

Initial dragon import and fedd_client testing for same

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