~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/compute/api.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2010-12-13 10:17:01 UTC
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20101213101701-txhhqbzsxw4avnxv
Tags: upstream-2011.1~bzr456
ImportĀ upstreamĀ versionĀ 2011.1~bzr456

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='', key_name=None,
 
59
                         key_data=None, security_group='default',
 
60
                         generate_hostname=generate_default_hostname):
 
61
        """Create the number of instances requested if quote and
 
62
        other arguments check out ok."""
 
63
 
 
64
        num_instances = quota.allowed_instances(context, max_count,
 
65
                                                instance_type)
 
66
        if num_instances < min_count:
 
67
            logging.warn("Quota exceeeded for %s, tried to run %s instances",
 
68
                         context.project_id, min_count)
 
69
            raise quota.QuotaError("Instance quota exceeded. You can only "
 
70
                                   "run %s more instances of this type." %
 
71
                                   num_instances, "InstanceLimitExceeded")
 
72
 
 
73
        is_vpn = image_id == FLAGS.vpn_image_id
 
74
        if not is_vpn:
 
75
            image = self.image_service.show(context, image_id)
 
76
            if kernel_id is None:
 
77
                kernel_id = image.get('kernelId', FLAGS.default_kernel)
 
78
            if ramdisk_id is None:
 
79
                ramdisk_id = image.get('ramdiskId', FLAGS.default_ramdisk)
 
80
 
 
81
            # Make sure we have access to kernel and ramdisk
 
82
            self.image_service.show(context, kernel_id)
 
83
            self.image_service.show(context, ramdisk_id)
 
84
 
 
85
        if security_group is None:
 
86
            security_group = ['default']
 
87
        if not type(security_group) is list:
 
88
            security_group = [security_group]
 
89
 
 
90
        security_groups = []
 
91
        self.ensure_default_security_group(context)
 
92
        for security_group_name in security_group:
 
93
            group = db.security_group_get_by_name(context,
 
94
                                                  context.project_id,
 
95
                                                  security_group_name)
 
96
            security_groups.append(group['id'])
 
97
 
 
98
        if key_data is None and key_name:
 
99
            key_pair = db.key_pair_get(context, context.user_id, key_name)
 
100
            key_data = key_pair['public_key']
 
101
 
 
102
        type_data = instance_types.INSTANCE_TYPES[instance_type]
 
103
        base_options = {
 
104
            'reservation_id': utils.generate_uid('r'),
 
105
            'image_id': image_id,
 
106
            'kernel_id': kernel_id,
 
107
            'ramdisk_id': ramdisk_id,
 
108
            'state_description': 'scheduling',
 
109
            'user_id': context.user_id,
 
110
            'project_id': context.project_id,
 
111
            'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
 
112
            'instance_type': instance_type,
 
113
            'memory_mb': type_data['memory_mb'],
 
114
            'vcpus': type_data['vcpus'],
 
115
            'local_gb': type_data['local_gb'],
 
116
            'display_name': display_name,
 
117
            'display_description': description,
 
118
            'key_name': key_name,
 
119
            'key_data': key_data}
 
120
 
 
121
        elevated = context.elevated()
 
122
        instances = []
 
123
        logging.debug("Going to run %s instances...", num_instances)
 
124
        for num in range(num_instances):
 
125
            instance = dict(mac_address=utils.generate_mac(),
 
126
                            launch_index=num,
 
127
                            **base_options)
 
128
            instance = self.db.instance_create(context, instance)
 
129
            instance_id = instance['id']
 
130
            internal_id = instance['internal_id']
 
131
 
 
132
            elevated = context.elevated()
 
133
            if not security_groups:
 
134
                security_groups = []
 
135
            for security_group_id in security_groups:
 
136
                self.db.instance_add_security_group(elevated,
 
137
                                                    instance_id,
 
138
                                                    security_group_id)
 
139
 
 
140
            # Set sane defaults if not specified
 
141
            updates = dict(hostname=generate_hostname(internal_id))
 
142
            if 'display_name' not in instance:
 
143
                updates['display_name'] = "Server %s" % internal_id
 
144
 
 
145
            instance = self.update_instance(context, instance_id, **updates)
 
146
            instances.append(instance)
 
147
 
 
148
            # TODO(vish): This probably should be done in the scheduler
 
149
            #             or in compute as a call.  The network should be
 
150
            #             allocated after the host is assigned and setup
 
151
            #             can happen at the same time.
 
152
            address = self.network_manager.allocate_fixed_ip(context,
 
153
                                                             instance_id,
 
154
                                                             is_vpn)
 
155
            rpc.cast(elevated,
 
156
                     self._get_network_topic(context),
 
157
                     {"method": "setup_fixed_ip",
 
158
                      "args": {"address": address}})
 
159
 
 
160
            logging.debug("Casting to scheduler for %s/%s's instance %s",
 
161
                          context.project_id, context.user_id, instance_id)
 
162
            rpc.cast(context,
 
163
                     FLAGS.scheduler_topic,
 
164
                     {"method": "run_instance",
 
165
                      "args": {"topic": FLAGS.compute_topic,
 
166
                               "instance_id": instance_id}})
 
167
 
 
168
        return instances
 
169
 
 
170
    def ensure_default_security_group(self, context):
 
171
        """ Create security group for the security context if it
 
172
        does not already exist
 
173
 
 
174
        :param context: the security context
 
175
 
 
176
        """
 
177
        try:
 
178
            db.security_group_get_by_name(context, context.project_id,
 
179
                                          'default')
 
180
        except exception.NotFound:
 
181
            values = {'name': 'default',
 
182
                      'description': 'default',
 
183
                      'user_id': context.user_id,
 
184
                      'project_id': context.project_id}
 
185
            db.security_group_create(context, values)
 
186
 
 
187
    def update_instance(self, context, instance_id, **kwargs):
 
188
        """Updates the instance in the datastore.
 
189
 
 
190
        :param context: The security context
 
191
        :param instance_id: ID of the instance to update
 
192
        :param kwargs: All additional keyword args are treated
 
193
                       as data fields of the instance to be
 
194
                       updated
 
195
 
 
196
        :retval None
 
197
 
 
198
        """
 
199
        return self.db.instance_update(context, instance_id, kwargs)
 
200
 
 
201
    def delete_instance(self, context, instance_id):
 
202
        logging.debug("Going to try and terminate %d" % instance_id)
 
203
        try:
 
204
            instance = self.db.instance_get_by_internal_id(context,
 
205
                                                           instance_id)
 
206
        except exception.NotFound as e:
 
207
            logging.warning("Instance %d was not found during terminate",
 
208
                            instance_id)
 
209
            raise e
 
210
 
 
211
        if (instance['state_description'] == 'terminating'):
 
212
            logging.warning("Instance %d is already being terminated",
 
213
                            instance_id)
 
214
            return
 
215
 
 
216
        self.update_instance(context,
 
217
                             instance['id'],
 
218
                             state_description='terminating',
 
219
                             state=0,
 
220
                             terminated_at=datetime.datetime.utcnow())
 
221
 
 
222
        # FIXME(ja): where should network deallocate occur?
 
223
        address = self.db.instance_get_floating_address(context,
 
224
                                                        instance['id'])
 
225
        if address:
 
226
            logging.debug("Disassociating address %s" % address)
 
227
            # NOTE(vish): Right now we don't really care if the ip is
 
228
            #             disassociated.  We may need to worry about
 
229
            #             checking this later.  Perhaps in the scheduler?
 
230
            rpc.cast(context,
 
231
                     self._get_network_topic(context),
 
232
                     {"method": "disassociate_floating_ip",
 
233
                      "args": {"floating_address": address}})
 
234
 
 
235
        address = self.db.instance_get_fixed_address(context, instance['id'])
 
236
        if address:
 
237
            logging.debug("Deallocating address %s" % address)
 
238
            # NOTE(vish): Currently, nothing needs to be done on the
 
239
            #             network node until release. If this changes,
 
240
            #             we will need to cast here.
 
241
            self.network_manager.deallocate_fixed_ip(context.elevated(),
 
242
                                                     address)
 
243
 
 
244
        host = instance['host']
 
245
        if host:
 
246
            rpc.cast(context,
 
247
                     self.db.queue_get_for(context, FLAGS.compute_topic, host),
 
248
                     {"method": "terminate_instance",
 
249
                      "args": {"instance_id": instance['id']}})
 
250
        else:
 
251
            self.db.instance_destroy(context, instance['id'])
 
252
 
 
253
    def get_instances(self, context, project_id=None):
 
254
        """Get all instances, possibly filtered by project ID or
 
255
        user ID. If there is no filter and the context is an admin,
 
256
        it will retreive all instances in the system."""
 
257
        if project_id or not context.is_admin:
 
258
            if not context.project:
 
259
                return self.db.instance_get_all_by_user(context,
 
260
                                                        context.user_id)
 
261
            if project_id is None:
 
262
                project_id = context.project_id
 
263
            return self.db.instance_get_all_by_project(context, project_id)
 
264
        return self.db.instance_get_all(context)
 
265
 
 
266
    def get_instance(self, context, instance_id):
 
267
        return self.db.instance_get_by_internal_id(context, instance_id)
 
268
 
 
269
    def reboot(self, context, instance_id):
 
270
        """Reboot the given instance."""
 
271
        instance = self.db.instance_get_by_internal_id(context, instance_id)
 
272
        host = instance['host']
 
273
        rpc.cast(context,
 
274
                 self.db.queue_get_for(context, FLAGS.compute_topic, host),
 
275
                 {"method": "reboot_instance",
 
276
                  "args": {"instance_id": instance['id']}})
 
277
 
 
278
    def rescue(self, context, instance_id):
 
279
        """Rescue the given instance."""
 
280
        instance = self.db.instance_get_by_internal_id(context, instance_id)
 
281
        host = instance['host']
 
282
        rpc.cast(context,
 
283
                 self.db.queue_get_for(context, FLAGS.compute_topic, host),
 
284
                 {"method": "rescue_instance",
 
285
                  "args": {"instance_id": instance['id']}})
 
286
 
 
287
    def unrescue(self, context, instance_id):
 
288
        """Unrescue the given instance."""
 
289
        instance = self.db.instance_get_by_internal_id(context, instance_id)
 
290
        host = instance['host']
 
291
        rpc.cast(context,
 
292
                 self.db.queue_get_for(context, FLAGS.compute_topic, host),
 
293
                 {"method": "unrescue_instance",
 
294
                  "args": {"instance_id": instance['id']}})
 
295
 
 
296
    def _get_network_topic(self, context):
 
297
        """Retrieves the network host for a project"""
 
298
        network_ref = self.network_manager.get_network(context)
 
299
        host = network_ref['host']
 
300
        if not host:
 
301
            host = rpc.call(context,
 
302
                            FLAGS.network_topic,
 
303
                            {"method": "set_network_host",
 
304
                             "args": {"network_id": network_ref['id']}})
 
305
        return self.db.queue_get_for(context, FLAGS.network_topic, host)