#!/usr/local/bin/python import sys import os import pwd import tempfile import subprocess import re import xml.parsers.expat import time from federation import fedid, service_error from federation.util import fedd_ssl_context, pack_id, unpack_id from federation.remote_service import service_caller from federation import topdl from optparse import OptionParser, OptionValueError class IDFormatException(RuntimeError): pass class access_method: """Encapsulates an access method generically.""" (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey') default_type = type_ssh def __init__(self, buf=None, type=None, file=None): self.buf = buf if type != None: self.type = type else: self.type = access_method.default_type if file != None: self.readfile(file) def readfile(self, file, type=None): f = open(file, "r") self.buf = f.read(); f.close() if type == None: if self.type == None: self.type = access_method.default_type else: self.type = type; class node_desc: def __init__(self, image, hardware, count=1): if getattr(image, "__iter__", None) == None: if image == None: self.image = [ ] else: self.image = [ image ] else: self.image = image if getattr(hardware, "__iter__", None) == None: if hardware == None: self.hardware = [ ] else: self.hardware = [ hardware ] else: self.hardware = hardware if count != None: self.count = int(count) else: self.count = 1 class fedd_client_opts(OptionParser): """Encapsulate option processing in this class, rather than in main""" def __init__(self): OptionParser.__init__(self, usage="%prog [opts] (--help for details)", version="0.1") self.add_option("--cert", action="store", dest="cert", type="string", help="my certificate file") self.add_option( "--debug", action="count", dest="debug", default=0, help="Set debug. Repeat for more information") self.add_option("--serializeOnly", action="store_true", dest="serialize_only", default=False, help="Print the SOAP request that would be sent and exit") self.add_option("--trusted", action="store", dest="trusted", type="string", help="Trusted certificates (required)") self.add_option("--url", action="store", dest="url", type="string",default="https://localhost:23235", help="URL to connect to (default %default)") self.add_option("--transport", action="store", type="choice", choices=("xmlrpc", "soap"), default="soap", help="Transport for request (xmlrpc|soap) (Default: %default)") self.add_option("--trace", action="store_const", dest="tracefile", const=sys.stderr, help="Print SOAP exchange to stderr") class fedd_new_opts(fedd_client_opts): def __init__(self): fedd_client_opts.__init__(self) self.add_option("--experiment_cert", dest="out_certfile", type="string", help="output certificate file") self.add_option("--experiment_name", dest="exp_name", type="string", help="Suggested experiment name") class fedd_create_opts(fedd_new_opts): def __init__(self): fedd_new_opts.__init__(self) self.add_option("--file", dest="file", help="experiment description file") self.add_option("--project", action="store", dest="project", type="string", help="Project to export from master") self.add_option("--master", dest="master", help="Master testbed in the federation") class fedd_split_opts(fedd_create_opts): def __init__(self ): fedd_create_opts.__init__(self) self.add_option('--fedkit', action='store_true', dest='fedkit', default=False, help="get output suitable for federation kit install") self.add_option('--gatewaykit', action='store_true', dest='gatewaykit', default=False, help="get output suitable for federation gateway kit install") class fedd_access_opts(fedd_create_opts): def __init__(self): fedd_create_opts.__init__(self) self.add_option("--label", action="store", dest="label", type="string", help="Label for output") if add_node_callback: self.add_option("--node", action="callback", type="string", callback=add_node_callback, callback_args=(node_descs,), help="Node description: image:hardware[:count]") self.add_option("--testbed", action="store", dest="testbed", type="string", help="Testbed identifier (URI) to contact (required)") class fedd_exp_data_opts(fedd_client_opts): def __init__(self): fedd_client_opts.__init__(self) self.add_option("--experiment_cert", dest="exp_certfile", type="string", help="experiment certificate file") self.add_option("--experiment_name", dest="exp_name", type="string", help="human readable experiment name") self.add_option("--data", dest="data", default=[], action="append", type="choice", choices=("id", "federant", "vtopo", "vis", "log", "status"), help="data to extract") class fedd_terminate_opts(fedd_exp_data_opts): def __init__(self): fedd_exp_data_opts.__init__(self) self.add_option("--force", dest="force", action="store_true", default=False, help="Force termination if experiment is in strange state") self.add_option("--logfile", dest="logfile", default=None, help="File to write log to") self.add_option("--print_log", dest="print_log", default=False, action="store_true", help="Print deallocation log to standard output") class fedd_multi_exp_data_opts(fedd_client_opts): def __init__(self): fedd_client_opts.__init__(self) self.add_option("--data", dest="data", default=[], action="append", type="choice", choices=("id", "federant", "vtopo", "vis", "log", "status"), help="data to extract") class fedd_spew_opts(fedd_client_opts): def __init__(self): fedd_client_opts.__init__(self) self.add_option("--experiment_cert", dest="exp_certfile", type="string", help="experiment name certificate file") self.add_option("--experiment_name", dest="exp_name", type="string", help="human readable experiment name") self.add_option("--logfile", dest="logfile", default=None, help="File to write log to") self.add_option('--update_time', dest='update', type='int', default=10, help='how often to update the printed log') class fedd_image_opts(fedd_exp_data_opts): def __init__(self): fedd_exp_data_opts.__init__(self) self.add_option("--output", dest="outfile", type="string", help="output image file") self.add_option("--format", dest="format", type="choice", choices=("jpg", "png", "dot", "svg"), help="Output file format override") self.add_option("--program", dest="neato", default=None, type="string", help="Program compatible with dot (from graphviz) used to " + \ "render image") self.add_option("--labels", dest='labels', action='store_true', default=True, help='Label nodes and edges') self.add_option("--no_labels", dest='labels', default=True, action='store_false', help='Label nodes and edges') self.add_option('--pixels', dest="pixels", default=None, type="int", help="Size of output in pixels (diagrams are square") class fedd_ns_image_opts(fedd_split_opts): def __init__(self): fedd_split_opts.__init__(self) self.add_option("--output", dest="outfile", type="string", help="output image file") self.add_option("--format", dest="format", type="choice", choices=("jpg", "png", "dot", "svg"), help="Output file format override") self.add_option("--program", dest="neato", default=None, type="string", help="Program compatible with dot (from graphviz) used to " + \ "render image") self.add_option("--labels", dest='labels', action='store_true', default=True, help='Label nodes and edges') self.add_option("--no_labels", dest='labels', default=True, action='store_false', help='Label nodes and edges') self.add_option('--pixels', dest="pixels", default=None, type="int", help="Size of output in pixels (diagrams are square") class fedd_start_opts(fedd_client_opts): def __init__(self): fedd_client_opts.__init__(self) self.add_option("--file", dest="file", help="experiment description file") def exit_with_fault(dict, out=sys.stderr): """ Print an error message and exit. The dictionary contains the FeddFaultBody elements.""" codestr = "" if dict.has_key('errstr'): codestr = "Error: %s" % dict['errstr'] if dict.has_key('code'): if len(codestr) > 0 : codestr += " (%d)" % dict['code'] else: codestr = "Error Code: %d" % dict['code'] print>>out, codestr print>>out, "Description: %s" % dict['desc'] sys.exit(dict.get('code', 20)) # Base class for the various client operations. It includes some commonly used # functions and classes the most important of which are the exception classes # and the service calling classes. class fedd_rpc: class RPCException: """ An error during the RPC exchange. It unifies errors from both SOAP and XMLPRC calls. """ def __init__(self, fb): self.desc = fb.get('desc', None) self.code = fb.get('code', -1) self.errstr = fb.get('errstr', None) class caller(service_caller): """ The caller is used by fedd_rpc.do_rpc to make the rpc call. The extra stashed information is used to parse responses a little. """ def __init__(self, pre): self.ResponseBody="%sResponseBody" % pre self.method = pre service_caller.__init__(self, self.method) def __init__(self): """ Specialize the class for the pre method """ self.caller = fedd_rpc.caller self.RPCException = fedd_rpc.RPCException def add_node_desc(self, option, opt_str, value, parser, node_descs): def none_if_zero(x): if len(x) > 0: return x else: return None params = map(none_if_zero, value.split(":")); if len(params) < 4 and len(params) > 1: node_descs.append(node_desc(*params)) else: raise OptionValueError("Bad node description: %s" % value) def get_user_info(self): pw = pwd.getpwuid(os.getuid()); try_cert=None user = None if pw != None: user = pw[0] try_cert = "%s/.ssl/emulab.pem" % pw[5]; if not os.access(try_cert, os.R_OK): try_cert = None return (user, try_cert) def do_rpc(self, req_dict, url, transport, cert, trusted, tracefile=None, serialize_only=False, caller=None): """ The work of sending and parsing the RPC as either XMLRPC or SOAP """ if caller is None: raise RuntimeError("Must provide caller to do_rpc") context = None while context == None: try: context = fedd_ssl_context(cert, trusted) except Exception, e: # Yes, doing this on message type is not ideal. The string # comes from OpenSSL, so check there is this stops working. if str(e) == "bad decrypt": print >>sys.stderr, "Bad Passphrase given." else: raise if transport == "soap": if serialize_only: print self.caller.serialize_soap(req_dict) sys.exit(0) else: try: resp = caller.call_soap_service(url, req_dict, context=context, tracefile=tracefile) except service_error, e: raise self.RPCException( {\ 'code': e.code, 'desc': e.desc, 'errstr': e.code_string()\ }) elif transport == "xmlrpc": if serialize_only: ser = dumps((req_dict,)) print ser sys.exit(0) else: try: resp = caller.call_xmlrpc_service(url, req_dict, context=context, tracefile=tracefile) except service_error, e: raise self.RPCException( {\ 'code': e.code, 'desc': e.desc, 'errstr': e.code_string()\ }) else: raise RuntimeError("Unknown RPC transport: %s" % transport) if resp.has_key(caller.ResponseBody): return resp[caller.ResponseBody] else: raise RuntimeError("No body in response??") class exp_data_base(fedd_rpc): def __init__(self): """ Init the various conversions """ fedd_rpc.__init__(self) # List of things one could ask for and what formatting routine is # called. self.params = { 'vis': ('vis', self.print_xml('vis')), 'vtopo': ('vtopo', self.print_xml('vtopo')), 'federant': ('federant', self.print_xml), 'id': ('experimentID', self.print_id), 'status': ('experimentStatus', self.print_string), 'log': ('allocationLog', self.print_string), 'access': ('experimentAccess', self.print_string), } # Utility functions def print_string(self, d, out=sys.stdout): print >>out, d def print_id(self, d, out=sys.stdout): if d: for id in d: for k in id.keys(): print >>out, "%s: %s" % (k, id[k]) class print_xml: """ Print the retrieved data is a simple xml representation of the dict. """ def __init__(self, top): self.xml = top def __call__(self, d, out=sys.stdout): str = "<%s>\n" % self.xml for t in ('node', 'lan'): if d.has_key(t): for x in d[t]: str += "<%s>" % t for k in x.keys(): str += "<%s>%s" % (k, x[k],k) str += "\n" % t str+= "" % self.xml print >>out, str # Querying experiment data follows the same control flow regardless of the # specific data retrieved. This class encapsulates that control flow. class exp_data(exp_data_base): def __init__(self): exp_data_base.__init__(self) def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_exp_data_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.exp_name and opts.exp_certfile: sys.exit("Only one of --experiment_cert and " +\ "--experiment_name permitted"); exp_id = None if opts.exp_certfile: exp_id = { 'fedid': fedid(file=opts.exp_certfile) } if opts.exp_name: exp_id = { 'localname' : opts.exp_name } if not exp_id: sys.exit("specify one of --experiment_cert and --experiment_name") req = { 'experiment': exp_id } try: resp_dict = self.do_rpc(req, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('Info')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) for d in opts.data: key, output = self.params[d] try: if resp_dict.has_key(key): output(resp_dict[key]) except RuntimeError, e: sys.exit("Bad response. %s" % e.message) class vtopo(exp_data): """ vtopo is just an info --data=vtopo request, so this adds that parameter to the arguments and executes exp_info when called. """ def __init__(self): exp_data.__init__(self) def __call__(self): sys.argv.append('--data=vtopo') exp_data.__call__(self) class vis(exp_data): """ vis is just an info --data=vis request, so this adds that parameter to the arguments and executes exp_info when called. """ def __init__(self): exp_data.__init__(self) def __call__(self): sys.argv.append('--data=vis') exp_data.__call__(self) class status(exp_data): """ status is just an info --data=status request, so this adds that parameter to the arguments and executes exp_info when called. """ def __init__(self): exp_data.__init__(self) def __call__(self): sys.argv.append('--data=status') exp_data.__call__(self) class multi_exp_data(exp_data_base): def __init__(self): exp_data_base.__init__(self) def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_multi_exp_data_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) req = { } try: resp_dict = self.do_rpc(req, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('MultiInfo')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) exps = resp_dict.get('info', []) if exps: print '---' for exp in exps: for d in opts.data: key, output = self.params[d] try: if exp.has_key(key): output(exp[key]) except RuntimeError, e: sys.exit("Bad response. %s" % e.message) print '---' class multi_status(exp_data_base): def __init__(self): exp_data_base.__init__(self) def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_client_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) req = { } try: resp_dict = self.do_rpc(req, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('MultiInfo')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) for exp in resp_dict.get('info', []): out = [] for eid in exp.get('experimentID', []): if eid.has_key('localname'): out.append(eid['localname']) break else: out.append("") for eid in exp.get('experimentID', []): if eid.has_key('fedid'): out.append("%s" % eid['fedid']) break else: out.append("") out.append(exp.get('experimentStatus', "")) for f in exp.get('federant', []): if f.get('master', False): em = f.get('emulab', None) if em: project = em.get('project', None) if project: tb = project.get('testbed', None) if tb and tb.has_key('localname'): out.append(tb['localname']) else: out.append("") pn = project.get('name', None) if pn and pn.has_key('localname'): out.append(pn['localname']) else: out.append("") else: out.extend(("", "")) else: out.extend(("", "")) break else: out.extend(("","")) print ":".join(out) class image(fedd_rpc): def __init__(self): """ Null constructor """ fedd_rpc.__init__(self) @staticmethod def gen_dot_topo(d, labels, dotfile): lans = { } links = { } for n in d.get('node', []): print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n['vname'] print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]' # Collect lan members (we have to draw extra stuff for these) for n in d.get('lan', []): v = n['vname'] m = n['member'] i = n['ip'] if m.find(':') != -1: m = m[0:m.find(':')] if lans.has_key(v): lans[v].append((m, i)) elif links.has_key(v): links[v].append((m, i)) if len(links[v]) > 2: lans[v] = links[v] del links[v] else: links[v] = [(m, i)] # Encode the lans and the links for l in lans.keys(): print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]' for n in lans[l]: if labels: print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \ (l, n[0], n[1]) else: print >>dotfile, '\t"%s" -- "%s"' % (l, n[0]) for k, l in links.items(): if len(l) == 2: if labels: print >>dotfile, \ ('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \ 'headlabel="%s"]') % \ (l[0][0], l[1][0], k, l[0][1], l[1][1]) else: print >>dotfile, '\t"%s" -- "%s" ' % (l[0][0], l[1][0]) def gen_image(self, d, nodes, file, fmt, neato, labels, pix=None): # Open up a temporary file for dot to turn into a visualization try: df, dotname = tempfile.mkstemp(prefix='fedd_client', suffix=".dot") dotfile = os.fdopen(df, 'w') except IOError: raise service_error(service_error.internal, "Failed to open file in genviz") if not neato: for f in ['/usr/bin/neato', '/usr/local/bin/neato', '/usr/bin/dot', '/usr/local/bin/dot']: if os.access(f, os.X_OK): neato = f break else: sys.exit("Cannot find graph rendering program") cmd = [neato, '-Gsplines=true'] if fmt != 'dot': cmd.append('-T%s' % fmt) if file: cmd.append('-o') cmd.append(file) cmd.append(dotname) #nodes = d.get('node',[]) if nodes < 10: size = 5 elif nodes < 50: size = 10 else: size = 18 if pix: dpi = pix / size else: dpi = None print >>dotfile, "graph G {" if dpi: print >>dotfile, '\tgraph [size="%i,%i", dpi="%i", ratio=fill];' \ % (size, size, dpi) else: print >>dotfile, '\tgraph [size="%i,%i", ratio=fill];' \ % (size, size) if labels: print >>dotfile, '\tnode [fontname=arial,fontsize=9,label="\N"];' print >>dotfile, '\tedge [fontname=arial,fontsize=9];\n' else: print >>dotfile, '\tnode [label=""];' self.gen_dot_topo(d, labels, dotfile) print >>dotfile, "}" dotfile.close() # Redirect the drawing program stderr dev_null = open("/dev/null", "w") rv = subprocess.call(cmd, stderr=dev_null) os.remove(dotname) dev_null.close() if rv != 0: sys.exit("Error creating graph") def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_image_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.exp_name and opts.exp_certfile: sys.exit("Only one of --experiment_cert and " +\ "--experiment_name permitted"); if opts.exp_certfile: exp_id = { 'fedid': fedid(file=opts.exp_certfile) } if opts.exp_name: exp_id = { 'localname' : opts.exp_name } if opts.format and opts.outfile: fmt = opts.format file = opts.outfile elif not opts.format and opts.outfile: fmt = opts.outfile[-3:] if fmt not in ("png", "jpg", "dot", "svg"): sys.exit("Unexpected file type and no format specified") file = opts.outfile elif opts.format and not opts.outfile: fmt = opts.format file = None else: fmt="dot" file = None req = { 'experiment': exp_id } try: resp_dict = self.do_rpc(req, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('Vtopo')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) if resp_dict.has_key('vtopo'): self.gen_image(resp_dict['vtopo'], len(resp_dict['vtopo'].get('node', [])), file, fmt, opts.neato, opts.labels, opts.pixels) else: sys.exit("Bad response. %s" % e.message) class ns_image(image): def __init__(self): """ Null constructor """ image.__init__(self) def generate_topo_dict(self, splitout): class topo_parse: """ Parse the topology XML and create the dats structure. This class is copied from federation.experiment_control. """ def __init__(self): # Typing of the subelements for data conversion self.str_subelements = ('vname', 'vnode', 'ips', 'ip', 'member') self.int_subelements = ( 'bandwidth',) self.float_subelements = ( 'delay',) # The final data structure self.nodes = [ ] self.lans = [ ] self.topo = { \ 'node': self.nodes,\ 'lan' : self.lans,\ } self.element = { } # Current element being created self.chars = "" # Last text seen def end_element(self, name): # After each sub element the contents is added to the current # element or to the appropriate list. if name == 'node': self.nodes.append(self.element) self.element = { } elif name == 'lan': self.lans.append(self.element) self.element = { } elif name in self.str_subelements: self.element[name] = self.chars self.chars = "" elif name in self.int_subelements: self.element[name] = int(self.chars) self.chars = "" elif name in self.float_subelements: self.element[name] = float(self.chars) self.chars = "" def found_chars(self, data): self.chars += data.rstrip() tp = topo_parse(); parser = xml.parsers.expat.ParserCreate() parser.EndElementHandler = tp.end_element parser.CharacterDataHandler = tp.found_chars m = re.search('^#\s+Begin\s+Vtopo\s*$(.*)^#\s+End\s+Vtopo', splitout, re.MULTILINE | re.DOTALL) if m: str = m.group(1) else: sys.exit("Badly formatted split") parser.Parse(str) return tp.topo def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_ns_image_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.file: exp_desc = "" try: f = open(opts.file, 'r') for line in f: exp_desc += line f.close() except IOError: sys.exit("Cannot read description file (%s)" %opts.file) else: sys.exit("Must specify an experiment description (--file)") if not opts.master: opts.master="dummy" req = { 'description': { 'ns2description': exp_desc }, 'master': opts.master, 'include_fedkit': opts.fedkit, 'include_gatewaykit': opts.gatewaykit, } if opts.format and opts.outfile: fmt = opts.format file = opts.outfile elif not opts.format and opts.outfile: fmt = opts.outfile[-3:] if fmt not in ("png", "jpg", "dot", "svg"): sys.exit("Unexpected file type and no format specified") file = opts.outfile elif opts.format and not opts.outfile: fmt = opts.format file = None else: fmt="dot" file = None try: resp_dict = self.do_rpc(req, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('Ns2Split')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) if resp_dict.has_key('output'): if len(resp_dict['output']) < 1: sys.exit("Bad response: could not split") topo = self.generate_topo_dict(resp_dict['output']) self.gen_image(topo, len(topo.get('node', [])), file, fmt, opts.neato, opts.labels, opts.pixels) else: sys.exit("Bad response. %s" % e.message) class topdl_image(image): def __init__(self): """ Null constructor """ image.__init__(self) @staticmethod def gen_dot_topo(t, labels, dotfile): lans = [ s for s in t.substrates if len(s.interfaces) != 2] links = [ s for s in t.substrates if len(s.interfaces) == 2] i = 0 for n in t.elements: if n.name: print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n.name[0] else: print >>dotfile, \ '\t"unnamed_node%d" [shape=box,style=filled,\\' % i i += 1 print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]' # Encode the lans and the links for l in lans: print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l.name print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]' for i in l.interfaces: ip = i.get_attribute('ip4_address') if labels and ip: print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \ (l.name, i.element.name[0], ip) else: print >>dotfile, '\t"%s" -- "%s"' % \ (l.name, i.element.name[0]) for l in links: s, d = l.interfaces[0:2] sip = s.get_attribute('ip4_address') dip = d.get_attribute('ip4_address') if labels and sip and dip: print >>dotfile, \ ('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \ 'headlabel="%s"]') % \ (s.element.name[0], d.element.name[0], l.name, sip, dip) else: print >>dotfile, '\t"%s" -- "%s" ' % \ (s.element.name[0], d.element.name[0]) def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_ns_image_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.file: exp_desc = "" try: top = topdl.topology_from_xml(filename=opts.file, top="experiment") except IOError: sys.exit("Cannot read description file (%s)" % opts.file) else: sys.exit("Must specify an experiment description (--file)") if not opts.master: opts.master="dummy" if opts.format and opts.outfile: fmt = opts.format file = opts.outfile elif not opts.format and opts.outfile: fmt = opts.outfile[-3:] if fmt not in ("png", "jpg", "dot", "svg"): sys.exit("Unexpected file type and no format specified") file = opts.outfile elif opts.format and not opts.outfile: fmt = opts.format file = None else: fmt="dot" file = None self.gen_image(top, len(top.elements), file, fmt, opts.neato, opts.labels, opts.pixels) class terminate(fedd_rpc): def __init__(self): """ Termination request """ fedd_rpc.__init__(self) def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_terminate_opts() (opts, args) = parser.parse_args() (user, cert) = self.get_user_info() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.exp_name and opts.exp_certfile: sys.exit("Only one of --experiment_cert and " +\ "--experiment_name permitted") if opts.print_log and opts.logfile: sys.exit("Only one of --logfile and --print_log is permitted") elif opts.print_log: out = sys.stdout elif opts.logfile: try: out = open(opts.logfile, "w") except IOError,e: sys.exit("Cannot open logfile: %s" %e) else: out = None exp_id = None if opts.exp_certfile: exp_id = { 'fedid': fedid(file=opts.exp_certfile) } if opts.exp_name: exp_id = { 'localname' : opts.exp_name } if not exp_id: sys.exit("Must give one of --experiment_cert and " +\ "--experiment_name"); req = { 'experiment': exp_id, 'force': opts.force } try: resp_dict = self.do_rpc(req, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('Terminate')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: print e sys.exit("Error processing RPC: %s" % e) if out: log = resp_dict.get('deallocationLog', None) if log: print >>out, log out.close() else: out.close() sys.exit("No log returned") class terminate_segment(fedd_rpc): def __init__(self): """ Termination request """ fedd_rpc.__init__(self) def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_terminate_opts() (opts, args) = parser.parse_args() (user, cert) = self.get_user_info() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.exp_name and opts.exp_certfile: sys.exit("Only one of --experiment_cert and " +\ "--experiment_name permitted") if opts.print_log and opts.logfile: sys.exit("Only one of --logfile and --print_log is permitted") elif opts.print_log: out = sys.stdout elif opts.logfile: try: out = open(opts.logfile, "w") except IOError,e: sys.exit("Cannot open logfile: %s" %e) else: out = None exp_id = None if opts.exp_certfile: exp_id = { 'fedid': fedid(file=opts.exp_certfile) } if opts.exp_name: exp_id = { 'localname' : opts.exp_name } if not exp_id: sys.exit("Must give one of --experiment_cert and " +\ "--experiment_name"); req = { 'allocID': exp_id, 'force': opts.force } try: resp_dict = self.do_rpc(req, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('TerminateSegment')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: print e sys.exit("Error processing RPC: %s" % e) if out: log = resp_dict.get('deallocationLog', None) if log: print >>out, log out.close() else: out.close() sys.exit("No log returned") class new(fedd_rpc): def __init__(self): fedd_rpc.__init__(self) def __call__(self): # Process the options using the customized option parser defined above parser = fedd_new_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) out_certfile = opts.out_certfile msg = { } if opts.exp_name: msg['experimentID'] = { 'localname': opts.exp_name } if opts.debug > 1: print >>sys.stderr, msg try: resp_dict = self.do_rpc(msg, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller("New")) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) if opts.debug > 1: print >>sys.stderr, resp_dict ea = resp_dict.get('experimentAccess', None) if out_certfile and ea and ea.has_key('X509'): try: f = open(out_certfile, "w") print >>f, ea['X509'] f.close() except IOError: sys.exit('Could not write to %s' % out_certfile) eid = resp_dict.get('experimentID', None) if eid: for id in eid: for k in id.keys(): print "%s: %s" % (k, id[k]) st = resp_dict.get('experimentStatus', None) if st: print "status: %s" % st class create(fedd_rpc): def __init__(self): fedd_rpc.__init__(self) def __call__(self): parser = fedd_create_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if not opts.project : parser.error('--project is required') if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.file: exp_desc = "" try: f = open(opts.file, 'r') for line in f: exp_desc += line f.close() except IOError: sys.exit("Cannot read description file (%s)" %opts.file) else: sys.exit("Must specify an experiment description (--file)") if not opts.master: sys.exit("Must specify a master testbed (--master)") out_certfile = opts.out_certfile msg = { } if opts.exp_name: msg['experimentID'] = { 'localname': opts.exp_name } if opts.debug > 1: print >>sys.stderr, msg try: resp_dict = self.do_rpc(msg, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('New')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) if opts.debug > 1: print >>sys.stderr, resp_dict ea = resp_dict.get('experimentAccess', None) if out_certfile and ea and ea.has_key('X509'): try: f = open(out_certfile, "w") print >>f, ea['X509'] f.close() except IOError: sys.exit('Could not write to %s' % out_certfile) eid = resp_dict.get('experimentID', None) e_fedid = None e_local = None if eid: for id in eid: for k in id.keys(): if k =='fedid': e_fedid = id[k] elif k =='localname': e_local = id[k] msg = { 'experimentdescription': { 'ns2description': exp_desc }, 'master': opts.master, 'exportProject': { 'localname': opts.project }, } if e_fedid: msg['experimentID'] = { 'fedid': e_fedid } elif e_local: msg['experimentID'] = { 'localname': e_local } else: sys.exit("New did not return an experiment ID??") if opts.debug > 1: print >>sys.stderr, msg try: resp_dict = self.do_rpc(msg, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('Create')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) if opts.debug > 1: print >>sys.stderr, resp_dict ea = resp_dict.get('experimentAccess', None) if out_certfile and ea and ea.has_key('X509'): try: f = open(out_certfile, "w") print >>f, ea['X509'] f.close() except IOError: sys.exit('Could not write to %s' % out_certfile) eid = resp_dict.get('experimentID', None) if eid: for id in eid: for k in id.keys(): print "%s: %s" % (k, id[k]) st = resp_dict.get('experimentStatus', None) if st: print "status: %s" % st class split(fedd_rpc): def __init__(self): fedd_rpc.__init__(self) def __call__(self): # Process the options using the customized option parser defined above parser = fedd_split_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) if opts.use_fedid == True: user = fid else: sys.exit("Cannot read certificate (%s)" % cert) if opts.file: exp_desc = "" try: f = open(opts.file, 'r') for line in f: exp_desc += line f.close() except IOError: sys.exit("Cannot read description file (%s)" %opts.file) else: sys.exit("Must specify an experiment description (--file)") if not opts.master: sys.exit("Must specify a master testbed (--master)") out_certfile = opts.out_certfile msg = { 'description': { 'ns2description': exp_desc }, 'master': opts.master, 'include_fedkit': opts.fedkit, 'include_gatewaykit': opts.gatewaykit, } if opts.debug > 1: print >>sys.stderr, msg try: resp_dict = self.do_rpc(msg, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('Ns2Split')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) if opts.debug > 1: print >>sys.stderr, resp_dict out = resp_dict.get('output', None) for line in out.splitlines(): print "%s" % line class access(fedd_rpc): def __init__(self): fedd_rpc.__init__(self) def print_response_as_testbed(self, resp, label, out=sys.stdout): """Print the response as input to the splitter script""" e = resp.get('emulab', None) if e: p = e['project'] fields = { "Boss": e['boss'], "OpsNode": e['ops'], "Domain": e['domain'], "FileServer": e['fileServer'], "EventServer": e['eventServer'], "Project": unpack_id(p['name']) } if (label != None): print >> out, "[%s]" % label for l, v in fields.iteritems(): print >>out, "%s: %s" % (l, v) for u in p['user']: print >>out, "User: %s" % unpack_id(u['userID']) for a in e['fedAttr']: print >>out, "%s: %s" % (a['attribute'], a['value']) def __call__(self): node_descs = [] proj = None # Process the options using the customized option parser defined above parser = fedd_access_opts() (opts, args) = parser.parse_args() if opts.testbed == None: parser.error("--testbed is required") if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) if opts.use_fedid == True: user = fid else: sys.exit("Cannot read certificate (%s)" % cert) msg = { 'allocID': pack_id('test alloc'), 'destinationTestbed': pack_id(opts.testbed), } if len(node_descs) > 0: msg['resources'] = { 'node': [ { 'image': n.image , 'hardware': n.hardware, 'count': n.count, } for n in node_descs], } if opts.debug > 1: print >>sys.stderr, msg try: resp_dict = self.do_rpc(msg, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('RequestAccess')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e.message) if opts.debug > 1: print >>sys.stderr, resp_dict self.print_response_as_testbed(resp_dict, opts.label) # Keep requesting experiment status and printing updates to the log until the # experiment is done being created. class spew_log(fedd_rpc): def __init__(self): """ Init the superclass """ fedd_rpc.__init__(self) def __call__(self): """ The control flow. Compose the request and print the response. """ # Process the options using the customized option parser defined above parser = fedd_spew_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.exp_name and opts.exp_certfile: sys.exit("Only one of --experiment_cert and " +\ "--experiment_name permitted"); if opts.exp_certfile: exp_id = { 'fedid': fedid(file=opts.exp_certfile) } if opts.exp_name: exp_id = { 'localname' : opts.exp_name } if opts.logfile: try: out = open(opts.logfile, "w") except IOError,e: sys.exit("Cannot open logfile: %s" %e) else: out = sys.stdout req = { 'experiment': exp_id } status = "starting" log = "" log_offset = 0 update = opts.update while status == 'starting': try: resp_dict = self.do_rpc(req, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('Info')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) status = resp_dict.get('experimentStatus', None) log = resp_dict.get('allocationLog', None) if not status: sys.exit("No status in Info response??") if log: if len(log) > log_offset: print >>out, log[log_offset:], out.flush() log_offset = len(log) if status == 'starting': time.sleep(update) print >>out print >>out, status out.close() class start_segment(fedd_rpc): def __init__(self): fedd_rpc.__init__(self) def __call__(self): # Process the options using the customized option parser defined above parser = fedd_start_opts() (opts, args) = parser.parse_args() if opts.trusted: if ( not os.access(opts.trusted, os.R_OK) ) : sys.exit("Cannot read trusted certificates (%s)" % opts.trusted) if opts.debug > 0: opts.tracefile=sys.stderr (user, cert) = self.get_user_info() if opts.cert != None: cert = opts.cert if cert == None: sys.exit("No certificate given (--cert) or found") if os.access(cert, os.R_OK): fid = fedid(file=cert) else: sys.exit("Cannot read certificate (%s)" % cert) if opts.file: try: top = topdl.topology_from_xml(filename=opts.file, top='experiment') except IOError: sys.exit("Cannot read description file (%s)" %opts.file) else: sys.exit("Must specify an experiment description (--file)") msg = { 'segmentdescription': { 'topdldescription': top.to_dict() }, 'allocID': pack_id(fid), 'master': False, } if opts.debug > 1: print >>sys.stderr, msg try: resp_dict = self.do_rpc(msg, opts.url, opts.transport, cert, opts.trusted, serialize_only=opts.serialize_only, tracefile=opts.tracefile, caller=self.caller('StartSegment')) except self.RPCException, e: exit_with_fault(\ {'desc': e.desc, 'errstr': e.errstr, 'code': e.code}) except RuntimeError, e: sys.exit("Error processing RPC: %s" % e) if opts.debug > 1: print >>sys.stderr, resp_dict print resp_dict cmds = {\ 'new': new(),\ 'create': create(),\ 'split': split(),\ 'access': access(),\ 'vtopo': vtopo(),\ 'vis': vis(),\ 'info': exp_data(),\ 'multiinfo': multi_exp_data(),\ 'multistatus': multi_status(),\ 'image': image(),\ 'ns_image': ns_image(),\ 'status': status(),\ 'terminate': terminate(),\ 'spewlog': spew_log(),\ 'topdl_image': topdl_image(),\ 'start_segment': start_segment(),\ 'terminate_segment': terminate_segment(),\ } operation = cmds.get(sys.argv[1], None) if operation: del sys.argv[1] operation() else: if sys.argv[1] == '--help': sys.exit(\ '''Only context sensitive help is available. For one of the commands: %s type %s command --help to get help, e.g., %s create --help ''' % (", ".join(cmds.keys()), sys.argv[0], sys.argv[0])) else: sys.exit("Bad command: %s. Valid ones are: %s" % \ (sys.argv[1], ", ".join(cmds.keys())))