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.
5
# Copyright 2011 Piston Cloud Computing, Inc.
8
# Licensed under the Apache License, Version 2.0 (the "License"); you may
9
# not use this file except in compliance with the License. You may obtain
10
# a copy of the License at
12
# http://www.apache.org/licenses/LICENSE-2.0
14
# Unless required by applicable law or agreed to in writing, software
15
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17
# License for the specific language governing permissions and limitations
19
"""Tests for compute service"""
29
import nova.common.policy
30
from nova import compute
31
from nova.compute import aggregate_states
32
from nova.compute import api as compute_api
33
from nova.compute import instance_types
34
from nova.compute import manager as compute_manager
35
from nova.compute import power_state
36
from nova.compute import rpcapi as compute_rpcapi
37
from nova.compute import task_states
38
from nova.compute import vm_states
39
from nova import context
41
from nova import exception
42
from nova import flags
43
from nova.image import fake as fake_image
44
from nova import log as logging
45
from nova.notifier import test_notifier
46
from nova.openstack.common import importutils
49
from nova.rpc import common as rpc_common
50
from nova.scheduler import driver as scheduler_driver
52
from nova.tests import fake_network
53
from nova import utils
57
LOG = logging.getLogger(__name__)
59
flags.DECLARE('stub_network', 'nova.compute.manager')
60
flags.DECLARE('live_migration_retry_count', 'nova.compute.manager')
61
flags.DECLARE('additional_compute_capabilities', 'nova.compute.manager')
64
FAKE_IMAGE_REF = 'fake-image-ref'
65
orig_rpc_call = rpc.call
66
orig_rpc_cast = rpc.cast
69
def rpc_call_wrapper(context, topic, msg, do_cast=True):
70
"""Stub out the scheduler creating the instance entry"""
71
if (topic == FLAGS.scheduler_topic and
72
msg['method'] == 'run_instance'):
73
request_spec = msg['args']['request_spec']
74
scheduler = scheduler_driver.Scheduler
75
num_instances = request_spec.get('num_instances', 1)
77
for num in xrange(num_instances):
78
request_spec['instance_properties']['launch_index'] = num
79
instance = scheduler().create_instance_db_entry(
80
context, request_spec)
81
encoded = scheduler_driver.encode_instance(instance)
82
instances.append(encoded)
86
orig_rpc_cast(context, topic, msg)
88
return orig_rpc_call(context, topic, msg)
91
def rpc_cast_wrapper(context, topic, msg):
92
"""Stub out the scheduler creating the instance entry in
93
the reservation_id case.
95
rpc_call_wrapper(context, topic, msg, do_cast=True)
98
def nop_report_driver_status(self):
102
class BaseTestCase(test.TestCase):
105
super(BaseTestCase, self).setUp()
106
self.flags(connection_type='fake',
108
notification_driver='nova.notifier.test_notifier',
109
network_manager='nova.network.manager.FlatManager')
110
self.compute = importutils.import_object(FLAGS.compute_manager)
112
self.user_id = 'fake'
113
self.project_id = 'fake'
114
self.context = context.RequestContext(self.user_id,
116
test_notifier.NOTIFICATIONS = []
118
def fake_show(meh, context, id):
119
return {'id': id, 'min_disk': None, 'min_ram': None,
120
'properties': {'kernel_id': 'fake_kernel_id',
121
'ramdisk_id': 'fake_ramdisk_id',
122
'something_else': 'meow'}}
124
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
125
self.stubs.Set(rpc, 'call', rpc_call_wrapper)
126
self.stubs.Set(rpc, 'cast', rpc_cast_wrapper)
129
instances = db.instance_get_all(self.context.elevated())
130
for instance in instances:
131
db.instance_destroy(self.context.elevated(), instance['id'])
132
super(BaseTestCase, self).tearDown()
134
def _create_fake_instance(self, params=None, type_name='m1.tiny'):
135
"""Create a test instance"""
140
inst['vm_state'] = vm_states.ACTIVE
141
inst['image_ref'] = FAKE_IMAGE_REF
142
inst['reservation_id'] = 'r-fakeres'
143
inst['launch_time'] = '10'
144
inst['user_id'] = self.user_id
145
inst['project_id'] = self.project_id
146
inst['host'] = 'fake_host'
147
type_id = instance_types.get_instance_type_by_name(type_name)['id']
148
inst['instance_type_id'] = type_id
149
inst['ami_launch_index'] = 0
150
inst['memory_mb'] = 0
152
inst['ephemeral_gb'] = 0
154
return db.instance_create(self.context, inst)
156
def _create_instance(self, params=None, type_name='m1.tiny'):
157
"""Create a test instance. Returns uuid"""
158
return self._create_fake_instance(params, type_name=type_name)['uuid']
160
def _create_instance_type(self, params=None):
161
"""Create a test instance type"""
165
context = self.context.elevated()
167
inst['name'] = 'm1.small'
168
inst['memory_mb'] = 1024
171
inst['ephemeral_gb'] = 10
172
inst['flavorid'] = '1'
174
inst['rxtx_factor'] = 1
176
return db.instance_type_create(context, inst)['id']
178
def _create_group(self):
179
values = {'name': 'testgroup',
180
'description': 'testgroup',
181
'user_id': self.user_id,
182
'project_id': self.project_id}
183
return db.security_group_create(self.context, values)
186
class ComputeTestCase(BaseTestCase):
188
def fake_get_nw_info(cls, ctxt, instance, *args, **kwargs):
189
self.assertTrue(ctxt.is_admin)
190
return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1,
193
super(ComputeTestCase, self).setUp()
194
self.stubs.Set(nova.network.API, 'get_instance_nw_info',
196
self.stubs.Set(nova.network.API, 'allocate_for_instance',
200
super(ComputeTestCase, self).tearDown()
201
utils.clear_time_override()
203
def test_wrap_instance_fault(self):
204
inst_uuid = "fake_uuid"
206
called = {'fault_added': False}
208
def did_it_add_fault(*args):
209
called['fault_added'] = True
211
self.stubs.Set(self.compute, 'add_instance_fault_from_exc',
214
@nova.compute.manager.wrap_instance_fault
215
def failer(self2, context, instance_uuid):
216
raise NotImplementedError()
218
self.assertRaises(NotImplementedError, failer,
219
self.compute, self.context, inst_uuid)
221
self.assertTrue(called['fault_added'])
223
def test_wrap_instance_fault_no_instance(self):
224
inst_uuid = "fake_uuid"
226
called = {'fault_added': False}
228
def did_it_add_fault(*args):
229
called['fault_added'] = True
231
self.stubs.Set(self.compute, 'add_instance_fault_from_exc',
234
@nova.compute.manager.wrap_instance_fault
235
def failer(self2, context, instance_uuid):
236
raise exception.InstanceNotFound()
238
self.assertRaises(exception.InstanceNotFound, failer,
239
self.compute, self.context, inst_uuid)
241
self.assertFalse(called['fault_added'])
243
def test_create_instance_with_img_ref_associates_config_drive(self):
244
"""Make sure create associates a config drive."""
246
instance = self._create_fake_instance(
247
params={'config_drive': '1234', })
250
self.compute.run_instance(self.context, instance['uuid'])
251
instances = db.instance_get_all(context.get_admin_context())
252
instance = instances[0]
254
self.assertTrue(instance.config_drive)
256
db.instance_destroy(self.context, instance['id'])
258
def test_create_instance_associates_config_drive(self):
259
"""Make sure create associates a config drive."""
261
instance = self._create_fake_instance(
262
params={'config_drive': '1234', })
265
self.compute.run_instance(self.context, instance['uuid'])
266
instances = db.instance_get_all(context.get_admin_context())
267
instance = instances[0]
269
self.assertTrue(instance.config_drive)
271
db.instance_destroy(self.context, instance['id'])
273
def test_default_access_ip(self):
274
self.flags(default_access_ip_network_name='test1', stub_network=False)
275
instance = self._create_fake_instance()
278
self.compute.run_instance(self.context, instance['uuid'],
280
instances = db.instance_get_all(context.get_admin_context())
281
instance = instances[0]
283
self.assertEqual(instance.access_ip_v4, '192.168.1.100')
284
self.assertEqual(instance.access_ip_v6, '2001:db8:0:1::1')
286
db.instance_destroy(self.context, instance['id'])
288
def test_no_default_access_ip(self):
289
instance = self._create_fake_instance()
292
self.compute.run_instance(self.context, instance['uuid'],
294
instances = db.instance_get_all(context.get_admin_context())
295
instance = instances[0]
297
self.assertFalse(instance.access_ip_v4)
298
self.assertFalse(instance.access_ip_v6)
300
db.instance_destroy(self.context, instance['id'])
302
def test_fail_to_schedule_persists(self):
303
"""check the persistence of the ERROR(scheduling) state"""
304
self._create_instance(params={'vm_state': vm_states.ERROR,
305
'task_state': task_states.SCHEDULING})
306
#check state is failed even after the periodic poll
307
self.compute.periodic_tasks(context.get_admin_context())
308
self._assert_state({'vm_state': vm_states.ERROR,
309
'task_state': task_states.SCHEDULING})
311
def test_run_instance_setup_block_device_mapping_fail(self):
312
""" block device mapping failure test.
314
Make sure that when there is a block device mapping problem,
315
the instance goes to ERROR state, keeping the task state
317
def fake(*args, **kwargs):
318
raise test.TestingException()
319
self.stubs.Set(nova.compute.manager.ComputeManager,
320
'_setup_block_device_mapping', fake)
321
instance_uuid = self._create_instance()
322
self.assertRaises(test.TestingException, self.compute.run_instance,
323
self.context, instance_uuid)
324
#check state is failed even after the periodic poll
325
self._assert_state({'vm_state': vm_states.ERROR,
326
'task_state': task_states.BLOCK_DEVICE_MAPPING})
327
self.compute.periodic_tasks(context.get_admin_context())
328
self._assert_state({'vm_state': vm_states.ERROR,
329
'task_state': task_states.BLOCK_DEVICE_MAPPING})
331
def test_run_instance_spawn_fail(self):
332
""" spawn failure test.
334
Make sure that when there is a spawning problem,
335
the instance goes to ERROR state, keeping the task state"""
336
def fake(*args, **kwargs):
337
raise test.TestingException()
338
self.stubs.Set(self.compute.driver, 'spawn', fake)
339
instance_uuid = self._create_instance()
340
self.assertRaises(test.TestingException, self.compute.run_instance,
341
self.context, instance_uuid)
342
#check state is failed even after the periodic poll
343
self._assert_state({'vm_state': vm_states.ERROR,
344
'task_state': task_states.SPAWNING})
345
self.compute.periodic_tasks(context.get_admin_context())
346
self._assert_state({'vm_state': vm_states.ERROR,
347
'task_state': task_states.SPAWNING})
349
def test_can_terminate_on_error_state(self):
350
"""Make sure that the instance can be terminated in ERROR state"""
351
elevated = context.get_admin_context()
352
#check failed to schedule --> terminate
353
instance_uuid = self._create_instance(params={'vm_state':
355
self.compute.terminate_instance(self.context, instance_uuid)
356
self.assertRaises(exception.InstanceNotFound, db.instance_get_by_uuid,
357
elevated, instance_uuid)
359
def test_run_terminate(self):
360
"""Make sure it is possible to run and terminate instance"""
361
instance = self._create_fake_instance()
363
self.compute.run_instance(self.context, instance['uuid'])
365
instances = db.instance_get_all(context.get_admin_context())
366
LOG.info(_("Running instances: %s"), instances)
367
self.assertEqual(len(instances), 1)
369
self.compute.terminate_instance(self.context, instance['uuid'])
371
instances = db.instance_get_all(context.get_admin_context())
372
LOG.info(_("After terminating instances: %s"), instances)
373
self.assertEqual(len(instances), 0)
375
def test_run_terminate_timestamps(self):
376
"""Make sure timestamps are set for launched and destroyed"""
377
instance = self._create_fake_instance()
378
self.assertEqual(instance['launched_at'], None)
379
self.assertEqual(instance['deleted_at'], None)
380
launch = utils.utcnow()
381
self.compute.run_instance(self.context, instance['uuid'])
382
instance = db.instance_get_by_uuid(self.context, instance['uuid'])
383
self.assert_(instance['launched_at'] > launch)
384
self.assertEqual(instance['deleted_at'], None)
385
terminate = utils.utcnow()
386
self.compute.terminate_instance(self.context, instance['uuid'])
387
context = self.context.elevated(read_deleted="only")
388
instance = db.instance_get_by_uuid(context, instance['uuid'])
389
self.assert_(instance['launched_at'] < terminate)
390
self.assert_(instance['deleted_at'] > terminate)
393
"""Ensure instance can be stopped"""
394
instance = self._create_fake_instance()
395
instance_uuid = instance['uuid']
396
self.compute.run_instance(self.context, instance_uuid)
397
self.compute.stop_instance(self.context, instance_uuid)
398
self.compute.terminate_instance(self.context, instance_uuid)
400
def test_start(self):
401
"""Ensure instance can be started"""
402
instance = self._create_fake_instance()
403
instance_uuid = instance['uuid']
404
self.compute.run_instance(self.context, instance_uuid)
405
self.compute.stop_instance(self.context, instance_uuid)
406
self.compute.start_instance(self.context, instance_uuid)
407
self.compute.terminate_instance(self.context, instance_uuid)
409
def test_rescue(self):
410
"""Ensure instance can be rescued and unrescued"""
412
called = {'rescued': False,
415
def fake_rescue(self, context, instance_ref, network_info, image_meta):
416
called['rescued'] = True
418
self.stubs.Set(nova.virt.fake.FakeConnection, 'rescue', fake_rescue)
420
def fake_unrescue(self, instance_ref, network_info):
421
called['unrescued'] = True
423
self.stubs.Set(nova.virt.fake.FakeConnection, 'unrescue',
426
instance = self._create_fake_instance()
427
instance_uuid = instance['uuid']
428
self.compute.run_instance(self.context, instance_uuid)
429
self.compute.rescue_instance(self.context, instance_uuid)
430
self.assertTrue(called['rescued'])
431
self.compute.unrescue_instance(self.context, instance_uuid)
432
self.assertTrue(called['unrescued'])
433
self.compute.terminate_instance(self.context, instance_uuid)
435
def test_power_on(self):
436
"""Ensure instance can be powered on"""
438
called = {'power_on': False}
440
def fake_driver_power_on(self, instance):
441
called['power_on'] = True
443
self.stubs.Set(nova.virt.driver.ComputeDriver, 'power_on',
444
fake_driver_power_on)
446
instance = self._create_fake_instance()
447
instance_uuid = instance['uuid']
448
self.compute.run_instance(self.context, instance_uuid)
449
self.compute.power_on_instance(self.context, instance_uuid)
450
self.assertTrue(called['power_on'])
451
self.compute.terminate_instance(self.context, instance_uuid)
453
def test_power_off(self):
454
"""Ensure instance can be powered off"""
456
called = {'power_off': False}
458
def fake_driver_power_off(self, instance):
459
called['power_off'] = True
461
self.stubs.Set(nova.virt.driver.ComputeDriver, 'power_off',
462
fake_driver_power_off)
464
instance = self._create_fake_instance()
465
instance_uuid = instance['uuid']
466
self.compute.run_instance(self.context, instance_uuid)
467
self.compute.power_off_instance(self.context, instance_uuid)
468
self.assertTrue(called['power_off'])
469
self.compute.terminate_instance(self.context, instance_uuid)
471
def test_pause(self):
472
"""Ensure instance can be paused and unpaused"""
473
instance = self._create_fake_instance()
474
instance_uuid = instance['uuid']
475
self.compute.run_instance(self.context, instance_uuid)
476
self.compute.pause_instance(self.context, instance_uuid)
477
self.compute.unpause_instance(self.context, instance_uuid)
478
self.compute.terminate_instance(self.context, instance_uuid)
480
def test_suspend(self):
481
"""ensure instance can be suspended and resumed"""
482
instance = self._create_fake_instance()
483
instance_uuid = instance['uuid']
484
self.compute.run_instance(self.context, instance_uuid)
485
self.compute.suspend_instance(self.context, instance_uuid)
486
self.compute.resume_instance(self.context, instance_uuid)
487
self.compute.terminate_instance(self.context, instance_uuid)
489
def test_rebuild(self):
490
"""Ensure instance can be rebuilt"""
491
instance = self._create_fake_instance()
492
instance_uuid = instance['uuid']
493
image_ref = instance['image_ref']
495
self.compute.run_instance(self.context, instance_uuid)
496
self.compute.rebuild_instance(self.context, instance_uuid,
497
image_ref, image_ref)
498
self.compute.terminate_instance(self.context, instance_uuid)
500
def test_rebuild_launch_time(self):
501
"""Ensure instance can be rebuilt"""
502
old_time = datetime.datetime(2012, 4, 1)
503
cur_time = datetime.datetime(2012, 12, 21, 12, 21)
504
utils.set_time_override(old_time)
505
instance = self._create_fake_instance()
506
instance_uuid = instance['uuid']
507
image_ref = instance['image_ref']
509
self.compute.run_instance(self.context, instance_uuid)
510
utils.set_time_override(cur_time)
511
self.compute.rebuild_instance(self.context, instance_uuid,
512
image_ref, image_ref)
513
instance = db.instance_get_by_uuid(self.context, instance_uuid)
514
self.assertEquals(cur_time, instance['launched_at'])
515
self.compute.terminate_instance(self.context, instance_uuid)
517
def test_reboot_soft(self):
518
"""Ensure instance can be soft rebooted"""
519
instance = self._create_fake_instance()
520
instance_uuid = instance['uuid']
522
self.compute.run_instance(self.context, instance_uuid)
523
db.instance_update(self.context, instance_uuid,
524
{'task_state': task_states.REBOOTING})
527
self.compute.reboot_instance(self.context, instance_uuid, reboot_type)
529
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
530
self.assertEqual(inst_ref['power_state'], power_state.RUNNING)
531
self.assertEqual(inst_ref['task_state'], None)
533
self.compute.terminate_instance(self.context, inst_ref['uuid'])
535
def test_reboot_hard(self):
536
"""Ensure instance can be hard rebooted"""
537
instance = self._create_fake_instance()
538
instance_uuid = instance['uuid']
540
self.compute.run_instance(self.context, instance_uuid)
541
db.instance_update(self.context, instance_uuid,
542
{'task_state': task_states.REBOOTING_HARD})
545
self.compute.reboot_instance(self.context, instance_uuid, reboot_type)
547
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
548
self.assertEqual(inst_ref['power_state'], power_state.RUNNING)
549
self.assertEqual(inst_ref['task_state'], None)
551
self.compute.terminate_instance(self.context, inst_ref['uuid'])
553
def test_set_admin_password(self):
554
"""Ensure instance can have its admin password set"""
555
instance = self._create_fake_instance()
556
instance_uuid = instance['uuid']
557
self.compute.run_instance(self.context, instance_uuid)
558
db.instance_update(self.context, instance_uuid,
559
{'task_state': task_states.UPDATING_PASSWORD})
561
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
562
self.assertEqual(inst_ref['vm_state'], vm_states.ACTIVE)
563
self.assertEqual(inst_ref['task_state'], task_states.UPDATING_PASSWORD)
565
self.compute.set_admin_password(self.context, instance_uuid)
567
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
568
self.assertEqual(inst_ref['vm_state'], vm_states.ACTIVE)
569
self.assertEqual(inst_ref['task_state'], None)
571
self.compute.terminate_instance(self.context, inst_ref['uuid'])
573
def test_set_admin_password_bad_state(self):
574
"""Test setting password while instance is rebuilding."""
575
instance = self._create_fake_instance()
576
self.compute.run_instance(self.context, instance['uuid'])
577
db.instance_update(self.context, instance['uuid'], {
578
"power_state": power_state.NOSTATE,
580
instance = db.instance_get_by_uuid(self.context, instance['uuid'])
582
self.assertEqual(instance['power_state'], power_state.NOSTATE)
584
def fake_driver_get_info(self2, _instance):
585
return {'state': power_state.NOSTATE,
591
self.stubs.Set(nova.virt.fake.FakeConnection, 'get_info',
592
fake_driver_get_info)
594
self.assertRaises(exception.Invalid,
595
self.compute.set_admin_password,
598
self.compute.terminate_instance(self.context, instance['uuid'])
600
def test_set_admin_password_driver_error(self):
601
"""Ensure error is raised admin password set"""
603
def fake_sleep(_time):
606
self.stubs.Set(time, 'sleep', fake_sleep)
608
def fake_driver_set_pass(self2, _instance, _pwd):
609
raise exception.NotAuthorized(_('Internal error'))
611
self.stubs.Set(nova.virt.fake.FakeConnection, 'set_admin_password',
612
fake_driver_set_pass)
614
instance = self._create_fake_instance()
615
instance_uuid = instance['uuid']
616
self.compute.run_instance(self.context, instance_uuid)
617
db.instance_update(self.context, instance_uuid,
618
{'task_state': task_states.UPDATING_PASSWORD})
620
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
621
self.assertEqual(inst_ref['vm_state'], vm_states.ACTIVE)
622
self.assertEqual(inst_ref['task_state'], task_states.UPDATING_PASSWORD)
624
#error raised from the driver should not reveal internal information
625
#so a new error is raised
626
self.assertRaises(exception.NovaException,
627
self.compute.set_admin_password,
628
self.context, instance_uuid)
630
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
631
self.assertEqual(inst_ref['vm_state'], vm_states.ERROR)
632
self.assertEqual(inst_ref['task_state'], task_states.UPDATING_PASSWORD)
634
self.compute.terminate_instance(self.context, inst_ref['uuid'])
636
def test_inject_file(self):
637
"""Ensure we can write a file to an instance"""
638
called = {'inject': False}
640
def fake_driver_inject_file(self2, instance, path, contents):
641
self.assertEqual(path, "/tmp/test")
642
self.assertEqual(contents, "File Contents")
643
called['inject'] = True
645
self.stubs.Set(nova.virt.fake.FakeConnection, 'inject_file',
646
fake_driver_inject_file)
648
instance = self._create_fake_instance()
649
self.compute.run_instance(self.context, instance['uuid'])
650
self.compute.inject_file(self.context, instance['uuid'], "/tmp/test",
652
self.assertTrue(called['inject'])
653
self.compute.terminate_instance(self.context, instance['uuid'])
655
def test_inject_network_info(self):
656
"""Ensure we can inject network info"""
657
called = {'inject': False}
659
def fake_driver_inject_network(self, instance, network_info):
660
called['inject'] = True
662
self.stubs.Set(nova.virt.fake.FakeConnection, 'inject_network_info',
663
fake_driver_inject_network)
665
instance = self._create_fake_instance()
666
instance_uuid = instance['uuid']
667
self.compute.run_instance(self.context, instance_uuid)
668
self.compute.inject_network_info(self.context, instance_uuid)
669
self.assertTrue(called['inject'])
670
self.compute.terminate_instance(self.context, instance_uuid)
672
def test_reset_network(self):
673
"""Ensure we can reset networking on an instance"""
674
called = {'reset': False}
676
def fake_driver_reset_network(self, instance):
677
called['reset'] = True
679
self.stubs.Set(nova.virt.fake.FakeConnection, 'reset_network',
680
fake_driver_reset_network)
682
instance = self._create_fake_instance()
683
instance_uuid = instance['uuid']
684
self.compute.run_instance(self.context, instance_uuid)
685
self.compute.reset_network(self.context, instance_uuid)
686
self.assertTrue(called['reset'])
687
self.compute.terminate_instance(self.context, instance_uuid)
689
def test_agent_update(self):
690
"""Ensure instance can have its agent updated"""
691
called = {'agent_update': False}
693
def fake_driver_agent_update(self2, instance, url, md5hash):
694
called['agent_update'] = True
695
self.assertEqual(url, 'http://fake/url/')
696
self.assertEqual(md5hash, 'fakehash')
698
self.stubs.Set(nova.virt.fake.FakeConnection, 'agent_update',
699
fake_driver_agent_update)
701
instance = self._create_fake_instance()
702
self.compute.run_instance(self.context, instance['uuid'])
703
self.compute.agent_update(self.context, instance['uuid'],
704
'http://fake/url/', 'fakehash')
705
self.assertTrue(called['agent_update'])
706
self.compute.terminate_instance(self.context, instance['uuid'])
708
def test_snapshot(self):
709
"""Ensure instance can be snapshotted"""
710
instance = self._create_fake_instance()
711
instance_uuid = instance['uuid']
712
name = "myfakesnapshot"
713
self.compute.run_instance(self.context, instance_uuid)
714
self.compute.snapshot_instance(self.context, instance_uuid, name)
715
self.compute.terminate_instance(self.context, instance_uuid)
717
def test_snapshot_fails(self):
718
"""Ensure task_state is set to None if snapshot fails"""
719
def fake_snapshot(*args, **kwargs):
720
raise test.TestingException()
722
self.stubs.Set(self.compute.driver, 'snapshot', fake_snapshot)
724
instance = self._create_fake_instance()
725
self.compute.run_instance(self.context, instance['uuid'])
726
self.assertRaises(test.TestingException,
727
self.compute.snapshot_instance,
728
self.context, instance['uuid'], "failing_snapshot")
729
self._assert_state({'task_state': None})
730
self.compute.terminate_instance(self.context, instance['uuid'])
732
def _assert_state(self, state_dict):
733
"""Assert state of VM is equal to state passed as parameter"""
734
instances = db.instance_get_all(context.get_admin_context())
735
self.assertEqual(len(instances), 1)
737
if 'vm_state' in state_dict:
738
self.assertEqual(state_dict['vm_state'], instances[0]['vm_state'])
739
if 'task_state' in state_dict:
740
self.assertEqual(state_dict['task_state'],
741
instances[0]['task_state'])
742
if 'power_state' in state_dict:
743
self.assertEqual(state_dict['power_state'],
744
instances[0]['power_state'])
746
def test_console_output(self):
747
"""Make sure we can get console output from instance"""
748
instance = self._create_fake_instance()
749
self.compute.run_instance(self.context, instance['uuid'])
751
output = self.compute.get_console_output(self.context,
753
self.assertEqual(output, 'FAKE CONSOLE OUTPUT\nANOTHER\nLAST LINE')
754
self.compute.terminate_instance(self.context, instance['uuid'])
756
def test_console_output_tail(self):
757
"""Make sure we can get console output from instance"""
758
instance = self._create_fake_instance()
759
self.compute.run_instance(self.context, instance['uuid'])
761
output = self.compute.get_console_output(self.context,
764
self.assertEqual(output, 'ANOTHER\nLAST LINE')
765
self.compute.terminate_instance(self.context, instance['uuid'])
767
def test_novnc_vnc_console(self):
768
"""Make sure we can a vnc console for an instance."""
769
instance = self._create_fake_instance()
770
self.compute.run_instance(self.context, instance['uuid'])
772
console = self.compute.get_vnc_console(self.context,
775
self.assert_(console)
776
self.compute.terminate_instance(self.context, instance['uuid'])
778
def test_xvpvnc_vnc_console(self):
779
"""Make sure we can a vnc console for an instance."""
780
instance = self._create_fake_instance()
781
self.compute.run_instance(self.context, instance['uuid'])
783
console = self.compute.get_vnc_console(self.context,
786
self.assert_(console)
787
self.compute.terminate_instance(self.context, instance['uuid'])
789
def test_invalid_vnc_console_type(self):
790
"""Make sure we can a vnc console for an instance."""
791
instance = self._create_fake_instance()
792
self.compute.run_instance(self.context, instance['uuid'])
794
self.assertRaises(exception.ConsoleTypeInvalid,
795
self.compute.get_vnc_console,
799
self.compute.terminate_instance(self.context, instance['uuid'])
801
def test_diagnostics(self):
802
"""Make sure we can get diagnostics for an instance."""
803
instance = self._create_fake_instance()
804
self.compute.run_instance(self.context, instance['uuid'])
806
diagnostics = self.compute.get_diagnostics(self.context,
808
self.assertEqual(diagnostics, 'FAKE_DIAGNOSTICS')
809
self.compute.terminate_instance(self.context, instance['uuid'])
811
def test_add_fixed_ip_usage_notification(self):
812
def dummy(*args, **kwargs):
815
self.stubs.Set(nova.network.API, 'add_fixed_ip_to_instance',
817
self.stubs.Set(nova.compute.manager.ComputeManager,
818
'inject_network_info', dummy)
819
self.stubs.Set(nova.compute.manager.ComputeManager,
820
'reset_network', dummy)
822
instance = self._create_fake_instance()
823
instance_uuid = instance['uuid']
825
self.assertEquals(len(test_notifier.NOTIFICATIONS), 0)
826
self.compute.add_fixed_ip_to_instance(self.context, instance_uuid, 1)
828
self.assertEquals(len(test_notifier.NOTIFICATIONS), 2)
829
self.compute.terminate_instance(self.context, instance_uuid)
831
def test_remove_fixed_ip_usage_notification(self):
832
def dummy(*args, **kwargs):
835
self.stubs.Set(nova.network.API, 'remove_fixed_ip_from_instance',
837
self.stubs.Set(nova.compute.manager.ComputeManager,
838
'inject_network_info', dummy)
839
self.stubs.Set(nova.compute.manager.ComputeManager,
840
'reset_network', dummy)
842
instance = self._create_fake_instance()
843
instance_uuid = instance['uuid']
845
self.assertEquals(len(test_notifier.NOTIFICATIONS), 0)
846
self.compute.remove_fixed_ip_from_instance(self.context,
850
self.assertEquals(len(test_notifier.NOTIFICATIONS), 2)
851
self.compute.terminate_instance(self.context, instance_uuid)
853
def test_run_instance_usage_notification(self):
854
"""Ensure run instance generates apropriate usage notification"""
855
inst_ref = self._create_fake_instance()
856
instance_uuid = inst_ref['uuid']
857
self.compute.run_instance(self.context, instance_uuid)
858
self.assertEquals(len(test_notifier.NOTIFICATIONS), 2)
859
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
860
msg = test_notifier.NOTIFICATIONS[0]
861
self.assertEquals(msg['event_type'], 'compute.instance.create.start')
862
# The last event is the one with the sugar in it.
863
msg = test_notifier.NOTIFICATIONS[1]
864
self.assertEquals(msg['priority'], 'INFO')
865
self.assertEquals(msg['event_type'], 'compute.instance.create.end')
866
payload = msg['payload']
867
self.assertEquals(payload['tenant_id'], self.project_id)
868
self.assertEquals(payload['user_id'], self.user_id)
869
self.assertEquals(payload['instance_id'], inst_ref.uuid)
870
self.assertEquals(payload['instance_type'], 'm1.tiny')
871
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
872
self.assertEquals(str(payload['instance_type_id']), str(type_id))
873
self.assertEquals(payload['state'], 'active')
874
self.assertTrue('display_name' in payload)
875
self.assertTrue('created_at' in payload)
876
self.assertTrue('launched_at' in payload)
877
self.assertTrue(payload['launched_at'])
878
image_ref_url = utils.generate_image_url(FAKE_IMAGE_REF)
879
self.assertEquals(payload['image_ref_url'], image_ref_url)
880
self.compute.terminate_instance(self.context, instance_uuid)
882
def test_terminate_usage_notification(self):
883
"""Ensure terminate_instance generates apropriate usage notification"""
884
old_time = datetime.datetime(2012, 4, 1)
885
cur_time = datetime.datetime(2012, 12, 21, 12, 21)
886
utils.set_time_override(old_time)
888
inst_ref = self._create_fake_instance()
889
self.compute.run_instance(self.context, inst_ref['uuid'])
890
test_notifier.NOTIFICATIONS = []
891
utils.set_time_override(cur_time)
892
self.compute.terminate_instance(self.context, inst_ref['uuid'])
894
self.assertEquals(len(test_notifier.NOTIFICATIONS), 4)
896
msg = test_notifier.NOTIFICATIONS[0]
897
self.assertEquals(msg['priority'], 'INFO')
898
self.assertEquals(msg['event_type'], 'compute.instance.delete.start')
899
msg1 = test_notifier.NOTIFICATIONS[1]
900
self.assertEquals(msg1['event_type'],
901
'compute.instance.shutdown.start')
902
msg1 = test_notifier.NOTIFICATIONS[2]
903
self.assertEquals(msg1['event_type'], 'compute.instance.shutdown.end')
904
msg1 = test_notifier.NOTIFICATIONS[3]
905
self.assertEquals(msg1['event_type'], 'compute.instance.delete.end')
906
payload = msg1['payload']
907
self.assertEquals(payload['tenant_id'], self.project_id)
908
self.assertEquals(payload['user_id'], self.user_id)
909
self.assertEquals(payload['instance_id'], inst_ref.uuid)
910
self.assertEquals(payload['instance_type'], 'm1.tiny')
911
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
912
self.assertEquals(str(payload['instance_type_id']), str(type_id))
913
self.assertTrue('display_name' in payload)
914
self.assertTrue('created_at' in payload)
915
self.assertTrue('launched_at' in payload)
916
self.assertTrue('deleted_at' in payload)
917
self.assertEqual(payload['deleted_at'], str(cur_time))
918
image_ref_url = utils.generate_image_url(FAKE_IMAGE_REF)
919
self.assertEquals(payload['image_ref_url'], image_ref_url)
921
def test_run_instance_existing(self):
922
"""Ensure failure when running an instance that already exists"""
923
instance = self._create_fake_instance()
924
self.compute.run_instance(self.context, instance['uuid'])
925
self.assertRaises(exception.Invalid,
926
self.compute.run_instance,
929
self.compute.terminate_instance(self.context, instance['uuid'])
931
def test_instance_set_to_error_on_uncaught_exception(self):
932
"""Test that instance is set to error state when exception is raised"""
933
instance = self._create_fake_instance()
934
instance_uuid = instance['uuid']
936
self.mox.StubOutWithMock(self.compute.network_api,
937
"allocate_for_instance")
938
self.compute.network_api.allocate_for_instance(
941
requested_networks=None,
942
vpn=False).AndRaise(rpc_common.RemoteError())
944
self.flags(stub_network=False)
948
self.assertRaises(rpc_common.RemoteError,
949
self.compute.run_instance,
953
instance = db.instance_get_by_uuid(context.get_admin_context(),
955
self.assertEqual(vm_states.ERROR, instance['vm_state'])
957
self.compute.terminate_instance(self.context, instance['uuid'])
959
def test_instance_termination_exception_sets_error(self):
960
"""Test that we handle InstanceTerminationFailure
961
which is propagated up from the underlying driver.
963
instance = self._create_fake_instance()
965
def fake_delete_instance(context, instance):
966
raise exception.InstanceTerminationFailure(reason='')
968
self.stubs.Set(self.compute, '_delete_instance',
969
fake_delete_instance)
971
self.compute.terminate_instance(self.context, instance['uuid'])
972
instance = db.instance_get_by_uuid(self.context, instance['uuid'])
973
self.assertEqual(instance['vm_state'], vm_states.ERROR)
975
def test_network_is_deallocated_on_spawn_failure(self):
976
"""When a spawn fails the network must be deallocated"""
977
instance = self._create_fake_instance()
979
self.mox.StubOutWithMock(self.compute, "_setup_block_device_mapping")
980
self.compute._setup_block_device_mapping(
982
mox.IgnoreArg()).AndRaise(rpc.common.RemoteError('', '', ''))
986
self.assertRaises(rpc.common.RemoteError,
987
self.compute.run_instance,
991
self.compute.terminate_instance(self.context, instance['uuid'])
993
def test_get_lock(self):
994
instance = self._create_fake_instance()
995
instance_uuid = instance['uuid']
996
self.assertFalse(self.compute.get_lock(self.context, instance_uuid))
997
db.instance_update(self.context, instance_uuid, {'locked': True})
998
self.assertTrue(self.compute.get_lock(self.context, instance_uuid))
1000
def test_lock(self):
1001
"""ensure locked instance cannot be changed"""
1002
instance = self._create_fake_instance()
1003
instance_uuid = instance['uuid']
1004
self.compute.run_instance(self.context, instance_uuid)
1006
non_admin_context = context.RequestContext(None,
1010
# decorator should return False (fail) with locked nonadmin context
1011
self.compute.lock_instance(self.context, instance_uuid)
1012
ret_val = self.compute.reboot_instance(non_admin_context,
1014
self.assertEqual(ret_val, False)
1016
# decorator should return None (success) with unlocked nonadmin context
1017
self.compute.unlock_instance(self.context, instance_uuid)
1018
ret_val = self.compute.reboot_instance(non_admin_context,
1020
self.assertEqual(ret_val, None)
1022
self.compute.terminate_instance(self.context, instance_uuid)
1024
def test_finish_resize(self):
1025
"""Contrived test to ensure finish_resize doesn't raise anything"""
1027
def fake(*args, **kwargs):
1030
self.stubs.Set(self.compute.driver, 'finish_migration', fake)
1032
context = self.context.elevated()
1033
instance = self._create_fake_instance()
1034
self.compute.prep_resize(context, instance['uuid'], 1, {},
1035
filter_properties={})
1036
migration_ref = db.migration_get_by_instance_and_status(context,
1037
instance['uuid'], 'pre-migrating')
1038
self.compute.finish_resize(context, instance['uuid'],
1039
int(migration_ref['id']), {}, {})
1040
self.compute.terminate_instance(self.context, instance['uuid'])
1042
def test_finish_resize_handles_error(self):
1043
"""Make sure we don't leave the instance in RESIZE on error"""
1045
def throw_up(*args, **kwargs):
1046
raise test.TestingException()
1048
def fake(*args, **kwargs):
1051
self.stubs.Set(self.compute.driver, 'finish_migration', throw_up)
1053
context = self.context.elevated()
1054
instance = self._create_fake_instance()
1055
self.compute.prep_resize(context, instance['uuid'], 1, {},
1056
filter_properties={})
1057
migration_ref = db.migration_get_by_instance_and_status(context,
1058
instance['uuid'], 'pre-migrating')
1060
self.assertRaises(test.TestingException, self.compute.finish_resize,
1061
context, instance['uuid'],
1062
int(migration_ref['id']), {}, {})
1064
instance = db.instance_get_by_uuid(context, instance['uuid'])
1065
self.assertEqual(instance['vm_state'], vm_states.ERROR)
1066
self.compute.terminate_instance(self.context, instance['uuid'])
1068
def test_rebuild_instance_notification(self):
1069
"""Ensure notifications on instance migrate/resize"""
1070
old_time = datetime.datetime(2012, 4, 1)
1071
cur_time = datetime.datetime(2012, 12, 21, 12, 21)
1072
utils.set_time_override(old_time)
1073
inst_ref = self._create_fake_instance()
1074
instance_uuid = inst_ref['uuid']
1075
self.compute.run_instance(self.context, instance_uuid)
1076
utils.set_time_override(cur_time)
1078
test_notifier.NOTIFICATIONS = []
1079
instance = db.instance_get_by_uuid(self.context, instance_uuid)
1081
image_ref = instance["image_ref"]
1082
new_image_ref = image_ref + '-new_image_ref'
1083
db.instance_update(self.context, instance_uuid,
1084
{'image_ref': new_image_ref})
1086
password = "new_password"
1088
self.compute._rebuild_instance(self.context, instance_uuid,
1089
image_ref, new_image_ref, dict(new_pass=password))
1091
instance = db.instance_get_by_uuid(self.context, instance_uuid)
1093
image_ref_url = utils.generate_image_url(image_ref)
1094
new_image_ref_url = utils.generate_image_url(new_image_ref)
1096
self.assertEquals(len(test_notifier.NOTIFICATIONS), 3)
1097
msg = test_notifier.NOTIFICATIONS[0]
1098
self.assertEquals(msg['event_type'],
1099
'compute.instance.exists')
1100
self.assertEquals(msg['payload']['image_ref_url'], image_ref_url)
1101
msg = test_notifier.NOTIFICATIONS[1]
1102
self.assertEquals(msg['event_type'],
1103
'compute.instance.rebuild.start')
1104
self.assertEquals(msg['payload']['image_ref_url'], new_image_ref_url)
1105
msg = test_notifier.NOTIFICATIONS[2]
1106
self.assertEquals(msg['event_type'],
1107
'compute.instance.rebuild.end')
1108
self.assertEquals(msg['priority'], 'INFO')
1109
payload = msg['payload']
1110
self.assertEquals(payload['tenant_id'], self.project_id)
1111
self.assertEquals(payload['user_id'], self.user_id)
1112
self.assertEquals(payload['instance_id'], instance_uuid)
1113
self.assertEquals(payload['instance_type'], 'm1.tiny')
1114
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
1115
self.assertEquals(str(payload['instance_type_id']), str(type_id))
1116
self.assertTrue('display_name' in payload)
1117
self.assertTrue('created_at' in payload)
1118
self.assertTrue('launched_at' in payload)
1119
self.assertEqual(payload['launched_at'], str(cur_time))
1120
self.assertEquals(payload['image_ref_url'], new_image_ref_url)
1121
self.compute.terminate_instance(self.context, instance_uuid)
1123
def test_finish_resize_instance_notification(self):
1124
"""Ensure notifications on instance migrate/resize"""
1125
old_time = datetime.datetime(2012, 4, 1)
1126
cur_time = datetime.datetime(2012, 12, 21, 12, 21)
1127
utils.set_time_override(old_time)
1128
instance = self._create_fake_instance()
1129
instance_uuid = instance['uuid']
1130
context = self.context.elevated()
1131
old_type_id = instance_types.get_instance_type_by_name(
1133
new_type_id = instance_types.get_instance_type_by_name(
1135
self.compute.run_instance(self.context, instance_uuid)
1137
db.instance_update(self.context, instance_uuid, {'host': 'foo'})
1138
self.compute.prep_resize(context, instance_uuid, new_type_id, {},
1139
filter_properties={})
1140
migration_ref = db.migration_get_by_instance_and_status(context,
1143
self.compute.resize_instance(context, instance_uuid,
1144
migration_ref['id'], {})
1145
utils.set_time_override(cur_time)
1146
test_notifier.NOTIFICATIONS = []
1148
self.compute.finish_resize(context, instance['uuid'],
1149
int(migration_ref['id']), {}, {})
1151
self.assertEquals(len(test_notifier.NOTIFICATIONS), 2)
1152
msg = test_notifier.NOTIFICATIONS[0]
1153
self.assertEquals(msg['event_type'],
1154
'compute.instance.finish_resize.start')
1155
msg = test_notifier.NOTIFICATIONS[1]
1156
self.assertEquals(msg['event_type'],
1157
'compute.instance.finish_resize.end')
1158
self.assertEquals(msg['priority'], 'INFO')
1159
payload = msg['payload']
1160
self.assertEquals(payload['tenant_id'], self.project_id)
1161
self.assertEquals(payload['user_id'], self.user_id)
1162
self.assertEquals(payload['instance_id'], instance_uuid)
1163
self.assertEquals(payload['instance_type'], 'm1.small')
1164
self.assertEquals(str(payload['instance_type_id']), str(new_type_id))
1165
self.assertTrue('display_name' in payload)
1166
self.assertTrue('created_at' in payload)
1167
self.assertTrue('launched_at' in payload)
1168
self.assertEqual(payload['launched_at'], str(cur_time))
1169
image_ref_url = utils.generate_image_url(FAKE_IMAGE_REF)
1170
self.assertEquals(payload['image_ref_url'], image_ref_url)
1171
self.compute.terminate_instance(context, instance_uuid)
1173
def test_resize_instance_notification(self):
1174
"""Ensure notifications on instance migrate/resize"""
1175
old_time = datetime.datetime(2012, 4, 1)
1176
cur_time = datetime.datetime(2012, 12, 21, 12, 21)
1177
utils.set_time_override(old_time)
1178
instance = self._create_fake_instance()
1179
instance_uuid = instance['uuid']
1180
context = self.context.elevated()
1182
self.compute.run_instance(self.context, instance_uuid)
1183
utils.set_time_override(cur_time)
1184
test_notifier.NOTIFICATIONS = []
1186
db.instance_update(self.context, instance_uuid, {'host': 'foo'})
1187
self.compute.prep_resize(context, instance_uuid, 1, {},
1188
filter_properties={})
1189
db.migration_get_by_instance_and_status(context,
1193
self.assertEquals(len(test_notifier.NOTIFICATIONS), 3)
1194
msg = test_notifier.NOTIFICATIONS[0]
1195
self.assertEquals(msg['event_type'],
1196
'compute.instance.exists')
1197
msg = test_notifier.NOTIFICATIONS[1]
1198
self.assertEquals(msg['event_type'],
1199
'compute.instance.resize.prep.start')
1200
msg = test_notifier.NOTIFICATIONS[2]
1201
self.assertEquals(msg['event_type'],
1202
'compute.instance.resize.prep.end')
1203
self.assertEquals(msg['priority'], 'INFO')
1204
payload = msg['payload']
1205
self.assertEquals(payload['tenant_id'], self.project_id)
1206
self.assertEquals(payload['user_id'], self.user_id)
1207
self.assertEquals(payload['instance_id'], instance_uuid)
1208
self.assertEquals(payload['instance_type'], 'm1.tiny')
1209
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
1210
self.assertEquals(str(payload['instance_type_id']), str(type_id))
1211
self.assertTrue('display_name' in payload)
1212
self.assertTrue('created_at' in payload)
1213
self.assertTrue('launched_at' in payload)
1214
image_ref_url = utils.generate_image_url(FAKE_IMAGE_REF)
1215
self.assertEquals(payload['image_ref_url'], image_ref_url)
1216
self.compute.terminate_instance(context, instance_uuid)
1218
def test_prep_resize_instance_migration_error(self):
1219
"""Ensure prep_resize raise a migration error"""
1220
self.flags(host="foo", allow_resize_to_same_host=False)
1222
instance = self._create_fake_instance()
1223
instance_uuid = instance['uuid']
1224
context = self.context.elevated()
1226
self.compute.run_instance(self.context, instance_uuid)
1227
db.instance_update(self.context, instance_uuid, {'host': 'foo'})
1229
self.assertRaises(exception.MigrationError, self.compute.prep_resize,
1230
context, instance_uuid, 1, {})
1231
self.compute.terminate_instance(context, instance_uuid)
1233
def test_resize_instance_driver_error(self):
1234
"""Ensure instance status set to Error on resize error"""
1236
def throw_up(*args, **kwargs):
1237
raise test.TestingException()
1239
self.stubs.Set(self.compute.driver, 'migrate_disk_and_power_off',
1242
instance = self._create_fake_instance()
1243
instance_uuid = instance['uuid']
1244
context = self.context.elevated()
1246
self.compute.run_instance(self.context, instance_uuid)
1247
db.instance_update(self.context, instance_uuid, {'host': 'foo'})
1248
self.compute.prep_resize(context, instance_uuid, 1, {},
1249
filter_properties={})
1250
migration_ref = db.migration_get_by_instance_and_status(context,
1251
instance_uuid, 'pre-migrating')
1254
self.assertRaises(test.TestingException, self.compute.resize_instance,
1255
context, instance_uuid, migration_ref['id'], {})
1256
instance = db.instance_get_by_uuid(context, instance_uuid)
1257
self.assertEqual(instance['vm_state'], vm_states.ERROR)
1259
self.compute.terminate_instance(context, instance_uuid)
1261
def test_resize_instance(self):
1262
"""Ensure instance can be migrated/resized"""
1263
instance = self._create_fake_instance()
1264
instance_uuid = instance['uuid']
1265
context = self.context.elevated()
1267
self.compute.run_instance(self.context, instance_uuid)
1268
db.instance_update(self.context, instance_uuid,
1270
self.compute.prep_resize(context, instance_uuid, 1, {},
1271
filter_properties={})
1272
migration_ref = db.migration_get_by_instance_and_status(context,
1273
instance_uuid, 'pre-migrating')
1274
self.compute.resize_instance(context, instance_uuid,
1275
migration_ref['id'], {})
1276
self.compute.terminate_instance(context, instance_uuid)
1278
def test_finish_revert_resize(self):
1279
"""Ensure that the flavor is reverted to the original on revert"""
1280
def fake(*args, **kwargs):
1283
self.stubs.Set(self.compute.driver, 'finish_migration', fake)
1284
self.stubs.Set(self.compute.driver, 'finish_revert_migration', fake)
1286
context = self.context.elevated()
1287
instance = self._create_fake_instance()
1288
instance_uuid = instance['uuid']
1290
self.compute.run_instance(self.context, instance_uuid)
1292
# Confirm the instance size before the resize starts
1293
inst_ref = db.instance_get_by_uuid(context, instance_uuid)
1294
instance_type_ref = db.instance_type_get(context,
1295
inst_ref['instance_type_id'])
1296
self.assertEqual(instance_type_ref['flavorid'], '1')
1298
db.instance_update(self.context, instance_uuid, {'host': 'foo'})
1300
new_instance_type_ref = db.instance_type_get_by_flavor_id(context, 3)
1301
self.compute.prep_resize(context, inst_ref['uuid'],
1302
new_instance_type_ref['id'], {},
1303
filter_properties={})
1305
migration_ref = db.migration_get_by_instance_and_status(context,
1306
inst_ref['uuid'], 'pre-migrating')
1308
self.compute.resize_instance(context, inst_ref['uuid'],
1309
migration_ref['id'], {})
1310
self.compute.finish_resize(context, inst_ref['uuid'],
1311
int(migration_ref['id']), {}, {})
1313
# Prove that the instance size is now the new size
1314
inst_ref = db.instance_get_by_uuid(context, instance_uuid)
1315
instance_type_ref = db.instance_type_get(context,
1316
inst_ref['instance_type_id'])
1317
self.assertEqual(instance_type_ref['flavorid'], '3')
1319
# Finally, revert and confirm the old flavor has been applied
1320
self.compute.revert_resize(context, inst_ref['uuid'],
1321
migration_ref['id'])
1322
self.compute.finish_revert_resize(context, inst_ref['uuid'],
1323
migration_ref['id'])
1325
instance = db.instance_get_by_uuid(context, instance['uuid'])
1326
self.assertEqual(instance['vm_state'], vm_states.ACTIVE)
1327
self.assertEqual(instance['task_state'], None)
1329
inst_ref = db.instance_get_by_uuid(context, instance_uuid)
1330
instance_type_ref = db.instance_type_get(context,
1331
inst_ref['instance_type_id'])
1332
self.assertEqual(instance_type_ref['flavorid'], '1')
1333
self.assertEqual(inst_ref['host'], migration_ref['source_compute'])
1335
self.compute.terminate_instance(context, inst_ref['uuid'])
1337
def test_get_by_flavor_id(self):
1338
type = instance_types.get_instance_type_by_flavor_id(1)
1339
self.assertEqual(type['name'], 'm1.tiny')
1341
def test_resize_same_source_fails(self):
1342
"""Ensure instance fails to migrate when source and destination are
1344
instance = self._create_fake_instance()
1345
self.compute.run_instance(self.context, instance['uuid'])
1346
instance = db.instance_get_by_uuid(self.context, instance['uuid'])
1347
self.assertRaises(exception.MigrationError, self.compute.prep_resize,
1348
self.context, instance['uuid'], 1, {})
1349
self.compute.terminate_instance(self.context, instance['uuid'])
1351
def test_resize_instance_handles_migration_error(self):
1352
"""Ensure vm_state is ERROR when error occurs"""
1353
def raise_migration_failure(*args):
1354
raise test.TestingException()
1355
self.stubs.Set(self.compute.driver,
1356
'migrate_disk_and_power_off',
1357
raise_migration_failure)
1359
inst_ref = self._create_fake_instance()
1360
context = self.context.elevated()
1362
self.compute.run_instance(self.context, inst_ref['uuid'])
1363
db.instance_update(self.context, inst_ref['uuid'], {'host': 'foo'})
1364
self.compute.prep_resize(context, inst_ref['uuid'], 1, {},
1365
filter_properties={})
1366
migration_ref = db.migration_get_by_instance_and_status(context,
1367
inst_ref['uuid'], 'pre-migrating')
1368
self.assertRaises(test.TestingException, self.compute.resize_instance,
1369
context, inst_ref['uuid'], migration_ref['id'], {})
1370
inst_ref = db.instance_get_by_uuid(context, inst_ref['uuid'])
1371
self.assertEqual(inst_ref['vm_state'], vm_states.ERROR)
1372
self.compute.terminate_instance(context, inst_ref['uuid'])
1374
def test_pre_live_migration_instance_has_no_fixed_ip(self):
1375
"""Confirm raising exception if instance doesn't have fixed_ip."""
1376
# creating instance testdata
1377
inst_ref = self._create_fake_instance({'host': 'dummy'})
1378
c = context.get_admin_context()
1381
self.stubs.Set(time, 'sleep', lambda t: None)
1382
self.assertRaises(exception.FixedIpNotFoundForInstance,
1383
self.compute.pre_live_migration,
1386
db.instance_destroy(c, inst_ref['id'])
1388
def test_pre_live_migration_works_correctly(self):
1389
"""Confirm setup_compute_volume is called when volume is mounted."""
1390
def stupid(*args, **kwargs):
1391
return fake_network.fake_get_instance_nw_info(self.stubs,
1393
self.stubs.Set(nova.compute.manager.ComputeManager,
1394
'_get_instance_nw_info', stupid)
1395
# creating instance testdata
1396
inst_ref = self._create_fake_instance({'host': 'dummy'})
1397
c = context.get_admin_context()
1400
self.mox.StubOutWithMock(self.compute.driver, 'pre_live_migration')
1401
self.compute.driver.pre_live_migration({'block_device_mapping': []})
1402
nw_info = fake_network.fake_get_instance_nw_info(self.stubs)
1403
self.mox.StubOutWithMock(self.compute.driver, 'plug_vifs')
1404
self.compute.driver.plug_vifs(mox.IsA(inst_ref), nw_info)
1405
self.mox.StubOutWithMock(self.compute.driver,
1406
'ensure_filtering_rules_for_instance')
1407
self.compute.driver.ensure_filtering_rules_for_instance(
1408
mox.IsA(inst_ref), nw_info)
1411
self.mox.ReplayAll()
1412
ret = self.compute.pre_live_migration(c, inst_ref['id'])
1413
self.assertEqual(ret, None)
1416
db.instance_destroy(c, inst_ref['id'])
1418
def test_live_migration_dest_raises_exception(self):
1419
"""Confirm exception when pre_live_migration fails."""
1420
# creating instance testdata
1421
inst_ref = self._create_fake_instance({'host': 'dummy'})
1423
c = context.get_admin_context()
1424
topic = db.queue_get_for(c, FLAGS.compute_topic, inst_ref['host'])
1426
# creating volume testdata
1427
volume_id = db.volume_create(c, {'size': 1})['id']
1428
values = {'instance_uuid': inst_ref['uuid'], 'device_name': '/dev/vdc',
1429
'delete_on_termination': False, 'volume_id': volume_id}
1430
db.block_device_mapping_create(c, values)
1433
self.mox.StubOutWithMock(rpc, 'call')
1434
rpc.call(c, FLAGS.volume_topic,
1435
{"method": "check_for_export",
1436
"args": {'instance_id': inst_ref['id']}})
1438
self.mox.StubOutWithMock(self.compute.driver, 'get_instance_disk_info')
1439
self.compute.driver.get_instance_disk_info(inst_ref.name)
1442
{"method": "pre_live_migration",
1443
"args": {'instance_id': inst_ref['id'],
1444
'block_migration': True,
1446
}).AndRaise(rpc.common.RemoteError('', '', ''))
1448
# mocks for rollback
1449
rpc.call(c, 'network', {'method': 'setup_networks_on_host',
1450
'args': {'instance_id': inst_ref['id'],
1451
'host': self.compute.host,
1452
'teardown': False}})
1453
rpc.call(c, topic, {"method": "remove_volume_connection",
1454
"args": {'instance_id': inst_ref['id'],
1455
'volume_id': volume_id}})
1456
rpc.cast(c, topic, {"method": "rollback_live_migration_at_destination",
1457
"args": {'instance_id': inst_ref['id']}})
1460
self.mox.ReplayAll()
1461
self.assertRaises(rpc_common.RemoteError,
1462
self.compute.live_migration,
1463
c, inst_ref['id'], inst_ref['host'], True)
1466
for bdms in db.block_device_mapping_get_all_by_instance(
1467
c, inst_ref['uuid']):
1468
db.block_device_mapping_destroy(c, bdms['id'])
1469
db.volume_destroy(c, volume_id)
1470
db.instance_destroy(c, inst_ref['id'])
1472
def test_live_migration_works_correctly(self):
1473
"""Confirm live_migration() works as expected correctly."""
1474
# creating instance testdata
1475
instance = self._create_fake_instance({'host': 'dummy'})
1476
instance_id = instance['id']
1477
c = context.get_admin_context()
1478
inst_ref = db.instance_get(c, instance_id)
1479
topic = db.queue_get_for(c, FLAGS.compute_topic, inst_ref['host'])
1482
self.mox.StubOutWithMock(rpc, 'call')
1483
rpc.call(c, topic, {"method": "pre_live_migration",
1484
"args": {'instance_id': instance_id,
1485
'block_migration': False,
1489
self.mox.ReplayAll()
1490
ret = self.compute.live_migration(c, inst_ref['id'], inst_ref['host'])
1491
self.assertEqual(ret, None)
1494
db.instance_destroy(c, instance_id)
1496
def test_post_live_migration_working_correctly(self):
1497
"""Confirm post_live_migration() works as expected correctly."""
1499
flo_addr = '1.2.1.2'
1502
c = context.get_admin_context()
1503
instance = self._create_fake_instance({
1504
'state_description': 'migrating',
1505
'state': power_state.PAUSED})
1506
instance_id = instance['id']
1507
i_ref = db.instance_get(c, instance_id)
1508
db.instance_update(c, i_ref['id'], {'vm_state': vm_states.MIGRATING,
1509
'power_state': power_state.PAUSED})
1510
v_ref = db.volume_create(c, {'size': 1, 'instance_id': instance_id})
1511
fix_addr = db.fixed_ip_create(c, {'address': '1.1.1.1',
1512
'instance_id': instance_id})
1513
fix_ref = db.fixed_ip_get_by_address(c, fix_addr)
1514
db.floating_ip_create(c, {'address': flo_addr,
1515
'fixed_ip_id': fix_ref['id']})
1518
self.mox.StubOutWithMock(self.compute.driver, 'unfilter_instance')
1519
self.compute.driver.unfilter_instance(i_ref, [])
1520
self.mox.StubOutWithMock(rpc, 'call')
1521
rpc.call(c, db.queue_get_for(c, FLAGS.compute_topic, dest),
1522
{"method": "post_live_migration_at_destination",
1523
"args": {'instance_id': i_ref['id'], 'block_migration': False}})
1524
self.mox.StubOutWithMock(self.compute.driver, 'unplug_vifs')
1525
self.compute.driver.unplug_vifs(i_ref, [])
1526
rpc.call(c, 'network', {'method': 'setup_networks_on_host',
1527
'args': {'instance_id': instance_id,
1528
'host': self.compute.host,
1532
self.mox.ReplayAll()
1533
self.compute.post_live_migration(c, i_ref, dest)
1535
# make sure floating ips are rewritten to destinatioin hostname.
1536
flo_refs = db.floating_ip_get_all_by_host(c, dest)
1537
self.assertTrue(flo_refs)
1538
self.assertEqual(flo_refs[0]['address'], flo_addr)
1541
db.instance_destroy(c, instance_id)
1542
db.volume_destroy(c, v_ref['id'])
1543
db.floating_ip_destroy(c, flo_addr)
1545
def test_run_kill_vm(self):
1546
"""Detect when a vm is terminated behind the scenes"""
1547
self.stubs.Set(compute_manager.ComputeManager,
1548
'_report_driver_status', nop_report_driver_status)
1550
instance = self._create_fake_instance()
1552
self.compute.run_instance(self.context, instance['uuid'])
1554
instances = db.instance_get_all(context.get_admin_context())
1555
LOG.info(_("Running instances: %s"), instances)
1556
self.assertEqual(len(instances), 1)
1558
instance_name = instances[0].name
1559
self.compute.driver.test_remove_vm(instance_name)
1561
# Force the compute manager to do its periodic poll
1562
ctxt = context.get_admin_context()
1563
self.compute._sync_power_states(ctxt)
1565
instances = db.instance_get_all(ctxt)
1566
LOG.info(_("After force-killing instances: %s"), instances)
1567
self.assertEqual(len(instances), 1)
1568
self.assertEqual(power_state.NOSTATE, instances[0]['power_state'])
1570
def test_add_instance_fault(self):
1572
instance_uuid = str(utils.gen_uuid())
1574
def fake_db_fault_create(ctxt, values):
1575
self.assertTrue(values['details'].startswith('test'))
1576
self.assertTrue('raise NotImplementedError' in values['details'])
1577
del values['details']
1581
'message': 'NotImplementedError',
1582
'instance_uuid': instance_uuid,
1584
self.assertEquals(expected, values)
1587
raise NotImplementedError('test')
1589
exc_info = sys.exc_info()
1591
self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
1593
ctxt = context.get_admin_context()
1594
self.compute.add_instance_fault_from_exc(ctxt, instance_uuid,
1595
NotImplementedError('test'),
1598
def test_add_instance_fault_user_error(self):
1600
instance_uuid = str(utils.gen_uuid())
1602
def fake_db_fault_create(ctxt, values):
1606
'message': 'Invalid',
1607
'details': 'fake details',
1608
'instance_uuid': instance_uuid,
1610
self.assertEquals(expected, values)
1612
user_exc = exception.Invalid('fake details', code=400)
1617
exc_info = sys.exc_info()
1619
self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
1621
ctxt = context.get_admin_context()
1622
self.compute.add_instance_fault_from_exc(ctxt, instance_uuid,
1625
def test_add_instance_fault_no_exc_info(self):
1626
instance_uuid = str(utils.gen_uuid())
1628
def fake_db_fault_create(ctxt, values):
1631
'message': 'NotImplementedError',
1633
'instance_uuid': instance_uuid,
1635
self.assertEquals(expected, values)
1637
self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
1639
ctxt = context.get_admin_context()
1640
self.compute.add_instance_fault_from_exc(ctxt, instance_uuid,
1641
NotImplementedError('test'))
1643
def test_get_additional_capabilities(self):
1644
self.flags(additional_compute_capabilities=['test3=xyzzy',
1647
caps = compute_manager._get_additional_capabilities()
1648
all_caps = dict(test3='xyzzy',
1651
for c, val in all_caps.items():
1652
self.assertTrue(c in caps, c)
1653
self.assertEquals(val, caps[c])
1655
def test_report_driver_status(self):
1656
test_caps = dict(test1=1024, test2='foo', nothertest='bar')
1657
self.flags(additional_compute_capabilities=['test3=xyzzy',
1660
self.mox.StubOutWithMock(self.compute.driver, 'get_host_stats')
1661
self.compute.driver.get_host_stats(refresh=True).AndReturn(test_caps)
1662
self.compute._last_host_check = 0
1663
self.mox.ReplayAll()
1665
self.compute._report_driver_status(context.get_admin_context())
1666
caps = self.compute.last_capabilities
1667
all_caps = dict(test1=1024, test2='foo', test3='xyzzy',
1668
test4=True, nothertest='bar')
1669
for c, val in all_caps.items():
1670
self.assertTrue(c in caps, c)
1671
self.assertEquals(val, caps[c])
1673
def test_cleanup_running_deleted_instances(self):
1674
admin_context = context.get_admin_context()
1675
deleted_at = utils.utcnow() - datetime.timedelta(hours=1, minutes=5)
1676
instance = self._create_fake_instance({"deleted_at": deleted_at,
1679
self.compute.host = instance['host']
1681
self.mox.StubOutWithMock(self.compute.driver, 'list_instances')
1682
self.compute.driver.list_instances().AndReturn([instance['name']])
1683
FLAGS.running_deleted_instance_timeout = 3600
1684
FLAGS.running_deleted_instance_action = 'reap'
1686
self.mox.StubOutWithMock(self.compute.db, "instance_get_all_by_host")
1687
self.compute.db.instance_get_all_by_host(admin_context,
1689
).AndReturn([instance])
1691
self.mox.StubOutWithMock(self.compute, "_shutdown_instance")
1692
self.compute._shutdown_instance(admin_context, instance,
1693
'Terminating').AndReturn(None)
1695
self.mox.StubOutWithMock(self.compute, "_cleanup_volumes")
1696
self.compute._cleanup_volumes(admin_context,
1697
instance['uuid']).AndReturn(None)
1699
self.mox.ReplayAll()
1700
self.compute._cleanup_running_deleted_instances(admin_context)
1702
def test_running_deleted_instances(self):
1703
self.mox.StubOutWithMock(self.compute.driver, 'list_instances')
1704
self.compute.driver.list_instances().AndReturn(['herp', 'derp'])
1705
self.compute.host = 'host'
1707
instance1 = mox.MockAnything()
1708
instance1.name = 'herp'
1709
instance1.deleted = True
1710
instance1.deleted_at = "sometimeago"
1712
instance2 = mox.MockAnything()
1713
instance2.name = 'derp'
1714
instance2.deleted = False
1715
instance2.deleted_at = None
1717
self.mox.StubOutWithMock(utils, 'is_older_than')
1718
utils.is_older_than('sometimeago',
1719
FLAGS.running_deleted_instance_timeout).AndReturn(True)
1721
self.mox.StubOutWithMock(self.compute.db, "instance_get_all_by_host")
1722
self.compute.db.instance_get_all_by_host('context',
1726
self.mox.ReplayAll()
1727
val = self.compute._running_deleted_instances('context')
1728
self.assertEqual(val, [instance1])
1730
def test_heal_instance_info_cache(self):
1731
# Update on every call for the test
1732
self.flags(heal_instance_info_cache_interval=-1)
1733
ctxt = context.get_admin_context()
1738
uuid = 'fake-uuid-%s' % x
1739
instance_map[uuid] = {'uuid': uuid, 'host': FLAGS.host}
1740
instances.append(instance_map[uuid])
1742
call_info = {'get_all_by_host': 0, 'get_by_uuid': 0,
1743
'get_nw_info': 0, 'expected_instance': None}
1745
def fake_instance_get_all_by_host(context, host):
1746
call_info['get_all_by_host'] += 1
1749
def fake_instance_get_by_uuid(context, instance_uuid):
1750
if instance_uuid not in instance_map:
1751
raise exception.InstanceNotFound
1752
call_info['get_by_uuid'] += 1
1753
return instance_map[instance_uuid]
1755
# NOTE(comstud): Override the stub in setUp()
1756
def fake_get_instance_nw_info(context, instance):
1757
# Note that this exception gets caught in compute/manager
1758
# and is ignored. However, the below increment of
1759
# 'get_nw_info' won't happen, and you'll get an assert
1760
# failure checking it below.
1761
self.assertEqual(instance, call_info['expected_instance'])
1762
call_info['get_nw_info'] += 1
1764
self.stubs.Set(db, 'instance_get_all_by_host',
1765
fake_instance_get_all_by_host)
1766
self.stubs.Set(db, 'instance_get_by_uuid',
1767
fake_instance_get_by_uuid)
1768
self.stubs.Set(self.compute.network_api, 'get_instance_nw_info',
1769
fake_get_instance_nw_info)
1771
call_info['expected_instance'] = instances[0]
1772
self.compute._heal_instance_info_cache(ctxt)
1773
self.assertEqual(call_info['get_all_by_host'], 1)
1774
self.assertEqual(call_info['get_by_uuid'], 0)
1775
self.assertEqual(call_info['get_nw_info'], 1)
1777
call_info['expected_instance'] = instances[1]
1778
self.compute._heal_instance_info_cache(ctxt)
1779
self.assertEqual(call_info['get_all_by_host'], 1)
1780
self.assertEqual(call_info['get_by_uuid'], 1)
1781
self.assertEqual(call_info['get_nw_info'], 2)
1783
# Make an instance switch hosts
1784
instances[2]['host'] = 'not-me'
1785
# Make an instance disappear
1786
instance_map.pop(instances[3]['uuid'])
1787
# '2' and '3' should be skipped..
1788
call_info['expected_instance'] = instances[4]
1789
self.compute._heal_instance_info_cache(ctxt)
1790
self.assertEqual(call_info['get_all_by_host'], 1)
1791
# Incremented for '2' and '4'.. '3' caused a raise above.
1792
self.assertEqual(call_info['get_by_uuid'], 3)
1793
self.assertEqual(call_info['get_nw_info'], 3)
1794
# Should be no more left.
1795
self.assertEqual(len(self.compute._instance_uuids_to_heal), 0)
1797
# This should cause a DB query now so we get first instance
1799
call_info['expected_instance'] = instances[0]
1800
self.compute._heal_instance_info_cache(ctxt)
1801
self.assertEqual(call_info['get_all_by_host'], 2)
1802
# Stays the same, beacuse the instance came from the DB
1803
self.assertEqual(call_info['get_by_uuid'], 3)
1804
self.assertEqual(call_info['get_nw_info'], 4)
1806
def test_poll_unconfirmed_resizes(self):
1807
instances = [{'uuid': 'fake_uuid1', 'vm_state': vm_states.ACTIVE,
1808
'task_state': task_states.RESIZE_VERIFY},
1809
{'uuid': 'noexist'},
1810
{'uuid': 'fake_uuid2', 'vm_state': vm_states.ERROR,
1811
'task_state': task_states.RESIZE_VERIFY},
1812
{'uuid': 'fake_uuid3', 'vm_state': vm_states.ACTIVE,
1813
'task_state': task_states.REBOOTING},
1814
{'uuid': 'fake_uuid4', 'vm_state': vm_states.ACTIVE,
1815
'task_state': task_states.RESIZE_VERIFY},
1816
{'uuid': 'fake_uuid5', 'vm_state': vm_states.ACTIVE,
1817
'task_state': task_states.RESIZE_VERIFY}]
1818
expected_migration_status = {'fake_uuid1': 'confirmed',
1820
'fake_uuid2': 'error',
1821
'fake_uuid3': 'error',
1823
'fake_uuid5': 'confirmed'}
1825
for i, instance in enumerate(instances, start=1):
1826
migrations.append({'id': i,
1827
'instance_uuid': instance['uuid'],
1830
def fake_instance_get_by_uuid(context, instance_uuid):
1831
# raise InstanceNotFound exception for uuid 'noexist'
1832
if instance_uuid == 'noexist':
1833
raise exception.InstanceNotFound(instance_id=instance_uuid)
1834
for instance in instances:
1835
if instance['uuid'] == instance_uuid:
1838
def fake_migration_get_all_unconfirmed(context, resize_confirm_window):
1841
def fake_migration_update(context, migration_id, values):
1842
for migration in migrations:
1843
if migration['id'] == migration_id and 'status' in values:
1844
migration['status'] = values['status']
1846
def fake_confirm_resize(context, instance):
1847
# raise exception for 'fake_uuid4' to check migration status
1848
# does not get set to 'error' on confirm_resize failure.
1849
if instance['uuid'] == 'fake_uuid4':
1850
raise test.TestingException
1851
for migration in migrations:
1852
if migration['instance_uuid'] == instance['uuid']:
1853
migration['status'] = 'confirmed'
1855
self.stubs.Set(db, 'instance_get_by_uuid',
1856
fake_instance_get_by_uuid)
1857
self.stubs.Set(db, 'migration_get_all_unconfirmed',
1858
fake_migration_get_all_unconfirmed)
1859
self.stubs.Set(db, 'migration_update',
1860
fake_migration_update)
1861
self.stubs.Set(self.compute.compute_api, 'confirm_resize',
1862
fake_confirm_resize)
1864
def fetch_instance_migration_status(instance_uuid):
1865
for migration in migrations:
1866
if migration['instance_uuid'] == instance_uuid:
1867
return migration['status']
1869
self.flags(resize_confirm_window=60)
1870
ctxt = context.get_admin_context()
1872
self.compute._poll_unconfirmed_resizes(ctxt)
1874
for uuid, status in expected_migration_status.iteritems():
1875
self.assertEqual(status, fetch_instance_migration_status(uuid))
1877
def test_instance_build_timeout_disabled(self):
1878
self.flags(instance_build_timeout=0)
1879
ctxt = context.get_admin_context()
1880
called = {'get_all': False, 'set_error_state': 0}
1881
created_at = utils.utcnow() + datetime.timedelta(seconds=-60)
1883
def fake_instance_get_all_by_filters(*args, **kwargs):
1884
called['get_all'] = True
1887
self.stubs.Set(db, 'instance_get_all_by_filters',
1888
fake_instance_get_all_by_filters)
1890
def fake_set_instance_error_state(_ctxt, instance_uuid, **kwargs):
1891
called['set_error_state'] += 1
1893
self.stubs.Set(self.compute, '_set_instance_error_state',
1894
fake_set_instance_error_state)
1899
uuid = 'fake-uuid-%s' % x
1900
instance_map[uuid] = {'uuid': uuid, 'host': FLAGS.host,
1901
'vm_state': vm_states.BUILDING,
1902
'created_at': created_at}
1903
instances.append(instance_map[uuid])
1905
self.compute._check_instance_build_time(ctxt)
1906
self.assertFalse(called['get_all'])
1907
self.assertEqual(called['set_error_state'], 0)
1909
def test_instance_build_timeout(self):
1910
self.flags(instance_build_timeout=30)
1911
ctxt = context.get_admin_context()
1912
called = {'get_all': False, 'set_error_state': 0}
1913
created_at = utils.utcnow() + datetime.timedelta(seconds=-60)
1915
def fake_instance_get_all_by_filters(*args, **kwargs):
1916
called['get_all'] = True
1919
self.stubs.Set(db, 'instance_get_all_by_filters',
1920
fake_instance_get_all_by_filters)
1922
def fake_set_instance_error_state(_ctxt, instance_uuid, **kwargs):
1923
called['set_error_state'] += 1
1925
self.stubs.Set(self.compute, '_set_instance_error_state',
1926
fake_set_instance_error_state)
1931
uuid = 'fake-uuid-%s' % x
1932
instance_map[uuid] = {'uuid': uuid, 'host': FLAGS.host,
1933
'vm_state': vm_states.BUILDING,
1934
'created_at': created_at}
1935
instances.append(instance_map[uuid])
1937
self.compute._check_instance_build_time(ctxt)
1938
self.assertTrue(called['get_all'])
1939
self.assertEqual(called['set_error_state'], 5)
1941
def test_instance_build_timeout_mixed_instances(self):
1942
self.flags(instance_build_timeout=30)
1943
ctxt = context.get_admin_context()
1944
called = {'get_all': False, 'set_error_state': 0}
1945
created_at = utils.utcnow() + datetime.timedelta(seconds=-60)
1947
def fake_instance_get_all_by_filters(*args, **kwargs):
1948
called['get_all'] = True
1951
self.stubs.Set(db, 'instance_get_all_by_filters',
1952
fake_instance_get_all_by_filters)
1954
def fake_set_instance_error_state(_ctxt, instance_uuid, **kwargs):
1955
called['set_error_state'] += 1
1957
self.stubs.Set(self.compute, '_set_instance_error_state',
1958
fake_set_instance_error_state)
1964
uuid = 'fake-uuid-%s' % x
1965
instance_map[uuid] = {'uuid': uuid, 'host': FLAGS.host,
1966
'vm_state': vm_states.BUILDING,
1967
'created_at': created_at}
1968
instances.append(instance_map[uuid])
1971
uuid = 'fake-uuid-5'
1972
instance_map[uuid] = {'uuid': uuid, 'host': FLAGS.host,
1973
'vm_state': vm_states.BUILDING,
1974
'created_at': utils.utcnow()}
1975
instances.append(instance_map[uuid])
1977
self.compute._check_instance_build_time(ctxt)
1978
self.assertTrue(called['get_all'])
1979
self.assertEqual(called['set_error_state'], 4)
1982
class ComputeAPITestCase(BaseTestCase):
1985
def fake_get_nw_info(cls, ctxt, instance):
1986
self.assertTrue(ctxt.is_admin)
1987
return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1,
1990
super(ComputeAPITestCase, self).setUp()
1991
self.stubs.Set(nova.network.API, 'get_instance_nw_info',
1993
self.compute_api = compute.API()
1996
'properties': {'kernel_id': 'fake_kernel_id',
1997
'ramdisk_id': 'fake_ramdisk_id'},
2000
def _run_instance(self):
2001
instance = self._create_fake_instance()
2002
instance_uuid = instance['uuid']
2003
self.compute.run_instance(self.context, instance_uuid)
2005
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2006
self.assertEqual(instance['task_state'], None)
2007
return instance, instance_uuid
2009
def test_create_with_too_little_ram(self):
2010
"""Test an instance type with too little memory"""
2012
inst_type = instance_types.get_default_instance_type()
2013
inst_type['memory_mb'] = 1
2015
def fake_show(*args):
2016
img = copy.copy(self.fake_image)
2019
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
2021
self.assertRaises(exception.InstanceTypeMemoryTooSmall,
2022
self.compute_api.create, self.context, inst_type, None)
2024
# Now increase the inst_type memory and make sure all is fine.
2025
inst_type['memory_mb'] = 2
2026
(refs, resv_id) = self.compute_api.create(self.context,
2028
db.instance_destroy(self.context, refs[0]['id'])
2030
def test_create_with_too_little_disk(self):
2031
"""Test an instance type with too little disk space"""
2033
inst_type = instance_types.get_default_instance_type()
2034
inst_type['root_gb'] = 1
2036
def fake_show(*args):
2037
img = copy.copy(self.fake_image)
2040
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
2042
self.assertRaises(exception.InstanceTypeDiskTooSmall,
2043
self.compute_api.create, self.context, inst_type, None)
2045
# Now increase the inst_type disk space and make sure all is fine.
2046
inst_type['root_gb'] = 2
2047
(refs, resv_id) = self.compute_api.create(self.context,
2049
db.instance_destroy(self.context, refs[0]['id'])
2051
def test_create_just_enough_ram_and_disk(self):
2052
"""Test an instance type with just enough ram and disk space"""
2054
inst_type = instance_types.get_default_instance_type()
2055
inst_type['root_gb'] = 2
2056
inst_type['memory_mb'] = 2
2058
def fake_show(*args):
2059
img = copy.copy(self.fake_image)
2063
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
2065
(refs, resv_id) = self.compute_api.create(self.context,
2067
db.instance_destroy(self.context, refs[0]['id'])
2069
def test_create_with_no_ram_and_disk_reqs(self):
2070
"""Test an instance type with no min_ram or min_disk"""
2072
inst_type = instance_types.get_default_instance_type()
2073
inst_type['root_gb'] = 1
2074
inst_type['memory_mb'] = 1
2076
def fake_show(*args):
2077
return copy.copy(self.fake_image)
2078
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
2080
(refs, resv_id) = self.compute_api.create(self.context,
2082
db.instance_destroy(self.context, refs[0]['id'])
2084
def test_create_instance_defaults_display_name(self):
2085
"""Verify that an instance cannot be created without a display_name."""
2086
cases = [dict(), dict(display_name=None)]
2087
for instance in cases:
2088
(ref, resv_id) = self.compute_api.create(self.context,
2089
instance_types.get_default_instance_type(), None, **instance)
2091
self.assertNotEqual(ref[0]['display_name'], None)
2093
db.instance_destroy(self.context, ref[0]['id'])
2095
def test_create_instance_sets_system_metadata(self):
2096
"""Make sure image properties are copied into system metadata."""
2097
(ref, resv_id) = self.compute_api.create(
2099
instance_type=instance_types.get_default_instance_type(),
2102
sys_metadata = db.instance_system_metadata_get(self.context,
2104
self.assertEqual(sys_metadata,
2105
{'image_kernel_id': 'fake_kernel_id',
2106
'image_ramdisk_id': 'fake_ramdisk_id',
2107
'image_something_else': 'meow'})
2109
db.instance_destroy(self.context, ref[0]['id'])
2111
def test_create_instance_associates_security_groups(self):
2112
"""Make sure create associates security groups"""
2113
group = self._create_group()
2114
(ref, resv_id) = self.compute_api.create(
2116
instance_type=instance_types.get_default_instance_type(),
2118
security_group=['testgroup'])
2120
self.assertEqual(len(db.security_group_get_by_instance(
2121
self.context, ref[0]['id'])), 1)
2122
group = db.security_group_get(self.context, group['id'])
2123
self.assert_(len(group.instances) == 1)
2125
db.security_group_destroy(self.context, group['id'])
2126
db.instance_destroy(self.context, ref[0]['id'])
2128
def test_create_instance_with_invalid_security_group_raises(self):
2129
instance_type = instance_types.get_default_instance_type()
2131
pre_build_len = len(db.instance_get_all(context.get_admin_context()))
2132
self.assertRaises(exception.SecurityGroupNotFoundForProject,
2133
self.compute_api.create,
2135
instance_type=instance_type,
2137
security_group=['this_is_a_fake_sec_group'])
2138
self.assertEqual(pre_build_len,
2139
len(db.instance_get_all(context.get_admin_context())))
2141
def test_default_hostname_generator(self):
2142
cases = [(None, 'server-1'), ('Hello, Server!', 'hello-server'),
2143
('<}\x1fh\x10e\x08l\x02l\x05o\x12!{>', 'hello'),
2144
('hello_server', 'hello-server')]
2145
for display_name, hostname in cases:
2146
(ref, resv_id) = self.compute_api.create(self.context,
2147
instance_types.get_default_instance_type(), None,
2148
display_name=display_name)
2150
self.assertEqual(ref[0]['hostname'], hostname)
2152
db.instance_destroy(self.context, ref[0]['id'])
2154
def test_destroy_instance_disassociates_security_groups(self):
2155
"""Make sure destroying disassociates security groups"""
2156
group = self._create_group()
2158
(ref, resv_id) = self.compute_api.create(
2160
instance_type=instance_types.get_default_instance_type(),
2162
security_group=['testgroup'])
2164
db.instance_destroy(self.context, ref[0]['id'])
2165
group = db.security_group_get(self.context, group['id'])
2166
self.assert_(len(group.instances) == 0)
2168
db.security_group_destroy(self.context, group['id'])
2170
def test_destroy_security_group_disassociates_instances(self):
2171
"""Make sure destroying security groups disassociates instances"""
2172
group = self._create_group()
2174
(ref, resv_id) = self.compute_api.create(
2176
instance_type=instance_types.get_default_instance_type(),
2178
security_group=['testgroup'])
2181
db.security_group_destroy(self.context, group['id'])
2182
admin_deleted_context = context.get_admin_context(
2183
read_deleted="only")
2184
group = db.security_group_get(admin_deleted_context, group['id'])
2185
self.assert_(len(group.instances) == 0)
2187
db.instance_destroy(self.context, ref[0]['id'])
2189
def test_start(self):
2190
instance = self._create_fake_instance()
2191
instance_uuid = instance['uuid']
2192
self.compute.run_instance(self.context, instance_uuid)
2194
self.compute.stop_instance(self.context, instance_uuid)
2196
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2197
self.assertEqual(instance['task_state'], None)
2199
self.compute_api.start(self.context, instance)
2201
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2202
self.assertEqual(instance['task_state'], task_states.STARTING)
2204
db.instance_destroy(self.context, instance['id'])
2206
def test_stop(self):
2207
instance = self._create_fake_instance()
2208
instance_uuid = instance['uuid']
2209
self.compute.run_instance(self.context, instance_uuid)
2211
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2212
self.assertEqual(instance['task_state'], None)
2214
self.compute_api.stop(self.context, instance)
2216
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2217
self.assertEqual(instance['task_state'], task_states.STOPPING)
2219
db.instance_destroy(self.context, instance['id'])
2221
def test_start_shutdown(self):
2222
def check_state(instance_uuid, power_state_, vm_state_, task_state_):
2223
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2224
self.assertEqual(instance['power_state'], power_state_)
2225
self.assertEqual(instance['vm_state'], vm_state_)
2226
self.assertEqual(instance['task_state'], task_state_)
2228
def start_check_state(instance_uuid,
2229
power_state_, vm_state_, task_state_):
2230
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2231
self.compute_api.start(self.context, instance)
2232
check_state(instance_uuid, power_state_, vm_state_, task_state_)
2234
instance = self._create_fake_instance()
2235
instance_uuid = instance['uuid']
2236
self.compute.run_instance(self.context, instance_uuid)
2238
check_state(instance_uuid, power_state.RUNNING, vm_states.ACTIVE, None)
2240
# NOTE(yamahata): emulate compute.manager._sync_power_state() that
2241
# the instance is shutdown by itself
2242
db.instance_update(self.context, instance_uuid,
2243
{'power_state': power_state.NOSTATE,
2244
'vm_state': vm_states.SHUTOFF})
2245
check_state(instance_uuid, power_state.NOSTATE, vm_states.SHUTOFF,
2248
start_check_state(instance_uuid,
2249
power_state.NOSTATE, vm_states.SHUTOFF, None)
2251
db.instance_update(self.context, instance_uuid,
2252
{'shutdown_terminate': False})
2253
start_check_state(instance_uuid, power_state.NOSTATE,
2254
vm_states.STOPPED, task_states.STARTING)
2256
db.instance_destroy(self.context, instance['id'])
2258
def test_delete(self):
2259
instance, instance_uuid = self._run_instance()
2261
self.compute_api.delete(self.context, instance)
2263
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2264
self.assertEqual(instance['task_state'], task_states.DELETING)
2266
db.instance_destroy(self.context, instance['id'])
2268
def test_delete_fail(self):
2269
instance, instance_uuid = self._run_instance()
2271
instance = db.instance_update(self.context, instance_uuid,
2272
{'disable_terminate': True})
2273
self.compute_api.delete(self.context, instance)
2275
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2276
self.assertEqual(instance['task_state'], None)
2278
db.instance_destroy(self.context, instance['id'])
2280
def test_delete_soft(self):
2281
instance, instance_uuid = self._run_instance()
2283
self.compute_api.soft_delete(self.context, instance)
2285
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2286
self.assertEqual(instance['task_state'], task_states.POWERING_OFF)
2288
db.instance_destroy(self.context, instance['id'])
2290
def test_delete_soft_fail(self):
2291
instance, instance_uuid = self._run_instance()
2293
instance = db.instance_update(self.context, instance_uuid,
2294
{'disable_terminate': True})
2295
self.compute_api.soft_delete(self.context, instance)
2297
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2298
self.assertEqual(instance['task_state'], None)
2300
db.instance_destroy(self.context, instance['id'])
2302
def test_force_delete(self):
2303
"""Ensure instance can be deleted after a soft delete"""
2304
instance = self._create_fake_instance()
2305
instance_uuid = instance['uuid']
2306
self.compute.run_instance(self.context, instance_uuid)
2308
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2309
self.compute_api.soft_delete(self.context, instance)
2311
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2312
self.assertEqual(instance['task_state'], task_states.POWERING_OFF)
2314
self.compute_api.force_delete(self.context, instance)
2316
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2317
self.assertEqual(instance['task_state'], task_states.DELETING)
2319
def test_suspend(self):
2320
"""Ensure instance can be suspended"""
2321
instance = self._create_fake_instance()
2322
instance_uuid = instance['uuid']
2323
self.compute.run_instance(self.context, instance_uuid)
2325
self.assertEqual(instance['task_state'], None)
2327
self.compute_api.suspend(self.context, instance)
2329
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2330
self.assertEqual(instance['task_state'], task_states.SUSPENDING)
2332
db.instance_destroy(self.context, instance['id'])
2334
def test_resume(self):
2335
"""Ensure instance can be resumed (if suspended)"""
2336
instance = self._create_fake_instance()
2337
instance_uuid = instance['uuid']
2338
instance_id = instance['id']
2339
self.compute.run_instance(self.context, instance_uuid)
2340
db.instance_update(self.context, instance_id,
2341
{'vm_state': vm_states.SUSPENDED})
2342
instance = db.instance_get(self.context, instance_id)
2344
self.assertEqual(instance['task_state'], None)
2346
self.compute_api.resume(self.context, instance)
2348
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2349
self.assertEqual(instance['task_state'], task_states.RESUMING)
2351
db.instance_destroy(self.context, instance['id'])
2353
def test_pause(self):
2354
"""Ensure instance can be paused"""
2355
instance = self._create_fake_instance()
2356
instance_uuid = instance['uuid']
2357
self.compute.run_instance(self.context, instance_uuid)
2359
self.assertEqual(instance['task_state'], None)
2361
self.compute_api.pause(self.context, instance)
2363
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2364
self.assertEqual(instance['task_state'], task_states.PAUSING)
2366
db.instance_destroy(self.context, instance['id'])
2368
def test_unpause(self):
2369
"""Ensure instance can be unpaused"""
2370
instance = self._create_fake_instance()
2371
instance_uuid = instance['uuid']
2372
self.compute.run_instance(self.context, instance_uuid)
2374
self.assertEqual(instance['task_state'], None)
2376
self.compute.pause_instance(self.context, instance_uuid)
2377
# set the state that the instance gets when pause finishes
2378
db.instance_update(self.context, instance['uuid'],
2379
{'vm_state': vm_states.PAUSED})
2380
instance = db.instance_get_by_uuid(self.context, instance['uuid'])
2382
self.compute_api.unpause(self.context, instance)
2384
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2385
self.assertEqual(instance['task_state'], task_states.UNPAUSING)
2387
db.instance_destroy(self.context, instance['id'])
2389
def test_restore(self):
2390
"""Ensure instance can be restored from a soft delete"""
2391
instance = self._create_fake_instance()
2392
instance_uuid = instance['uuid']
2393
self.compute.run_instance(self.context, instance_uuid)
2395
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2396
self.compute_api.soft_delete(self.context, instance)
2398
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2399
self.assertEqual(instance['task_state'], task_states.POWERING_OFF)
2401
self.compute_api.restore(self.context, instance)
2403
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2404
self.assertEqual(instance['task_state'], task_states.POWERING_ON)
2406
db.instance_destroy(self.context, instance['id'])
2408
def test_rebuild(self):
2409
inst_ref = self._create_fake_instance()
2410
instance_uuid = inst_ref['uuid']
2411
self.compute.run_instance(self.context, instance_uuid)
2413
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2414
self.assertEqual(instance['task_state'], None)
2415
# Set some image metadata that should get wiped out and reset
2416
# as well as some other metadata that should be preserved.
2417
db.instance_system_metadata_update(self.context, instance_uuid,
2418
{'image_kernel_id': 'old-data',
2419
'image_ramdisk_id': 'old_data',
2420
'image_something_else': 'old-data',
2421
'image_should_remove': 'bye-bye',
2422
'preserved': 'preserve this!'},
2425
# Make sure Compute API updates the image_ref before casting to
2427
orig_update = self.compute_api.update
2428
info = {'image_ref': None}
2430
def update_wrapper(*args, **kwargs):
2431
if 'image_ref' in kwargs:
2432
info['image_ref'] = kwargs['image_ref']
2433
return orig_update(*args, **kwargs)
2435
self.stubs.Set(self.compute_api, 'update', update_wrapper)
2437
image_ref = instance["image_ref"] + '-new_image_ref'
2438
password = "new_password"
2439
self.compute_api.rebuild(self.context, instance, image_ref, password)
2440
self.assertEqual(info['image_ref'], image_ref)
2442
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2443
self.assertEqual(instance['vm_state'], vm_states.REBUILDING)
2444
sys_metadata = db.instance_system_metadata_get(self.context,
2446
self.assertEqual(sys_metadata,
2447
{'image_kernel_id': 'fake_kernel_id',
2448
'image_ramdisk_id': 'fake_ramdisk_id',
2449
'image_something_else': 'meow',
2450
'preserved': 'preserve this!'})
2451
db.instance_destroy(self.context, instance['id'])
2453
def test_reboot_soft(self):
2454
"""Ensure instance can be soft rebooted"""
2455
inst_ref = self._create_fake_instance()
2456
instance_uuid = inst_ref['uuid']
2457
self.compute.run_instance(self.context, instance_uuid)
2459
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
2460
self.assertEqual(inst_ref['task_state'], None)
2462
reboot_type = "SOFT"
2463
self.compute_api.reboot(self.context, inst_ref, reboot_type)
2465
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
2466
self.assertEqual(inst_ref['task_state'], task_states.REBOOTING)
2468
db.instance_destroy(self.context, inst_ref['id'])
2470
def test_reboot_hard(self):
2471
"""Ensure instance can be hard rebooted"""
2472
inst_ref = self._create_fake_instance()
2473
instance_uuid = inst_ref['uuid']
2474
self.compute.run_instance(self.context, instance_uuid)
2476
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
2477
self.assertEqual(inst_ref['task_state'], None)
2479
reboot_type = "HARD"
2480
self.compute_api.reboot(self.context, inst_ref, reboot_type)
2482
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
2483
self.assertEqual(inst_ref['task_state'], task_states.REBOOTING_HARD)
2485
db.instance_destroy(self.context, inst_ref['id'])
2487
def test_hostname_create(self):
2488
"""Ensure instance hostname is set during creation."""
2489
inst_type = instance_types.get_instance_type_by_name('m1.tiny')
2490
(instances, _) = self.compute_api.create(self.context,
2493
display_name='test host')
2495
self.assertEqual('test-host', instances[0]['hostname'])
2497
def test_set_admin_password(self):
2498
"""Ensure instance can have its admin password set"""
2499
instance = self._create_fake_instance()
2500
instance_uuid = instance['uuid']
2501
self.compute.run_instance(self.context, instance_uuid)
2503
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
2504
self.assertEqual(inst_ref['vm_state'], vm_states.ACTIVE)
2505
self.assertEqual(inst_ref['task_state'], None)
2507
self.compute_api.set_admin_password(self.context, inst_ref)
2509
inst_ref = db.instance_get_by_uuid(self.context, instance_uuid)
2510
self.assertEqual(inst_ref['vm_state'], vm_states.ACTIVE)
2511
self.assertEqual(inst_ref['task_state'], task_states.UPDATING_PASSWORD)
2513
self.compute.terminate_instance(self.context, instance_uuid)
2515
def test_rescue_unrescue(self):
2516
instance = self._create_fake_instance()
2517
instance_uuid = instance['uuid']
2518
self.compute.run_instance(self.context, instance_uuid)
2520
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2521
self.assertEqual(instance['vm_state'], vm_states.ACTIVE)
2522
self.assertEqual(instance['task_state'], None)
2524
self.compute_api.rescue(self.context, instance)
2526
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2527
self.assertEqual(instance['vm_state'], vm_states.ACTIVE)
2528
self.assertEqual(instance['task_state'], task_states.RESCUING)
2530
params = {'vm_state': vm_states.RESCUED, 'task_state': None}
2531
db.instance_update(self.context, instance_uuid, params)
2533
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2534
self.compute_api.unrescue(self.context, instance)
2536
instance = db.instance_get_by_uuid(self.context, instance_uuid)
2537
self.assertEqual(instance['vm_state'], vm_states.RESCUED)
2538
self.assertEqual(instance['task_state'], task_states.UNRESCUING)
2540
self.compute.terminate_instance(self.context, instance_uuid)
2542
def test_snapshot(self):
2543
"""Ensure a snapshot of an instance can be created"""
2544
instance = self._create_fake_instance()
2545
image = self.compute_api.snapshot(self.context, instance, 'snap1',
2546
{'extra_param': 'value1'})
2548
self.assertEqual(image['name'], 'snap1')
2549
properties = image['properties']
2550
self.assertTrue('backup_type' not in properties)
2551
self.assertEqual(properties['image_type'], 'snapshot')
2552
self.assertEqual(properties['instance_uuid'], instance['uuid'])
2553
self.assertEqual(properties['extra_param'], 'value1')
2555
db.instance_destroy(self.context, instance['id'])
2557
def test_snapshot_minram_mindisk_VHD(self):
2558
"""Ensure a snapshots min_ram and min_disk are correct.
2560
A snapshot of a non-shrinkable VHD should have min_ram
2561
and min_disk set to that of the original instances flavor.
2564
def fake_show(*args):
2565
img = copy.copy(self.fake_image)
2566
img['disk_format'] = 'vhd'
2568
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
2570
instance = self._create_fake_instance()
2571
inst_params = {'root_gb': 2, 'memory_mb': 256}
2572
instance['instance_type'].update(inst_params)
2574
image = self.compute_api.snapshot(self.context, instance, 'snap1',
2575
{'extra_param': 'value1'})
2577
self.assertEqual(image['name'], 'snap1')
2578
self.assertEqual(image['min_ram'], 256)
2579
self.assertEqual(image['min_disk'], 2)
2580
properties = image['properties']
2581
self.assertTrue('backup_type' not in properties)
2582
self.assertEqual(properties['image_type'], 'snapshot')
2583
self.assertEqual(properties['instance_uuid'], instance['uuid'])
2584
self.assertEqual(properties['extra_param'], 'value1')
2586
db.instance_destroy(self.context, instance['id'])
2588
def test_snapshot_minram_mindisk(self):
2589
"""Ensure a snapshots min_ram and min_disk are correct.
2591
A snapshot of an instance should have min_ram and min_disk
2592
set to that of the instances original image unless that
2593
image had a disk format of vhd.
2596
def fake_show(*args):
2597
img = copy.copy(self.fake_image)
2598
img['disk_format'] = 'raw'
2599
img['min_ram'] = 512
2602
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
2604
instance = self._create_fake_instance()
2606
image = self.compute_api.snapshot(self.context, instance, 'snap1',
2607
{'extra_param': 'value1'})
2609
self.assertEqual(image['name'], 'snap1')
2610
self.assertEqual(image['min_ram'], 512)
2611
self.assertEqual(image['min_disk'], 1)
2612
properties = image['properties']
2613
self.assertTrue('backup_type' not in properties)
2614
self.assertEqual(properties['image_type'], 'snapshot')
2615
self.assertEqual(properties['instance_uuid'], instance['uuid'])
2616
self.assertEqual(properties['extra_param'], 'value1')
2618
db.instance_destroy(self.context, instance['id'])
2620
def test_snapshot_minram_mindisk_img_missing_minram(self):
2621
"""Ensure a snapshots min_ram and min_disk are correct.
2623
Do not show an attribute that the orig img did not have.
2626
def fake_show(*args):
2627
img = copy.copy(self.fake_image)
2628
img['disk_format'] = 'raw'
2631
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
2633
instance = self._create_fake_instance()
2635
image = self.compute_api.snapshot(self.context, instance, 'snap1',
2636
{'extra_param': 'value1'})
2638
self.assertEqual(image['name'], 'snap1')
2639
self.assertFalse('min_ram' in image)
2640
self.assertEqual(image['min_disk'], 1)
2641
properties = image['properties']
2642
self.assertTrue('backup_type' not in properties)
2643
self.assertEqual(properties['image_type'], 'snapshot')
2644
self.assertEqual(properties['instance_uuid'], instance['uuid'])
2645
self.assertEqual(properties['extra_param'], 'value1')
2647
db.instance_destroy(self.context, instance['id'])
2649
def test_snapshot_minram_mindisk_no_image(self):
2650
"""Ensure a snapshots min_ram and min_disk are correct.
2652
A snapshots min_ram and min_disk should be set to default if
2653
an instances original image cannot be found.
2656
def fake_show(*args):
2657
raise exception.ImageNotFound
2659
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
2661
instance = self._create_fake_instance()
2663
image = self.compute_api.snapshot(self.context, instance, 'snap1',
2664
{'extra_param': 'value1'})
2666
self.assertEqual(image['name'], 'snap1')
2668
# min_ram and min_disk are not returned when set to default
2669
self.assertFalse('min_ram' in image)
2670
self.assertFalse('min_disk' in image)
2672
properties = image['properties']
2673
self.assertTrue('backup_type' not in properties)
2674
self.assertEqual(properties['image_type'], 'snapshot')
2675
self.assertEqual(properties['instance_uuid'], instance['uuid'])
2676
self.assertEqual(properties['extra_param'], 'value1')
2678
db.instance_destroy(self.context, instance['id'])
2680
def test_backup(self):
2681
"""Can't backup an instance which is already being backed up."""
2682
instance = self._create_fake_instance()
2683
image = self.compute_api.backup(self.context, instance,
2684
'backup1', 'DAILY', None,
2685
{'extra_param': 'value1'})
2687
self.assertEqual(image['name'], 'backup1')
2688
properties = image['properties']
2689
self.assertEqual(properties['backup_type'], 'DAILY')
2690
self.assertEqual(properties['image_type'], 'backup')
2691
self.assertEqual(properties['instance_uuid'], instance['uuid'])
2692
self.assertEqual(properties['extra_param'], 'value1')
2694
db.instance_destroy(self.context, instance['id'])
2696
def test_backup_conflict(self):
2697
"""Can't backup an instance which is already being backed up."""
2698
instance = self._create_fake_instance()
2699
instance_uuid = instance['uuid']
2700
instance_values = {'task_state': task_states.IMAGE_BACKUP}
2701
db.instance_update(self.context, instance_uuid, instance_values)
2702
instance = self.compute_api.get(self.context, instance_uuid)
2704
self.assertRaises(exception.InstanceInvalidState,
2705
self.compute_api.backup,
2712
db.instance_destroy(self.context, instance['id'])
2714
def test_snapshot_conflict(self):
2715
"""Can't snapshot an instance which is already being snapshotted."""
2716
instance = self._create_fake_instance()
2717
instance_uuid = instance['uuid']
2718
instance_values = {'task_state': task_states.IMAGE_SNAPSHOT}
2719
db.instance_update(self.context, instance_uuid, instance_values)
2720
instance = self.compute_api.get(self.context, instance_uuid)
2722
self.assertRaises(exception.InstanceInvalidState,
2723
self.compute_api.snapshot,
2728
db.instance_destroy(self.context, instance['id'])
2730
def test_resize_confirm_through_api(self):
2731
instance = self._create_fake_instance()
2732
context = self.context.elevated()
2733
self.compute.run_instance(self.context, instance['uuid'])
2734
instance = db.instance_get_by_uuid(context, instance['uuid'])
2735
self.compute_api.resize(context, instance, '4')
2737
# create a fake migration record (manager does this)
2738
db.migration_create(context,
2739
{'instance_uuid': instance['uuid'],
2740
'status': 'finished'})
2741
# set the state that the instance gets when resize finishes
2742
db.instance_update(self.context, instance['uuid'],
2743
{'task_state': task_states.RESIZE_VERIFY,
2744
'vm_state': vm_states.ACTIVE})
2745
instance = db.instance_get_by_uuid(context, instance['uuid'])
2747
self.compute_api.confirm_resize(context, instance)
2748
self.compute.terminate_instance(context, instance['uuid'])
2750
def test_resize_revert_through_api(self):
2751
instance = self._create_fake_instance()
2752
context = self.context.elevated()
2753
instance = db.instance_get_by_uuid(context, instance['uuid'])
2754
self.compute.run_instance(self.context, instance['uuid'])
2756
self.compute_api.resize(context, instance, '4')
2758
# create a fake migration record (manager does this)
2759
db.migration_create(context,
2760
{'instance_uuid': instance['uuid'],
2761
'status': 'finished'})
2762
# set the state that the instance gets when resize finishes
2763
db.instance_update(self.context, instance['uuid'],
2764
{'task_state': task_states.RESIZE_VERIFY,
2765
'vm_state': vm_states.ACTIVE})
2766
instance = db.instance_get_by_uuid(context, instance['uuid'])
2768
self.compute_api.revert_resize(context, instance)
2770
instance = db.instance_get_by_uuid(context, instance['uuid'])
2771
self.assertEqual(instance['vm_state'], vm_states.RESIZING)
2772
self.assertEqual(instance['task_state'], task_states.RESIZE_REVERTING)
2774
self.compute.terminate_instance(context, instance['uuid'])
2776
def test_resize_invalid_flavor_fails(self):
2777
"""Ensure invalid flavors raise"""
2778
instance = self._create_fake_instance()
2779
context = self.context.elevated()
2780
instance = db.instance_get_by_uuid(context, instance['uuid'])
2781
self.compute.run_instance(self.context, instance['uuid'])
2783
self.assertRaises(exception.NotFound, self.compute_api.resize,
2784
context, instance, 200)
2786
self.compute.terminate_instance(context, instance['uuid'])
2788
def test_resize_same_size_fails(self):
2789
"""Ensure invalid flavors raise"""
2790
context = self.context.elevated()
2791
instance = self._create_fake_instance()
2792
instance = db.instance_get_by_uuid(context, instance['uuid'])
2794
self.compute.run_instance(self.context, instance['uuid'])
2796
self.assertRaises(exception.CannotResizeToSameSize,
2797
self.compute_api.resize, context, instance, 1)
2799
self.compute.terminate_instance(context, instance['uuid'])
2801
def test_migrate(self):
2802
context = self.context.elevated()
2803
instance = self._create_fake_instance()
2804
instance = db.instance_get_by_uuid(context, instance['uuid'])
2805
self.compute.run_instance(self.context, instance['uuid'])
2806
# Migrate simply calls resize() without a flavor_id.
2807
self.compute_api.resize(context, instance, None)
2808
self.compute.terminate_instance(context, instance['uuid'])
2810
def test_resize_request_spec(self):
2811
def _fake_cast(context, topic, msg):
2812
request_spec = msg['args']['request_spec']
2813
filter_properties = msg['args']['filter_properties']
2814
instance_properties = request_spec['instance_properties']
2815
self.assertEqual(instance_properties['host'], 'host2')
2816
self.assertIn('host2', filter_properties['ignore_hosts'])
2818
self.stubs.Set(rpc, 'cast', _fake_cast)
2820
context = self.context.elevated()
2821
instance = self._create_fake_instance(dict(host='host2'))
2822
instance = db.instance_get_by_uuid(context, instance['uuid'])
2823
self.compute.run_instance(self.context, instance['uuid'])
2825
self.compute_api.resize(context, instance, None)
2827
self.compute.terminate_instance(context, instance['uuid'])
2829
def test_resize_request_spec_noavoid(self):
2830
def _fake_cast(context, topic, msg):
2831
request_spec = msg['args']['request_spec']
2832
filter_properties = msg['args']['filter_properties']
2833
instance_properties = request_spec['instance_properties']
2834
self.assertEqual(instance_properties['host'], 'host2')
2835
self.assertNotIn('host2', filter_properties['ignore_hosts'])
2837
self.stubs.Set(rpc, 'cast', _fake_cast)
2838
self.flags(allow_resize_to_same_host=True)
2840
context = self.context.elevated()
2841
instance = self._create_fake_instance(dict(host='host2'))
2842
instance = db.instance_get_by_uuid(context, instance['uuid'])
2843
self.compute.run_instance(self.context, instance['uuid'])
2845
self.compute_api.resize(context, instance, None)
2847
self.compute.terminate_instance(context, instance['uuid'])
2849
def test_associate_floating_ip(self):
2850
"""Ensure we can associate a floating ip with an instance"""
2851
called = {'associate': False}
2853
def fake_associate_ip_network_api(self, ctxt, floating_address,
2855
called['associate'] = True
2857
self.stubs.Set(nova.network.API, 'associate_floating_ip',
2858
fake_associate_ip_network_api)
2860
instance = self._create_fake_instance()
2861
instance_uuid = instance['uuid']
2864
self.compute.run_instance(self.context, instance_uuid)
2865
self.compute_api.associate_floating_ip(self.context,
2868
self.assertTrue(called['associate'])
2869
self.compute.terminate_instance(self.context, instance_uuid)
2871
def test_associate_floating_ip_no_fixed_ip(self):
2872
"""Should fail if instance has no fixed ip."""
2874
def fake_get_nw_info(self, ctxt, instance):
2877
self.stubs.Set(nova.network.API, 'get_instance_nw_info',
2880
instance = self._create_fake_instance()
2881
instance_uuid = instance['uuid']
2884
self.compute.run_instance(self.context, instance_uuid)
2885
self.assertRaises(exception.FixedIpNotFoundForInstance,
2886
self.compute_api.associate_floating_ip,
2890
self.compute.terminate_instance(self.context, instance_uuid)
2893
"""Test get instance"""
2894
c = context.get_admin_context()
2895
exp_instance = self._create_fake_instance()
2896
expected = dict(exp_instance.iteritems())
2897
expected['name'] = exp_instance['name']
2899
def fake_db_get(context, instance_uuid):
2902
self.stubs.Set(db, 'instance_get_by_uuid', fake_db_get)
2904
instance = self.compute_api.get(c, exp_instance['uuid'])
2905
self.assertEquals(expected, instance)
2907
def test_get_with_integer_id(self):
2908
"""Test get instance with an integer id"""
2909
c = context.get_admin_context()
2910
exp_instance = self._create_fake_instance()
2911
expected = dict(exp_instance.iteritems())
2912
expected['name'] = exp_instance['name']
2914
def fake_db_get(context, instance_id):
2917
self.stubs.Set(db, 'instance_get', fake_db_get)
2919
instance = self.compute_api.get(c, exp_instance['id'])
2920
self.assertEquals(expected, instance)
2922
def test_get_all_by_name_regexp(self):
2923
"""Test searching instances by name (display_name)"""
2924
c = context.get_admin_context()
2925
instance1 = self._create_fake_instance({'display_name': 'woot'})
2926
instance2 = self._create_fake_instance({
2927
'display_name': 'woo'})
2928
instance3 = self._create_fake_instance({
2929
'display_name': 'not-woot'})
2931
instances = self.compute_api.get_all(c,
2932
search_opts={'name': 'woo.*'})
2933
self.assertEqual(len(instances), 2)
2934
instance_uuids = [instance['uuid'] for instance in instances]
2935
self.assertTrue(instance1['uuid'] in instance_uuids)
2936
self.assertTrue(instance2['uuid'] in instance_uuids)
2938
instances = self.compute_api.get_all(c,
2939
search_opts={'name': 'woot.*'})
2940
instance_uuids = [instance['uuid'] for instance in instances]
2941
self.assertEqual(len(instances), 1)
2942
self.assertTrue(instance1['uuid'] in instance_uuids)
2944
instances = self.compute_api.get_all(c,
2945
search_opts={'name': '.*oot.*'})
2946
self.assertEqual(len(instances), 2)
2947
instance_uuids = [instance['uuid'] for instance in instances]
2948
self.assertTrue(instance1['uuid'] in instance_uuids)
2949
self.assertTrue(instance3['uuid'] in instance_uuids)
2951
instances = self.compute_api.get_all(c,
2952
search_opts={'name': 'n.*'})
2953
self.assertEqual(len(instances), 1)
2954
instance_uuids = [instance['uuid'] for instance in instances]
2955
self.assertTrue(instance3['uuid'] in instance_uuids)
2957
instances = self.compute_api.get_all(c,
2958
search_opts={'name': 'noth.*'})
2959
self.assertEqual(len(instances), 0)
2961
db.instance_destroy(c, instance1['id'])
2962
db.instance_destroy(c, instance2['id'])
2963
db.instance_destroy(c, instance3['id'])
2965
def test_get_all_by_instance_name_regexp(self):
2966
"""Test searching instances by name"""
2967
self.flags(instance_name_template='instance-%d')
2969
c = context.get_admin_context()
2970
instance1 = self._create_fake_instance()
2971
instance2 = self._create_fake_instance({'id': 2})
2972
instance3 = self._create_fake_instance({'id': 10})
2974
instances = self.compute_api.get_all(c,
2975
search_opts={'instance_name': 'instance.*'})
2976
self.assertEqual(len(instances), 3)
2978
instances = self.compute_api.get_all(c,
2979
search_opts={'instance_name': '.*\-\d$'})
2980
self.assertEqual(len(instances), 2)
2981
instance_uuids = [instance['uuid'] for instance in instances]
2982
self.assertTrue(instance1['uuid'] in instance_uuids)
2983
self.assertTrue(instance2['uuid'] in instance_uuids)
2985
instances = self.compute_api.get_all(c,
2986
search_opts={'instance_name': 'i.*2'})
2987
self.assertEqual(len(instances), 1)
2988
self.assertEqual(instances[0]['uuid'], instance2['uuid'])
2990
db.instance_destroy(c, instance1['id'])
2991
db.instance_destroy(c, instance2['id'])
2992
db.instance_destroy(c, instance3['id'])
2994
def test_get_all_by_multiple_options_at_once(self):
2995
"""Test searching by multiple options at once"""
2996
c = context.get_admin_context()
2997
network_manager = fake_network.FakeNetworkManager()
2998
self.stubs.Set(self.compute_api.network_api,
2999
'get_instance_uuids_by_ip_filter',
3000
network_manager.get_instance_uuids_by_ip_filter)
3001
self.stubs.Set(network_manager.db,
3002
'instance_get_id_to_uuid_mapping',
3003
db.instance_get_id_to_uuid_mapping)
3005
instance1 = self._create_fake_instance({'display_name': 'woot',
3007
instance2 = self._create_fake_instance({
3008
'display_name': 'woo',
3010
instance3 = self._create_fake_instance({
3011
'display_name': 'not-woot',
3014
# ip ends up matching 2nd octet here.. so all 3 match ip
3015
# but 'name' only matches one
3016
instances = self.compute_api.get_all(c,
3017
search_opts={'ip': '.*\.1', 'name': 'not.*'})
3018
self.assertEqual(len(instances), 1)
3019
self.assertEqual(instances[0]['uuid'], instance3['uuid'])
3021
# ip ends up matching any ip with a '1' in the last octet..
3022
# so instance 1 and 3.. but name should only match #1
3023
# but 'name' only matches one
3024
instances = self.compute_api.get_all(c,
3025
search_opts={'ip': '.*\.1$', 'name': '^woo.*'})
3026
self.assertEqual(len(instances), 1)
3027
self.assertEqual(instances[0]['uuid'], instance1['uuid'])
3029
# same as above but no match on name (name matches instance1
3030
# but the ip query doesn't
3031
instances = self.compute_api.get_all(c,
3032
search_opts={'ip': '.*\.2$', 'name': '^woot.*'})
3033
self.assertEqual(len(instances), 0)
3035
# ip matches all 3... ipv6 matches #2+#3...name matches #3
3036
instances = self.compute_api.get_all(c,
3037
search_opts={'ip': '.*\.1',
3039
'ip6': '^.*12.*34.*'})
3040
self.assertEqual(len(instances), 1)
3041
self.assertEqual(instances[0]['uuid'], instance3['uuid'])
3043
db.instance_destroy(c, instance1['id'])
3044
db.instance_destroy(c, instance2['id'])
3045
db.instance_destroy(c, instance3['id'])
3047
def test_get_all_by_image(self):
3048
"""Test searching instances by image"""
3050
c = context.get_admin_context()
3051
instance1 = self._create_fake_instance({'image_ref': '1234'})
3052
instance2 = self._create_fake_instance({'image_ref': '4567'})
3053
instance3 = self._create_fake_instance({'image_ref': '4567'})
3055
instances = self.compute_api.get_all(c, search_opts={'image': '123'})
3056
self.assertEqual(len(instances), 0)
3058
instances = self.compute_api.get_all(c, search_opts={'image': '1234'})
3059
self.assertEqual(len(instances), 1)
3060
self.assertEqual(instances[0]['uuid'], instance1['uuid'])
3062
instances = self.compute_api.get_all(c, search_opts={'image': '4567'})
3063
self.assertEqual(len(instances), 2)
3064
instance_uuids = [instance['uuid'] for instance in instances]
3065
self.assertTrue(instance2['uuid'] in instance_uuids)
3066
self.assertTrue(instance3['uuid'] in instance_uuids)
3068
# Test passing a list as search arg
3069
instances = self.compute_api.get_all(c,
3070
search_opts={'image': ['1234', '4567']})
3071
self.assertEqual(len(instances), 3)
3073
db.instance_destroy(c, instance1['id'])
3074
db.instance_destroy(c, instance2['id'])
3075
db.instance_destroy(c, instance3['id'])
3077
def test_get_all_by_flavor(self):
3078
"""Test searching instances by image"""
3080
c = context.get_admin_context()
3081
instance1 = self._create_fake_instance({'instance_type_id': 1})
3082
instance2 = self._create_fake_instance({'instance_type_id': 2})
3083
instance3 = self._create_fake_instance({'instance_type_id': 2})
3085
# NOTE(comstud): Migrations set up the instance_types table
3086
# for us. Therefore, we assume the following is true for
3088
# instance_type_id 1 == flavor 3
3089
# instance_type_id 2 == flavor 1
3090
# instance_type_id 3 == flavor 4
3091
# instance_type_id 4 == flavor 5
3092
# instance_type_id 5 == flavor 2
3094
instances = self.compute_api.get_all(c,
3095
search_opts={'flavor': 5})
3096
self.assertEqual(len(instances), 0)
3098
# ensure unknown filter maps to an empty list, not an exception
3099
instances = self.compute_api.get_all(c, search_opts={'flavor': 99})
3100
self.assertEqual(instances, [])
3102
instances = self.compute_api.get_all(c, search_opts={'flavor': 3})
3103
self.assertEqual(len(instances), 1)
3104
self.assertEqual(instances[0]['id'], instance1['id'])
3106
instances = self.compute_api.get_all(c, search_opts={'flavor': 1})
3107
self.assertEqual(len(instances), 2)
3108
instance_uuids = [instance['uuid'] for instance in instances]
3109
self.assertTrue(instance2['uuid'] in instance_uuids)
3110
self.assertTrue(instance3['uuid'] in instance_uuids)
3112
db.instance_destroy(c, instance1['id'])
3113
db.instance_destroy(c, instance2['id'])
3114
db.instance_destroy(c, instance3['id'])
3116
def test_get_all_by_state(self):
3117
"""Test searching instances by state"""
3119
c = context.get_admin_context()
3120
instance1 = self._create_fake_instance({
3121
'power_state': power_state.SHUTDOWN,
3123
instance2 = self._create_fake_instance({
3124
'power_state': power_state.RUNNING,
3126
instance3 = self._create_fake_instance({
3127
'power_state': power_state.RUNNING,
3130
instances = self.compute_api.get_all(c,
3131
search_opts={'power_state': power_state.SUSPENDED})
3132
self.assertEqual(len(instances), 0)
3134
instances = self.compute_api.get_all(c,
3135
search_opts={'power_state': power_state.SHUTDOWN})
3136
self.assertEqual(len(instances), 1)
3137
self.assertEqual(instances[0]['uuid'], instance1['uuid'])
3139
instances = self.compute_api.get_all(c,
3140
search_opts={'power_state': power_state.RUNNING})
3141
self.assertEqual(len(instances), 2)
3142
instance_uuids = [instance['uuid'] for instance in instances]
3143
self.assertTrue(instance2['uuid'] in instance_uuids)
3144
self.assertTrue(instance3['uuid'] in instance_uuids)
3146
# Test passing a list as search arg
3147
instances = self.compute_api.get_all(c,
3148
search_opts={'power_state': [power_state.SHUTDOWN,
3149
power_state.RUNNING]})
3150
self.assertEqual(len(instances), 3)
3152
db.instance_destroy(c, instance1['id'])
3153
db.instance_destroy(c, instance2['id'])
3154
db.instance_destroy(c, instance3['id'])
3156
def test_get_all_by_metadata(self):
3157
"""Test searching instances by metadata"""
3159
c = context.get_admin_context()
3160
instance0 = self._create_fake_instance()
3161
instance1 = self._create_fake_instance({
3162
'metadata': {'key1': 'value1'}})
3163
instance2 = self._create_fake_instance({
3164
'metadata': {'key2': 'value2'}})
3165
instance3 = self._create_fake_instance({
3166
'metadata': {'key3': 'value3'}})
3167
instance4 = self._create_fake_instance({
3168
'metadata': {'key3': 'value3',
3172
instances = self.compute_api.get_all(c,
3173
search_opts={'metadata': {}})
3174
self.assertEqual(len(instances), 5)
3176
# wrong key/value combination
3177
instances = self.compute_api.get_all(c,
3178
search_opts={'metadata': {'key1': 'value3'}})
3179
self.assertEqual(len(instances), 0)
3182
instances = self.compute_api.get_all(c,
3183
search_opts={'metadata': {'key5': 'value1'}})
3184
self.assertEqual(len(instances), 0)
3186
# find existing instance
3187
instances = self.compute_api.get_all(c,
3188
search_opts={'metadata': {'key2': 'value2'}})
3189
self.assertEqual(len(instances), 1)
3190
self.assertEqual(instances[0]['uuid'], instance2['uuid'])
3192
instances = self.compute_api.get_all(c,
3193
search_opts={'metadata': {'key3': 'value3'}})
3194
self.assertEqual(len(instances), 2)
3195
instance_uuids = [instance['uuid'] for instance in instances]
3196
self.assertTrue(instance3['uuid'] in instance_uuids)
3197
self.assertTrue(instance4['uuid'] in instance_uuids)
3199
# multiple criterias as a dict
3200
instances = self.compute_api.get_all(c,
3201
search_opts={'metadata': {'key3': 'value3',
3203
self.assertEqual(len(instances), 1)
3204
self.assertEqual(instances[0]['uuid'], instance4['uuid'])
3206
# multiple criterias as a list
3207
instances = self.compute_api.get_all(c,
3208
search_opts={'metadata': [{'key4': 'value4'},
3209
{'key3': 'value3'}]})
3210
self.assertEqual(len(instances), 1)
3211
self.assertEqual(instances[0]['uuid'], instance4['uuid'])
3213
db.instance_destroy(c, instance0['id'])
3214
db.instance_destroy(c, instance1['id'])
3215
db.instance_destroy(c, instance2['id'])
3216
db.instance_destroy(c, instance3['id'])
3217
db.instance_destroy(c, instance4['id'])
3219
def test_instance_metadata(self):
3220
"""Test searching instances by state"""
3221
_context = context.get_admin_context()
3222
instance = self._create_fake_instance({'metadata': {'key1': 'value1'}})
3224
metadata = self.compute_api.get_instance_metadata(_context, instance)
3225
self.assertEqual(metadata, {'key1': 'value1'})
3227
self.compute_api.update_instance_metadata(_context, instance,
3229
metadata = self.compute_api.get_instance_metadata(_context, instance)
3230
self.assertEqual(metadata, {'key1': 'value1', 'key2': 'value2'})
3232
new_metadata = {'key2': 'bah', 'key3': 'value3'}
3233
self.compute_api.update_instance_metadata(_context, instance,
3234
new_metadata, delete=True)
3235
metadata = self.compute_api.get_instance_metadata(_context, instance)
3236
self.assertEqual(metadata, new_metadata)
3238
self.compute_api.delete_instance_metadata(_context, instance, 'key2')
3239
metadata = self.compute_api.get_instance_metadata(_context, instance)
3240
self.assertEqual(metadata, {'key3': 'value3'})
3242
db.instance_destroy(_context, instance['id'])
3244
def test_get_instance_faults(self):
3245
"""Get an instances latest fault"""
3246
instance = self._create_fake_instance()
3250
'instance_uuid': instance['uuid'],
3251
'message': "HTTPNotFound",
3252
'details': "Stock details for test",
3253
'created_at': datetime.datetime(2010, 10, 10, 12, 0, 0),
3256
def return_fault(_ctxt, instance_uuids):
3257
return dict.fromkeys(instance_uuids, [fault_fixture])
3259
self.stubs.Set(nova.db,
3260
'instance_fault_get_by_instance_uuids',
3263
_context = context.get_admin_context()
3264
output = self.compute_api.get_instance_faults(_context, [instance])
3265
expected = {instance['uuid']: [fault_fixture]}
3266
self.assertEqual(output, expected)
3268
db.instance_destroy(_context, instance['id'])
3271
def _parse_db_block_device_mapping(bdm_ref):
3272
attr_list = ('delete_on_termination', 'device_name', 'no_device',
3273
'virtual_name', 'volume_id', 'volume_size', 'snapshot_id')
3275
for attr in attr_list:
3276
val = bdm_ref.get(attr, None)
3282
def test_update_block_device_mapping(self):
3284
instance_type = {'swap': swap_size}
3285
instance = self._create_fake_instance()
3287
{'virtual': 'ami', 'device': 'sda1'},
3288
{'virtual': 'root', 'device': '/dev/sda1'},
3290
{'virtual': 'swap', 'device': 'sdb4'},
3291
{'virtual': 'swap', 'device': 'sdb3'},
3292
{'virtual': 'swap', 'device': 'sdb2'},
3293
{'virtual': 'swap', 'device': 'sdb1'},
3295
{'virtual': 'ephemeral0', 'device': 'sdc1'},
3296
{'virtual': 'ephemeral1', 'device': 'sdc2'},
3297
{'virtual': 'ephemeral2', 'device': 'sdc3'}]
3298
block_device_mapping = [
3300
{'device_name': '/dev/sda1',
3301
'snapshot_id': '00000000-aaaa-bbbb-cccc-000000000000',
3302
'delete_on_termination': False},
3306
{'device_name': '/dev/sdb2',
3307
'snapshot_id': '11111111-aaaa-bbbb-cccc-111111111111',
3308
'delete_on_termination': False},
3309
{'device_name': '/dev/sdb3',
3310
'snapshot_id': '22222222-aaaa-bbbb-cccc-222222222222'},
3311
{'device_name': '/dev/sdb4',
3314
# overwrite ephemeral
3315
{'device_name': '/dev/sdc2',
3316
'snapshot_id': '33333333-aaaa-bbbb-cccc-333333333333',
3317
'delete_on_termination': False},
3318
{'device_name': '/dev/sdc3',
3319
'snapshot_id': '44444444-aaaa-bbbb-cccc-444444444444'},
3320
{'device_name': '/dev/sdc4',
3324
{'device_name': '/dev/sdd1',
3325
'snapshot_id': '55555555-aaaa-bbbb-cccc-555555555555',
3326
'delete_on_termination': False},
3327
{'device_name': '/dev/sdd2',
3328
'snapshot_id': '66666666-aaaa-bbbb-cccc-666666666666'},
3329
{'device_name': '/dev/sdd3',
3330
'snapshot_id': '77777777-aaaa-bbbb-cccc-777777777777'},
3331
{'device_name': '/dev/sdd4',
3334
self.compute_api._update_image_block_device_mapping(
3335
self.context, instance_type, instance['uuid'], mappings)
3337
bdms = [self._parse_db_block_device_mapping(bdm_ref)
3338
for bdm_ref in db.block_device_mapping_get_all_by_instance(
3339
self.context, instance['uuid'])]
3341
{'virtual_name': 'swap', 'device_name': '/dev/sdb1',
3342
'volume_size': swap_size},
3343
{'virtual_name': 'ephemeral0', 'device_name': '/dev/sdc1'},
3345
# NOTE(yamahata): ATM only ephemeral0 is supported.
3346
# they're ignored for now
3347
#{'virtual_name': 'ephemeral1', 'device_name': '/dev/sdc2'},
3348
#{'virtual_name': 'ephemeral2', 'device_name': '/dev/sdc3'}
3351
expected_result.sort()
3352
self.assertDictListMatch(bdms, expected_result)
3354
self.compute_api._update_block_device_mapping(
3355
self.context, instance_types.get_default_instance_type(),
3356
instance['uuid'], block_device_mapping)
3357
bdms = [self._parse_db_block_device_mapping(bdm_ref)
3358
for bdm_ref in db.block_device_mapping_get_all_by_instance(
3359
self.context, instance['uuid'])]
3361
{'snapshot_id': '00000000-aaaa-bbbb-cccc-000000000000',
3362
'device_name': '/dev/sda1'},
3364
{'virtual_name': 'swap', 'device_name': '/dev/sdb1',
3365
'volume_size': swap_size},
3366
{'snapshot_id': '11111111-aaaa-bbbb-cccc-111111111111',
3367
'device_name': '/dev/sdb2'},
3368
{'snapshot_id': '22222222-aaaa-bbbb-cccc-222222222222',
3369
'device_name': '/dev/sdb3'},
3370
{'no_device': True, 'device_name': '/dev/sdb4'},
3372
{'virtual_name': 'ephemeral0', 'device_name': '/dev/sdc1'},
3373
{'snapshot_id': '33333333-aaaa-bbbb-cccc-333333333333',
3374
'device_name': '/dev/sdc2'},
3375
{'snapshot_id': '44444444-aaaa-bbbb-cccc-444444444444',
3376
'device_name': '/dev/sdc3'},
3377
{'no_device': True, 'device_name': '/dev/sdc4'},
3379
{'snapshot_id': '55555555-aaaa-bbbb-cccc-555555555555',
3380
'device_name': '/dev/sdd1'},
3381
{'snapshot_id': '66666666-aaaa-bbbb-cccc-666666666666',
3382
'device_name': '/dev/sdd2'},
3383
{'snapshot_id': '77777777-aaaa-bbbb-cccc-777777777777',
3384
'device_name': '/dev/sdd3'},
3385
{'no_device': True, 'device_name': '/dev/sdd4'}]
3387
expected_result.sort()
3388
self.assertDictListMatch(bdms, expected_result)
3390
for bdm in db.block_device_mapping_get_all_by_instance(
3391
self.context, instance['uuid']):
3392
db.block_device_mapping_destroy(self.context, bdm['id'])
3393
instance = db.instance_get_by_uuid(self.context, instance['uuid'])
3394
self.compute.terminate_instance(self.context, instance['uuid'])
3396
def test_volume_size(self):
3399
inst_type = {'ephemeral_gb': ephemeral_size, 'swap': swap_size}
3400
self.assertEqual(self.compute_api._volume_size(inst_type,
3403
self.assertEqual(self.compute_api._volume_size(inst_type,
3406
self.assertEqual(self.compute_api._volume_size(inst_type,
3410
def test_reservation_id_one_instance(self):
3411
"""Verify building an instance has a reservation_id that
3412
matches return value from create"""
3413
(refs, resv_id) = self.compute_api.create(self.context,
3414
instance_types.get_default_instance_type(), None)
3416
self.assertEqual(len(refs), 1)
3417
self.assertEqual(refs[0]['reservation_id'], resv_id)
3419
db.instance_destroy(self.context, refs[0]['id'])
3421
def test_reservation_ids_two_instances(self):
3422
"""Verify building 2 instances at once results in a
3423
reservation_id being returned equal to reservation id set
3426
(refs, resv_id) = self.compute_api.create(self.context,
3427
instance_types.get_default_instance_type(), None,
3428
min_count=2, max_count=2)
3430
self.assertEqual(len(refs), 2)
3431
self.assertNotEqual(resv_id, None)
3433
for instance in refs:
3434
self.assertEqual(instance['reservation_id'], resv_id)
3436
db.instance_destroy(self.context, refs[0]['id'])
3438
def test_instance_name_template(self):
3439
"""Test the instance_name template"""
3440
self.flags(instance_name_template='instance-%d')
3441
i_ref = self._create_fake_instance()
3442
self.assertEqual(i_ref['name'], 'instance-%d' % i_ref['id'])
3443
db.instance_destroy(self.context, i_ref['id'])
3445
self.flags(instance_name_template='instance-%(uuid)s')
3446
i_ref = self._create_fake_instance()
3447
self.assertEqual(i_ref['name'], 'instance-%s' % i_ref['uuid'])
3448
db.instance_destroy(self.context, i_ref['id'])
3450
self.flags(instance_name_template='%(id)d-%(uuid)s')
3451
i_ref = self._create_fake_instance()
3452
self.assertEqual(i_ref['name'], '%d-%s' %
3453
(i_ref['id'], i_ref['uuid']))
3454
db.instance_destroy(self.context, i_ref['id'])
3456
# not allowed.. default is uuid
3457
self.flags(instance_name_template='%(name)s')
3458
i_ref = self._create_fake_instance()
3459
self.assertEqual(i_ref['name'], i_ref['uuid'])
3460
db.instance_destroy(self.context, i_ref['id'])
3462
def test_add_remove_fixed_ip(self):
3463
instance = self._create_fake_instance()
3464
self.compute_api.add_fixed_ip(self.context, instance, '1')
3465
self.compute_api.remove_fixed_ip(self.context, instance, '192.168.1.1')
3466
self.compute_api.delete(self.context, instance)
3468
def test_attach_volume_invalid(self):
3469
self.assertRaises(exception.InvalidDevicePath,
3470
self.compute_api.attach_volume,
3476
def test_vnc_console(self):
3477
"""Make sure we can a vnc console for an instance."""
3479
fake_instance = {'uuid': 'fake_uuid',
3480
'host': 'fake_compute_host'}
3481
fake_console_type = "novnc"
3482
fake_connect_info = {'token': 'fake_token',
3483
'console_type': fake_console_type,
3484
'host': 'fake_console_host',
3485
'port': 'fake_console_port',
3486
'internal_access_path': 'fake_access_path'}
3487
fake_connect_info2 = copy.deepcopy(fake_connect_info)
3488
fake_connect_info2['access_url'] = 'fake_console_url'
3490
self.mox.StubOutWithMock(rpc, 'call')
3492
rpc_msg1 = {'method': 'get_vnc_console',
3493
'args': {'instance_uuid': fake_instance['uuid'],
3494
'console_type': fake_console_type},
3495
'version': compute_rpcapi.ComputeAPI.RPC_API_VERSION}
3496
rpc_msg2 = {'method': 'authorize_console',
3497
'args': fake_connect_info,
3500
rpc.call(self.context, 'compute.%s' % fake_instance['host'],
3501
rpc_msg1, None).AndReturn(fake_connect_info2)
3502
rpc.call(self.context, FLAGS.consoleauth_topic,
3503
rpc_msg2, None).AndReturn(None)
3505
self.mox.ReplayAll()
3507
console = self.compute_api.get_vnc_console(self.context,
3508
fake_instance, fake_console_type)
3509
self.assertEqual(console, {'url': 'fake_console_url'})
3511
def test_console_output(self):
3512
fake_instance = {'uuid': 'fake_uuid',
3513
'host': 'fake_compute_host'}
3514
fake_tail_length = 699
3515
fake_console_output = 'fake console output'
3517
self.mox.StubOutWithMock(rpc, 'call')
3519
rpc_msg = {'method': 'get_console_output',
3520
'args': {'instance_uuid': fake_instance['uuid'],
3521
'tail_length': fake_tail_length},
3522
'version': compute_rpcapi.ComputeAPI.RPC_API_VERSION}
3523
rpc.call(self.context, 'compute.%s' % fake_instance['host'],
3524
rpc_msg, None).AndReturn(fake_console_output)
3526
self.mox.ReplayAll()
3528
output = self.compute_api.get_console_output(self.context,
3529
fake_instance, tail_length=fake_tail_length)
3530
self.assertEqual(output, fake_console_output)
3532
def test_attach_volume(self):
3533
"""Ensure instance can be soft rebooted"""
3535
def fake_check_attach(*args, **kwargs):
3538
def fake_reserve_volume(*args, **kwargs):
3541
def fake_volume_get(self, context, volume_id):
3542
return {'id': volume_id}
3544
self.stubs.Set(nova.volume.api.API, 'get', fake_volume_get)
3545
self.stubs.Set(nova.volume.api.API, 'check_attach', fake_check_attach)
3546
self.stubs.Set(nova.volume.api.API, 'reserve_volume',
3547
fake_reserve_volume)
3548
instance = self._create_fake_instance()
3549
self.compute_api.attach_volume(self.context, instance, 1, '/dev/vdb')
3551
def test_inject_network_info(self):
3552
instance = self._create_fake_instance()
3553
self.compute.run_instance(self.context, instance['uuid'])
3554
instance = self.compute_api.get(self.context, instance['uuid'])
3555
self.compute_api.inject_network_info(self.context, instance)
3556
self.compute_api.delete(self.context, instance)
3558
def test_reset_network(self):
3559
instance = self._create_fake_instance()
3560
self.compute.run_instance(self.context, instance['uuid'])
3561
instance = self.compute_api.get(self.context, instance['uuid'])
3562
self.compute_api.reset_network(self.context, instance)
3564
def test_lock(self):
3565
instance = self._create_fake_instance()
3566
self.compute_api.lock(self.context, instance)
3567
self.compute_api.delete(self.context, instance)
3569
def test_unlock(self):
3570
instance = self._create_fake_instance()
3571
self.compute_api.unlock(self.context, instance)
3572
self.compute_api.delete(self.context, instance)
3574
def test_get_lock(self):
3575
instance = self._create_fake_instance()
3576
self.assertFalse(self.compute_api.get_lock(self.context, instance))
3577
db.instance_update(self.context, instance['uuid'], {'locked': True})
3578
self.assertTrue(self.compute_api.get_lock(self.context, instance))
3580
def test_add_remove_security_group(self):
3581
instance = self._create_fake_instance()
3583
self.compute.run_instance(self.context, instance['uuid'])
3584
instance = self.compute_api.get(self.context, instance['uuid'])
3585
security_group_name = self._create_group()['name']
3586
self.compute_api.add_security_group(self.context,
3588
security_group_name)
3589
self.compute_api.remove_security_group(self.context,
3591
security_group_name)
3593
def test_get_diagnostics(self):
3594
instance = self._create_fake_instance()
3595
self.compute_api.get_diagnostics(self.context, instance)
3596
self.compute_api.delete(self.context, instance)
3598
def test_inject_file(self):
3599
"""Ensure we can write a file to an instance"""
3600
instance = self._create_fake_instance()
3601
self.compute_api.inject_file(self.context, instance,
3602
"/tmp/test", "File Contents")
3603
db.instance_destroy(self.context, instance['id'])
3606
def fake_rpc_method(context, topic, msg, do_cast=True):
3610
def _create_service_entries(context, values={'avail_zone1': ['fake_host1',
3612
'avail_zone2': ['fake_host3'], }):
3613
for avail_zone, hosts in values.iteritems():
3615
db.service_create(context,
3617
'binary': 'nova-compute',
3620
'availability_zone': avail_zone})
3624
class ComputeAPIAggrTestCase(test.TestCase):
3625
"""This is for unit coverage of aggregate-related methods
3626
defined in nova.compute.api."""
3629
super(ComputeAPIAggrTestCase, self).setUp()
3630
self.api = compute_api.AggregateAPI()
3631
self.context = context.get_admin_context()
3632
self.stubs.Set(rpc, 'call', fake_rpc_method)
3633
self.stubs.Set(rpc, 'cast', fake_rpc_method)
3635
def test_create_invalid_availability_zone(self):
3636
"""Ensure InvalidAggregateAction is raised with wrong avail_zone."""
3637
self.assertRaises(exception.InvalidAggregateAction,
3638
self.api.create_aggregate,
3639
self.context, 'fake_aggr', 'fake_avail_zone')
3641
def test_update_aggregate_metadata(self):
3642
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
3643
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
3645
metadata = {'foo_key1': 'foo_value1',
3646
'foo_key2': 'foo_value2', }
3647
aggr = self.api.update_aggregate_metadata(self.context, aggr['id'],
3649
metadata['foo_key1'] = None
3650
expected = self.api.update_aggregate_metadata(self.context,
3651
aggr['id'], metadata)
3652
self.assertDictMatch(expected['metadata'], {'foo_key2': 'foo_value2'})
3654
def test_delete_aggregate(self):
3655
"""Ensure we can delete an aggregate."""
3656
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
3657
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
3659
self.api.delete_aggregate(self.context, aggr['id'])
3660
expected = db.aggregate_get(self.context.elevated(read_deleted='yes'),
3662
self.assertNotEqual(aggr['operational_state'],
3663
expected['operational_state'])
3665
def test_delete_non_empty_aggregate(self):
3666
"""Ensure InvalidAggregateAction is raised when non empty aggregate."""
3667
_create_service_entries(self.context,
3668
{'fake_availability_zone': ['fake_host']})
3669
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
3670
'fake_availability_zone')
3671
self.api.add_host_to_aggregate(self.context, aggr['id'], 'fake_host')
3672
self.assertRaises(exception.InvalidAggregateAction,
3673
self.api.delete_aggregate, self.context, aggr['id'])
3675
def test_add_host_to_aggregate(self):
3676
"""Ensure we can add a host to an aggregate."""
3677
values = _create_service_entries(self.context)
3678
fake_zone = values.keys()[0]
3679
fake_host = values[fake_zone][0]
3680
aggr = self.api.create_aggregate(self.context,
3681
'fake_aggregate', fake_zone)
3682
aggr = self.api.add_host_to_aggregate(self.context,
3683
aggr['id'], fake_host)
3684
self.assertEqual(aggr['operational_state'], aggregate_states.CHANGING)
3686
def test_add_host_to_aggregate_multiple(self):
3687
"""Ensure we can add multiple hosts to an aggregate."""
3688
values = _create_service_entries(self.context)
3689
fake_zone = values.keys()[0]
3690
aggr = self.api.create_aggregate(self.context,
3691
'fake_aggregate', fake_zone)
3692
# let's mock the fact that the aggregate is active already!
3693
status = {'operational_state': aggregate_states.ACTIVE}
3694
db.aggregate_update(self.context, aggr['id'], status)
3695
for host in values[fake_zone]:
3696
aggr = self.api.add_host_to_aggregate(self.context,
3698
self.assertEqual(len(aggr['hosts']), len(values[fake_zone]))
3699
self.assertEqual(aggr['operational_state'],
3700
aggregate_states.ACTIVE)
3702
def test_add_host_to_aggregate_invalid_changing_status(self):
3703
"""Ensure InvalidAggregateAction is raised when adding host while
3704
aggregate is not ready."""
3705
values = _create_service_entries(self.context)
3706
fake_zone = values.keys()[0]
3707
fake_host = values[fake_zone][0]
3708
aggr = self.api.create_aggregate(self.context,
3709
'fake_aggregate', fake_zone)
3710
aggr = self.api.add_host_to_aggregate(self.context,
3711
aggr['id'], fake_host)
3712
self.assertEqual(aggr['operational_state'],
3713
aggregate_states.CHANGING)
3714
self.assertRaises(exception.InvalidAggregateAction,
3715
self.api.add_host_to_aggregate, self.context,
3716
aggr['id'], fake_host)
3718
def test_add_host_to_aggregate_invalid_dismissed_status(self):
3719
"""Ensure InvalidAggregateAction is raised when aggregate is
3721
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
3722
aggr = self.api.create_aggregate(self.context,
3723
'fake_aggregate', 'fake_zone')
3724
# let's mock the fact that the aggregate is dismissed!
3725
status = {'operational_state': aggregate_states.DISMISSED}
3726
db.aggregate_update(self.context, aggr['id'], status)
3727
self.assertRaises(exception.InvalidAggregateAction,
3728
self.api.add_host_to_aggregate, self.context,
3729
aggr['id'], 'fake_host')
3731
def test_add_host_to_aggregate_invalid_error_status(self):
3732
"""Ensure InvalidAggregateAction is raised when aggregate is
3734
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
3735
aggr = self.api.create_aggregate(self.context,
3736
'fake_aggregate', 'fake_zone')
3737
# let's mock the fact that the aggregate is in error!
3738
status = {'operational_state': aggregate_states.ERROR}
3739
db.aggregate_update(self.context, aggr['id'], status)
3740
self.assertRaises(exception.InvalidAggregateAction,
3741
self.api.add_host_to_aggregate, self.context,
3742
aggr['id'], 'fake_host')
3744
def test_add_host_to_aggregate_zones_mismatch(self):
3745
"""Ensure InvalidAggregateAction is raised when zones don't match."""
3746
_create_service_entries(self.context, {'fake_zoneX': ['fake_host1'],
3747
'fake_zoneY': ['fake_host2']})
3748
aggr = self.api.create_aggregate(self.context,
3749
'fake_aggregate', 'fake_zoneY')
3750
self.assertRaises(exception.InvalidAggregateAction,
3751
self.api.add_host_to_aggregate,
3752
self.context, aggr['id'], 'fake_host1')
3754
def test_add_host_to_aggregate_raise_not_found(self):
3755
"""Ensure ComputeHostNotFound is raised when adding invalid host."""
3756
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
3757
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
3759
self.assertRaises(exception.ComputeHostNotFound,
3760
self.api.add_host_to_aggregate,
3761
self.context, aggr['id'], 'invalid_host')
3763
def test_remove_host_from_aggregate_active(self):
3764
"""Ensure we can remove a host from an aggregate."""
3765
values = _create_service_entries(self.context)
3766
fake_zone = values.keys()[0]
3767
aggr = self.api.create_aggregate(self.context,
3768
'fake_aggregate', fake_zone)
3769
# let's mock the fact that the aggregate is active already!
3770
status = {'operational_state': aggregate_states.ACTIVE}
3771
db.aggregate_update(self.context, aggr['id'], status)
3772
for host in values[fake_zone]:
3773
aggr = self.api.add_host_to_aggregate(self.context,
3775
expected = self.api.remove_host_from_aggregate(self.context,
3777
values[fake_zone][0])
3778
self.assertEqual(len(aggr['hosts']) - 1, len(expected['hosts']))
3779
self.assertEqual(expected['operational_state'],
3780
aggregate_states.ACTIVE)
3782
def test_remove_host_from_aggregate_error(self):
3783
"""Ensure we can remove a host from an aggregate even if in error."""
3784
values = _create_service_entries(self.context)
3785
fake_zone = values.keys()[0]
3786
aggr = self.api.create_aggregate(self.context,
3787
'fake_aggregate', fake_zone)
3788
# let's mock the fact that the aggregate is ready!
3789
status = {'operational_state': aggregate_states.ACTIVE}
3790
db.aggregate_update(self.context, aggr['id'], status)
3791
for host in values[fake_zone]:
3792
aggr = self.api.add_host_to_aggregate(self.context,
3794
# let's mock the fact that the aggregate is in error!
3795
status = {'operational_state': aggregate_states.ERROR}
3796
expected = self.api.remove_host_from_aggregate(self.context,
3798
values[fake_zone][0])
3799
self.assertEqual(len(aggr['hosts']) - 1, len(expected['hosts']))
3800
self.assertEqual(expected['operational_state'],
3801
aggregate_states.ACTIVE)
3803
def test_remove_host_from_aggregate_invalid_dismissed_status(self):
3804
"""Ensure InvalidAggregateAction is raised when aggregate is
3806
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
3807
aggr = self.api.create_aggregate(self.context,
3808
'fake_aggregate', 'fake_zone')
3809
# let's mock the fact that the aggregate is dismissed!
3810
status = {'operational_state': aggregate_states.DISMISSED}
3811
db.aggregate_update(self.context, aggr['id'], status)
3812
self.assertRaises(exception.InvalidAggregateAction,
3813
self.api.remove_host_from_aggregate, self.context,
3814
aggr['id'], 'fake_host')
3816
def test_remove_host_from_aggregate_invalid_changing_status(self):
3817
"""Ensure InvalidAggregateAction is raised when aggregate is
3819
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
3820
aggr = self.api.create_aggregate(self.context,
3821
'fake_aggregate', 'fake_zone')
3822
# let's mock the fact that the aggregate is changing!
3823
status = {'operational_state': aggregate_states.CHANGING}
3824
db.aggregate_update(self.context, aggr['id'], status)
3825
self.assertRaises(exception.InvalidAggregateAction,
3826
self.api.remove_host_from_aggregate, self.context,
3827
aggr['id'], 'fake_host')
3829
def test_remove_host_from_aggregate_raise_not_found(self):
3830
"""Ensure ComputeHostNotFound is raised when removing invalid host."""
3831
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
3832
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
3834
self.assertRaises(exception.ComputeHostNotFound,
3835
self.api.remove_host_from_aggregate,
3836
self.context, aggr['id'], 'invalid_host')
3839
class ComputeAggrTestCase(BaseTestCase):
3840
"""This is for unit coverage of aggregate-related methods
3841
defined in nova.compute.manager."""
3844
super(ComputeAggrTestCase, self).setUp()
3845
self.context = context.get_admin_context()
3846
values = {'name': 'test_aggr',
3847
'availability_zone': 'test_zone', }
3848
self.aggr = db.aggregate_create(self.context, values)
3850
def test_add_aggregate_host(self):
3851
def fake_driver_add_to_aggregate(context, aggregate, host):
3852
fake_driver_add_to_aggregate.called = True
3853
return {"foo": "bar"}
3854
self.stubs.Set(self.compute.driver, "add_to_aggregate",
3855
fake_driver_add_to_aggregate)
3857
self.compute.add_aggregate_host(self.context, self.aggr.id, "host")
3858
self.assertTrue(fake_driver_add_to_aggregate.called)
3860
def test_add_aggregate_host_raise_err(self):
3861
"""Ensure the undo operation works correctly on add."""
3862
def fake_driver_add_to_aggregate(context, aggregate, host):
3863
raise exception.AggregateError
3864
self.stubs.Set(self.compute.driver, "add_to_aggregate",
3865
fake_driver_add_to_aggregate)
3867
state = {'operational_state': aggregate_states.ACTIVE}
3868
db.aggregate_update(self.context, self.aggr.id, state)
3869
db.aggregate_host_add(self.context, self.aggr.id, 'fake_host')
3871
self.assertRaises(exception.AggregateError,
3872
self.compute.add_aggregate_host,
3873
self.context, self.aggr.id, "fake_host")
3874
excepted = db.aggregate_get(self.context, self.aggr.id)
3875
self.assertEqual(excepted.operational_state, aggregate_states.ERROR)
3876
self.assertEqual(excepted.hosts, [])
3878
def test_remove_aggregate_host(self):
3879
def fake_driver_remove_from_aggregate(context, aggregate, host):
3880
fake_driver_remove_from_aggregate.called = True
3881
self.assertEqual("host", host, "host")
3882
return {"foo": "bar"}
3883
self.stubs.Set(self.compute.driver, "remove_from_aggregate",
3884
fake_driver_remove_from_aggregate)
3886
self.compute.remove_aggregate_host(self.context, self.aggr.id, "host")
3887
self.assertTrue(fake_driver_remove_from_aggregate.called)
3889
def test_remove_aggregate_host_raise_err(self):
3890
"""Ensure the undo operation works correctly on remove."""
3891
def fake_driver_remove_from_aggregate(context, aggregate, host):
3892
raise exception.AggregateError
3893
self.stubs.Set(self.compute.driver, "remove_from_aggregate",
3894
fake_driver_remove_from_aggregate)
3896
state = {'operational_state': aggregate_states.ACTIVE}
3897
db.aggregate_update(self.context, self.aggr.id, state)
3899
self.assertRaises(exception.AggregateError,
3900
self.compute.remove_aggregate_host,
3901
self.context, self.aggr.id, "fake_host")
3902
excepted = db.aggregate_get(self.context, self.aggr.id)
3903
self.assertEqual(excepted.operational_state, aggregate_states.ERROR)
3904
self.assertEqual(excepted.hosts, ['fake_host'])
3907
class ComputePolicyTestCase(BaseTestCase):
3910
super(ComputePolicyTestCase, self).setUp()
3914
self.compute_api = compute.API()
3917
super(ComputePolicyTestCase, self).tearDown()
3920
def _set_rules(self, rules):
3921
nova.common.policy.set_brain(nova.common.policy.HttpBrain(rules))
3923
def test_actions_are_prefixed(self):
3924
self.mox.StubOutWithMock(nova.policy, 'enforce')
3925
nova.policy.enforce(self.context, 'compute:reboot', {})
3926
self.mox.ReplayAll()
3927
nova.compute.api.check_policy(self.context, 'reboot', {})
3929
def test_wrapped_method(self):
3930
instance = self._create_fake_instance()
3931
# Reset this to None for this policy check. If it's set, it
3932
# tries to do a compute_api.update() and we're not testing for
3934
instance['host'] = None
3935
self.compute.run_instance(self.context, instance['uuid'])
3937
# force delete to fail
3938
rules = {"compute:delete": [["false:false"]]}
3939
self._set_rules(rules)
3941
self.assertRaises(exception.PolicyNotAuthorized,
3942
self.compute_api.delete, self.context, instance)
3944
# reset rules to allow deletion
3945
rules = {"compute:delete": []}
3946
self._set_rules(rules)
3948
self.compute_api.delete(self.context, instance)
3950
def test_create_fail(self):
3951
rules = {"compute:create": [["false:false"]]}
3952
self._set_rules(rules)
3954
self.assertRaises(exception.PolicyNotAuthorized,
3955
self.compute_api.create, self.context, '1', '1')
3957
def test_create_attach_volume_fail(self):
3959
"compute:create": [],
3960
"compute:create:attach_network": [["false:false"]],
3961
"compute:create:attach_volume": [],
3963
self._set_rules(rules)
3965
self.assertRaises(exception.PolicyNotAuthorized,
3966
self.compute_api.create, self.context, '1', '1',
3967
requested_networks='blah',
3968
block_device_mapping='blah')
3970
def test_create_attach_network_fail(self):
3972
"compute:create": [],
3973
"compute:create:attach_network": [],
3974
"compute:create:attach_volume": [["false:false"]],
3976
self._set_rules(rules)
3978
self.assertRaises(exception.PolicyNotAuthorized,
3979
self.compute_api.create, self.context, '1', '1',
3980
requested_networks='blah',
3981
block_device_mapping='blah')
3983
def test_get_fail(self):
3984
instance = self._create_fake_instance()
3987
"compute:get": [["false:false"]],
3989
self._set_rules(rules)
3991
self.assertRaises(exception.PolicyNotAuthorized,
3992
self.compute_api.get, self.context, instance['uuid'])
3994
def test_get_all_fail(self):
3996
"compute:get_all": [["false:false"]],
3998
self._set_rules(rules)
4000
self.assertRaises(exception.PolicyNotAuthorized,
4001
self.compute_api.get_all, self.context)
4003
def test_get_instance_faults(self):
4004
instance1 = self._create_fake_instance()
4005
instance2 = self._create_fake_instance()
4006
instances = [instance1, instance2]
4009
"compute:get_instance_faults": [["false:false"]],
4011
self._set_rules(rules)
4013
self.assertRaises(exception.PolicyNotAuthorized,
4014
self.compute_api.get_instance_faults,
4015
self.context, instances)
4018
class ComputeHostAPITestCase(BaseTestCase):
4020
super(ComputeHostAPITestCase, self).setUp()
4021
self.host_api = compute_api.HostAPI()
4023
def _rpc_call_stub(self, call_info):
4024
def fake_rpc_call(context, topic, msg, timeout=None):
4025
call_info['context'] = context
4026
call_info['topic'] = topic
4027
call_info['msg'] = msg
4028
self.stubs.Set(rpc, 'call', fake_rpc_call)
4030
def test_set_host_enabled(self):
4031
ctxt = context.RequestContext('fake', 'fake')
4033
self._rpc_call_stub(call_info)
4035
self.host_api.set_host_enabled(ctxt, 'fake_host', 'fake_enabled')
4036
self.assertEqual(call_info['context'], ctxt)
4037
self.assertEqual(call_info['topic'], 'compute.fake_host')
4038
self.assertEqual(call_info['msg'],
4039
{'method': 'set_host_enabled',
4040
'args': {'enabled': 'fake_enabled'},
4041
'version': compute_rpcapi.ComputeAPI.RPC_API_VERSION})
4043
def test_host_power_action(self):
4044
ctxt = context.RequestContext('fake', 'fake')
4046
self._rpc_call_stub(call_info)
4047
self.host_api.host_power_action(ctxt, 'fake_host', 'fake_action')
4048
self.assertEqual(call_info['context'], ctxt)
4049
self.assertEqual(call_info['topic'], 'compute.fake_host')
4050
self.assertEqual(call_info['msg'],
4051
{'method': 'host_power_action',
4052
'args': {'action': 'fake_action'},
4053
'version': compute_rpcapi.ComputeAPI.RPC_API_VERSION})
4055
def test_set_host_maintenance(self):
4056
ctxt = context.RequestContext('fake', 'fake')
4058
self._rpc_call_stub(call_info)
4059
self.host_api.set_host_maintenance(ctxt, 'fake_host', 'fake_mode')
4060
self.assertEqual(call_info['context'], ctxt)
4061
self.assertEqual(call_info['topic'], 'compute.fake_host')
4062
self.assertEqual(call_info['msg'],
4063
{'method': 'host_maintenance_mode',
4064
'args': {'host': 'fake_host', 'mode': 'fake_mode'},
4065
'version': compute_rpcapi.ComputeAPI.RPC_API_VERSION})
4068
class KeypairAPITestCase(BaseTestCase):
4070
super(KeypairAPITestCase, self).setUp()
4071
self.keypair_api = compute_api.KeypairAPI()
4072
self.ctxt = context.RequestContext('fake', 'fake')
4073
self._keypair_db_call_stubs()
4074
self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLnVkqJu9WVf' \
4075
'/5StU3JCrBR2r1s1j8K1tux+5XeSvdqaM8lMFNorzbY5iyoBbRS56gy' \
4076
'1jmm43QsMPJsrpfUZKcJpRENSe3OxIIwWXRoiapZe78u/a9xKwj0avF' \
4077
'YMcws9Rk9iAB7W4K1nEJbyCPl5lRBoyqeHBqrnnuXWEgGxJCK0Ah6wc' \
4078
'OzwlEiVjdf4kxzXrwPHyi7Ea1qvnNXTziF8yYmUlH4C8UXfpTQckwSw' \
4079
'pDyxZUc63P8q+vPbs3Q2kw+/7vvkCKHJAXVI+oCiyMMfffoTq16M1xf' \
4080
'V58JstgtTqAXG+ZFpicGajREUE/E3hO5MGgcHmyzIrWHKpe1n3oEGuz'
4081
self.fingerprint = '4e:48:c6:a0:4a:f9:dd:b5:4c:85:54:5a:af:43:47:5a'
4083
def _keypair_db_call_stubs(self):
4085
def db_key_pair_get_all_by_user(self, user_id):
4088
def db_key_pair_create(self, keypair):
4091
def db_key_pair_destroy(context, user_id, name):
4094
self.stubs.Set(db, "key_pair_get_all_by_user",
4095
db_key_pair_get_all_by_user)
4096
self.stubs.Set(db, "key_pair_create",
4098
self.stubs.Set(db, "key_pair_destroy",
4099
db_key_pair_destroy)
4101
def test_create_keypair(self):
4102
keypair = self.keypair_api.create_key_pair(self.ctxt,
4103
self.ctxt.user_id, 'foo')
4104
self.assertEqual('foo', keypair['name'])
4106
def test_create_keypair_name_too_long(self):
4107
self.assertRaises(exception.InvalidKeypair,
4108
self.keypair_api.create_key_pair,
4109
self.ctxt, self.ctxt.user_id, 'x' * 256)
4111
def test_create_keypair_invalid_chars(self):
4112
self.assertRaises(exception.InvalidKeypair,
4113
self.keypair_api.create_key_pair,
4114
self.ctxt, self.ctxt.user_id, '* BAD CHARACTERS! *')
4116
def test_create_keypair_already_exists(self):
4117
def db_key_pair_get(context, user_id, name):
4119
self.stubs.Set(db, "key_pair_get",
4121
self.assertRaises(exception.KeyPairExists,
4122
self.keypair_api.create_key_pair,
4123
self.ctxt, self.ctxt.user_id, 'foo')
4125
def test_create_keypair_quota_limit(self):
4126
def db_key_pair_count_by_user_max(self, user_id):
4127
return FLAGS.quota_key_pairs
4128
self.stubs.Set(db, "key_pair_count_by_user",
4129
db_key_pair_count_by_user_max)
4130
self.assertRaises(exception.KeypairLimitExceeded,
4131
self.keypair_api.create_key_pair,
4132
self.ctxt, self.ctxt.user_id, 'foo')
4134
def test_import_keypair(self):
4135
keypair = self.keypair_api.import_key_pair(self.ctxt,
4139
self.assertEqual('foo', keypair['name'])
4140
self.assertEqual(self.fingerprint, keypair['fingerprint'])
4141
self.assertEqual(self.pub_key, keypair['public_key'])
4143
def test_import_keypair_bad_public_key(self):
4144
self.assertRaises(exception.InvalidKeypair,
4145
self.keypair_api.import_key_pair,
4146
self.ctxt, self.ctxt.user_id, 'foo', 'bad key data')
4148
def test_import_keypair_name_too_long(self):
4149
self.assertRaises(exception.InvalidKeypair,
4150
self.keypair_api.import_key_pair,
4151
self.ctxt, self.ctxt.user_id, 'x' * 256,
4154
def test_import_keypair_invalid_chars(self):
4155
self.assertRaises(exception.InvalidKeypair,
4156
self.keypair_api.import_key_pair,
4157
self.ctxt, self.ctxt.user_id,
4158
'* BAD CHARACTERS! *', self.pub_key)
4160
def test_import_keypair_quota_limit(self):
4161
def db_key_pair_count_by_user_max(self, user_id):
4162
return FLAGS.quota_key_pairs
4163
self.stubs.Set(db, "key_pair_count_by_user",
4164
db_key_pair_count_by_user_max)
4165
self.assertRaises(exception.KeypairLimitExceeded,
4166
self.keypair_api.import_key_pair,
4167
self.ctxt, self.ctxt.user_id, 'foo', self.pub_key)