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