Changeset 06c1dba for fedd


Ignore:
Timestamp:
Jan 9, 2012 4:05:27 PM (9 years ago)
Author:
Ted Faber <faber@…>
Branches:
compt_changes, info-ops, master
Children:
c7141dc
Parents:
7f57435
Message:

Fisrt step to multi-user - checkpoint

Location:
fedd/federation
Files:
1 added
1 deleted
2 edited
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

    r7f57435 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

    r7f57435 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
Note: See TracChangeset for help on using the changeset viewer.