OpenStack Compute (Nova)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2763 lines
131KB

  1. # Copyright 2012 IBM Corp.
  2. # Copyright 2013 Red Hat, Inc.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. """Tests for the conductor service."""
  16. import copy
  17. import mock
  18. from mox3 import mox
  19. import oslo_messaging as messaging
  20. from oslo_serialization import jsonutils
  21. from oslo_utils import timeutils
  22. from oslo_versionedobjects import exception as ovo_exc
  23. import six
  24. from nova import block_device
  25. from nova.compute import flavors
  26. from nova.compute import rpcapi as compute_rpcapi
  27. from nova.compute import task_states
  28. from nova.compute import vm_states
  29. from nova.conductor import api as conductor_api
  30. from nova.conductor import manager as conductor_manager
  31. from nova.conductor import rpcapi as conductor_rpcapi
  32. from nova.conductor.tasks import live_migrate
  33. from nova.conductor.tasks import migrate
  34. from nova import conf
  35. from nova import context
  36. from nova import db
  37. from nova.db.sqlalchemy import api as db_api
  38. from nova.db.sqlalchemy import api_models
  39. from nova import exception as exc
  40. from nova.image import api as image_api
  41. from nova import objects
  42. from nova.objects import base as obj_base
  43. from nova.objects import fields
  44. from nova import rpc
  45. from nova.scheduler import client as scheduler_client
  46. from nova.scheduler import utils as scheduler_utils
  47. from nova import test
  48. from nova.tests import fixtures
  49. from nova.tests.unit.api.openstack import fakes
  50. from nova.tests.unit import cast_as_call
  51. from nova.tests.unit.compute import test_compute
  52. from nova.tests.unit import fake_build_request
  53. from nova.tests.unit import fake_instance
  54. from nova.tests.unit import fake_notifier
  55. from nova.tests.unit import fake_request_spec
  56. from nova.tests.unit import fake_server_actions
  57. from nova.tests.unit import utils as test_utils
  58. from nova.tests import uuidsentinel as uuids
  59. from nova import utils
  60. CONF = conf.CONF
  61. class FakeContext(context.RequestContext):
  62. def elevated(self):
  63. """Return a consistent elevated context so we can detect it."""
  64. if not hasattr(self, '_elevated'):
  65. self._elevated = super(FakeContext, self).elevated()
  66. return self._elevated
  67. class _BaseTestCase(object):
  68. def setUp(self):
  69. super(_BaseTestCase, self).setUp()
  70. self.user_id = fakes.FAKE_USER_ID
  71. self.project_id = fakes.FAKE_PROJECT_ID
  72. self.context = FakeContext(self.user_id, self.project_id)
  73. fake_notifier.stub_notifier(self)
  74. self.addCleanup(fake_notifier.reset)
  75. def fake_deserialize_context(serializer, ctxt_dict):
  76. self.assertEqual(self.context.user_id, ctxt_dict['user_id'])
  77. self.assertEqual(self.context.project_id, ctxt_dict['project_id'])
  78. return self.context
  79. self.stubs.Set(rpc.RequestContextSerializer, 'deserialize_context',
  80. fake_deserialize_context)
  81. self.useFixture(fixtures.SpawnIsSynchronousFixture())
  82. class ConductorTestCase(_BaseTestCase, test.TestCase):
  83. """Conductor Manager Tests."""
  84. def setUp(self):
  85. super(ConductorTestCase, self).setUp()
  86. self.conductor = conductor_manager.ConductorManager()
  87. self.conductor_manager = self.conductor
  88. def _test_object_action(self, is_classmethod, raise_exception):
  89. class TestObject(obj_base.NovaObject):
  90. def foo(self, raise_exception=False):
  91. if raise_exception:
  92. raise Exception('test')
  93. else:
  94. return 'test'
  95. @classmethod
  96. def bar(cls, context, raise_exception=False):
  97. if raise_exception:
  98. raise Exception('test')
  99. else:
  100. return 'test'
  101. obj_base.NovaObjectRegistry.register(TestObject)
  102. obj = TestObject()
  103. # NOTE(danms): After a trip over RPC, any tuple will be a list,
  104. # so use a list here to make sure we can handle it
  105. fake_args = []
  106. if is_classmethod:
  107. versions = {'TestObject': '1.0'}
  108. result = self.conductor.object_class_action_versions(
  109. self.context, TestObject.obj_name(), 'bar', versions,
  110. fake_args, {'raise_exception': raise_exception})
  111. else:
  112. updates, result = self.conductor.object_action(
  113. self.context, obj, 'foo', fake_args,
  114. {'raise_exception': raise_exception})
  115. self.assertEqual('test', result)
  116. def test_object_action(self):
  117. self._test_object_action(False, False)
  118. def test_object_action_on_raise(self):
  119. self.assertRaises(messaging.ExpectedException,
  120. self._test_object_action, False, True)
  121. def test_object_class_action(self):
  122. self._test_object_action(True, False)
  123. def test_object_class_action_on_raise(self):
  124. self.assertRaises(messaging.ExpectedException,
  125. self._test_object_action, True, True)
  126. def test_object_action_copies_object(self):
  127. class TestObject(obj_base.NovaObject):
  128. fields = {'dict': fields.DictOfStringsField()}
  129. def touch_dict(self):
  130. self.dict['foo'] = 'bar'
  131. self.obj_reset_changes()
  132. obj_base.NovaObjectRegistry.register(TestObject)
  133. obj = TestObject()
  134. obj.dict = {}
  135. obj.obj_reset_changes()
  136. updates, result = self.conductor.object_action(
  137. self.context, obj, 'touch_dict', tuple(), {})
  138. # NOTE(danms): If conductor did not properly copy the object, then
  139. # the new and reference copies of the nested dict object will be
  140. # the same, and thus 'dict' will not be reported as changed
  141. self.assertIn('dict', updates)
  142. self.assertEqual({'foo': 'bar'}, updates['dict'])
  143. def test_object_class_action_versions(self):
  144. @obj_base.NovaObjectRegistry.register
  145. class TestObject(obj_base.NovaObject):
  146. VERSION = '1.10'
  147. @classmethod
  148. def foo(cls, context):
  149. return cls()
  150. versions = {
  151. 'TestObject': '1.2',
  152. 'OtherObj': '1.0',
  153. }
  154. with mock.patch.object(self.conductor_manager,
  155. '_object_dispatch') as m:
  156. m.return_value = TestObject()
  157. m.return_value.obj_to_primitive = mock.MagicMock()
  158. self.conductor.object_class_action_versions(
  159. self.context, TestObject.obj_name(), 'foo', versions,
  160. tuple(), {})
  161. m.return_value.obj_to_primitive.assert_called_once_with(
  162. target_version='1.2', version_manifest=versions)
  163. def test_object_class_action_versions_old_object(self):
  164. # Make sure we return older than requested objects unmodified,
  165. # see bug #1596119.
  166. @obj_base.NovaObjectRegistry.register
  167. class TestObject(obj_base.NovaObject):
  168. VERSION = '1.10'
  169. @classmethod
  170. def foo(cls, context):
  171. return cls()
  172. versions = {
  173. 'TestObject': '1.10',
  174. 'OtherObj': '1.0',
  175. }
  176. with mock.patch.object(self.conductor_manager,
  177. '_object_dispatch') as m:
  178. m.return_value = TestObject()
  179. m.return_value.VERSION = '1.9'
  180. m.return_value.obj_to_primitive = mock.MagicMock()
  181. obj = self.conductor.object_class_action_versions(
  182. self.context, TestObject.obj_name(), 'foo', versions,
  183. tuple(), {})
  184. self.assertFalse(m.return_value.obj_to_primitive.called)
  185. self.assertEqual('1.9', obj.VERSION)
  186. def test_object_class_action_versions_major_version_diff(self):
  187. @obj_base.NovaObjectRegistry.register
  188. class TestObject(obj_base.NovaObject):
  189. VERSION = '2.10'
  190. @classmethod
  191. def foo(cls, context):
  192. return cls()
  193. versions = {
  194. 'TestObject': '2.10',
  195. 'OtherObj': '1.0',
  196. }
  197. with mock.patch.object(self.conductor_manager,
  198. '_object_dispatch') as m:
  199. m.return_value = TestObject()
  200. m.return_value.VERSION = '1.9'
  201. self.assertRaises(
  202. ovo_exc.InvalidTargetVersion,
  203. self.conductor.object_class_action_versions,
  204. self.context, TestObject.obj_name(), 'foo', versions,
  205. tuple(), {})
  206. def test_reset(self):
  207. with mock.patch.object(objects.Service, 'clear_min_version_cache'
  208. ) as mock_clear_cache:
  209. self.conductor.reset()
  210. mock_clear_cache.assert_called_once_with()
  211. def test_provider_fw_rule_get_all(self):
  212. result = self.conductor.provider_fw_rule_get_all(self.context)
  213. self.assertEqual([], result)
  214. class ConductorRPCAPITestCase(_BaseTestCase, test.TestCase):
  215. """Conductor RPC API Tests."""
  216. def setUp(self):
  217. super(ConductorRPCAPITestCase, self).setUp()
  218. self.conductor_service = self.start_service(
  219. 'conductor', manager='nova.conductor.manager.ConductorManager')
  220. self.conductor_manager = self.conductor_service.manager
  221. self.conductor = conductor_rpcapi.ConductorAPI()
  222. class ConductorAPITestCase(_BaseTestCase, test.TestCase):
  223. """Conductor API Tests."""
  224. def setUp(self):
  225. super(ConductorAPITestCase, self).setUp()
  226. self.conductor_service = self.start_service(
  227. 'conductor', manager='nova.conductor.manager.ConductorManager')
  228. self.conductor = conductor_api.API()
  229. self.conductor_manager = self.conductor_service.manager
  230. def test_wait_until_ready(self):
  231. timeouts = []
  232. calls = dict(count=0)
  233. def fake_ping(context, message, timeout):
  234. timeouts.append(timeout)
  235. calls['count'] += 1
  236. if calls['count'] < 15:
  237. raise messaging.MessagingTimeout("fake")
  238. self.stubs.Set(self.conductor.base_rpcapi, 'ping', fake_ping)
  239. self.conductor.wait_until_ready(self.context)
  240. self.assertEqual(timeouts.count(10), 10)
  241. self.assertIn(None, timeouts)
  242. class _BaseTaskTestCase(object):
  243. def setUp(self):
  244. super(_BaseTaskTestCase, self).setUp()
  245. self.user_id = fakes.FAKE_USER_ID
  246. self.project_id = fakes.FAKE_PROJECT_ID
  247. self.context = FakeContext(self.user_id, self.project_id)
  248. fake_server_actions.stub_out_action_events(self)
  249. def fake_deserialize_context(serializer, ctxt_dict):
  250. self.assertEqual(self.context.user_id, ctxt_dict['user_id'])
  251. self.assertEqual(self.context.project_id, ctxt_dict['project_id'])
  252. return self.context
  253. self.stubs.Set(rpc.RequestContextSerializer, 'deserialize_context',
  254. fake_deserialize_context)
  255. self.useFixture(fixtures.SpawnIsSynchronousFixture())
  256. def _prepare_rebuild_args(self, update_args=None):
  257. # Args that don't get passed in to the method but do get passed to RPC
  258. migration = update_args and update_args.pop('migration', None)
  259. node = update_args and update_args.pop('node', None)
  260. limits = update_args and update_args.pop('limits', None)
  261. rebuild_args = {'new_pass': 'admin_password',
  262. 'injected_files': 'files_to_inject',
  263. 'image_ref': 'image_ref',
  264. 'orig_image_ref': 'orig_image_ref',
  265. 'orig_sys_metadata': 'orig_sys_meta',
  266. 'bdms': {},
  267. 'recreate': False,
  268. 'on_shared_storage': False,
  269. 'preserve_ephemeral': False,
  270. 'host': 'compute-host',
  271. 'request_spec': None}
  272. if update_args:
  273. rebuild_args.update(update_args)
  274. compute_rebuild_args = copy.deepcopy(rebuild_args)
  275. compute_rebuild_args['migration'] = migration
  276. compute_rebuild_args['node'] = node
  277. compute_rebuild_args['limits'] = limits
  278. # Args that are passed in to the method but don't get passed to RPC
  279. compute_rebuild_args.pop('request_spec')
  280. return rebuild_args, compute_rebuild_args
  281. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  282. @mock.patch.object(objects.RequestSpec, 'save')
  283. @mock.patch.object(migrate.MigrationTask, 'execute')
  284. @mock.patch.object(utils, 'get_image_from_system_metadata')
  285. @mock.patch.object(objects.RequestSpec, 'from_components')
  286. def _test_cold_migrate(self, spec_from_components, get_image_from_metadata,
  287. migration_task_execute, spec_save, get_im,
  288. clean_shutdown=True):
  289. get_im.return_value.cell_mapping = (
  290. objects.CellMappingList.get_all(self.context)[0])
  291. get_image_from_metadata.return_value = 'image'
  292. inst = fake_instance.fake_db_instance(image_ref='image_ref')
  293. inst_obj = objects.Instance._from_db_object(
  294. self.context, objects.Instance(), inst, [])
  295. inst_obj.system_metadata = {'image_hw_disk_bus': 'scsi'}
  296. flavor = flavors.get_default_flavor()
  297. flavor.extra_specs = {'extra_specs': 'fake'}
  298. inst_obj.flavor = flavor
  299. fake_spec = fake_request_spec.fake_spec_obj()
  300. spec_from_components.return_value = fake_spec
  301. scheduler_hint = {'filter_properties': {}}
  302. if isinstance(self.conductor, conductor_api.ComputeTaskAPI):
  303. # The API method is actually 'resize_instance'. It gets
  304. # converted into 'migrate_server' when doing RPC.
  305. self.conductor.resize_instance(
  306. self.context, inst_obj, {}, scheduler_hint, flavor, [],
  307. clean_shutdown)
  308. else:
  309. self.conductor.migrate_server(
  310. self.context, inst_obj, scheduler_hint,
  311. False, False, flavor, None, None, [],
  312. clean_shutdown)
  313. get_image_from_metadata.assert_called_once_with(
  314. inst_obj.system_metadata)
  315. migration_task_execute.assert_called_once_with()
  316. spec_save.assert_called_once_with()
  317. def test_cold_migrate(self):
  318. self._test_cold_migrate()
  319. def test_cold_migrate_forced_shutdown(self):
  320. self._test_cold_migrate(clean_shutdown=False)
  321. @mock.patch('nova.objects.BuildRequest.get_by_instance_uuid')
  322. @mock.patch('nova.availability_zones.get_host_availability_zone')
  323. @mock.patch('nova.objects.Instance.save')
  324. @mock.patch.object(objects.RequestSpec, 'from_primitives')
  325. def test_build_instances(self, mock_fp, mock_save, mock_getaz,
  326. mock_buildreq):
  327. fake_spec = objects.RequestSpec
  328. mock_fp.return_value = fake_spec
  329. instance_type = flavors.get_default_flavor()
  330. # NOTE(danms): Avoid datetime timezone issues with converted flavors
  331. instance_type.created_at = None
  332. instances = [objects.Instance(context=self.context,
  333. id=i,
  334. uuid=uuids.fake,
  335. flavor=instance_type) for i in range(2)]
  336. instance_type_p = obj_base.obj_to_primitive(instance_type)
  337. instance_properties = obj_base.obj_to_primitive(instances[0])
  338. instance_properties['system_metadata'] = flavors.save_flavor_info(
  339. {}, instance_type)
  340. self.mox.StubOutWithMock(self.conductor_manager, '_schedule_instances')
  341. self.mox.StubOutWithMock(db,
  342. 'block_device_mapping_get_all_by_instance')
  343. self.mox.StubOutWithMock(self.conductor_manager.compute_rpcapi,
  344. 'build_and_run_instance')
  345. spec = {'image': {'fake_data': 'should_pass_silently'},
  346. 'instance_properties': instance_properties,
  347. 'instance_type': instance_type_p,
  348. 'num_instances': 2}
  349. filter_properties = {'retry': {'num_attempts': 1, 'hosts': []}}
  350. self.conductor_manager._schedule_instances(self.context,
  351. fake_spec, [uuids.fake, uuids.fake]).AndReturn(
  352. [{'host': 'host1', 'nodename': 'node1', 'limits': []},
  353. {'host': 'host2', 'nodename': 'node2', 'limits': []}])
  354. db.block_device_mapping_get_all_by_instance(self.context,
  355. instances[0].uuid).AndReturn([])
  356. self.conductor_manager.compute_rpcapi.build_and_run_instance(
  357. self.context,
  358. instance=mox.IgnoreArg(),
  359. host='host1',
  360. image={'fake_data': 'should_pass_silently'},
  361. request_spec={
  362. 'image': {'fake_data': 'should_pass_silently'},
  363. 'instance_properties': instance_properties,
  364. 'instance_type': instance_type_p,
  365. 'num_instances': 2},
  366. filter_properties={'retry': {'num_attempts': 1,
  367. 'hosts': [['host1', 'node1']]},
  368. 'limits': []},
  369. admin_password='admin_password',
  370. injected_files='injected_files',
  371. requested_networks=None,
  372. security_groups='security_groups',
  373. block_device_mapping=mox.IgnoreArg(),
  374. node='node1', limits=[])
  375. db.block_device_mapping_get_all_by_instance(self.context,
  376. instances[1].uuid).AndReturn([])
  377. self.conductor_manager.compute_rpcapi.build_and_run_instance(
  378. self.context,
  379. instance=mox.IgnoreArg(),
  380. host='host2',
  381. image={'fake_data': 'should_pass_silently'},
  382. request_spec={
  383. 'image': {'fake_data': 'should_pass_silently'},
  384. 'instance_properties': instance_properties,
  385. 'instance_type': instance_type_p,
  386. 'num_instances': 2},
  387. filter_properties={'limits': [],
  388. 'retry': {'num_attempts': 1,
  389. 'hosts': [['host2', 'node2']]}},
  390. admin_password='admin_password',
  391. injected_files='injected_files',
  392. requested_networks=None,
  393. security_groups='security_groups',
  394. block_device_mapping=mox.IgnoreArg(),
  395. node='node2', limits=[])
  396. self.mox.ReplayAll()
  397. # build_instances() is a cast, we need to wait for it to complete
  398. self.useFixture(cast_as_call.CastAsCall(self))
  399. mock_getaz.return_value = 'myaz'
  400. self.conductor.build_instances(self.context,
  401. instances=instances,
  402. image={'fake_data': 'should_pass_silently'},
  403. filter_properties={},
  404. admin_password='admin_password',
  405. injected_files='injected_files',
  406. requested_networks=None,
  407. security_groups='security_groups',
  408. block_device_mapping='block_device_mapping',
  409. legacy_bdm=False)
  410. mock_getaz.assert_has_calls([
  411. mock.call(self.context, 'host1'),
  412. mock.call(self.context, 'host2')])
  413. mock_fp.assert_called_once_with(self.context, spec, filter_properties)
  414. @mock.patch.object(scheduler_utils, 'build_request_spec')
  415. @mock.patch.object(scheduler_utils, 'setup_instance_group')
  416. @mock.patch.object(scheduler_utils, 'set_vm_state_and_notify')
  417. @mock.patch.object(scheduler_client.SchedulerClient,
  418. 'select_destinations')
  419. @mock.patch.object(conductor_manager.ComputeTaskManager,
  420. '_cleanup_allocated_networks')
  421. @mock.patch.object(conductor_manager.ComputeTaskManager,
  422. '_destroy_build_request')
  423. def test_build_instances_scheduler_failure(
  424. self, dest_build_req_mock, cleanup_mock, sd_mock, state_mock,
  425. sig_mock, bs_mock):
  426. instances = [fake_instance.fake_instance_obj(self.context)
  427. for i in range(2)]
  428. image = {'fake-data': 'should_pass_silently'}
  429. spec = {'fake': 'specs',
  430. 'instance_properties': instances[0]}
  431. exception = exc.NoValidHost(reason='fake-reason')
  432. dest_build_req_mock.side_effect = (
  433. exc.BuildRequestNotFound(uuid='fake'),
  434. None)
  435. bs_mock.return_value = spec
  436. sd_mock.side_effect = exception
  437. updates = {'vm_state': vm_states.ERROR, 'task_state': None}
  438. # build_instances() is a cast, we need to wait for it to complete
  439. self.useFixture(cast_as_call.CastAsCall(self))
  440. self.conductor.build_instances(
  441. self.context,
  442. instances=instances,
  443. image=image,
  444. filter_properties={},
  445. admin_password='admin_password',
  446. injected_files='injected_files',
  447. requested_networks=None,
  448. security_groups='security_groups',
  449. block_device_mapping='block_device_mapping',
  450. legacy_bdm=False)
  451. set_state_calls = []
  452. cleanup_network_calls = []
  453. dest_build_req_calls = []
  454. for instance in instances:
  455. set_state_calls.append(mock.call(
  456. self.context, instance.uuid, 'compute_task', 'build_instances',
  457. updates, exception, spec))
  458. cleanup_network_calls.append(mock.call(
  459. self.context, mock.ANY, None))
  460. dest_build_req_calls.append(
  461. mock.call(self.context, test.MatchType(type(instance))))
  462. state_mock.assert_has_calls(set_state_calls)
  463. cleanup_mock.assert_has_calls(cleanup_network_calls)
  464. dest_build_req_mock.assert_has_calls(dest_build_req_calls)
  465. def test_build_instances_retry_exceeded(self):
  466. instances = [fake_instance.fake_instance_obj(self.context)]
  467. image = {'fake-data': 'should_pass_silently'}
  468. filter_properties = {'retry': {'num_attempts': 10, 'hosts': []}}
  469. updates = {'vm_state': vm_states.ERROR, 'task_state': None}
  470. @mock.patch.object(conductor_manager.ComputeTaskManager,
  471. '_cleanup_allocated_networks')
  472. @mock.patch.object(scheduler_utils, 'set_vm_state_and_notify')
  473. @mock.patch.object(scheduler_utils, 'build_request_spec')
  474. @mock.patch.object(scheduler_utils, 'populate_retry')
  475. def _test(populate_retry, build_spec,
  476. set_vm_state_and_notify, cleanup_mock):
  477. # build_instances() is a cast, we need to wait for it to
  478. # complete
  479. self.useFixture(cast_as_call.CastAsCall(self))
  480. populate_retry.side_effect = exc.MaxRetriesExceeded(
  481. reason="Too many try")
  482. self.conductor.build_instances(
  483. self.context,
  484. instances=instances,
  485. image=image,
  486. filter_properties=filter_properties,
  487. admin_password='admin_password',
  488. injected_files='injected_files',
  489. requested_networks=None,
  490. security_groups='security_groups',
  491. block_device_mapping='block_device_mapping',
  492. legacy_bdm=False)
  493. populate_retry.assert_called_once_with(
  494. filter_properties, instances[0].uuid)
  495. set_vm_state_and_notify.assert_called_once_with(
  496. self.context, instances[0].uuid, 'compute_task',
  497. 'build_instances', updates, mock.ANY, build_spec.return_value)
  498. cleanup_mock.assert_called_once_with(self.context, mock.ANY, None)
  499. _test()
  500. @mock.patch.object(scheduler_utils, 'build_request_spec')
  501. @mock.patch.object(scheduler_utils, 'setup_instance_group')
  502. @mock.patch.object(conductor_manager.ComputeTaskManager,
  503. '_set_vm_state_and_notify')
  504. @mock.patch.object(conductor_manager.ComputeTaskManager,
  505. '_cleanup_allocated_networks')
  506. def test_build_instances_scheduler_group_failure(
  507. self, cleanup_mock, state_mock, sig_mock, bs_mock):
  508. instances = [fake_instance.fake_instance_obj(self.context)
  509. for i in range(2)]
  510. image = {'fake-data': 'should_pass_silently'}
  511. spec = {'fake': 'specs',
  512. 'instance_properties': instances[0]}
  513. bs_mock.return_value = spec
  514. exception = exc.UnsupportedPolicyException(reason='fake-reason')
  515. sig_mock.side_effect = exception
  516. updates = {'vm_state': vm_states.ERROR, 'task_state': None}
  517. # build_instances() is a cast, we need to wait for it to complete
  518. self.useFixture(cast_as_call.CastAsCall(self))
  519. self.conductor.build_instances(
  520. context=self.context,
  521. instances=instances,
  522. image=image,
  523. filter_properties={},
  524. admin_password='admin_password',
  525. injected_files='injected_files',
  526. requested_networks=None,
  527. security_groups='security_groups',
  528. block_device_mapping='block_device_mapping',
  529. legacy_bdm=False)
  530. set_state_calls = []
  531. cleanup_network_calls = []
  532. for instance in instances:
  533. set_state_calls.append(mock.call(
  534. self.context, instance.uuid, 'build_instances', updates,
  535. exception, spec))
  536. cleanup_network_calls.append(mock.call(
  537. self.context, mock.ANY, None))
  538. state_mock.assert_has_calls(set_state_calls)
  539. cleanup_mock.assert_has_calls(cleanup_network_calls)
  540. @mock.patch.object(objects.BuildRequest, 'get_by_instance_uuid')
  541. @mock.patch.object(objects.Instance, 'save')
  542. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid',
  543. side_effect=exc.InstanceMappingNotFound(uuid='fake'))
  544. @mock.patch.object(objects.HostMapping, 'get_by_host')
  545. @mock.patch.object(scheduler_client.SchedulerClient,
  546. 'select_destinations')
  547. @mock.patch.object(conductor_manager.ComputeTaskManager,
  548. '_set_vm_state_and_notify')
  549. def test_build_instances_no_instance_mapping(self, _mock_set_state,
  550. mock_select_dests, mock_get_by_host, mock_get_inst_map_by_uuid,
  551. _mock_save, _mock_buildreq):
  552. mock_select_dests.return_value = [
  553. {'host': 'host1', 'nodename': 'node1', 'limits': []},
  554. {'host': 'host2', 'nodename': 'node2', 'limits': []}]
  555. instances = [fake_instance.fake_instance_obj(self.context)
  556. for i in range(2)]
  557. image = {'fake-data': 'should_pass_silently'}
  558. # build_instances() is a cast, we need to wait for it to complete
  559. self.useFixture(cast_as_call.CastAsCall(self))
  560. with mock.patch.object(self.conductor_manager.compute_rpcapi,
  561. 'build_and_run_instance'):
  562. self.conductor.build_instances(
  563. context=self.context,
  564. instances=instances,
  565. image=image,
  566. filter_properties={},
  567. admin_password='admin_password',
  568. injected_files='injected_files',
  569. requested_networks=None,
  570. security_groups='security_groups',
  571. block_device_mapping='block_device_mapping',
  572. legacy_bdm=False)
  573. mock_get_inst_map_by_uuid.assert_has_calls([
  574. mock.call(self.context, instances[0].uuid),
  575. mock.call(self.context, instances[1].uuid)])
  576. self.assertFalse(mock_get_by_host.called)
  577. @mock.patch.object(objects.BuildRequest, 'get_by_instance_uuid')
  578. @mock.patch.object(objects.Instance, 'save')
  579. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  580. @mock.patch.object(objects.HostMapping, 'get_by_host',
  581. side_effect=exc.HostMappingNotFound(name='fake'))
  582. @mock.patch.object(scheduler_client.SchedulerClient,
  583. 'select_destinations')
  584. @mock.patch.object(conductor_manager.ComputeTaskManager,
  585. '_set_vm_state_and_notify')
  586. def test_build_instances_no_host_mapping(self, _mock_set_state,
  587. mock_select_dests, mock_get_by_host, mock_get_inst_map_by_uuid,
  588. _mock_save, mock_buildreq):
  589. mock_select_dests.return_value = [
  590. {'host': 'host1', 'nodename': 'node1', 'limits': []},
  591. {'host': 'host2', 'nodename': 'node2', 'limits': []}]
  592. num_instances = 2
  593. instances = [fake_instance.fake_instance_obj(self.context)
  594. for i in range(num_instances)]
  595. inst_mapping_mocks = [mock.Mock() for i in range(num_instances)]
  596. mock_get_inst_map_by_uuid.side_effect = inst_mapping_mocks
  597. image = {'fake-data': 'should_pass_silently'}
  598. # build_instances() is a cast, we need to wait for it to complete
  599. self.useFixture(cast_as_call.CastAsCall(self))
  600. with mock.patch.object(self.conductor_manager.compute_rpcapi,
  601. 'build_and_run_instance'):
  602. self.conductor.build_instances(
  603. context=self.context,
  604. instances=instances,
  605. image=image,
  606. filter_properties={},
  607. admin_password='admin_password',
  608. injected_files='injected_files',
  609. requested_networks=None,
  610. security_groups='security_groups',
  611. block_device_mapping='block_device_mapping',
  612. legacy_bdm=False)
  613. for instance in instances:
  614. mock_get_inst_map_by_uuid.assert_any_call(self.context,
  615. instance.uuid)
  616. for inst_mapping in inst_mapping_mocks:
  617. inst_mapping.destroy.assert_called_once_with()
  618. mock_get_by_host.assert_has_calls([mock.call(self.context, 'host1'),
  619. mock.call(self.context, 'host2')])
  620. @mock.patch.object(objects.BuildRequest, 'get_by_instance_uuid')
  621. @mock.patch.object(objects.Instance, 'save')
  622. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  623. @mock.patch.object(objects.HostMapping, 'get_by_host')
  624. @mock.patch.object(scheduler_client.SchedulerClient,
  625. 'select_destinations')
  626. @mock.patch.object(conductor_manager.ComputeTaskManager,
  627. '_set_vm_state_and_notify')
  628. def test_build_instances_update_instance_mapping(self, _mock_set_state,
  629. mock_select_dests, mock_get_by_host, mock_get_inst_map_by_uuid,
  630. _mock_save, _mock_buildreq):
  631. mock_select_dests.return_value = [
  632. {'host': 'host1', 'nodename': 'node1', 'limits': []},
  633. {'host': 'host2', 'nodename': 'node2', 'limits': []}]
  634. mock_get_by_host.side_effect = [
  635. objects.HostMapping(cell_mapping=objects.CellMapping(id=1)),
  636. objects.HostMapping(cell_mapping=objects.CellMapping(id=2))]
  637. num_instances = 2
  638. instances = [fake_instance.fake_instance_obj(self.context)
  639. for i in range(num_instances)]
  640. inst_mapping_mocks = [mock.Mock() for i in range(num_instances)]
  641. mock_get_inst_map_by_uuid.side_effect = inst_mapping_mocks
  642. image = {'fake-data': 'should_pass_silently'}
  643. # build_instances() is a cast, we need to wait for it to complete
  644. self.useFixture(cast_as_call.CastAsCall(self))
  645. with mock.patch.object(self.conductor_manager.compute_rpcapi,
  646. 'build_and_run_instance'):
  647. self.conductor.build_instances(
  648. context=self.context,
  649. instances=instances,
  650. image=image,
  651. filter_properties={},
  652. admin_password='admin_password',
  653. injected_files='injected_files',
  654. requested_networks=None,
  655. security_groups='security_groups',
  656. block_device_mapping='block_device_mapping',
  657. legacy_bdm=False)
  658. for instance in instances:
  659. mock_get_inst_map_by_uuid.assert_any_call(self.context,
  660. instance.uuid)
  661. for inst_mapping in inst_mapping_mocks:
  662. inst_mapping.save.assert_called_once_with()
  663. self.assertEqual(1, inst_mapping_mocks[0].cell_mapping.id)
  664. self.assertEqual(2, inst_mapping_mocks[1].cell_mapping.id)
  665. mock_get_by_host.assert_has_calls([mock.call(self.context, 'host1'),
  666. mock.call(self.context, 'host2')])
  667. @mock.patch.object(objects.Instance, 'save', new=mock.MagicMock())
  668. @mock.patch.object(objects.BuildRequest, 'get_by_instance_uuid')
  669. @mock.patch.object(scheduler_client.SchedulerClient,
  670. 'select_destinations')
  671. @mock.patch.object(conductor_manager.ComputeTaskManager,
  672. '_set_vm_state_and_notify', new=mock.MagicMock())
  673. def test_build_instances_destroy_build_request(self, mock_select_dests,
  674. mock_build_req_get):
  675. mock_select_dests.return_value = [
  676. {'host': 'host1', 'nodename': 'node1', 'limits': []},
  677. {'host': 'host2', 'nodename': 'node2', 'limits': []}]
  678. num_instances = 2
  679. instances = [fake_instance.fake_instance_obj(self.context)
  680. for i in range(num_instances)]
  681. build_req_mocks = [mock.Mock() for i in range(num_instances)]
  682. mock_build_req_get.side_effect = build_req_mocks
  683. image = {'fake-data': 'should_pass_silently'}
  684. # build_instances() is a cast, we need to wait for it to complete
  685. self.useFixture(cast_as_call.CastAsCall(self))
  686. @mock.patch.object(self.conductor_manager.compute_rpcapi,
  687. 'build_and_run_instance', new=mock.MagicMock())
  688. @mock.patch.object(self.conductor_manager,
  689. '_populate_instance_mapping', new=mock.MagicMock())
  690. def do_test():
  691. self.conductor.build_instances(
  692. context=self.context,
  693. instances=instances,
  694. image=image,
  695. filter_properties={},
  696. admin_password='admin_password',
  697. injected_files='injected_files',
  698. requested_networks=None,
  699. security_groups='security_groups',
  700. block_device_mapping='block_device_mapping',
  701. legacy_bdm=False)
  702. do_test()
  703. for build_req in build_req_mocks:
  704. build_req.destroy.assert_called_once_with()
  705. @mock.patch.object(objects.Instance, 'save', new=mock.MagicMock())
  706. @mock.patch.object(scheduler_client.SchedulerClient,
  707. 'select_destinations')
  708. @mock.patch.object(conductor_manager.ComputeTaskManager,
  709. '_set_vm_state_and_notify', new=mock.MagicMock())
  710. def test_build_instances_reschedule_ignores_build_request(self,
  711. mock_select_dests):
  712. # This test calls build_instances as if it was a reschedule. This means
  713. # that the exc.BuildRequestNotFound() exception raised by
  714. # conductor_manager._destroy_build_request() should not cause the
  715. # build to stop.
  716. mock_select_dests.return_value = [
  717. {'host': 'host1', 'nodename': 'node1', 'limits': []}]
  718. instance = fake_instance.fake_instance_obj(self.context)
  719. image = {'fake-data': 'should_pass_silently'}
  720. # build_instances() is a cast, we need to wait for it to complete
  721. self.useFixture(cast_as_call.CastAsCall(self))
  722. @mock.patch.object(self.conductor_manager.compute_rpcapi,
  723. 'build_and_run_instance')
  724. @mock.patch.object(self.conductor_manager,
  725. '_populate_instance_mapping')
  726. @mock.patch.object(self.conductor_manager,
  727. '_destroy_build_request',
  728. side_effect=exc.BuildRequestNotFound(uuid='fake'))
  729. def do_test(mock_destroy_build_req, mock_pop_inst_map,
  730. mock_build_and_run):
  731. self.conductor.build_instances(
  732. context=self.context,
  733. instances=[instance],
  734. image=image,
  735. filter_properties={'retry': {'num_attempts': 1, 'hosts': []}},
  736. admin_password='admin_password',
  737. injected_files='injected_files',
  738. requested_networks=None,
  739. security_groups='security_groups',
  740. block_device_mapping='block_device_mapping',
  741. legacy_bdm=False)
  742. mock_build_and_run.assert_called_once_with(
  743. self.context,
  744. instance=mock.ANY,
  745. host='host1',
  746. image=image,
  747. request_spec=mock.ANY,
  748. filter_properties={'retry': {'num_attempts': 2,
  749. 'hosts': [['host1', 'node1']]},
  750. 'limits': []},
  751. admin_password='admin_password',
  752. injected_files='injected_files',
  753. requested_networks=None,
  754. security_groups='security_groups',
  755. block_device_mapping=test.MatchType(
  756. objects.BlockDeviceMappingList),
  757. node='node1', limits=[])
  758. mock_pop_inst_map.assert_not_called()
  759. mock_destroy_build_req.assert_not_called()
  760. do_test()
  761. def test_unshelve_instance_on_host(self):
  762. instance = self._create_fake_instance_obj()
  763. instance.vm_state = vm_states.SHELVED
  764. instance.task_state = task_states.UNSHELVING
  765. instance.save()
  766. system_metadata = instance.system_metadata
  767. self.mox.StubOutWithMock(self.conductor_manager.compute_rpcapi,
  768. 'start_instance')
  769. self.mox.StubOutWithMock(self.conductor_manager.compute_rpcapi,
  770. 'unshelve_instance')
  771. self.conductor_manager.compute_rpcapi.start_instance(self.context,
  772. instance)
  773. self.mox.ReplayAll()
  774. system_metadata['shelved_at'] = timeutils.utcnow()
  775. system_metadata['shelved_image_id'] = 'fake_image_id'
  776. system_metadata['shelved_host'] = 'fake-mini'
  777. self.conductor_manager.unshelve_instance(self.context, instance)
  778. def test_unshelve_offload_instance_on_host_with_request_spec(self):
  779. instance = self._create_fake_instance_obj()
  780. instance.vm_state = vm_states.SHELVED_OFFLOADED
  781. instance.task_state = task_states.UNSHELVING
  782. instance.save()
  783. system_metadata = instance.system_metadata
  784. system_metadata['shelved_at'] = timeutils.utcnow()
  785. system_metadata['shelved_image_id'] = 'fake_image_id'
  786. system_metadata['shelved_host'] = 'fake-mini'
  787. fake_spec = fake_request_spec.fake_spec_obj()
  788. # FIXME(sbauza): Modify the fake RequestSpec object to either add a
  789. # non-empty SchedulerRetries object or nullify the field
  790. fake_spec.retry = None
  791. # FIXME(sbauza): Modify the fake RequestSpec object to either add a
  792. # non-empty SchedulerLimits object or nullify the field
  793. fake_spec.limits = None
  794. # FIXME(sbauza): Modify the fake RequestSpec object to either add a
  795. # non-empty InstanceGroup object or nullify the field
  796. fake_spec.instance_group = None
  797. filter_properties = fake_spec.to_legacy_filter_properties_dict()
  798. request_spec = fake_spec.to_legacy_request_spec_dict()
  799. host = {'host': 'host1', 'nodename': 'node1', 'limits': []}
  800. # unshelve_instance() is a cast, we need to wait for it to complete
  801. self.useFixture(cast_as_call.CastAsCall(self))
  802. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  803. @mock.patch.object(self.conductor_manager.compute_rpcapi,
  804. 'unshelve_instance')
  805. @mock.patch.object(scheduler_utils, 'populate_filter_properties')
  806. @mock.patch.object(scheduler_utils, 'populate_retry')
  807. @mock.patch.object(self.conductor_manager, '_schedule_instances')
  808. @mock.patch.object(objects.RequestSpec, 'from_primitives')
  809. @mock.patch.object(objects.RequestSpec, 'to_legacy_request_spec_dict')
  810. @mock.patch.object(objects.RequestSpec,
  811. 'to_legacy_filter_properties_dict')
  812. @mock.patch.object(objects.RequestSpec, 'reset_forced_destinations')
  813. def do_test(reset_forced_destinations,
  814. to_filtprops, to_reqspec, from_primitives, sched_instances,
  815. populate_retry, populate_filter_properties,
  816. unshelve_instance, get_by_instance_uuid):
  817. cell_mapping = objects.CellMapping.get_by_uuid(self.context,
  818. uuids.cell1)
  819. get_by_instance_uuid.return_value = objects.InstanceMapping(
  820. cell_mapping=cell_mapping)
  821. to_filtprops.return_value = filter_properties
  822. to_reqspec.return_value = request_spec
  823. from_primitives.return_value = fake_spec
  824. sched_instances.return_value = [host]
  825. self.conductor.unshelve_instance(self.context, instance, fake_spec)
  826. reset_forced_destinations.assert_called_once_with()
  827. from_primitives.assert_called_once_with(self.context, request_spec,
  828. filter_properties)
  829. sched_instances.assert_called_once_with(self.context, fake_spec,
  830. [instance.uuid])
  831. self.assertEqual(cell_mapping,
  832. fake_spec.requested_destination.cell)
  833. # NOTE(sbauza): Since the instance is dehydrated when passing
  834. # through the RPC API, we can only assert mock.ANY for it
  835. unshelve_instance.assert_called_once_with(
  836. self.context, mock.ANY, host['host'], image=mock.ANY,
  837. filter_properties=filter_properties, node=host['nodename']
  838. )
  839. do_test()
  840. def test_unshelve_offloaded_instance_glance_image_not_found(self):
  841. shelved_image_id = "image_not_found"
  842. instance = self._create_fake_instance_obj()
  843. instance.vm_state = vm_states.SHELVED_OFFLOADED
  844. instance.task_state = task_states.UNSHELVING
  845. instance.save()
  846. system_metadata = instance.system_metadata
  847. self.mox.StubOutWithMock(self.conductor_manager.image_api, 'get')
  848. e = exc.ImageNotFound(image_id=shelved_image_id)
  849. self.conductor_manager.image_api.get(
  850. self.context, shelved_image_id, show_deleted=False).AndRaise(e)
  851. self.mox.ReplayAll()
  852. system_metadata['shelved_at'] = timeutils.utcnow()
  853. system_metadata['shelved_host'] = 'fake-mini'
  854. system_metadata['shelved_image_id'] = shelved_image_id
  855. self.assertRaises(
  856. exc.UnshelveException,
  857. self.conductor_manager.unshelve_instance,
  858. self.context, instance)
  859. self.assertEqual(instance.vm_state, vm_states.ERROR)
  860. def test_unshelve_offloaded_instance_image_id_is_none(self):
  861. instance = self._create_fake_instance_obj()
  862. instance.vm_state = vm_states.SHELVED_OFFLOADED
  863. instance.task_state = task_states.UNSHELVING
  864. # 'shelved_image_id' is None for volumebacked instance
  865. instance.system_metadata['shelved_image_id'] = None
  866. with test.nested(
  867. mock.patch.object(self.conductor_manager,
  868. '_schedule_instances'),
  869. mock.patch.object(self.conductor_manager.compute_rpcapi,
  870. 'unshelve_instance'),
  871. mock.patch.object(objects.InstanceMapping,
  872. 'get_by_instance_uuid'),
  873. ) as (schedule_mock, unshelve_mock, get_by_instance_uuid):
  874. schedule_mock.return_value = [{'host': 'fake_host',
  875. 'nodename': 'fake_node',
  876. 'limits': {}}]
  877. get_by_instance_uuid.return_value = objects.InstanceMapping(
  878. cell_mapping=objects.CellMapping.get_by_uuid(
  879. self.context, uuids.cell1))
  880. self.conductor_manager.unshelve_instance(self.context, instance)
  881. self.assertEqual(1, unshelve_mock.call_count)
  882. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  883. @mock.patch.object(objects.RequestSpec, 'from_primitives')
  884. def test_unshelve_instance_schedule_and_rebuild(self, fp, mock_im):
  885. fake_spec = objects.RequestSpec()
  886. # Set requested_destination to test setting cell_mapping in
  887. # existing object.
  888. fake_spec.requested_destination = objects.Destination(
  889. host="dummy", cell=None)
  890. fp.return_value = fake_spec
  891. cell_mapping = objects.CellMapping.get_by_uuid(self.context,
  892. uuids.cell1)
  893. mock_im.return_value = objects.InstanceMapping(
  894. cell_mapping=cell_mapping)
  895. instance = self._create_fake_instance_obj()
  896. instance.vm_state = vm_states.SHELVED_OFFLOADED
  897. instance.save()
  898. system_metadata = instance.system_metadata
  899. self.mox.StubOutWithMock(self.conductor_manager.image_api, 'get')
  900. self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
  901. self.mox.StubOutWithMock(self.conductor_manager, '_schedule_instances')
  902. self.mox.StubOutWithMock(self.conductor_manager.compute_rpcapi,
  903. 'unshelve_instance')
  904. self.conductor_manager.image_api.get(self.context,
  905. 'fake_image_id', show_deleted=False).AndReturn('fake_image')
  906. scheduler_utils.build_request_spec(self.context, 'fake_image',
  907. mox.IgnoreArg()).AndReturn('req_spec')
  908. self.conductor_manager._schedule_instances(self.context,
  909. fake_spec, [instance.uuid]).AndReturn(
  910. [{'host': 'fake_host',
  911. 'nodename': 'fake_node',
  912. 'limits': {}}])
  913. self.conductor_manager.compute_rpcapi.unshelve_instance(self.context,
  914. instance, 'fake_host', image='fake_image',
  915. filter_properties={'limits': {},
  916. 'retry': {'num_attempts': 1,
  917. 'hosts': [['fake_host',
  918. 'fake_node']]}},
  919. node='fake_node')
  920. self.mox.ReplayAll()
  921. system_metadata['shelved_at'] = timeutils.utcnow()
  922. system_metadata['shelved_image_id'] = 'fake_image_id'
  923. system_metadata['shelved_host'] = 'fake-mini'
  924. self.conductor_manager.unshelve_instance(self.context, instance)
  925. fp.assert_called_once_with(self.context, 'req_spec', mock.ANY)
  926. self.assertEqual(cell_mapping, fake_spec.requested_destination.cell)
  927. def test_unshelve_instance_schedule_and_rebuild_novalid_host(self):
  928. instance = self._create_fake_instance_obj()
  929. instance.vm_state = vm_states.SHELVED_OFFLOADED
  930. instance.save()
  931. system_metadata = instance.system_metadata
  932. def fake_schedule_instances(context, request_spec, *instances):
  933. raise exc.NoValidHost(reason='')
  934. with test.nested(
  935. mock.patch.object(self.conductor_manager.image_api, 'get',
  936. return_value='fake_image'),
  937. mock.patch.object(self.conductor_manager, '_schedule_instances',
  938. fake_schedule_instances),
  939. mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid'),
  940. mock.patch.object(objects.Instance, 'save')
  941. ) as (_get_image, _schedule_instances, get_by_instance_uuid, save):
  942. get_by_instance_uuid.return_value = objects.InstanceMapping(
  943. cell_mapping=objects.CellMapping.get_by_uuid(
  944. self.context, uuids.cell1))
  945. system_metadata['shelved_at'] = timeutils.utcnow()
  946. system_metadata['shelved_image_id'] = 'fake_image_id'
  947. system_metadata['shelved_host'] = 'fake-mini'
  948. self.conductor_manager.unshelve_instance(self.context, instance)
  949. _get_image.assert_has_calls([mock.call(self.context,
  950. system_metadata['shelved_image_id'],
  951. show_deleted=False)])
  952. self.assertEqual(vm_states.SHELVED_OFFLOADED, instance.vm_state)
  953. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  954. @mock.patch.object(conductor_manager.ComputeTaskManager,
  955. '_schedule_instances',
  956. side_effect=messaging.MessagingTimeout())
  957. @mock.patch.object(image_api.API, 'get', return_value='fake_image')
  958. @mock.patch.object(objects.Instance, 'save')
  959. def test_unshelve_instance_schedule_and_rebuild_messaging_exception(
  960. self, mock_save, mock_get_image, mock_schedule_instances, mock_im):
  961. mock_im.return_value = objects.InstanceMapping(
  962. cell_mapping=objects.CellMapping.get_by_uuid(self.context,
  963. uuids.cell1))
  964. instance = self._create_fake_instance_obj()
  965. instance.vm_state = vm_states.SHELVED_OFFLOADED
  966. instance.task_state = task_states.UNSHELVING
  967. instance.save()
  968. system_metadata = instance.system_metadata
  969. system_metadata['shelved_at'] = timeutils.utcnow()
  970. system_metadata['shelved_image_id'] = 'fake_image_id'
  971. system_metadata['shelved_host'] = 'fake-mini'
  972. self.assertRaises(messaging.MessagingTimeout,
  973. self.conductor_manager.unshelve_instance,
  974. self.context, instance)
  975. mock_get_image.assert_has_calls([mock.call(self.context,
  976. system_metadata['shelved_image_id'],
  977. show_deleted=False)])
  978. self.assertEqual(vm_states.SHELVED_OFFLOADED, instance.vm_state)
  979. self.assertIsNone(instance.task_state)
  980. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  981. @mock.patch.object(objects.RequestSpec, 'from_primitives')
  982. def test_unshelve_instance_schedule_and_rebuild_volume_backed(
  983. self, fp, mock_im):
  984. fake_spec = objects.RequestSpec()
  985. fp.return_value = fake_spec
  986. mock_im.return_value = objects.InstanceMapping(
  987. cell_mapping=objects.CellMapping.get_by_uuid(self.context,
  988. uuids.cell1))
  989. instance = self._create_fake_instance_obj()
  990. instance.vm_state = vm_states.SHELVED_OFFLOADED
  991. instance.save()
  992. system_metadata = instance.system_metadata
  993. self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
  994. self.mox.StubOutWithMock(self.conductor_manager, '_schedule_instances')
  995. self.mox.StubOutWithMock(self.conductor_manager.compute_rpcapi,
  996. 'unshelve_instance')
  997. scheduler_utils.build_request_spec(self.context, None,
  998. mox.IgnoreArg()).AndReturn('req_spec')
  999. self.conductor_manager._schedule_instances(self.context,
  1000. fake_spec, [instance.uuid]).AndReturn(
  1001. [{'host': 'fake_host',
  1002. 'nodename': 'fake_node',
  1003. 'limits': {}}])
  1004. self.conductor_manager.compute_rpcapi.unshelve_instance(self.context,
  1005. instance, 'fake_host', image=None,
  1006. filter_properties={'limits': {},
  1007. 'retry': {'num_attempts': 1,
  1008. 'hosts': [['fake_host',
  1009. 'fake_node']]}},
  1010. node='fake_node')
  1011. self.mox.ReplayAll()
  1012. system_metadata['shelved_at'] = timeutils.utcnow()
  1013. system_metadata['shelved_host'] = 'fake-mini'
  1014. self.conductor_manager.unshelve_instance(self.context, instance)
  1015. fp.assert_called_once_with(self.context, 'req_spec', mock.ANY)
  1016. def test_rebuild_instance(self):
  1017. inst_obj = self._create_fake_instance_obj()
  1018. rebuild_args, compute_args = self._prepare_rebuild_args(
  1019. {'host': inst_obj.host})
  1020. with test.nested(
  1021. mock.patch.object(self.conductor_manager.compute_rpcapi,
  1022. 'rebuild_instance'),
  1023. mock.patch.object(self.conductor_manager.scheduler_client,
  1024. 'select_destinations')
  1025. ) as (rebuild_mock, select_dest_mock):
  1026. self.conductor_manager.rebuild_instance(context=self.context,
  1027. instance=inst_obj,
  1028. **rebuild_args)
  1029. self.assertFalse(select_dest_mock.called)
  1030. rebuild_mock.assert_called_once_with(self.context,
  1031. instance=inst_obj,
  1032. **compute_args)
  1033. def test_rebuild_instance_with_scheduler(self):
  1034. inst_obj = self._create_fake_instance_obj()
  1035. inst_obj.host = 'noselect'
  1036. expected_host = 'thebesthost'
  1037. expected_node = 'thebestnode'
  1038. expected_limits = 'fake-limits'
  1039. rebuild_args, compute_args = self._prepare_rebuild_args(
  1040. {'host': None, 'node': expected_node, 'limits': expected_limits})
  1041. request_spec = {}
  1042. filter_properties = {'ignore_hosts': [(inst_obj.host)]}
  1043. fake_spec = objects.RequestSpec()
  1044. inst_uuids = [inst_obj.uuid]
  1045. with test.nested(
  1046. mock.patch.object(self.conductor_manager.compute_rpcapi,
  1047. 'rebuild_instance'),
  1048. mock.patch.object(scheduler_utils, 'setup_instance_group',
  1049. return_value=False),
  1050. mock.patch.object(objects.RequestSpec, 'from_primitives',
  1051. return_value=fake_spec),
  1052. mock.patch.object(self.conductor_manager.scheduler_client,
  1053. 'select_destinations',
  1054. return_value=[{'host': expected_host,
  1055. 'nodename': expected_node,
  1056. 'limits': expected_limits}]),
  1057. mock.patch('nova.scheduler.utils.build_request_spec',
  1058. return_value=request_spec)
  1059. ) as (rebuild_mock, sig_mock, fp_mock, select_dest_mock, bs_mock):
  1060. self.conductor_manager.rebuild_instance(context=self.context,
  1061. instance=inst_obj,
  1062. **rebuild_args)
  1063. fp_mock.assert_called_once_with(self.context, request_spec,
  1064. filter_properties)
  1065. select_dest_mock.assert_called_once_with(self.context, fake_spec,
  1066. inst_uuids)
  1067. compute_args['host'] = expected_host
  1068. rebuild_mock.assert_called_once_with(self.context,
  1069. instance=inst_obj,
  1070. **compute_args)
  1071. self.assertEqual('compute.instance.rebuild.scheduled',
  1072. fake_notifier.NOTIFICATIONS[0].event_type)
  1073. def test_rebuild_instance_with_scheduler_no_host(self):
  1074. inst_obj = self._create_fake_instance_obj()
  1075. inst_obj.host = 'noselect'
  1076. rebuild_args, _ = self._prepare_rebuild_args({'host': None})
  1077. request_spec = {}
  1078. filter_properties = {'ignore_hosts': [(inst_obj.host)]}
  1079. fake_spec = objects.RequestSpec()
  1080. with test.nested(
  1081. mock.patch.object(self.conductor_manager.compute_rpcapi,
  1082. 'rebuild_instance'),
  1083. mock.patch.object(scheduler_utils, 'setup_instance_group',
  1084. return_value=False),
  1085. mock.patch.object(objects.RequestSpec, 'from_primitives',
  1086. return_value=fake_spec),
  1087. mock.patch.object(self.conductor_manager.scheduler_client,
  1088. 'select_destinations',
  1089. side_effect=exc.NoValidHost(reason='')),
  1090. mock.patch('nova.scheduler.utils.build_request_spec',
  1091. return_value=request_spec)
  1092. ) as (rebuild_mock, sig_mock, fp_mock, select_dest_mock, bs_mock):
  1093. self.assertRaises(exc.NoValidHost,
  1094. self.conductor_manager.rebuild_instance,
  1095. context=self.context, instance=inst_obj,
  1096. **rebuild_args)
  1097. fp_mock.assert_called_once_with(self.context, request_spec,
  1098. filter_properties)
  1099. select_dest_mock.assert_called_once_with(self.context, fake_spec,
  1100. [inst_obj.uuid])
  1101. self.assertFalse(rebuild_mock.called)
  1102. @mock.patch.object(conductor_manager.compute_rpcapi.ComputeAPI,
  1103. 'rebuild_instance')
  1104. @mock.patch.object(scheduler_utils, 'setup_instance_group')
  1105. @mock.patch.object(conductor_manager.scheduler_client.SchedulerClient,
  1106. 'select_destinations')
  1107. @mock.patch('nova.scheduler.utils.build_request_spec')
  1108. @mock.patch.object(conductor_manager.ComputeTaskManager,
  1109. '_set_vm_state_and_notify')
  1110. def test_rebuild_instance_with_scheduler_group_failure(self,
  1111. state_mock,
  1112. bs_mock,
  1113. select_dest_mock,
  1114. sig_mock,
  1115. rebuild_mock):
  1116. inst_obj = self._create_fake_instance_obj()
  1117. rebuild_args, _ = self._prepare_rebuild_args({'host': None})
  1118. request_spec = {}
  1119. bs_mock.return_value = request_spec
  1120. exception = exc.UnsupportedPolicyException(reason='')
  1121. sig_mock.side_effect = exception
  1122. # build_instances() is a cast, we need to wait for it to complete
  1123. self.useFixture(cast_as_call.CastAsCall(self))
  1124. # Create the migration record (normally created by the compute API).
  1125. migration = objects.Migration(self.context,
  1126. source_compute=inst_obj.host,
  1127. source_node=inst_obj.node,
  1128. instance_uuid=inst_obj.uuid,
  1129. status='accepted',
  1130. migration_type='evacuation')
  1131. migration.create()
  1132. self.assertRaises(exc.UnsupportedPolicyException,
  1133. self.conductor.rebuild_instance,
  1134. self.context,
  1135. inst_obj,
  1136. **rebuild_args)
  1137. updates = {'vm_state': vm_states.ACTIVE, 'task_state': None}
  1138. state_mock.assert_called_once_with(self.context, inst_obj.uuid,
  1139. 'rebuild_server', updates,
  1140. exception, mock.ANY)
  1141. self.assertFalse(select_dest_mock.called)
  1142. self.assertFalse(rebuild_mock.called)
  1143. # Assert the migration status was updated.
  1144. migration = objects.Migration.get_by_id(self.context, migration.id)
  1145. self.assertEqual('error', migration.status)
  1146. def test_rebuild_instance_evacuate_migration_record(self):
  1147. inst_obj = self._create_fake_instance_obj()
  1148. migration = objects.Migration(context=self.context,
  1149. source_compute=inst_obj.host,
  1150. source_node=inst_obj.node,
  1151. instance_uuid=inst_obj.uuid,
  1152. status='accepted',
  1153. migration_type='evacuation')
  1154. rebuild_args, compute_args = self._prepare_rebuild_args(
  1155. {'host': inst_obj.host, 'migration': migration})
  1156. with test.nested(
  1157. mock.patch.object(self.conductor_manager.compute_rpcapi,
  1158. 'rebuild_instance'),
  1159. mock.patch.object(self.conductor_manager.scheduler_client,
  1160. 'select_destinations'),
  1161. mock.patch.object(objects.Migration, 'get_by_instance_and_status',
  1162. return_value=migration)
  1163. ) as (rebuild_mock, select_dest_mock, get_migration_mock):
  1164. self.conductor_manager.rebuild_instance(context=self.context,
  1165. instance=inst_obj,
  1166. **rebuild_args)
  1167. self.assertFalse(select_dest_mock.called)
  1168. rebuild_mock.assert_called_once_with(self.context,
  1169. instance=inst_obj,
  1170. **compute_args)
  1171. def test_rebuild_instance_with_request_spec(self):
  1172. inst_obj = self._create_fake_instance_obj()
  1173. inst_obj.host = 'noselect'
  1174. expected_host = 'thebesthost'
  1175. expected_node = 'thebestnode'
  1176. expected_limits = 'fake-limits'
  1177. fake_spec = objects.RequestSpec(ignore_hosts=[])
  1178. rebuild_args, compute_args = self._prepare_rebuild_args(
  1179. {'host': None, 'node': expected_node, 'limits': expected_limits,
  1180. 'request_spec': fake_spec})
  1181. with test.nested(
  1182. mock.patch.object(self.conductor_manager.compute_rpcapi,
  1183. 'rebuild_instance'),
  1184. mock.patch.object(scheduler_utils, 'setup_instance_group',
  1185. return_value=False),
  1186. mock.patch.object(self.conductor_manager.scheduler_client,
  1187. 'select_destinations',
  1188. return_value=[{'host': expected_host,
  1189. 'nodename': expected_node,
  1190. 'limits': expected_limits}]),
  1191. mock.patch.object(fake_spec, 'reset_forced_destinations'),
  1192. ) as (rebuild_mock, sig_mock, select_dest_mock, reset_fd):
  1193. self.conductor_manager.rebuild_instance(context=self.context,
  1194. instance=inst_obj,
  1195. **rebuild_args)
  1196. reset_fd.assert_called_once_with()
  1197. select_dest_mock.assert_called_once_with(self.context,
  1198. fake_spec, [inst_obj.uuid])
  1199. compute_args['host'] = expected_host
  1200. rebuild_mock.assert_called_once_with(self.context,
  1201. instance=inst_obj,
  1202. **compute_args)
  1203. self.assertEqual('compute.instance.rebuild.scheduled',
  1204. fake_notifier.NOTIFICATIONS[0].event_type)
  1205. class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
  1206. """ComputeTaskManager Tests."""
  1207. NUMBER_OF_CELLS = 2
  1208. def setUp(self):
  1209. super(ConductorTaskTestCase, self).setUp()
  1210. self.conductor = conductor_manager.ComputeTaskManager()
  1211. self.conductor_manager = self.conductor
  1212. params = {}
  1213. self.ctxt = params['context'] = context.RequestContext(
  1214. 'fake-user', 'fake-project').elevated()
  1215. build_request = fake_build_request.fake_req_obj(self.ctxt)
  1216. del build_request.instance.id
  1217. build_request.create()
  1218. params['build_requests'] = objects.BuildRequestList(
  1219. objects=[build_request])
  1220. im = objects.InstanceMapping(
  1221. self.ctxt, instance_uuid=build_request.instance.uuid,
  1222. cell_mapping=None, project_id=self.ctxt.project_id)
  1223. im.create()
  1224. rs = fake_request_spec.fake_spec_obj(remove_id=True)
  1225. rs._context = self.ctxt
  1226. rs.instance_uuid = build_request.instance_uuid
  1227. rs.instance_group = None
  1228. rs.retry = None
  1229. rs.limits = None
  1230. rs.create()
  1231. params['request_specs'] = [rs]
  1232. params['image'] = {'fake_data': 'should_pass_silently'}
  1233. params['admin_password'] = 'admin_password',
  1234. params['injected_files'] = 'injected_files'
  1235. params['requested_networks'] = None
  1236. bdm = objects.BlockDeviceMapping(self.ctxt, **dict(
  1237. source_type='blank', destination_type='local',
  1238. guest_format='foo', device_type='disk', disk_bus='',
  1239. boot_index=1, device_name='xvda', delete_on_termination=False,
  1240. snapshot_id=None, volume_id=None, volume_size=1,
  1241. image_id='bar', no_device=False, connection_info=None,
  1242. tag=''))
  1243. params['block_device_mapping'] = objects.BlockDeviceMappingList(
  1244. objects=[bdm])
  1245. tag = objects.Tag(self.ctxt, tag='tag1')
  1246. params['tags'] = objects.TagList(objects=[tag])
  1247. self.params = params
  1248. @mock.patch('nova.availability_zones.get_host_availability_zone')
  1249. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1250. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1251. def _do_schedule_and_build_instances_test(self, params,
  1252. select_destinations,
  1253. build_and_run_instance,
  1254. get_az):
  1255. select_destinations.return_value = [{'host': 'fake-host',
  1256. 'nodename': 'fake-nodename',
  1257. 'limits': None}]
  1258. get_az.return_value = 'myaz'
  1259. details = {}
  1260. def _build_and_run_instance(ctxt, *args, **kwargs):
  1261. details['instance'] = kwargs['instance']
  1262. self.assertTrue(kwargs['instance'].id)
  1263. self.assertTrue(kwargs['filter_properties'].get('retry'))
  1264. self.assertEqual(1, len(kwargs['block_device_mapping']))
  1265. # FIXME(danms): How to validate the db connection here?
  1266. self.start_service('compute', host='fake-host')
  1267. build_and_run_instance.side_effect = _build_and_run_instance
  1268. self.conductor.schedule_and_build_instances(**params)
  1269. self.assertTrue(build_and_run_instance.called)
  1270. get_az.assert_called_once_with(mock.ANY, 'fake-host')
  1271. instance_uuid = details['instance'].uuid
  1272. bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
  1273. self.ctxt, instance_uuid)
  1274. ephemeral = list(filter(block_device.new_format_is_ephemeral, bdms))
  1275. self.assertEqual(1, len(ephemeral))
  1276. swap = list(filter(block_device.new_format_is_swap, bdms))
  1277. self.assertEqual(0, len(swap))
  1278. self.assertEqual(1, ephemeral[0].volume_size)
  1279. return instance_uuid
  1280. def test_schedule_and_build_instances(self):
  1281. instance_uuid = self._do_schedule_and_build_instances_test(
  1282. self.params)
  1283. cells = objects.CellMappingList.get_all(self.ctxt)
  1284. # NOTE(danms): Assert that we created the InstanceAction in the
  1285. # correct cell
  1286. # NOTE(Kevin Zheng): Also assert tags in the correct cell
  1287. for cell in cells:
  1288. with context.target_cell(self.ctxt, cell) as cctxt:
  1289. actions = objects.InstanceActionList.get_by_instance_uuid(
  1290. cctxt, instance_uuid)
  1291. if cell.name == 'cell1':
  1292. self.assertEqual(1, len(actions))
  1293. tags = objects.TagList.get_by_resource_id(
  1294. cctxt, instance_uuid)
  1295. self.assertEqual(1, len(tags))
  1296. else:
  1297. self.assertEqual(0, len(actions))
  1298. def test_schedule_and_build_instances_no_tags_provided(self):
  1299. params = copy.deepcopy(self.params)
  1300. del params['tags']
  1301. instance_uuid = self._do_schedule_and_build_instances_test(params)
  1302. cells = objects.CellMappingList.get_all(self.ctxt)
  1303. # NOTE(danms): Assert that we created the InstanceAction in the
  1304. # correct cell
  1305. # NOTE(Kevin Zheng): Also assert tags in the correct cell
  1306. for cell in cells:
  1307. with context.target_cell(self.ctxt, cell) as cctxt:
  1308. actions = objects.InstanceActionList.get_by_instance_uuid(
  1309. cctxt, instance_uuid)
  1310. if cell.name == 'cell1':
  1311. self.assertEqual(1, len(actions))
  1312. tags = objects.TagList.get_by_resource_id(
  1313. cctxt, instance_uuid)
  1314. self.assertEqual(0, len(tags))
  1315. else:
  1316. self.assertEqual(0, len(actions))
  1317. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1318. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1319. @mock.patch('nova.objects.HostMapping.get_by_host')
  1320. def test_schedule_and_build_multiple_instances(self,
  1321. get_hostmapping,
  1322. select_destinations,
  1323. build_and_run_instance):
  1324. # This list needs to match the number of build_requests and the number
  1325. # of request_specs in params.
  1326. select_destinations.return_value = [{'host': 'fake-host',
  1327. 'nodename': 'fake-nodename',
  1328. 'limits': None},
  1329. {'host': 'fake-host',
  1330. 'nodename': 'fake-nodename',
  1331. 'limits': None},
  1332. {'host': 'fake-host2',
  1333. 'nodename': 'fake-nodename2',
  1334. 'limits': None},
  1335. {'host': 'fake-host2',
  1336. 'nodename': 'fake-nodename2',
  1337. 'limits': None}]
  1338. params = self.params
  1339. self.start_service('compute', host='fake-host')
  1340. self.start_service('compute', host='fake-host2')
  1341. # Because of the cache, this should only be called twice,
  1342. # once for the first and once for the third request.
  1343. get_hostmapping.side_effect = self.host_mappings.values()
  1344. # create three additional build requests for a total of four
  1345. for x in range(3):
  1346. build_request = fake_build_request.fake_req_obj(self.ctxt)
  1347. del build_request.instance.id
  1348. build_request.create()
  1349. params['build_requests'].objects.append(build_request)
  1350. im2 = objects.InstanceMapping(
  1351. self.ctxt, instance_uuid=build_request.instance.uuid,
  1352. cell_mapping=None, project_id=self.ctxt.project_id)
  1353. im2.create()
  1354. params['request_specs'].append(objects.RequestSpec(
  1355. instance_uuid=build_request.instance_uuid,
  1356. instance_group=None))
  1357. # Now let's have some fun and delete the third build request before
  1358. # passing the object on to schedule_and_build_instances so that the
  1359. # instance will be created for that build request but when it calls
  1360. # BuildRequest.destroy(), it will raise BuildRequestNotFound and we'll
  1361. # cleanup the instance instead of passing it to build_and_run_instance
  1362. # and we make sure that the fourth build request still gets processed.
  1363. deleted_build_request = params['build_requests'][2]
  1364. deleted_build_request.destroy()
  1365. def _build_and_run_instance(ctxt, *args, **kwargs):
  1366. # Make sure the instance wasn't the one that was deleted.
  1367. instance = kwargs['instance']
  1368. self.assertNotEqual(deleted_build_request.instance_uuid,
  1369. instance.uuid)
  1370. # This just makes sure that the instance was created in the DB.
  1371. self.assertTrue(kwargs['instance'].id)
  1372. self.assertEqual(1, len(kwargs['block_device_mapping']))
  1373. # FIXME(danms): How to validate the db connection here?
  1374. build_and_run_instance.side_effect = _build_and_run_instance
  1375. self.conductor.schedule_and_build_instances(**params)
  1376. self.assertEqual(3, build_and_run_instance.call_count)
  1377. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1378. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1379. @mock.patch('nova.objects.HostMapping.get_by_host')
  1380. def test_schedule_and_build_multiple_cells(
  1381. self, get_hostmapping, select_destinations,
  1382. build_and_run_instance):
  1383. """Test that creates two instances in separate cells."""
  1384. # This list needs to match the number of build_requests and the number
  1385. # of request_specs in params.
  1386. select_destinations.return_value = [{'host': 'fake-host',
  1387. 'nodename': 'fake-nodename',
  1388. 'limits': None},
  1389. {'host': 'fake-host2',
  1390. 'nodename': 'fake-nodename2',
  1391. 'limits': None}]
  1392. params = self.params
  1393. # The cells are created in the base TestCase setup.
  1394. self.start_service('compute', host='fake-host', cell='cell1')
  1395. self.start_service('compute', host='fake-host2', cell='cell2')
  1396. get_hostmapping.side_effect = self.host_mappings.values()
  1397. # create an additional build request and request spec
  1398. build_request = fake_build_request.fake_req_obj(self.ctxt)
  1399. del build_request.instance.id
  1400. build_request.create()
  1401. params['build_requests'].objects.append(build_request)
  1402. im2 = objects.InstanceMapping(
  1403. self.ctxt, instance_uuid=build_request.instance.uuid,
  1404. cell_mapping=None, project_id=self.ctxt.project_id)
  1405. im2.create()
  1406. params['request_specs'].append(objects.RequestSpec(
  1407. instance_uuid=build_request.instance_uuid,
  1408. instance_group=None))
  1409. instance_cells = set()
  1410. def _build_and_run_instance(ctxt, *args, **kwargs):
  1411. instance = kwargs['instance']
  1412. # Keep track of the cells that the instances were created in.
  1413. inst_mapping = objects.InstanceMapping.get_by_instance_uuid(
  1414. ctxt, instance.uuid)
  1415. instance_cells.add(inst_mapping.cell_mapping.uuid)
  1416. build_and_run_instance.side_effect = _build_and_run_instance
  1417. self.conductor.schedule_and_build_instances(**params)
  1418. self.assertEqual(2, build_and_run_instance.call_count)
  1419. self.assertEqual(2, len(instance_cells))
  1420. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1421. def test_schedule_and_build_scheduler_failure(self, select_destinations):
  1422. select_destinations.side_effect = Exception
  1423. self.start_service('compute', host='fake-host')
  1424. self.conductor.schedule_and_build_instances(**self.params)
  1425. with conductor_manager.try_target_cell(self.ctxt,
  1426. self.cell_mappings['cell0']):
  1427. instance = objects.Instance.get_by_uuid(
  1428. self.ctxt, self.params['build_requests'][0].instance_uuid)
  1429. self.assertEqual('error', instance.vm_state)
  1430. self.assertIsNone(instance.task_state)
  1431. @mock.patch('nova.objects.TagList.destroy')
  1432. @mock.patch('nova.objects.TagList.create')
  1433. @mock.patch('nova.compute.utils.notify_about_instance_usage')
  1434. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1435. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1436. @mock.patch('nova.objects.BuildRequest.destroy')
  1437. @mock.patch('nova.conductor.manager.ComputeTaskManager._bury_in_cell0')
  1438. def test_schedule_and_build_delete_during_scheduling(self,
  1439. bury,
  1440. br_destroy,
  1441. select_destinations,
  1442. build_and_run,
  1443. notify,
  1444. taglist_create,
  1445. taglist_destroy):
  1446. br_destroy.side_effect = exc.BuildRequestNotFound(uuid='foo')
  1447. self.start_service('compute', host='fake-host')
  1448. select_destinations.return_value = [{'host': 'fake-host',
  1449. 'nodename': 'nodesarestupid',
  1450. 'limits': None}]
  1451. taglist_create.return_value = self.params['tags']
  1452. self.conductor.schedule_and_build_instances(**self.params)
  1453. self.assertFalse(build_and_run.called)
  1454. self.assertFalse(bury.called)
  1455. self.assertTrue(br_destroy.called)
  1456. taglist_destroy.assert_called_once_with(
  1457. test.MatchType(context.RequestContext),
  1458. self.params['build_requests'][0].instance_uuid)
  1459. # Make sure TagList.destroy was called with the targeted context.
  1460. self.assertIsNotNone(taglist_destroy.call_args[0][0].db_connection)
  1461. test_utils.assert_instance_delete_notification_by_uuid(
  1462. notify, self.params['build_requests'][0].instance_uuid,
  1463. self.conductor.notifier, test.MatchType(context.RequestContext),
  1464. expect_targeted_context=True)
  1465. @mock.patch('nova.objects.Instance.destroy')
  1466. @mock.patch('nova.compute.utils.notify_about_instance_usage')
  1467. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1468. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1469. @mock.patch('nova.objects.BuildRequest.destroy')
  1470. @mock.patch('nova.conductor.manager.ComputeTaskManager._bury_in_cell0')
  1471. def test_schedule_and_build_delete_during_scheduling_host_changed(
  1472. self, bury, br_destroy, select_destinations,
  1473. build_and_run, notify, instance_destroy):
  1474. br_destroy.side_effect = exc.BuildRequestNotFound(uuid='foo')
  1475. instance_destroy.side_effect = [
  1476. exc.ObjectActionError(action='destroy',
  1477. reason='host changed'),
  1478. None,
  1479. ]
  1480. self.start_service('compute', host='fake-host')
  1481. select_destinations.return_value = [{'host': 'fake-host',
  1482. 'nodename': 'nodesarestupid',
  1483. 'limits': None}]
  1484. self.conductor.schedule_and_build_instances(**self.params)
  1485. self.assertFalse(build_and_run.called)
  1486. self.assertFalse(bury.called)
  1487. self.assertTrue(br_destroy.called)
  1488. self.assertEqual(2, instance_destroy.call_count)
  1489. test_utils.assert_instance_delete_notification_by_uuid(
  1490. notify, self.params['build_requests'][0].instance_uuid,
  1491. self.conductor.notifier, test.MatchType(context.RequestContext),
  1492. expect_targeted_context=True)
  1493. @mock.patch('nova.objects.Instance.destroy')
  1494. @mock.patch('nova.compute.utils.notify_about_instance_usage')
  1495. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1496. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1497. @mock.patch('nova.objects.BuildRequest.destroy')
  1498. @mock.patch('nova.conductor.manager.ComputeTaskManager._bury_in_cell0')
  1499. def test_schedule_and_build_delete_during_scheduling_instance_not_found(
  1500. self, bury, br_destroy, select_destinations,
  1501. build_and_run, notify, instance_destroy):
  1502. br_destroy.side_effect = exc.BuildRequestNotFound(uuid='foo')
  1503. instance_destroy.side_effect = [
  1504. exc.InstanceNotFound(instance_id='fake'),
  1505. None,
  1506. ]
  1507. self.start_service('compute', host='fake-host')
  1508. select_destinations.return_value = [{'host': 'fake-host',
  1509. 'nodename': 'nodesarestupid',
  1510. 'limits': None}]
  1511. self.conductor.schedule_and_build_instances(**self.params)
  1512. self.assertFalse(build_and_run.called)
  1513. self.assertFalse(bury.called)
  1514. self.assertTrue(br_destroy.called)
  1515. self.assertEqual(1, instance_destroy.call_count)
  1516. test_utils.assert_instance_delete_notification_by_uuid(
  1517. notify, self.params['build_requests'][0].instance_uuid,
  1518. self.conductor.notifier, test.MatchType(context.RequestContext),
  1519. expect_targeted_context=True)
  1520. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1521. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1522. @mock.patch('nova.objects.BuildRequest.get_by_instance_uuid')
  1523. @mock.patch('nova.objects.BuildRequest.destroy')
  1524. @mock.patch('nova.conductor.manager.ComputeTaskManager._bury_in_cell0')
  1525. @mock.patch('nova.objects.Instance.create')
  1526. def test_schedule_and_build_delete_before_scheduling(self, inst_create,
  1527. bury, br_destroy,
  1528. br_get_by_inst,
  1529. select_destinations,
  1530. build_and_run):
  1531. """Tests the case that the build request is deleted before the instance
  1532. is created, so we do not create the instance.
  1533. """
  1534. inst_uuid = self.params['build_requests'][0].instance.uuid
  1535. br_get_by_inst.side_effect = exc.BuildRequestNotFound(uuid=inst_uuid)
  1536. self.start_service('compute', host='fake-host')
  1537. select_destinations.return_value = [{'host': 'fake-host',
  1538. 'nodename': 'nodesarestupid',
  1539. 'limits': None}]
  1540. self.conductor.schedule_and_build_instances(**self.params)
  1541. # we don't create the instance since the build request is gone
  1542. self.assertFalse(inst_create.called)
  1543. # we don't build the instance since we didn't create it
  1544. self.assertFalse(build_and_run.called)
  1545. # we don't bury the instance in cell0 since it's already deleted
  1546. self.assertFalse(bury.called)
  1547. # we don't don't destroy the build request since it's already gone
  1548. self.assertFalse(br_destroy.called)
  1549. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1550. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1551. @mock.patch('nova.objects.BuildRequest.destroy')
  1552. @mock.patch('nova.conductor.manager.ComputeTaskManager._bury_in_cell0')
  1553. def test_schedule_and_build_unmapped_host_ends_up_in_cell0(self,
  1554. bury,
  1555. br_destroy,
  1556. select_dest,
  1557. build_and_run):
  1558. def _fake_bury(ctxt, request_spec, exc,
  1559. build_requests=None, instances=None):
  1560. self.assertIn('not mapped to any cell', str(exc))
  1561. self.assertEqual(1, len(build_requests))
  1562. self.assertEqual(1, len(instances))
  1563. self.assertEqual(build_requests[0].instance_uuid,
  1564. instances[0].uuid)
  1565. bury.side_effect = _fake_bury
  1566. select_dest.return_value = [{'host': 'missing-host',
  1567. 'nodename': 'nodesarestupid',
  1568. 'limits': None}]
  1569. self.conductor.schedule_and_build_instances(**self.params)
  1570. self.assertTrue(bury.called)
  1571. self.assertFalse(build_and_run.called)
  1572. @mock.patch('nova.objects.quotas.Quotas.check_deltas')
  1573. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1574. def test_schedule_and_build_over_quota_during_recheck(self, mock_select,
  1575. mock_check):
  1576. mock_select.return_value = [{'host': 'fake-host',
  1577. 'nodename': 'fake-nodename',
  1578. 'limits': None}]
  1579. # Simulate a race where the first check passes and the recheck fails.
  1580. # First check occurs in compute/api.
  1581. fake_quotas = {'instances': 5, 'cores': 10, 'ram': 4096}
  1582. fake_headroom = {'instances': 5, 'cores': 10, 'ram': 4096}
  1583. fake_usages = {'instances': 5, 'cores': 10, 'ram': 4096}
  1584. e = exc.OverQuota(overs=['instances'], quotas=fake_quotas,
  1585. headroom=fake_headroom, usages=fake_usages)
  1586. mock_check.side_effect = e
  1587. # This is needed to register the compute node in a cell.
  1588. self.start_service('compute', host='fake-host')
  1589. self.assertRaises(
  1590. exc.TooManyInstances,
  1591. self.conductor.schedule_and_build_instances, **self.params)
  1592. project_id = self.params['context'].project_id
  1593. mock_check.assert_called_once_with(
  1594. self.params['context'], {'instances': 0, 'cores': 0, 'ram': 0},
  1595. project_id, user_id=None, check_project_id=project_id,
  1596. check_user_id=None)
  1597. # Verify we set the instance to ERROR state and set the fault message.
  1598. instances = objects.InstanceList.get_all(self.ctxt)
  1599. self.assertEqual(1, len(instances))
  1600. instance = instances[0]
  1601. self.assertEqual(vm_states.ERROR, instance.vm_state)
  1602. self.assertIsNone(instance.task_state)
  1603. self.assertIn('Quota exceeded', instance.fault.message)
  1604. # Verify we removed the build objects.
  1605. build_requests = objects.BuildRequestList.get_all(self.ctxt)
  1606. # Verify that the instance is mapped to a cell
  1607. inst_mapping = objects.InstanceMapping.get_by_instance_uuid(
  1608. self.ctxt, instance.uuid)
  1609. self.assertIsNotNone(inst_mapping.cell_mapping)
  1610. self.assertEqual(0, len(build_requests))
  1611. @db_api.api_context_manager.reader
  1612. def request_spec_get_all(context):
  1613. return context.session.query(api_models.RequestSpec).all()
  1614. request_specs = request_spec_get_all(self.ctxt)
  1615. self.assertEqual(0, len(request_specs))
  1616. @mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
  1617. @mock.patch('nova.objects.quotas.Quotas.check_deltas')
  1618. @mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
  1619. def test_schedule_and_build_no_quota_recheck(self, mock_select,
  1620. mock_check, mock_build):
  1621. mock_select.return_value = [{'host': 'fake-host',
  1622. 'nodename': 'fake-nodename',
  1623. 'limits': None}]
  1624. # Disable recheck_quota.
  1625. self.flags(recheck_quota=False, group='quota')
  1626. # This is needed to register the compute node in a cell.
  1627. self.start_service('compute', host='fake-host')
  1628. self.conductor.schedule_and_build_instances(**self.params)
  1629. # check_deltas should not have been called a second time. The first
  1630. # check occurs in compute/api.
  1631. mock_check.assert_not_called()
  1632. self.assertTrue(mock_build.called)
  1633. @mock.patch('nova.objects.CellMapping.get_by_uuid')
  1634. def test_bury_in_cell0_no_cell0(self, mock_cm_get):
  1635. mock_cm_get.side_effect = exc.CellMappingNotFound(uuid='0')
  1636. # Without an iterable build_requests in the database, this
  1637. # wouldn't work if it continued past the cell0 lookup.
  1638. self.conductor._bury_in_cell0(self.ctxt, None, None,
  1639. build_requests=1)
  1640. self.assertTrue(mock_cm_get.called)
  1641. def test_bury_in_cell0(self):
  1642. bare_br = self.params['build_requests'][0]
  1643. inst_br = fake_build_request.fake_req_obj(self.ctxt)
  1644. del inst_br.instance.id
  1645. inst_br.create()
  1646. inst = inst_br.get_new_instance(self.ctxt)
  1647. deleted_br = fake_build_request.fake_req_obj(self.ctxt)
  1648. del deleted_br.instance.id
  1649. deleted_br.create()
  1650. deleted_inst = inst_br.get_new_instance(self.ctxt)
  1651. deleted_br.destroy()
  1652. fast_deleted_br = fake_build_request.fake_req_obj(self.ctxt)
  1653. del fast_deleted_br.instance.id
  1654. fast_deleted_br.create()
  1655. fast_deleted_br.destroy()
  1656. self.conductor._bury_in_cell0(self.ctxt,
  1657. self.params['request_specs'][0],
  1658. Exception('Foo'),
  1659. build_requests=[bare_br, inst_br,
  1660. deleted_br,
  1661. fast_deleted_br],
  1662. instances=[inst, deleted_inst])
  1663. with conductor_manager.try_target_cell(self.ctxt,
  1664. self.cell_mappings['cell0']):
  1665. self.ctxt.read_deleted = 'yes'
  1666. build_requests = objects.BuildRequestList.get_all(self.ctxt)
  1667. instances = objects.InstanceList.get_all(self.ctxt)
  1668. self.assertEqual(0, len(build_requests))
  1669. self.assertEqual(4, len(instances))
  1670. inst_states = {inst.uuid: (inst.deleted, inst.vm_state)
  1671. for inst in instances}
  1672. expected = {
  1673. bare_br.instance_uuid: (False, vm_states.ERROR),
  1674. inst_br.instance_uuid: (False, vm_states.ERROR),
  1675. deleted_br.instance_uuid: (True, vm_states.ERROR),
  1676. fast_deleted_br.instance_uuid: (True, vm_states.ERROR),
  1677. }
  1678. self.assertEqual(expected, inst_states)
  1679. def test_reset(self):
  1680. with mock.patch('nova.compute.rpcapi.ComputeAPI') as mock_rpc:
  1681. old_rpcapi = self.conductor_manager.compute_rpcapi
  1682. self.conductor_manager.reset()
  1683. mock_rpc.assert_called_once_with()
  1684. self.assertNotEqual(old_rpcapi,
  1685. self.conductor_manager.compute_rpcapi)
  1686. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  1687. def test_migrate_server_fails_with_rebuild(self, get_im):
  1688. get_im.return_value.cell_mapping = (
  1689. objects.CellMappingList.get_all(self.context)[0])
  1690. instance = fake_instance.fake_instance_obj(self.context,
  1691. vm_state=vm_states.ACTIVE)
  1692. self.assertRaises(NotImplementedError, self.conductor.migrate_server,
  1693. self.context, instance, None, True, True, None, None, None)
  1694. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  1695. def test_migrate_server_fails_with_flavor(self, get_im):
  1696. get_im.return_value.cell_mapping = (
  1697. objects.CellMappingList.get_all(self.context)[0])
  1698. flavor = flavors.get_flavor_by_name('m1.tiny')
  1699. instance = fake_instance.fake_instance_obj(self.context,
  1700. vm_state=vm_states.ACTIVE,
  1701. flavor=flavor)
  1702. self.assertRaises(NotImplementedError, self.conductor.migrate_server,
  1703. self.context, instance, None, True, False, flavor, None, None)
  1704. def _build_request_spec(self, instance):
  1705. return {
  1706. 'instance_properties': {
  1707. 'uuid': instance['uuid'], },
  1708. }
  1709. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  1710. @mock.patch.object(scheduler_utils, 'set_vm_state_and_notify')
  1711. @mock.patch.object(live_migrate.LiveMigrationTask, 'execute')
  1712. def _test_migrate_server_deals_with_expected_exceptions(self, ex,
  1713. mock_execute, mock_set, get_im):
  1714. get_im.return_value.cell_mapping = (
  1715. objects.CellMappingList.get_all(self.context)[0])
  1716. instance = fake_instance.fake_db_instance(uuid=uuids.instance,
  1717. vm_state=vm_states.ACTIVE)
  1718. inst_obj = objects.Instance._from_db_object(
  1719. self.context, objects.Instance(), instance, [])
  1720. mock_execute.side_effect = ex
  1721. self.conductor = utils.ExceptionHelper(self.conductor)
  1722. self.assertRaises(type(ex),
  1723. self.conductor.migrate_server, self.context, inst_obj,
  1724. {'host': 'destination'}, True, False, None, 'block_migration',
  1725. 'disk_over_commit')
  1726. mock_set.assert_called_once_with(self.context,
  1727. inst_obj.uuid,
  1728. 'compute_task', 'migrate_server',
  1729. {'vm_state': vm_states.ACTIVE,
  1730. 'task_state': None,
  1731. 'expected_task_state': task_states.MIGRATING},
  1732. ex, self._build_request_spec(inst_obj))
  1733. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  1734. def test_migrate_server_deals_with_invalidcpuinfo_exception(self, get_im):
  1735. get_im.return_value.cell_mapping = (
  1736. objects.CellMappingList.get_all(self.context)[0])
  1737. instance = fake_instance.fake_db_instance(uuid=uuids.instance,
  1738. vm_state=vm_states.ACTIVE)
  1739. inst_obj = objects.Instance._from_db_object(
  1740. self.context, objects.Instance(), instance, [])
  1741. self.mox.StubOutWithMock(live_migrate.LiveMigrationTask, 'execute')
  1742. self.mox.StubOutWithMock(scheduler_utils,
  1743. 'set_vm_state_and_notify')
  1744. ex = exc.InvalidCPUInfo(reason="invalid cpu info.")
  1745. task = self.conductor._build_live_migrate_task(
  1746. self.context, inst_obj, 'destination', 'block_migration',
  1747. 'disk_over_commit', mox.IsA(objects.Migration))
  1748. task.execute().AndRaise(ex)
  1749. scheduler_utils.set_vm_state_and_notify(self.context,
  1750. inst_obj.uuid,
  1751. 'compute_task', 'migrate_server',
  1752. {'vm_state': vm_states.ACTIVE,
  1753. 'task_state': None,
  1754. 'expected_task_state': task_states.MIGRATING},
  1755. ex, self._build_request_spec(inst_obj))
  1756. self.mox.ReplayAll()
  1757. self.conductor = utils.ExceptionHelper(self.conductor)
  1758. self.assertRaises(exc.InvalidCPUInfo,
  1759. self.conductor.migrate_server, self.context, inst_obj,
  1760. {'host': 'destination'}, True, False, None, 'block_migration',
  1761. 'disk_over_commit')
  1762. def test_migrate_server_deals_with_expected_exception(self):
  1763. exs = [exc.InstanceInvalidState(instance_uuid="fake", attr='',
  1764. state='', method=''),
  1765. exc.DestinationHypervisorTooOld(),
  1766. exc.HypervisorUnavailable(host='dummy'),
  1767. exc.LiveMigrationWithOldNovaNotSupported(),
  1768. exc.MigrationPreCheckError(reason='dummy'),
  1769. exc.MigrationPreCheckClientException(reason='dummy'),
  1770. exc.InvalidSharedStorage(path='dummy', reason='dummy'),
  1771. exc.NoValidHost(reason='dummy'),
  1772. exc.ComputeServiceUnavailable(host='dummy'),
  1773. exc.InvalidHypervisorType(),
  1774. exc.InvalidCPUInfo(reason='dummy'),
  1775. exc.UnableToMigrateToSelf(instance_id='dummy', host='dummy'),
  1776. exc.InvalidLocalStorage(path='dummy', reason='dummy'),
  1777. exc.MigrationSchedulerRPCError(reason='dummy'),
  1778. exc.ComputeHostNotFound(host='dummy')]
  1779. for ex in exs:
  1780. self._test_migrate_server_deals_with_expected_exceptions(ex)
  1781. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  1782. @mock.patch.object(scheduler_utils, 'set_vm_state_and_notify')
  1783. @mock.patch.object(live_migrate.LiveMigrationTask, 'execute')
  1784. def test_migrate_server_deals_with_unexpected_exceptions(self,
  1785. mock_live_migrate, mock_set_state, get_im):
  1786. get_im.return_value.cell_mapping = (
  1787. objects.CellMappingList.get_all(self.context)[0])
  1788. expected_ex = IOError('fake error')
  1789. mock_live_migrate.side_effect = expected_ex
  1790. instance = fake_instance.fake_db_instance()
  1791. inst_obj = objects.Instance._from_db_object(
  1792. self.context, objects.Instance(), instance, [])
  1793. ex = self.assertRaises(exc.MigrationError,
  1794. self.conductor.migrate_server, self.context, inst_obj,
  1795. {'host': 'destination'}, True, False, None, 'block_migration',
  1796. 'disk_over_commit')
  1797. request_spec = {'instance_properties': {
  1798. 'uuid': instance['uuid'], },
  1799. }
  1800. mock_set_state.assert_called_once_with(self.context,
  1801. instance['uuid'],
  1802. 'compute_task', 'migrate_server',
  1803. dict(vm_state=vm_states.ERROR,
  1804. task_state=None,
  1805. expected_task_state=task_states.MIGRATING,),
  1806. expected_ex, request_spec)
  1807. self.assertEqual(ex.kwargs['reason'], six.text_type(expected_ex))
  1808. def test_set_vm_state_and_notify(self):
  1809. self.mox.StubOutWithMock(scheduler_utils,
  1810. 'set_vm_state_and_notify')
  1811. scheduler_utils.set_vm_state_and_notify(
  1812. self.context, 1, 'compute_task', 'method', 'updates',
  1813. 'ex', 'request_spec')
  1814. self.mox.ReplayAll()
  1815. self.conductor._set_vm_state_and_notify(
  1816. self.context, 1, 'method', 'updates', 'ex', 'request_spec')
  1817. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  1818. @mock.patch.object(objects.RequestSpec, 'from_components')
  1819. @mock.patch.object(scheduler_utils, 'setup_instance_group')
  1820. @mock.patch.object(utils, 'get_image_from_system_metadata')
  1821. @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations')
  1822. @mock.patch.object(conductor_manager.ComputeTaskManager,
  1823. '_set_vm_state_and_notify')
  1824. @mock.patch.object(migrate.MigrationTask, 'rollback')
  1825. def test_cold_migrate_no_valid_host_back_in_active_state(
  1826. self, rollback_mock, notify_mock, select_dest_mock,
  1827. metadata_mock, sig_mock, spec_fc_mock, im_mock):
  1828. flavor = flavors.get_flavor_by_name('m1.tiny')
  1829. inst_obj = objects.Instance(
  1830. image_ref='fake-image_ref',
  1831. instance_type_id=flavor['id'],
  1832. vm_state=vm_states.ACTIVE,
  1833. system_metadata={},
  1834. uuid=uuids.instance,
  1835. user_id=fakes.FAKE_USER_ID,
  1836. flavor=flavor,
  1837. availability_zone=None,
  1838. pci_requests=None,
  1839. numa_topology=None)
  1840. resvs = 'fake-resvs'
  1841. image = 'fake-image'
  1842. fake_spec = objects.RequestSpec(image=objects.ImageMeta())
  1843. spec_fc_mock.return_value = fake_spec
  1844. legacy_request_spec = fake_spec.to_legacy_request_spec_dict()
  1845. metadata_mock.return_value = image
  1846. exc_info = exc.NoValidHost(reason="")
  1847. select_dest_mock.side_effect = exc_info
  1848. updates = {'vm_state': vm_states.ACTIVE,
  1849. 'task_state': None}
  1850. im_mock.return_value = objects.InstanceMapping(
  1851. cell_mapping=objects.CellMapping.get_by_uuid(self.context,
  1852. uuids.cell1))
  1853. self.assertRaises(exc.NoValidHost,
  1854. self.conductor._cold_migrate,
  1855. self.context, inst_obj,
  1856. flavor, {}, [resvs],
  1857. True, None)
  1858. metadata_mock.assert_called_with({})
  1859. sig_mock.assert_called_once_with(self.context, fake_spec)
  1860. notify_mock.assert_called_once_with(self.context, inst_obj.uuid,
  1861. 'migrate_server', updates,
  1862. exc_info, legacy_request_spec)
  1863. rollback_mock.assert_called_once_with()
  1864. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  1865. @mock.patch.object(scheduler_utils, 'setup_instance_group')
  1866. @mock.patch.object(objects.RequestSpec, 'from_components')
  1867. @mock.patch.object(utils, 'get_image_from_system_metadata')
  1868. @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations')
  1869. @mock.patch.object(conductor_manager.ComputeTaskManager,
  1870. '_set_vm_state_and_notify')
  1871. @mock.patch.object(migrate.MigrationTask, 'rollback')
  1872. def test_cold_migrate_no_valid_host_back_in_stopped_state(
  1873. self, rollback_mock, notify_mock, select_dest_mock,
  1874. metadata_mock, spec_fc_mock, sig_mock, im_mock):
  1875. flavor = flavors.get_flavor_by_name('m1.tiny')
  1876. inst_obj = objects.Instance(
  1877. image_ref='fake-image_ref',
  1878. vm_state=vm_states.STOPPED,
  1879. instance_type_id=flavor['id'],
  1880. system_metadata={},
  1881. uuid=uuids.instance,
  1882. user_id=fakes.FAKE_USER_ID,
  1883. flavor=flavor,
  1884. numa_topology=None,
  1885. pci_requests=None,
  1886. availability_zone=None)
  1887. image = 'fake-image'
  1888. resvs = 'fake-resvs'
  1889. fake_spec = objects.RequestSpec(image=objects.ImageMeta())
  1890. spec_fc_mock.return_value = fake_spec
  1891. legacy_request_spec = fake_spec.to_legacy_request_spec_dict()
  1892. im_mock.return_value = objects.InstanceMapping(
  1893. cell_mapping=objects.CellMapping.get_by_uuid(self.context,
  1894. uuids.cell1))
  1895. metadata_mock.return_value = image
  1896. exc_info = exc.NoValidHost(reason="")
  1897. select_dest_mock.side_effect = exc_info
  1898. updates = {'vm_state': vm_states.STOPPED,
  1899. 'task_state': None}
  1900. self.assertRaises(exc.NoValidHost,
  1901. self.conductor._cold_migrate,
  1902. self.context, inst_obj,
  1903. flavor, {}, [resvs],
  1904. True, None)
  1905. metadata_mock.assert_called_with({})
  1906. sig_mock.assert_called_once_with(self.context, fake_spec)
  1907. notify_mock.assert_called_once_with(self.context, inst_obj.uuid,
  1908. 'migrate_server', updates,
  1909. exc_info, legacy_request_spec)
  1910. rollback_mock.assert_called_once_with()
  1911. def test_cold_migrate_no_valid_host_error_msg(self):
  1912. flavor = flavors.get_flavor_by_name('m1.tiny')
  1913. inst_obj = objects.Instance(
  1914. image_ref='fake-image_ref',
  1915. vm_state=vm_states.STOPPED,
  1916. instance_type_id=flavor['id'],
  1917. system_metadata={},
  1918. uuid=uuids.instance,
  1919. user_id=fakes.FAKE_USER_ID)
  1920. fake_spec = fake_request_spec.fake_spec_obj()
  1921. resvs = 'fake-resvs'
  1922. image = 'fake-image'
  1923. with test.nested(
  1924. mock.patch.object(utils, 'get_image_from_system_metadata',
  1925. return_value=image),
  1926. mock.patch.object(self.conductor, '_set_vm_state_and_notify'),
  1927. mock.patch.object(migrate.MigrationTask,
  1928. 'execute',
  1929. side_effect=exc.NoValidHost(reason="")),
  1930. mock.patch.object(migrate.MigrationTask, 'rollback')
  1931. ) as (image_mock, set_vm_mock, task_execute_mock,
  1932. task_rollback_mock):
  1933. nvh = self.assertRaises(exc.NoValidHost,
  1934. self.conductor._cold_migrate, self.context,
  1935. inst_obj, flavor, {}, [resvs],
  1936. True, fake_spec)
  1937. self.assertIn('cold migrate', nvh.message)
  1938. @mock.patch.object(utils, 'get_image_from_system_metadata')
  1939. @mock.patch.object(migrate.MigrationTask, 'execute')
  1940. @mock.patch.object(migrate.MigrationTask, 'rollback')
  1941. @mock.patch.object(conductor_manager.ComputeTaskManager,
  1942. '_set_vm_state_and_notify')
  1943. @mock.patch.object(objects.RequestSpec, 'from_components')
  1944. def test_cold_migrate_no_valid_host_in_group(self,
  1945. spec_fc_mock,
  1946. set_vm_mock,
  1947. task_rollback_mock,
  1948. task_exec_mock,
  1949. image_mock):
  1950. flavor = flavors.get_flavor_by_name('m1.tiny')
  1951. inst_obj = objects.Instance(
  1952. image_ref='fake-image_ref',
  1953. vm_state=vm_states.STOPPED,
  1954. instance_type_id=flavor['id'],
  1955. system_metadata={},
  1956. uuid=uuids.instance,
  1957. user_id=fakes.FAKE_USER_ID,
  1958. flavor=flavor,
  1959. numa_topology=None,
  1960. pci_requests=None,
  1961. availability_zone=None)
  1962. resvs = 'fake-resvs'
  1963. image = 'fake-image'
  1964. exception = exc.UnsupportedPolicyException(reason='')
  1965. fake_spec = fake_request_spec.fake_spec_obj()
  1966. spec_fc_mock.return_value = fake_spec
  1967. legacy_request_spec = fake_spec.to_legacy_request_spec_dict()
  1968. image_mock.return_value = image
  1969. task_exec_mock.side_effect = exception
  1970. self.assertRaises(exc.UnsupportedPolicyException,
  1971. self.conductor._cold_migrate, self.context,
  1972. inst_obj, flavor, {}, [resvs], True, None)
  1973. updates = {'vm_state': vm_states.STOPPED, 'task_state': None}
  1974. set_vm_mock.assert_called_once_with(self.context, inst_obj.uuid,
  1975. 'migrate_server', updates,
  1976. exception, legacy_request_spec)
  1977. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  1978. @mock.patch.object(scheduler_utils, 'setup_instance_group')
  1979. @mock.patch.object(objects.RequestSpec, 'from_components')
  1980. @mock.patch.object(utils, 'get_image_from_system_metadata')
  1981. @mock.patch.object(scheduler_client.SchedulerClient, 'select_destinations')
  1982. @mock.patch.object(conductor_manager.ComputeTaskManager,
  1983. '_set_vm_state_and_notify')
  1984. @mock.patch.object(migrate.MigrationTask, 'rollback')
  1985. @mock.patch.object(compute_rpcapi.ComputeAPI, 'prep_resize')
  1986. def test_cold_migrate_exception_host_in_error_state_and_raise(
  1987. self, prep_resize_mock, rollback_mock, notify_mock,
  1988. select_dest_mock, metadata_mock, spec_fc_mock,
  1989. sig_mock, im_mock):
  1990. flavor = flavors.get_flavor_by_name('m1.tiny')
  1991. inst_obj = objects.Instance(
  1992. image_ref='fake-image_ref',
  1993. vm_state=vm_states.STOPPED,
  1994. instance_type_id=flavor['id'],
  1995. system_metadata={},
  1996. uuid=uuids.instance,
  1997. user_id=fakes.FAKE_USER_ID,
  1998. flavor=flavor,
  1999. availability_zone=None,
  2000. pci_requests=None,
  2001. numa_topology=None)
  2002. image = 'fake-image'
  2003. resvs = 'fake-resvs'
  2004. fake_spec = objects.RequestSpec(image=objects.ImageMeta())
  2005. legacy_request_spec = fake_spec.to_legacy_request_spec_dict()
  2006. spec_fc_mock.return_value = fake_spec
  2007. im_mock.return_value = objects.InstanceMapping(
  2008. cell_mapping=objects.CellMapping.get_by_uuid(self.context,
  2009. uuids.cell1))
  2010. hosts = [dict(host='host1', nodename=None, limits={})]
  2011. metadata_mock.return_value = image
  2012. exc_info = test.TestingException('something happened')
  2013. select_dest_mock.return_value = hosts
  2014. updates = {'vm_state': vm_states.STOPPED,
  2015. 'task_state': None}
  2016. prep_resize_mock.side_effect = exc_info
  2017. self.assertRaises(test.TestingException,
  2018. self.conductor._cold_migrate,
  2019. self.context, inst_obj, flavor,
  2020. {}, [resvs], True, None)
  2021. # Filter properties are populated during code execution
  2022. legacy_filter_props = {'retry': {'num_attempts': 1,
  2023. 'hosts': [['host1', None]]},
  2024. 'limits': {}}
  2025. metadata_mock.assert_called_with({})
  2026. sig_mock.assert_called_once_with(self.context, fake_spec)
  2027. select_dest_mock.assert_called_once_with(
  2028. self.context, fake_spec, [inst_obj.uuid])
  2029. prep_resize_mock.assert_called_once_with(
  2030. self.context, inst_obj, legacy_request_spec['image'],
  2031. flavor, hosts[0]['host'], [resvs],
  2032. request_spec=legacy_request_spec,
  2033. filter_properties=legacy_filter_props,
  2034. node=hosts[0]['nodename'], clean_shutdown=True)
  2035. notify_mock.assert_called_once_with(self.context, inst_obj.uuid,
  2036. 'migrate_server', updates,
  2037. exc_info, legacy_request_spec)
  2038. rollback_mock.assert_called_once_with()
  2039. @mock.patch.object(objects.RequestSpec, 'save')
  2040. @mock.patch.object(migrate.MigrationTask, 'execute')
  2041. @mock.patch.object(utils, 'get_image_from_system_metadata')
  2042. def test_cold_migrate_updates_flavor_if_existing_reqspec(self,
  2043. image_mock,
  2044. task_exec_mock,
  2045. spec_save_mock):
  2046. flavor = flavors.get_flavor_by_name('m1.tiny')
  2047. inst_obj = objects.Instance(
  2048. image_ref='fake-image_ref',
  2049. vm_state=vm_states.STOPPED,
  2050. instance_type_id=flavor['id'],
  2051. system_metadata={},
  2052. uuid=uuids.instance,
  2053. user_id=fakes.FAKE_USER_ID,
  2054. flavor=flavor,
  2055. availability_zone=None,
  2056. pci_requests=None,
  2057. numa_topology=None)
  2058. resvs = 'fake-resvs'
  2059. image = 'fake-image'
  2060. fake_spec = fake_request_spec.fake_spec_obj()
  2061. image_mock.return_value = image
  2062. # Just make sure we have an original flavor which is different from
  2063. # the new one
  2064. self.assertNotEqual(flavor, fake_spec.flavor)
  2065. with mock.patch.object(
  2066. fake_spec, 'to_legacy_request_spec_dict') as spec_to_dict_mock:
  2067. self.conductor._cold_migrate(self.context, inst_obj, flavor, {},
  2068. [resvs], True, fake_spec)
  2069. spec_to_dict_mock.assert_called_once_with()
  2070. # Now the RequestSpec should be updated...
  2071. self.assertEqual(flavor, fake_spec.flavor)
  2072. # ...and persisted
  2073. spec_save_mock.assert_called_once_with()
  2074. def test_resize_no_valid_host_error_msg(self):
  2075. flavor = flavors.get_flavor_by_name('m1.tiny')
  2076. flavor_new = flavors.get_flavor_by_name('m1.small')
  2077. inst_obj = objects.Instance(
  2078. image_ref='fake-image_ref',
  2079. vm_state=vm_states.STOPPED,
  2080. instance_type_id=flavor['id'],
  2081. system_metadata={},
  2082. uuid=uuids.instance,
  2083. user_id=fakes.FAKE_USER_ID)
  2084. fake_spec = fake_request_spec.fake_spec_obj()
  2085. resvs = 'fake-resvs'
  2086. image = 'fake-image'
  2087. with test.nested(
  2088. mock.patch.object(utils, 'get_image_from_system_metadata',
  2089. return_value=image),
  2090. mock.patch.object(scheduler_utils, 'build_request_spec',
  2091. return_value=fake_spec),
  2092. mock.patch.object(self.conductor, '_set_vm_state_and_notify'),
  2093. mock.patch.object(migrate.MigrationTask,
  2094. 'execute',
  2095. side_effect=exc.NoValidHost(reason="")),
  2096. mock.patch.object(migrate.MigrationTask, 'rollback')
  2097. ) as (image_mock, brs_mock, vm_st_mock, task_execute_mock,
  2098. task_rb_mock):
  2099. nvh = self.assertRaises(exc.NoValidHost,
  2100. self.conductor._cold_migrate, self.context,
  2101. inst_obj, flavor_new, {},
  2102. [resvs], True, fake_spec)
  2103. self.assertIn('resize', nvh.message)
  2104. @mock.patch('nova.objects.BuildRequest.get_by_instance_uuid')
  2105. @mock.patch.object(objects.RequestSpec, 'from_primitives')
  2106. def test_build_instances_instance_not_found(self, fp, _mock_buildreq):
  2107. fake_spec = objects.RequestSpec()
  2108. fp.return_value = fake_spec
  2109. instances = [fake_instance.fake_instance_obj(self.context)
  2110. for i in range(2)]
  2111. self.mox.StubOutWithMock(instances[0], 'save')
  2112. self.mox.StubOutWithMock(instances[1], 'save')
  2113. image = {'fake-data': 'should_pass_silently'}
  2114. spec = {'fake': 'specs',
  2115. 'instance_properties': instances[0]}
  2116. self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
  2117. self.mox.StubOutWithMock(self.conductor_manager, '_schedule_instances')
  2118. self.mox.StubOutWithMock(self.conductor_manager.compute_rpcapi,
  2119. 'build_and_run_instance')
  2120. scheduler_utils.build_request_spec(self.context, image,
  2121. mox.IgnoreArg()).AndReturn(spec)
  2122. filter_properties = {'retry': {'num_attempts': 1, 'hosts': []}}
  2123. inst_uuids = [inst.uuid for inst in instances]
  2124. self.conductor_manager._schedule_instances(self.context,
  2125. fake_spec, inst_uuids).AndReturn(
  2126. [{'host': 'host1', 'nodename': 'node1', 'limits': []},
  2127. {'host': 'host2', 'nodename': 'node2', 'limits': []}])
  2128. instances[0].save().AndRaise(
  2129. exc.InstanceNotFound(instance_id=instances[0].uuid))
  2130. instances[1].save()
  2131. self.conductor_manager.compute_rpcapi.build_and_run_instance(
  2132. self.context, instance=instances[1], host='host2',
  2133. image={'fake-data': 'should_pass_silently'}, request_spec=spec,
  2134. filter_properties={'limits': [],
  2135. 'retry': {'num_attempts': 1,
  2136. 'hosts': [['host2',
  2137. 'node2']]}},
  2138. admin_password='admin_password',
  2139. injected_files='injected_files',
  2140. requested_networks=None,
  2141. security_groups='security_groups',
  2142. block_device_mapping=mox.IsA(objects.BlockDeviceMappingList),
  2143. node='node2', limits=[])
  2144. self.mox.ReplayAll()
  2145. # build_instances() is a cast, we need to wait for it to complete
  2146. self.useFixture(cast_as_call.CastAsCall(self))
  2147. self.conductor.build_instances(self.context,
  2148. instances=instances,
  2149. image=image,
  2150. filter_properties={},
  2151. admin_password='admin_password',
  2152. injected_files='injected_files',
  2153. requested_networks=None,
  2154. security_groups='security_groups',
  2155. block_device_mapping='block_device_mapping',
  2156. legacy_bdm=False)
  2157. fp.assert_called_once_with(self.context, spec, filter_properties)
  2158. @mock.patch.object(scheduler_utils, 'setup_instance_group')
  2159. @mock.patch.object(scheduler_utils, 'build_request_spec')
  2160. def test_build_instances_info_cache_not_found(self, build_request_spec,
  2161. setup_instance_group):
  2162. instances = [fake_instance.fake_instance_obj(self.context)
  2163. for i in range(2)]
  2164. image = {'fake-data': 'should_pass_silently'}
  2165. destinations = [{'host': 'host1', 'nodename': 'node1', 'limits': []},
  2166. {'host': 'host2', 'nodename': 'node2', 'limits': []}]
  2167. spec = {'fake': 'specs',
  2168. 'instance_properties': instances[0]}
  2169. build_request_spec.return_value = spec
  2170. fake_spec = objects.RequestSpec()
  2171. with test.nested(
  2172. mock.patch.object(instances[0], 'save',
  2173. side_effect=exc.InstanceInfoCacheNotFound(
  2174. instance_uuid=instances[0].uuid)),
  2175. mock.patch.object(instances[1], 'save'),
  2176. mock.patch.object(objects.RequestSpec, 'from_primitives',
  2177. return_value=fake_spec),
  2178. mock.patch.object(self.conductor_manager.scheduler_client,
  2179. 'select_destinations', return_value=destinations),
  2180. mock.patch.object(self.conductor_manager.compute_rpcapi,
  2181. 'build_and_run_instance'),
  2182. mock.patch.object(objects.BuildRequest, 'get_by_instance_uuid')
  2183. ) as (inst1_save, inst2_save, from_primitives,
  2184. select_destinations,
  2185. build_and_run_instance, get_buildreq):
  2186. # build_instances() is a cast, we need to wait for it to complete
  2187. self.useFixture(cast_as_call.CastAsCall(self))
  2188. self.conductor.build_instances(self.context,
  2189. instances=instances,
  2190. image=image,
  2191. filter_properties={},
  2192. admin_password='admin_password',
  2193. injected_files='injected_files',
  2194. requested_networks=None,
  2195. security_groups='security_groups',
  2196. block_device_mapping='block_device_mapping',
  2197. legacy_bdm=False)
  2198. setup_instance_group.assert_called_once_with(
  2199. self.context, fake_spec)
  2200. get_buildreq.return_value.destroy.assert_called_once_with()
  2201. build_and_run_instance.assert_called_once_with(self.context,
  2202. instance=instances[1], host='host2', image={'fake-data':
  2203. 'should_pass_silently'}, request_spec=spec,
  2204. filter_properties={'limits': [],
  2205. 'retry': {'num_attempts': 1,
  2206. 'hosts': [['host2',
  2207. 'node2']]}},
  2208. admin_password='admin_password',
  2209. injected_files='injected_files',
  2210. requested_networks=None,
  2211. security_groups='security_groups',
  2212. block_device_mapping=mock.ANY,
  2213. node='node2', limits=[])
  2214. def test_cleanup_allocated_networks_none_requested(self):
  2215. # Tests that we don't deallocate networks if 'none' were specifically
  2216. # requested.
  2217. fake_inst = fake_instance.fake_instance_obj(
  2218. self.context, expected_attrs=['system_metadata'])
  2219. requested_networks = objects.NetworkRequestList(
  2220. objects=[objects.NetworkRequest(network_id='none')])
  2221. with mock.patch.object(self.conductor.network_api,
  2222. 'deallocate_for_instance') as deallocate:
  2223. with mock.patch.object(fake_inst, 'save') as mock_save:
  2224. self.conductor._cleanup_allocated_networks(
  2225. self.context, fake_inst, requested_networks)
  2226. self.assertFalse(deallocate.called)
  2227. self.assertEqual('False',
  2228. fake_inst.system_metadata['network_allocated'],
  2229. fake_inst.system_metadata)
  2230. mock_save.assert_called_once_with()
  2231. def test_cleanup_allocated_networks_auto_or_none_provided(self):
  2232. # Tests that we deallocate networks if auto-allocating networks or
  2233. # requested_networks=None.
  2234. fake_inst = fake_instance.fake_instance_obj(
  2235. self.context, expected_attrs=['system_metadata'])
  2236. requested_networks = objects.NetworkRequestList(
  2237. objects=[objects.NetworkRequest(network_id='auto')])
  2238. for req_net in (requested_networks, None):
  2239. with mock.patch.object(self.conductor.network_api,
  2240. 'deallocate_for_instance') as deallocate:
  2241. with mock.patch.object(fake_inst, 'save') as mock_save:
  2242. self.conductor._cleanup_allocated_networks(
  2243. self.context, fake_inst, req_net)
  2244. deallocate.assert_called_once_with(
  2245. self.context, fake_inst, requested_networks=req_net)
  2246. self.assertEqual('False',
  2247. fake_inst.system_metadata['network_allocated'],
  2248. fake_inst.system_metadata)
  2249. mock_save.assert_called_once_with()
  2250. @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename',
  2251. side_effect=exc.ComputeHostNotFound('source-host'))
  2252. def test_allocate_for_evacuate_dest_host_source_node_not_found_no_reqspec(
  2253. self, get_compute_node):
  2254. """Tests that the source node for the instance isn't found. In this
  2255. case there is no request spec provided.
  2256. """
  2257. instance = self.params['build_requests'][0].instance
  2258. instance.host = 'source-host'
  2259. with mock.patch.object(self.conductor,
  2260. '_set_vm_state_and_notify') as notify:
  2261. ex = self.assertRaises(
  2262. exc.ComputeHostNotFound,
  2263. self.conductor._allocate_for_evacuate_dest_host,
  2264. self.ctxt, instance, 'dest-host')
  2265. get_compute_node.assert_called_once_with(
  2266. self.ctxt, instance.host, instance.node)
  2267. notify.assert_called_once_with(
  2268. self.ctxt, instance.uuid, 'rebuild_server',
  2269. {'vm_state': instance.vm_state, 'task_state': None}, ex, {})
  2270. @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename',
  2271. return_value=objects.ComputeNode(host='source-host'))
  2272. @mock.patch.object(objects.ComputeNode,
  2273. 'get_first_node_by_host_for_old_compat',
  2274. side_effect=exc.ComputeHostNotFound(host='dest-host'))
  2275. def test_allocate_for_evacuate_dest_host_dest_node_not_found_reqspec(
  2276. self, get_dest_node, get_source_node):
  2277. """Tests that the destination node for the request isn't found. In this
  2278. case there is a request spec provided.
  2279. """
  2280. instance = self.params['build_requests'][0].instance
  2281. instance.host = 'source-host'
  2282. reqspec = self.params['request_specs'][0]
  2283. with mock.patch.object(self.conductor,
  2284. '_set_vm_state_and_notify') as notify:
  2285. ex = self.assertRaises(
  2286. exc.ComputeHostNotFound,
  2287. self.conductor._allocate_for_evacuate_dest_host,
  2288. self.ctxt, instance, 'dest-host', reqspec)
  2289. get_source_node.assert_called_once_with(
  2290. self.ctxt, instance.host, instance.node)
  2291. get_dest_node.assert_called_once_with(
  2292. self.ctxt, 'dest-host', use_slave=True)
  2293. notify.assert_called_once_with(
  2294. self.ctxt, instance.uuid, 'rebuild_server',
  2295. {'vm_state': instance.vm_state, 'task_state': None}, ex,
  2296. reqspec.to_legacy_request_spec_dict())
  2297. @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename',
  2298. return_value=objects.ComputeNode(host='source-host'))
  2299. @mock.patch.object(objects.ComputeNode,
  2300. 'get_first_node_by_host_for_old_compat',
  2301. return_value=objects.ComputeNode(host='dest-host'))
  2302. def test_allocate_for_evacuate_dest_host_claim_fails(
  2303. self, get_dest_node, get_source_node):
  2304. """Tests that the allocation claim fails."""
  2305. instance = self.params['build_requests'][0].instance
  2306. instance.host = 'source-host'
  2307. reqspec = self.params['request_specs'][0]
  2308. with test.nested(
  2309. mock.patch.object(self.conductor,
  2310. '_set_vm_state_and_notify'),
  2311. mock.patch.object(scheduler_utils,
  2312. 'claim_resources_on_destination',
  2313. side_effect=exc.NoValidHost(reason='I am full'))
  2314. ) as (
  2315. notify, claim
  2316. ):
  2317. ex = self.assertRaises(
  2318. exc.NoValidHost,
  2319. self.conductor._allocate_for_evacuate_dest_host,
  2320. self.ctxt, instance, 'dest-host', reqspec)
  2321. get_source_node.assert_called_once_with(
  2322. self.ctxt, instance.host, instance.node)
  2323. get_dest_node.assert_called_once_with(
  2324. self.ctxt, 'dest-host', use_slave=True)
  2325. claim.assert_called_once_with(
  2326. self.conductor.scheduler_client.reportclient, instance,
  2327. get_source_node.return_value, get_dest_node.return_value)
  2328. notify.assert_called_once_with(
  2329. self.ctxt, instance.uuid, 'rebuild_server',
  2330. {'vm_state': instance.vm_state, 'task_state': None}, ex,
  2331. reqspec.to_legacy_request_spec_dict())
  2332. class ConductorTaskRPCAPITestCase(_BaseTaskTestCase,
  2333. test_compute.BaseTestCase):
  2334. """Conductor compute_task RPC namespace Tests."""
  2335. def setUp(self):
  2336. super(ConductorTaskRPCAPITestCase, self).setUp()
  2337. self.conductor_service = self.start_service(
  2338. 'conductor', manager='nova.conductor.manager.ConductorManager')
  2339. self.conductor = conductor_rpcapi.ComputeTaskAPI()
  2340. service_manager = self.conductor_service.manager
  2341. self.conductor_manager = service_manager.compute_task_mgr
  2342. def test_live_migrate_instance(self):
  2343. inst = fake_instance.fake_db_instance()
  2344. inst_obj = objects.Instance._from_db_object(
  2345. self.context, objects.Instance(), inst, [])
  2346. version = '1.15'
  2347. scheduler_hint = {'host': 'destination'}
  2348. cctxt_mock = mock.MagicMock()
  2349. @mock.patch.object(self.conductor.client, 'prepare',
  2350. return_value=cctxt_mock)
  2351. def _test(prepare_mock):
  2352. self.conductor.live_migrate_instance(
  2353. self.context, inst_obj, scheduler_hint,
  2354. 'block_migration', 'disk_over_commit', request_spec=None)
  2355. prepare_mock.assert_called_once_with(version=version)
  2356. kw = {'instance': inst_obj, 'scheduler_hint': scheduler_hint,
  2357. 'block_migration': 'block_migration',
  2358. 'disk_over_commit': 'disk_over_commit',
  2359. 'request_spec': None,
  2360. }
  2361. cctxt_mock.cast.assert_called_once_with(
  2362. self.context, 'live_migrate_instance', **kw)
  2363. _test()
  2364. @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
  2365. def test_targets_cell_no_instance_mapping(self, mock_im):
  2366. @conductor_manager.targets_cell
  2367. def test(self, context, instance):
  2368. return mock.sentinel.iransofaraway
  2369. mock_im.side_effect = exc.InstanceMappingNotFound(uuid='something')
  2370. ctxt = mock.MagicMock()
  2371. inst = mock.MagicMock()
  2372. self.assertEqual(mock.sentinel.iransofaraway,
  2373. test(None, ctxt, inst))
  2374. mock_im.assert_called_once_with(ctxt, inst.uuid)
  2375. def test_schedule_and_build_instances_with_tags(self):
  2376. build_request = fake_build_request.fake_req_obj(self.context)
  2377. request_spec = objects.RequestSpec(
  2378. instance_uuid=build_request.instance_uuid)
  2379. image = {'fake_data': 'should_pass_silently'}
  2380. admin_password = 'fake_password'
  2381. injected_file = 'fake'
  2382. requested_network = None
  2383. block_device_mapping = None
  2384. tags = ['fake_tag']
  2385. version = '1.17'
  2386. cctxt_mock = mock.MagicMock()
  2387. @mock.patch.object(self.conductor.client, 'can_send_version',
  2388. return_value=True)
  2389. @mock.patch.object(self.conductor.client, 'prepare',
  2390. return_value=cctxt_mock)
  2391. def _test(prepare_mock, can_send_mock):
  2392. self.conductor.schedule_and_build_instances(
  2393. self.context, build_request, request_spec, image,
  2394. admin_password, injected_file, requested_network,
  2395. block_device_mapping, tags=tags)
  2396. prepare_mock.assert_called_once_with(version=version)
  2397. kw = {'build_requests': build_request,
  2398. 'request_specs': request_spec,
  2399. 'image': jsonutils.to_primitive(image),
  2400. 'admin_password': admin_password,
  2401. 'injected_files': injected_file,
  2402. 'requested_networks': requested_network,
  2403. 'block_device_mapping': block_device_mapping,
  2404. 'tags': tags}
  2405. cctxt_mock.cast.assert_called_once_with(
  2406. self.context, 'schedule_and_build_instances', **kw)
  2407. _test()
  2408. def test_schedule_and_build_instances_with_tags_cannot_send(self):
  2409. build_request = fake_build_request.fake_req_obj(self.context)
  2410. request_spec = objects.RequestSpec(
  2411. instance_uuid=build_request.instance_uuid)
  2412. image = {'fake_data': 'should_pass_silently'}
  2413. admin_password = 'fake_password'
  2414. injected_file = 'fake'
  2415. requested_network = None
  2416. block_device_mapping = None
  2417. tags = ['fake_tag']
  2418. cctxt_mock = mock.MagicMock()
  2419. @mock.patch.object(self.conductor.client, 'can_send_version',
  2420. return_value=False)
  2421. @mock.patch.object(self.conductor.client, 'prepare',
  2422. return_value=cctxt_mock)
  2423. def _test(prepare_mock, can_send_mock):
  2424. self.conductor.schedule_and_build_instances(
  2425. self.context, build_request, request_spec, image,
  2426. admin_password, injected_file, requested_network,
  2427. block_device_mapping, tags=tags)
  2428. prepare_mock.assert_called_once_with(version='1.16')
  2429. kw = {'build_requests': build_request,
  2430. 'request_specs': request_spec,
  2431. 'image': jsonutils.to_primitive(image),
  2432. 'admin_password': admin_password,
  2433. 'injected_files': injected_file,
  2434. 'requested_networks': requested_network,
  2435. 'block_device_mapping': block_device_mapping}
  2436. cctxt_mock.cast.assert_called_once_with(
  2437. self.context, 'schedule_and_build_instances', **kw)
  2438. _test()
  2439. class ConductorTaskAPITestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
  2440. """Compute task API Tests."""
  2441. def setUp(self):
  2442. super(ConductorTaskAPITestCase, self).setUp()
  2443. self.conductor_service = self.start_service(
  2444. 'conductor', manager='nova.conductor.manager.ConductorManager')
  2445. self.conductor = conductor_api.ComputeTaskAPI()
  2446. service_manager = self.conductor_service.manager
  2447. self.conductor_manager = service_manager.compute_task_mgr
  2448. def test_live_migrate(self):
  2449. inst = fake_instance.fake_db_instance()
  2450. inst_obj = objects.Instance._from_db_object(
  2451. self.context, objects.Instance(), inst, [])
  2452. with mock.patch.object(self.conductor.conductor_compute_rpcapi,
  2453. 'migrate_server') as mock_migrate_server:
  2454. self.conductor.live_migrate_instance(self.context, inst_obj,
  2455. 'destination', 'block_migration', 'disk_over_commit')
  2456. mock_migrate_server.assert_called_once_with(
  2457. self.context, inst_obj, {'host': 'destination'}, True, False,
  2458. None, 'block_migration', 'disk_over_commit', None,
  2459. request_spec=None)