Changeset 06c1dba
- Timestamp:
- Jan 9, 2012 4:05:27 PM (13 years ago)
- Branches:
- compt_changes, info-ops, master
- Children:
- c7141dc
- Parents:
- 7f57435
- Location:
- fedd/federation
- Files:
-
- 1 added
- 1 deleted
- 2 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
fedd/federation/emulab_access.py
r7f57435 r06c1dba 32 32 import topdl 33 33 import list_log 34 import proxy_emulab_segment 35 import local_emulab_segment 34 import emulab_segment 36 35 37 36 … … 98 97 99 98 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 115 103 116 104 self.restricted = [ ] -
fedd/federation/emulab_segment.py
r7f57435 r06c1dba 1 1 #!/usr/local/bin/python 2 2 3 import sys, os 4 import re 5 6 import tempfile 7 import subprocess 3 8 import logging 9 import time 10 import signal 11 4 12 import util 5 import topdl6 13 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 14 from ssh_emulab_segment import ssh_emulab_segment 15 from xmlrpc_emulab_segment import xmlrpc_emulab_segment 18 16 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 = name24 self.pname = pname25 self.status = status26 self.osname = osname27 self.osversion = osversion28 self.image = image29 def getOS(self):30 rv = None31 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 rv37 17 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 18 class 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) 45 23 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 47 35 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_info36 # 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 56 44 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) 94 46 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) 98 61 else: 99 return (code, resp.get('output', None))62 return False 100 63 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): 102 84 """ 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. 104 92 """ 105 93 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) 113 95 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) 117 98 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 124 class 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): 119 131 """ 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 123 133 """ 124 134 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 146 146 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) 147 class 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) 153 152 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 155 157 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) 158 class 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) 175 163 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 12 12 from service_error import service_error 13 13 14 class proxy_segment:14 class ssh_emulab_segment: 15 15 """ 16 16 Base class for segment starter classes that access their underlying testebd
Note: See TracChangeset
for help on using the changeset viewer.