~rlane/nova/ldap-schema-modifications-1

« back to all changes in this revision

Viewing changes to nova/compute/api.py

  • Committer: Ryan Lane
  • Date: 2010-12-08 08:21:44 UTC
  • mfrom: (384.1.3 trunk)
  • Revision ID: laner@controller-20101208082144-xb81vvcfp4zh45zu
MergeĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
# Copyright 2010 United States Government as represented by the
 
4
# Administrator of the National Aeronautics and Space Administration.
 
5
# All Rights Reserved.
 
6
#
 
7
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
8
#    not use this file except in compliance with the License. You may obtain
 
9
#    a copy of the License at
 
10
#
 
11
#         http://www.apache.org/licenses/LICENSE-2.0
 
12
#
 
13
#    Unless required by applicable law or agreed to in writing, software
 
14
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
15
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
16
#    License for the specific language governing permissions and limitations
 
17
#    under the License.
 
18
 
 
19
"""
 
20
Handles all API requests relating to instances (guest vms).
 
21
"""
 
22
 
 
23
import datetime
 
24
import logging
 
25
import time
 
26
 
 
27
from nova import db
 
28
from nova import exception
 
29
from nova import flags
 
30
from nova import quota
 
31
from nova import rpc
 
32
from nova import utils
 
33
from nova.compute import instance_types
 
34
from nova.db import base
 
35
 
 
36
FLAGS = flags.FLAGS
 
37
 
 
38
 
 
39
def generate_default_hostname(internal_id):
 
40
    """Default function to generate a hostname given an instance reference."""
 
41
    return str(internal_id)
 
42
 
 
43
 
 
44
class ComputeAPI(base.Base):
 
45
    """API for interacting with the compute manager."""
 
46
 
 
47
    def __init__(self, network_manager=None, image_service=None, **kwargs):
 
48
        if not network_manager:
 
49
            network_manager = utils.import_object(FLAGS.network_manager)
 
50
        self.network_manager = network_manager
 
51
        if not image_service:
 
52
            image_service = utils.import_object(FLAGS.image_service)
 
53
        self.image_service = image_service
 
54
        super(ComputeAPI, self).__init__(**kwargs)
 
55
 
 
56
    def create_instances(self, context, instance_type, image_id, min_count=1,
 
57
                         max_count=1, kernel_id=None, ramdisk_id=None,
 
58
                         display_name='', description='', user_data='',
 
59
                         key_name=None, key_data=None,
 
60
                         security_group='default',
 
61
                         generate_hostname=generate_default_hostname):
 
62
        """Create the number of instances requested if quote and
 
63
        other arguments check out ok."""
 
64
 
 
65
        num_instances = quota.allowed_instances(context, max_count,
 
66
                                                instance_type)
 
67
        if num_instances < min_count:
 
68
            logging.warn("Quota exceeeded for %s, tried to run %s instances",
 
69
                         context.project_id, min_count)
 
70
            raise quota.QuotaError("Instance quota exceeded. You can only "
 
71
                                   "run %s more instances of this type." %
 
72
                                   num_instances, "InstanceLimitExceeded")
 
73
 
 
74
        is_vpn = image_id == FLAGS.vpn_image_id
 
75
        if not is_vpn:
 
76
            image = self.image_service.show(context, image_id)
 
77
            if kernel_id is None:
 
78
                kernel_id = image.get('kernelId', FLAGS.default_kernel)
 
79
            if ramdisk_id is None:
 
80
                ramdisk_id = image.get('ramdiskId', FLAGS.default_ramdisk)
 
81
 
 
82
            # Make sure we have access to kernel and ramdisk
 
83
            self.image_service.show(context, kernel_id)
 
84
            self.image_service.show(context, ramdisk_id)
 
85
 
 
86
        if security_group is None:
 
87
            security_group = ['default']
 
88
        if not type(security_group) is list:
 
89
            security_group = [security_group]
 
90
 
 
91
        security_groups = []
 
92
        self.ensure_default_security_group(context)
 
93
        for security_group_name in security_group:
 
94
            group = db.security_group_get_by_name(context,
 
95
                                                  context.project_id,
 
96
                                                  security_group_name)
 
97
            security_groups.append(group['id'])
 
98
 
 
99
        if key_data is None and key_name:
 
100
            key_pair = db.key_pair_get(context, context.user_id, key_name)
 
101
            key_data = key_pair['public_key']
 
102
 
 
103
        type_data = instance_types.INSTANCE_TYPES[instance_type]
 
104
        base_options = {
 
105
            'reservation_id': utils.generate_uid('r'),
 
106
            'image_id': image_id,
 
107
            'kernel_id': kernel_id,
 
108
            'ramdisk_id': ramdisk_id,
 
109
            'state_description': 'scheduling',
 
110
            'user_id': context.user_id,
 
111
            'project_id': context.project_id,
 
112
            'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
 
113
            'instance_type': instance_type,
 
114
            'memory_mb': type_data['memory_mb'],
 
115
            'vcpus': type_data['vcpus'],
 
116
            'local_gb': type_data['local_gb'],
 
117
            'display_name': display_name,
 
118
            'display_description': description,
 
119
            'key_name': key_name,
 
120
            'key_data': key_data}
 
121
 
 
122
        elevated = context.elevated()
 
123
        instances = []
 
124
        logging.debug("Going to run %s instances...", num_instances)
 
125
        for num in range(num_instances):
 
126
            instance = dict(mac_address=utils.generate_mac(),
 
127
                            launch_index=num,
 
128
                            **base_options)
 
129
            instance = self.db.instance_create(context, instance)
 
130
            instance_id = instance['id']
 
131
            internal_id = instance['internal_id']
 
132
 
 
133
            elevated = context.elevated()
 
134
            if not security_groups:
 
135
                security_groups = []
 
136
            for security_group_id in security_groups:
 
137
                self.db.instance_add_security_group(elevated,
 
138
                                                    instance_id,
 
139
                                                    security_group_id)
 
140
 
 
141
            # Set sane defaults if not specified
 
142
            updates = dict(hostname=generate_hostname(internal_id))
 
143
            if 'display_name' not in instance:
 
144
                updates['display_name'] = "Server %s" % internal_id
 
145
 
 
146
            instance = self.update_instance(context, instance_id, **updates)
 
147
            instances.append(instance)
 
148
 
 
149
            # TODO(vish): This probably should be done in the scheduler
 
150
            #             or in compute as a call.  The network should be
 
151
            #             allocated after the host is assigned and setup
 
152
            #             can happen at the same time.
 
153
            address = self.network_manager.allocate_fixed_ip(context,
 
154
                                                             instance_id,
 
155
                                                             is_vpn)
 
156
            rpc.cast(elevated,
 
157
                     self._get_network_topic(context),
 
158
                     {"method": "setup_fixed_ip",
 
159
                      "args": {"address": address}})
 
160
 
 
161
            logging.debug("Casting to scheduler for %s/%s's instance %s" %
 
162
                          (context.project_id, context.user_id, instance_id))
 
163
            rpc.cast(context,
 
164
                     FLAGS.scheduler_topic,
 
165
                     {"method": "run_instance",
 
166
                      "args": {"topic": FLAGS.compute_topic,
 
167
                               "instance_id": instance_id}})
 
168
 
 
169
        return instances
 
170
 
 
171
    def ensure_default_security_group(self, context):
 
172
        try:
 
173
            db.security_group_get_by_name(context, context.project_id,
 
174
                                          'default')
 
175
        except exception.NotFound:
 
176
            values = {'name': 'default',
 
177
                      'description': 'default',
 
178
                      'user_id': context.user_id,
 
179
                      'project_id': context.project_id}
 
180
            group = db.security_group_create(context, values)
 
181
 
 
182
    def update_instance(self, context, instance_id, **kwargs):
 
183
        """Updates the instance in the datastore.
 
184
 
 
185
        :param context: The security context
 
186
        :param instance_id: ID of the instance to update
 
187
        :param kwargs: All additional keyword args are treated
 
188
                       as data fields of the instance to be
 
189
                       updated
 
190
 
 
191
        :retval None
 
192
 
 
193
        """
 
194
        return self.db.instance_update(context, instance_id, kwargs)
 
195
 
 
196
    def delete_instance(self, context, instance_id):
 
197
        logging.debug("Going to try and terminate %d" % instance_id)
 
198
        try:
 
199
            instance = self.db.instance_get_by_internal_id(context,
 
200
                                                           instance_id)
 
201
        except exception.NotFound as e:
 
202
            logging.warning("Instance %d was not found during terminate",
 
203
                            instance_id)
 
204
            raise e
 
205
 
 
206
        if (instance['state_description'] == 'terminating'):
 
207
            logging.warning("Instance %d is already being terminated",
 
208
                            instance_id)
 
209
            return
 
210
 
 
211
        self.update_instance(context,
 
212
                             instance['id'],
 
213
                             state_description='terminating',
 
214
                             state=0,
 
215
                             terminated_at=datetime.datetime.utcnow())
 
216
 
 
217
        # FIXME(ja): where should network deallocate occur?
 
218
        address = self.db.instance_get_floating_address(context,
 
219
                                                        instance['id'])
 
220
        if address:
 
221
            logging.debug("Disassociating address %s" % address)
 
222
            # NOTE(vish): Right now we don't really care if the ip is
 
223
            #             disassociated.  We may need to worry about
 
224
            #             checking this later.  Perhaps in the scheduler?
 
225
            rpc.cast(context,
 
226
                     self._get_network_topic(context),
 
227
                     {"method": "disassociate_floating_ip",
 
228
                      "args": {"floating_address": address}})
 
229
 
 
230
        address = self.db.instance_get_fixed_address(context, instance['id'])
 
231
        if address:
 
232
            logging.debug("Deallocating address %s" % address)
 
233
            # NOTE(vish): Currently, nothing needs to be done on the
 
234
            #             network node until release. If this changes,
 
235
            #             we will need to cast here.
 
236
            self.network_manager.deallocate_fixed_ip(context.elevated(),
 
237
                                                     address)
 
238
 
 
239
        host = instance['host']
 
240
        if host:
 
241
            rpc.cast(context,
 
242
                     self.db.queue_get_for(context, FLAGS.compute_topic, host),
 
243
                     {"method": "terminate_instance",
 
244
                      "args": {"instance_id": instance['id']}})
 
245
        else:
 
246
            self.db.instance_destroy(context, instance['id'])
 
247
 
 
248
    def get_instances(self, context, project_id=None):
 
249
        """Get all instances, possibly filtered by project ID or
 
250
        user ID. If there is no filter and the context is an admin,
 
251
        it will retreive all instances in the system."""
 
252
        if project_id or not context.is_admin:
 
253
            if not context.project:
 
254
                return self.db.instance_get_all_by_user(context,
 
255
                                                        context.user_id)
 
256
            if project_id is None:
 
257
                project_id = context.project_id
 
258
            return self.db.instance_get_all_by_project(context, project_id)
 
259
        return self.db.instance_get_all(context)
 
260
 
 
261
    def get_instance(self, context, instance_id):
 
262
        return self.db.instance_get_by_internal_id(context, instance_id)
 
263
 
 
264
    def reboot(self, context, instance_id):
 
265
        """Reboot the given instance."""
 
266
        instance = self.db.instance_get_by_internal_id(context, instance_id)
 
267
        host = instance['host']
 
268
        rpc.cast(context,
 
269
                 self.db.queue_get_for(context, FLAGS.compute_topic, host),
 
270
                 {"method": "reboot_instance",
 
271
                  "args": {"instance_id": instance['id']}})
 
272
 
 
273
    def rescue(self, context, instance_id):
 
274
        """Rescue the given instance."""
 
275
        instance = self.db.instance_get_by_internal_id(context, instance_id)
 
276
        host = instance['host']
 
277
        rpc.cast(context,
 
278
                 self.db.queue_get_for(context, FLAGS.compute_topic, host),
 
279
                 {"method": "rescue_instance",
 
280
                  "args": {"instance_id": instance['id']}})
 
281
 
 
282
    def unrescue(self, context, instance_id):
 
283
        """Unrescue the given instance."""
 
284
        instance = self.db.instance_get_by_internal_id(context, instance_id)
 
285
        host = instance['host']
 
286
        rpc.cast(context,
 
287
                 self.db.queue_get_for(context, FLAGS.compute_topic, host),
 
288
                 {"method": "unrescue_instance",
 
289
                  "args": {"instance_id": instance['id']}})
 
290
 
 
291
    def _get_network_topic(self, context):
 
292
        """Retrieves the network host for a project"""
 
293
        network_ref = self.network_manager.get_network(context)
 
294
        host = network_ref['host']
 
295
        if not host:
 
296
            host = rpc.call(context,
 
297
                            FLAGS.network_topic,
 
298
                            {"method": "set_network_host",
 
299
                             "args": {"network_id": network_ref['id']}})
 
300
        return self.db.queue_get_for(context, FLAGS.network_topic, host)