mod_PLC4.py.in 28.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# -*- python -*-
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#

#
# This is a modified version of mod_PLCNM.py.in, lifted because the upgrade
# to PLC 4.0 and the NM is going to require discrete modules for talking to
# both the new and old PLCs for awhile.  Not only that, but the new NM is
# somewhat different, so it makes sense to put the PLC4 and new NM stuff in its
# own file at this point.
#

import sys
sys.path.append("@prefix@/lib")

import xmlrpclib
import getopt
import fcntl
import time
import calendar
import cPickle
import os
26
import socket
27
28
29
30
31

from libtestbed import *
from aspects import wrap_around
from timer_advisories import timeAdvice

32
33
#from sshhttp import *
import popen2
34

35
36
37
38
39
40
41
42
43
#
# output control vars
#
verbose = 0
debug = 0

#
# PLC constants
#
44
DEF_PLC_URI = "https://www.planet-lab.org/PLCAPI/"
45
#DEF_PLC_URI = "https://n.djplc.tbres.emulab.net/PLCAPI/"
46
47
48
49
50
# these are now sucked in from a file
DEF_PLC_USER = ""
DEF_PLC_PASS = ""
DEF_PLC_PASS_FILE = "@prefix@/etc/plab/plc.pw"

51
52
DEF_NM_PORT = "812"
DEF_NM_LEGACY_PORT = "814"
53
54
55
56

#
# A bunch of time constants / intervals (in seconds)
#
57
58
59
MAX_PLC_LEASELEN = 2*MONTH-5*DAY   # defined by PLC as ~two months (56 days)
                                   # but have to use 55 now cause PLC won't take 56.
MIN_LEASE_WINDOW = 2*MONTH-12*DAY  # minimum time until expiration
60
61
62
63
64
65
66
67
68
69
70
71
72
MAX_LEASE_SLOP = 600 # (ten minutes)
MAX_CACHE_TIME = HOUR # (one hour)

EMULABMAN_EMAIL = "emulabman@emulab.net"

DEF_SLICE_DESC = "Slice created by Emulab"
DEF_EMULAB_URL = "http://www.emulab.net"

INSTMETHOD_DELEGATED = "delegated"
INSTMETHOD_PLCINST = "plc-instantiated"
DEF_EMULAB_INSTMETHOD = INSTMETHOD_DELEGATED

# XXX: need to figure out what these are in the new NM's context
73
# hm, doesn't seem to have a version() call
74
75
76
77
MAJOR_VERS = 1
MINOR_VERS = 0
MIN_REV    = 10

78
79
class NM3agent:
    def __init__(self, IP, nodeid, nmport = DEF_NM_LEGACY_PORT):
80
81
        self.__server = xmlrpclib.ServerProxy("http://" + IP + ":" +
                                              nmport + "/")
82
83
84
85
86
87
        self.__vers = [0,0,0]
        self.IP = IP
        self.nodeid = nodeid
        pass

    def create_sliver(self, ticket):
88
89
90
91
        res = self.__server.create_sliver(xmlrpclib.Binary(ticket))
        if debug:
            print "NM3: create_sliver: res = %s" % str(res)
        return res
92
93

    def delete_sliver(self, rcap):
94
95
96
97
        res = self.__server.delete_sliver(rcap)
        if debug:
            print "NM3: delete_sliver: res = %s" % str(res)
        return res
98
99

    def version(self):
100
101
102
103
104
105
106
107
108
        if self.__vers == [0,0,0]:
            res = self.__server.version()
            if type(res) == list and len(res) == 2 and res[0] == 0:
                verslist = res[1].split(".")
                major = verslist[0]
                minor, revision = verslist[1].split("-")
                self.__vers = [int(major), int(minor), int(revision)]
                pass
            pass
109
        return self.__vers
110
111
112
    
    def getAgentClass(self):
        return self.__class__
113
114
115

    pass

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
DEF_NM_DELEGATE_ACCT = "utah_elab_delegate"
DEF_NM_DELEGATE_KEY = "/root/.ssh/id_rsa.plabdel"
DEF_NM_SSHCMD = "/usr/bin/ssh -oStrictHostKeyChecking=no -l %s -i %s %s"

class NM4agent:
    def __init__(self,IP,nodeid,nmport=DEF_NM_PORT,
                 del_acct=DEF_NM_DELEGATE_ACCT,
                 del_key=DEF_NM_DELEGATE_KEY):
        #self._sPipeMethod = SshConnection(DEF_NM_SSHCMD % (del_acct,
        #                                                   del_key,
        #                                                   IP))

        # Instead of ssh xmlrpc transport, we use xmlrpclib load/dumps.
        self._isopen = False
        self.delacct = del_acct
        self.delkey = del_key
        
        self.__vers = [4,0,0]
        self.IP = IP
        self.nodeid = nodeid
        pass

    def _open(self):
        if not self._isopen:
            try:
                self.__agentconn = popen2.Popen3(DEF_NM_SSHCMD % (self.delacct,
                                                                  self.delkey,
                                                                  self.IP))
                self._isopen = True
            except:
                raise
            pass
        pass

    def _close(self):
        if self._isopen:
            # Nothing else we can do except wait for the connection to die,
            # and that's silly... or kill the pid ourself---but the connection
            # will naturally die after the response.
            self._isopen = False
        pass

    def _xcall(self,cmd,args=()):
        self._open()
        if debug:
            print "NM4agent: sending xmlrpc request (%s,%s)" % (cmd,str(args))
        print >>self.__agentconn.tochild, xmlrpclib.dumps(args,cmd)
        self.__agentconn.tochild.close()
        if debug:
            print "NM4agent: waiting for response"
        retval = xmlrpclib.loads(self.__agentconn.fromchild.read())
        if debug:
            print "NM4agent: response = '%s'" % str(retval)
        self.__agentconn.wait()
        if debug:
            print "NM4agent: _xcall complete"
        self._close()

        # XXX: we whack the retval to be compat with NMv3
        rret = retval[0][0]
        if rret == 1:
            rret = 0
            pass
        retval = [ rret, [''] ]
        
        return retval
    
    def deliver_ticket(self,ticket):
        if debug:
            print "NM4agent: delivering ticket '%s'" % str(ticket)
        return self._xcall('Ticket',(ticket,))
    
    def create_sliver(self,slice_name):
        if debug:
            print "NM4agent: creating sliver for slice %s" % slice_name
        return self._xcall('Create',(slice_name,))

    def delete_sliver(self,slice_name):
        if debug:
            print "NM4agent: destroying sliver for slice %s" % slice_name
        return self._xcall('Destroy',(slice_name,))

    def start_sliver(self,slice_name):
        return self._xcall('Start',(slice_name,))

    def stop_sliver(self,slice_name):
        return self._xcall('Stop',(slice_name,))

    # NM v4 does not have a version method...
    def version(self):
        return self.__vers
    
    def getAgentClass(self):
        return self.__class__
    pass

DEF_NM_LEGACY_AGENT = NM3agent
DEF_NM_AGENT = NM4agent

class NMagent_wrapper:
    def __init__(self,IP,nodeid):
        # first try a tcp connection to the legacy NM port; if timeout,
        # try default agent right away.
        self.__agent = None
        
        if DEF_NM_LEGACY_AGENT != None:
            legacy_nm = False
            
            #sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            #sock.settimeout(10.0)
            try:
                #sock.connect((str(IP),int(DEF_NM_LEGACY_PORT)))

                tmp_agent = DEF_NM_LEGACY_AGENT(IP,nodeid)
                tmp_agent.version()
                
                legacy_nm = True
                if debug:
                    print "NM %s (%s) IS legacy" % (IP,nodeid)
                    pass
            except:
                print "NM on %s (%s) does not appear to be legacy." % (IP,
                                                                       nodeid)
                traceback.print_exc()
                pass
            
            if legacy_nm:
                self.__agent = DEF_NM_LEGACY_AGENT(IP,nodeid)
                pass
            else:
                self.__agent = DEF_NM_AGENT(IP,nodeid)
                pass
            pass
        else:
            self.__agent = DEF_NM_AGENT(IP,nodeid)
            pass
        pass
    
    def getAgent(self):
        return self.__agent

    def getAgentClass(self):
        return self.__agent.__class__

    def create_sliver(self,slicename,ticketdata):
        arg = None
        
        if self.__agent.__class__ == NM4agent:
            arg = slicename
            pass
        else:
            arg = ticketdata
            pass

        # if NM4agent, try delivering the ticket first, to maximize our
        # chances:
        if self.__agent.__class__ == NM4agent:
            try:
                res = tryXmlrpcCmd(self.__agent.deliver_ticket,ticketdata)
                if res[0] == 0:
                    print "WARNING: while trying to deliver ticket for slice %s: %s" % (slicename,str(res[1]))
                else:
                    if debug:
                        print "Ticket delivery succeeded for slice %s" % slicename
                        pass
                    pass
                pass
            except:
                print "WARNING: exception while delivering ticket for slice %s" % slicename
                traceback.print_exc()
                pass
            pass
        else:
            if debug:
                print "DEBUG: type(agent) = %s %s" % (str(type(self.__agent)),
                                                      str(type(NM4agent)))
            pass
        
        res = tryXmlrpcCmd(self.__agent.create_sliver,arg)
        if self.__agent.__class__ == NM4agent:
            #
            # XXX - fix later
            #
            # return the new way -- we don't want to store a ticket
            # until rollout is mostly done.
            return (res,'')
        else:
            if debug:
                print "res is %s" % str(res)
                pass
            retval = (res,None)
            try:
                retval = (res,cPickle.dumps(res[1][0]))
            except:
                pass
            return retval

        return None
    
    def delete_sliver(self,slicename,ticketdata):
        arg = None
        if self.__agent.__class__ == NM4agent:
            arg = slicename
            pass
        else:
            arg = ticketdata
            pass
        return tryXmlrpcCmd(self.__agent.delete_sliver,arg)

    def update_sliver(self,slicename,ticketdata):
        arg = None
        if self.__agent.__class__ == NM4agent:
            return tryXmlrpcCmd(self.__agent.deliver_ticket,ticketdata)
        else:
            return tryXmlrpcCmd(self.__agent.create_sliver,ticketdata)
        return None
    
    pass
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363

#
# The real PLC agent.  Wraps up standard arguments to the
# PLC XMLRPC interface.
#
class PLCagent:
    def __init__(self, slicename,
                 uri = DEF_PLC_URI,
                 username = "",
                 password = ""):

        if username == "":
            username = mod_PLC4.username
            pass
        if password == "":
            password = mod_PLC4.password
            pass
        
        if not slicename:
            raise RuntimeError, "Must provide a slicename!"
        self.__slice = {}
        self.__slice['sliceName'] = slicename
        self.__slicename = slicename
        self.__auth = {}
        self.__auth['AuthMethod'] = "password"
        self.__auth['Username'] = username
        self.__auth['AuthString'] = password
        self.__auth['Role'] = "pi"
        self.__insmeth = "delegated"
        try:
364
365
366
367
            # Need allow_none=1 to marshal `None' since PLC4 uses it
            # in wildcard situations...
            self.__server = xmlrpclib.ServerProxy(uri,allow_none=1)
            self.server = self.__server
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
        except:
            print "Failed to create XML-RPC proxy"
            raise
        return

    def getSliceName(self):
        return self.__slice['sliceName']

    def SliceCreate(self,
                    sliceurl = DEF_EMULAB_URL,
                    slicedesc = DEF_SLICE_DESC,
                    instmethod = DEF_EMULAB_INSTMETHOD):
        return self.__server.AddSlice(self.__auth,
                                      { 'url' : sliceurl,
                                        'instantiation' : instmethod,
                                        'name' : self.__slicename,
                                        'description' : slicedesc })

    def SliceDelete(self):
        return self.__server.DeleteSlice(self.__auth, self.__slicename)

    def SliceUpdate(self,slicedesc = DEF_SLICE_DESC,sliceURL = DEF_EMULAB_URL):
        return self.__server.UpdateSlice(self.__auth, self.__slicename,
                                         { 'url' : sliceURL,
                                           'description' : slicedesc })

    def SliceRenew(self,expdate):
        return self.__server.UpdateSlice(self.__auth, self.__slicename,
                                         { 'expires' : expdate })

    # XXX: this should work ok if xmlrpc converts lists to arrays...
    def SliceNodesAdd(self,nodelist):
        if not type(nodelist) == list:
            nodelist = [nodelist]
            pass
        return self.__server.AddSliceToNodes(self.__auth,self.__slicename,
                                             nodelist)
    
    def SliceNodesDel(self,nodelist):
        if not type(nodelist) == list:
            nodelist = [nodelist,]
        return self.__server.DeleteSliceFromNodes(self.__auth,self.__slicename,
                                                  nodelist)

    # Ick, have to use GetSlices and GetNodes.  Ick!  Ick!
    def SliceNodesList(self):
        # use a return filter to get only the node ids.
        retval = self.__server.GetSlices(self.__auth,
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
                                         [ self.__slicename ])
        # well, there's a bug in PLC with return filters in GetSlices...
        #                                 [ 'node_ids' ])

        if len(retval) > 0 and retval[0]['name'] == self.__slicename:
            nidlist = retval[0]['node_ids']
            # then use a node filter to GetNodes to get the names.
            # XXX: probably should convert over to using node ids at
            # some point.
            retval = self.__server.GetNodes(self.__auth,
                                            nidlist,
                                            [ 'hostname' ])
        else:
            retval = []
        
        nhostlist = map(lambda x: x['hostname'], retval)
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467

        return nhostlist

    # PLC 4's default behavior is to add one user at a time.  Thus, we
    # just call their function for each list item; if a fault occurs, it'll
    # go back to the caller.  The reason we can do it this way is because
    # subsequent calls to AddPersonToSlice don't fail if the person is already
    # a member of the slice.  It's nasty, but whatever.
    def SliceUsersAdd(self, userlist):
        if type(userlist) != list:
            userlist = [userlist]
            pass
        for user in userlist:
            retval = self.__server.AddPersonToSlice(self.__auth,
                                                    user,
                                                    self.__slicename)
            pass
        # this is 1 if all (i.e., the last) call succeeds
        return retval

    # Same PLC 4 behavior change as for AddPersonToSlice.
    def SliceUsersDel(self, userlist):
        if type(userlist) != list:
            userlist = [userlist]
            pass
        for user in userlist:
            retval = self.__server.DeletePersonFromSlice(self.__auth,
                                                         user,
                                                         self.__slicename)
            pass
        return retval

    # Ick, have to implement this in terms of GetSlices and GetPersons
    # just as for the node hostnames in SliceNodesList.
    def SliceUsersList(self):
        retval = self.__server.GetSlices(self.__auth,
468
469
470
471
472
473
474
475
476
477
478
479
                                         [ self.__slicename ])
        # XXX PLC bug
        #                                 [ 'name','person_ids' ])
        if len(retval) > 0 and retval[0]['name'] == self.__slicename:
            uidlist = retval[0]['person_ids']
            retval = self.__server.GetPersons(self.__auth,
                                              uidlist,
                                              [ 'email' ])
        else:
            retval = []
        
        usernamelist = map(lambda x: x['email'], retval)
480
481
482
483
484

        return usernamelist

    # XXX: keep the commented version around to get tickets for the old
    # NMs.
485
486
    def SliceGetTicketLegacy(self):
        return self.__server.SliceGetTicket(self.__auth,self.__slicename)
487
488
489
490
491
492
    
    def SliceGetTicket(self):
        return self.__server.GetSliceTicket(self.__auth,self.__slicename)

    # XXX: this returns a lot more crap than we use, but we'll keep it intact
    # for the future...
493
494
495
496
497
    def SliceInfo(self,slicefilter=None,returnfilter=None):
        return self.__server.GetSlices(self.__auth,slicefilter,returnfilter)

    def AuthCheck(self):
        return self.__server.AuthCheck(self.__auth)
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515

    pass # end of PLCagent class


class mod_PLC4:
    username = ""
    password = ""
    
    def __init__(self):
        self.modname = "mod_PLC4"
        self.__PLCagent = None
        self.__sliceexpdict = {}
        self.__sliceexptime = 0

        # try to grab the master account info from the file:
        try:
            file = open(DEF_PLC_PASS_FILE,'r')
            lines = file.readlines()
516
517
            mod_PLC4.username = lines[0].strip('\n')
            mod_PLC4.password = lines[1].strip('\n')
518
519
520
521
522
523
524
            pass
        except:
            print "Failed to retrive master passwd from %s" % DEF_PLC_PASS_FILE
            raise
        
        return

525
    def createSlice(self,slice):
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551

        agent = self.__getAgent(slice.slicename)
        res = None
        now = calendar.timegm(time.gmtime())

        try:
            res = tryXmlrpcCmd(agent.SliceCreate)
            if debug:
                print "SliceCreate result: %s" % res
                pass
            pass
        except:
            print "Failed to create slice %s" % slice.slicename
            raise
        
        try:
            res = tryXmlrpcCmd(agent.SliceUsersAdd,
                               EMULABMAN_EMAIL)
            if debug:
                print "SliceUsersAdd result: %s" % res
                pass
            pass
        except:
            print "Failed to assign emulabman to slice %s" % slice.slicename
            raise

552
553
554
555
556
557
        # The new NM will check to see if the slice has been added to the
        # nodes, and will delete the ticket if when it contacts PLC it finds
        # that the slice doesn't include the local NM's node... or something
        # like that... so we must add the nodes to the slice.
        try:
            # XXX: test hack
558
559
            #nodelist = ['pcwf3.emulab.net']
            nodelist = map(lambda x: x[2], slice.getSliceNodes())
560
561
562
563
564
565
566
567
568
            res = tryXmlrpcCmd(agent.SliceNodesAdd, nodelist)
            if debug:
                print "SliceNodesAdd result: %s" % res
                pass
            pass
        except:
            print "Failed to add nodes to slice %s" % slice.slicename
            raise

569
        try:
570
571
            # XXX - fix for new NM and rollout.
            #PLCticket = tryXmlrpcCmd(agent.SliceGetTicket)
572
            PLCticket = tryXmlrpcCmd(agent.SliceGetTicketLegacy)
573
574
575
576
577
578
579
            if debug:
                print PLCticket
                pass
            pass
        except:
            print "Failed to get PLC ticket for slice %s" % slice.slicename
            raise
580
        
581
582
583
        # XXX: fix for PLC 4
        leaseend = now + MAX_PLC_LEASELEN
        return (res, cPickle.dumps(PLCticket), leaseend)
584
    
585
586
587
588
589
590
591
592
593
594
595
596
597
598
    def deleteSlice(self, slice):
        agent = self.__getAgent(slice.slicename)
        # XXX: fix OKstrs based on what plc actually returns
        tryXmlrpcCmd(agent.SliceDelete, OKstrs = ["does not exist"])
        pass

    def renewSlice(self, slice, force = False):
        agent = self.__getAgent(slice.slicename)
        ret = 0
        now = int(time.time()) # seconds since the epoch (UTC)
 
        # Get current PLC timeout for this slice
        leaseend = self.getSliceExpTime(slice.slicename)

599
600
601
602
603
604
        if debug:
            print "PLC says %s/%s expires at %s" % \
                  (slice.eid,slice.pid,
                   time.strftime("%Y-%m-%d %H:%M:%S",
                                 time.gmtime(leaseend)))

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
        # Warn that we weren't able to get the exp. time from PLC,
        # but don't fail - try to renew anyway.
        if not leaseend:
            print "Couldn't get slice expiration time from PLC!"
            leaseend = slice.leaseend
            pass

        # Allow some slop in our recorded time versus PLC's.  This is necessary
        # since we calculate the expiration locally.  If we are off by too much
        # then adjust to PLC's recorded expiration.
        if abs(leaseend - slice.leaseend) > MAX_LEASE_SLOP:
            print "Warning: recorded lease for %s doesn't agree with PLC" % \
                  slice.slicename
            print "\tRecorded: %s  Actual: %s" % (slice.leaseend, leaseend)
            slice.leaseend = leaseend
            pass

        # Expired!  Just bitch about it; try renewal anyway.  The renewal
        # code in libplab will send email.
        if leaseend < now:
            print "Slice %s (%s/%s) has expired!" % \
                  (slice.slicename, slice.pid, slice.eid)
            pass

        # If the lease is at least as large as the minimum window,
        # don't bother renewing it.
        if leaseend - now > MIN_LEASE_WINDOW and not force:
            print "Slice %s (%s/%s) doesn't need to be renewed" % \
                  (slice.slicename, slice.pid, slice.eid)
            return 1

        # Max out leaseend as far as (politically) possible
637
638
639
640
641
642
643
        newleaseend = now + MAX_PLC_LEASELEN

        if debug:
            print "Renewing until %s (adding now +%d s)" % \
                  (time.strftime("%Y-%m-%d %H:%M:%S",
                                 time.gmtime(newleaseend)),
                   newleaseend - now)
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
        
        try:
            # XXX: again fix NOKstrs as necessary
            res = tryXmlrpcCmd(agent.SliceRenew,
                               newleaseend,
                               NOKstrs = ["does not exist"])
            # Get the updated ticket.
            slice.slicemeta = self.getSliceMeta(slice)
            ret = 1
            if debug:
                print "SliceRenew returns: %s" % res
                pass
            pass
        except:
            print "Failed to renew lease for slice %s" % slice.slicename
            traceback.print_exc()
            ret = 0
            pass
        else:
            slice.leaseend = newleaseend
            pass
        
        return ret

668
    def getSliceMetaLegacy(self, slice):
669
670
671
        agent = self.__getAgent(slice.slicename)
        
        try:
672
            PLCticket = tryXmlrpcCmd(agent.SliceGetTicketLegacy)
673
674
675
676
677
            if debug:
                print PLCticket
                pass
            pass
        except:
678
            print "Failed to get legacy PLC ticket for slice %s" % slice.slicename
679
680
681
682
            raise

        return cPickle.dumps(PLCticket)

683
684
685
686
687
688
689
690
691
692
693
694
695
696
    def getSliceMeta(self,slice):
        agent = self.__getAgent(slice.slicename)

        try:
            retval = tryXmlrpcCmd(agent.SliceGetTicket)
            if debug:
                print str(retval)
                pass
            pass
        except:
            print "Failed to get PLC ticket for slice %s" % slice.slicename
            raise
        return cPickle.dumps(retval)

697
698
    # XXX: fix to use new NM
    def createNode(self, node):
699
        plcagent = self.__getAgent(node.slice.slicename)
700
701

        ticketdata = cPickle.loads(node.slice.slicemeta)
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
        node.nmagent = NMagent_wrapper(node.IP,node.nodeid)

        # XXX: if node is NM4agent, grab new-style ticket from PLC
        # (flip during rollout once a majority of nodes have NMv4).
        #
        if (node.nmagent.getAgent()).__class__ == NM4agent:
            try:
                ticketdata = tryXmlrpcCmd(plcagent.SliceGetTicket)
                if debug:
                    print "DEBUG: got new ticket data successfully"
            except:
                print "Error: could not get ticket for %s" % node.slice.slicename
                traceback.print_exc()
                pass
            pass
717
                  
718
719
720
721
722
        res = tryXmlrpcCmd(plcagent.SliceNodesAdd, node.hostname,
                           OKstrs = ["already assigned"])
        if debug:
            print "mod_PLC4: createNode: res = %s" % str(res)
            pass
723
724

        # Make sure node is running compatible interface
725
726
727
728
729
730
731
732
733
734
735
736
#        try:
#            vers = agent.version()
#            pass
#        except:
#            print "Unable to check version on remote NM agent!"
#            raise
#        if vers[0] != MAJOR_VERS or vers[1] != MINOR_VERS \
#               or vers[2] < MIN_REV:
#            raise RuntimeError, \
#                  "Remote node manager version incompatible on %s: %s" % \
#                  (node.nodeid, ".".join(map(lambda x: str(x), vers)))
#        pass
737
738

        try:
739
            res = node.nmagent.create_sliver(node.slice.slicename,ticketdata)
740
741
742
743
            if debug:
                print res
                pass

744
745
746
            realres = res[0]

            if not realres[0] == 0:
747
                raise RuntimeError, "create_sliver failed: %d, %s" % \
748
                      (realres[0], realres[1])
749
750
751
752
            pass
        except:
            print "Failed to create sliver %s on slice %s" % \
                  (node.nodeid, node.slice.slicename)
753
754
755
            if debug:
                traceback.print_exc()
                pass
756
757
758
759
760
761
762
            # XXX: Can we clean up on the plab side here?
            #      delete_sliver requires an rcap, but we don't have one
            #      in this case (since sliver creation failed).
            # self.freeNode(node)
            raise

        # send back the rcap
763
764
765
766
767
        #return (res, cPickle.dumps(res[1][0]), node.slice.leaseend)
        # instead, we send back whatever the wrapped agent sends back... which
        # is some form of rcap.
        return (realres,res[1],node.slice.leaseend)
    
768
    def freeNode(self, node):
769
770
771
772
773
774
775
        rcap = None
        try:
            rcap = cPickle.loads(node.nodemeta)
        except:
            print "WARNING: couldn't load rcap"
            pass
        node.nmagent = NMagent_wrapper(node.IP,node.nodeid)
776
777
778
        res = None

        try:
779
            res = node.nmagent.delete_sliver(node.slice.slicename,rcap)
780
781
782
783
784
785
786
787
788
789
790
791
792
            if debug:
                print res
                pass
            if not res[0] == 0:
                raise RuntimeError, "delete_sliver failed: %d" % res[0]
            pass
        except:
            print "Failed to release node %s from slice %s" % \
                  (node.nodeid, node.slice.slicename)
            raise
        
        return res

793
    # XXX: MIGHT need fixing...
794
    def renewNode(self, node, length = 0):
795
796
        return self.createNode(node)
    
797
798
799
800
801
802
803
804
805
    # XXX: add, now that the NM can do this...
    def startNode(self,node):
        return None

    # XXX: add, now that the NM can do this...
    def stopNode(self,node):
        return None

    # XXX: this is broken, appears to be bug in GetSlices xmlrpc call in PLC4
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
    def getSliceExpTime(self, slicename):
        """
        Grab the expiration time for a slice according to PLC.
        Entries are cached for a time specified in this module by
        MAX_CACHE_TIME.  Returns seconds since the epoch in UTC.
        """
        agent = self.__getAgent(slicename)
        # Refresh the slice expiration cache if:
        # 1) cache is cold
        # 2) cache is too old
        # 3) given slice is not in cache
        if not self.__sliceexpdict or \
           self.__sliceexptime < time.time() - MAX_CACHE_TIME or \
           not self.__sliceexpdict.has_key(slicename):
            #
            # This behavior (of getting ALL our slice expiration times)
            # can only be emulated by getting the slice list, and then
            # checking the expiration times for ALL slices in the list...
            # GetSlices in PLC 4 doesn't give you everything if you ask for
            # nothing.
            #
827
828
            if debug:
                print "calling getSliceExpTime with slicename %s" % slicename
829
830
831
            sdict = tryXmlrpcCmd(agent.SliceInfo,[slicename])
            # bug in PLC return filter...
            #                     ([slicename],['name','expires']))
832
833
834
835
836
837
838
839
840
841
842
843
844
845
            for entry in sdict:
                self.__sliceexpdict[entry['name']] = entry
                pass
            self.__sliceexptime = time.time()
            pass

        if not self.__sliceexpdict.has_key(slicename):
            print "Slice %s unknown to PLC" % slicename
            return None

        leaseend = self.__sliceexpdict[slicename]['expires']

        return leaseend

846
847
848
    def doAuthCheck(self,slicename):
        return tryXmlrpcCmd(self.__getAgent(slicename).AuthCheck)

849
850
851
852
853
854
855
856
857
858
859
860
    def __getAgent(self, slicename):
        """
        Returns a PLC agent object for the specified slice.  May cache.
        """
        if not self.__PLCagent or \
           not self.__PLCagent.getSliceName() == slicename:
            self.__PLCagent = PLCagent(slicename)
            pass

        return self.__PLCagent

    pass # end of mod_PLC class