Fixed OAR.
[sfa.git] / sfa / openstack / osaggregate.py
1 import os
2 import base64
3 import string
4 import random    
5 from collections import defaultdict
6 from nova.exception import ImageNotFound
7 from nova.api.ec2.cloud import CloudController
8 from sfa.util.faults import SfaAPIError
9 from sfa.rspecs.rspec import RSpec
10 from sfa.rspecs.elements.hardware_type import HardwareType
11 from sfa.rspecs.elements.node import Node
12 from sfa.rspecs.elements.sliver import Sliver
13 from sfa.rspecs.elements.login import Login
14 from sfa.rspecs.elements.disk_image import DiskImage
15 from sfa.rspecs.elements.services import Services
16 from sfa.util.xrn import Xrn
17 from sfa.util.osxrn import OSXrn
18 from sfa.rspecs.version_manager import VersionManager
19 from sfa.openstack.image import ImageManager
20 from sfa.openstack.security_group import SecurityGroup
21 from sfa.util.sfalogging import logger
22
23 def instance_to_sliver(instance, slice_xrn=None):
24     # should include?
25     # * instance.image_ref
26     # * instance.kernel_id
27     # * instance.ramdisk_id
28     import nova.db.sqlalchemy.models
29     name=None
30     type=None
31     sliver_id = None
32     if isinstance(instance, dict):
33         # this is an isntance type dict
34         name = instance['name']
35         type = instance['name']
36     elif isinstance(instance, nova.db.sqlalchemy.models.Instance):
37         # this is an object that describes a running instance
38         name = instance.display_name
39         type = instance.instance_type.name
40     else:
41         raise SfaAPIError("instnace must be an instance_type dict or" + \
42                            " a nova.db.sqlalchemy.models.Instance object")
43     if slice_xrn:
44         xrn = Xrn(slice_xrn, 'slice')
45         sliver_id = xrn.get_sliver_id(instance.project_id, instance.hostname, instance.id)
46
47     sliver = Sliver({'slice_id': sliver_id,
48                      'name': name,
49                      'type':  type,
50                      'tags': []})
51     return sliver
52     
53
54 def ec2_id(id=None, type=None):
55     ec2_id = None
56     if type == 'ovf':
57         type = 'ami'   
58     if id and type:
59         ec2_id = CloudController.image_ec2_id(id, type)        
60     return ec2_id
61
62
63 class OSAggregate:
64
65     def __init__(self, driver):
66         self.driver = driver
67
68     def get_rspec(self, slice_xrn=None, version=None, options={}):
69         version_manager = VersionManager()
70         version = version_manager.get_version(version)
71         if not slice_xrn:
72             rspec_version = version_manager._get_version(version.type, version.version, 'ad')
73             nodes = self.get_aggregate_nodes()
74         else:
75             rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
76             nodes = self.get_slice_nodes(slice_xrn)
77         rspec = RSpec(version=rspec_version, user_options=options)
78         rspec.version.add_nodes(nodes)
79         return rspec.toxml()
80
81     def get_slice_nodes(self, slice_xrn):
82         image_manager = ImageManager(self.driver)
83         name = OSXrn(xrn = slice_xrn).name
84         instances = self.driver.shell.db.instance_get_all_by_project(name)
85         rspec_nodes = []
86         for instance in instances:
87             rspec_node = Node()
88             xrn = OSXrn(instance.hostname, 'node')
89             rspec_node['component_id'] = xrn.urn
90             rspec_node['component_name'] = xrn.name
91             rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()   
92             sliver = instance_to_sliver(instance)
93             disk_image = image_manager.get_disk_image(instance.image_ref)
94             sliver['disk_images'] = [disk_image.to_rspec_object()]
95             rspec_node['slivers'] = [sliver]
96             rspec_nodes.append(rspec_node)
97         return rspec_nodes
98
99     def get_aggregate_nodes(self):
100                 
101         zones = self.driver.shell.db.zone_get_all()
102         if not zones:
103             zones = ['cloud']
104         else:
105             zones = [zone.name for zone in zones]
106
107         # available sliver/instance/vm types
108         instances = self.driver.shell.db.instance_type_get_all().values()
109         # available images
110         image_manager = ImageManager(self.driver)
111         disk_images = image_manager.get_available_disk_images()
112         disk_image_objects = [image.to_rspec_object() \
113                                for image in disk_images]  
114         rspec_nodes = []
115         for zone in zones:
116             rspec_node = Node()
117             xrn = OSXrn(zone, 'node')
118             rspec_node['component_id'] = xrn.urn
119             rspec_node['component_name'] = xrn.name
120             rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
121             rspec_node['exclusive'] = 'false'
122             rspec_node['hardware_types'] = [HardwareType({'name': 'plos-pc'}),
123                                                 HardwareType({'name': 'pc'})]
124             slivers = []
125             for instance in instances:
126                 sliver = instance_to_sliver(instance)
127                 sliver['disk_images'] = disk_image_objects
128                 slivers.append(sliver)
129         
130             rspec_node['slivers'] = slivers
131             rspec_nodes.append(rspec_node) 
132
133         return rspec_nodes 
134
135
136     def create_project(self, slicename, users, options={}):
137         """
138         Create the slice if it doesn't alredy exist. Create user
139         accounts that don't already exist   
140         """
141         from nova.exception import ProjectNotFound, UserNotFound
142         for user in users:
143             username = Xrn(user['urn']).get_leaf()
144             try:
145                 self.driver.shell.auth_manager.get_user(username)
146             except nova.exception.UserNotFound:
147                 self.driver.shell.auth_manager.create_user(username)
148             self.verify_user_keys(username, user['keys'], options)
149
150         try:
151             slice = self.driver.shell.auth_manager.get_project(slicename)
152         except ProjectNotFound:
153             # assume that the first user is the project manager
154             proj_manager = Xrn(users[0]['urn']).get_leaf()
155             self.driver.shell.auth_manager.create_project(slicename, proj_manager) 
156
157     def verify_user_keys(self, username, keys, options={}):
158         """
159         Add requested keys.
160         """
161         append = options.get('append', True)    
162         existing_keys = self.driver.shell.db.key_pair_get_all_by_user(username)
163         existing_pub_keys = [key.public_key for key in existing_keys]
164         removed_pub_keys = set(existing_pub_keys).difference(keys)
165         added_pub_keys = set(keys).difference(existing_pub_keys)
166         pubkeys = []
167         # add new keys
168         for public_key in added_pub_keys:
169             key = {}
170             key['user_id'] = username
171             key['name'] =  username
172             key['public'] = public_key
173             self.driver.shell.db.key_pair_create(key)
174
175         # remove old keys
176         if not append:
177             for key in existing_keys:
178                 if key.public_key in removed_pub_keys:
179                     self.driver.shell.db.key_pair_destroy(username, key.name)
180
181
182     def create_security_group(self, group_name, fw_rules=[]):
183         security_group = SecurityGroup(self.driver)
184         security_group.create_security_group(group_name)
185         for rule in fw_rules:
186             security_group.add_rule_to_group(group_name, 
187                                              protocol = rule.get('protocol'), 
188                                              cidr_ip = rule.get('cidr_ip'), 
189                                              port_range = rule.get('port_range'), 
190                                              icmp_type_code = rule.get('icmp_type_code'))
191
192     def add_rule_to_security_group(self, group_name, **kwds):
193         security_group = SecurityGroup(self.driver)
194         security_group.add_rule_to_group(group_name=group_name, 
195                                          protocol=kwds.get('protocol'), 
196                                          cidr_ip =kwds.get('cidr_ip'), 
197                                          icmp_type_code = kwds.get('icmp_type_code'))
198
199  
200     def reserve_instance(self, image_id, kernel_id, ramdisk_id, \
201                          instance_type, key_name, user_data, group_name):
202         conn  = self.driver.euca_shell.get_euca_connection()
203         logger.info('Reserving an instance: image: %s, kernel: ' \
204                     '%s, ramdisk: %s, type: %s, key: %s' % \
205                     (image_id, kernel_id, ramdisk_id,
206                     instance_type, key_name))
207         try:
208             reservation = conn.run_instances(image_id=image_id,
209                                              kernel_id=kernel_id,
210                                              ramdisk_id=ramdisk_id,
211                                              instance_type=instance_type,
212                                              key_name=key_name,
213                                              user_data = user_data,
214                                              security_groups=[group_name])
215                                              #placement=zone,
216                                              #min_count=min_count,
217                                              #max_count=max_count,           
218                                               
219         except Exception, err:
220             logger.log_exc(err)
221     
222                
223     def run_instances(self, slicename, rspec, keyname, pubkeys):
224         """
225         Create the security groups and instances. 
226         """
227         # the default image to use for instnaces that dont
228         # explicitly request an image.
229         # Just choose the first available image for now.
230         image_manager = ImageManager(self.driver)
231         available_images = image_manager.get_available_disk_images()
232         default_image_id = None
233         default_aki_id  = None
234         default_ari_id = None
235         default_image = available_images[0]
236         default_image_id = ec2_id(default_image.id, default_image.container_format)  
237         default_aki_id = ec2_id(default_image.kernel_id, 'aki')  
238         default_ari_id = ec2_id(default_image.ramdisk_id, 'ari')
239
240         # get requested slivers
241         rspec = RSpec(rspec)
242         user_data = "\n".join(pubkeys)
243         requested_instances = defaultdict(list)
244         # iterate over clouds/zones/nodes
245         for node in rspec.version.get_nodes_with_slivers():
246             instance_types = node.get('slivers', [])
247             if isinstance(instance_types, list):
248                 # iterate over sliver/instance types
249                 for instance_type in instance_types:
250                     fw_rules = instance_type.get('fw_rules', [])
251                     # Each sliver get's its own security group.  
252                     # Keep security group names unique by appending some random 
253                     # characters on end.
254                     random_name = "".join([random.choice(string.letters+string.digits) 
255                                            for i in xrange(6)])
256                     group_name = slicename + random_name
257                     self.create_security_group(group_name, fw_rules)
258                     ami_id = default_image_id
259                     aki_id = default_aki_id
260                     ari_id = default_ari_id
261                     req_image = instance_type.get('disk_images')
262                     if req_image and isinstance(req_image, list):
263                         req_image_name = req_image[0]['name']
264                         disk_image = image_manager.get_disk_image(name=req_image_name)
265                         if disk_image:
266                             ami_id = ec2_id(disk_image.id, disk_image.container_format)
267                             aki_id = ec2_id(disk_image.kernel_id, 'aki')
268                             ari_id = ec2_id(disk_image.ramdisk_id, 'ari')
269                     # start the instance
270                     self.reserve_instance(image_id=ami_id, 
271                                           kernel_id=aki_id, 
272                                           ramdisk_id=ari_id, 
273                                           instance_type=instance_type['name'], 
274                                           key_name=keyname, 
275                                           user_data=user_data, 
276                                           group_name=group_name)
277
278
279     def delete_instances(self, project_name):
280         instances = self.driver.shell.db.instance_get_all_by_project(project_name)
281         security_group_manager = SecurityGroup(self.driver)
282         for instance in instances:
283             # deleate this instance's security groups
284             for security_group in instance.security_groups:
285                 # dont delete the default security group
286                 if security_group.name != 'default': 
287                     security_group_manager.delete_security_group(security_group.name)
288             # destroy instance
289             self.driver.shell.db.instance_destroy(instance.id)
290         return 1
291
292     def stop_instances(self, project_name):
293         instances = self.driver.shell.db.instance_get_all_by_project(project_name)
294         for instance in instances:
295             self.driver.shell.db.instance_stop(instance.id)
296         return 1
297
298     def update_instances(self, project_name):
299         pass