source: fedd/federation/emulab_segment.py @ db3da0b

compt_changesinfo-ops
Last change on this file since db3da0b was 4b256af, checked in by Ted Faber <faber@…>, 12 years ago

Track rebooting/reloading better.

  • Property mode set to 100644
File size: 9.6 KB
Line 
1#!/usr/local/bin/python
2
3import logging 
4import util
5import topdl
6
7from M2Crypto import SSL
8from M2Crypto.m2xmlrpclib import SSL_Transport
9from xmlrpclib import ServerProxy, dumps, loads, Fault, Error, Binary
10from M2Crypto.SSL import SSLError
11from M2Crypto.BIO import BIOError
12from socket import error as socket_error
13from socket import sslerror
14import httplib
15from federation.util import fedd_ssl_context
16from federation import service_error
17from federation.operation_status import operation_status
18
19class 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('image', self.image)
36            return rv
37
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 = """
43set ns [new Simulator]
44source tb_compat.tcl
45
46set a [$ns node]
47
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
56
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)
94
95        code = resp.get('code', -1)
96        if code ==0:
97            return (code, resp.get('value', None))
98        else:
99            return (code, resp.get('output', None))
100
101    def get_state(self, pid, eid):
102        """
103        Return the state of the experiment as reported by emulab
104        """
105
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'
113
114        if self.log:
115            self.log.debug("State is %s" % state)
116        return state
117
118    def make_null_experiment(self, pid, eid, tmpdir, gid=None):
119        """
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.
123        """
124
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)
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)
153
154            return code == 0
155
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)
175
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_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: 
302                        return e.os[0].get_attribute('image')
303        return None
304
305
306    def do_operation(self, op, lnode, pnode, params, top):
307        """
308        Carry out operation on node in the given experiment.
309        """
310        def get_param(params, name):
311            if params is None:
312                return None
313            for d in params:
314                if 'attribute' in d and d['attribute'] == name:
315                    return d.get('value', None)
316            else:
317                return None
318
319        op = op.lower()
320
321        if op == 'restore':
322            state = get_param(params, 'state')
323            if state is None:
324                self.status.append(operation_status(lnode, 
325                        operation_status.bad_param, 'No state to restore'))
326                return False
327            elif state == 'initial': 
328                image = self.get_image(lnode, top)
329                if image:
330                    pid, iid = image.split('/')
331                    p = {'nodes': pnode, 'imagename': iid, 'imageproj': pid, 
332                            'wait': False}
333                    code, result = self.emulab_call('node.reload', p)
334                    if code == 0: 
335                        self.status.append(operation_status(lnode,
336                            operation_status.success, 'reloading'))
337                        return True
338                    else:
339                        self.status.append(operation_status(lnode,
340                                operation_status.federant, 
341                                'Error code: %d' % code))
342                        return False
343                else:
344                    self.status.append(operation_status(lnode,
345                            operation_status.federant, 
346                            'cannot find imageid??'))
347                    return False
348            elif state == 'boot': 
349                p = {'nodes': pnode, 'wait': False}
350                code, result = self.emulab_call('node.reboot', p)
351                if code == 0: 
352                    self.status.append(operation_status(lnode,
353                        operation_status.success, 'rebooting'))
354                    return True
355                else:
356                    self.status.append(operation_status(lnode,
357                            operation_status.federant, 'Error code: %d' % code))
358                    return False
359            else: 
360                self.status.append(operation_status(lnode, 
361                        operation_status.bad_param, 
362                        "Unsupported state %s" % state))
363                return False
364        else: 
365            self.status.append(operation_status(lnode, operation_status.unsupp))
366            return False
367
Note: See TracBrowser for help on using the repository browser.