1 from __future__ import with_statement
2 from sfa.util.faults import *
3 from sfa.util.namespace import *
4 from sfa.util.rspec import RSpec
5 from sfa.server.registry import Registries
6 from sfa.plc.nodes import *
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 xml.etree import ElementTree as ET
18 # The data structure used to represent a cloud.
19 # It contains the cloud name, its ip address, image information,
20 # key pairs, and clusters information.
24 # Initialize the aggregate manager by reading a configuration file.
27 configParser = ConfigParser()
28 configParser.read(['/etc/sfa/eucalyptus_aggregate.conf', 'eucalyptus_aggregate.conf'])
29 if len(configParser.sections()) < 1:
30 print >>sys.stderr, 'No cloud defined in the config file'
31 raise 'Cannot find cloud definition in configuration file.'
33 # Only read the first section.
34 cloudSec = configParser.sections()[0]
35 cloud['name'] = cloudSec
36 cloud['access_key'] = configParser.get(cloudSec, 'access_key')
37 cloud['secret_key'] = configParser.get(cloudSec, 'secret_key')
38 cloud['cloud_url'] = configParser.get(cloudSec, 'cloud_url')
39 cloudURL = cloud['cloud_url']
40 if cloudURL.find('https://') >= 0:
41 cloudURL = cloudURL.replace('https://', '')
42 elif cloudURL.find('http://') >= 0:
43 cloudURL = cloudURL.replace('http://', '')
44 (cloud['ip'], parts) = cloudURL.split(':')
47 # Creates a connection to Eucalytpus. This function is inspired by
48 # the make_connection() in Euca2ools.
50 # @return A connection object or None
52 def getEucaConnection():
54 accessKey = cloud['access_key']
55 secretKey = cloud['secret_key']
56 eucaURL = cloud['cloud_url']
61 if not accessKey or not secretKey or not eucaURL:
62 print >>sys.stderr, 'Please set ALL of the required environment ' \
63 'variables by sourcing the eucarc file.'
66 # Split the url into parts
67 if eucaURL.find('https://') >= 0:
69 eucaURL = eucaURL.replace('https://', '')
70 elif eucaURL.find('http://') >= 0:
72 eucaURL = eucaURL.replace('http://', '')
73 (eucaHost, parts) = eucaURL.split(':')
75 parts = parts.split('/')
76 eucaPort = int(parts[0])
78 srvPath = '/'.join(parts)
80 return boto.connect_ec2(aws_access_key_id=accessKey,
81 aws_secret_access_key=secretKey,
83 region=RegionInfo(None, 'eucalyptus', eucaHost),
88 # A class that builds the RSpec for Eucalyptus.
90 class EucaRSpecBuilder(object):
92 # Initizes a RSpec builder
94 # @param cloud A dictionary containing data about a
95 # cloud (ex. clusters, ip)
96 def __init__(self, cloud):
97 self.eucaRSpec = XMLBuilder()
98 self.cloudInfo = cloud
101 # Creates the ClusterSpec stanza.
103 # @param clusters Clusters information.
105 def __clustersXML(self, clusters):
107 for cluster in clusters:
108 instances = cluster['instances']
109 with xml.ClusterSpec(id=cluster['name'], ip=cluster['ip']):
110 for inst in instances:
111 with xml.Node(instanceType=inst[0]):
120 with xml.DiskSpace(unit='GB'):
124 # Creates the Images stanza.
126 # @param images A list of images in Eucalyptus.
128 def __imagesXML(self, images):
132 with xml.Image(id=image.id):
136 xml << image.architecture
140 xml << image.location
143 # Creates the KeyPairs stanza.
145 # @param keypairs A list of key pairs in Eucalyptus.
147 def __keyPairsXML(self, keypairs):
155 # Generates the RSpec.
158 if not self.cloudInfo:
159 print >>sys.stderr, 'No cloud information'
163 cloud = self.cloudInfo
164 with xml.RSpec(name='eucalyptus'):
166 with xml.CloudSpec(id=cloud['name'], ip=cloud['ip']):
167 self.__keyPairsXML(cloud['keypairs'])
168 self.__imagesXML(cloud['images'])
169 self.__clustersXML(cloud['clusters'])
171 with xml.CloudSpec(id=cloud['name'], ip=cloud['ip']):
172 with xml.Credential(type='X509'):
174 with xml.Node(instanceType='m1.small', number='1'):
176 xml << 'eki-F26610C6'
180 xml << 'emi-88760F45'
186 # A parser to parse the output of availability-zones.
188 # Note: Only one cluster is supported. If more than one, this will
191 class ZoneResultParser(object):
192 def __init__(self, zones):
196 if len(self.zones) < 3:
202 cluster['name'] = self.zones[0].name
203 cluster['ip'] = self.zones[0].state
205 for i in range(2, len(self.zones)):
206 currZone = self.zones[i]
207 instType = currZone.name.split()[1]
209 stateString = currZone.state.split('/')
210 rscString = stateString[1].split()
212 instFree = int(stateString[0])
213 instMax = int(rscString[0])
214 instNumCpu = int(rscString[1])
215 instRam = int(rscString[2])
216 instDiskSpace = int(rscString[3])
218 instTuple = (instType, instFree, instMax, instNumCpu, instRam, instDiskSpace)
219 instList.append(instTuple)
220 cluster['instances'] = instList
221 clusterList.append(cluster)
225 def get_rspec(api, hrn, origin_hrn):
227 conn = getEucaConnection()
230 print >>sys.stderr, 'Error: Cannot make a connection to the cloud'
235 zones = conn.get_all_zones(['verbose'])
236 p = ZoneResultParser(zones)
238 cloud['clusters'] = clusters
241 images = conn.get_all_images()
242 cloud['images'] = images
245 keyPairs = conn.get_all_key_pairs()
246 cloud['keypairs'] = keyPairs
247 except EC2ResponseError:
248 errTree = ET.fromstring(EC2ResponseError.body)
249 errMsgE = errTree.find('.//Message')
250 print >>sys.stderr, errMsgE.text
252 rspec = EucaRSpecBuilder(cloud).toXML()
257 Hook called via 'sfi.py create'
259 def create_slice(api, hrn, xml):
264 #r.parseFile(sys.argv[1])
266 #create_slice(None,'plc',rspec)
267 rspec = get_rspec('euca', 'hrn:euca', 'oring_hrn')
270 if __name__ == "__main__":