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