Changeset 06c1dba88c7544851f08b5817f4ea0064ed4fefc

Show
Ignore:
Timestamp:
01/09/12 16:05:27 (4 months ago)
Author:
Ted Faber <faber@…>
Children:
c7141dc6739a15071ba1ae8386e6457078c1cc41
Parents:
7f57435a6dc5f5eb9336148c09eb218296e23659
git-committer:
Ted Faber <faber@isi.edu> / 2012-01-09T16:05:27Z-0800
Message:

Fisrt step to multi-user - checkpoint

Location:
fedd/federation
Files:
1 added
1 removed
2 modified
1 moved

Legend:

Unmodified
Added
Removed
  • fedd/federation/emulab_access.py

    r7f57435 r06c1dba  
    3232import topdl 
    3333import list_log 
    34 import proxy_emulab_segment 
    35 import local_emulab_segment 
     34import emulab_segment 
    3635 
    3736 
     
    9897 
    9998        self.access_type = self.access_type.lower() 
    100         if self.access_type == 'remote_emulab': 
    101             self.start_segment = proxy_emulab_segment.start_segment 
    102             self.stop_segment = proxy_emulab_segment.stop_segment 
    103             self.info_segment = proxy_emulab_segment.info_segment 
    104             self.operation_segment = proxy_emulab_segment.operation_segment 
    105         elif self.access_type == 'local_emulab': 
    106             self.start_segment = local_emulab_segment.start_segment 
    107             self.stop_segment = local_emulab_segment.stop_segment 
    108             self.info_segment = local_emulab_segment.info_segment 
    109             self.operation_segment = local_emulab_segment.operation_segment 
    110         else: 
    111             self.start_segment = None 
    112             self.stop_segment = None 
    113             self.info_segment = None 
    114             self.operation_segment = None 
     99        self.start_segment = emulab_segment.start_segment 
     100        self.stop_segment = emulab_segment.stop_segment 
     101        self.info_segment = emulab_segment.info_segment 
     102        self.operation_segment = emulab_segment.operation_segment 
    115103 
    116104        self.restricted = [ ] 
  • fedd/federation/emulab_segment.py

    rcebcdce r06c1dba  
    11#!/usr/local/bin/python 
    22 
     3import sys, os 
     4import re 
     5 
     6import tempfile 
     7import subprocess 
    38import logging  
     9import time 
     10import signal 
     11 
    412import util 
    5 import topdl 
    613 
    7 from M2Crypto import SSL 
    8 from M2Crypto.m2xmlrpclib import SSL_Transport 
    9 from xmlrpclib import ServerProxy, dumps, loads, Fault, Error, Binary 
    10 from M2Crypto.SSL import SSLError 
    11 from M2Crypto.BIO import BIOError 
    12 from socket import error as socket_error 
    13 from socket import sslerror 
    14 import httplib 
    15 from federation.util import fedd_ssl_context 
    16 from federation import service_error 
    17 from federation.operation_status import operation_status 
     14from ssh_emulab_segment import ssh_emulab_segment 
     15from xmlrpc_emulab_segment import xmlrpc_emulab_segment 
    1816 
    19 class emulab_segment: 
    20     class node_info: 
    21         def __init__(self, name, pname, status=None, osname=None,  
    22                 osversion=None, image=None): 
    23             self.name = name 
    24             self.pname = pname 
    25             self.status = status 
    26             self.osname = osname 
    27             self.osversion = osversion 
    28             self.image = image 
    29         def getOS(self): 
    30             rv = None 
    31             if self.osname or self.osversion: 
    32                 rv = topdl.OperatingSystem(name=self.osname,  
    33                         version=self.osversion) 
    34             if self.image and rv: 
    35                 rv.set_attribute('emulab_access:image', self.image) 
    36             return rv 
    3717 
    38     def __init__(self, boss, cert): 
    39         self.ctxt = fedd_ssl_context(my_cert=cert) 
    40         self.ctxt.set_verify(SSL.verify_none, 10) 
    41         self.boss = boss 
    42         self.null = """ 
    43 set ns [new Simulator] 
    44 source tb_compat.tcl 
     18class start_segment(ssh_emulab_segment, xmlrpc_emulab_segment): 
     19    def __init__(self, log=None, keyfile=None, debug=False, boss=None, 
     20            cert=None): 
     21        ssh_emulab_segment.__init__(self, log=log, keyfile=keyfile, debug=debug) 
     22        xmlrpc_emulab_segment.__init__(self, boss=boss, cert=cert) 
    4523 
    46 set a [$ns node] 
     24    def set_up_experiment_filespace(self, user, host, pid, eid, tmpdir): 
     25        """ 
     26        Send all the software and configuration files into the experiment's 
     27        file space.  To reduce the number of ssh connections, we script many 
     28        changes and execute the script. 
     29        """ 
     30        # Configuration directories on the remote machine 
     31        proj_dir = "/proj/%s/exp/%s/tmp" % (pid, eid) 
     32        softdir = "/proj/%s/software/%s" % (pid, eid) 
     33        # Local software dir 
     34        lsoftdir = "%s/software" % tmpdir 
    4735 
    48 $ns rtproto Session 
    49 $ns run  
    50 """ 
    51         self.log =  getattr(self, 'log', None)  
    52         self.debug = getattr(self, 'debug', False) 
    53         self.node = { } 
    54         self.status = [ ] 
    55         self.node_info = emulab_segment.node_info 
     36        # Open up a temporary file to contain a script for setting up the 
     37        # filespace for the new experiment. 
     38        self.log.info("[start_segment]: creating script file") 
     39        try: 
     40            sf, scriptname = tempfile.mkstemp() 
     41            scriptfile = os.fdopen(sf, 'w') 
     42        except EnvironmentError: 
     43            return False 
    5644 
    57     def emulab_call(self, method, params): 
    58         VERSION = 0.1 
    59         try: 
    60             transport = SSL_Transport(self.ctxt) 
    61             port = ServerProxy(self.boss, transport=transport) 
    62             remote_method = getattr(port, method, None) 
    63             if remote_method is not None: 
    64                 resp = remote_method(VERSION, params) 
    65             else: 
    66                 raise service_error(service_error.internal,  
    67                         "Bad method: %s" % method) 
    68         except socket_error, e: 
    69             raise service_error(service_error.connect,  
    70                     "Cannot connect" % e) 
    71             raise e 
    72         except BIOError, e: 
    73             if self.log: 
    74                 self.log.warn("BIO error: %s" % e) 
    75             raise e 
    76         except sslerror, e: 
    77             if self.log: 
    78                 self.log.warn("SSL (socket) error: %s" %  e) 
    79             raise e 
    80         except SSLError, e: 
    81             if self.log: 
    82                 self.log.warn("SSL error: %s" % e) 
    83             raise e 
    84         except httplib.HTTPException, e: 
    85             if self.log: 
    86                 self.log.warn("HTTP error: %s" % e) 
    87             raise e 
    88         except Fault, f: 
    89             raise service_error(service_error.protocol,  
    90                     "Remote XMLRPC Fault: %s" % f) 
    91         except Error, e: 
    92             raise service_error(service_error.protocol,  
    93                     "Remote XMLRPC Fault: %s" % e) 
     45        scriptbase = os.path.basename(scriptname) 
    9446 
    95         code = resp.get('code', -1) 
    96         if code ==0: 
    97             return (code, resp.get('value', None)) 
     47        # Script the filesystem changes 
     48        print >>scriptfile, "/bin/rm -rf %s" % proj_dir 
     49        # Clear and create the software directory 
     50        print >>scriptfile, "/bin/rm -rf %s/*" % softdir 
     51        print >>scriptfile, 'mkdir -p %s' % proj_dir 
     52        if os.path.isdir(lsoftdir): 
     53            print >>scriptfile, 'mkdir -p %s' % softdir 
     54        print >>scriptfile, "rm -f %s" % scriptbase 
     55        scriptfile.close() 
     56 
     57        # Move the script to the remote machine 
     58        # XXX: could collide tempfile names on the remote host 
     59        if self.scp_file(scriptname, user, host, scriptbase): 
     60            os.remove(scriptname) 
    9861        else: 
    99             return (code, resp.get('output', None)) 
     62            return False 
    10063 
    101     def get_state(self, pid, eid): 
     64        # Execute the script (and the script's last line deletes it) 
     65        if not self.ssh_cmd(user, host, "sh -x %s" % scriptbase): 
     66            return False 
     67 
     68        for f in os.listdir(tmpdir): 
     69            if not os.path.isdir("%s/%s" % (tmpdir, f)): 
     70                if not self.scp_file("%s/%s" % (tmpdir, f), user, host, 
     71                        "%s/%s" % (proj_dir, f)): 
     72                    return False 
     73        if os.path.isdir(lsoftdir): 
     74            for f in os.listdir(lsoftdir): 
     75                if not os.path.isdir("%s/%s" % (lsoftdir, f)): 
     76                    if not self.scp_file("%s/%s" % (lsoftdir, f),  
     77                            user, host, "%s/%s" % (softdir, f)): 
     78                        return False 
     79        return True 
     80 
     81 
     82    def __call__(self, parent, eid, pid, user, tclfile, tmpdir, timeout=0,  
     83            gid=None): 
    10284        """ 
    103         Return the state of the experiment as reported by emulab 
     85        Start a sub-experiment on a federant. 
     86 
     87        Get the current state, and terminate the experiment if it exists. The 
     88        group membership of the experiment is difficult to determine or change, 
     89        so start with a clean slate.  Create a new one and ship data 
     90        and configs and start the experiment.  There are small ordering 
     91        differences based on the initial state of the sub-experiment. 
    10492        """ 
    10593 
    106         if self.debug: 
    107             state = 'swapped' 
    108         else: 
    109             params =  { 'proj': pid, 'exp': eid } 
    110             code, state = self.emulab_call('experiment.state', params) 
    111             if code != 0: 
    112                 state = 'none' 
     94        state = self.get_state(pid, eid) 
    11395 
    114         if self.log: 
    115             self.log.debug("State is %s" % state) 
    116         return state 
     96        if state != 'none': 
     97            self.terminate_exp(pid, eid) 
    11798 
    118     def make_null_experiment(self, pid, eid, tmpdir, gid=None): 
     99        if not self.make_null_experiment(pid, eid, tmpdir, gid): 
     100            return False 
     101 
     102        if not self.set_up_experiment_filespace(pid, eid, tmpdir): 
     103            return False 
     104 
     105        # Put the file into a string to pass to emulab. 
     106        try: 
     107            tcl = "".join([ l for l in open(tclfile,"r")]) 
     108        except EnvironmentError, e: 
     109            self.log.error("Can't read %s: %s" % (tclfile, e)) 
     110            return False 
     111         
     112        # Stage the new configuration 
     113        if not self.modify_exp(pid, eid, tcl): 
     114            self.log.error("modify failed") 
     115            return False 
     116 
     117        if not self.swap_exp(pid, eid, 'in'): 
     118            self.log.error("swap in failed") 
     119            return False 
     120        # Everything has gone OK. 
     121        self.get_mapping(pid,eid) 
     122        return True 
     123 
     124class stop_segment(local_segment, emulab_segment): 
     125    def __init__(self, log=None, keyfile=None, debug=False, boss=None,  
     126            cert=None): 
     127        local_segment.__init__(self, log=log, keyfile=keyfile, debug=debug) 
     128        emulab_segment.__init__(self, boss=boss, cert=cert) 
     129 
     130    def __call__(self, parent, user, pid, eid, gid=None, terminate=False): 
    119131        """ 
    120         Create a null copy of the experiment so that we capture any logs there 
    121         if the modify fails.  Emulab software discards the logs from a failed 
    122         startexp. 
     132        Stop a sub experiment by calling swapexp on the federant 
    123133        """ 
    124134 
    125         if self.debug: 
    126             if self.log: 
    127                 self.log.debug("[make_null_experiment]: " + \ 
    128                         "(debug) Creating experiment") 
    129             return True 
    130         else: 
    131             params = { 
    132                     'proj': pid, 
    133                     'exp': eid,  
    134                     'nsfilestr': self.null, 
    135                     'batch': False, 
    136                     'idleswap': 0, 
    137                     'noidleswap_reason': 'Federated experiment', 
    138                     'noswapin': True, 
    139                     'wait': True 
    140                     } 
    141             if gid is not None: 
    142                 params['group'] = gid 
    143             if self.log: 
    144                 self.log.info("[make_null_experiment]: Creating experiment") 
    145             code, value = self.emulab_call('experiment.startexp', params) 
     135        self.log.info("[stop_segment]: Stopping %s" % eid) 
     136        rv = False 
     137        try: 
     138            # Clean out tar files: we've gone over quota in the past 
     139            self.ssh_cmd("rm -rf /proj/%s/software/%s" % (pid, eid)) 
     140            rv = self.swap_exp(pid, eid, 'out') 
     141            if terminate: 
     142                rv = self.terminate_exp(pid, eid) 
     143        except self.cmd_timeout: 
     144            rv = False 
     145        return rv 
    146146 
    147             if self.log: 
    148                 if code == 0:  
    149                     self.log.info('[make_null_experiment]: Create succeeded') 
    150                 else:  
    151                     self.log.error('[make_null_experiment]: Create failed: %s' \ 
    152                             % value) 
     147class info_segment(local_segment, emulab_segment): 
     148    def __init__(self, log=None, keyfile=None, debug=False, boss=None,  
     149            cert=None): 
     150        local_segment.__init__(self, log=log, keyfile=keyfile, debug=debug) 
     151        emulab_segment.__init__(self, boss=boss, cert=cert) 
    153152 
    154             return code == 0 
     153    def __call__(self, parent, user, pid, eid): 
     154        self.log.info("[info_segment]: Getting info from %s" % eid) 
     155        self.get_mapping(pid,eid) 
     156        return True 
    155157 
    156     def swap_exp(self, pid, eid, direction='out', wait=True): 
    157         """ 
    158         Swap experiment in. 
    159         """ 
    160         if self.debug: 
    161             if self.log: 
    162                 self.log.info("[swap_exp]: (debug) Swapping %s %s" % \ 
    163                         (eid, direction)) 
    164             return True 
    165         else: 
    166             if self.log: 
    167                 self.log.info("[swap_exp]: Swapping %s %s" % (eid, direction)) 
    168             params = { 
    169                     'proj': pid, 
    170                     'exp': eid, 
    171                     'direction': direction, 
    172                     'wait': wait, 
    173                     } 
    174             code, value = self.emulab_call('experiment.swapexp', params) 
     158class operation_segment(local_segment, emulab_segment): 
     159    def __init__(self, log=None, keyfile=None, debug=False, boss=None,  
     160            cert=None): 
     161        local_segment.__init__(self, log=log, keyfile=keyfile, debug=debug) 
     162        emulab_segment.__init__(self, boss=boss, cert=cert) 
    175163 
    176             if self.log: 
    177                 if code == 0: self.log.info('[swap_exp]: Swap succeeded') 
    178                 else: self.log.error('[swap_exp]: Swap failed: %s' % value) 
    179  
    180         return code == 0 
    181  
    182     def terminate_exp(self, pid, eid, wait=True): 
    183         """ 
    184         Completely terminate experiment 
    185         """ 
    186         if self.debug: 
    187             if self.log: 
    188                 self.log.info("[swap_exp]: (debug) terminate %s" %  eid) 
    189             return True 
    190         else: 
    191             if self.log: 
    192                 self.log.info("[swap_exp]: Terminating %s" % eid) 
    193             params = { 
    194                     'proj': pid, 
    195                     'exp': eid, 
    196                     'wait': wait, 
    197                     } 
    198             code, value = self.emulab_call('experiment.endexp', params) 
    199  
    200             if self.log: 
    201                 if code == 0: self.log.info('[swap_exp]: Terminate succeeded') 
    202                 else: self.log.error('[swap_exp]: Terminate failed: %s' % value) 
    203  
    204         return code == 0 
    205  
    206     def modify_exp(self, pid, eid, tcl, wait=True): 
    207         if self.debug: 
    208             self.log.info("[modify_exp]: (debug) Modifying %s" % eid) 
    209             return True 
    210         else: 
    211             self.log.info("[modify_exp]: Modifying %s" % eid) 
    212             params = { 
    213                     'proj': pid, 
    214                     'exp': eid, 
    215                     'nsfilestr': tcl, 
    216                     'wait': wait, 
    217                     'reboot': True, 
    218                     'restart_eventsys': True, 
    219                     } 
    220             code, value = self.emulab_call('experiment.modify', params) 
    221             if self.log: 
    222                 if code == 0:  
    223                     self.log.info('[modify_exp]: Modify succeeded') 
    224                 else:  
    225                     self.log.error('[modify_exp]: Modify failed: %s' \ 
    226                             % value) 
    227             return code == 0 
    228  
    229     def get_osid_map(self): 
    230         oses = { } 
    231         code, osids = self.emulab_call('osid.getlist', {}) 
    232         for key, val in osids.items(): 
    233             val['imageid'] = key 
    234             oses[val['osid']] = val 
    235         return oses 
    236  
    237     def get_mapping(self, pid, eid): 
    238         """ 
    239         Get the physical to virtual mapping from the expinfo command and save 
    240         it in the self.map member. 
    241         """ 
    242  
    243         ev_active = ('ISUP', 'ALWAYSUP' ) 
    244         ev_starting = ('REBOOTED', 'REBOOTING','PXEBOOTING',  
    245                 'BOOTING', 'RELOADSETUP', 'RELOADING', 'RELOADDONE',  
    246                 'RELOADDONEV2', 'TBSETUP') 
    247         ev_terminating = ( 'SHUTDOWN' ) 
    248  
    249  
    250         if self.debug: 
    251             if self.log: 
    252                 self.log.info("[get_mapping] (debug) Generating mapping") 
    253                 return True 
    254         else: 
    255             if self.log: 
    256                 self.log.info("[get_mapping] Generating mapping") 
    257  
    258             osidmap = self.get_osid_map() 
    259  
    260             params = { 
    261                     'proj': pid, 
    262                     'exp': eid, 
    263                     'aspect': 'mapping' 
    264                     } 
    265             code, nodes = self.emulab_call('experiment.info', params) 
    266             if code ==0: 
    267                 for k, v in nodes.items(): 
    268                     if v.get('erole', False) and 'pnode' in v: 
    269                         st = v.get('status', 'up') 
    270                         ev = v.get('eventstatus', 'ISUP') 
    271                         os = v.get('osid', None) 
    272  
    273                         if st == 'up': 
    274                             if ev in ev_active: st = 'active' 
    275                             elif ev in ev_starting: st = 'starting' 
    276                             elif ev in ev_terminating: st = 'terminating' 
    277                             else: st = 'failed' 
    278                         else: st = 'failed' 
    279  
    280                         if os and os in osidmap: 
    281                            osname = osidmap[os].get('OS', None) 
    282                            osversion = osidmap[os].get('version', None) 
    283                            osimage = "%s/%s" % \ 
    284                                    ( osidmap[os].get('pid', ''),  
    285                                            osidmap[os].get('imageid', '')) 
    286                         else: 
    287                             osname = osversion = osimage = None 
    288  
    289                         self.node[k] = self.node_info(k, v['pnode'], st, 
    290                                 osname, osversion, osimage) 
    291                 if self.log: 
    292                     self.log.info("Mapping complete") 
    293                 return True 
    294             else: 
    295                 raise service_error(service_error.internal, 
    296                         "Cannot get node mapping of segment:%s/%s" % (pid, eid)) 
    297     def get_initial_image(self, node, top): 
    298         for e in top.elements: 
    299             if isinstance(e, topdl.Computer): 
    300                 if node == e.name: 
    301                     if e.os and len(e.os) == 1:  
    302                         return e.os[0].get_attribute( 
    303                                 'emulab_access:initial_image') 
    304         return None 
    305  
    306  
    307     def do_operation(self, op, lnode, pnode, params, top): 
    308         """ 
    309         Carry out operation on node in the given experiment. 
    310         """ 
    311         def get_param(params, name): 
    312             if params is None: 
    313                 return None 
    314             for d in params: 
    315                 if 'attribute' in d and d['attribute'] == name: 
    316                     return d.get('value', None) 
    317             else: 
    318                 return None 
    319  
    320         op = op.lower() 
    321  
    322         if op == 'restore': 
    323             state = get_param(params, 'state') 
    324             if state is None: 
    325                 self.status.append(operation_status(lnode,  
    326                         operation_status.bad_param, 'No state to restore')) 
    327                 return False 
    328             elif state == 'initial':  
    329                 image = self.get_initial_image(lnode, top) 
    330                 if image: 
    331                     pid, iid = image.split('/') 
    332                     p = {'nodes': pnode, 'imagename': iid, 'imageproj': pid,  
    333                             'wait': False} 
    334                     code, result = self.emulab_call('node.reload', p) 
    335                     if code == 0:  
    336                         self.status.append(operation_status(lnode, 
    337                             operation_status.success, 'reloading')) 
    338                         return True 
    339                     else: 
    340                         self.status.append(operation_status(lnode, 
    341                                 operation_status.federant,  
    342                                 'Error code: %d' % code)) 
    343                         return False 
    344                 else: 
    345                     self.status.append(operation_status(lnode, 
    346                             operation_status.federant,  
    347                             'cannot find imageid??')) 
    348                     return False 
    349             elif state == 'boot':  
    350                 p = {'nodes': pnode, 'wait': False} 
    351                 code, result = self.emulab_call('node.reboot', p) 
    352                 if code == 0:  
    353                     self.status.append(operation_status(lnode, 
    354                         operation_status.success, 'rebooting')) 
    355                     return True 
    356                 else: 
    357                     self.status.append(operation_status(lnode, 
    358                             operation_status.federant, 'Error code: %d' % code)) 
    359                     return False 
    360             else:  
    361                 if '/' in state: 
    362                     pid, iid = state.split('/') 
    363                 else: 
    364                     pid = 'emulab-ops' 
    365                     iid = state 
    366  
    367                 p = {'nodes': pnode, 'imagename': iid, 'imageproj': pid,  
    368                         'wait': False} 
    369                 code, result = self.emulab_call('node.reload', p) 
    370                 if code == 0:  
    371                     self.status.append(operation_status(lnode, 
    372                         operation_status.success, 'reloading')) 
    373                     return True 
    374                 else: 
    375                     self.status.append(operation_status(lnode, 
    376                             operation_status.federant,  
    377                             'Error code: %d' % code)) 
    378                     return False 
    379         else:  
    380             self.status.append(operation_status(lnode, operation_status.unsupp)) 
    381             return False 
    382  
     164    def __call__(self, parent, op, targets, param, top): 
     165        for l, p in targets.items(): 
     166            self.log.info("[operation_segment]: Calling op %s on %s(%s)" % \ 
     167                    (op, l,p)) 
     168            self.do_operation(op, l, p, param, top) 
     169        return True 
  • fedd/federation/ssh_emulab_segment.py

    r5bf359d r06c1dba  
    1212from service_error import service_error 
    1313 
    14 class proxy_segment: 
     14class ssh_emulab_segment: 
    1515    """ 
    1616    Base class for segment starter classes that access their underlying testebd