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