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='', 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."""
65
num_instances = quota.allowed_instances(context, max_count,
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")
74
is_vpn = image_id == FLAGS.vpn_image_id
76
image = self.image_service.show(context, image_id)
78
kernel_id = image.get('kernelId', FLAGS.default_kernel)
79
if ramdisk_id is None:
80
ramdisk_id = image.get('ramdiskId', FLAGS.default_ramdisk)
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)
86
if security_group is None:
87
security_group = ['default']
88
if not type(security_group) is list:
89
security_group = [security_group]
92
self.ensure_default_security_group(context)
93
for security_group_name in security_group:
94
group = db.security_group_get_by_name(context,
97
security_groups.append(group['id'])
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']
103
type_data = instance_types.INSTANCE_TYPES[instance_type]
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}
122
elevated = context.elevated()
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(),
129
instance = self.db.instance_create(context, instance)
130
instance_id = instance['id']
131
internal_id = instance['internal_id']
133
elevated = context.elevated()
134
if not security_groups:
136
for security_group_id in security_groups:
137
self.db.instance_add_security_group(elevated,
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
146
instance = self.update_instance(context, instance_id, **updates)
147
instances.append(instance)
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,
157
self._get_network_topic(context),
158
{"method": "setup_fixed_ip",
159
"args": {"address": address}})
161
logging.debug("Casting to scheduler for %s/%s's instance %s" %
162
(context.project_id, context.user_id, instance_id))
164
FLAGS.scheduler_topic,
165
{"method": "run_instance",
166
"args": {"topic": FLAGS.compute_topic,
167
"instance_id": instance_id}})
171
def ensure_default_security_group(self, context):
173
db.security_group_get_by_name(context, context.project_id,
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)
182
def update_instance(self, context, instance_id, **kwargs):
183
"""Updates the instance in the datastore.
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
194
return self.db.instance_update(context, instance_id, kwargs)
196
def delete_instance(self, context, instance_id):
197
logging.debug("Going to try and terminate %d" % instance_id)
199
instance = self.db.instance_get_by_internal_id(context,
201
except exception.NotFound as e:
202
logging.warning("Instance %d was not found during terminate",
206
if (instance['state_description'] == 'terminating'):
207
logging.warning("Instance %d is already being terminated",
211
self.update_instance(context,
213
state_description='terminating',
215
terminated_at=datetime.datetime.utcnow())
217
# FIXME(ja): where should network deallocate occur?
218
address = self.db.instance_get_floating_address(context,
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?
226
self._get_network_topic(context),
227
{"method": "disassociate_floating_ip",
228
"args": {"floating_address": address}})
230
address = self.db.instance_get_fixed_address(context, instance['id'])
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(),
239
host = instance['host']
242
self.db.queue_get_for(context, FLAGS.compute_topic, host),
243
{"method": "terminate_instance",
244
"args": {"instance_id": instance['id']}})
246
self.db.instance_destroy(context, instance['id'])
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,
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)
261
def get_instance(self, context, instance_id):
262
return self.db.instance_get_by_internal_id(context, instance_id)
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']
269
self.db.queue_get_for(context, FLAGS.compute_topic, host),
270
{"method": "reboot_instance",
271
"args": {"instance_id": instance['id']}})
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']
278
self.db.queue_get_for(context, FLAGS.compute_topic, host),
279
{"method": "rescue_instance",
280
"args": {"instance_id": instance['id']}})
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']
287
self.db.queue_get_for(context, FLAGS.compute_topic, host),
288
{"method": "unrescue_instance",
289
"args": {"instance_id": instance['id']}})
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']
296
host = rpc.call(context,
298
{"method": "set_network_host",
299
"args": {"network_id": network_ref['id']}})
300
return self.db.queue_get_for(context, FLAGS.network_topic, host)