#!/usr/local/bin/python import logging import util from deter import topdl from M2Crypto import SSL from M2Crypto.m2xmlrpclib import SSL_Transport from xmlrpclib import ServerProxy, dumps, loads, Fault, Error, Binary from M2Crypto.SSL import SSLError from M2Crypto.BIO import BIOError from socket import error as socket_error from socket import sslerror import httplib from federation.util import fedd_ssl_context from federation import service_error from federation.operation_status import operation_status class xmlrpc_emulab_segment: class node_info: def __init__(self, name, pname, lname=None, status=None, osname=None, osversion=None, image=None): self.name = name self.pname = pname self.lname = lname self.status = status self.osname = osname self.osversion = osversion self.image = image def getOS(self): rv = None if self.osname or self.osversion: rv = topdl.OperatingSystem(name=self.osname, version=self.osversion) if self.image and rv: rv.set_attribute('emulab_access:image', self.image) return rv class lan_info: def __init__(self, name, cap=None, delay=None): self.name = name self.cap = cap self.delay = delay def __repr__(self): return "%s %s %s" % (self.name, self.cap, self.delay) class interface_info: def __init__(self, name, node, sub, cap=None, delay=None): self.name = name self.node = node self.sub = sub self.cap = cap self.delay = delay def __repr__(self): return "%s %s %s %s %s" % (self.name, self.node, self.sub, self.cap, self.delay) def __init__(self, boss, ops, cert): self.ctxt = fedd_ssl_context(my_cert=cert) self.ctxt.set_verify(SSL.verify_none, 10) self.boss = boss self.ops = ops self.null = """ set ns [new Simulator] source tb_compat.tcl set a [$ns node] $ns rtproto Session $ns run """ self.log = getattr(self, 'log', None) self.debug = getattr(self, 'debug', False) self.node = { } self.subs = { } self.interfaces = { } self.status = [ ] self.node_info = xmlrpc_emulab_segment.node_info self.lan_info = xmlrpc_emulab_segment.lan_info self.interface_info = xmlrpc_emulab_segment.interface_info def emulab_call(self, method, params): VERSION = 0.1 try: transport = SSL_Transport(self.ctxt) port = ServerProxy(self.boss, transport=transport) remote_method = getattr(port, method, None) if remote_method is not None: resp = remote_method(VERSION, params) else: raise service_error(service_error.internal, "Bad method: %s" % method) except socket_error, e: raise service_error(service_error.connect, "Cannot connect: %s" % e) except BIOError, e: if self.log: self.log.warn("BIO error: %s" % e) raise e except sslerror, e: if self.log: self.log.warn("SSL (socket) error: %s" % e) raise e except SSLError, e: if self.log: self.log.warn("SSL error: %s" % e) raise e except httplib.HTTPException, e: if self.log: self.log.warn("HTTP error: %s" % e) raise e except Fault, f: raise service_error(service_error.protocol, "Remote XMLRPC Fault: %s" % f) except Error, e: raise service_error(service_error.protocol, "Remote XMLRPC Fault: %s" % e) code = resp.get('code', -1) if code ==0: return (code, resp.get('value', None)) else: return (code, resp.get('output', None)) def get_state(self, pid, eid): """ Return the state of the experiment as reported by emulab """ if self.debug: state = 'swapped' else: params = { 'proj': pid, 'exp': eid } code, state = self.emulab_call('experiment.state', params) if code != 0: state = 'none' if self.log: self.log.debug("State is %s" % state) return state def make_null_experiment(self, pid, eid, tmpdir, gid=None, ns_str=None): """ Create a null copy of the experiment so that we capture any logs there if the modify fails. Emulab software discards the logs from a failed startexp. If ns_str is given, use that to construct the null experiment. """ if ns_str is None: ns_str = self.null if self.debug: if self.log: self.log.debug("[make_null_experiment]: " + \ "(debug) Creating experiment") return True else: params = { 'proj': pid, 'exp': eid, 'nsfilestr': ns_str, 'batch': False, 'idleswap': 0, 'noidleswap_reason': 'Federated experiment', 'noswapin': True, 'wait': True } if gid is not None: params['group'] = gid if self.log: self.log.info("[make_null_experiment]: Creating experiment") code, value = self.emulab_call('experiment.startexp', params) if self.log: if code == 0: self.log.info('[make_null_experiment]: Create succeeded') else: self.log.error('[make_null_experiment]: Create failed: %s' \ % value) return code == 0 def swap_exp(self, pid, eid, direction='out', wait=True): """ Swap experiment in. """ if self.debug: if self.log: self.log.info("[swap_exp]: (debug) Swapping %s %s" % \ (eid, direction)) return True else: if self.log: self.log.info("[swap_exp]: Swapping %s %s" % (eid, direction)) params = { 'proj': pid, 'exp': eid, 'direction': direction, 'wait': wait, } code, value = self.emulab_call('experiment.swapexp', params) if self.log: if code == 0: self.log.info('[swap_exp]: Swap succeeded') else: self.log.error('[swap_exp]: Swap failed: %s' % value) return code == 0 def terminate_exp(self, pid, eid, wait=True): """ Completely terminate experiment """ if self.debug: if self.log: self.log.info("[swap_exp]: (debug) terminate %s" % eid) return True else: if self.log: self.log.info("[swap_exp]: Terminating %s" % eid) params = { 'proj': pid, 'exp': eid, 'wait': wait, } code, value = self.emulab_call('experiment.endexp', params) if self.log: if code == 0: self.log.info('[swap_exp]: Terminate succeeded') else: self.log.error('[swap_exp]: Terminate failed: %s' % value) return code == 0 def modify_exp(self, pid, eid, tcl, wait=True): if self.debug: self.log.info("[modify_exp]: (debug) Modifying %s" % eid) return True else: self.log.info("[modify_exp]: Modifying %s" % eid) params = { 'proj': pid, 'exp': eid, 'nsfilestr': tcl, 'wait': wait, 'reboot': True, 'restart_eventsys': True, } code, value = self.emulab_call('experiment.modify', params) if self.log: if code == 0: self.log.info('[modify_exp]: Modify succeeded') else: self.log.error('[modify_exp]: Modify failed: %s' \ % value) return code == 0 def get_osid_map(self): oses = { } code, osids = self.emulab_call('osid.getlist', {}) for key, val in osids.items(): val['imageid'] = key oses[val['osid']] = val return oses def get_links(self, pid, eid): params = { 'proj': pid, 'exp': eid, 'aspect': 'links' } code, links = self.emulab_call('experiment.info', params) if code ==0: lans = { } # Links contains per-interface information about all the LANs in # the emulab experiment. First construct the LANs, then look for # overrides. for i in links.values(): if i['name'] not in lans: lans[i['name']] = { 'cap': None, 'delay': None }; cap = max(i['bandwidth'], i['r_bandwidth']) delay = max(i['delay'], i['r_delay']) if delay == 0.0: delay = None if lans[i['name']]['cap'] is None or \ cap > lans[i['name']]['cap']: lans[i['name']]['cap'] = cap if (lans[i['name']]['delay'] is None and delay is not None) or \ delay > lans[i['name']]['delay']: lans[i['name']]['delay'] = delay for s, info in lans.items(): self.subs[s] = self.lan_info(s, info['cap'], info['delay']) # XXX: should deal better with r_delay != delay, etc for i in links.values(): li = self.subs[i['name']] cap = max(i['bandwidth'], i['r_bandwidth']) delay = max(i['delay'], i['r_delay']) if delay == 0.0: delay = None if cap != li.cap or delay != li.delay: node, dummy = i['member'].split(':', 1) self.interfaces[i['member']] = \ self.interface_info(i['member'], node, i['name'], cap, delay) if self.log: self.log.info("Link mapping complete") return True else: raise service_error(service_error.internal, "Cannot get link mapping of segment:%s/%s" % (pid, eid)) def get_nodes(self, pid, eid): ev_active = ('ISUP', 'ALWAYSUP' ) ev_starting = ('REBOOTED', 'REBOOTING','PXEBOOTING', 'BOOTING', 'RELOADSETUP', 'RELOADING', 'RELOADDONE', 'RELOADDONEV2', 'TBSETUP') ev_terminating = ( 'SHUTDOWN' ) osidmap = self.get_osid_map() params = { 'proj': pid, 'exp': eid, 'aspect': 'mapping' } code, nodes = self.emulab_call('experiment.info', params) if code ==0: for k, v in nodes.items(): if v.get('erole', False) and 'pnode' in v: st = v.get('status', 'up') ev = v.get('eventstatus', 'ISUP') os = v.get('osid', None) lname = "%s.%s.%s" % (k, eid, pid) if st == 'up': if ev in ev_active: st = 'active' elif ev in ev_starting: st = 'starting' elif ev in ev_terminating: st = 'terminating' else: st = 'failed' else: st = 'failed' if os and os in osidmap: osname = osidmap[os].get('OS', None) osversion = osidmap[os].get('version', None) osimage = "%s/%s" % \ ( osidmap[os].get('pid', ''), osidmap[os].get('imageid', '')) else: osname = osversion = osimage = None self.node[k] = self.node_info(k, v['pnode'], lname, st, osname, osversion, osimage) if self.log: self.log.info("Node mapping complete") return True else: raise service_error(service_error.internal, "Cannot get node mapping of segment:%s/%s" % (pid, eid)) def get_mapping(self, pid, eid): """ Get the physical to virtual mapping from the expinfo command and save it in the self.map member. """ if self.debug: if self.log: self.log.info("[get_mapping] (debug) Generating mapping") return True else: if self.log: self.log.info("[get_mapping] Generating mapping") self.get_nodes(pid, eid) self.get_links(pid, eid) return True def get_initial_image(self, node, top): for e in top.elements: if isinstance(e, topdl.Computer): if node == e.name: if e.os and len(e.os) == 1: return e.os[0].get_attribute( 'emulab_access:initial_image') return None def do_operation(self, op, lnode, pnode, params, top): """ Carry out operation on node in the given experiment. """ def get_param(params, name): if params is None: return None for d in params: if 'attribute' in d and d['attribute'] == name: return d.get('value', None) else: return None op = op.lower() if op == 'restore': state = get_param(params, 'state') if state is None: self.status.append(operation_status(lnode, operation_status.bad_param, 'No state to restore')) return False elif state == 'initial': image = self.get_initial_image(lnode, top) if image: pid, iid = image.split('/') p = {'nodes': pnode, 'imagename': iid, 'imageproj': pid, 'wait': False} code, result = self.emulab_call('node.reload', p) if code == 0: self.status.append(operation_status(lnode, operation_status.success, 'reloading')) return True else: self.status.append(operation_status(lnode, operation_status.federant, 'Error code: %d' % code)) return False else: self.status.append(operation_status(lnode, operation_status.federant, 'cannot find imageid??')) return False elif state == 'boot': p = {'nodes': pnode, 'wait': False} code, result = self.emulab_call('node.reboot', p) if code == 0: self.status.append(operation_status(lnode, operation_status.success, 'rebooting')) return True else: self.status.append(operation_status(lnode, operation_status.federant, 'Error code: %d' % code)) return False else: if '/' in state: pid, iid = state.split('/') else: pid = 'emulab-ops' iid = state p = {'nodes': pnode, 'imagename': iid, 'imageproj': pid, 'wait': False} code, result = self.emulab_call('node.reload', p) if code == 0: self.status.append(operation_status(lnode, operation_status.success, 'reloading')) return True else: self.status.append(operation_status(lnode, operation_status.federant, 'Error code: %d' % code)) return False else: self.status.append(operation_status(lnode, operation_status.unsupp)) return False