1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2010 United States Government as represented by the
4
# Administrator of the National Aeronautics and Space Administration.
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
11
# http://www.apache.org/licenses/LICENSE-2.0
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
20
Handles all API requests relating to instances (guest vms).
28
from nova import exception
29
from nova import flags
30
from nova import quota
32
from nova import utils
33
from nova.compute import instance_types
34
from nova.db import base
39
def generate_default_hostname(internal_id):
40
"""Default function to generate a hostname given an instance reference."""
41
return str(internal_id)
44
class ComputeAPI(base.Base):
45
"""API for interacting with the compute manager."""
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
52
image_service = utils.import_object(FLAGS.image_service)
53
self.image_service = image_service
54
super(ComputeAPI, self).__init__(**kwargs)
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."""
64
num_instances = quota.allowed_instances(context, max_count,
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")
73
is_vpn = image_id == FLAGS.vpn_image_id
75
image = self.image_service.show(context, image_id)
77
kernel_id = image.get('kernelId', FLAGS.default_kernel)
78
if ramdisk_id is None:
79
ramdisk_id = image.get('ramdiskId', FLAGS.default_ramdisk)
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)
85
if security_group is None:
86
security_group = ['default']
87
if not type(security_group) is list:
88
security_group = [security_group]
91
self.ensure_default_security_group(context)
92
for security_group_name in security_group:
93
group = db.security_group_get_by_name(context,
96
security_groups.append(group['id'])
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']
102
type_data = instance_types.INSTANCE_TYPES[instance_type]
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}
121
elevated = context.elevated()
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(),
128
instance = self.db.instance_create(context, instance)
129
instance_id = instance['id']
130
internal_id = instance['internal_id']
132
elevated = context.elevated()
133
if not security_groups:
135
for security_group_id in security_groups:
136
self.db.instance_add_security_group(elevated,
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
145
instance = self.update_instance(context, instance_id, **updates)
146
instances.append(instance)
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,
156
self._get_network_topic(context),
157
{"method": "setup_fixed_ip",
158
"args": {"address": address}})
160
logging.debug("Casting to scheduler for %s/%s's instance %s",
161
context.project_id, context.user_id, instance_id)
163
FLAGS.scheduler_topic,
164
{"method": "run_instance",
165
"args": {"topic": FLAGS.compute_topic,
166
"instance_id": instance_id}})
170
def ensure_default_security_group(self, context):
171
""" Create security group for the security context if it
172
does not already exist
174
:param context: the security context
178
db.security_group_get_by_name(context, context.project_id,
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)
187
def update_instance(self, context, instance_id, **kwargs):
188
"""Updates the instance in the datastore.
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
199
return self.db.instance_update(context, instance_id, kwargs)
201
def delete_instance(self, context, instance_id):
202
logging.debug("Going to try and terminate %d" % instance_id)
204
instance = self.db.instance_get_by_internal_id(context,
206
except exception.NotFound as e:
207
logging.warning("Instance %d was not found during terminate",
211
if (instance['state_description'] == 'terminating'):
212
logging.warning("Instance %d is already being terminated",
216
self.update_instance(context,
218
state_description='terminating',
220
terminated_at=datetime.datetime.utcnow())
222
# FIXME(ja): where should network deallocate occur?
223
address = self.db.instance_get_floating_address(context,
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?
231
self._get_network_topic(context),
232
{"method": "disassociate_floating_ip",
233
"args": {"floating_address": address}})
235
address = self.db.instance_get_fixed_address(context, instance['id'])
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(),
244
host = instance['host']
247
self.db.queue_get_for(context, FLAGS.compute_topic, host),
248
{"method": "terminate_instance",
249
"args": {"instance_id": instance['id']}})
251
self.db.instance_destroy(context, instance['id'])
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,
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)
266
def get_instance(self, context, instance_id):
267
return self.db.instance_get_by_internal_id(context, instance_id)
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']
274
self.db.queue_get_for(context, FLAGS.compute_topic, host),
275
{"method": "reboot_instance",
276
"args": {"instance_id": instance['id']}})
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']
283
self.db.queue_get_for(context, FLAGS.compute_topic, host),
284
{"method": "rescue_instance",
285
"args": {"instance_id": instance['id']}})
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']
292
self.db.queue_get_for(context, FLAGS.compute_topic, host),
293
{"method": "unrescue_instance",
294
"args": {"instance_id": instance['id']}})
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']
301
host = rpc.call(context,
303
{"method": "set_network_host",
304
"args": {"network_id": network_ref['id']}})
305
return self.db.queue_get_for(context, FLAGS.network_topic, host)