removed imports on sfa.util.rspec that is gone
[sfa.git] / sfa / managers / aggregate_manager_eucalyptus.py
1 from __future__ import with_statement 
2
3 import sys
4 import os, errno
5 import logging
6 import datetime
7
8 import boto
9 from boto.ec2.regioninfo import RegionInfo
10 from boto.exception import EC2ResponseError
11 from ConfigParser import ConfigParser
12 from xmlbuilder import XMLBuilder
13 from lxml import etree as ET
14 from sqlobject import *
15
16 from sfa.util.faults import *
17 from sfa.util.xrn import urn_to_hrn, Xrn
18 from sfa.server.registry import Registries
19 from sfa.trust.credential import Credential
20 from sfa.plc.api import SfaAPI
21 from sfa.plc.aggregate import Aggregate
22 from sfa.plc.slices import *
23 from sfa.util.plxrn import hrn_to_pl_slicename, slicename_to_hrn
24 from sfa.util.callids import Callids
25 from sfa.util.sfalogging import logger
26 from sfa.rspecs.sfa_rspec import sfa_rspec_version
27 from sfa.util.version import version_core
28
29 from multiprocessing import Process
30 from time import sleep
31
32 ##
33 # The data structure used to represent a cloud.
34 # It contains the cloud name, its ip address, image information,
35 # key pairs, and clusters information.
36 #
37 cloud = {}
38
39 ##
40 # The location of the RelaxNG schema.
41 #
42 EUCALYPTUS_RSPEC_SCHEMA='/etc/sfa/eucalyptus.rng'
43
44 api = SfaAPI()
45
46 ##
47 # Meta data of an instance.
48 #
49 class Meta(SQLObject):
50     instance   = SingleJoin('EucaInstance')
51     state      = StringCol(default = 'new')
52     pub_addr   = StringCol(default = None)
53     pri_addr   = StringCol(default = None)
54     start_time = DateTimeCol(default = None)
55
56 ##
57 # A representation of an Eucalyptus instance. This is a support class
58 # for instance <-> slice mapping.
59 #
60 class EucaInstance(SQLObject):
61     instance_id = StringCol(unique=True, default=None)
62     kernel_id   = StringCol()
63     image_id    = StringCol()
64     ramdisk_id  = StringCol()
65     inst_type   = StringCol()
66     key_pair    = StringCol()
67     slice       = ForeignKey('Slice')
68     meta        = ForeignKey('Meta')
69
70     ##
71     # Contacts Eucalyptus and tries to reserve this instance.
72     # 
73     # @param botoConn A connection to Eucalyptus.
74     # @param pubKeys A list of public keys for the instance.
75     #
76     def reserveInstance(self, botoConn, pubKeys):
77         logger = logging.getLogger('EucaAggregate')
78         logger.info('Reserving an instance: image: %s, kernel: ' \
79                     '%s, ramdisk: %s, type: %s, key: %s' % \
80                     (self.image_id, self.kernel_id, self.ramdisk_id,
81                     self.inst_type, self.key_pair))
82
83         # XXX The return statement is for testing. REMOVE in production
84         #return
85
86         try:
87             reservation = botoConn.run_instances(self.image_id,
88                                                  kernel_id = self.kernel_id,
89                                                  ramdisk_id = self.ramdisk_id,
90                                                  instance_type = self.inst_type,
91                                                  key_name  = self.key_pair,
92                                                  user_data = pubKeys)
93             for instance in reservation.instances:
94                 self.instance_id = instance.id
95
96         # If there is an error, destroy itself.
97         except EC2ResponseError, ec2RespErr:
98             errTree = ET.fromstring(ec2RespErr.body)
99             msg = errTree.find('.//Message')
100             logger.error(msg.text)
101             self.destroySelf()
102
103 ##
104 # A representation of a PlanetLab slice. This is a support class
105 # for instance <-> slice mapping.
106 #
107 class Slice(SQLObject):
108     slice_hrn = StringCol()
109     #slice_index = DatabaseIndex('slice_hrn')
110     instances = MultipleJoin('EucaInstance')
111
112 ##
113 # Initialize the aggregate manager by reading a configuration file.
114 #
115 def init_server():
116     logger = logging.getLogger('EucaAggregate')
117     fileHandler = logging.FileHandler('/var/log/euca.log')
118     fileHandler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
119     logger.addHandler(fileHandler)
120     fileHandler.setLevel(logging.DEBUG)
121     logger.setLevel(logging.DEBUG)
122
123     configParser = ConfigParser()
124     configParser.read(['/etc/sfa/eucalyptus_aggregate.conf', 'eucalyptus_aggregate.conf'])
125     if len(configParser.sections()) < 1:
126         logger.error('No cloud defined in the config file')
127         raise Exception('Cannot find cloud definition in configuration file.')
128
129     # Only read the first section.
130     cloudSec = configParser.sections()[0]
131     cloud['name'] = cloudSec
132     cloud['access_key'] = configParser.get(cloudSec, 'access_key')
133     cloud['secret_key'] = configParser.get(cloudSec, 'secret_key')
134     cloud['cloud_url']  = configParser.get(cloudSec, 'cloud_url')
135     cloudURL = cloud['cloud_url']
136     if cloudURL.find('https://') >= 0:
137         cloudURL = cloudURL.replace('https://', '')
138     elif cloudURL.find('http://') >= 0:
139         cloudURL = cloudURL.replace('http://', '')
140     (cloud['ip'], parts) = cloudURL.split(':')
141
142     # Create image bundles
143     images = getEucaConnection().get_all_images()
144     cloud['images'] = images
145     cloud['imageBundles'] = {}
146     for i in images:
147         if i.type != 'machine' or i.kernel_id is None: continue
148         name = os.path.dirname(i.location)
149         detail = {'imageID' : i.id, 'kernelID' : i.kernel_id, 'ramdiskID' : i.ramdisk_id}
150         cloud['imageBundles'][name] = detail
151
152     # Initialize sqlite3 database and tables.
153     dbPath = '/etc/sfa/db'
154     dbName = 'euca_aggregate.db'
155
156     if not os.path.isdir(dbPath):
157         logger.info('%s not found. Creating directory ...' % dbPath)
158         os.mkdir(dbPath)
159
160     conn = connectionForURI('sqlite://%s/%s' % (dbPath, dbName))
161     sqlhub.processConnection = conn
162     Slice.createTable(ifNotExists=True)
163     EucaInstance.createTable(ifNotExists=True)
164     Meta.createTable(ifNotExists=True)
165
166     # Start the update process to keep track of the meta data
167     # about Eucalyptus instance.
168     Process(target=updateMeta).start()
169
170     # Make sure the schema exists.
171     if not os.path.exists(EUCALYPTUS_RSPEC_SCHEMA):
172         err = 'Cannot location schema at %s' % EUCALYPTUS_RSPEC_SCHEMA
173         logger.error(err)
174         raise Exception(err)
175
176 ##
177 # Creates a connection to Eucalytpus. This function is inspired by 
178 # the make_connection() in Euca2ools.
179 #
180 # @return A connection object or None
181 #
182 def getEucaConnection():
183     global cloud
184     accessKey = cloud['access_key']
185     secretKey = cloud['secret_key']
186     eucaURL   = cloud['cloud_url']
187     useSSL    = False
188     srvPath   = '/'
189     eucaPort  = 8773
190     logger    = logging.getLogger('EucaAggregate')
191
192     if not accessKey or not secretKey or not eucaURL:
193         logger.error('Please set ALL of the required environment ' \
194                      'variables by sourcing the eucarc file.')
195         return None
196     
197     # Split the url into parts
198     if eucaURL.find('https://') >= 0:
199         useSSL  = True
200         eucaURL = eucaURL.replace('https://', '')
201     elif eucaURL.find('http://') >= 0:
202         useSSL  = False
203         eucaURL = eucaURL.replace('http://', '')
204     (eucaHost, parts) = eucaURL.split(':')
205     if len(parts) > 1:
206         parts = parts.split('/')
207         eucaPort = int(parts[0])
208         parts = parts[1:]
209         srvPath = '/'.join(parts)
210
211     return boto.connect_ec2(aws_access_key_id=accessKey,
212                             aws_secret_access_key=secretKey,
213                             is_secure=useSSL,
214                             region=RegionInfo(None, 'eucalyptus', eucaHost), 
215                             port=eucaPort,
216                             path=srvPath)
217
218 ##
219 # Returns a string of keys that belong to the users of the given slice.
220 # @param sliceHRN The hunman readable name of the slice.
221 # @return sting()
222 #
223 # This method is no longer needed because the user keys are passed into
224 # CreateSliver
225 #
226 def getKeysForSlice(api, sliceHRN):
227     logger   = logging.getLogger('EucaAggregate')
228     cred     = api.getCredential()
229     registry = api.registries[api.hrn]
230     keys     = []
231
232     # Get the slice record
233     records = registry.Resolve(sliceHRN, cred)
234     if not records:
235         logging.warn('Cannot find any record for slice %s' % sliceHRN)
236         return []
237
238     # Find who can log into this slice
239     persons = records[0]['persons']
240
241     # Extract the keys from persons records
242     for p in persons:
243         sliceUser = registry.Resolve(p, cred)
244         userKeys = sliceUser[0]['keys']
245         keys += userKeys
246
247     return '\n'.join(keys)
248
249 ##
250 # A class that builds the RSpec for Eucalyptus.
251 #
252 class EucaRSpecBuilder(object):
253     ##
254     # Initizes a RSpec builder
255     #
256     # @param cloud A dictionary containing data about a 
257     #              cloud (ex. clusters, ip)
258     def __init__(self, cloud):
259         self.eucaRSpec = XMLBuilder(format = True, tab_step = "  ")
260         self.cloudInfo = cloud
261
262     ##
263     # Creates a request stanza.
264     # 
265     # @param num The number of instances to create.
266     # @param image The disk image id.
267     # @param kernel The kernel image id.
268     # @param keypair Key pair to embed.
269     # @param ramdisk Ramdisk id (optional).
270     #
271     def __requestXML(self, num, image, kernel, keypair, ramdisk = ''):
272         xml = self.eucaRSpec
273         with xml.request:
274             with xml.instances:
275                 xml << str(num)
276             with xml.kernel_image(id=kernel):
277                 xml << ''
278             if ramdisk == '':
279                 with xml.ramdisk:
280                     xml << ''
281             else:
282                 with xml.ramdisk(id=ramdisk):
283                     xml << ''
284             with xml.disk_image(id=image):
285                 xml << ''
286             with xml.keypair:
287                 xml << keypair
288
289     ##
290     # Creates the cluster stanza.
291     #
292     # @param clusters Clusters information.
293     #
294     def __clustersXML(self, clusters):
295         cloud = self.cloudInfo
296         xml = self.eucaRSpec
297
298         for cluster in clusters:
299             instances = cluster['instances']
300             with xml.cluster(id=cluster['name']):
301                 with xml.ipv4:
302                     xml << cluster['ip']
303                 with xml.vm_types:
304                     for inst in instances:
305                         with xml.vm_type(name=inst[0]):
306                             with xml.free_slots:
307                                 xml << str(inst[1])
308                             with xml.max_instances:
309                                 xml << str(inst[2])
310                             with xml.cores:
311                                 xml << str(inst[3])
312                             with xml.memory(unit='MB'):
313                                 xml << str(inst[4])
314                             with xml.disk_space(unit='GB'):
315                                 xml << str(inst[5])
316                             if 'instances' in cloud and inst[0] in cloud['instances']:
317                                 existingEucaInstances = cloud['instances'][inst[0]]
318                                 with xml.euca_instances:
319                                     for eucaInst in existingEucaInstances:
320                                         with xml.euca_instance(id=eucaInst['id']):
321                                             with xml.state:
322                                                 xml << eucaInst['state']
323                                             with xml.public_dns:
324                                                 xml << eucaInst['public_dns']
325
326     def __imageBundleXML(self, bundles):
327         xml = self.eucaRSpec
328         with xml.bundles:
329             for bundle in bundles.keys():
330                 with xml.bundle(id=bundle):
331                     xml << ''
332
333     ##
334     # Creates the Images stanza.
335     #
336     # @param images A list of images in Eucalyptus.
337     #
338     def __imagesXML(self, images):
339         xml = self.eucaRSpec
340         with xml.images:
341             for image in images:
342                 with xml.image(id=image.id):
343                     with xml.type:
344                         xml << image.type
345                     with xml.arch:
346                         xml << image.architecture
347                     with xml.state:
348                         xml << image.state
349                     with xml.location:
350                         xml << image.location
351
352     ##
353     # Creates the KeyPairs stanza.
354     #
355     # @param keypairs A list of key pairs in Eucalyptus.
356     #
357     def __keyPairsXML(self, keypairs):
358         xml = self.eucaRSpec
359         with xml.keypairs:
360             for key in keypairs:
361                 with xml.keypair:
362                     xml << key.name
363
364     ##
365     # Generates the RSpec.
366     #
367     def toXML(self):
368         logger = logging.getLogger('EucaAggregate')
369         if not self.cloudInfo:
370             logger.error('No cloud information')
371             return ''
372
373         xml = self.eucaRSpec
374         cloud = self.cloudInfo
375         with xml.RSpec(type='eucalyptus'):
376             with xml.network(name=cloud['name']):
377                 with xml.ipv4:
378                     xml << cloud['ip']
379                 #self.__keyPairsXML(cloud['keypairs'])
380                 #self.__imagesXML(cloud['images'])
381                 self.__imageBundleXML(cloud['imageBundles'])
382                 self.__clustersXML(cloud['clusters'])
383         return str(xml)
384
385 ##
386 # A parser to parse the output of availability-zones.
387 #
388 # Note: Only one cluster is supported. If more than one, this will
389 #       not work.
390 #
391 class ZoneResultParser(object):
392     def __init__(self, zones):
393         self.zones = zones
394
395     def parse(self):
396         if len(self.zones) < 3:
397             return
398         clusterList = []
399         cluster = {} 
400         instList = []
401
402         cluster['name'] = self.zones[0].name
403         cluster['ip']   = self.zones[0].state
404
405         for i in range(2, len(self.zones)):
406             currZone = self.zones[i]
407             instType = currZone.name.split()[1]
408
409             stateString = currZone.state.split('/')
410             rscString   = stateString[1].split()
411
412             instFree      = int(stateString[0])
413             instMax       = int(rscString[0])
414             instNumCpu    = int(rscString[1])
415             instRam       = int(rscString[2])
416             instDiskSpace = int(rscString[3])
417
418             instTuple = (instType, instFree, instMax, instNumCpu, instRam, instDiskSpace)
419             instList.append(instTuple)
420         cluster['instances'] = instList
421         clusterList.append(cluster)
422
423         return clusterList
424
425 def ListResources(api, creds, options, call_id): 
426     if Callids().already_handled(call_id): return ""
427     global cloud
428     # get slice's hrn from options
429     xrn = options.get('geni_slice_urn', '')
430     hrn, type = urn_to_hrn(xrn)
431     logger = logging.getLogger('EucaAggregate')
432
433     # get hrn of the original caller
434     origin_hrn = options.get('origin_hrn', None)
435     if not origin_hrn:
436         origin_hrn = Credential(string=creds[0]).get_gid_caller().get_hrn()
437
438     conn = getEucaConnection()
439
440     if not conn:
441         logger.error('Cannot create a connection to Eucalyptus')
442         return 'Cannot create a connection to Eucalyptus'
443
444     try:
445         # Zones
446         zones = conn.get_all_zones(['verbose'])
447         p = ZoneResultParser(zones)
448         clusters = p.parse()
449         cloud['clusters'] = clusters
450         
451         # Images
452         images = conn.get_all_images()
453         cloud['images'] = images
454         cloud['imageBundles'] = {}
455         for i in images:
456             if i.type != 'machine' or i.kernel_id is None: continue
457             name = os.path.dirname(i.location)
458             detail = {'imageID' : i.id, 'kernelID' : i.kernel_id, 'ramdiskID' : i.ramdisk_id}
459             cloud['imageBundles'][name] = detail
460
461         # Key Pairs
462         keyPairs = conn.get_all_key_pairs()
463         cloud['keypairs'] = keyPairs
464
465         if hrn:
466             instanceId = []
467             instances  = []
468
469             # Get the instances that belong to the given slice from sqlite3
470             # XXX use getOne() in production because the slice's hrn is supposed
471             # to be unique. For testing, uniqueness is turned off in the db.
472             # If the slice isn't found in the database, create a record for the 
473             # slice.
474             matchedSlices = list(Slice.select(Slice.q.slice_hrn == hrn))
475             if matchedSlices:
476                 theSlice = matchedSlices[-1]
477             else:
478                 theSlice = Slice(slice_hrn = hrn)
479             for instance in theSlice.instances:
480                 instanceId.append(instance.instance_id)
481
482             # Get the information about those instances using their ids.
483             if len(instanceId) > 0:
484                 reservations = conn.get_all_instances(instanceId)
485             else:
486                 reservations = []
487             for reservation in reservations:
488                 for instance in reservation.instances:
489                     instances.append(instance)
490
491             # Construct a dictionary for the EucaRSpecBuilder
492             instancesDict = {}
493             for instance in instances:
494                 instList = instancesDict.setdefault(instance.instance_type, [])
495                 instInfoDict = {} 
496
497                 instInfoDict['id'] = instance.id
498                 instInfoDict['public_dns'] = instance.public_dns_name
499                 instInfoDict['state'] = instance.state
500                 instInfoDict['key'] = instance.key_name
501
502                 instList.append(instInfoDict)
503             cloud['instances'] = instancesDict
504
505     except EC2ResponseError, ec2RespErr:
506         errTree = ET.fromstring(ec2RespErr.body)
507         errMsgE = errTree.find('.//Message')
508         logger.error(errMsgE.text)
509
510     rspec = EucaRSpecBuilder(cloud).toXML()
511
512     # Remove the instances records so next time they won't 
513     # show up.
514     if 'instances' in cloud:
515         del cloud['instances']
516
517     return rspec
518
519 """
520 Hook called via 'sfi.py create'
521 """
522 def CreateSliver(api, slice_xrn, creds, xml, users, call_id):
523     if Callids().already_handled(call_id): return ""
524
525     global cloud
526     logger = logging.getLogger('EucaAggregate')
527     logger.debug("In CreateSliver")
528
529     aggregate = Aggregate(api)
530     slices = Slices(api)
531     (hrn, type) = urn_to_hrn(slice_xrn)
532     peer = slices.get_peer(hrn)
533     sfa_peer = slices.get_sfa_peer(hrn)
534     slice_record=None
535     if users:
536         slice_record = users[0].get('slice_record', {})
537
538     conn = getEucaConnection()
539     if not conn:
540         logger.error('Cannot create a connection to Eucalyptus')
541         return ""
542
543     # Validate RSpec
544     schemaXML = ET.parse(EUCALYPTUS_RSPEC_SCHEMA)
545     rspecValidator = ET.RelaxNG(schemaXML)
546     rspecXML = ET.XML(xml)
547     for network in rspecXML.iterfind("./network"):
548         if network.get('name') != cloud['name']:
549             # Throw away everything except my own RSpec
550             # sfa_logger().error("CreateSliver: deleting %s from rspec"%network.get('id'))
551             network.getparent().remove(network)
552     if not rspecValidator(rspecXML):
553         error = rspecValidator.error_log.last_error
554         message = '%s (line %s)' % (error.message, error.line) 
555         raise InvalidRSpec(message)
556
557     """
558     Create the sliver[s] (slice) at this aggregate.
559     Verify HRN and initialize the slice record in PLC if necessary.
560     """
561
562     # ensure site record exists
563     site = slices.verify_site(hrn, slice_record, peer, sfa_peer)
564     # ensure slice record exists
565     slice = slices.verify_slice(hrn, slice_record, peer, sfa_peer)
566     # ensure person records exists
567     persons = slices.verify_persons(hrn, slice, users, peer, sfa_peer)
568
569     # Get the slice from db or create one.
570     s = Slice.select(Slice.q.slice_hrn == hrn).getOne(None)
571     if s is None:
572         s = Slice(slice_hrn = hrn)
573
574     # Process any changes in existing instance allocation
575     pendingRmInst = []
576     for sliceInst in s.instances:
577         pendingRmInst.append(sliceInst.instance_id)
578     existingInstGroup = rspecXML.findall(".//euca_instances")
579     for instGroup in existingInstGroup:
580         for existingInst in instGroup:
581             if existingInst.get('id') in pendingRmInst:
582                 pendingRmInst.remove(existingInst.get('id'))
583     for inst in pendingRmInst:
584         dbInst = EucaInstance.select(EucaInstance.q.instance_id == inst).getOne(None)
585         if dbInst.meta.state != 'deleted':
586             logger.debug('Instance %s will be terminated' % inst)
587             # Terminate instances one at a time for robustness
588             conn.terminate_instances([inst])
589             # Only change the state but do not remove the entry from the DB.
590             dbInst.meta.state = 'deleted'
591             #dbInst.destroySelf()
592
593     # Process new instance requests
594     requests = rspecXML.findall(".//request")
595     if requests:
596         # Get all the public keys associate with slice.
597         keys = []
598         for user in users:
599             keys += user['keys']
600             logger.debug("Keys: %s" % user['keys'])
601         pubKeys = '\n'.join(keys)
602         logger.debug('Passing the following keys to the instance:\n%s' % pubKeys)
603     for req in requests:
604         vmTypeElement = req.getparent()
605         instType = vmTypeElement.get('name')
606         numInst  = int(req.find('instances').text)
607         
608         bundleName = req.find('bundle').text
609         if not cloud['imageBundles'][bundleName]:
610             logger.error('Cannot find bundle %s' % bundleName)
611         bundleInfo = cloud['imageBundles'][bundleName]
612         instKernel  = bundleInfo['kernelID']
613         instDiskImg = bundleInfo['imageID']
614         instRamDisk = bundleInfo['ramdiskID']
615         instKey     = None
616
617         # Create the instances
618         for i in range(0, numInst):
619             eucaInst = EucaInstance(slice      = s,
620                                     kernel_id  = instKernel,
621                                     image_id   = instDiskImg,
622                                     ramdisk_id = instRamDisk,
623                                     key_pair   = instKey,
624                                     inst_type  = instType,
625                                     meta       = Meta(start_time=datetime.datetime.now()))
626             eucaInst.reserveInstance(conn, pubKeys)
627
628     # xxx - should return altered rspec 
629     # with enough data for the client to understand what's happened
630     return xml
631
632 ##
633 # Return information on the IP addresses bound to each slice's instances
634 #
635 def dumpInstanceInfo():
636     logger = logging.getLogger('EucaMeta')
637     outdir = "/var/www/html/euca/"
638     outfile = outdir + "instances.txt"
639
640     try:
641         os.makedirs(outdir)
642     except OSError, e:
643         if e.errno != errno.EEXIST:
644             raise
645
646     dbResults = Meta.select(
647         AND(Meta.q.pri_addr != None,
648             Meta.q.state    == 'running')
649         )
650     dbResults = list(dbResults)
651     f = open(outfile, "w")
652     for r in dbResults:
653         instId = r.instance.instance_id
654         ipaddr = r.pri_addr
655         hrn = r.instance.slice.slice_hrn
656         logger.debug('[dumpInstanceInfo] %s %s %s' % (instId, ipaddr, hrn))
657         f.write("%s %s %s\n" % (instId, ipaddr, hrn))
658     f.close()
659
660 ##
661 # A separate process that will update the meta data.
662 #
663 def updateMeta():
664     logger = logging.getLogger('EucaMeta')
665     fileHandler = logging.FileHandler('/var/log/euca_meta.log')
666     fileHandler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
667     logger.addHandler(fileHandler)
668     fileHandler.setLevel(logging.DEBUG)
669     logger.setLevel(logging.DEBUG)
670
671     while True:
672         sleep(30)
673
674         # Get IDs of the instances that don't have IPs yet.
675         dbResults = Meta.select(
676                       AND(Meta.q.pri_addr == None,
677                           Meta.q.state    != 'deleted')
678                     )
679         dbResults = list(dbResults)
680         logger.debug('[update process] dbResults: %s' % dbResults)
681         instids = []
682         for r in dbResults:
683             if not r.instance:
684                 continue
685             instids.append(r.instance.instance_id)
686         logger.debug('[update process] Instance Id: %s' % ', '.join(instids))
687
688         # Get instance information from Eucalyptus
689         conn = getEucaConnection()
690         vmInstances = []
691         reservations = conn.get_all_instances(instids)
692         for reservation in reservations:
693             vmInstances += reservation.instances
694
695         # Check the IPs
696         instIPs = [ {'id':i.id, 'pri_addr':i.private_dns_name, 'pub_addr':i.public_dns_name}
697                     for i in vmInstances if i.private_dns_name != '0.0.0.0' ]
698         logger.debug('[update process] IP dict: %s' % str(instIPs))
699
700         # Update the local DB
701         for ipData in instIPs:
702             dbInst = EucaInstance.select(EucaInstance.q.instance_id == ipData['id']).getOne(None)
703             if not dbInst:
704                 logger.info('[update process] Could not find %s in DB' % ipData['id'])
705                 continue
706             dbInst.meta.pri_addr = ipData['pri_addr']
707             dbInst.meta.pub_addr = ipData['pub_addr']
708             dbInst.meta.state    = 'running'
709
710         dumpInstanceInfo()
711
712 def GetVersion(api):
713     xrn=Xrn(api.hrn)
714     request_rspec_versions = [dict(sfa_rspec_version)]
715     ad_rspec_versions = [dict(sfa_rspec_version)]
716     version_more = {'interface':'aggregate',
717                     'testbed':'myplc',
718                     'hrn':xrn.get_hrn(),
719                     'request_rspec_versions': request_rspec_versions,
720                     'ad_rspec_versions': ad_rspec_versions,
721                     'default_ad_rspec': dict(sfa_rspec_version)
722                     }
723     return version_core(version_more)
724
725 def main():
726     init_server()
727
728     #theRSpec = None
729     #with open(sys.argv[1]) as xml:
730     #    theRSpec = xml.read()
731     #CreateSliver(None, 'planetcloud.pc.test', theRSpec, 'call-id-cloudtest')
732
733     #rspec = ListResources('euca', 'planetcloud.pc.test', 'planetcloud.pc.marcoy', 'test_euca')
734     #print rspec
735
736     server_key_file = '/var/lib/sfa/authorities/server.key'
737     server_cert_file = '/var/lib/sfa/authorities/server.cert'
738     api = SfaAPI(key_file = server_key_file, cert_file = server_cert_file, interface='aggregate')
739     print getKeysForSlice(api, 'gc.gc.test1')
740
741 if __name__ == "__main__":
742     main()
743