#!/usr/local/bin/python import logging import util 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 emulab_segment: class node_info: def __init__(self, name, pname, status=None, osname=None, osversion=None, image=None): self.name = name self.pname = pname 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('image', self.image) return rv def __init__(self, boss, cert): self.ctxt = fedd_ssl_context(my_cert=cert) self.ctxt.set_verify(SSL.verify_none, 10) self.boss = boss 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.status = [ ] self.node_info = emulab_segment.node_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" % e) raise 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): """ 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 self.debug: if self.log: self.log.debug("[make_null_experiment]: " + \ "(debug) Creating experiment") return True else: params = { 'proj': pid, 'exp': eid, 'nsfilestr': self.null, '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_mapping(self, pid, eid): """ Get the physical to virtual mapping from the expinfo command and save it in the self.map member. """ ev_active = ('ISUP', 'ALWAYSUP' ) ev_starting = ('REBOOTED', 'REBOOTING','PXEBOOTING', 'BOOTING', 'RELOADSETUP', 'RELOADING', 'RELOADDONE', 'RELOADDONEV2', 'TBSETUP') ev_terminating = ( 'SHUTDOWN' ) 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") 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) 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'], st, osname, osversion, osimage) if self.log: self.log.info("Mapping complete") return True else: raise service_error(service_error.internal, "Cannot get node mapping of segment:%s/%s" % (pid, eid)) def get_image(self, node, top): for e in top.elements: if isinstance(e, topdl.Computer): if node == e.name: if e.os: return e.os[0].get_attribute('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_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