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.
 
 
 
 
 

11108 lines
546 KiB

  1. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  2. # not use this file except in compliance with the License. You may obtain
  3. # a copy of the License at
  4. #
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. # License for the specific language governing permissions and limitations
  11. # under the License.
  12. """Unit tests for ComputeManager()."""
  13. import contextlib
  14. import copy
  15. import datetime
  16. import time
  17. from cinderclient import exceptions as cinder_exception
  18. from cursive import exception as cursive_exception
  19. import ddt
  20. from eventlet import event as eventlet_event
  21. from eventlet import timeout as eventlet_timeout
  22. from keystoneauth1 import exceptions as keystone_exception
  23. import mock
  24. import netaddr
  25. from oslo_log import log as logging
  26. import oslo_messaging as messaging
  27. from oslo_serialization import jsonutils
  28. from oslo_service import fixture as service_fixture
  29. from oslo_utils.fixture import uuidsentinel as uuids
  30. from oslo_utils import timeutils
  31. from oslo_utils import uuidutils
  32. import six
  33. import testtools
  34. import nova
  35. from nova.compute import build_results
  36. from nova.compute import manager
  37. from nova.compute import power_state
  38. from nova.compute import resource_tracker
  39. from nova.compute import task_states
  40. from nova.compute import utils as compute_utils
  41. from nova.compute import vm_states
  42. from nova.conductor import api as conductor_api
  43. import nova.conf
  44. from nova import context
  45. from nova.db import api as db
  46. from nova import exception
  47. from nova.network import api as network_api
  48. from nova.network import model as network_model
  49. from nova.network.neutronv2 import api as neutronv2_api
  50. from nova import objects
  51. from nova.objects import base as base_obj
  52. from nova.objects import block_device as block_device_obj
  53. from nova.objects import fields
  54. from nova.objects import instance as instance_obj
  55. from nova.objects import migrate_data as migrate_data_obj
  56. from nova.objects import network_request as net_req_obj
  57. from nova.pci import request as pci_request
  58. from nova.scheduler.client import report
  59. from nova import test
  60. from nova.tests import fixtures
  61. from nova.tests.unit.api.openstack import fakes
  62. from nova.tests.unit.compute import fake_resource_tracker
  63. from nova.tests.unit import fake_block_device
  64. from nova.tests.unit import fake_flavor
  65. from nova.tests.unit import fake_instance
  66. from nova.tests.unit import fake_network
  67. from nova.tests.unit import fake_network_cache_model
  68. from nova.tests.unit import fake_notifier
  69. from nova.tests.unit.objects import test_instance_fault
  70. from nova.tests.unit.objects import test_instance_info_cache
  71. from nova.tests.unit.objects import test_instance_numa
  72. from nova import utils
  73. from nova.virt.block_device import DriverVolumeBlockDevice as driver_bdm_volume
  74. from nova.virt import driver as virt_driver
  75. from nova.virt import event as virtevent
  76. from nova.virt import fake as fake_driver
  77. from nova.virt import hardware
  78. from nova.volume import cinder
  79. CONF = nova.conf.CONF
  80. fake_host_list = [mock.sentinel.host1]
  81. @ddt.ddt
  82. class ComputeManagerUnitTestCase(test.NoDBTestCase,
  83. fake_resource_tracker.RTMockMixin):
  84. def setUp(self):
  85. super(ComputeManagerUnitTestCase, self).setUp()
  86. self.compute = manager.ComputeManager()
  87. self.context = context.RequestContext(fakes.FAKE_USER_ID,
  88. fakes.FAKE_PROJECT_ID)
  89. self.useFixture(fixtures.SpawnIsSynchronousFixture())
  90. self.useFixture(fixtures.EventReporterStub())
  91. self.allocations = {
  92. uuids.provider1: {
  93. "generation": 0,
  94. "resources": {
  95. "VCPU": 1,
  96. "MEMORY_MB": 512
  97. }
  98. }
  99. }
  100. @mock.patch.object(manager.ComputeManager, '_get_power_state')
  101. @mock.patch.object(manager.ComputeManager, '_sync_instance_power_state')
  102. @mock.patch.object(objects.Instance, 'get_by_uuid')
  103. @mock.patch.object(objects.Migration, 'get_by_instance_and_status')
  104. @mock.patch.object(neutronv2_api.API, 'migrate_instance_start')
  105. def _test_handle_lifecycle_event(self, migrate_instance_start,
  106. mock_get_migration, mock_get,
  107. mock_sync, mock_get_power_state,
  108. transition, event_pwr_state,
  109. current_pwr_state):
  110. event = mock.Mock()
  111. mock_get.return_value = fake_instance.fake_instance_obj(self.context,
  112. task_state=task_states.MIGRATING)
  113. event.get_transition.return_value = transition
  114. mock_get_power_state.return_value = current_pwr_state
  115. self.compute.handle_lifecycle_event(event)
  116. expected_attrs = []
  117. if transition in [virtevent.EVENT_LIFECYCLE_POSTCOPY_STARTED,
  118. virtevent.EVENT_LIFECYCLE_MIGRATION_COMPLETED]:
  119. expected_attrs.append('info_cache')
  120. mock_get.assert_called_once_with(
  121. test.MatchType(context.RequestContext),
  122. event.get_instance_uuid.return_value,
  123. expected_attrs=expected_attrs)
  124. if event_pwr_state == current_pwr_state:
  125. mock_sync.assert_called_with(mock.ANY, mock_get.return_value,
  126. event_pwr_state)
  127. else:
  128. self.assertFalse(mock_sync.called)
  129. migrate_finish_statuses = {
  130. virtevent.EVENT_LIFECYCLE_POSTCOPY_STARTED: 'running (post-copy)',
  131. virtevent.EVENT_LIFECYCLE_MIGRATION_COMPLETED: 'running'
  132. }
  133. if transition in migrate_finish_statuses:
  134. mock_get_migration.assert_called_with(
  135. test.MatchType(context.RequestContext),
  136. mock_get.return_value.uuid,
  137. migrate_finish_statuses[transition])
  138. migrate_instance_start.assert_called_once_with(
  139. test.MatchType(context.RequestContext),
  140. mock_get.return_value,
  141. mock_get_migration.return_value)
  142. else:
  143. mock_get_migration.assert_not_called()
  144. migrate_instance_start.assert_not_called()
  145. def test_handle_lifecycle_event(self):
  146. event_map = {virtevent.EVENT_LIFECYCLE_STOPPED: power_state.SHUTDOWN,
  147. virtevent.EVENT_LIFECYCLE_STARTED: power_state.RUNNING,
  148. virtevent.EVENT_LIFECYCLE_PAUSED: power_state.PAUSED,
  149. virtevent.EVENT_LIFECYCLE_RESUMED: power_state.RUNNING,
  150. virtevent.EVENT_LIFECYCLE_SUSPENDED:
  151. power_state.SUSPENDED,
  152. virtevent.EVENT_LIFECYCLE_POSTCOPY_STARTED:
  153. power_state.PAUSED,
  154. virtevent.EVENT_LIFECYCLE_MIGRATION_COMPLETED:
  155. power_state.PAUSED,
  156. }
  157. for transition, pwr_state in event_map.items():
  158. self._test_handle_lifecycle_event(transition=transition,
  159. event_pwr_state=pwr_state,
  160. current_pwr_state=pwr_state)
  161. def test_handle_lifecycle_event_state_mismatch(self):
  162. self._test_handle_lifecycle_event(
  163. transition=virtevent.EVENT_LIFECYCLE_STOPPED,
  164. event_pwr_state=power_state.SHUTDOWN,
  165. current_pwr_state=power_state.RUNNING)
  166. @mock.patch('nova.objects.Instance.get_by_uuid')
  167. @mock.patch('nova.compute.manager.ComputeManager.'
  168. '_sync_instance_power_state')
  169. @mock.patch('nova.objects.Migration.get_by_instance_and_status',
  170. side_effect=exception.MigrationNotFoundByStatus(
  171. instance_id=uuids.instance, status='running (post-copy)'))
  172. def test_handle_lifecycle_event_postcopy_migration_not_found(
  173. self, mock_get_migration, mock_sync, mock_get_instance):
  174. """Tests a EVENT_LIFECYCLE_POSTCOPY_STARTED scenario where the
  175. migration record is not found by the expected status.
  176. """
  177. inst = fake_instance.fake_instance_obj(
  178. self.context, uuid=uuids.instance,
  179. task_state=task_states.MIGRATING)
  180. mock_get_instance.return_value = inst
  181. event = virtevent.LifecycleEvent(
  182. uuids.instance, virtevent.EVENT_LIFECYCLE_POSTCOPY_STARTED)
  183. with mock.patch.object(self.compute, '_get_power_state',
  184. return_value=power_state.PAUSED):
  185. with mock.patch.object(self.compute.network_api,
  186. 'migrate_instance_start') as mig_start:
  187. self.compute.handle_lifecycle_event(event)
  188. # Since we failed to find the migration record, we shouldn't call
  189. # migrate_instance_start.
  190. mig_start.assert_not_called()
  191. mock_get_migration.assert_called_once_with(
  192. test.MatchType(context.RequestContext), uuids.instance,
  193. 'running (post-copy)')
  194. @mock.patch('nova.compute.utils.notify_about_instance_action')
  195. def test_delete_instance_info_cache_delete_ordering(self, mock_notify):
  196. call_tracker = mock.Mock()
  197. call_tracker.clear_events_for_instance.return_value = None
  198. mgr_class = self.compute.__class__
  199. orig_delete = mgr_class._delete_instance
  200. specd_compute = mock.create_autospec(mgr_class)
  201. # spec out everything except for the method we really want
  202. # to test, then use call_tracker to verify call sequence
  203. specd_compute._delete_instance = orig_delete
  204. specd_compute.host = 'compute'
  205. mock_inst = mock.Mock()
  206. mock_inst.uuid = uuids.instance
  207. mock_inst.save = mock.Mock()
  208. mock_inst.destroy = mock.Mock()
  209. mock_inst.system_metadata = mock.Mock()
  210. def _mark_notify(*args, **kwargs):
  211. call_tracker._notify_about_instance_usage(*args, **kwargs)
  212. def _mark_shutdown(*args, **kwargs):
  213. call_tracker._shutdown_instance(*args, **kwargs)
  214. specd_compute.instance_events = call_tracker
  215. specd_compute._notify_about_instance_usage = _mark_notify
  216. specd_compute._shutdown_instance = _mark_shutdown
  217. mock_bdms = mock.Mock()
  218. specd_compute._delete_instance(specd_compute,
  219. self.context,
  220. mock_inst,
  221. mock_bdms)
  222. methods_called = [n for n, a, k in call_tracker.mock_calls]
  223. self.assertEqual(['clear_events_for_instance',
  224. '_notify_about_instance_usage',
  225. '_shutdown_instance',
  226. '_notify_about_instance_usage'],
  227. methods_called)
  228. mock_notify.assert_has_calls([
  229. mock.call(self.context,
  230. mock_inst,
  231. specd_compute.host,
  232. action='delete',
  233. phase='start',
  234. bdms=mock_bdms),
  235. mock.call(self.context,
  236. mock_inst,
  237. specd_compute.host,
  238. action='delete',
  239. phase='end',
  240. bdms=mock_bdms)])
  241. def _make_compute_node(self, hyp_hostname, cn_id):
  242. cn = mock.Mock(spec_set=['hypervisor_hostname', 'id', 'uuid',
  243. 'destroy'])
  244. cn.id = cn_id
  245. cn.hypervisor_hostname = hyp_hostname
  246. return cn
  247. def test_update_available_resource_for_node(self):
  248. rt = self._mock_rt(spec_set=['update_available_resource'])
  249. self.compute._update_available_resource_for_node(
  250. self.context,
  251. mock.sentinel.node,
  252. )
  253. rt.update_available_resource.assert_called_once_with(
  254. self.context,
  255. mock.sentinel.node,
  256. startup=False,
  257. )
  258. @mock.patch('nova.compute.manager.LOG')
  259. def test_update_available_resource_for_node_reshape_failed(self, log_mock):
  260. """ReshapeFailed logs and reraises."""
  261. rt = self._mock_rt(spec_set=['update_available_resource'])
  262. rt.update_available_resource.side_effect = exception.ReshapeFailed(
  263. error='error')
  264. self.assertRaises(exception.ReshapeFailed,
  265. self.compute._update_available_resource_for_node,
  266. self.context, mock.sentinel.node,
  267. # While we're here, unit test the startup kwarg
  268. startup=True)
  269. rt.update_available_resource.assert_called_once_with(
  270. self.context, mock.sentinel.node, startup=True)
  271. log_mock.critical.assert_called_once()
  272. @mock.patch('nova.compute.manager.LOG')
  273. def test_update_available_resource_for_node_reshape_needed(self, log_mock):
  274. """ReshapeNeeded logs and reraises."""
  275. rt = self._mock_rt(spec_set=['update_available_resource'])
  276. rt.update_available_resource.side_effect = exception.ReshapeNeeded()
  277. self.assertRaises(exception.ReshapeNeeded,
  278. self.compute._update_available_resource_for_node,
  279. self.context, mock.sentinel.node,
  280. # While we're here, unit test the startup kwarg
  281. startup=True)
  282. rt.update_available_resource.assert_called_once_with(
  283. self.context, mock.sentinel.node, startup=True)
  284. log_mock.exception.assert_called_once()
  285. @mock.patch.object(manager, 'LOG')
  286. @mock.patch.object(manager.ComputeManager,
  287. '_update_available_resource_for_node')
  288. @mock.patch.object(fake_driver.FakeDriver, 'get_available_nodes')
  289. @mock.patch.object(manager.ComputeManager, '_get_compute_nodes_in_db')
  290. def test_update_available_resource(self, get_db_nodes, get_avail_nodes,
  291. update_mock, mock_log):
  292. mock_rt = self._mock_rt()
  293. rc_mock = self.useFixture(fixtures.fixtures.MockPatchObject(
  294. self.compute, 'reportclient')).mock
  295. rc_mock.delete_resource_provider.side_effect = (
  296. keystone_exception.EndpointNotFound)
  297. db_nodes = [self._make_compute_node('node%s' % i, i)
  298. for i in range(1, 5)]
  299. avail_nodes = set(['node2', 'node3', 'node4', 'node5'])
  300. avail_nodes_l = list(avail_nodes)
  301. get_db_nodes.return_value = db_nodes
  302. get_avail_nodes.return_value = avail_nodes
  303. self.compute.update_available_resource(self.context, startup=True)
  304. get_db_nodes.assert_called_once_with(self.context, use_slave=True,
  305. startup=True)
  306. self.assertEqual(len(avail_nodes_l), update_mock.call_count)
  307. update_mock.assert_has_calls(
  308. [mock.call(self.context, node, startup=True)
  309. for node in avail_nodes_l]
  310. )
  311. # First node in set should have been removed from DB
  312. for db_node in db_nodes:
  313. if db_node.hypervisor_hostname == 'node1':
  314. db_node.destroy.assert_called_once_with()
  315. rc_mock.delete_resource_provider.assert_called_once_with(
  316. self.context, db_node, cascade=True)
  317. mock_rt.remove_node.assert_called_once_with(
  318. 'node1')
  319. mock_log.error.assert_called_once_with(
  320. "Failed to delete compute node resource provider for "
  321. "compute node %s: %s", db_node.uuid, mock.ANY)
  322. else:
  323. self.assertFalse(db_node.destroy.called)
  324. @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
  325. 'delete_resource_provider')
  326. @mock.patch.object(manager.ComputeManager,
  327. '_update_available_resource_for_node')
  328. @mock.patch.object(fake_driver.FakeDriver, 'get_available_nodes')
  329. @mock.patch.object(manager.ComputeManager, '_get_compute_nodes_in_db')
  330. def test_update_available_resource_not_ready(self, get_db_nodes,
  331. get_avail_nodes,
  332. update_mock,
  333. del_rp_mock):
  334. db_nodes = [self._make_compute_node('node1', 1)]
  335. get_db_nodes.return_value = db_nodes
  336. get_avail_nodes.side_effect = exception.VirtDriverNotReady
  337. self.compute.update_available_resource(self.context)
  338. # these shouldn't get processed on VirtDriverNotReady
  339. update_mock.assert_not_called()
  340. del_rp_mock.assert_not_called()
  341. @mock.patch('nova.context.get_admin_context')
  342. def test_pre_start_hook(self, get_admin_context):
  343. """Very simple test just to make sure update_available_resource is
  344. called as expected.
  345. """
  346. with mock.patch.object(
  347. self.compute, 'update_available_resource') as update_res:
  348. self.compute.pre_start_hook()
  349. update_res.assert_called_once_with(
  350. get_admin_context.return_value, startup=True)
  351. @mock.patch.object(objects.ComputeNodeList, 'get_all_by_host',
  352. side_effect=exception.NotFound)
  353. @mock.patch('nova.compute.manager.LOG')
  354. def test_get_compute_nodes_in_db_on_startup(self, mock_log,
  355. get_all_by_host):
  356. """Tests to make sure we only log a warning when we do not find a
  357. compute node on startup since this may be expected.
  358. """
  359. self.assertEqual([], self.compute._get_compute_nodes_in_db(
  360. self.context, startup=True))
  361. get_all_by_host.assert_called_once_with(
  362. self.context, self.compute.host, use_slave=False)
  363. self.assertTrue(mock_log.warning.called)
  364. self.assertFalse(mock_log.error.called)
  365. def _trusted_certs_setup_instance(self, include_trusted_certs=True):
  366. instance = fake_instance.fake_instance_obj(self.context)
  367. if include_trusted_certs:
  368. instance.trusted_certs = objects.trusted_certs.TrustedCerts(
  369. ids=['fake-trusted-cert-1', 'fake-trusted-cert-2'])
  370. else:
  371. instance.trusted_certs = None
  372. return instance
  373. def test_check_trusted_certs_provided_no_support(self):
  374. instance = self._trusted_certs_setup_instance()
  375. with mock.patch.dict(self.compute.driver.capabilities,
  376. supports_trusted_certs=False):
  377. self.assertRaises(exception.BuildAbortException,
  378. self.compute._check_trusted_certs,
  379. instance)
  380. def test_check_trusted_certs_not_provided_no_support(self):
  381. instance = self._trusted_certs_setup_instance(
  382. include_trusted_certs=False)
  383. with mock.patch.dict(self.compute.driver.capabilities,
  384. supports_trusted_certs=False):
  385. self.compute._check_trusted_certs(instance)
  386. def test_check_trusted_certs_provided_support(self):
  387. instance = self._trusted_certs_setup_instance()
  388. with mock.patch.dict(self.compute.driver.capabilities,
  389. supports_trusted_certs=True):
  390. self.compute._check_trusted_certs(instance)
  391. def test_check_device_tagging_no_tagging(self):
  392. bdms = objects.BlockDeviceMappingList(objects=[
  393. objects.BlockDeviceMapping(source_type='volume',
  394. destination_type='volume',
  395. instance_uuid=uuids.instance)])
  396. net_req = net_req_obj.NetworkRequest(tag=None)
  397. net_req_list = net_req_obj.NetworkRequestList(objects=[net_req])
  398. with mock.patch.dict(self.compute.driver.capabilities,
  399. supports_device_tagging=False):
  400. self.compute._check_device_tagging(net_req_list, bdms)
  401. def test_check_device_tagging_no_networks(self):
  402. bdms = objects.BlockDeviceMappingList(objects=[
  403. objects.BlockDeviceMapping(source_type='volume',
  404. destination_type='volume',
  405. instance_uuid=uuids.instance)])
  406. with mock.patch.dict(self.compute.driver.capabilities,
  407. supports_device_tagging=False):
  408. self.compute._check_device_tagging(None, bdms)
  409. def test_check_device_tagging_tagged_net_req_no_virt_support(self):
  410. bdms = objects.BlockDeviceMappingList(objects=[
  411. objects.BlockDeviceMapping(source_type='volume',
  412. destination_type='volume',
  413. instance_uuid=uuids.instance)])
  414. net_req = net_req_obj.NetworkRequest(port_id=uuids.bar, tag='foo')
  415. net_req_list = net_req_obj.NetworkRequestList(objects=[net_req])
  416. with mock.patch.dict(self.compute.driver.capabilities,
  417. supports_device_tagging=False):
  418. self.assertRaises(exception.BuildAbortException,
  419. self.compute._check_device_tagging,
  420. net_req_list, bdms)
  421. def test_check_device_tagging_tagged_bdm_no_driver_support(self):
  422. bdms = objects.BlockDeviceMappingList(objects=[
  423. objects.BlockDeviceMapping(source_type='volume',
  424. destination_type='volume',
  425. tag='foo',
  426. instance_uuid=uuids.instance)])
  427. with mock.patch.dict(self.compute.driver.capabilities,
  428. supports_device_tagging=False):
  429. self.assertRaises(exception.BuildAbortException,
  430. self.compute._check_device_tagging,
  431. None, bdms)
  432. def test_check_device_tagging_tagged_bdm_no_driver_support_declared(self):
  433. bdms = objects.BlockDeviceMappingList(objects=[
  434. objects.BlockDeviceMapping(source_type='volume',
  435. destination_type='volume',
  436. tag='foo',
  437. instance_uuid=uuids.instance)])
  438. with mock.patch.dict(self.compute.driver.capabilities):
  439. self.compute.driver.capabilities.pop('supports_device_tagging',
  440. None)
  441. self.assertRaises(exception.BuildAbortException,
  442. self.compute._check_device_tagging,
  443. None, bdms)
  444. def test_check_device_tagging_tagged_bdm_with_driver_support(self):
  445. bdms = objects.BlockDeviceMappingList(objects=[
  446. objects.BlockDeviceMapping(source_type='volume',
  447. destination_type='volume',
  448. tag='foo',
  449. instance_uuid=uuids.instance)])
  450. net_req = net_req_obj.NetworkRequest(network_id=uuids.bar)
  451. net_req_list = net_req_obj.NetworkRequestList(objects=[net_req])
  452. with mock.patch.dict(self.compute.driver.capabilities,
  453. supports_device_tagging=True):
  454. self.compute._check_device_tagging(net_req_list, bdms)
  455. def test_check_device_tagging_tagged_net_req_with_driver_support(self):
  456. bdms = objects.BlockDeviceMappingList(objects=[
  457. objects.BlockDeviceMapping(source_type='volume',
  458. destination_type='volume',
  459. instance_uuid=uuids.instance)])
  460. net_req = net_req_obj.NetworkRequest(network_id=uuids.bar, tag='foo')
  461. net_req_list = net_req_obj.NetworkRequestList(objects=[net_req])
  462. with mock.patch.dict(self.compute.driver.capabilities,
  463. supports_device_tagging=True):
  464. self.compute._check_device_tagging(net_req_list, bdms)
  465. @mock.patch.object(objects.BlockDeviceMapping, 'create')
  466. @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid',
  467. return_value=objects.BlockDeviceMappingList())
  468. def test_reserve_block_device_name_with_tag(self, mock_get, mock_create):
  469. instance = fake_instance.fake_instance_obj(self.context)
  470. with test.nested(
  471. mock.patch.object(self.compute,
  472. '_get_device_name_for_instance',
  473. return_value='/dev/vda'),
  474. mock.patch.dict(self.compute.driver.capabilities,
  475. supports_tagged_attach_volume=True)):
  476. bdm = self.compute.reserve_block_device_name(
  477. self.context, instance, None, None, None, None, 'foo',
  478. False)
  479. self.assertEqual('foo', bdm.tag)
  480. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  481. def test_reserve_block_device_name_raises(self, _):
  482. with mock.patch.dict(self.compute.driver.capabilities,
  483. supports_tagged_attach_volume=False):
  484. self.assertRaises(exception.VolumeTaggedAttachNotSupported,
  485. self.compute.reserve_block_device_name,
  486. self.context,
  487. fake_instance.fake_instance_obj(self.context),
  488. 'fake_device', 'fake_volume_id', 'fake_disk_bus',
  489. 'fake_device_type', 'foo', False)
  490. @mock.patch.object(objects.BlockDeviceMapping, 'create')
  491. @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid',
  492. return_value=objects.BlockDeviceMappingList())
  493. def test_reserve_block_device_name_multiattach(self, mock_get,
  494. mock_create):
  495. """Tests the case that multiattach=True and the driver supports it."""
  496. instance = fake_instance.fake_instance_obj(self.context)
  497. with test.nested(
  498. mock.patch.object(self.compute,
  499. '_get_device_name_for_instance',
  500. return_value='/dev/vda'),
  501. mock.patch.dict(self.compute.driver.capabilities,
  502. supports_multiattach=True)):
  503. self.compute.reserve_block_device_name(
  504. self.context, instance, device=None, volume_id=uuids.volume_id,
  505. disk_bus=None, device_type=None, tag=None, multiattach=True)
  506. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  507. def test_reserve_block_device_name_multiattach_raises(self, _):
  508. with mock.patch.dict(self.compute.driver.capabilities,
  509. supports_multiattach=False):
  510. self.assertRaises(exception.MultiattachNotSupportedByVirtDriver,
  511. self.compute.reserve_block_device_name,
  512. self.context,
  513. fake_instance.fake_instance_obj(self.context),
  514. 'fake_device', 'fake_volume_id', 'fake_disk_bus',
  515. 'fake_device_type', tag=None, multiattach=True)
  516. @mock.patch.object(objects.Instance, 'save')
  517. @mock.patch.object(time, 'sleep')
  518. def test_allocate_network_succeeds_after_retries(
  519. self, mock_sleep, mock_save):
  520. self.flags(network_allocate_retries=8)
  521. instance = fake_instance.fake_instance_obj(
  522. self.context, expected_attrs=['system_metadata'])
  523. is_vpn = 'fake-is-vpn'
  524. req_networks = objects.NetworkRequestList(
  525. objects=[objects.NetworkRequest(network_id='fake')])
  526. sec_groups = 'fake-sec-groups'
  527. final_result = 'meow'
  528. rp_mapping = {}
  529. expected_sleep_times = [mock.call(t) for t in
  530. (1, 2, 4, 8, 16, 30, 30)]
  531. with mock.patch.object(
  532. self.compute.network_api, 'allocate_for_instance',
  533. side_effect=[test.TestingException()] * 7 + [final_result]):
  534. res = self.compute._allocate_network_async(self.context, instance,
  535. req_networks,
  536. sec_groups,
  537. is_vpn,
  538. rp_mapping)
  539. self.assertEqual(7, mock_sleep.call_count)
  540. mock_sleep.assert_has_calls(expected_sleep_times)
  541. self.assertEqual(final_result, res)
  542. # Ensure save is not called in while allocating networks, the instance
  543. # is saved after the allocation.
  544. self.assertFalse(mock_save.called)
  545. self.assertEqual('True', instance.system_metadata['network_allocated'])
  546. def test_allocate_network_fails(self):
  547. self.flags(network_allocate_retries=0)
  548. instance = {}
  549. is_vpn = 'fake-is-vpn'
  550. req_networks = objects.NetworkRequestList(
  551. objects=[objects.NetworkRequest(network_id='fake')])
  552. sec_groups = 'fake-sec-groups'
  553. rp_mapping = {}
  554. with mock.patch.object(
  555. self.compute.network_api, 'allocate_for_instance',
  556. side_effect=test.TestingException) as mock_allocate:
  557. self.assertRaises(test.TestingException,
  558. self.compute._allocate_network_async,
  559. self.context, instance, req_networks,
  560. sec_groups, is_vpn, rp_mapping)
  561. mock_allocate.assert_called_once_with(
  562. self.context, instance, vpn=is_vpn,
  563. requested_networks=req_networks,
  564. security_groups=sec_groups,
  565. bind_host_id=instance.get('host'),
  566. resource_provider_mapping=rp_mapping)
  567. @mock.patch.object(manager.ComputeManager, '_instance_update')
  568. @mock.patch.object(time, 'sleep')
  569. def test_allocate_network_with_conf_value_is_one(
  570. self, sleep, _instance_update):
  571. self.flags(network_allocate_retries=1)
  572. instance = fake_instance.fake_instance_obj(
  573. self.context, expected_attrs=['system_metadata'])
  574. is_vpn = 'fake-is-vpn'
  575. req_networks = objects.NetworkRequestList(
  576. objects=[objects.NetworkRequest(network_id='fake')])
  577. sec_groups = 'fake-sec-groups'
  578. final_result = 'zhangtralon'
  579. rp_mapping = {}
  580. with mock.patch.object(self.compute.network_api,
  581. 'allocate_for_instance',
  582. side_effect = [test.TestingException(),
  583. final_result]):
  584. res = self.compute._allocate_network_async(self.context, instance,
  585. req_networks,
  586. sec_groups,
  587. is_vpn,
  588. rp_mapping)
  589. self.assertEqual(final_result, res)
  590. self.assertEqual(1, sleep.call_count)
  591. def test_allocate_network_skip_for_no_allocate(self):
  592. # Ensures that we don't do anything if requested_networks has 'none'
  593. # for the network_id.
  594. req_networks = objects.NetworkRequestList(
  595. objects=[objects.NetworkRequest(network_id='none')])
  596. nwinfo = self.compute._allocate_network_async(
  597. self.context, mock.sentinel.instance, req_networks,
  598. security_groups=['default'], is_vpn=False,
  599. resource_provider_mapping={})
  600. self.assertEqual(0, len(nwinfo))
  601. @mock.patch('nova.compute.manager.ComputeManager.'
  602. '_do_build_and_run_instance')
  603. def _test_max_concurrent_builds(self, mock_dbari):
  604. with mock.patch.object(self.compute,
  605. '_build_semaphore') as mock_sem:
  606. instance = objects.Instance(uuid=uuidutils.generate_uuid())
  607. for i in (1, 2, 3):
  608. self.compute.build_and_run_instance(self.context, instance,
  609. mock.sentinel.image,
  610. mock.sentinel.request_spec,
  611. {})
  612. self.assertEqual(3, mock_sem.__enter__.call_count)
  613. def test_max_concurrent_builds_limited(self):
  614. self.flags(max_concurrent_builds=2)
  615. self._test_max_concurrent_builds()
  616. def test_max_concurrent_builds_unlimited(self):
  617. self.flags(max_concurrent_builds=0)
  618. self._test_max_concurrent_builds()
  619. def test_max_concurrent_builds_semaphore_limited(self):
  620. self.flags(max_concurrent_builds=123)
  621. self.assertEqual(123,
  622. manager.ComputeManager()._build_semaphore.balance)
  623. def test_max_concurrent_builds_semaphore_unlimited(self):
  624. self.flags(max_concurrent_builds=0)
  625. compute = manager.ComputeManager()
  626. self.assertEqual(0, compute._build_semaphore.balance)
  627. self.assertIsInstance(compute._build_semaphore,
  628. compute_utils.UnlimitedSemaphore)
  629. def test_nil_out_inst_obj_host_and_node_sets_nil(self):
  630. instance = fake_instance.fake_instance_obj(self.context,
  631. uuid=uuids.instance,
  632. host='foo-host',
  633. node='foo-node',
  634. launched_on='foo-host')
  635. self.assertIsNotNone(instance.host)
  636. self.assertIsNotNone(instance.node)
  637. self.assertIsNotNone(instance.launched_on)
  638. self.compute._nil_out_instance_obj_host_and_node(instance)
  639. self.assertIsNone(instance.host)
  640. self.assertIsNone(instance.node)
  641. self.assertIsNone(instance.launched_on)
  642. def test_init_host(self):
  643. our_host = self.compute.host
  644. inst = fake_instance.fake_db_instance(
  645. vm_state=vm_states.ACTIVE,
  646. info_cache=dict(test_instance_info_cache.fake_info_cache,
  647. network_info=None),
  648. security_groups=None)
  649. startup_instances = [inst, inst, inst]
  650. def _make_instance_list(db_list):
  651. return instance_obj._make_instance_list(
  652. self.context, objects.InstanceList(), db_list, None)
  653. @mock.patch.object(manager.ComputeManager, '_get_nodes')
  654. @mock.patch.object(manager.ComputeManager,
  655. '_error_out_instances_whose_build_was_interrupted')
  656. @mock.patch.object(fake_driver.FakeDriver, 'init_host')
  657. @mock.patch.object(fake_driver.FakeDriver, 'filter_defer_apply_on')
  658. @mock.patch.object(fake_driver.FakeDriver, 'filter_defer_apply_off')
  659. @mock.patch.object(objects.InstanceList, 'get_by_host')
  660. @mock.patch.object(context, 'get_admin_context')
  661. @mock.patch.object(manager.ComputeManager,
  662. '_destroy_evacuated_instances')
  663. @mock.patch.object(manager.ComputeManager,
  664. '_validate_pinning_configuration')
  665. @mock.patch.object(manager.ComputeManager, '_init_instance')
  666. @mock.patch.object(self.compute, '_update_scheduler_instance_info')
  667. def _do_mock_calls(mock_update_scheduler, mock_inst_init,
  668. mock_validate_pinning,
  669. mock_destroy, mock_admin_ctxt, mock_host_get,
  670. mock_filter_off, mock_filter_on, mock_init_host,
  671. mock_error_interrupted, mock_get_nodes,
  672. defer_iptables_apply):
  673. mock_admin_ctxt.return_value = self.context
  674. inst_list = _make_instance_list(startup_instances)
  675. mock_host_get.return_value = inst_list
  676. our_node = objects.ComputeNode(
  677. host='fake-host', uuid=uuids.our_node_uuid,
  678. hypervisor_hostname='fake-node')
  679. mock_get_nodes.return_value = {uuids.our_node_uuid: our_node}
  680. self.compute.init_host()
  681. if defer_iptables_apply:
  682. self.assertTrue(mock_filter_on.called)
  683. mock_validate_pinning.assert_called_once_with(inst_list)
  684. mock_destroy.assert_called_once_with(
  685. self.context, {uuids.our_node_uuid: our_node})
  686. mock_inst_init.assert_has_calls(
  687. [mock.call(self.context, inst_list[0]),
  688. mock.call(self.context, inst_list[1]),
  689. mock.call(self.context, inst_list[2])])
  690. if defer_iptables_apply:
  691. self.assertTrue(mock_filter_off.called)
  692. mock_init_host.assert_called_once_with(host=our_host)
  693. mock_host_get.assert_called_once_with(self.context, our_host,
  694. expected_attrs=['info_cache', 'metadata', 'numa_topology'])
  695. mock_update_scheduler.assert_called_once_with(
  696. self.context, inst_list)
  697. mock_error_interrupted.assert_called_once_with(
  698. self.context, {inst.uuid for inst in inst_list},
  699. mock_get_nodes.return_value.keys())
  700. # Test with defer_iptables_apply
  701. self.flags(defer_iptables_apply=True)
  702. _do_mock_calls(defer_iptables_apply=True)
  703. # Test without defer_iptables_apply
  704. self.flags(defer_iptables_apply=False)
  705. _do_mock_calls(defer_iptables_apply=False)
  706. @mock.patch('nova.compute.manager.ComputeManager._get_nodes')
  707. @mock.patch('nova.compute.manager.ComputeManager.'
  708. '_error_out_instances_whose_build_was_interrupted')
  709. @mock.patch('nova.objects.InstanceList.get_by_host',
  710. return_value=objects.InstanceList())
  711. @mock.patch('nova.compute.manager.ComputeManager.'
  712. '_destroy_evacuated_instances')
  713. @mock.patch('nova.compute.manager.ComputeManager._init_instance',
  714. mock.NonCallableMock())
  715. @mock.patch('nova.compute.manager.ComputeManager.'
  716. '_update_scheduler_instance_info', mock.NonCallableMock())
  717. def test_init_host_no_instances(
  718. self, mock_destroy_evac_instances, mock_get_by_host,
  719. mock_error_interrupted, mock_get_nodes):
  720. """Tests the case that init_host runs and there are no instances
  721. on this host yet (it's brand new). Uses NonCallableMock for the
  722. methods we assert should not be called.
  723. """
  724. mock_get_nodes.return_value = {
  725. uuids.cn_uuid1: objects.ComputeNode(
  726. uuid=uuids.cn_uuid1, hypervisor_hostname='node1')}
  727. self.compute.init_host()
  728. mock_error_interrupted.assert_called_once_with(
  729. test.MatchType(nova.context.RequestContext), set(),
  730. mock_get_nodes.return_value.keys())
  731. mock_get_nodes.assert_called_once_with(
  732. test.MatchType(nova.context.RequestContext))
  733. @mock.patch('nova.objects.InstanceList')
  734. @mock.patch('nova.objects.MigrationList.get_by_filters')
  735. def test_cleanup_host(self, mock_miglist_get, mock_instance_list):
  736. # just testing whether the cleanup_host method
  737. # when fired will invoke the underlying driver's
  738. # equivalent method.
  739. mock_miglist_get.return_value = []
  740. mock_instance_list.get_by_host.return_value = []
  741. with mock.patch.object(self.compute, 'driver') as mock_driver:
  742. self.compute.init_host()
  743. mock_driver.init_host.assert_called_once_with(host='fake-mini')
  744. self.compute.cleanup_host()
  745. # register_event_listener is called on startup (init_host) and
  746. # in cleanup_host
  747. mock_driver.register_event_listener.assert_has_calls([
  748. mock.call(self.compute.handle_events), mock.call(None)])
  749. mock_driver.cleanup_host.assert_called_once_with(host='fake-mini')
  750. def test_cleanup_live_migrations_in_pool_with_record(self):
  751. fake_future = mock.MagicMock()
  752. fake_instance_uuid = uuids.instance
  753. fake_migration = objects.Migration(
  754. uuid=uuids.migration, instance_uuid=fake_instance_uuid)
  755. fake_migration.save = mock.MagicMock()
  756. self.compute._waiting_live_migrations[fake_instance_uuid] = (
  757. fake_migration, fake_future)
  758. with mock.patch.object(self.compute, '_live_migration_executor'
  759. ) as mock_migration_pool:
  760. self.compute._cleanup_live_migrations_in_pool()
  761. mock_migration_pool.shutdown.assert_called_once_with(wait=False)
  762. self.assertEqual('cancelled', fake_migration.status)
  763. fake_future.cancel.assert_called_once_with()
  764. self.assertEqual({}, self.compute._waiting_live_migrations)
  765. # test again with Future is None
  766. self.compute._waiting_live_migrations[fake_instance_uuid] = (
  767. None, None)
  768. self.compute._cleanup_live_migrations_in_pool()
  769. mock_migration_pool.shutdown.assert_called_with(wait=False)
  770. self.assertEqual(2, mock_migration_pool.shutdown.call_count)
  771. self.assertEqual({}, self.compute._waiting_live_migrations)
  772. def test_init_virt_events_disabled(self):
  773. self.flags(handle_virt_lifecycle_events=False, group='workarounds')
  774. with mock.patch.object(self.compute.driver,
  775. 'register_event_listener') as mock_register:
  776. self.compute.init_virt_events()
  777. self.assertFalse(mock_register.called)
  778. @mock.patch('nova.compute.manager.ComputeManager._get_nodes')
  779. @mock.patch.object(manager.ComputeManager,
  780. '_error_out_instances_whose_build_was_interrupted')
  781. @mock.patch('nova.scheduler.utils.resources_from_flavor')
  782. @mock.patch.object(manager.ComputeManager, '_get_instances_on_driver')
  783. @mock.patch.object(manager.ComputeManager, 'init_virt_events')
  784. @mock.patch.object(context, 'get_admin_context')
  785. @mock.patch.object(objects.InstanceList, 'get_by_host')
  786. @mock.patch.object(fake_driver.FakeDriver, 'destroy')
  787. @mock.patch.object(fake_driver.FakeDriver, 'init_host')
  788. @mock.patch('nova.utils.temporary_mutation')
  789. @mock.patch('nova.objects.MigrationList.get_by_filters')
  790. @mock.patch('nova.objects.Migration.save')
  791. def test_init_host_with_evacuated_instance(self, mock_save, mock_mig_get,
  792. mock_temp_mut, mock_init_host, mock_destroy, mock_host_get,
  793. mock_admin_ctxt, mock_init_virt, mock_get_inst, mock_resources,
  794. mock_error_interrupted, mock_get_nodes):
  795. our_host = self.compute.host
  796. not_our_host = 'not-' + our_host
  797. deleted_instance = fake_instance.fake_instance_obj(
  798. self.context, host=not_our_host, uuid=uuids.deleted_instance)
  799. migration = objects.Migration(instance_uuid=deleted_instance.uuid)
  800. migration.source_node = 'fake-node'
  801. mock_mig_get.return_value = [migration]
  802. mock_admin_ctxt.return_value = self.context
  803. mock_host_get.return_value = objects.InstanceList()
  804. our_node = objects.ComputeNode(
  805. host=our_host, uuid=uuids.our_node_uuid,
  806. hypervisor_hostname='fake-node')
  807. mock_get_nodes.return_value = {uuids.our_node_uuid: our_node}
  808. mock_resources.return_value = mock.sentinel.my_resources
  809. # simulate failed instance
  810. mock_get_inst.return_value = [deleted_instance]
  811. with test.nested(
  812. mock.patch.object(
  813. self.compute.network_api, 'get_instance_nw_info',
  814. side_effect = exception.InstanceNotFound(
  815. instance_id=deleted_instance['uuid'])),
  816. mock.patch.object(
  817. self.compute.reportclient,
  818. 'remove_provider_tree_from_instance_allocation')
  819. ) as (mock_get_net, mock_remove_allocation):
  820. self.compute.init_host()
  821. mock_remove_allocation.assert_called_once_with(
  822. self.context, deleted_instance.uuid, uuids.our_node_uuid)
  823. mock_init_host.assert_called_once_with(host=our_host)
  824. mock_host_get.assert_called_once_with(self.context, our_host,
  825. expected_attrs=['info_cache', 'metadata', 'numa_topology'])
  826. mock_init_virt.assert_called_once_with()
  827. mock_temp_mut.assert_called_once_with(self.context, read_deleted='yes')
  828. mock_get_inst.assert_called_once_with(self.context)
  829. mock_get_net.assert_called_once_with(self.context, deleted_instance)
  830. # ensure driver.destroy is called so that driver may
  831. # clean up any dangling files
  832. mock_destroy.assert_called_once_with(self.context, deleted_instance,
  833. mock.ANY, mock.ANY, mock.ANY)
  834. mock_save.assert_called_once_with()
  835. mock_error_interrupted.assert_called_once_with(
  836. self.context, {deleted_instance.uuid},
  837. mock_get_nodes.return_value.keys())
  838. @mock.patch('nova.compute.manager.ComputeManager._get_nodes')
  839. @mock.patch.object(manager.ComputeManager,
  840. '_error_out_instances_whose_build_was_interrupted')
  841. @mock.patch.object(context, 'get_admin_context')
  842. @mock.patch.object(objects.InstanceList, 'get_by_host')
  843. @mock.patch.object(fake_driver.FakeDriver, 'init_host')
  844. @mock.patch('nova.compute.manager.ComputeManager._init_instance')
  845. @mock.patch('nova.compute.manager.ComputeManager.'
  846. '_destroy_evacuated_instances')
  847. def test_init_host_with_in_progress_evacuations(self, mock_destroy_evac,
  848. mock_init_instance, mock_init_host, mock_host_get,
  849. mock_admin_ctxt, mock_error_interrupted, mock_get_nodes):
  850. """Assert that init_instance is not called for instances that are
  851. evacuating from the host during init_host.
  852. """
  853. active_instance = fake_instance.fake_instance_obj(
  854. self.context, host=self.compute.host, uuid=uuids.active_instance)
  855. evacuating_instance = fake_instance.fake_instance_obj(
  856. self.context, host=self.compute.host, uuid=uuids.evac_instance)
  857. instance_list = objects.InstanceList(self.context,
  858. objects=[active_instance, evacuating_instance])
  859. mock_host_get.return_value = instance_list
  860. mock_admin_ctxt.return_value = self.context
  861. mock_destroy_evac.return_value = {
  862. uuids.evac_instance: evacuating_instance
  863. }
  864. our_node = objects.ComputeNode(
  865. host='fake-host', uuid=uuids.our_node_uuid,
  866. hypervisor_hostname='fake-node')
  867. mock_get_nodes.return_value = {uuids.our_node_uuid: our_node}
  868. self.compute.init_host()
  869. mock_init_instance.assert_called_once_with(
  870. self.context, active_instance)
  871. mock_error_interrupted.assert_called_once_with(
  872. self.context, {active_instance.uuid, evacuating_instance.uuid},
  873. mock_get_nodes.return_value.keys())
  874. @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename')
  875. @mock.patch.object(fake_driver.FakeDriver, 'get_available_nodes')
  876. def test_get_nodes(self, mock_driver_get_nodes, mock_get_by_host_and_node):
  877. mock_driver_get_nodes.return_value = ['fake-node1', 'fake-node2']
  878. cn1 = objects.ComputeNode(uuid=uuids.cn1)
  879. cn2 = objects.ComputeNode(uuid=uuids.cn2)
  880. mock_get_by_host_and_node.side_effect = [cn1, cn2]
  881. nodes = self.compute._get_nodes(self.context)
  882. self.assertEqual({uuids.cn1: cn1, uuids.cn2: cn2}, nodes)
  883. mock_driver_get_nodes.assert_called_once_with()
  884. mock_get_by_host_and_node.assert_has_calls([
  885. mock.call(self.context, self.compute.host, 'fake-node1'),
  886. mock.call(self.context, self.compute.host, 'fake-node2'),
  887. ])
  888. @mock.patch.object(manager.LOG, 'warning')
  889. @mock.patch.object(
  890. objects.ComputeNode, 'get_by_host_and_nodename',
  891. new_callable=mock.NonCallableMock)
  892. @mock.patch.object(
  893. fake_driver.FakeDriver, 'get_available_nodes',
  894. side_effect=exception.VirtDriverNotReady)
  895. def test_get_nodes_driver_not_ready(
  896. self, mock_driver_get_nodes, mock_get_by_host_and_node,
  897. mock_log_warning):
  898. mock_driver_get_nodes.return_value = ['fake-node1', 'fake-node2']
  899. nodes = self.compute._get_nodes(self.context)
  900. self.assertEqual({}, nodes)
  901. mock_log_warning.assert_called_once_with(
  902. "Virt driver is not ready. If this is the first time this service "
  903. "is starting on this host, then you can ignore this warning.")
  904. @mock.patch.object(manager.LOG, 'warning')
  905. @mock.patch.object(objects.ComputeNode, 'get_by_host_and_nodename')
  906. @mock.patch.object(fake_driver.FakeDriver, 'get_available_nodes')
  907. def test_get_nodes_node_not_found(
  908. self, mock_driver_get_nodes, mock_get_by_host_and_node,
  909. mock_log_warning):
  910. mock_driver_get_nodes.return_value = ['fake-node1', 'fake-node2']
  911. cn2 = objects.ComputeNode(uuid=uuids.cn2)
  912. mock_get_by_host_and_node.side_effect = [
  913. exception.ComputeHostNotFound(host='fake-node1'), cn2]
  914. nodes = self.compute._get_nodes(self.context)
  915. self.assertEqual({uuids.cn2: cn2}, nodes)
  916. mock_driver_get_nodes.assert_called_once_with()
  917. mock_get_by_host_and_node.assert_has_calls([
  918. mock.call(self.context, self.compute.host, 'fake-node1'),
  919. mock.call(self.context, self.compute.host, 'fake-node2'),
  920. ])
  921. mock_log_warning.assert_called_once_with(
  922. "Compute node %s not found in the database. If this is the first "
  923. "time this service is starting on this host, then you can ignore "
  924. "this warning.", 'fake-node1')
  925. @mock.patch.object(objects.InstanceList, 'get_by_host',
  926. new=mock.Mock())
  927. @mock.patch('nova.compute.manager.ComputeManager.'
  928. '_validate_pinning_configuration')
  929. def test_init_host_pinning_configuration_validation_failure(self,
  930. mock_validate_pinning):
  931. """Test that we fail init_host if the pinning configuration check
  932. fails.
  933. """
  934. mock_validate_pinning.side_effect = exception.InvalidConfiguration
  935. self.assertRaises(exception.InvalidConfiguration,
  936. self.compute.init_host)
  937. @mock.patch.object(objects.Instance, 'save')
  938. @mock.patch.object(objects.InstanceList, 'get_by_filters')
  939. @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
  940. 'get_allocations_for_resource_provider')
  941. def test_init_host_with_interrupted_instance_build(
  942. self, mock_get_allocations, mock_get_instances,
  943. mock_instance_save):
  944. active_instance = fake_instance.fake_instance_obj(
  945. self.context, host=self.compute.host, uuid=uuids.active_instance)
  946. evacuating_instance = fake_instance.fake_instance_obj(
  947. self.context, host=self.compute.host, uuid=uuids.evac_instance)
  948. interrupted_instance = fake_instance.fake_instance_obj(
  949. self.context, host=None, uuid=uuids.interrupted_instance,
  950. vm_state=vm_states.BUILDING)
  951. # we have 3 different instances. We need consumers for each instance
  952. # in placement and an extra consumer that is not an instance
  953. allocations = {
  954. uuids.active_instance: "fake-resources-active",
  955. uuids.evac_instance: "fake-resources-evacuating",
  956. uuids.interrupted_instance: "fake-resources-interrupted",
  957. uuids.not_an_instance: "fake-resources-not-an-instance",
  958. }
  959. mock_get_allocations.return_value = report.ProviderAllocInfo(
  960. allocations=allocations)
  961. # get is called with a uuid filter containing interrupted_instance,
  962. # error_instance, and not_an_instance but it will only return the
  963. # interrupted_instance as the error_instance is not in building state
  964. # and not_an_instance does not match with any instance in the db.
  965. mock_get_instances.return_value = objects.InstanceList(
  966. self.context, objects=[interrupted_instance])
  967. # interrupted_instance and error_instance is not in the list passed in
  968. # because it is not assigned to the compute and therefore not processed
  969. # by init_host and init_instance
  970. self.compute._error_out_instances_whose_build_was_interrupted(
  971. self.context,
  972. {inst.uuid for inst in [active_instance, evacuating_instance]},
  973. [uuids.cn_uuid])
  974. mock_get_instances.assert_called_once_with(
  975. self.context,
  976. {'vm_state': 'building',
  977. 'uuid': {uuids.interrupted_instance, uuids.not_an_instance}
  978. },
  979. expected_attrs=[])
  980. # this is expected to be called only once for interrupted_instance
  981. mock_instance_save.assert_called_once_with()
  982. self.assertEqual(vm_states.ERROR, interrupted_instance.vm_state)
  983. @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
  984. 'get_allocations_for_resource_provider')
  985. def test_init_host_with_interrupted_instance_build_compute_rp_not_found(
  986. self, mock_get_allocations):
  987. mock_get_allocations.side_effect = [
  988. exception.ResourceProviderAllocationRetrievalFailed(
  989. rp_uuid=uuids.cn1_uuid, error='error'),
  990. report.ProviderAllocInfo(allocations={})
  991. ]
  992. self.compute._error_out_instances_whose_build_was_interrupted(
  993. self.context, set(), [uuids.cn1_uuid, uuids.cn2_uuid])
  994. # check that nova skip the node that is not found in placement and
  995. # continue with the next
  996. mock_get_allocations.assert_has_calls(
  997. [
  998. mock.call(self.context, uuids.cn1_uuid),
  999. mock.call(self.context, uuids.cn2_uuid),
  1000. ]
  1001. )
  1002. def test_init_instance_with_binding_failed_vif_type(self):
  1003. # this instance will plug a 'binding_failed' vif
  1004. instance = fake_instance.fake_instance_obj(
  1005. self.context,
  1006. uuid=uuids.instance,
  1007. info_cache=None,
  1008. power_state=power_state.RUNNING,
  1009. vm_state=vm_states.ACTIVE,
  1010. task_state=None,
  1011. host=self.compute.host,
  1012. expected_attrs=['info_cache'])
  1013. with test.nested(
  1014. mock.patch.object(context, 'get_admin_context',
  1015. return_value=self.context),
  1016. mock.patch.object(objects.Instance, 'get_network_info',
  1017. return_value=network_model.NetworkInfo()),
  1018. mock.patch.object(self.compute.driver, 'plug_vifs',
  1019. side_effect=exception.VirtualInterfacePlugException(
  1020. "Unexpected vif_type=binding_failed")),
  1021. mock.patch.object(self.compute, '_set_instance_obj_error_state')
  1022. ) as (get_admin_context, get_nw_info, plug_vifs, set_error_state):
  1023. self.compute._init_instance(self.context, instance)
  1024. set_error_state.assert_called_once_with(self.context, instance)
  1025. def _test__validate_pinning_configuration(self, supports_pcpus=True):
  1026. instance_1 = fake_instance.fake_instance_obj(
  1027. self.context, uuid=uuids.instance_1)
  1028. instance_2 = fake_instance.fake_instance_obj(
  1029. self.context, uuid=uuids.instance_2)
  1030. instance_3 = fake_instance.fake_instance_obj(
  1031. self.context, uuid=uuids.instance_3)
  1032. instance_4 = fake_instance.fake_instance_obj(
  1033. self.context, uuid=uuids.instnace_4)
  1034. instance_1.numa_topology = None
  1035. numa_wo_pinning = test_instance_numa.get_fake_obj_numa_topology(
  1036. self.context)
  1037. instance_2.numa_topology = numa_wo_pinning
  1038. numa_w_pinning = test_instance_numa.get_fake_obj_numa_topology(
  1039. self.context)
  1040. numa_w_pinning.cells[0].pin_vcpus((1, 10), (2, 11))
  1041. numa_w_pinning.cells[0].cpu_policy = (
  1042. fields.CPUAllocationPolicy.DEDICATED)
  1043. numa_w_pinning.cells[1].pin_vcpus((3, 0), (4, 1))
  1044. numa_w_pinning.cells[1].cpu_policy = (
  1045. fields.CPUAllocationPolicy.DEDICATED)
  1046. instance_3.numa_topology = numa_w_pinning
  1047. instance_4.deleted = True
  1048. instances = objects.InstanceList(objects=[
  1049. instance_1, instance_2, instance_3, instance_4])
  1050. with mock.patch.dict(self.compute.driver.capabilities,
  1051. supports_pcpus=supports_pcpus):
  1052. self.compute._validate_pinning_configuration(instances)
  1053. def test__validate_pinning_configuration_invalid_unpinned_config(self):
  1054. """Test that configuring only 'cpu_dedicated_set' when there are
  1055. unpinned instances on the host results in an error.
  1056. """
  1057. self.flags(cpu_dedicated_set='0-7', group='compute')
  1058. ex = self.assertRaises(
  1059. exception.InvalidConfiguration,
  1060. self._test__validate_pinning_configuration)
  1061. self.assertIn('This host has unpinned instances but has no CPUs '
  1062. 'set aside for this purpose;',
  1063. six.text_type(ex))
  1064. def test__validate_pinning_configuration_invalid_pinned_config(self):
  1065. """Test that configuring only 'cpu_shared_set' when there are pinned
  1066. instances on the host results in an error
  1067. """
  1068. self.flags(cpu_shared_set='0-7', group='compute')
  1069. ex = self.assertRaises(
  1070. exception.InvalidConfiguration,
  1071. self._test__validate_pinning_configuration)
  1072. self.assertIn('This host has pinned instances but has no CPUs '
  1073. 'set aside for this purpose;',
  1074. six.text_type(ex))
  1075. @mock.patch.object(manager.LOG, 'warning')
  1076. def test__validate_pinning_configuration_warning(self, mock_log):
  1077. """Test that configuring 'cpu_dedicated_set' such that some pinned
  1078. cores of the instance are outside the range it specifies results in a
  1079. warning.
  1080. """
  1081. self.flags(cpu_shared_set='0-7', cpu_dedicated_set='8-15',
  1082. group='compute')
  1083. self._test__validate_pinning_configuration()
  1084. self.assertEqual(1, mock_log.call_count)
  1085. self.assertIn('Instance is pinned to host CPUs %(cpus)s '
  1086. 'but one or more of these CPUs are not included in ',
  1087. six.text_type(mock_log.call_args[0]))
  1088. def test__validate_pinning_configuration_no_config(self):
  1089. """Test that the entire check is skipped if there's no host
  1090. configuration.
  1091. """
  1092. self._test__validate_pinning_configuration()
  1093. def test__validate_pinning_configuration_not_supported(self):
  1094. """Test that the entire check is skipped if the driver doesn't even
  1095. support PCPUs.
  1096. """
  1097. self._test__validate_pinning_configuration(supports_pcpus=False)
  1098. def test__get_power_state_InstanceNotFound(self):
  1099. instance = fake_instance.fake_instance_obj(
  1100. self.context,
  1101. power_state=power_state.RUNNING)
  1102. with mock.patch.object(self.compute.driver,
  1103. 'get_info',
  1104. side_effect=exception.InstanceNotFound(instance_id=1)):
  1105. self.assertEqual(self.compute._get_power_state(self.context,
  1106. instance),
  1107. power_state.NOSTATE)
  1108. def test__get_power_state_NotFound(self):
  1109. instance = fake_instance.fake_instance_obj(
  1110. self.context,
  1111. power_state=power_state.RUNNING)
  1112. with mock.patch.object(self.compute.driver,
  1113. 'get_info',
  1114. side_effect=exception.NotFound()):
  1115. self.assertRaises(exception.NotFound,
  1116. self.compute._get_power_state,
  1117. self.context, instance)
  1118. @mock.patch.object(manager.ComputeManager, '_get_power_state')
  1119. @mock.patch.object(fake_driver.FakeDriver, 'plug_vifs')
  1120. @mock.patch.object(fake_driver.FakeDriver, 'resume_state_on_host_boot')
  1121. @mock.patch.object(manager.ComputeManager,
  1122. '_get_instance_block_device_info')
  1123. @mock.patch.object(manager.ComputeManager, '_set_instance_obj_error_state')
  1124. def test_init_instance_failed_resume_sets_error(self, mock_set_inst,
  1125. mock_get_inst, mock_resume, mock_plug, mock_get_power):
  1126. instance = fake_instance.fake_instance_obj(
  1127. self.context,
  1128. uuid=uuids.instance,
  1129. info_cache=None,
  1130. power_state=power_state.RUNNING,
  1131. vm_state=vm_states.ACTIVE,
  1132. task_state=None,
  1133. host=self.compute.host,
  1134. expected_attrs=['info_cache'])
  1135. self.flags(resume_guests_state_on_host_boot=True)
  1136. mock_get_power.side_effect = (power_state.SHUTDOWN,
  1137. power_state.SHUTDOWN)
  1138. mock_get_inst.return_value = 'fake-bdm'
  1139. mock_resume.side_effect = test.TestingException
  1140. self.compute._init_instance('fake-context', instance)
  1141. mock_get_power.assert_has_calls([mock.call(mock.ANY, instance),
  1142. mock.call(mock.ANY, instance)])
  1143. mock_plug.assert_called_once_with(instance, mock.ANY)
  1144. mock_get_inst.assert_called_once_with(mock.ANY, instance)
  1145. mock_resume.assert_called_once_with(mock.ANY, instance, mock.ANY,
  1146. 'fake-bdm')
  1147. mock_set_inst.assert_called_once_with(mock.ANY, instance)
  1148. @mock.patch.object(objects.BlockDeviceMapping, 'destroy')
  1149. @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid')
  1150. @mock.patch.object(objects.Instance, 'destroy')
  1151. @mock.patch.object(objects.Instance, 'obj_load_attr')
  1152. @mock.patch.object(objects.quotas, 'ids_from_instance')
  1153. def test_init_instance_complete_partial_deletion(
  1154. self, mock_ids_from_instance,
  1155. mock_inst_destroy, mock_obj_load_attr, mock_get_by_instance_uuid,
  1156. mock_bdm_destroy):
  1157. """Test to complete deletion for instances in DELETED status but not
  1158. marked as deleted in the DB
  1159. """
  1160. instance = fake_instance.fake_instance_obj(
  1161. self.context,
  1162. project_id=fakes.FAKE_PROJECT_ID,
  1163. uuid=uuids.instance,
  1164. vcpus=1,
  1165. memory_mb=64,
  1166. power_state=power_state.SHUTDOWN,
  1167. vm_state=vm_states.DELETED,
  1168. host=self.compute.host,
  1169. task_state=None,
  1170. deleted=False,
  1171. deleted_at=None,
  1172. metadata={},
  1173. system_metadata={},
  1174. expected_attrs=['metadata', 'system_metadata'])
  1175. # Make sure instance vm_state is marked as 'DELETED' but instance is
  1176. # not destroyed from db.
  1177. self.assertEqual(vm_states.DELETED, instance.vm_state)
  1178. self.assertFalse(instance.deleted)
  1179. def fake_inst_destroy():
  1180. instance.deleted = True
  1181. instance.deleted_at = timeutils.utcnow()
  1182. mock_ids_from_instance.return_value = (instance.project_id,
  1183. instance.user_id)
  1184. mock_inst_destroy.side_effect = fake_inst_destroy()
  1185. self.compute._init_instance(self.context, instance)
  1186. # Make sure that instance.destroy method was called and
  1187. # instance was deleted from db.
  1188. self.assertNotEqual(0, instance.deleted)
  1189. @mock.patch('nova.compute.manager.LOG')
  1190. def test_init_instance_complete_partial_deletion_raises_exception(
  1191. self, mock_log):
  1192. instance = fake_instance.fake_instance_obj(
  1193. self.context,
  1194. project_id=fakes.FAKE_PROJECT_ID,
  1195. uuid=uuids.instance,
  1196. vcpus=1,
  1197. memory_mb=64,
  1198. power_state=power_state.SHUTDOWN,
  1199. vm_state=vm_states.DELETED,
  1200. host=self.compute.host,
  1201. task_state=None,
  1202. deleted=False,
  1203. deleted_at=None,
  1204. metadata={},
  1205. system_metadata={},
  1206. expected_attrs=['metadata', 'system_metadata'])
  1207. with mock.patch.object(self.compute,
  1208. '_complete_partial_deletion') as mock_deletion:
  1209. mock_deletion.side_effect = test.TestingException()
  1210. self.compute._init_instance(self, instance)
  1211. msg = u'Failed to complete a deletion'
  1212. mock_log.exception.assert_called_once_with(msg, instance=instance)
  1213. def test_init_instance_stuck_in_deleting(self):
  1214. instance = fake_instance.fake_instance_obj(
  1215. self.context,
  1216. project_id=fakes.FAKE_PROJECT_ID,
  1217. uuid=uuids.instance,
  1218. vcpus=1,
  1219. memory_mb=64,
  1220. power_state=power_state.RUNNING,
  1221. vm_state=vm_states.ACTIVE,
  1222. host=self.compute.host,
  1223. task_state=task_states.DELETING)
  1224. bdms = []
  1225. with test.nested(
  1226. mock.patch.object(objects.BlockDeviceMappingList,
  1227. 'get_by_instance_uuid',
  1228. return_value=bdms),
  1229. mock.patch.object(self.compute, '_delete_instance'),
  1230. mock.patch.object(instance, 'obj_load_attr')
  1231. ) as (mock_get, mock_delete, mock_load):
  1232. self.compute._init_instance(self.context, instance)
  1233. mock_get.assert_called_once_with(self.context, instance.uuid)
  1234. mock_delete.assert_called_once_with(self.context, instance,
  1235. bdms)
  1236. @mock.patch.object(objects.Instance, 'get_by_uuid')
  1237. @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid')
  1238. def test_init_instance_stuck_in_deleting_raises_exception(
  1239. self, mock_get_by_instance_uuid, mock_get_by_uuid):
  1240. instance = fake_instance.fake_instance_obj(
  1241. self.context,
  1242. project_id=fakes.FAKE_PROJECT_ID,
  1243. uuid=uuids.instance,
  1244. vcpus=1,
  1245. memory_mb=64,
  1246. metadata={},
  1247. system_metadata={},
  1248. host=self.compute.host,
  1249. vm_state=vm_states.ACTIVE,
  1250. task_state=task_states.DELETING,
  1251. expected_attrs=['metadata', 'system_metadata'])
  1252. bdms = []
  1253. def _create_patch(name, attr):
  1254. patcher = mock.patch.object(name, attr)
  1255. mocked_obj = patcher.start()
  1256. self.addCleanup(patcher.stop)
  1257. return mocked_obj
  1258. mock_delete_instance = _create_patch(self.compute, '_delete_instance')
  1259. mock_set_instance_error_state = _create_patch(
  1260. self.compute, '_set_instance_obj_error_state')
  1261. mock_get_by_instance_uuid.return_value = bdms
  1262. mock_get_by_uuid.return_value = instance
  1263. mock_delete_instance.side_effect = test.TestingException('test')
  1264. self.compute._init_instance(self.context, instance)
  1265. mock_set_instance_error_state.assert_called_once_with(
  1266. self.context, instance)
  1267. def _test_init_instance_reverts_crashed_migrations(self,
  1268. old_vm_state=None):
  1269. power_on = True if (not old_vm_state or
  1270. old_vm_state == vm_states.ACTIVE) else False
  1271. sys_meta = {
  1272. 'old_vm_state': old_vm_state
  1273. }
  1274. instance = fake_instance.fake_instance_obj(
  1275. self.context,
  1276. uuid=uuids.instance,
  1277. vm_state=vm_states.ERROR,
  1278. task_state=task_states.RESIZE_MIGRATING,
  1279. power_state=power_state.SHUTDOWN,
  1280. system_metadata=sys_meta,
  1281. host=self.compute.host,
  1282. expected_attrs=['system_metadata'])
  1283. instance.migration_context = objects.MigrationContext(migration_id=42)
  1284. migration = objects.Migration(source_compute='fake-host1',
  1285. dest_compute='fake-host2')
  1286. with test.nested(
  1287. mock.patch.object(objects.Instance, 'get_network_info',
  1288. return_value=network_model.NetworkInfo()),
  1289. mock.patch.object(self.compute.driver, 'plug_vifs'),
  1290. mock.patch.object(self.compute.driver, 'finish_revert_migration'),
  1291. mock.patch.object(self.compute, '_get_instance_block_device_info',
  1292. return_value=[]),
  1293. mock.patch.object(self.compute.driver, 'get_info'),
  1294. mock.patch.object(instance, 'save'),
  1295. mock.patch.object(self.compute, '_retry_reboot',
  1296. return_value=(False, None)),
  1297. mock.patch.object(objects.Migration, 'get_by_id_and_instance',
  1298. return_value=migration)
  1299. ) as (mock_get_nw, mock_plug, mock_finish, mock_get_inst,
  1300. mock_get_info, mock_save, mock_retry, mock_get_mig):
  1301. mock_get_info.side_effect = (
  1302. hardware.InstanceInfo(state=power_state.SHUTDOWN),
  1303. hardware.InstanceInfo(state=power_state.SHUTDOWN))
  1304. self.compute._init_instance(self.context, instance)
  1305. mock_get_mig.assert_called_with(self.context, 42, instance.uuid)
  1306. mock_retry.assert_called_once_with(self.context, instance,
  1307. power_state.SHUTDOWN)
  1308. mock_get_nw.assert_called_once_with()
  1309. mock_plug.assert_called_once_with(instance, [])
  1310. mock_get_inst.assert_called_once_with(self.context, instance)
  1311. mock_finish.assert_called_once_with(self.context, instance,
  1312. [], migration, [], power_on)
  1313. mock_save.assert_called_once_with()
  1314. mock_get_info.assert_has_calls(
  1315. [mock.call(instance, use_cache=False),
  1316. mock.call(instance, use_cache=False)])
  1317. self.assertIsNone(instance.task_state)
  1318. def test_init_instance_reverts_crashed_migration_from_active(self):
  1319. self._test_init_instance_reverts_crashed_migrations(
  1320. old_vm_state=vm_states.ACTIVE)
  1321. def test_init_instance_reverts_crashed_migration_from_stopped(self):
  1322. self._test_init_instance_reverts_crashed_migrations(
  1323. old_vm_state=vm_states.STOPPED)
  1324. def test_init_instance_reverts_crashed_migration_no_old_state(self):
  1325. self._test_init_instance_reverts_crashed_migrations(old_vm_state=None)
  1326. def test_init_instance_resets_crashed_live_migration(self):
  1327. instance = fake_instance.fake_instance_obj(
  1328. self.context,
  1329. uuid=uuids.instance,
  1330. vm_state=vm_states.ACTIVE,
  1331. host=self.compute.host,
  1332. task_state=task_states.MIGRATING)
  1333. migration = objects.Migration(source_compute='fake-host1', id=39,
  1334. dest_compute='fake-host2')
  1335. with test.nested(
  1336. mock.patch.object(instance, 'save'),
  1337. mock.patch('nova.objects.Instance.get_network_info',
  1338. return_value=network_model.NetworkInfo()),
  1339. mock.patch.object(objects.Migration, 'get_by_instance_and_status',
  1340. return_value=migration),
  1341. mock.patch.object(self.compute, 'live_migration_abort'),
  1342. mock.patch.object(self.compute, '_set_migration_status')
  1343. ) as (save, get_nw_info, mock_get_status, mock_abort, mock_set_migr):
  1344. self.compute._init_instance(self.context, instance)
  1345. save.assert_called_once_with(expected_task_state=['migrating'])
  1346. get_nw_info.assert_called_once_with()
  1347. mock_get_status.assert_called_with(self.context, instance.uuid,
  1348. 'running')
  1349. mock_abort.assert_called_with(self.context, instance,
  1350. migration.id)
  1351. mock_set_migr.assert_called_with(migration, 'error')
  1352. self.assertIsNone(instance.task_state)
  1353. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  1354. def _test_init_instance_sets_building_error(self, vm_state,
  1355. task_state=None):
  1356. instance = fake_instance.fake_instance_obj(
  1357. self.context,
  1358. uuid=uuids.instance,
  1359. vm_state=vm_state,
  1360. host=self.compute.host,
  1361. task_state=task_state)
  1362. with mock.patch.object(instance, 'save') as save:
  1363. self.compute._init_instance(self.context, instance)
  1364. save.assert_called_once_with()
  1365. self.assertIsNone(instance.task_state)
  1366. self.assertEqual(vm_states.ERROR, instance.vm_state)
  1367. def test_init_instance_sets_building_error(self):
  1368. self._test_init_instance_sets_building_error(vm_states.BUILDING)
  1369. def test_init_instance_sets_rebuilding_errors(self):
  1370. tasks = [task_states.REBUILDING,
  1371. task_states.REBUILD_BLOCK_DEVICE_MAPPING,
  1372. task_states.REBUILD_SPAWNING]
  1373. vms = [vm_states.ACTIVE, vm_states.STOPPED]
  1374. for vm_state in vms:
  1375. for task_state in tasks:
  1376. self._test_init_instance_sets_building_error(
  1377. vm_state, task_state)
  1378. def _test_init_instance_sets_building_tasks_error(self, instance):
  1379. instance.host = self.compute.host
  1380. with mock.patch.object(instance, 'save') as save:
  1381. self.compute._init_instance(self.context, instance)
  1382. save.assert_called_once_with()
  1383. self.assertIsNone(instance.task_state)
  1384. self.assertEqual(vm_states.ERROR, instance.vm_state)
  1385. def test_init_instance_sets_building_tasks_error_scheduling(self):
  1386. instance = fake_instance.fake_instance_obj(
  1387. self.context,
  1388. uuid=uuids.instance,
  1389. vm_state=None,
  1390. task_state=task_states.SCHEDULING)
  1391. self._test_init_instance_sets_building_tasks_error(instance)
  1392. def test_init_instance_sets_building_tasks_error_block_device(self):
  1393. instance = objects.Instance(self.context)
  1394. instance.uuid = uuids.instance
  1395. instance.vm_state = None
  1396. instance.task_state = task_states.BLOCK_DEVICE_MAPPING
  1397. self._test_init_instance_sets_building_tasks_error(instance)
  1398. def test_init_instance_sets_building_tasks_error_networking(self):
  1399. instance = objects.Instance(self.context)
  1400. instance.uuid = uuids.instance
  1401. instance.vm_state = None
  1402. instance.task_state = task_states.NETWORKING
  1403. self._test_init_instance_sets_building_tasks_error(instance)
  1404. def test_init_instance_sets_building_tasks_error_spawning(self):
  1405. instance = objects.Instance(self.context)
  1406. instance.uuid = uuids.instance
  1407. instance.vm_state = None
  1408. instance.task_state = task_states.SPAWNING
  1409. self._test_init_instance_sets_building_tasks_error(instance)
  1410. def _test_init_instance_cleans_image_states(self, instance):
  1411. with mock.patch.object(instance, 'save') as save:
  1412. self.compute._get_power_state = mock.Mock()
  1413. self.compute.driver.post_interrupted_snapshot_cleanup = mock.Mock()
  1414. instance.info_cache = None
  1415. instance.power_state = power_state.RUNNING
  1416. instance.host = self.compute.host
  1417. self.compute._init_instance(self.context, instance)
  1418. save.assert_called_once_with()
  1419. self.compute.driver.post_interrupted_snapshot_cleanup.\
  1420. assert_called_once_with(self.context, instance)
  1421. self.assertIsNone(instance.task_state)
  1422. @mock.patch('nova.compute.manager.ComputeManager._get_power_state',
  1423. return_value=power_state.RUNNING)
  1424. @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid')
  1425. def _test_init_instance_cleans_task_states(self, powerstate, state,
  1426. mock_get_uuid, mock_get_power_state):
  1427. instance = objects.Instance(self.context)
  1428. instance.uuid = uuids.instance
  1429. instance.info_cache = None
  1430. instance.power_state = power_state.RUNNING
  1431. instance.vm_state = vm_states.ACTIVE
  1432. instance.task_state = state
  1433. instance.host = self.compute.host
  1434. mock_get_power_state.return_value = powerstate
  1435. self.compute._init_instance(self.context, instance)
  1436. return instance
  1437. def test_init_instance_cleans_image_state_pending_upload(self):
  1438. instance = objects.Instance(self.context)
  1439. instance.uuid = uuids.instance
  1440. instance.vm_state = vm_states.ACTIVE
  1441. instance.task_state = task_states.IMAGE_PENDING_UPLOAD
  1442. self._test_init_instance_cleans_image_states(instance)
  1443. def test_init_instance_cleans_image_state_uploading(self):
  1444. instance = objects.Instance(self.context)
  1445. instance.uuid = uuids.instance
  1446. instance.vm_state = vm_states.ACTIVE
  1447. instance.task_state = task_states.IMAGE_UPLOADING
  1448. self._test_init_instance_cleans_image_states(instance)
  1449. def test_init_instance_cleans_image_state_snapshot(self):
  1450. instance = objects.Instance(self.context)
  1451. instance.uuid = uuids.instance
  1452. instance.vm_state = vm_states.ACTIVE
  1453. instance.task_state = task_states.IMAGE_SNAPSHOT
  1454. self._test_init_instance_cleans_image_states(instance)
  1455. def test_init_instance_cleans_image_state_snapshot_pending(self):
  1456. instance = objects.Instance(self.context)
  1457. instance.uuid = uuids.instance
  1458. instance.vm_state = vm_states.ACTIVE
  1459. instance.task_state = task_states.IMAGE_SNAPSHOT_PENDING
  1460. self._test_init_instance_cleans_image_states(instance)
  1461. @mock.patch.object(objects.Instance, 'save')
  1462. def test_init_instance_cleans_running_pausing(self, mock_save):
  1463. instance = self._test_init_instance_cleans_task_states(
  1464. power_state.RUNNING, task_states.PAUSING)
  1465. mock_save.assert_called_once_with()
  1466. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  1467. self.assertIsNone(instance.task_state)
  1468. @mock.patch.object(objects.Instance, 'save')
  1469. def test_init_instance_cleans_running_unpausing(self, mock_save):
  1470. instance = self._test_init_instance_cleans_task_states(
  1471. power_state.RUNNING, task_states.UNPAUSING)
  1472. mock_save.assert_called_once_with()
  1473. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  1474. self.assertIsNone(instance.task_state)
  1475. @mock.patch('nova.compute.manager.ComputeManager.unpause_instance')
  1476. def test_init_instance_cleans_paused_unpausing(self, mock_unpause):
  1477. def fake_unpause(context, instance):
  1478. instance.task_state = None
  1479. mock_unpause.side_effect = fake_unpause
  1480. instance = self._test_init_instance_cleans_task_states(
  1481. power_state.PAUSED, task_states.UNPAUSING)
  1482. mock_unpause.assert_called_once_with(self.context, instance)
  1483. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  1484. self.assertIsNone(instance.task_state)
  1485. def test_init_instance_deletes_error_deleting_instance(self):
  1486. instance = fake_instance.fake_instance_obj(
  1487. self.context,
  1488. project_id=fakes.FAKE_PROJECT_ID,
  1489. uuid=uuids.instance,
  1490. vcpus=1,
  1491. memory_mb=64,
  1492. vm_state=vm_states.ERROR,
  1493. host=self.compute.host,
  1494. task_state=task_states.DELETING)
  1495. bdms = []
  1496. with test.nested(
  1497. mock.patch.object(objects.BlockDeviceMappingList,
  1498. 'get_by_instance_uuid',
  1499. return_value=bdms),
  1500. mock.patch.object(self.compute, '_delete_instance'),
  1501. mock.patch.object(instance, 'obj_load_attr')
  1502. ) as (mock_get, mock_delete, mock_load):
  1503. self.compute._init_instance(self.context, instance)
  1504. mock_get.assert_called_once_with(self.context, instance.uuid)
  1505. mock_delete.assert_called_once_with(self.context, instance,
  1506. bdms)
  1507. def test_init_instance_resize_prep(self):
  1508. instance = fake_instance.fake_instance_obj(
  1509. self.context,
  1510. uuid=uuids.instance,
  1511. vm_state=vm_states.ACTIVE,
  1512. host=self.compute.host,
  1513. task_state=task_states.RESIZE_PREP,
  1514. power_state=power_state.RUNNING)
  1515. with test.nested(
  1516. mock.patch.object(self.compute, '_get_power_state',
  1517. return_value=power_state.RUNNING),
  1518. mock.patch.object(objects.Instance, 'get_network_info'),
  1519. mock.patch.object(instance, 'save', autospec=True)
  1520. ) as (mock_get_power_state, mock_nw_info, mock_instance_save):
  1521. self.compute._init_instance(self.context, instance)
  1522. mock_instance_save.assert_called_once_with()
  1523. self.assertIsNone(instance.task_state)
  1524. @mock.patch('nova.virt.fake.FakeDriver.power_off')
  1525. @mock.patch.object(compute_utils, 'get_value_from_system_metadata',
  1526. return_value=CONF.shutdown_timeout)
  1527. def test_power_off_values(self, mock_get_metadata, mock_power_off):
  1528. self.flags(shutdown_retry_interval=20, group='compute')
  1529. instance = fake_instance.fake_instance_obj(
  1530. self.context,
  1531. uuid=uuids.instance,
  1532. vm_state=vm_states.ACTIVE,
  1533. task_state=task_states.POWERING_OFF)
  1534. self.compute._power_off_instance(
  1535. self.context, instance,
  1536. clean_shutdown=True)
  1537. mock_power_off.assert_called_once_with(
  1538. instance,
  1539. CONF.shutdown_timeout,
  1540. 20)
  1541. @mock.patch('nova.context.RequestContext.elevated')
  1542. @mock.patch('nova.objects.Instance.get_network_info')
  1543. @mock.patch(
  1544. 'nova.compute.manager.ComputeManager._get_instance_block_device_info')
  1545. @mock.patch('nova.virt.driver.ComputeDriver.destroy')
  1546. @mock.patch('nova.virt.fake.FakeDriver.get_volume_connector')
  1547. @mock.patch('nova.compute.utils.notify_about_instance_action')
  1548. @mock.patch(
  1549. 'nova.compute.manager.ComputeManager._notify_about_instance_usage')
  1550. def test_shutdown_instance_versioned_notifications(self,
  1551. mock_notify_unversioned, mock_notify, mock_connector,
  1552. mock_destroy, mock_blk_device_info, mock_nw_info, mock_elevated):
  1553. mock_elevated.return_value = self.context
  1554. instance = fake_instance.fake_instance_obj(
  1555. self.context,
  1556. uuid=uuids.instance,
  1557. vm_state=vm_states.ERROR,
  1558. task_state=task_states.DELETING)
  1559. bdms = [mock.Mock(id=1, is_volume=True)]
  1560. self.compute._shutdown_instance(self.context, instance, bdms,
  1561. notify=True, try_deallocate_networks=False)
  1562. mock_notify.assert_has_calls([
  1563. mock.call(self.context, instance, 'fake-mini',
  1564. action='shutdown', phase='start', bdms=bdms),
  1565. mock.call(self.context, instance, 'fake-mini',
  1566. action='shutdown', phase='end', bdms=bdms)])
  1567. @mock.patch('nova.context.RequestContext.elevated')
  1568. @mock.patch('nova.objects.Instance.get_network_info')
  1569. @mock.patch(
  1570. 'nova.compute.manager.ComputeManager._get_instance_block_device_info')
  1571. @mock.patch('nova.virt.driver.ComputeDriver.destroy')
  1572. @mock.patch('nova.virt.fake.FakeDriver.get_volume_connector')
  1573. def _test_shutdown_instance_exception(self, exc, mock_connector,
  1574. mock_destroy, mock_blk_device_info, mock_nw_info, mock_elevated):
  1575. mock_connector.side_effect = exc
  1576. mock_elevated.return_value = self.context
  1577. instance = fake_instance.fake_instance_obj(
  1578. self.context,
  1579. uuid=uuids.instance,
  1580. vm_state=vm_states.ERROR,
  1581. task_state=task_states.DELETING)
  1582. bdms = [mock.Mock(id=1, is_volume=True, attachment_id=None)]
  1583. self.compute._shutdown_instance(self.context, instance, bdms,
  1584. notify=False, try_deallocate_networks=False)
  1585. mock_connector.assert_called_once_with(instance)
  1586. def test_shutdown_instance_endpoint_not_found(self):
  1587. exc = cinder_exception.EndpointNotFound
  1588. self._test_shutdown_instance_exception(exc)
  1589. def test_shutdown_instance_client_exception(self):
  1590. exc = cinder_exception.ClientException(code=9001)
  1591. self._test_shutdown_instance_exception(exc)
  1592. def test_shutdown_instance_volume_not_found(self):
  1593. exc = exception.VolumeNotFound(volume_id=42)
  1594. self._test_shutdown_instance_exception(exc)
  1595. def test_shutdown_instance_disk_not_found(self):
  1596. exc = exception.DiskNotFound(location="not\\here")
  1597. self._test_shutdown_instance_exception(exc)
  1598. def test_shutdown_instance_other_exception(self):
  1599. exc = Exception('some other exception')
  1600. self._test_shutdown_instance_exception(exc)
  1601. def _test_init_instance_retries_reboot(self, instance, reboot_type,
  1602. return_power_state):
  1603. instance.host = self.compute.host
  1604. with test.nested(
  1605. mock.patch.object(self.compute, '_get_power_state',
  1606. return_value=return_power_state),
  1607. mock.patch.object(self.compute, 'reboot_instance'),
  1608. mock.patch.object(objects.Instance, 'get_network_info')
  1609. ) as (
  1610. _get_power_state,
  1611. reboot_instance,
  1612. get_network_info
  1613. ):
  1614. self.compute._init_instance(self.context, instance)
  1615. call = mock.call(self.context, instance, block_device_info=None,
  1616. reboot_type=reboot_type)
  1617. reboot_instance.assert_has_calls([call])
  1618. def test_init_instance_retries_reboot_pending(self):
  1619. instance = objects.Instance(self.context)
  1620. instance.uuid = uuids.instance
  1621. instance.task_state = task_states.REBOOT_PENDING
  1622. for state in vm_states.ALLOW_SOFT_REBOOT:
  1623. instance.vm_state = state
  1624. self._test_init_instance_retries_reboot(instance, 'SOFT',
  1625. power_state.RUNNING)
  1626. def test_init_instance_retries_reboot_pending_hard(self):
  1627. instance = objects.Instance(self.context)
  1628. instance.uuid = uuids.instance
  1629. instance.task_state = task_states.REBOOT_PENDING_HARD
  1630. for state in vm_states.ALLOW_HARD_REBOOT:
  1631. # NOTE(dave-mcnally) while a reboot of a vm in error state is
  1632. # possible we don't attempt to recover an error during init
  1633. if state == vm_states.ERROR:
  1634. continue
  1635. instance.vm_state = state
  1636. self._test_init_instance_retries_reboot(instance, 'HARD',
  1637. power_state.RUNNING)
  1638. def test_init_instance_retries_reboot_pending_soft_became_hard(self):
  1639. instance = objects.Instance(self.context)
  1640. instance.uuid = uuids.instance
  1641. instance.task_state = task_states.REBOOT_PENDING
  1642. for state in vm_states.ALLOW_HARD_REBOOT:
  1643. # NOTE(dave-mcnally) while a reboot of a vm in error state is
  1644. # possible we don't attempt to recover an error during init
  1645. if state == vm_states.ERROR:
  1646. continue
  1647. instance.vm_state = state
  1648. with mock.patch.object(instance, 'save'):
  1649. self._test_init_instance_retries_reboot(instance, 'HARD',
  1650. power_state.SHUTDOWN)
  1651. self.assertEqual(task_states.REBOOT_PENDING_HARD,
  1652. instance.task_state)
  1653. def test_init_instance_retries_reboot_started(self):
  1654. instance = objects.Instance(self.context)
  1655. instance.uuid = uuids.instance
  1656. instance.vm_state = vm_states.ACTIVE
  1657. instance.task_state = task_states.REBOOT_STARTED
  1658. with mock.patch.object(instance, 'save'):
  1659. self._test_init_instance_retries_reboot(instance, 'HARD',
  1660. power_state.NOSTATE)
  1661. def test_init_instance_retries_reboot_started_hard(self):
  1662. instance = objects.Instance(self.context)
  1663. instance.uuid = uuids.instance
  1664. instance.vm_state = vm_states.ACTIVE
  1665. instance.task_state = task_states.REBOOT_STARTED_HARD
  1666. self._test_init_instance_retries_reboot(instance, 'HARD',
  1667. power_state.NOSTATE)
  1668. def _test_init_instance_cleans_reboot_state(self, instance):
  1669. instance.host = self.compute.host
  1670. with test.nested(
  1671. mock.patch.object(self.compute, '_get_power_state',
  1672. return_value=power_state.RUNNING),
  1673. mock.patch.object(instance, 'save', autospec=True),
  1674. mock.patch.object(objects.Instance, 'get_network_info')
  1675. ) as (
  1676. _get_power_state,
  1677. instance_save,
  1678. get_network_info
  1679. ):
  1680. self.compute._init_instance(self.context, instance)
  1681. instance_save.assert_called_once_with()
  1682. self.assertIsNone(instance.task_state)
  1683. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  1684. def test_init_instance_cleans_image_state_reboot_started(self):
  1685. instance = objects.Instance(self.context)
  1686. instance.uuid = uuids.instance
  1687. instance.vm_state = vm_states.ACTIVE
  1688. instance.task_state = task_states.REBOOT_STARTED
  1689. instance.power_state = power_state.RUNNING
  1690. self._test_init_instance_cleans_reboot_state(instance)
  1691. def test_init_instance_cleans_image_state_reboot_started_hard(self):
  1692. instance = objects.Instance(self.context)
  1693. instance.uuid = uuids.instance
  1694. instance.vm_state = vm_states.ACTIVE
  1695. instance.task_state = task_states.REBOOT_STARTED_HARD
  1696. instance.power_state = power_state.RUNNING
  1697. self._test_init_instance_cleans_reboot_state(instance)
  1698. def test_init_instance_retries_power_off(self):
  1699. instance = objects.Instance(self.context)
  1700. instance.uuid = uuids.instance
  1701. instance.id = 1
  1702. instance.vm_state = vm_states.ACTIVE
  1703. instance.task_state = task_states.POWERING_OFF
  1704. instance.host = self.compute.host
  1705. with mock.patch.object(self.compute, 'stop_instance'):
  1706. self.compute._init_instance(self.context, instance)
  1707. call = mock.call(self.context, instance, True)
  1708. self.compute.stop_instance.assert_has_calls([call])
  1709. def test_init_instance_retries_power_on(self):
  1710. instance = objects.Instance(self.context)
  1711. instance.uuid = uuids.instance
  1712. instance.id = 1
  1713. instance.vm_state = vm_states.ACTIVE
  1714. instance.task_state = task_states.POWERING_ON
  1715. instance.host = self.compute.host
  1716. with mock.patch.object(self.compute, 'start_instance'):
  1717. self.compute._init_instance(self.context, instance)
  1718. call = mock.call(self.context, instance)
  1719. self.compute.start_instance.assert_has_calls([call])
  1720. def test_init_instance_retries_power_on_silent_exception(self):
  1721. instance = objects.Instance(self.context)
  1722. instance.uuid = uuids.instance
  1723. instance.id = 1
  1724. instance.vm_state = vm_states.ACTIVE
  1725. instance.task_state = task_states.POWERING_ON
  1726. instance.host = self.compute.host
  1727. with mock.patch.object(self.compute, 'start_instance',
  1728. return_value=Exception):
  1729. init_return = self.compute._init_instance(self.context, instance)
  1730. call = mock.call(self.context, instance)
  1731. self.compute.start_instance.assert_has_calls([call])
  1732. self.assertIsNone(init_return)
  1733. def test_init_instance_retries_power_off_silent_exception(self):
  1734. instance = objects.Instance(self.context)
  1735. instance.uuid = uuids.instance
  1736. instance.id = 1
  1737. instance.vm_state = vm_states.ACTIVE
  1738. instance.task_state = task_states.POWERING_OFF
  1739. instance.host = self.compute.host
  1740. with mock.patch.object(self.compute, 'stop_instance',
  1741. return_value=Exception):
  1742. init_return = self.compute._init_instance(self.context, instance)
  1743. call = mock.call(self.context, instance, True)
  1744. self.compute.stop_instance.assert_has_calls([call])
  1745. self.assertIsNone(init_return)
  1746. def test_get_power_state(self):
  1747. instance = objects.Instance(self.context)
  1748. instance.uuid = uuids.instance
  1749. instance.id = 1
  1750. instance.vm_state = vm_states.STOPPED
  1751. instance.task_state = None
  1752. instance.host = self.compute.host
  1753. with mock.patch.object(self.compute.driver, 'get_info') as mock_info:
  1754. mock_info.return_value = hardware.InstanceInfo(
  1755. state=power_state.SHUTDOWN)
  1756. res = self.compute._get_power_state(self.context, instance)
  1757. mock_info.assert_called_once_with(instance, use_cache=False)
  1758. self.assertEqual(res, power_state.SHUTDOWN)
  1759. @mock.patch('nova.objects.InstanceList.get_by_filters')
  1760. def test_get_instances_on_driver(self, mock_instance_list):
  1761. driver_instances = []
  1762. for x in range(10):
  1763. driver_instances.append(fake_instance.fake_db_instance())
  1764. def _make_instance_list(db_list):
  1765. return instance_obj._make_instance_list(
  1766. self.context, objects.InstanceList(), db_list, None)
  1767. driver_uuids = [inst['uuid'] for inst in driver_instances]
  1768. mock_instance_list.return_value = _make_instance_list(driver_instances)
  1769. with mock.patch.object(self.compute.driver,
  1770. 'list_instance_uuids') as mock_driver_uuids:
  1771. mock_driver_uuids.return_value = driver_uuids
  1772. result = self.compute._get_instances_on_driver(self.context)
  1773. self.assertEqual([x['uuid'] for x in driver_instances],
  1774. [x['uuid'] for x in result])
  1775. expected_filters = {'uuid': driver_uuids}
  1776. mock_instance_list.assert_called_with(self.context, expected_filters,
  1777. use_slave=True)
  1778. @mock.patch('nova.objects.InstanceList.get_by_filters')
  1779. def test_get_instances_on_driver_empty(self, mock_instance_list):
  1780. with mock.patch.object(self.compute.driver,
  1781. 'list_instance_uuids') as mock_driver_uuids:
  1782. mock_driver_uuids.return_value = []
  1783. result = self.compute._get_instances_on_driver(self.context)
  1784. # Short circuit DB call, get_by_filters should not be called
  1785. self.assertEqual(0, mock_instance_list.call_count)
  1786. self.assertEqual(1, mock_driver_uuids.call_count)
  1787. self.assertEqual([], [x['uuid'] for x in result])
  1788. @mock.patch('nova.objects.InstanceList.get_by_filters')
  1789. def test_get_instances_on_driver_fallback(self, mock_instance_list):
  1790. # Test getting instances when driver doesn't support
  1791. # 'list_instance_uuids'
  1792. self.compute.host = 'host'
  1793. filters = {}
  1794. self.flags(instance_name_template='inst-%i')
  1795. all_instances = []
  1796. driver_instances = []
  1797. for x in range(10):
  1798. instance = fake_instance.fake_db_instance(name='inst-%i' % x,
  1799. id=x)
  1800. if x % 2:
  1801. driver_instances.append(instance)
  1802. all_instances.append(instance)
  1803. def _make_instance_list(db_list):
  1804. return instance_obj._make_instance_list(
  1805. self.context, objects.InstanceList(), db_list, None)
  1806. driver_instance_names = [inst['name'] for inst in driver_instances]
  1807. mock_instance_list.return_value = _make_instance_list(all_instances)
  1808. with test.nested(
  1809. mock.patch.object(self.compute.driver, 'list_instance_uuids'),
  1810. mock.patch.object(self.compute.driver, 'list_instances')
  1811. ) as (
  1812. mock_driver_uuids,
  1813. mock_driver_instances
  1814. ):
  1815. mock_driver_uuids.side_effect = NotImplementedError()
  1816. mock_driver_instances.return_value = driver_instance_names
  1817. result = self.compute._get_instances_on_driver(self.context,
  1818. filters)
  1819. self.assertEqual([x['uuid'] for x in driver_instances],
  1820. [x['uuid'] for x in result])
  1821. expected_filters = {'host': self.compute.host}
  1822. mock_instance_list.assert_called_with(self.context, expected_filters,
  1823. use_slave=True)
  1824. @mock.patch.object(compute_utils, 'notify_usage_exists')
  1825. @mock.patch.object(objects.TaskLog, 'end_task')
  1826. @mock.patch.object(objects.TaskLog, 'begin_task')
  1827. @mock.patch.object(objects.InstanceList, 'get_active_by_window_joined')
  1828. @mock.patch.object(objects.TaskLog, 'get')
  1829. def test_instance_usage_audit(self, mock_get, mock_get_active, mock_begin,
  1830. mock_end, mock_notify):
  1831. instances = [objects.Instance(uuid=uuids.instance)]
  1832. def fake_task_log(*a, **k):
  1833. pass
  1834. def fake_get(*a, **k):
  1835. return instances
  1836. mock_get.side_effect = fake_task_log
  1837. mock_get_active.side_effect = fake_get
  1838. mock_begin.side_effect = fake_task_log
  1839. mock_end.side_effect = fake_task_log
  1840. self.flags(instance_usage_audit=True)
  1841. self.compute._instance_usage_audit(self.context)
  1842. mock_notify.assert_called_once_with(
  1843. self.compute.notifier, self.context, instances[0], 'fake-mini',
  1844. ignore_missing_network_data=False)
  1845. self.assertTrue(mock_get.called)
  1846. self.assertTrue(mock_get_active.called)
  1847. self.assertTrue(mock_begin.called)
  1848. self.assertTrue(mock_end.called)
  1849. @mock.patch.object(objects.InstanceList, 'get_by_host')
  1850. def test_sync_power_states(self, mock_get):
  1851. instance = mock.Mock()
  1852. mock_get.return_value = [instance]
  1853. with mock.patch.object(self.compute._sync_power_pool,
  1854. 'spawn_n') as mock_spawn:
  1855. self.compute._sync_power_states(mock.sentinel.context)
  1856. mock_get.assert_called_with(mock.sentinel.context,
  1857. self.compute.host, expected_attrs=[],
  1858. use_slave=True)
  1859. mock_spawn.assert_called_once_with(mock.ANY, instance)
  1860. @mock.patch('nova.objects.InstanceList.get_by_host', new=mock.Mock())
  1861. @mock.patch('nova.compute.manager.ComputeManager.'
  1862. '_query_driver_power_state_and_sync',
  1863. new_callable=mock.NonCallableMock)
  1864. def test_sync_power_states_virt_driver_not_ready(self, _mock_sync):
  1865. """"Tests that the periodic task exits early if the driver raises
  1866. VirtDriverNotReady.
  1867. """
  1868. with mock.patch.object(
  1869. self.compute.driver, 'get_num_instances',
  1870. side_effect=exception.VirtDriverNotReady) as gni:
  1871. self.compute._sync_power_states(mock.sentinel.context)
  1872. gni.assert_called_once_with()
  1873. def _get_sync_instance(self, power_state, vm_state, task_state=None,
  1874. shutdown_terminate=False):
  1875. instance = objects.Instance()
  1876. instance.uuid = uuids.instance
  1877. instance.power_state = power_state
  1878. instance.vm_state = vm_state
  1879. instance.host = self.compute.host
  1880. instance.task_state = task_state
  1881. instance.shutdown_terminate = shutdown_terminate
  1882. return instance
  1883. @mock.patch.object(objects.Instance, 'refresh')
  1884. def test_sync_instance_power_state_match(self, mock_refresh):
  1885. instance = self._get_sync_instance(power_state.RUNNING,
  1886. vm_states.ACTIVE)
  1887. self.compute._sync_instance_power_state(self.context, instance,
  1888. power_state.RUNNING)
  1889. mock_refresh.assert_called_once_with(use_slave=False)
  1890. @mock.patch.object(fake_driver.FakeDriver, 'get_info')
  1891. @mock.patch.object(objects.Instance, 'refresh')
  1892. @mock.patch.object(objects.Instance, 'save')
  1893. def test_sync_instance_power_state_running_stopped(self, mock_save,
  1894. mock_refresh,
  1895. mock_get_info):
  1896. mock_get_info.return_value = hardware.InstanceInfo(
  1897. state=power_state.SHUTDOWN)
  1898. instance = self._get_sync_instance(power_state.RUNNING,
  1899. vm_states.ACTIVE)
  1900. self.compute._sync_instance_power_state(self.context, instance,
  1901. power_state.SHUTDOWN)
  1902. self.assertEqual(instance.power_state, power_state.SHUTDOWN)
  1903. mock_refresh.assert_called_once_with(use_slave=False)
  1904. self.assertTrue(mock_save.called)
  1905. mock_get_info.assert_called_once_with(instance, use_cache=False)
  1906. def _test_sync_to_stop(self, vm_power_state, vm_state, driver_power_state,
  1907. stop=True, force=False, shutdown_terminate=False):
  1908. instance = self._get_sync_instance(
  1909. vm_power_state, vm_state, shutdown_terminate=shutdown_terminate)
  1910. with test.nested(
  1911. mock.patch.object(objects.Instance, 'refresh'),
  1912. mock.patch.object(objects.Instance, 'save'),
  1913. mock.patch.object(self.compute.compute_api, 'stop'),
  1914. mock.patch.object(self.compute.compute_api, 'delete'),
  1915. mock.patch.object(self.compute.compute_api, 'force_stop'),
  1916. mock.patch.object(self.compute.driver, 'get_info')
  1917. ) as (mock_refresh, mock_save, mock_stop, mock_delete, mock_force,
  1918. mock_get_info):
  1919. mock_get_info.return_value = hardware.InstanceInfo(
  1920. state=driver_power_state)
  1921. self.compute._sync_instance_power_state(self.context, instance,
  1922. driver_power_state)
  1923. if shutdown_terminate:
  1924. mock_delete.assert_called_once_with(self.context, instance)
  1925. elif stop:
  1926. if force:
  1927. mock_force.assert_called_once_with(self.context, instance)
  1928. else:
  1929. mock_stop.assert_called_once_with(self.context, instance)
  1930. if (vm_state == vm_states.ACTIVE and
  1931. vm_power_state in (power_state.SHUTDOWN,
  1932. power_state.CRASHED)):
  1933. mock_get_info.assert_called_once_with(instance,
  1934. use_cache=False)
  1935. mock_refresh.assert_called_once_with(use_slave=False)
  1936. self.assertTrue(mock_save.called)
  1937. def test_sync_instance_power_state_to_stop(self):
  1938. for ps in (power_state.SHUTDOWN, power_state.CRASHED,
  1939. power_state.SUSPENDED):
  1940. self._test_sync_to_stop(power_state.RUNNING, vm_states.ACTIVE, ps)
  1941. for ps in (power_state.SHUTDOWN, power_state.CRASHED):
  1942. self._test_sync_to_stop(power_state.PAUSED, vm_states.PAUSED, ps,
  1943. force=True)
  1944. self._test_sync_to_stop(power_state.SHUTDOWN, vm_states.STOPPED,
  1945. power_state.RUNNING, force=True)
  1946. def test_sync_instance_power_state_to_terminate(self):
  1947. self._test_sync_to_stop(power_state.RUNNING, vm_states.ACTIVE,
  1948. power_state.SHUTDOWN,
  1949. force=False, shutdown_terminate=True)
  1950. def test_sync_instance_power_state_to_no_stop(self):
  1951. for ps in (power_state.PAUSED, power_state.NOSTATE):
  1952. self._test_sync_to_stop(power_state.RUNNING, vm_states.ACTIVE, ps,
  1953. stop=False)
  1954. for vs in (vm_states.SOFT_DELETED, vm_states.DELETED):
  1955. for ps in (power_state.NOSTATE, power_state.SHUTDOWN):
  1956. self._test_sync_to_stop(power_state.RUNNING, vs, ps,
  1957. stop=False)
  1958. @mock.patch('nova.compute.manager.ComputeManager.'
  1959. '_sync_instance_power_state')
  1960. def test_query_driver_power_state_and_sync_pending_task(
  1961. self, mock_sync_power_state):
  1962. with mock.patch.object(self.compute.driver,
  1963. 'get_info') as mock_get_info:
  1964. db_instance = objects.Instance(uuid=uuids.db_instance,
  1965. task_state=task_states.POWERING_OFF)
  1966. self.compute._query_driver_power_state_and_sync(self.context,
  1967. db_instance)
  1968. self.assertFalse(mock_get_info.called)
  1969. self.assertFalse(mock_sync_power_state.called)
  1970. @mock.patch('nova.compute.manager.ComputeManager.'
  1971. '_sync_instance_power_state')
  1972. def test_query_driver_power_state_and_sync_not_found_driver(
  1973. self, mock_sync_power_state):
  1974. error = exception.InstanceNotFound(instance_id=1)
  1975. with mock.patch.object(self.compute.driver,
  1976. 'get_info', side_effect=error) as mock_get_info:
  1977. db_instance = objects.Instance(uuid=uuids.db_instance,
  1978. task_state=None)
  1979. self.compute._query_driver_power_state_and_sync(self.context,
  1980. db_instance)
  1981. mock_get_info.assert_called_once_with(db_instance)
  1982. mock_sync_power_state.assert_called_once_with(self.context,
  1983. db_instance,
  1984. power_state.NOSTATE,
  1985. use_slave=True)
  1986. def test_cleanup_running_deleted_instances_virt_driver_not_ready(self):
  1987. """Tests the scenario that the driver raises VirtDriverNotReady
  1988. when listing instances so the task returns early.
  1989. """
  1990. self.flags(running_deleted_instance_action='shutdown')
  1991. with mock.patch.object(self.compute, '_running_deleted_instances',
  1992. side_effect=exception.VirtDriverNotReady) as ls:
  1993. # Mock the virt driver to make sure nothing calls it.
  1994. with mock.patch.object(self.compute, 'driver',
  1995. new_callable=mock.NonCallableMock):
  1996. self.compute._cleanup_running_deleted_instances(self.context)
  1997. ls.assert_called_once_with(test.MatchType(context.RequestContext))
  1998. @mock.patch.object(virt_driver.ComputeDriver, 'delete_instance_files')
  1999. @mock.patch.object(objects.InstanceList, 'get_by_filters')
  2000. def test_run_pending_deletes(self, mock_get, mock_delete):
  2001. self.flags(instance_delete_interval=10)
  2002. class FakeInstance(object):
  2003. def __init__(self, uuid, name, smd):
  2004. self.uuid = uuid
  2005. self.name = name
  2006. self.system_metadata = smd
  2007. self.cleaned = False
  2008. def __getitem__(self, name):
  2009. return getattr(self, name)
  2010. def save(self):
  2011. pass
  2012. def _fake_get(ctx, filter, expected_attrs, use_slave):
  2013. mock_get.assert_called_once_with(
  2014. {'read_deleted': 'yes'},
  2015. {'deleted': True, 'soft_deleted': False, 'host': 'fake-mini',
  2016. 'cleaned': False},
  2017. expected_attrs=['system_metadata'],
  2018. use_slave=True)
  2019. return [a, b, c]
  2020. a = FakeInstance(uuids.instanceA, 'apple', {'clean_attempts': '100'})
  2021. b = FakeInstance(uuids.instanceB, 'orange', {'clean_attempts': '3'})
  2022. c = FakeInstance(uuids.instanceC, 'banana', {})
  2023. mock_get.side_effect = _fake_get
  2024. mock_delete.side_effect = [True, False]
  2025. self.compute._run_pending_deletes({})
  2026. self.assertFalse(a.cleaned)
  2027. self.assertEqual('100', a.system_metadata['clean_attempts'])
  2028. self.assertTrue(b.cleaned)
  2029. self.assertEqual('4', b.system_metadata['clean_attempts'])
  2030. self.assertFalse(c.cleaned)
  2031. self.assertEqual('1', c.system_metadata['clean_attempts'])
  2032. mock_delete.assert_has_calls([mock.call(mock.ANY),
  2033. mock.call(mock.ANY)])
  2034. @mock.patch.object(objects.Migration, 'save')
  2035. @mock.patch.object(objects.MigrationList, 'get_by_filters')
  2036. @mock.patch.object(objects.InstanceList, 'get_by_filters')
  2037. def _test_cleanup_incomplete_migrations(self, inst_host,
  2038. mock_inst_get_by_filters,
  2039. mock_migration_get_by_filters,
  2040. mock_save):
  2041. def fake_inst(context, uuid, host):
  2042. inst = objects.Instance(context)
  2043. inst.uuid = uuid
  2044. inst.host = host
  2045. return inst
  2046. def fake_migration(uuid, status, inst_uuid, src_host, dest_host):
  2047. migration = objects.Migration()
  2048. migration.uuid = uuid
  2049. migration.status = status
  2050. migration.instance_uuid = inst_uuid
  2051. migration.source_compute = src_host
  2052. migration.dest_compute = dest_host
  2053. return migration
  2054. fake_instances = [fake_inst(self.context, uuids.instance_1, inst_host),
  2055. fake_inst(self.context, uuids.instance_2, inst_host)]
  2056. fake_migrations = [fake_migration(uuids.mig1, 'error',
  2057. uuids.instance_1,
  2058. 'fake-host', 'fake-mini'),
  2059. fake_migration(uuids.mig2, 'error',
  2060. uuids.instance_2,
  2061. 'fake-host', 'fake-mini')]
  2062. mock_migration_get_by_filters.return_value = fake_migrations
  2063. mock_inst_get_by_filters.return_value = fake_instances
  2064. with mock.patch.object(self.compute.driver, 'delete_instance_files'):
  2065. self.compute._cleanup_incomplete_migrations(self.context)
  2066. # Ensure that migration status is set to 'failed' after instance
  2067. # files deletion for those instances whose instance.host is not
  2068. # same as compute host where periodic task is running.
  2069. for inst in fake_instances:
  2070. if inst.host != CONF.host:
  2071. for mig in fake_migrations:
  2072. if inst.uuid == mig.instance_uuid:
  2073. self.assertEqual('failed', mig.status)
  2074. def test_cleanup_incomplete_migrations_dest_node(self):
  2075. """Test to ensure instance files are deleted from destination node.
  2076. If instance gets deleted during resizing/revert-resizing operation,
  2077. in that case instance files gets deleted from instance.host (source
  2078. host here), but there is possibility that instance files could be
  2079. present on destination node.
  2080. This test ensures that `_cleanup_incomplete_migration` periodic
  2081. task deletes orphaned instance files from destination compute node.
  2082. """
  2083. self.flags(host='fake-mini')
  2084. self._test_cleanup_incomplete_migrations('fake-host')
  2085. def test_cleanup_incomplete_migrations_source_node(self):
  2086. """Test to ensure instance files are deleted from source node.
  2087. If instance gets deleted during resizing/revert-resizing operation,
  2088. in that case instance files gets deleted from instance.host (dest
  2089. host here), but there is possibility that instance files could be
  2090. present on source node.
  2091. This test ensures that `_cleanup_incomplete_migration` periodic
  2092. task deletes orphaned instance files from source compute node.
  2093. """
  2094. self.flags(host='fake-host')
  2095. self._test_cleanup_incomplete_migrations('fake-mini')
  2096. def test_attach_interface_failure(self):
  2097. # Test that the fault methods are invoked when an attach fails
  2098. db_instance = fake_instance.fake_db_instance()
  2099. f_instance = objects.Instance._from_db_object(self.context,
  2100. objects.Instance(),
  2101. db_instance)
  2102. e = exception.InterfaceAttachFailed(instance_uuid=f_instance.uuid)
  2103. @mock.patch.object(compute_utils, 'EventReporter')
  2104. @mock.patch.object(compute_utils, 'notify_about_instance_action')
  2105. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  2106. @mock.patch.object(self.compute.network_api,
  2107. 'allocate_port_for_instance',
  2108. side_effect=e)
  2109. @mock.patch.object(self.compute, '_instance_update',
  2110. side_effect=lambda *a, **k: {})
  2111. def do_test(update, meth, add_fault, notify, event):
  2112. self.assertRaises(exception.InterfaceAttachFailed,
  2113. self.compute.attach_interface,
  2114. self.context, f_instance, 'net_id', 'port_id',
  2115. None, None)
  2116. add_fault.assert_has_calls([
  2117. mock.call(self.context, f_instance, e,
  2118. mock.ANY)])
  2119. event.assert_called_once_with(
  2120. self.context, 'compute_attach_interface', CONF.host,
  2121. f_instance.uuid)
  2122. with mock.patch.dict(self.compute.driver.capabilities,
  2123. supports_attach_interface=True):
  2124. do_test()
  2125. def test_detach_interface_failure(self):
  2126. # Test that the fault methods are invoked when a detach fails
  2127. # Build test data that will cause a PortNotFound exception
  2128. nw_info = network_model.NetworkInfo([])
  2129. info_cache = objects.InstanceInfoCache(network_info=nw_info,
  2130. instance_uuid=uuids.instance)
  2131. f_instance = objects.Instance(id=3, uuid=uuids.instance,
  2132. info_cache=info_cache)
  2133. @mock.patch.object(compute_utils, 'EventReporter')
  2134. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  2135. @mock.patch.object(self.compute, '_set_instance_obj_error_state')
  2136. def do_test(meth, add_fault, event):
  2137. self.assertRaises(exception.PortNotFound,
  2138. self.compute.detach_interface,
  2139. self.context, f_instance, 'port_id')
  2140. add_fault.assert_has_calls(
  2141. [mock.call(self.context, f_instance, mock.ANY, mock.ANY)])
  2142. event.assert_called_once_with(
  2143. self.context, 'compute_detach_interface', CONF.host,
  2144. f_instance.uuid)
  2145. do_test()
  2146. @mock.patch('nova.compute.manager.LOG.log')
  2147. @mock.patch.object(compute_utils, 'EventReporter')
  2148. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  2149. @mock.patch.object(compute_utils, 'notify_about_instance_action')
  2150. def test_detach_interface_instance_not_found(self, mock_notify, mock_fault,
  2151. mock_event, mock_log):
  2152. nw_info = network_model.NetworkInfo([
  2153. network_model.VIF(uuids.port_id)])
  2154. info_cache = objects.InstanceInfoCache(network_info=nw_info,
  2155. instance_uuid=uuids.instance)
  2156. instance = objects.Instance(id=1, uuid=uuids.instance,
  2157. info_cache=info_cache)
  2158. with mock.patch.object(self.compute.driver, 'detach_interface',
  2159. side_effect=exception.InstanceNotFound(
  2160. instance_id=uuids.instance)):
  2161. self.assertRaises(exception.InterfaceDetachFailed,
  2162. self.compute.detach_interface,
  2163. self.context, instance, uuids.port_id)
  2164. self.assertEqual(1, mock_log.call_count)
  2165. self.assertEqual(logging.DEBUG, mock_log.call_args[0][0])
  2166. @mock.patch.object(compute_utils, 'EventReporter')
  2167. @mock.patch.object(virt_driver.ComputeDriver, 'get_volume_connector',
  2168. return_value={})
  2169. @mock.patch.object(manager.ComputeManager, '_instance_update',
  2170. return_value={})
  2171. @mock.patch.object(db, 'instance_fault_create')
  2172. @mock.patch.object(db, 'block_device_mapping_update')
  2173. @mock.patch.object(db,
  2174. 'block_device_mapping_get_by_instance_and_volume_id')
  2175. @mock.patch.object(cinder.API, 'migrate_volume_completion')
  2176. @mock.patch.object(cinder.API, 'terminate_connection')
  2177. @mock.patch.object(cinder.API, 'unreserve_volume')
  2178. @mock.patch.object(cinder.API, 'get')
  2179. @mock.patch.object(cinder.API, 'roll_detaching')
  2180. @mock.patch.object(compute_utils, 'notify_about_volume_swap')
  2181. def _test_swap_volume(self, mock_notify, mock_roll_detaching,
  2182. mock_cinder_get, mock_unreserve_volume,
  2183. mock_terminate_connection,
  2184. mock_migrate_volume_completion,
  2185. mock_bdm_get, mock_bdm_update,
  2186. mock_instance_fault_create,
  2187. mock_instance_update,
  2188. mock_get_volume_connector,
  2189. mock_event,
  2190. expected_exception=None):
  2191. # This test ensures that volume_id arguments are passed to volume_api
  2192. # and that volume states are OK
  2193. volumes = {}
  2194. volumes[uuids.old_volume] = {'id': uuids.old_volume,
  2195. 'display_name': 'old_volume',
  2196. 'status': 'detaching',
  2197. 'size': 1}
  2198. volumes[uuids.new_volume] = {'id': uuids.new_volume,
  2199. 'display_name': 'new_volume',
  2200. 'status': 'available',
  2201. 'size': 2}
  2202. fake_bdm = fake_block_device.FakeDbBlockDeviceDict(
  2203. {'device_name': '/dev/vdb', 'source_type': 'volume',
  2204. 'destination_type': 'volume',
  2205. 'instance_uuid': uuids.instance,
  2206. 'connection_info': '{"foo": "bar"}',
  2207. 'volume_id': uuids.old_volume,
  2208. 'attachment_id': None})
  2209. def fake_vol_api_roll_detaching(context, volume_id):
  2210. self.assertTrue(uuidutils.is_uuid_like(volume_id))
  2211. if volumes[volume_id]['status'] == 'detaching':
  2212. volumes[volume_id]['status'] = 'in-use'
  2213. def fake_vol_api_func(context, volume, *args):
  2214. self.assertTrue(uuidutils.is_uuid_like(volume))
  2215. return {}
  2216. def fake_vol_get(context, volume_id):
  2217. self.assertTrue(uuidutils.is_uuid_like(volume_id))
  2218. return volumes[volume_id]
  2219. def fake_vol_unreserve(context, volume_id):
  2220. self.assertTrue(uuidutils.is_uuid_like(volume_id))
  2221. if volumes[volume_id]['status'] == 'attaching':
  2222. volumes[volume_id]['status'] = 'available'
  2223. def fake_vol_migrate_volume_completion(context, old_volume_id,
  2224. new_volume_id, error=False):
  2225. self.assertTrue(uuidutils.is_uuid_like(old_volume_id))
  2226. self.assertTrue(uuidutils.is_uuid_like(new_volume_id))
  2227. volumes[old_volume_id]['status'] = 'in-use'
  2228. return {'save_volume_id': new_volume_id}
  2229. def fake_block_device_mapping_update(ctxt, id, updates, legacy):
  2230. self.assertEqual(2, updates['volume_size'])
  2231. return fake_bdm
  2232. mock_roll_detaching.side_effect = fake_vol_api_roll_detaching
  2233. mock_terminate_connection.side_effect = fake_vol_api_func
  2234. mock_cinder_get.side_effect = fake_vol_get
  2235. mock_migrate_volume_completion.side_effect = (
  2236. fake_vol_migrate_volume_completion)
  2237. mock_unreserve_volume.side_effect = fake_vol_unreserve
  2238. mock_bdm_get.return_value = fake_bdm
  2239. mock_bdm_update.side_effect = fake_block_device_mapping_update
  2240. mock_instance_fault_create.return_value = (
  2241. test_instance_fault.fake_faults['fake-uuid'][0])
  2242. instance1 = fake_instance.fake_instance_obj(
  2243. self.context, **{'uuid': uuids.instance})
  2244. if expected_exception:
  2245. volumes[uuids.old_volume]['status'] = 'detaching'
  2246. volumes[uuids.new_volume]['status'] = 'attaching'
  2247. self.assertRaises(expected_exception, self.compute.swap_volume,
  2248. self.context, uuids.old_volume, uuids.new_volume,
  2249. instance1, None)
  2250. self.assertEqual('in-use', volumes[uuids.old_volume]['status'])
  2251. self.assertEqual('available', volumes[uuids.new_volume]['status'])
  2252. self.assertEqual(2, mock_notify.call_count)
  2253. mock_notify.assert_any_call(
  2254. test.MatchType(context.RequestContext), instance1,
  2255. self.compute.host,
  2256. fields.NotificationPhase.START,
  2257. uuids.old_volume, uuids.new_volume)
  2258. mock_notify.assert_any_call(
  2259. test.MatchType(context.RequestContext), instance1,
  2260. self.compute.host,
  2261. fields.NotificationPhase.ERROR,
  2262. uuids.old_volume, uuids.new_volume,
  2263. test.MatchType(expected_exception), mock.ANY)
  2264. else:
  2265. self.compute.swap_volume(self.context, uuids.old_volume,
  2266. uuids.new_volume, instance1, None)
  2267. self.assertEqual(volumes[uuids.old_volume]['status'], 'in-use')
  2268. self.assertEqual(2, mock_notify.call_count)
  2269. mock_notify.assert_any_call(test.MatchType(context.RequestContext),
  2270. instance1, self.compute.host,
  2271. fields.NotificationPhase.START,
  2272. uuids.old_volume, uuids.new_volume)
  2273. mock_notify.assert_any_call(test.MatchType(context.RequestContext),
  2274. instance1, self.compute.host,
  2275. fields.NotificationPhase.END,
  2276. uuids.old_volume, uuids.new_volume)
  2277. mock_event.assert_called_once_with(self.context,
  2278. 'compute_swap_volume',
  2279. CONF.host,
  2280. instance1.uuid)
  2281. def _assert_volume_api(self, context, volume, *args):
  2282. self.assertTrue(uuidutils.is_uuid_like(volume))
  2283. return {}
  2284. def _assert_swap_volume(self, context, old_connection_info,
  2285. new_connection_info, instance, mountpoint,
  2286. resize_to):
  2287. self.assertEqual(2, resize_to)
  2288. @mock.patch.object(cinder.API, 'initialize_connection')
  2289. @mock.patch.object(fake_driver.FakeDriver, 'swap_volume')
  2290. def test_swap_volume_volume_api_usage(self, mock_swap_volume,
  2291. mock_initialize_connection):
  2292. mock_initialize_connection.side_effect = self._assert_volume_api
  2293. mock_swap_volume.side_effect = self._assert_swap_volume
  2294. self._test_swap_volume()
  2295. @mock.patch.object(cinder.API, 'initialize_connection')
  2296. @mock.patch.object(fake_driver.FakeDriver, 'swap_volume',
  2297. side_effect=test.TestingException())
  2298. def test_swap_volume_with_compute_driver_exception(
  2299. self, mock_swap_volume, mock_initialize_connection):
  2300. mock_initialize_connection.side_effect = self._assert_volume_api
  2301. self._test_swap_volume(expected_exception=test.TestingException)
  2302. @mock.patch.object(cinder.API, 'initialize_connection',
  2303. side_effect=test.TestingException())
  2304. @mock.patch.object(fake_driver.FakeDriver, 'swap_volume')
  2305. def test_swap_volume_with_initialize_connection_exception(
  2306. self, mock_swap_volume, mock_initialize_connection):
  2307. self._test_swap_volume(expected_exception=test.TestingException)
  2308. @mock.patch('nova.compute.utils.notify_about_volume_swap')
  2309. @mock.patch(
  2310. 'nova.db.api.block_device_mapping_get_by_instance_and_volume_id')
  2311. @mock.patch('nova.db.api.block_device_mapping_update')
  2312. @mock.patch('nova.volume.cinder.API.get')
  2313. @mock.patch('nova.virt.libvirt.LibvirtDriver.get_volume_connector')
  2314. @mock.patch('nova.compute.manager.ComputeManager._swap_volume')
  2315. def test_swap_volume_delete_on_termination_flag(self, swap_volume_mock,
  2316. volume_connector_mock,
  2317. get_volume_mock,
  2318. update_bdm_mock,
  2319. get_bdm_mock,
  2320. notify_mock):
  2321. # This test ensures that delete_on_termination flag arguments
  2322. # are reserved
  2323. volumes = {}
  2324. old_volume_id = uuids.fake
  2325. volumes[old_volume_id] = {'id': old_volume_id,
  2326. 'display_name': 'old_volume',
  2327. 'status': 'detaching',
  2328. 'size': 2}
  2329. new_volume_id = uuids.fake_2
  2330. volumes[new_volume_id] = {'id': new_volume_id,
  2331. 'display_name': 'new_volume',
  2332. 'status': 'available',
  2333. 'size': 2}
  2334. fake_bdm = fake_block_device.FakeDbBlockDeviceDict(
  2335. {'device_name': '/dev/vdb', 'source_type': 'volume',
  2336. 'destination_type': 'volume',
  2337. 'instance_uuid': uuids.instance,
  2338. 'delete_on_termination': True,
  2339. 'connection_info': '{"foo": "bar"}',
  2340. 'attachment_id': None})
  2341. comp_ret = {'save_volume_id': old_volume_id}
  2342. new_info = {"foo": "bar", "serial": old_volume_id}
  2343. swap_volume_mock.return_value = (comp_ret, new_info)
  2344. volume_connector_mock.return_value = {}
  2345. update_bdm_mock.return_value = fake_bdm
  2346. get_bdm_mock.return_value = fake_bdm
  2347. get_volume_mock.return_value = volumes[old_volume_id]
  2348. self.compute.swap_volume(self.context, old_volume_id, new_volume_id,
  2349. fake_instance.fake_instance_obj(self.context,
  2350. **{'uuid': uuids.instance}),
  2351. None)
  2352. update_values = {'no_device': False,
  2353. 'connection_info': jsonutils.dumps(new_info),
  2354. 'volume_id': old_volume_id,
  2355. 'source_type': u'volume',
  2356. 'snapshot_id': None,
  2357. 'destination_type': u'volume'}
  2358. update_bdm_mock.assert_called_once_with(mock.ANY, mock.ANY,
  2359. update_values, legacy=False)
  2360. @mock.patch.object(compute_utils, 'notify_about_volume_swap')
  2361. @mock.patch.object(objects.BlockDeviceMapping,
  2362. 'get_by_volume_and_instance')
  2363. @mock.patch('nova.volume.cinder.API.get')
  2364. @mock.patch('nova.volume.cinder.API.attachment_update')
  2365. @mock.patch('nova.volume.cinder.API.attachment_delete')
  2366. @mock.patch('nova.volume.cinder.API.attachment_complete')
  2367. @mock.patch('nova.volume.cinder.API.migrate_volume_completion',
  2368. return_value={'save_volume_id': uuids.old_volume_id})
  2369. def test_swap_volume_with_new_attachment_id_cinder_migrate_true(
  2370. self, migrate_volume_completion, attachment_complete,
  2371. attachment_delete, attachment_update, get_volume, get_bdm,
  2372. notify_about_volume_swap):
  2373. """Tests a swap volume operation with a new style volume attachment
  2374. passed in from the compute API, and the case that Cinder initiated
  2375. the swap volume because of a volume retype situation. This is a happy
  2376. path test. Since it is a retype there is no volume size change.
  2377. """
  2378. bdm = objects.BlockDeviceMapping(
  2379. volume_id=uuids.old_volume_id, device_name='/dev/vda',
  2380. attachment_id=uuids.old_attachment_id,
  2381. connection_info='{"data": {}}', volume_size=1)
  2382. old_volume = {
  2383. 'id': uuids.old_volume_id, 'size': 1, 'status': 'retyping',
  2384. 'migration_status': 'migrating', 'multiattach': False
  2385. }
  2386. new_volume = {
  2387. 'id': uuids.new_volume_id, 'size': 1, 'status': 'reserved',
  2388. 'migration_status': 'migrating', 'multiattach': False
  2389. }
  2390. attachment_update.return_value = {"connection_info": {"data": {}}}
  2391. get_bdm.return_value = bdm
  2392. get_volume.side_effect = (old_volume, new_volume)
  2393. instance = fake_instance.fake_instance_obj(self.context)
  2394. with test.nested(
  2395. mock.patch.object(self.context, 'elevated',
  2396. return_value=self.context),
  2397. mock.patch.object(self.compute.driver, 'get_volume_connector',
  2398. return_value=mock.sentinel.connector),
  2399. mock.patch.object(bdm, 'save')
  2400. ) as (
  2401. mock_elevated, mock_get_volume_connector, mock_save
  2402. ):
  2403. self.compute.swap_volume(
  2404. self.context, uuids.old_volume_id, uuids.new_volume_id,
  2405. instance, uuids.new_attachment_id)
  2406. # Assert the expected calls.
  2407. get_bdm.assert_called_once_with(
  2408. self.context, uuids.old_volume_id, instance.uuid)
  2409. # We updated the new attachment with the host connector.
  2410. attachment_update.assert_called_once_with(
  2411. self.context, uuids.new_attachment_id, mock.sentinel.connector,
  2412. bdm.device_name)
  2413. # We tell Cinder that the new volume is connected
  2414. attachment_complete.assert_called_once_with(
  2415. self.context, uuids.new_attachment_id)
  2416. # After a successful swap volume, we deleted the old attachment.
  2417. attachment_delete.assert_called_once_with(
  2418. self.context, uuids.old_attachment_id)
  2419. # After a successful swap volume, we tell Cinder so it can complete
  2420. # the retype operation.
  2421. migrate_volume_completion.assert_called_once_with(
  2422. self.context, uuids.old_volume_id, uuids.new_volume_id,
  2423. error=False)
  2424. # The BDM should have been updated. Since it's a retype, the old
  2425. # volume ID is returned from Cinder so that's what goes into the
  2426. # BDM but the new attachment ID is saved.
  2427. mock_save.assert_called_once_with()
  2428. self.assertEqual(uuids.old_volume_id, bdm.volume_id)
  2429. self.assertEqual(uuids.new_attachment_id, bdm.attachment_id)
  2430. self.assertEqual(1, bdm.volume_size)
  2431. self.assertEqual(uuids.old_volume_id,
  2432. jsonutils.loads(bdm.connection_info)['serial'])
  2433. @mock.patch.object(compute_utils, 'notify_about_volume_swap')
  2434. @mock.patch.object(objects.BlockDeviceMapping,
  2435. 'get_by_volume_and_instance')
  2436. @mock.patch('nova.volume.cinder.API.get')
  2437. @mock.patch('nova.volume.cinder.API.attachment_update')
  2438. @mock.patch('nova.volume.cinder.API.attachment_delete')
  2439. @mock.patch('nova.volume.cinder.API.attachment_complete')
  2440. @mock.patch('nova.volume.cinder.API.migrate_volume_completion')
  2441. def test_swap_volume_with_new_attachment_id_cinder_migrate_false(
  2442. self, migrate_volume_completion, attachment_complete,
  2443. attachment_delete, attachment_update, get_volume, get_bdm,
  2444. notify_about_volume_swap):
  2445. """Tests a swap volume operation with a new style volume attachment
  2446. passed in from the compute API, and the case that Cinder did not
  2447. initiate the swap volume. This is a happy path test. Since it is not a
  2448. retype we also change the size.
  2449. """
  2450. bdm = objects.BlockDeviceMapping(
  2451. volume_id=uuids.old_volume_id, device_name='/dev/vda',
  2452. attachment_id=uuids.old_attachment_id,
  2453. connection_info='{"data": {}}')
  2454. old_volume = {
  2455. 'id': uuids.old_volume_id, 'size': 1, 'status': 'detaching',
  2456. 'multiattach': False
  2457. }
  2458. new_volume = {
  2459. 'id': uuids.new_volume_id, 'size': 2, 'status': 'reserved',
  2460. 'multiattach': False
  2461. }
  2462. attachment_update.return_value = {"connection_info": {"data": {}}}
  2463. get_bdm.return_value = bdm
  2464. get_volume.side_effect = (old_volume, new_volume)
  2465. instance = fake_instance.fake_instance_obj(self.context)
  2466. with test.nested(
  2467. mock.patch.object(self.context, 'elevated',
  2468. return_value=self.context),
  2469. mock.patch.object(self.compute.driver, 'get_volume_connector',
  2470. return_value=mock.sentinel.connector),
  2471. mock.patch.object(bdm, 'save')
  2472. ) as (
  2473. mock_elevated, mock_get_volume_connector, mock_save
  2474. ):
  2475. self.compute.swap_volume(
  2476. self.context, uuids.old_volume_id, uuids.new_volume_id,
  2477. instance, uuids.new_attachment_id)
  2478. # Assert the expected calls.
  2479. get_bdm.assert_called_once_with(
  2480. self.context, uuids.old_volume_id, instance.uuid)
  2481. # We updated the new attachment with the host connector.
  2482. attachment_update.assert_called_once_with(
  2483. self.context, uuids.new_attachment_id, mock.sentinel.connector,
  2484. bdm.device_name)
  2485. # We tell Cinder that the new volume is connected
  2486. attachment_complete.assert_called_once_with(
  2487. self.context, uuids.new_attachment_id)
  2488. # After a successful swap volume, we deleted the old attachment.
  2489. attachment_delete.assert_called_once_with(
  2490. self.context, uuids.old_attachment_id)
  2491. # After a successful swap volume, since it was not a
  2492. # Cinder-initiated call, we don't call migrate_volume_completion.
  2493. migrate_volume_completion.assert_not_called()
  2494. # The BDM should have been updated. Since it's a not a retype, the
  2495. # volume_id is now the new volume ID.
  2496. mock_save.assert_called_once_with()
  2497. self.assertEqual(uuids.new_volume_id, bdm.volume_id)
  2498. self.assertEqual(uuids.new_attachment_id, bdm.attachment_id)
  2499. self.assertEqual(2, bdm.volume_size)
  2500. new_conn_info = jsonutils.loads(bdm.connection_info)
  2501. self.assertEqual(uuids.new_volume_id, new_conn_info['serial'])
  2502. self.assertNotIn('multiattach', new_conn_info)
  2503. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  2504. @mock.patch.object(compute_utils, 'notify_about_volume_swap')
  2505. @mock.patch.object(objects.BlockDeviceMapping,
  2506. 'get_by_volume_and_instance')
  2507. @mock.patch('nova.volume.cinder.API.get')
  2508. @mock.patch('nova.volume.cinder.API.attachment_update',
  2509. side_effect=exception.VolumeAttachmentNotFound(
  2510. attachment_id=uuids.new_attachment_id))
  2511. @mock.patch('nova.volume.cinder.API.roll_detaching')
  2512. @mock.patch('nova.volume.cinder.API.attachment_delete')
  2513. @mock.patch('nova.volume.cinder.API.migrate_volume_completion')
  2514. def test_swap_volume_with_new_attachment_id_attachment_update_fails(
  2515. self, migrate_volume_completion, attachment_delete, roll_detaching,
  2516. attachment_update, get_volume, get_bdm, notify_about_volume_swap,
  2517. add_instance_fault_from_exc):
  2518. """Tests a swap volume operation with a new style volume attachment
  2519. passed in from the compute API, and the case that Cinder initiated
  2520. the swap volume because of a volume migrate situation. This is a
  2521. negative test where attachment_update fails.
  2522. """
  2523. bdm = objects.BlockDeviceMapping(
  2524. volume_id=uuids.old_volume_id, device_name='/dev/vda',
  2525. attachment_id=uuids.old_attachment_id,
  2526. connection_info='{"data": {}}')
  2527. old_volume = {
  2528. 'id': uuids.old_volume_id, 'size': 1, 'status': 'in-use',
  2529. 'migration_status': 'migrating', 'multiattach': False
  2530. }
  2531. new_volume = {
  2532. 'id': uuids.new_volume_id, 'size': 1, 'status': 'reserved',
  2533. 'migration_status': 'migrating', 'multiattach': False
  2534. }
  2535. get_bdm.return_value = bdm
  2536. get_volume.side_effect = (old_volume, new_volume)
  2537. instance = fake_instance.fake_instance_obj(self.context)
  2538. with test.nested(
  2539. mock.patch.object(self.context, 'elevated',
  2540. return_value=self.context),
  2541. mock.patch.object(self.compute.driver, 'get_volume_connector',
  2542. return_value=mock.sentinel.connector)
  2543. ) as (
  2544. mock_elevated, mock_get_volume_connector
  2545. ):
  2546. self.assertRaises(
  2547. exception.VolumeAttachmentNotFound, self.compute.swap_volume,
  2548. self.context, uuids.old_volume_id, uuids.new_volume_id,
  2549. instance, uuids.new_attachment_id)
  2550. # Assert the expected calls.
  2551. get_bdm.assert_called_once_with(
  2552. self.context, uuids.old_volume_id, instance.uuid)
  2553. # We tried to update the new attachment with the host connector.
  2554. attachment_update.assert_called_once_with(
  2555. self.context, uuids.new_attachment_id, mock.sentinel.connector,
  2556. bdm.device_name)
  2557. # After a failure, we rollback the detaching status of the old
  2558. # volume.
  2559. roll_detaching.assert_called_once_with(
  2560. self.context, uuids.old_volume_id)
  2561. # After a failure, we deleted the new attachment.
  2562. attachment_delete.assert_called_once_with(
  2563. self.context, uuids.new_attachment_id)
  2564. # After a failure for a Cinder-initiated swap volume, we called
  2565. # migrate_volume_completion to let Cinder know things blew up.
  2566. migrate_volume_completion.assert_called_once_with(
  2567. self.context, uuids.old_volume_id, uuids.new_volume_id,
  2568. error=True)
  2569. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  2570. @mock.patch.object(compute_utils, 'notify_about_volume_swap')
  2571. @mock.patch.object(objects.BlockDeviceMapping,
  2572. 'get_by_volume_and_instance')
  2573. @mock.patch('nova.volume.cinder.API.get')
  2574. @mock.patch('nova.volume.cinder.API.attachment_update')
  2575. @mock.patch('nova.volume.cinder.API.roll_detaching')
  2576. @mock.patch('nova.volume.cinder.API.attachment_delete')
  2577. @mock.patch('nova.volume.cinder.API.migrate_volume_completion')
  2578. def test_swap_volume_with_new_attachment_id_driver_swap_fails(
  2579. self, migrate_volume_completion, attachment_delete, roll_detaching,
  2580. attachment_update, get_volume, get_bdm, notify_about_volume_swap,
  2581. add_instance_fault_from_exc):
  2582. """Tests a swap volume operation with a new style volume attachment
  2583. passed in from the compute API, and the case that Cinder did not
  2584. initiate the swap volume. This is a negative test where the compute
  2585. driver swap_volume method fails.
  2586. """
  2587. bdm = objects.BlockDeviceMapping(
  2588. volume_id=uuids.old_volume_id, device_name='/dev/vda',
  2589. attachment_id=uuids.old_attachment_id,
  2590. connection_info='{"data": {}}')
  2591. old_volume = {
  2592. 'id': uuids.old_volume_id, 'size': 1, 'status': 'detaching',
  2593. 'multiattach': False
  2594. }
  2595. new_volume = {
  2596. 'id': uuids.new_volume_id, 'size': 2, 'status': 'reserved',
  2597. 'multiattach': False
  2598. }
  2599. attachment_update.return_value = {"connection_info": {"data": {}}}
  2600. get_bdm.return_value = bdm
  2601. get_volume.side_effect = (old_volume, new_volume)
  2602. instance = fake_instance.fake_instance_obj(self.context)
  2603. with test.nested(
  2604. mock.patch.object(self.context, 'elevated',
  2605. return_value=self.context),
  2606. mock.patch.object(self.compute.driver, 'get_volume_connector',
  2607. return_value=mock.sentinel.connector),
  2608. mock.patch.object(self.compute.driver, 'swap_volume',
  2609. side_effect=test.TestingException('yikes'))
  2610. ) as (
  2611. mock_elevated, mock_get_volume_connector, mock_driver_swap
  2612. ):
  2613. self.assertRaises(
  2614. test.TestingException, self.compute.swap_volume,
  2615. self.context, uuids.old_volume_id, uuids.new_volume_id,
  2616. instance, uuids.new_attachment_id)
  2617. # Assert the expected calls.
  2618. # The new connection_info has the new_volume_id as the serial.
  2619. new_cinfo = mock_driver_swap.call_args[0][2]
  2620. self.assertIn('serial', new_cinfo)
  2621. self.assertEqual(uuids.new_volume_id, new_cinfo['serial'])
  2622. get_bdm.assert_called_once_with(
  2623. self.context, uuids.old_volume_id, instance.uuid)
  2624. # We updated the new attachment with the host connector.
  2625. attachment_update.assert_called_once_with(
  2626. self.context, uuids.new_attachment_id, mock.sentinel.connector,
  2627. bdm.device_name)
  2628. # After a failure, we rollback the detaching status of the old
  2629. # volume.
  2630. roll_detaching.assert_called_once_with(
  2631. self.context, uuids.old_volume_id)
  2632. # After a failed swap volume, we deleted the new attachment.
  2633. attachment_delete.assert_called_once_with(
  2634. self.context, uuids.new_attachment_id)
  2635. # After a failed swap volume, since it was not a
  2636. # Cinder-initiated call, we don't call migrate_volume_completion.
  2637. migrate_volume_completion.assert_not_called()
  2638. @mock.patch('nova.volume.cinder.API.attachment_update')
  2639. def test_swap_volume_with_multiattach(self, attachment_update):
  2640. """Tests swap volume where the volume being swapped-to supports
  2641. multiattach as well as the compute driver, so the attachment for the
  2642. new volume (created in the API) is updated with the host connector
  2643. and the new_connection_info is updated with the multiattach flag.
  2644. """
  2645. bdm = objects.BlockDeviceMapping(
  2646. volume_id=uuids.old_volume_id, device_name='/dev/vda',
  2647. attachment_id=uuids.old_attachment_id,
  2648. connection_info='{"data": {}}')
  2649. new_volume = {
  2650. 'id': uuids.new_volume_id, 'size': 2, 'status': 'reserved',
  2651. 'multiattach': True
  2652. }
  2653. attachment_update.return_value = {"connection_info": {"data": {}}}
  2654. connector = mock.sentinel.connector
  2655. with mock.patch.dict(self.compute.driver.capabilities,
  2656. {'supports_multiattach': True}):
  2657. _, new_cinfo = self.compute._init_volume_connection(
  2658. self.context, new_volume, uuids.old_volume_id,
  2659. connector, bdm, uuids.new_attachment_id, bdm.device_name)
  2660. self.assertEqual(uuids.new_volume_id, new_cinfo['serial'])
  2661. self.assertIn('multiattach', new_cinfo)
  2662. self.assertTrue(new_cinfo['multiattach'])
  2663. attachment_update.assert_called_once_with(
  2664. self.context, uuids.new_attachment_id, connector,
  2665. bdm.device_name)
  2666. def test_swap_volume_with_multiattach_no_driver_support(self):
  2667. """Tests a swap volume scenario where the new volume being swapped-to
  2668. supports multiattach but the virt driver does not, so swap volume
  2669. fails.
  2670. """
  2671. bdm = objects.BlockDeviceMapping(
  2672. volume_id=uuids.old_volume_id, device_name='/dev/vda',
  2673. attachment_id=uuids.old_attachment_id,
  2674. connection_info='{"data": {}}')
  2675. new_volume = {
  2676. 'id': uuids.new_volume_id, 'size': 2, 'status': 'reserved',
  2677. 'multiattach': True
  2678. }
  2679. connector = {'host': 'localhost'}
  2680. with mock.patch.dict(self.compute.driver.capabilities,
  2681. {'supports_multiattach': False}):
  2682. self.assertRaises(exception.MultiattachNotSupportedByVirtDriver,
  2683. self.compute._init_volume_connection,
  2684. self.context, new_volume, uuids.old_volume_id,
  2685. connector, bdm, uuids.new_attachment_id,
  2686. bdm.device_name)
  2687. def test_live_migration_claim(self):
  2688. mock_claim = mock.Mock()
  2689. mock_claim.claimed_numa_topology = mock.sentinel.claimed_topology
  2690. stub_image_meta = objects.ImageMeta()
  2691. post_claim_md = migrate_data_obj.LiveMigrateData()
  2692. with test.nested(
  2693. mock.patch.object(self.compute.rt,
  2694. 'live_migration_claim',
  2695. return_value=mock_claim),
  2696. mock.patch.object(self.compute, '_get_nodename',
  2697. return_value='fake-dest-node'),
  2698. mock.patch.object(objects.ImageMeta, 'from_instance',
  2699. return_value=stub_image_meta),
  2700. mock.patch.object(fake_driver.FakeDriver,
  2701. 'post_claim_migrate_data',
  2702. return_value=post_claim_md)
  2703. ) as (mock_lm_claim, mock_get_nodename, mock_from_instance,
  2704. mock_post_claim_migrate_data):
  2705. instance = objects.Instance(flavor=objects.Flavor())
  2706. md = objects.LibvirtLiveMigrateData()
  2707. migration = objects.Migration()
  2708. self.assertEqual(
  2709. post_claim_md,
  2710. self.compute._live_migration_claim(
  2711. self.context, instance, md, migration,
  2712. mock.sentinel.limits))
  2713. mock_lm_claim.assert_called_once_with(
  2714. self.context, instance, 'fake-dest-node', migration,
  2715. mock.sentinel.limits)
  2716. mock_post_claim_migrate_data.assert_called_once_with(
  2717. self.context, instance, md, mock_claim)
  2718. def test_live_migration_claim_claim_raises(self):
  2719. stub_image_meta = objects.ImageMeta()
  2720. with test.nested(
  2721. mock.patch.object(
  2722. self.compute.rt, 'live_migration_claim',
  2723. side_effect=exception.ComputeResourcesUnavailable(
  2724. reason='bork')),
  2725. mock.patch.object(self.compute, '_get_nodename',
  2726. return_value='fake-dest-node'),
  2727. mock.patch.object(objects.ImageMeta, 'from_instance',
  2728. return_value=stub_image_meta),
  2729. mock.patch.object(fake_driver.FakeDriver,
  2730. 'post_claim_migrate_data')
  2731. ) as (mock_lm_claim, mock_get_nodename, mock_from_instance,
  2732. mock_post_claim_migrate_data):
  2733. instance = objects.Instance(flavor=objects.Flavor())
  2734. migration = objects.Migration()
  2735. self.assertRaises(
  2736. exception.MigrationPreCheckError,
  2737. self.compute._live_migration_claim,
  2738. self.context, instance, objects.LibvirtLiveMigrateData(),
  2739. migration, mock.sentinel.limits)
  2740. mock_lm_claim.assert_called_once_with(
  2741. self.context, instance, 'fake-dest-node', migration,
  2742. mock.sentinel.limits)
  2743. mock_get_nodename.assert_called_once_with(instance)
  2744. mock_post_claim_migrate_data.assert_not_called()
  2745. def test_drop_move_claim_at_destination(self):
  2746. instance = objects.Instance(flavor=objects.Flavor())
  2747. with test.nested(
  2748. mock.patch.object(self.compute.rt, 'drop_move_claim'),
  2749. mock.patch.object(self.compute, '_get_nodename',
  2750. return_value='fake-node')
  2751. ) as (mock_drop_move_claim, mock_get_nodename):
  2752. self.compute.drop_move_claim_at_destination(self.context, instance)
  2753. mock_get_nodename.assert_called_once_with(instance)
  2754. mock_drop_move_claim.assert_called_once_with(
  2755. self.context, instance, 'fake-node',
  2756. instance_type=instance.flavor)
  2757. @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid')
  2758. @mock.patch.object(fake_driver.FakeDriver,
  2759. 'check_can_live_migrate_source')
  2760. @mock.patch.object(manager.ComputeManager,
  2761. '_get_instance_block_device_info')
  2762. @mock.patch.object(compute_utils, 'is_volume_backed_instance')
  2763. @mock.patch.object(compute_utils, 'EventReporter')
  2764. @mock.patch.object(manager.ComputeManager, '_source_can_numa_live_migrate')
  2765. def test_check_can_live_migrate_source(
  2766. self, mock_src_can_numa, mock_event, mock_volume, mock_get_inst,
  2767. mock_check, mock_get_bdms):
  2768. fake_bdms = objects.BlockDeviceMappingList()
  2769. mock_get_bdms.return_value = fake_bdms
  2770. drvr_check_result = migrate_data_obj.LiveMigrateData()
  2771. mock_check.return_value = drvr_check_result
  2772. can_numa_result = migrate_data_obj.LiveMigrateData()
  2773. mock_src_can_numa.return_value = can_numa_result
  2774. is_volume_backed = 'volume_backed'
  2775. dest_check_data = migrate_data_obj.LiveMigrateData()
  2776. db_instance = fake_instance.fake_db_instance()
  2777. instance = objects.Instance._from_db_object(
  2778. self.context, objects.Instance(), db_instance)
  2779. mock_volume.return_value = is_volume_backed
  2780. mock_get_inst.return_value = {'block_device_mapping': 'fake'}
  2781. result = self.compute.check_can_live_migrate_source(
  2782. self.context, instance=instance,
  2783. dest_check_data=dest_check_data)
  2784. self.assertEqual(can_numa_result, result)
  2785. mock_src_can_numa.assert_called_once_with(
  2786. self.context, dest_check_data, drvr_check_result)
  2787. mock_event.assert_called_once_with(
  2788. self.context, 'compute_check_can_live_migrate_source', CONF.host,
  2789. instance.uuid)
  2790. mock_check.assert_called_once_with(self.context, instance,
  2791. dest_check_data,
  2792. {'block_device_mapping': 'fake'})
  2793. mock_volume.assert_called_once_with(self.context, instance, fake_bdms)
  2794. mock_get_inst.assert_called_once_with(self.context, instance,
  2795. refresh_conn_info=False,
  2796. bdms=fake_bdms)
  2797. self.assertTrue(dest_check_data.is_volume_backed)
  2798. def _test_check_can_live_migrate_destination(self, do_raise=False,
  2799. src_numa_lm=None):
  2800. db_instance = fake_instance.fake_db_instance(host='fake-host')
  2801. instance = objects.Instance._from_db_object(
  2802. self.context, objects.Instance(), db_instance)
  2803. instance.host = 'fake-host'
  2804. block_migration = 'block_migration'
  2805. disk_over_commit = 'disk_over_commit'
  2806. src_info = 'src_info'
  2807. dest_info = 'dest_info'
  2808. dest_check_data = objects.LibvirtLiveMigrateData(
  2809. dst_supports_numa_live_migration=True)
  2810. mig_data = objects.LibvirtLiveMigrateData()
  2811. if src_numa_lm is not None:
  2812. mig_data.src_supports_numa_live_migration = src_numa_lm
  2813. with test.nested(
  2814. mock.patch.object(self.compute, '_live_migration_claim'),
  2815. mock.patch.object(self.compute, '_get_compute_info'),
  2816. mock.patch.object(self.compute.driver,
  2817. 'check_can_live_migrate_destination'),
  2818. mock.patch.object(self.compute.compute_rpcapi,
  2819. 'check_can_live_migrate_source'),
  2820. mock.patch.object(self.compute.driver,
  2821. 'cleanup_live_migration_destination_check'),
  2822. mock.patch.object(db, 'instance_fault_create'),
  2823. mock.patch.object(compute_utils, 'EventReporter'),
  2824. mock.patch.object(migrate_data_obj.VIFMigrateData,
  2825. 'create_skeleton_migrate_vifs',
  2826. return_value=[]),
  2827. mock.patch.object(instance, 'get_network_info'),
  2828. mock.patch.object(self.compute, '_claim_pci_for_instance_vifs'),
  2829. mock.patch.object(self.compute,
  2830. '_update_migrate_vifs_profile_with_pci'),
  2831. mock.patch.object(self.compute, '_dest_can_numa_live_migrate'),
  2832. mock.patch('nova.compute.manager.LOG'),
  2833. ) as (mock_lm_claim, mock_get, mock_check_dest, mock_check_src,
  2834. mock_check_clean, mock_fault_create, mock_event,
  2835. mock_create_mig_vif, mock_nw_info, mock_claim_pci,
  2836. mock_update_mig_vif, mock_dest_can_numa, mock_log):
  2837. mock_get.side_effect = (src_info, dest_info)
  2838. mock_check_dest.return_value = dest_check_data
  2839. mock_dest_can_numa.return_value = dest_check_data
  2840. post_claim_md = objects.LibvirtLiveMigrateData(
  2841. dst_numa_info=objects.LibvirtLiveMigrateNUMAInfo())
  2842. mock_lm_claim.return_value = post_claim_md
  2843. if do_raise:
  2844. mock_check_src.side_effect = test.TestingException
  2845. mock_fault_create.return_value = \
  2846. test_instance_fault.fake_faults['fake-uuid'][0]
  2847. else:
  2848. mock_check_src.return_value = mig_data
  2849. migration = objects.Migration()
  2850. limits = objects.SchedulerLimits()
  2851. result = self.compute.check_can_live_migrate_destination(
  2852. self.context, instance=instance,
  2853. block_migration=block_migration,
  2854. disk_over_commit=disk_over_commit, migration=migration,
  2855. limits=limits)
  2856. if do_raise:
  2857. mock_fault_create.assert_called_once_with(self.context,
  2858. mock.ANY)
  2859. mock_check_src.assert_called_once_with(self.context, instance,
  2860. dest_check_data)
  2861. mock_dest_can_numa.assert_called_with(dest_check_data,
  2862. migration)
  2863. if not src_numa_lm:
  2864. mock_lm_claim.assert_not_called()
  2865. self.assertEqual(mig_data, result)
  2866. mock_log.info.assert_called()
  2867. self.assertThat(
  2868. mock_log.info.call_args[0][0],
  2869. testtools.matchers.MatchesRegex(
  2870. 'Destination was ready for NUMA live migration'))
  2871. else:
  2872. mock_lm_claim.assert_called_once_with(
  2873. self.context, instance, mig_data, migration, limits)
  2874. self.assertEqual(post_claim_md, result)
  2875. mock_check_clean.assert_called_once_with(self.context,
  2876. dest_check_data)
  2877. mock_get.assert_has_calls([mock.call(self.context, 'fake-host'),
  2878. mock.call(self.context, CONF.host)])
  2879. mock_check_dest.assert_called_once_with(self.context, instance,
  2880. src_info, dest_info, block_migration, disk_over_commit)
  2881. mock_event.assert_called_once_with(
  2882. self.context, 'compute_check_can_live_migrate_destination',
  2883. CONF.host, instance.uuid)
  2884. def test_check_can_live_migrate_destination_success(self):
  2885. self._test_check_can_live_migrate_destination()
  2886. def test_check_can_live_migrate_destination_fail(self):
  2887. self.assertRaises(
  2888. test.TestingException,
  2889. self._test_check_can_live_migrate_destination,
  2890. do_raise=True)
  2891. def test_check_can_live_migrate_destination_src_numa_lm_false(self):
  2892. self._test_check_can_live_migrate_destination(src_numa_lm=False)
  2893. def test_check_can_live_migrate_destination_src_numa_lm_true(self):
  2894. self._test_check_can_live_migrate_destination(src_numa_lm=True)
  2895. def test_dest_can_numa_live_migrate(self):
  2896. positive_dest_check_data = objects.LibvirtLiveMigrateData(
  2897. dst_supports_numa_live_migration=True)
  2898. negative_dest_check_data = objects.LibvirtLiveMigrateData()
  2899. self.assertNotIn(
  2900. 'dst_supports_numa_live_migration',
  2901. self.compute._dest_can_numa_live_migrate(
  2902. copy.deepcopy(positive_dest_check_data), None))
  2903. self.assertIn(
  2904. 'dst_supports_numa_live_migration',
  2905. self.compute._dest_can_numa_live_migrate(
  2906. copy.deepcopy(positive_dest_check_data), 'fake-migration'))
  2907. self.assertNotIn(
  2908. 'dst_supports_numa_live_migration',
  2909. self.compute._dest_can_numa_live_migrate(
  2910. copy.deepcopy(negative_dest_check_data), 'fake-migration'))
  2911. self.assertNotIn(
  2912. 'dst_supports_numa_live_migration',
  2913. self.compute._dest_can_numa_live_migrate(
  2914. copy.deepcopy(negative_dest_check_data), None))
  2915. def test_source_can_numa_live_migrate(self):
  2916. positive_dest_check_data = objects.LibvirtLiveMigrateData(
  2917. dst_supports_numa_live_migration=True)
  2918. negative_dest_check_data = objects.LibvirtLiveMigrateData()
  2919. positive_source_check_data = objects.LibvirtLiveMigrateData(
  2920. src_supports_numa_live_migration=True)
  2921. negative_source_check_data = objects.LibvirtLiveMigrateData()
  2922. with mock.patch.object(self.compute.compute_rpcapi,
  2923. 'supports_numa_live_migration',
  2924. return_value=True
  2925. ) as mock_supports_numa_lm:
  2926. self.assertIn(
  2927. 'src_supports_numa_live_migration',
  2928. self.compute._source_can_numa_live_migrate(
  2929. self.context, positive_dest_check_data,
  2930. copy.deepcopy(positive_source_check_data)))
  2931. mock_supports_numa_lm.assert_called_once_with(self.context)
  2932. self.assertNotIn(
  2933. 'src_supports_numa_live_migration',
  2934. self.compute._source_can_numa_live_migrate(
  2935. self.context, positive_dest_check_data,
  2936. copy.deepcopy(negative_source_check_data)))
  2937. self.assertNotIn(
  2938. 'src_supports_numa_live_migration',
  2939. self.compute._source_can_numa_live_migrate(
  2940. self.context, negative_dest_check_data,
  2941. copy.deepcopy(positive_source_check_data)))
  2942. self.assertNotIn(
  2943. 'src_supports_numa_live_migration',
  2944. self.compute._source_can_numa_live_migrate(
  2945. self.context, negative_dest_check_data,
  2946. copy.deepcopy(negative_source_check_data)))
  2947. with mock.patch.object(self.compute.compute_rpcapi,
  2948. 'supports_numa_live_migration',
  2949. return_value=False
  2950. ) as mock_supports_numa_lm:
  2951. self.assertNotIn(
  2952. 'src_supports_numa_live_migration',
  2953. self.compute._source_can_numa_live_migrate(
  2954. self.context, positive_dest_check_data,
  2955. copy.deepcopy(positive_source_check_data)))
  2956. mock_supports_numa_lm.assert_called_once_with(self.context)
  2957. self.assertNotIn(
  2958. 'src_supports_numa_live_migration',
  2959. self.compute._source_can_numa_live_migrate(
  2960. self.context, positive_dest_check_data,
  2961. copy.deepcopy(negative_source_check_data)))
  2962. self.assertNotIn(
  2963. 'src_supports_numa_live_migration',
  2964. self.compute._source_can_numa_live_migrate(
  2965. self.context, negative_dest_check_data,
  2966. copy.deepcopy(positive_source_check_data)))
  2967. self.assertNotIn(
  2968. 'src_supports_numa_live_migration',
  2969. self.compute._source_can_numa_live_migrate(
  2970. self.context, negative_dest_check_data,
  2971. copy.deepcopy(negative_source_check_data)))
  2972. @mock.patch('nova.compute.manager.InstanceEvents._lock_name')
  2973. def test_prepare_for_instance_event(self, lock_name_mock):
  2974. inst_obj = objects.Instance(uuid=uuids.instance)
  2975. result = self.compute.instance_events.prepare_for_instance_event(
  2976. inst_obj, 'test-event', None)
  2977. self.assertIn(uuids.instance, self.compute.instance_events._events)
  2978. self.assertIn(('test-event', None),
  2979. self.compute.instance_events._events[uuids.instance])
  2980. self.assertEqual(
  2981. result,
  2982. self.compute.instance_events._events[uuids.instance]
  2983. [('test-event', None)])
  2984. self.assertTrue(hasattr(result, 'send'))
  2985. lock_name_mock.assert_called_once_with(inst_obj)
  2986. @mock.patch('nova.compute.manager.InstanceEvents._lock_name')
  2987. def test_pop_instance_event(self, lock_name_mock):
  2988. event = eventlet_event.Event()
  2989. self.compute.instance_events._events = {
  2990. uuids.instance: {
  2991. ('network-vif-plugged', None): event,
  2992. }
  2993. }
  2994. inst_obj = objects.Instance(uuid=uuids.instance)
  2995. event_obj = objects.InstanceExternalEvent(name='network-vif-plugged',
  2996. tag=None)
  2997. result = self.compute.instance_events.pop_instance_event(inst_obj,
  2998. event_obj)
  2999. self.assertEqual(result, event)
  3000. lock_name_mock.assert_called_once_with(inst_obj)
  3001. @mock.patch('nova.compute.manager.InstanceEvents._lock_name')
  3002. def test_clear_events_for_instance(self, lock_name_mock):
  3003. event = eventlet_event.Event()
  3004. self.compute.instance_events._events = {
  3005. uuids.instance: {
  3006. ('test-event', None): event,
  3007. }
  3008. }
  3009. inst_obj = objects.Instance(uuid=uuids.instance)
  3010. result = self.compute.instance_events.clear_events_for_instance(
  3011. inst_obj)
  3012. self.assertEqual(result, {'test-event-None': event})
  3013. lock_name_mock.assert_called_once_with(inst_obj)
  3014. def test_instance_events_lock_name(self):
  3015. inst_obj = objects.Instance(uuid=uuids.instance)
  3016. result = self.compute.instance_events._lock_name(inst_obj)
  3017. self.assertEqual(result, "%s-events" % uuids.instance)
  3018. def test_prepare_for_instance_event_again(self):
  3019. inst_obj = objects.Instance(uuid=uuids.instance)
  3020. self.compute.instance_events.prepare_for_instance_event(
  3021. inst_obj, 'test-event', None)
  3022. # A second attempt will avoid creating a new list; make sure we
  3023. # get the current list
  3024. result = self.compute.instance_events.prepare_for_instance_event(
  3025. inst_obj, 'test-event', None)
  3026. self.assertIn(uuids.instance, self.compute.instance_events._events)
  3027. self.assertIn(('test-event', None),
  3028. self.compute.instance_events._events[uuids.instance])
  3029. self.assertEqual(
  3030. result,
  3031. self.compute.instance_events._events[uuids.instance]
  3032. [('test-event', None)])
  3033. self.assertTrue(hasattr(result, 'send'))
  3034. def test_process_instance_event(self):
  3035. event = eventlet_event.Event()
  3036. self.compute.instance_events._events = {
  3037. uuids.instance: {
  3038. ('network-vif-plugged', None): event,
  3039. }
  3040. }
  3041. inst_obj = objects.Instance(uuid=uuids.instance)
  3042. event_obj = objects.InstanceExternalEvent(name='network-vif-plugged',
  3043. tag=None)
  3044. self.compute._process_instance_event(inst_obj, event_obj)
  3045. self.assertTrue(event.ready())
  3046. self.assertEqual(event_obj, event.wait())
  3047. self.assertEqual({}, self.compute.instance_events._events)
  3048. @ddt.data(task_states.DELETING,
  3049. task_states.MIGRATING)
  3050. @mock.patch('nova.compute.manager.LOG')
  3051. def test_process_instance_event_expected_task(self, task_state, mock_log):
  3052. """Tests that we don't log a warning when we get a
  3053. network-vif-unplugged event for an instance that's undergoing a task
  3054. state transition that will generate the expected event.
  3055. """
  3056. inst_obj = objects.Instance(uuid=uuids.instance,
  3057. vm_state=vm_states.ACTIVE,
  3058. task_state=task_state)
  3059. event_obj = objects.InstanceExternalEvent(name='network-vif-unplugged',
  3060. tag=uuids.port_id)
  3061. with mock.patch.object(self.compute.instance_events,
  3062. 'pop_instance_event', return_value=None):
  3063. self.compute._process_instance_event(inst_obj, event_obj)
  3064. # assert we logged at debug level
  3065. mock_log.debug.assert_called()
  3066. self.assertThat(mock_log.debug.call_args[0][0],
  3067. testtools.matchers.MatchesRegex(
  3068. 'Received event .* for instance with task_state '
  3069. '%s'))
  3070. @mock.patch('nova.compute.manager.LOG')
  3071. def test_process_instance_event_unexpected_warning(self, mock_log):
  3072. """Tests that we log a warning when we get an unexpected event."""
  3073. inst_obj = objects.Instance(uuid=uuids.instance,
  3074. vm_state=vm_states.ACTIVE,
  3075. task_state=None)
  3076. event_obj = objects.InstanceExternalEvent(name='network-vif-unplugged',
  3077. tag=uuids.port_id)
  3078. with mock.patch.object(self.compute.instance_events,
  3079. 'pop_instance_event', return_value=None):
  3080. self.compute._process_instance_event(inst_obj, event_obj)
  3081. # assert we logged at warning level
  3082. mock_log.warning.assert_called()
  3083. self.assertThat(mock_log.warning.call_args[0][0],
  3084. testtools.matchers.MatchesRegex(
  3085. 'Received unexpected event .* for '
  3086. 'instance with vm_state .* and '
  3087. 'task_state .*.'))
  3088. def test_process_instance_vif_deleted_event(self):
  3089. vif1 = fake_network_cache_model.new_vif()
  3090. vif1['id'] = '1'
  3091. vif2 = fake_network_cache_model.new_vif()
  3092. vif2['id'] = '2'
  3093. nw_info = network_model.NetworkInfo([vif1, vif2])
  3094. info_cache = objects.InstanceInfoCache(network_info=nw_info,
  3095. instance_uuid=uuids.instance)
  3096. inst_obj = objects.Instance(id=3, uuid=uuids.instance,
  3097. info_cache=info_cache)
  3098. @mock.patch.object(manager.base_net_api,
  3099. 'update_instance_cache_with_nw_info')
  3100. @mock.patch.object(self.compute.driver, 'detach_interface')
  3101. def do_test(detach_interface, update_instance_cache_with_nw_info):
  3102. self.compute._process_instance_vif_deleted_event(self.context,
  3103. inst_obj,
  3104. vif2['id'])
  3105. update_instance_cache_with_nw_info.assert_called_once_with(
  3106. self.compute.network_api,
  3107. self.context,
  3108. inst_obj,
  3109. nw_info=[vif1])
  3110. detach_interface.assert_called_once_with(self.context,
  3111. inst_obj, vif2)
  3112. do_test()
  3113. def test_process_instance_vif_deleted_event_not_implemented_error(self):
  3114. """Tests the case where driver.detach_interface raises
  3115. NotImplementedError.
  3116. """
  3117. vif = fake_network_cache_model.new_vif()
  3118. nw_info = network_model.NetworkInfo([vif])
  3119. info_cache = objects.InstanceInfoCache(network_info=nw_info,
  3120. instance_uuid=uuids.instance)
  3121. inst_obj = objects.Instance(id=3, uuid=uuids.instance,
  3122. info_cache=info_cache)
  3123. @mock.patch.object(manager.base_net_api,
  3124. 'update_instance_cache_with_nw_info')
  3125. @mock.patch.object(self.compute.driver, 'detach_interface',
  3126. side_effect=NotImplementedError)
  3127. def do_test(detach_interface, update_instance_cache_with_nw_info):
  3128. self.compute._process_instance_vif_deleted_event(
  3129. self.context, inst_obj, vif['id'])
  3130. update_instance_cache_with_nw_info.assert_called_once_with(
  3131. self.compute.network_api, self.context, inst_obj, nw_info=[])
  3132. detach_interface.assert_called_once_with(
  3133. self.context, inst_obj, vif)
  3134. do_test()
  3135. @mock.patch('nova.compute.manager.LOG.info') # This is needed for py35.
  3136. @mock.patch('nova.compute.manager.LOG.log')
  3137. def test_process_instance_vif_deleted_event_instance_not_found(
  3138. self, mock_log, mock_log_info):
  3139. """Tests the case where driver.detach_interface raises
  3140. InstanceNotFound.
  3141. """
  3142. vif = fake_network_cache_model.new_vif()
  3143. nw_info = network_model.NetworkInfo([vif])
  3144. info_cache = objects.InstanceInfoCache(network_info=nw_info,
  3145. instance_uuid=uuids.instance)
  3146. inst_obj = objects.Instance(id=3, uuid=uuids.instance,
  3147. info_cache=info_cache)
  3148. @mock.patch.object(manager.base_net_api,
  3149. 'update_instance_cache_with_nw_info')
  3150. @mock.patch.object(self.compute.driver, 'detach_interface',
  3151. side_effect=exception.InstanceNotFound(
  3152. instance_id=uuids.instance))
  3153. def do_test(detach_interface, update_instance_cache_with_nw_info):
  3154. self.compute._process_instance_vif_deleted_event(
  3155. self.context, inst_obj, vif['id'])
  3156. update_instance_cache_with_nw_info.assert_called_once_with(
  3157. self.compute.network_api, self.context, inst_obj, nw_info=[])
  3158. detach_interface.assert_called_once_with(
  3159. self.context, inst_obj, vif)
  3160. # LOG.log should have been called with a DEBUG level message.
  3161. self.assertEqual(1, mock_log.call_count, mock_log.mock_calls)
  3162. self.assertEqual(logging.DEBUG, mock_log.call_args[0][0])
  3163. do_test()
  3164. def test_power_update(self):
  3165. instance = objects.Instance(self.context)
  3166. instance.uuid = uuids.instance
  3167. instance.id = 1
  3168. instance.vm_state = vm_states.STOPPED
  3169. instance.task_state = None
  3170. instance.power_state = power_state.SHUTDOWN
  3171. instance.host = self.compute.host
  3172. with test.nested(
  3173. mock.patch.object(nova.compute.utils,
  3174. 'notify_about_instance_action'),
  3175. mock.patch.object(self.compute, '_notify_about_instance_usage'),
  3176. mock.patch.object(self.compute.driver, 'power_update_event'),
  3177. mock.patch.object(objects.Instance, 'save'),
  3178. mock.patch.object(manager, 'LOG')
  3179. ) as (
  3180. mock_instance_notify, mock_instance_usage, mock_event, mock_save,
  3181. mock_log
  3182. ):
  3183. self.compute.power_update(self.context, instance, "POWER_ON")
  3184. calls = [mock.call(self.context, instance, self.compute.host,
  3185. action=fields.NotificationAction.POWER_ON,
  3186. phase=fields.NotificationPhase.START),
  3187. mock.call(self.context, instance, self.compute.host,
  3188. action=fields.NotificationAction.POWER_ON,
  3189. phase=fields.NotificationPhase.END)]
  3190. mock_instance_notify.assert_has_calls(calls)
  3191. calls = [mock.call(self.context, instance, "power_on.start"),
  3192. mock.call(self.context, instance, "power_on.end")]
  3193. mock_instance_usage.assert_has_calls(calls)
  3194. mock_event.assert_called_once_with(instance, 'POWER_ON')
  3195. mock_save.assert_called_once_with(
  3196. expected_task_state=[None])
  3197. self.assertEqual(2, mock_log.debug.call_count)
  3198. self.assertIn('Trying to', mock_log.debug.call_args[0][0])
  3199. def test_power_update_not_implemented(self):
  3200. instance = objects.Instance(self.context)
  3201. instance.uuid = uuids.instance
  3202. instance.id = 1
  3203. instance.vm_state = vm_states.STOPPED
  3204. instance.task_state = None
  3205. instance.power_state = power_state.SHUTDOWN
  3206. instance.host = self.compute.host
  3207. with test.nested(
  3208. mock.patch.object(nova.compute.utils,
  3209. 'notify_about_instance_action'),
  3210. mock.patch.object(self.compute, '_notify_about_instance_usage'),
  3211. mock.patch.object(self.compute.driver, 'power_update_event',
  3212. side_effect=NotImplementedError()),
  3213. mock.patch.object(instance, 'save'),
  3214. mock.patch.object(nova.compute.utils,
  3215. 'add_instance_fault_from_exc'),
  3216. ) as (
  3217. mock_instance_notify, mock_instance_usage, mock_event,
  3218. mock_save, mock_fault
  3219. ):
  3220. self.assertRaises(NotImplementedError,
  3221. self.compute.power_update, self.context, instance, "POWER_ON")
  3222. self.assertIsNone(instance.task_state)
  3223. self.assertEqual(2, mock_save.call_count)
  3224. # second save is done by revert_task_state
  3225. mock_save.assert_has_calls(
  3226. [mock.call(expected_task_state=[None]), mock.call()])
  3227. mock_instance_notify.assert_called_once_with(
  3228. self.context, instance, self.compute.host,
  3229. action=fields.NotificationAction.POWER_ON,
  3230. phase=fields.NotificationPhase.START)
  3231. mock_instance_usage.assert_called_once_with(
  3232. self.context, instance, "power_on.start")
  3233. mock_fault.assert_called_once_with(
  3234. self.context, instance, mock.ANY, mock.ANY)
  3235. def test_external_instance_event_power_update_invalid_state(self):
  3236. instance = objects.Instance(self.context)
  3237. instance.uuid = uuids.instance1
  3238. instance.id = 1
  3239. instance.vm_state = vm_states.ACTIVE
  3240. instance.task_state = task_states.POWERING_OFF
  3241. instance.power_state = power_state.RUNNING
  3242. instance.host = 'host1'
  3243. instance.migration_context = None
  3244. with test.nested(
  3245. mock.patch.object(nova.compute.utils,
  3246. 'notify_about_instance_action'),
  3247. mock.patch.object(self.compute, '_notify_about_instance_usage'),
  3248. mock.patch.object(self.compute.driver, 'power_update_event'),
  3249. mock.patch.object(objects.Instance, 'save'),
  3250. mock.patch.object(manager, 'LOG')
  3251. ) as (
  3252. mock_instance_notify, mock_instance_usage, mock_event, mock_save,
  3253. mock_log
  3254. ):
  3255. self.compute.power_update(self.context, instance, "POWER_ON")
  3256. mock_instance_notify.assert_not_called()
  3257. mock_instance_usage.assert_not_called()
  3258. mock_event.assert_not_called()
  3259. mock_save.assert_not_called()
  3260. self.assertEqual(1, mock_log.info.call_count)
  3261. self.assertIn('is a no-op', mock_log.info.call_args[0][0])
  3262. def test_external_instance_event_power_update_unexpected_task_state(self):
  3263. instance = objects.Instance(self.context)
  3264. instance.uuid = uuids.instance1
  3265. instance.id = 1
  3266. instance.vm_state = vm_states.ACTIVE
  3267. instance.task_state = None
  3268. instance.power_state = power_state.RUNNING
  3269. instance.host = 'host1'
  3270. instance.migration_context = None
  3271. with test.nested(
  3272. mock.patch.object(nova.compute.utils,
  3273. 'notify_about_instance_action'),
  3274. mock.patch.object(self.compute, '_notify_about_instance_usage'),
  3275. mock.patch.object(self.compute.driver, 'power_update_event'),
  3276. mock.patch.object(objects.Instance, 'save',
  3277. side_effect=exception.UnexpectedTaskStateError("blah")),
  3278. mock.patch.object(manager, 'LOG')
  3279. ) as (
  3280. mock_instance_notify, mock_instance_usage, mock_event, mock_save,
  3281. mock_log
  3282. ):
  3283. self.compute.power_update(self.context, instance, "POWER_OFF")
  3284. mock_instance_notify.assert_not_called()
  3285. mock_instance_usage.assert_not_called()
  3286. mock_event.assert_not_called()
  3287. self.assertEqual(1, mock_log.info.call_count)
  3288. self.assertIn('possibly preempted', mock_log.info.call_args[0][0])
  3289. def test_extend_volume(self):
  3290. inst_obj = objects.Instance(id=3, uuid=uuids.instance)
  3291. connection_info = {'foo': 'bar'}
  3292. new_size = 20
  3293. bdm = objects.BlockDeviceMapping(
  3294. source_type='volume',
  3295. destination_type='volume',
  3296. volume_id=uuids.volume_id,
  3297. volume_size=10,
  3298. instance_uuid=uuids.instance,
  3299. device_name='/dev/vda',
  3300. connection_info=jsonutils.dumps(connection_info))
  3301. @mock.patch.object(self.compute, 'volume_api')
  3302. @mock.patch.object(self.compute.driver, 'extend_volume')
  3303. @mock.patch.object(objects.BlockDeviceMapping,
  3304. 'get_by_volume_and_instance')
  3305. @mock.patch.object(objects.BlockDeviceMapping, 'save')
  3306. def do_test(bdm_save, bdm_get_by_vol_and_inst, extend_volume,
  3307. volume_api):
  3308. bdm_get_by_vol_and_inst.return_value = bdm
  3309. volume_api.get.return_value = {'size': new_size}
  3310. self.compute.extend_volume(
  3311. self.context, inst_obj, uuids.volume_id)
  3312. bdm_save.assert_called_once_with()
  3313. extend_volume.assert_called_once_with(
  3314. connection_info, inst_obj, new_size * pow(1024, 3))
  3315. do_test()
  3316. def test_extend_volume_not_implemented_error(self):
  3317. """Tests the case where driver.extend_volume raises
  3318. NotImplementedError.
  3319. """
  3320. inst_obj = objects.Instance(id=3, uuid=uuids.instance)
  3321. connection_info = {'foo': 'bar'}
  3322. bdm = objects.BlockDeviceMapping(
  3323. source_type='volume',
  3324. destination_type='volume',
  3325. volume_id=uuids.volume_id,
  3326. volume_size=10,
  3327. instance_uuid=uuids.instance,
  3328. device_name='/dev/vda',
  3329. connection_info=jsonutils.dumps(connection_info))
  3330. @mock.patch.object(self.compute, 'volume_api')
  3331. @mock.patch.object(objects.BlockDeviceMapping,
  3332. 'get_by_volume_and_instance')
  3333. @mock.patch.object(objects.BlockDeviceMapping, 'save')
  3334. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  3335. def do_test(add_fault_mock, bdm_save, bdm_get_by_vol_and_inst,
  3336. volume_api):
  3337. bdm_get_by_vol_and_inst.return_value = bdm
  3338. volume_api.get.return_value = {'size': 20}
  3339. self.assertRaises(
  3340. exception.ExtendVolumeNotSupported,
  3341. self.compute.extend_volume,
  3342. self.context, inst_obj, uuids.volume_id)
  3343. add_fault_mock.assert_called_once_with(
  3344. self.context, inst_obj, mock.ANY, mock.ANY)
  3345. with mock.patch.dict(self.compute.driver.capabilities,
  3346. supports_extend_volume=False):
  3347. do_test()
  3348. def test_extend_volume_volume_not_found(self):
  3349. """Tests the case where driver.extend_volume tries to extend
  3350. a volume not attached to the specified instance.
  3351. """
  3352. inst_obj = objects.Instance(id=3, uuid=uuids.instance)
  3353. @mock.patch.object(objects.BlockDeviceMapping,
  3354. 'get_by_volume_and_instance',
  3355. side_effect=exception.NotFound())
  3356. def do_test(bdm_get_by_vol_and_inst):
  3357. self.compute.extend_volume(
  3358. self.context, inst_obj, uuids.volume_id)
  3359. do_test()
  3360. def test_external_instance_event(self):
  3361. instances = [
  3362. objects.Instance(id=1, uuid=uuids.instance_1),
  3363. objects.Instance(id=2, uuid=uuids.instance_2),
  3364. objects.Instance(id=3, uuid=uuids.instance_3),
  3365. objects.Instance(id=4, uuid=uuids.instance_4),
  3366. objects.Instance(id=4, uuid=uuids.instance_5)]
  3367. events = [
  3368. objects.InstanceExternalEvent(name='network-changed',
  3369. tag='tag1',
  3370. instance_uuid=uuids.instance_1),
  3371. objects.InstanceExternalEvent(name='network-vif-plugged',
  3372. instance_uuid=uuids.instance_2,
  3373. tag='tag2'),
  3374. objects.InstanceExternalEvent(name='network-vif-deleted',
  3375. instance_uuid=uuids.instance_3,
  3376. tag='tag3'),
  3377. objects.InstanceExternalEvent(name='volume-extended',
  3378. instance_uuid=uuids.instance_4,
  3379. tag='tag4'),
  3380. objects.InstanceExternalEvent(name='power-update',
  3381. instance_uuid=uuids.instance_5,
  3382. tag='POWER_ON')]
  3383. @mock.patch.object(self.compute, 'power_update')
  3384. @mock.patch.object(self.compute,
  3385. 'extend_volume')
  3386. @mock.patch.object(self.compute, '_process_instance_vif_deleted_event')
  3387. @mock.patch.object(self.compute.network_api, 'get_instance_nw_info')
  3388. @mock.patch.object(self.compute, '_process_instance_event')
  3389. def do_test(_process_instance_event, get_instance_nw_info,
  3390. _process_instance_vif_deleted_event, extend_volume,
  3391. power_update):
  3392. self.compute.external_instance_event(self.context,
  3393. instances, events)
  3394. get_instance_nw_info.assert_called_once_with(self.context,
  3395. instances[0],
  3396. refresh_vif_id='tag1')
  3397. _process_instance_event.assert_called_once_with(instances[1],
  3398. events[1])
  3399. _process_instance_vif_deleted_event.assert_called_once_with(
  3400. self.context, instances[2], events[2].tag)
  3401. extend_volume.assert_called_once_with(
  3402. self.context, instances[3], events[3].tag)
  3403. power_update.assert_called_once_with(
  3404. self.context, instances[4], events[4].tag)
  3405. do_test()
  3406. def test_external_instance_event_with_exception(self):
  3407. vif1 = fake_network_cache_model.new_vif()
  3408. vif1['id'] = '1'
  3409. vif2 = fake_network_cache_model.new_vif()
  3410. vif2['id'] = '2'
  3411. nw_info = network_model.NetworkInfo([vif1, vif2])
  3412. info_cache = objects.InstanceInfoCache(network_info=nw_info,
  3413. instance_uuid=uuids.instance_2)
  3414. instances = [
  3415. objects.Instance(id=1, uuid=uuids.instance_1),
  3416. objects.Instance(id=2, uuid=uuids.instance_2,
  3417. info_cache=info_cache),
  3418. objects.Instance(id=3, uuid=uuids.instance_3),
  3419. # instance_4 doesn't have info_cache set so it will be lazy-loaded
  3420. # and blow up with an InstanceNotFound error.
  3421. objects.Instance(id=4, uuid=uuids.instance_4),
  3422. objects.Instance(id=5, uuid=uuids.instance_5),
  3423. ]
  3424. events = [
  3425. objects.InstanceExternalEvent(name='network-changed',
  3426. tag='tag1',
  3427. instance_uuid=uuids.instance_1),
  3428. objects.InstanceExternalEvent(name='network-vif-deleted',
  3429. instance_uuid=uuids.instance_2,
  3430. tag='2'),
  3431. objects.InstanceExternalEvent(name='network-vif-plugged',
  3432. instance_uuid=uuids.instance_3,
  3433. tag='tag3'),
  3434. objects.InstanceExternalEvent(name='network-vif-deleted',
  3435. instance_uuid=uuids.instance_4,
  3436. tag='tag4'),
  3437. objects.InstanceExternalEvent(name='volume-extended',
  3438. instance_uuid=uuids.instance_5,
  3439. tag='tag5'),
  3440. ]
  3441. # Make sure all the four events are handled despite the exceptions in
  3442. # processing events 1, 2, 4 and 5.
  3443. @mock.patch.object(objects.BlockDeviceMapping,
  3444. 'get_by_volume_and_instance',
  3445. side_effect=exception.InstanceNotFound(
  3446. instance_id=uuids.instance_5))
  3447. @mock.patch.object(instances[3], 'obj_load_attr',
  3448. side_effect=exception.InstanceNotFound(
  3449. instance_id=uuids.instance_4))
  3450. @mock.patch.object(manager.base_net_api,
  3451. 'update_instance_cache_with_nw_info')
  3452. @mock.patch.object(self.compute.driver, 'detach_interface',
  3453. side_effect=exception.NovaException)
  3454. @mock.patch.object(self.compute.network_api, 'get_instance_nw_info',
  3455. side_effect=exception.InstanceInfoCacheNotFound(
  3456. instance_uuid=uuids.instance_1))
  3457. @mock.patch.object(self.compute, '_process_instance_event')
  3458. def do_test(_process_instance_event, get_instance_nw_info,
  3459. detach_interface, update_instance_cache_with_nw_info,
  3460. obj_load_attr, bdm_get_by_vol_and_inst):
  3461. self.compute.external_instance_event(self.context,
  3462. instances, events)
  3463. get_instance_nw_info.assert_called_once_with(self.context,
  3464. instances[0],
  3465. refresh_vif_id='tag1')
  3466. update_instance_cache_with_nw_info.assert_called_once_with(
  3467. self.compute.network_api,
  3468. self.context,
  3469. instances[1],
  3470. nw_info=[vif1])
  3471. detach_interface.assert_called_once_with(self.context,
  3472. instances[1], vif2)
  3473. _process_instance_event.assert_called_once_with(instances[2],
  3474. events[2])
  3475. obj_load_attr.assert_called_once_with('info_cache')
  3476. bdm_get_by_vol_and_inst.assert_called_once_with(
  3477. self.context, 'tag5', instances[4].uuid)
  3478. do_test()
  3479. def test_cancel_all_events(self):
  3480. inst = objects.Instance(uuid=uuids.instance)
  3481. fake_eventlet_event = mock.MagicMock()
  3482. self.compute.instance_events._events = {
  3483. inst.uuid: {
  3484. ('network-vif-plugged', uuids.portid): fake_eventlet_event,
  3485. }
  3486. }
  3487. self.compute.instance_events.cancel_all_events()
  3488. # call it again to make sure we handle that gracefully
  3489. self.compute.instance_events.cancel_all_events()
  3490. self.assertTrue(fake_eventlet_event.send.called)
  3491. event = fake_eventlet_event.send.call_args_list[0][0][0]
  3492. self.assertEqual('network-vif-plugged', event.name)
  3493. self.assertEqual(uuids.portid, event.tag)
  3494. self.assertEqual('failed', event.status)
  3495. def test_cleanup_cancels_all_events(self):
  3496. with mock.patch.object(self.compute, 'instance_events') as mock_ev:
  3497. self.compute.cleanup_host()
  3498. mock_ev.cancel_all_events.assert_called_once_with()
  3499. def test_cleanup_blocks_new_events(self):
  3500. instance = objects.Instance(uuid=uuids.instance)
  3501. self.compute.instance_events.cancel_all_events()
  3502. callback = mock.MagicMock()
  3503. body = mock.MagicMock()
  3504. with self.compute.virtapi.wait_for_instance_event(
  3505. instance, [('network-vif-plugged', 'bar')],
  3506. error_callback=callback):
  3507. body()
  3508. self.assertTrue(body.called)
  3509. callback.assert_called_once_with('network-vif-plugged-bar', instance)
  3510. def test_pop_events_fails_gracefully(self):
  3511. inst = objects.Instance(uuid=uuids.instance)
  3512. event = mock.MagicMock()
  3513. self.compute.instance_events._events = None
  3514. self.assertIsNone(
  3515. self.compute.instance_events.pop_instance_event(inst, event))
  3516. def test_clear_events_fails_gracefully(self):
  3517. inst = objects.Instance(uuid=uuids.instance)
  3518. self.compute.instance_events._events = None
  3519. self.assertEqual(
  3520. self.compute.instance_events.clear_events_for_instance(inst), {})
  3521. def test_retry_reboot_pending_soft(self):
  3522. instance = objects.Instance(self.context)
  3523. instance.uuid = uuids.instance
  3524. instance.task_state = task_states.REBOOT_PENDING
  3525. instance.vm_state = vm_states.ACTIVE
  3526. allow_reboot, reboot_type = self.compute._retry_reboot(
  3527. context, instance, power_state.RUNNING)
  3528. self.assertTrue(allow_reboot)
  3529. self.assertEqual(reboot_type, 'SOFT')
  3530. def test_retry_reboot_pending_hard(self):
  3531. instance = objects.Instance(self.context)
  3532. instance.uuid = uuids.instance
  3533. instance.task_state = task_states.REBOOT_PENDING_HARD
  3534. instance.vm_state = vm_states.ACTIVE
  3535. allow_reboot, reboot_type = self.compute._retry_reboot(
  3536. context, instance, power_state.RUNNING)
  3537. self.assertTrue(allow_reboot)
  3538. self.assertEqual(reboot_type, 'HARD')
  3539. def test_retry_reboot_starting_soft_off(self):
  3540. instance = objects.Instance(self.context)
  3541. instance.uuid = uuids.instance
  3542. instance.task_state = task_states.REBOOT_STARTED
  3543. allow_reboot, reboot_type = self.compute._retry_reboot(
  3544. context, instance, power_state.NOSTATE)
  3545. self.assertTrue(allow_reboot)
  3546. self.assertEqual(reboot_type, 'HARD')
  3547. def test_retry_reboot_starting_hard_off(self):
  3548. instance = objects.Instance(self.context)
  3549. instance.uuid = uuids.instance
  3550. instance.task_state = task_states.REBOOT_STARTED_HARD
  3551. allow_reboot, reboot_type = self.compute._retry_reboot(
  3552. context, instance, power_state.NOSTATE)
  3553. self.assertTrue(allow_reboot)
  3554. self.assertEqual(reboot_type, 'HARD')
  3555. def test_retry_reboot_starting_hard_on(self):
  3556. instance = objects.Instance(self.context)
  3557. instance.uuid = uuids.instance
  3558. instance.task_state = task_states.REBOOT_STARTED_HARD
  3559. allow_reboot, reboot_type = self.compute._retry_reboot(
  3560. context, instance, power_state.RUNNING)
  3561. self.assertFalse(allow_reboot)
  3562. self.assertEqual(reboot_type, 'HARD')
  3563. def test_retry_reboot_no_reboot(self):
  3564. instance = objects.Instance(self.context)
  3565. instance.uuid = uuids.instance
  3566. instance.task_state = 'bar'
  3567. allow_reboot, reboot_type = self.compute._retry_reboot(
  3568. context, instance, power_state.RUNNING)
  3569. self.assertFalse(allow_reboot)
  3570. self.assertEqual(reboot_type, 'HARD')
  3571. @mock.patch('nova.objects.BlockDeviceMapping.get_by_volume_and_instance')
  3572. def test_remove_volume_connection(self, bdm_get):
  3573. inst = mock.Mock()
  3574. inst.uuid = uuids.instance_uuid
  3575. fake_bdm = fake_block_device.FakeDbBlockDeviceDict(
  3576. {'source_type': 'volume', 'destination_type': 'volume',
  3577. 'volume_id': uuids.volume_id, 'device_name': '/dev/vdb',
  3578. 'connection_info': '{"test": "test"}'})
  3579. bdm = objects.BlockDeviceMapping(context=self.context, **fake_bdm)
  3580. bdm_get.return_value = bdm
  3581. with test.nested(
  3582. mock.patch.object(self.compute, 'volume_api'),
  3583. mock.patch.object(self.compute, 'driver'),
  3584. mock.patch.object(driver_bdm_volume, 'driver_detach'),
  3585. ) as (mock_volume_api, mock_virt_driver, mock_driver_detach):
  3586. connector = mock.Mock()
  3587. def fake_driver_detach(context, instance, volume_api, virt_driver):
  3588. # This is just here to validate the function signature.
  3589. pass
  3590. # There should be an easier way to do this with autospec...
  3591. mock_driver_detach.side_effect = fake_driver_detach
  3592. mock_virt_driver.get_volume_connector.return_value = connector
  3593. self.compute.remove_volume_connection(self.context,
  3594. uuids.volume_id, inst)
  3595. bdm_get.assert_called_once_with(self.context, uuids.volume_id,
  3596. uuids.instance_uuid)
  3597. mock_driver_detach.assert_called_once_with(self.context, inst,
  3598. mock_volume_api,
  3599. mock_virt_driver)
  3600. mock_volume_api.terminate_connection.assert_called_once_with(
  3601. self.context, uuids.volume_id, connector)
  3602. def test_remove_volume_connection_cinder_v3_api(self):
  3603. instance = fake_instance.fake_instance_obj(self.context,
  3604. uuid=uuids.instance)
  3605. volume_id = uuids.volume
  3606. vol_bdm = fake_block_device.fake_bdm_object(
  3607. self.context,
  3608. {'source_type': 'volume', 'destination_type': 'volume',
  3609. 'volume_id': volume_id, 'device_name': '/dev/vdb',
  3610. 'instance_uuid': instance.uuid,
  3611. 'connection_info': '{"test": "test"}'})
  3612. vol_bdm.attachment_id = uuids.attachment
  3613. @mock.patch.object(self.compute.volume_api, 'terminate_connection')
  3614. @mock.patch.object(self.compute, 'driver')
  3615. @mock.patch.object(driver_bdm_volume, 'driver_detach')
  3616. @mock.patch.object(objects.BlockDeviceMapping,
  3617. 'get_by_volume_and_instance')
  3618. def _test(mock_get_bdms, mock_detach, mock_driver, mock_terminate):
  3619. mock_get_bdms.return_value = vol_bdm
  3620. self.compute.remove_volume_connection(self.context,
  3621. volume_id, instance)
  3622. mock_detach.assert_called_once_with(self.context, instance,
  3623. self.compute.volume_api,
  3624. mock_driver)
  3625. mock_terminate.assert_not_called()
  3626. _test()
  3627. @mock.patch('nova.volume.cinder.API.attachment_delete')
  3628. @mock.patch('nova.virt.block_device.DriverVolumeBlockDevice.driver_detach')
  3629. def test_remove_volume_connection_v3_delete_attachment_true(
  3630. self, driver_detach, attachment_delete):
  3631. """Tests _remove_volume_connection with a cinder v3 style volume
  3632. attachment and delete_attachment=True.
  3633. """
  3634. instance = objects.Instance(uuid=uuids.instance)
  3635. volume_id = uuids.volume
  3636. vol_bdm = fake_block_device.fake_bdm_object(
  3637. self.context,
  3638. {'source_type': 'volume', 'destination_type': 'volume',
  3639. 'volume_id': volume_id, 'device_name': '/dev/vdb',
  3640. 'instance_uuid': instance.uuid,
  3641. 'connection_info': '{"test": "test"}'})
  3642. vol_bdm.attachment_id = uuids.attachment_id
  3643. self.compute._remove_volume_connection(
  3644. self.context, vol_bdm, instance, delete_attachment=True)
  3645. driver_detach.assert_called_once_with(
  3646. self.context, instance, self.compute.volume_api,
  3647. self.compute.driver)
  3648. attachment_delete.assert_called_once_with(
  3649. self.context, vol_bdm.attachment_id)
  3650. def test_delete_disk_metadata(self):
  3651. bdm = objects.BlockDeviceMapping(volume_id=uuids.volume_id, tag='foo')
  3652. instance = fake_instance.fake_instance_obj(self.context)
  3653. instance.device_metadata = objects.InstanceDeviceMetadata(
  3654. devices=[objects.DiskMetadata(serial=uuids.volume_id,
  3655. tag='foo')])
  3656. instance.save = mock.Mock()
  3657. self.compute._delete_disk_metadata(instance, bdm)
  3658. self.assertEqual(0, len(instance.device_metadata.devices))
  3659. instance.save.assert_called_once_with()
  3660. def test_delete_disk_metadata_no_serial(self):
  3661. bdm = objects.BlockDeviceMapping(tag='foo')
  3662. instance = fake_instance.fake_instance_obj(self.context)
  3663. instance.device_metadata = objects.InstanceDeviceMetadata(
  3664. devices=[objects.DiskMetadata(tag='foo')])
  3665. self.compute._delete_disk_metadata(instance, bdm)
  3666. # NOTE(artom) This looks weird because we haven't deleted anything, but
  3667. # it's normal behaviour for when DiskMetadata doesn't have serial set
  3668. # and we can't find it based on BlockDeviceMapping's volume_id.
  3669. self.assertEqual(1, len(instance.device_metadata.devices))
  3670. def test_detach_volume(self):
  3671. # TODO(lyarwood): Test DriverVolumeBlockDevice.detach in
  3672. # ../virt/test_block_device.py
  3673. self._test_detach_volume()
  3674. def test_detach_volume_not_destroy_bdm(self):
  3675. # TODO(lyarwood): Test DriverVolumeBlockDevice.detach in
  3676. # ../virt/test_block_device.py
  3677. self._test_detach_volume(destroy_bdm=False)
  3678. @mock.patch('nova.compute.manager.ComputeManager.'
  3679. '_notify_about_instance_usage')
  3680. @mock.patch.object(driver_bdm_volume, 'detach')
  3681. @mock.patch('nova.compute.utils.notify_about_volume_attach_detach')
  3682. @mock.patch('nova.compute.manager.ComputeManager._delete_disk_metadata')
  3683. def test_detach_untagged_volume_metadata_not_deleted(
  3684. self, mock_delete_metadata, _, __, ___):
  3685. inst_obj = mock.Mock()
  3686. fake_bdm = fake_block_device.FakeDbBlockDeviceDict(
  3687. {'source_type': 'volume', 'destination_type': 'volume',
  3688. 'volume_id': uuids.volume, 'device_name': '/dev/vdb',
  3689. 'connection_info': '{"test": "test"}'})
  3690. bdm = objects.BlockDeviceMapping(context=self.context, **fake_bdm)
  3691. self.compute._detach_volume(self.context, bdm, inst_obj,
  3692. destroy_bdm=False,
  3693. attachment_id=uuids.attachment)
  3694. self.assertFalse(mock_delete_metadata.called)
  3695. @mock.patch('nova.compute.manager.ComputeManager.'
  3696. '_notify_about_instance_usage')
  3697. @mock.patch.object(driver_bdm_volume, 'detach')
  3698. @mock.patch('nova.compute.utils.notify_about_volume_attach_detach')
  3699. @mock.patch('nova.compute.manager.ComputeManager._delete_disk_metadata')
  3700. def test_detach_tagged_volume(self, mock_delete_metadata, _, __, ___):
  3701. inst_obj = mock.Mock()
  3702. fake_bdm = fake_block_device.FakeDbBlockDeviceDict(
  3703. {'source_type': 'volume', 'destination_type': 'volume',
  3704. 'volume_id': uuids.volume, 'device_name': '/dev/vdb',
  3705. 'connection_info': '{"test": "test"}', 'tag': 'foo'})
  3706. bdm = objects.BlockDeviceMapping(context=self.context, **fake_bdm)
  3707. self.compute._detach_volume(self.context, bdm, inst_obj,
  3708. destroy_bdm=False,
  3709. attachment_id=uuids.attachment)
  3710. mock_delete_metadata.assert_called_once_with(inst_obj, bdm)
  3711. @mock.patch.object(driver_bdm_volume, 'detach')
  3712. @mock.patch('nova.compute.manager.ComputeManager.'
  3713. '_notify_about_instance_usage')
  3714. @mock.patch('nova.compute.utils.notify_about_volume_attach_detach')
  3715. def _test_detach_volume(self, mock_notify_attach_detach, notify_inst_usage,
  3716. detach, destroy_bdm=True):
  3717. # TODO(lyarwood): Test DriverVolumeBlockDevice.detach in
  3718. # ../virt/test_block_device.py
  3719. volume_id = uuids.volume
  3720. inst_obj = mock.Mock()
  3721. inst_obj.uuid = uuids.instance
  3722. inst_obj.host = CONF.host
  3723. attachment_id = uuids.attachment
  3724. fake_bdm = fake_block_device.FakeDbBlockDeviceDict(
  3725. {'source_type': 'volume', 'destination_type': 'volume',
  3726. 'volume_id': volume_id, 'device_name': '/dev/vdb',
  3727. 'connection_info': '{"test": "test"}'})
  3728. bdm = objects.BlockDeviceMapping(context=self.context, **fake_bdm)
  3729. with test.nested(
  3730. mock.patch.object(self.compute, 'volume_api'),
  3731. mock.patch.object(self.compute, 'driver'),
  3732. mock.patch.object(bdm, 'destroy'),
  3733. ) as (volume_api, driver, bdm_destroy):
  3734. self.compute._detach_volume(self.context, bdm, inst_obj,
  3735. destroy_bdm=destroy_bdm,
  3736. attachment_id=attachment_id)
  3737. detach.assert_called_once_with(self.context, inst_obj,
  3738. self.compute.volume_api, self.compute.driver,
  3739. attachment_id=attachment_id,
  3740. destroy_bdm=destroy_bdm)
  3741. notify_inst_usage.assert_called_once_with(
  3742. self.context, inst_obj, "volume.detach",
  3743. extra_usage_info={'volume_id': volume_id})
  3744. if destroy_bdm:
  3745. bdm_destroy.assert_called_once_with()
  3746. else:
  3747. self.assertFalse(bdm_destroy.called)
  3748. mock_notify_attach_detach.assert_has_calls([
  3749. mock.call(self.context, inst_obj, 'fake-mini',
  3750. action='volume_detach', phase='start',
  3751. volume_id=volume_id),
  3752. mock.call(self.context, inst_obj, 'fake-mini',
  3753. action='volume_detach', phase='end',
  3754. volume_id=volume_id),
  3755. ])
  3756. def test_detach_volume_evacuate(self):
  3757. """For evacuate, terminate_connection is called with original host."""
  3758. # TODO(lyarwood): Test DriverVolumeBlockDevice.driver_detach in
  3759. # ../virt/test_block_device.py
  3760. expected_connector = {'host': 'evacuated-host'}
  3761. conn_info_str = '{"connector": {"host": "evacuated-host"}}'
  3762. self._test_detach_volume_evacuate(conn_info_str,
  3763. expected=expected_connector)
  3764. def test_detach_volume_evacuate_legacy(self):
  3765. """Test coverage for evacuate with legacy attachments.
  3766. In this case, legacy means the volume was attached to the instance
  3767. before nova stashed the connector in connection_info. The connector
  3768. sent to terminate_connection will still be for the local host in this
  3769. case because nova does not have the info to get the connector for the
  3770. original (evacuated) host.
  3771. """
  3772. # TODO(lyarwood): Test DriverVolumeBlockDevice.driver_detach in
  3773. # ../virt/test_block_device.py
  3774. conn_info_str = '{"foo": "bar"}' # Has no 'connector'.
  3775. self._test_detach_volume_evacuate(conn_info_str)
  3776. def test_detach_volume_evacuate_mismatch(self):
  3777. """Test coverage for evacuate with connector mismatch.
  3778. For evacuate, if the stashed connector also has the wrong host,
  3779. then log it and stay with the local connector.
  3780. """
  3781. # TODO(lyarwood): Test DriverVolumeBlockDevice.driver_detach in
  3782. # ../virt/test_block_device.py
  3783. conn_info_str = '{"connector": {"host": "other-host"}}'
  3784. self._test_detach_volume_evacuate(conn_info_str)
  3785. @mock.patch('nova.compute.manager.ComputeManager.'
  3786. '_notify_about_instance_usage')
  3787. @mock.patch('nova.compute.utils.notify_about_volume_attach_detach')
  3788. def _test_detach_volume_evacuate(self, conn_info_str,
  3789. mock_notify_attach_detach,
  3790. notify_inst_usage,
  3791. expected=None):
  3792. """Re-usable code for detach volume evacuate test cases.
  3793. :param conn_info_str: String form of the stashed connector.
  3794. :param expected: Dict of the connector that is expected in the
  3795. terminate call (optional). Default is to expect the
  3796. local connector to be used.
  3797. """
  3798. # TODO(lyarwood): Test DriverVolumeBlockDevice.driver_detach in
  3799. # ../virt/test_block_device.py
  3800. volume_id = 'vol_id'
  3801. instance = fake_instance.fake_instance_obj(self.context,
  3802. host='evacuated-host')
  3803. fake_bdm = fake_block_device.FakeDbBlockDeviceDict(
  3804. {'source_type': 'volume', 'destination_type': 'volume',
  3805. 'volume_id': volume_id, 'device_name': '/dev/vdb',
  3806. 'connection_info': '{"test": "test"}'})
  3807. bdm = objects.BlockDeviceMapping(context=self.context, **fake_bdm)
  3808. bdm.connection_info = conn_info_str
  3809. local_connector = {'host': 'local-connector-host'}
  3810. expected_connector = local_connector if not expected else expected
  3811. with test.nested(
  3812. mock.patch.object(self.compute, 'volume_api'),
  3813. mock.patch.object(self.compute, 'driver'),
  3814. mock.patch.object(driver_bdm_volume, 'driver_detach'),
  3815. ) as (volume_api, driver, driver_detach):
  3816. driver.get_volume_connector.return_value = local_connector
  3817. self.compute._detach_volume(self.context,
  3818. bdm,
  3819. instance,
  3820. destroy_bdm=False)
  3821. driver_detach.assert_not_called()
  3822. driver.get_volume_connector.assert_called_once_with(instance)
  3823. volume_api.terminate_connection.assert_called_once_with(
  3824. self.context, volume_id, expected_connector)
  3825. volume_api.detach.assert_called_once_with(mock.ANY,
  3826. volume_id,
  3827. instance.uuid,
  3828. None)
  3829. notify_inst_usage.assert_called_once_with(
  3830. self.context, instance, "volume.detach",
  3831. extra_usage_info={'volume_id': volume_id}
  3832. )
  3833. mock_notify_attach_detach.assert_has_calls([
  3834. mock.call(self.context, instance, 'fake-mini',
  3835. action='volume_detach', phase='start',
  3836. volume_id=volume_id),
  3837. mock.call(self.context, instance, 'fake-mini',
  3838. action='volume_detach', phase='end',
  3839. volume_id=volume_id),
  3840. ])
  3841. @mock.patch('nova.compute.utils.notify_about_instance_rescue_action')
  3842. def _test_rescue(self, mock_notify, clean_shutdown=True):
  3843. instance = fake_instance.fake_instance_obj(
  3844. self.context, vm_state=vm_states.ACTIVE)
  3845. fake_nw_info = network_model.NetworkInfo()
  3846. rescue_image_meta = objects.ImageMeta.from_dict(
  3847. {'id': uuids.image_id, 'name': uuids.image_name})
  3848. with test.nested(
  3849. mock.patch.object(self.context, 'elevated',
  3850. return_value=self.context),
  3851. mock.patch.object(self.compute.network_api, 'get_instance_nw_info',
  3852. return_value=fake_nw_info),
  3853. mock.patch.object(self.compute, '_get_rescue_image',
  3854. return_value=rescue_image_meta),
  3855. mock.patch.object(self.compute, '_notify_about_instance_usage'),
  3856. mock.patch.object(self.compute, '_power_off_instance'),
  3857. mock.patch.object(self.compute.driver, 'rescue'),
  3858. mock.patch.object(compute_utils, 'notify_usage_exists'),
  3859. mock.patch.object(self.compute, '_get_power_state',
  3860. return_value=power_state.RUNNING),
  3861. mock.patch.object(instance, 'save')
  3862. ) as (
  3863. elevated_context, get_nw_info, get_rescue_image,
  3864. notify_instance_usage, power_off_instance, driver_rescue,
  3865. notify_usage_exists, get_power_state, instance_save
  3866. ):
  3867. self.compute.rescue_instance(
  3868. self.context, instance, rescue_password='verybadpass',
  3869. rescue_image_ref=None, clean_shutdown=clean_shutdown)
  3870. # assert the field values on the instance object
  3871. self.assertEqual(vm_states.RESCUED, instance.vm_state)
  3872. self.assertIsNone(instance.task_state)
  3873. self.assertEqual(power_state.RUNNING, instance.power_state)
  3874. self.assertIsNotNone(instance.launched_at)
  3875. # assert our mock calls
  3876. get_nw_info.assert_called_once_with(self.context, instance)
  3877. get_rescue_image.assert_called_once_with(
  3878. self.context, instance, None)
  3879. extra_usage_info = {'rescue_image_name': uuids.image_name}
  3880. notify_calls = [
  3881. mock.call(self.context, instance, "rescue.start",
  3882. extra_usage_info=extra_usage_info,
  3883. network_info=fake_nw_info),
  3884. mock.call(self.context, instance, "rescue.end",
  3885. extra_usage_info=extra_usage_info,
  3886. network_info=fake_nw_info)
  3887. ]
  3888. notify_instance_usage.assert_has_calls(notify_calls)
  3889. power_off_instance.assert_called_once_with(self.context, instance,
  3890. clean_shutdown)
  3891. driver_rescue.assert_called_once_with(
  3892. self.context, instance, fake_nw_info, rescue_image_meta,
  3893. 'verybadpass')
  3894. notify_usage_exists.assert_called_once_with(self.compute.notifier,
  3895. self.context, instance, 'fake-mini', current_period=True)
  3896. mock_notify.assert_has_calls([
  3897. mock.call(self.context, instance, 'fake-mini', None,
  3898. phase='start'),
  3899. mock.call(self.context, instance, 'fake-mini', None,
  3900. phase='end')])
  3901. instance_save.assert_called_once_with(
  3902. expected_task_state=task_states.RESCUING)
  3903. def test_rescue(self):
  3904. self._test_rescue()
  3905. def test_rescue_forced_shutdown(self):
  3906. self._test_rescue(clean_shutdown=False)
  3907. @mock.patch('nova.compute.utils.notify_about_instance_action')
  3908. def test_unrescue(self, mock_notify):
  3909. instance = fake_instance.fake_instance_obj(
  3910. self.context, vm_state=vm_states.RESCUED)
  3911. fake_nw_info = network_model.NetworkInfo()
  3912. with test.nested(
  3913. mock.patch.object(self.context, 'elevated',
  3914. return_value=self.context),
  3915. mock.patch.object(self.compute.network_api, 'get_instance_nw_info',
  3916. return_value=fake_nw_info),
  3917. mock.patch.object(self.compute, '_notify_about_instance_usage'),
  3918. mock.patch.object(self.compute.driver, 'unrescue'),
  3919. mock.patch.object(self.compute, '_get_power_state',
  3920. return_value=power_state.RUNNING),
  3921. mock.patch.object(instance, 'save')
  3922. ) as (
  3923. elevated_context, get_nw_info, notify_instance_usage,
  3924. driver_unrescue, get_power_state, instance_save
  3925. ):
  3926. self.compute.unrescue_instance(self.context, instance)
  3927. # assert the field values on the instance object
  3928. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  3929. self.assertIsNone(instance.task_state)
  3930. self.assertEqual(power_state.RUNNING, instance.power_state)
  3931. # assert our mock calls
  3932. get_nw_info.assert_called_once_with(self.context, instance)
  3933. notify_calls = [
  3934. mock.call(self.context, instance, "unrescue.start",
  3935. network_info=fake_nw_info),
  3936. mock.call(self.context, instance, "unrescue.end",
  3937. network_info=fake_nw_info)
  3938. ]
  3939. notify_instance_usage.assert_has_calls(notify_calls)
  3940. driver_unrescue.assert_called_once_with(instance, fake_nw_info)
  3941. mock_notify.assert_has_calls([
  3942. mock.call(self.context, instance, 'fake-mini',
  3943. action='unrescue', phase='start'),
  3944. mock.call(self.context, instance, 'fake-mini',
  3945. action='unrescue', phase='end')])
  3946. instance_save.assert_called_once_with(
  3947. expected_task_state=task_states.UNRESCUING)
  3948. @mock.patch('nova.compute.manager.ComputeManager._get_power_state',
  3949. return_value=power_state.RUNNING)
  3950. @mock.patch.object(objects.Instance, 'save')
  3951. def test_set_admin_password(self, instance_save_mock, power_state_mock):
  3952. # Ensure instance can have its admin password set.
  3953. instance = fake_instance.fake_instance_obj(
  3954. self.context,
  3955. vm_state=vm_states.ACTIVE,
  3956. task_state=task_states.UPDATING_PASSWORD)
  3957. @mock.patch.object(self.context, 'elevated', return_value=self.context)
  3958. @mock.patch.object(self.compute.driver, 'set_admin_password')
  3959. def do_test(driver_mock, elevated_mock):
  3960. # call the manager method
  3961. self.compute.set_admin_password(self.context, instance,
  3962. 'fake-pass')
  3963. # make our assertions
  3964. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  3965. self.assertIsNone(instance.task_state)
  3966. power_state_mock.assert_called_once_with(self.context, instance)
  3967. driver_mock.assert_called_once_with(instance, 'fake-pass')
  3968. instance_save_mock.assert_called_once_with(
  3969. expected_task_state=task_states.UPDATING_PASSWORD)
  3970. do_test()
  3971. @mock.patch('nova.compute.manager.ComputeManager._get_power_state',
  3972. return_value=power_state.NOSTATE)
  3973. @mock.patch('nova.compute.manager.ComputeManager._instance_update')
  3974. @mock.patch.object(objects.Instance, 'save')
  3975. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  3976. def test_set_admin_password_bad_state(self, add_fault_mock,
  3977. instance_save_mock, update_mock,
  3978. power_state_mock):
  3979. # Test setting password while instance is rebuilding.
  3980. instance = fake_instance.fake_instance_obj(self.context)
  3981. with mock.patch.object(self.context, 'elevated',
  3982. return_value=self.context):
  3983. # call the manager method
  3984. self.assertRaises(exception.InstancePasswordSetFailed,
  3985. self.compute.set_admin_password,
  3986. self.context, instance, None)
  3987. # make our assertions
  3988. power_state_mock.assert_called_once_with(self.context, instance)
  3989. instance_save_mock.assert_called_once_with(
  3990. expected_task_state=task_states.UPDATING_PASSWORD)
  3991. add_fault_mock.assert_called_once_with(
  3992. self.context, instance, mock.ANY, mock.ANY)
  3993. @mock.patch('nova.utils.generate_password', return_value='fake-pass')
  3994. @mock.patch('nova.compute.manager.ComputeManager._get_power_state',
  3995. return_value=power_state.RUNNING)
  3996. @mock.patch('nova.compute.manager.ComputeManager._instance_update')
  3997. @mock.patch.object(objects.Instance, 'save')
  3998. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  3999. def _do_test_set_admin_password_driver_error(self, exc,
  4000. expected_vm_state,
  4001. expected_task_state,
  4002. expected_exception,
  4003. add_fault_mock,
  4004. instance_save_mock,
  4005. update_mock,
  4006. power_state_mock,
  4007. gen_password_mock):
  4008. # Ensure expected exception is raised if set_admin_password fails.
  4009. instance = fake_instance.fake_instance_obj(
  4010. self.context,
  4011. vm_state=vm_states.ACTIVE,
  4012. task_state=task_states.UPDATING_PASSWORD)
  4013. @mock.patch.object(self.context, 'elevated', return_value=self.context)
  4014. @mock.patch.object(self.compute.driver, 'set_admin_password',
  4015. side_effect=exc)
  4016. def do_test(driver_mock, elevated_mock):
  4017. # error raised from the driver should not reveal internal
  4018. # information so a new error is raised
  4019. self.assertRaises(expected_exception,
  4020. self.compute.set_admin_password,
  4021. self.context,
  4022. instance=instance,
  4023. new_pass=None)
  4024. if expected_exception != exception.InstancePasswordSetFailed:
  4025. instance_save_mock.assert_called_once_with(
  4026. expected_task_state=task_states.UPDATING_PASSWORD)
  4027. self.assertEqual(expected_vm_state, instance.vm_state)
  4028. # check revert_task_state decorator
  4029. update_mock.assert_called_once_with(
  4030. self.context, instance, task_state=expected_task_state)
  4031. # check wrap_instance_fault decorator
  4032. add_fault_mock.assert_called_once_with(
  4033. self.context, instance, mock.ANY, mock.ANY)
  4034. do_test()
  4035. def test_set_admin_password_driver_not_authorized(self):
  4036. # Ensure expected exception is raised if set_admin_password not
  4037. # authorized.
  4038. exc = exception.Forbidden('Internal error')
  4039. expected_exception = exception.InstancePasswordSetFailed
  4040. self._do_test_set_admin_password_driver_error(
  4041. exc, vm_states.ACTIVE, None, expected_exception)
  4042. def test_set_admin_password_driver_not_implemented(self):
  4043. # Ensure expected exception is raised if set_admin_password not
  4044. # implemented by driver.
  4045. exc = NotImplementedError()
  4046. expected_exception = NotImplementedError
  4047. self._do_test_set_admin_password_driver_error(
  4048. exc, vm_states.ACTIVE, None, expected_exception)
  4049. def test_set_admin_password_driver_not_supported(self):
  4050. exc = exception.SetAdminPasswdNotSupported()
  4051. expected_exception = exception.SetAdminPasswdNotSupported
  4052. self._do_test_set_admin_password_driver_error(
  4053. exc, vm_states.ACTIVE, None, expected_exception)
  4054. def test_set_admin_password_guest_agent_no_enabled(self):
  4055. exc = exception.QemuGuestAgentNotEnabled()
  4056. expected_exception = exception.InstanceAgentNotEnabled
  4057. self._do_test_set_admin_password_driver_error(
  4058. exc, vm_states.ACTIVE, None, expected_exception)
  4059. def test_destroy_evacuated_instances_no_migrations(self):
  4060. with mock.patch(
  4061. 'nova.objects.MigrationList.get_by_filters') as migration_list:
  4062. migration_list.return_value = []
  4063. result = self.compute._destroy_evacuated_instances(
  4064. self.context, {})
  4065. self.assertEqual({}, result)
  4066. def test_destroy_evacuated_instances(self):
  4067. our_host = self.compute.host
  4068. flavor = objects.Flavor()
  4069. instance_1 = objects.Instance(self.context, flavor=flavor)
  4070. instance_1.uuid = uuids.instance_1
  4071. instance_1.task_state = None
  4072. instance_1.vm_state = vm_states.ACTIVE
  4073. instance_1.host = 'not-' + our_host
  4074. instance_1.user_id = uuids.user_id
  4075. instance_1.project_id = uuids.project_id
  4076. instance_2 = objects.Instance(self.context, flavor=flavor)
  4077. instance_2.uuid = uuids.instance_2
  4078. instance_2.task_state = None
  4079. instance_2.vm_state = vm_states.ACTIVE
  4080. instance_2.host = 'not-' + our_host
  4081. instance_2.user_id = uuids.user_id
  4082. instance_2.project_id = uuids.project_id
  4083. instance_2.deleted = False
  4084. # Only instance 2 has a migration record
  4085. migration = objects.Migration(instance_uuid=instance_2.uuid)
  4086. # Consider the migration successful
  4087. migration.status = 'done'
  4088. migration.source_node = 'fake-node'
  4089. node_cache = {
  4090. uuids.our_node_uuid:
  4091. objects.ComputeNode(
  4092. uuid=uuids.our_node_uuid,
  4093. hypervisor_hostname='fake-node')
  4094. }
  4095. with test.nested(
  4096. mock.patch.object(self.compute, '_get_instances_on_driver',
  4097. return_value=[instance_1,
  4098. instance_2]),
  4099. mock.patch.object(self.compute.network_api, 'get_instance_nw_info',
  4100. return_value=None),
  4101. mock.patch.object(self.compute, '_get_instance_block_device_info',
  4102. return_value={}),
  4103. mock.patch.object(self.compute, '_is_instance_storage_shared',
  4104. return_value=False),
  4105. mock.patch.object(self.compute.driver, 'destroy'),
  4106. mock.patch('nova.objects.MigrationList.get_by_filters'),
  4107. mock.patch('nova.objects.Migration.save'),
  4108. mock.patch('nova.scheduler.utils.resources_from_flavor'),
  4109. mock.patch.object(self.compute.reportclient,
  4110. 'remove_provider_tree_from_instance_allocation')
  4111. ) as (_get_instances_on_driver, get_instance_nw_info,
  4112. _get_instance_block_device_info, _is_instance_storage_shared,
  4113. destroy, migration_list, migration_save, get_resources,
  4114. remove_allocation):
  4115. migration_list.return_value = [migration]
  4116. get_resources.return_value = mock.sentinel.resources
  4117. self.compute._destroy_evacuated_instances(self.context, node_cache)
  4118. # Only instance 2 should be deleted. Instance 1 is still running
  4119. # here, but no migration from our host exists, so ignore it
  4120. destroy.assert_called_once_with(self.context, instance_2, None,
  4121. {}, True)
  4122. remove_allocation.assert_called_once_with(
  4123. self.context, instance_2.uuid, uuids.our_node_uuid)
  4124. def test_destroy_evacuated_instances_node_deleted(self):
  4125. our_host = self.compute.host
  4126. flavor = objects.Flavor()
  4127. instance_1 = objects.Instance(self.context, flavor=flavor)
  4128. instance_1.uuid = uuids.instance_1
  4129. instance_1.task_state = None
  4130. instance_1.vm_state = vm_states.ACTIVE
  4131. instance_1.host = 'not-' + our_host
  4132. instance_1.user_id = uuids.user_id
  4133. instance_1.project_id = uuids.project_id
  4134. instance_1.deleted = False
  4135. instance_2 = objects.Instance(self.context, flavor=flavor)
  4136. instance_2.uuid = uuids.instance_2
  4137. instance_2.task_state = None
  4138. instance_2.vm_state = vm_states.ACTIVE
  4139. instance_2.host = 'not-' + our_host
  4140. instance_2.user_id = uuids.user_id
  4141. instance_2.project_id = uuids.project_id
  4142. instance_2.deleted = False
  4143. migration_1 = objects.Migration(instance_uuid=instance_1.uuid)
  4144. # Consider the migration successful but the node was deleted while the
  4145. # compute was down
  4146. migration_1.status = 'done'
  4147. migration_1.source_node = 'deleted-node'
  4148. migration_2 = objects.Migration(instance_uuid=instance_2.uuid)
  4149. # Consider the migration successful
  4150. migration_2.status = 'done'
  4151. migration_2.source_node = 'fake-node'
  4152. node_cache = {
  4153. uuids.our_node_uuid:
  4154. objects.ComputeNode(
  4155. uuid=uuids.our_node_uuid,
  4156. hypervisor_hostname='fake-node')
  4157. }
  4158. with test.nested(
  4159. mock.patch.object(self.compute, '_get_instances_on_driver',
  4160. return_value=[instance_1,
  4161. instance_2]),
  4162. mock.patch.object(self.compute.network_api, 'get_instance_nw_info',
  4163. return_value=None),
  4164. mock.patch.object(self.compute, '_get_instance_block_device_info',
  4165. return_value={}),
  4166. mock.patch.object(self.compute, '_is_instance_storage_shared',
  4167. return_value=False),
  4168. mock.patch.object(self.compute.driver, 'destroy'),
  4169. mock.patch('nova.objects.MigrationList.get_by_filters'),
  4170. mock.patch('nova.objects.Migration.save'),
  4171. mock.patch('nova.scheduler.utils.resources_from_flavor'),
  4172. mock.patch.object(self.compute.reportclient,
  4173. 'remove_provider_tree_from_instance_allocation')
  4174. ) as (_get_instances_on_driver, get_instance_nw_info,
  4175. _get_instance_block_device_info, _is_instance_storage_shared,
  4176. destroy, migration_list, migration_save, get_resources,
  4177. remove_allocation):
  4178. migration_list.return_value = [migration_1, migration_2]
  4179. get_resources.return_value = mock.sentinel.resources
  4180. self.compute._destroy_evacuated_instances(self.context, node_cache)
  4181. # both instance_1 and instance_2 is destroyed in the driver
  4182. destroy.assert_has_calls(
  4183. [mock.call(self.context, instance_1, None, {}, True),
  4184. mock.call(self.context, instance_2, None, {}, True)],
  4185. any_order=True)
  4186. # but only instance_2 is deallocated as the compute node for
  4187. # instance_1 is already deleted
  4188. remove_allocation.assert_called_once_with(
  4189. self.context, instance_2.uuid, uuids.our_node_uuid)
  4190. def test_destroy_evacuated_instances_not_on_hypervisor(self):
  4191. our_host = self.compute.host
  4192. flavor = objects.Flavor()
  4193. instance_1 = objects.Instance(self.context, flavor=flavor)
  4194. instance_1.uuid = uuids.instance_1
  4195. instance_1.task_state = None
  4196. instance_1.vm_state = vm_states.ACTIVE
  4197. instance_1.host = 'not-' + our_host
  4198. instance_1.user_id = uuids.user_id
  4199. instance_1.project_id = uuids.project_id
  4200. instance_1.deleted = False
  4201. migration_1 = objects.Migration(instance_uuid=instance_1.uuid)
  4202. migration_1.status = 'done'
  4203. migration_1.source_node = 'our-node'
  4204. node_cache = {
  4205. uuids.our_node_uuid:
  4206. objects.ComputeNode(
  4207. uuid=uuids.our_node_uuid,
  4208. hypervisor_hostname='our-node')
  4209. }
  4210. with test.nested(
  4211. mock.patch.object(self.compute, '_get_instances_on_driver',
  4212. return_value=[]),
  4213. mock.patch.object(self.compute.driver, 'destroy'),
  4214. mock.patch('nova.objects.MigrationList.get_by_filters'),
  4215. mock.patch('nova.objects.Migration.save'),
  4216. mock.patch('nova.scheduler.utils.resources_from_flavor'),
  4217. mock.patch.object(self.compute.reportclient,
  4218. 'remove_provider_tree_from_instance_allocation'),
  4219. mock.patch('nova.objects.Instance.get_by_uuid')
  4220. ) as (_get_intances_on_driver, destroy, migration_list, migration_save,
  4221. get_resources, remove_allocation, instance_get_by_uuid):
  4222. migration_list.return_value = [migration_1]
  4223. instance_get_by_uuid.return_value = instance_1
  4224. get_resources.return_value = mock.sentinel.resources
  4225. self.compute._destroy_evacuated_instances(self.context, node_cache)
  4226. # nothing to be destroyed as the driver returned no instances on
  4227. # the hypervisor
  4228. self.assertFalse(destroy.called)
  4229. # but our only instance still cleaned up in placement
  4230. remove_allocation.assert_called_once_with(
  4231. self.context, instance_1.uuid, uuids.our_node_uuid)
  4232. instance_get_by_uuid.assert_called_once_with(
  4233. self.context, instance_1.uuid)
  4234. def test_destroy_evacuated_instances_not_on_hyp_and_instance_deleted(self):
  4235. migration_1 = objects.Migration(instance_uuid=uuids.instance_1)
  4236. migration_1.status = 'done'
  4237. migration_1.source_node = 'our-node'
  4238. with test.nested(
  4239. mock.patch.object(self.compute, '_get_instances_on_driver',
  4240. return_value=[]),
  4241. mock.patch.object(self.compute.driver, 'destroy'),
  4242. mock.patch('nova.objects.MigrationList.get_by_filters'),
  4243. mock.patch('nova.objects.Migration.save'),
  4244. mock.patch('nova.scheduler.utils.resources_from_flavor'),
  4245. mock.patch.object(self.compute.reportclient,
  4246. 'remove_provider_tree_from_instance_allocation'),
  4247. mock.patch('nova.objects.Instance.get_by_uuid')
  4248. ) as (_get_instances_on_driver,
  4249. destroy, migration_list, migration_save, get_resources,
  4250. remove_allocation, instance_get_by_uuid):
  4251. migration_list.return_value = [migration_1]
  4252. instance_get_by_uuid.side_effect = exception.InstanceNotFound(
  4253. instance_id=uuids.instance_1)
  4254. self.compute._destroy_evacuated_instances(self.context, {})
  4255. # nothing to be destroyed as the driver returned no instances on
  4256. # the hypervisor
  4257. self.assertFalse(destroy.called)
  4258. instance_get_by_uuid.assert_called_once_with(
  4259. self.context, uuids.instance_1)
  4260. # nothing to be cleaned as the instance was deleted already
  4261. self.assertFalse(remove_allocation.called)
  4262. @mock.patch('nova.compute.manager.ComputeManager.'
  4263. '_destroy_evacuated_instances')
  4264. @mock.patch('nova.compute.manager.LOG')
  4265. def test_init_host_foreign_instance(self, mock_log, mock_destroy):
  4266. inst = mock.MagicMock()
  4267. inst.host = self.compute.host + '-alt'
  4268. self.compute._init_instance(mock.sentinel.context, inst)
  4269. self.assertFalse(inst.save.called)
  4270. self.assertTrue(mock_log.warning.called)
  4271. msg = mock_log.warning.call_args_list[0]
  4272. self.assertIn('appears to not be owned by this host', msg[0][0])
  4273. def test_init_host_pci_passthrough_whitelist_validation_failure(self):
  4274. # Tests that we fail init_host if there is a pci.passthrough_whitelist
  4275. # configured incorrectly.
  4276. self.flags(passthrough_whitelist=[
  4277. # it's invalid to specify both in the same devspec
  4278. jsonutils.dumps({'address': 'foo', 'devname': 'bar'})],
  4279. group='pci')
  4280. self.assertRaises(exception.PciDeviceInvalidDeviceName,
  4281. self.compute.init_host)
  4282. @mock.patch('nova.compute.manager.ComputeManager._instance_update')
  4283. def test_error_out_instance_on_exception_not_implemented_err(self,
  4284. inst_update_mock):
  4285. instance = fake_instance.fake_instance_obj(self.context)
  4286. def do_test():
  4287. with self.compute._error_out_instance_on_exception(
  4288. self.context, instance, instance_state=vm_states.STOPPED):
  4289. raise NotImplementedError('test')
  4290. self.assertRaises(NotImplementedError, do_test)
  4291. inst_update_mock.assert_called_once_with(
  4292. self.context, instance,
  4293. vm_state=vm_states.STOPPED, task_state=None)
  4294. @mock.patch('nova.compute.manager.ComputeManager._instance_update')
  4295. def test_error_out_instance_on_exception_inst_fault_rollback(self,
  4296. inst_update_mock):
  4297. instance = fake_instance.fake_instance_obj(self.context)
  4298. def do_test():
  4299. with self.compute._error_out_instance_on_exception(
  4300. self.context, instance, instance_state=vm_states.STOPPED):
  4301. raise exception.InstanceFaultRollback(
  4302. inner_exception=test.TestingException('test'))
  4303. self.assertRaises(test.TestingException, do_test)
  4304. # The vm_state should be set to the instance_state parameter.
  4305. inst_update_mock.assert_called_once_with(
  4306. self.context, instance,
  4307. vm_state=vm_states.STOPPED, task_state=None)
  4308. @mock.patch('nova.compute.manager.ComputeManager.'
  4309. '_set_instance_obj_error_state')
  4310. def test_error_out_instance_on_exception_unknown_with_quotas(self,
  4311. set_error):
  4312. instance = fake_instance.fake_instance_obj(self.context)
  4313. def do_test():
  4314. with self.compute._error_out_instance_on_exception(
  4315. self.context, instance):
  4316. raise test.TestingException('test')
  4317. self.assertRaises(test.TestingException, do_test)
  4318. set_error.assert_called_once_with(self.context, instance)
  4319. @mock.patch('nova.compute.manager.ComputeManager.'
  4320. '_detach_volume')
  4321. def test_cleanup_volumes(self, mock_detach):
  4322. instance = fake_instance.fake_instance_obj(self.context)
  4323. bdm_do_not_delete_dict = fake_block_device.FakeDbBlockDeviceDict(
  4324. {'volume_id': 'fake-id1', 'source_type': 'image',
  4325. 'delete_on_termination': False})
  4326. bdm_delete_dict = fake_block_device.FakeDbBlockDeviceDict(
  4327. {'volume_id': 'fake-id2', 'source_type': 'image',
  4328. 'delete_on_termination': True})
  4329. bdms = block_device_obj.block_device_make_list(self.context,
  4330. [bdm_do_not_delete_dict, bdm_delete_dict])
  4331. with mock.patch.object(self.compute.volume_api,
  4332. 'delete') as volume_delete:
  4333. self.compute._cleanup_volumes(self.context, instance, bdms)
  4334. calls = [mock.call(self.context, bdm, instance,
  4335. destroy_bdm=bdm.delete_on_termination)
  4336. for bdm in bdms]
  4337. self.assertEqual(calls, mock_detach.call_args_list)
  4338. volume_delete.assert_called_once_with(self.context,
  4339. bdms[1].volume_id)
  4340. @mock.patch('nova.compute.manager.ComputeManager.'
  4341. '_detach_volume')
  4342. def test_cleanup_volumes_exception_do_not_raise(self, mock_detach):
  4343. instance = fake_instance.fake_instance_obj(self.context)
  4344. bdm_dict1 = fake_block_device.FakeDbBlockDeviceDict(
  4345. {'volume_id': 'fake-id1', 'source_type': 'image',
  4346. 'delete_on_termination': True})
  4347. bdm_dict2 = fake_block_device.FakeDbBlockDeviceDict(
  4348. {'volume_id': 'fake-id2', 'source_type': 'image',
  4349. 'delete_on_termination': True})
  4350. bdms = block_device_obj.block_device_make_list(self.context,
  4351. [bdm_dict1, bdm_dict2])
  4352. with mock.patch.object(self.compute.volume_api,
  4353. 'delete',
  4354. side_effect=[test.TestingException(), None]) as volume_delete:
  4355. self.compute._cleanup_volumes(self.context, instance, bdms,
  4356. raise_exc=False)
  4357. calls = [mock.call(self.context, bdm.volume_id) for bdm in bdms]
  4358. self.assertEqual(calls, volume_delete.call_args_list)
  4359. calls = [mock.call(self.context, bdm, instance,
  4360. destroy_bdm=True) for bdm in bdms]
  4361. self.assertEqual(calls, mock_detach.call_args_list)
  4362. @mock.patch('nova.compute.manager.ComputeManager.'
  4363. '_detach_volume')
  4364. def test_cleanup_volumes_exception_raise(self, mock_detach):
  4365. instance = fake_instance.fake_instance_obj(self.context)
  4366. bdm_dict1 = fake_block_device.FakeDbBlockDeviceDict(
  4367. {'volume_id': 'fake-id1', 'source_type': 'image',
  4368. 'delete_on_termination': True})
  4369. bdm_dict2 = fake_block_device.FakeDbBlockDeviceDict(
  4370. {'volume_id': 'fake-id2', 'source_type': 'image',
  4371. 'delete_on_termination': True})
  4372. bdms = block_device_obj.block_device_make_list(self.context,
  4373. [bdm_dict1, bdm_dict2])
  4374. with mock.patch.object(self.compute.volume_api,
  4375. 'delete',
  4376. side_effect=[test.TestingException(), None]) as volume_delete:
  4377. self.assertRaises(test.TestingException,
  4378. self.compute._cleanup_volumes, self.context, instance,
  4379. bdms)
  4380. calls = [mock.call(self.context, bdm.volume_id) for bdm in bdms]
  4381. self.assertEqual(calls, volume_delete.call_args_list)
  4382. calls = [mock.call(self.context, bdm, instance,
  4383. destroy_bdm=bdm.delete_on_termination)
  4384. for bdm in bdms]
  4385. self.assertEqual(calls, mock_detach.call_args_list)
  4386. @mock.patch('nova.compute.manager.ComputeManager._detach_volume',
  4387. side_effect=exception.CinderConnectionFailed(reason='idk'))
  4388. def test_cleanup_volumes_detach_fails_raise_exc(self, mock_detach):
  4389. instance = fake_instance.fake_instance_obj(self.context)
  4390. bdms = block_device_obj.block_device_make_list(
  4391. self.context,
  4392. [fake_block_device.FakeDbBlockDeviceDict(
  4393. {'volume_id': uuids.volume_id,
  4394. 'source_type': 'volume',
  4395. 'destination_type': 'volume',
  4396. 'delete_on_termination': False})])
  4397. self.assertRaises(exception.CinderConnectionFailed,
  4398. self.compute._cleanup_volumes, self.context,
  4399. instance, bdms)
  4400. mock_detach.assert_called_once_with(
  4401. self.context, bdms[0], instance, destroy_bdm=False)
  4402. def test_stop_instance_task_state_none_power_state_shutdown(self):
  4403. # Tests that stop_instance doesn't puke when the instance power_state
  4404. # is shutdown and the task_state is None.
  4405. instance = fake_instance.fake_instance_obj(
  4406. self.context, vm_state=vm_states.ACTIVE,
  4407. task_state=None, power_state=power_state.SHUTDOWN)
  4408. @mock.patch.object(self.compute, '_get_power_state',
  4409. return_value=power_state.SHUTDOWN)
  4410. @mock.patch.object(compute_utils, 'notify_about_instance_action')
  4411. @mock.patch.object(self.compute, '_notify_about_instance_usage')
  4412. @mock.patch.object(self.compute, '_power_off_instance')
  4413. @mock.patch.object(instance, 'save')
  4414. def do_test(save_mock, power_off_mock, notify_mock,
  4415. notify_action_mock, get_state_mock):
  4416. # run the code
  4417. self.compute.stop_instance(self.context, instance, True)
  4418. # assert the calls
  4419. self.assertEqual(2, get_state_mock.call_count)
  4420. notify_mock.assert_has_calls([
  4421. mock.call(self.context, instance, 'power_off.start'),
  4422. mock.call(self.context, instance, 'power_off.end')
  4423. ])
  4424. notify_action_mock.assert_has_calls([
  4425. mock.call(self.context, instance, 'fake-mini',
  4426. action='power_off', phase='start'),
  4427. mock.call(self.context, instance, 'fake-mini',
  4428. action='power_off', phase='end'),
  4429. ])
  4430. power_off_mock.assert_called_once_with(
  4431. self.context, instance, True)
  4432. save_mock.assert_called_once_with(
  4433. expected_task_state=[task_states.POWERING_OFF, None])
  4434. self.assertEqual(power_state.SHUTDOWN, instance.power_state)
  4435. self.assertIsNone(instance.task_state)
  4436. self.assertEqual(vm_states.STOPPED, instance.vm_state)
  4437. do_test()
  4438. def test_reset_network_driver_not_implemented(self):
  4439. instance = fake_instance.fake_instance_obj(self.context)
  4440. @mock.patch.object(self.compute.driver, 'reset_network',
  4441. side_effect=NotImplementedError())
  4442. @mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
  4443. def do_test(mock_add_fault, mock_reset):
  4444. self.assertRaises(messaging.ExpectedException,
  4445. self.compute.reset_network,
  4446. self.context,
  4447. instance)
  4448. self.compute = utils.ExceptionHelper(self.compute)
  4449. self.assertRaises(NotImplementedError,
  4450. self.compute.reset_network,
  4451. self.context,
  4452. instance)
  4453. do_test()
  4454. @mock.patch.object(manager.ComputeManager, '_set_migration_status')
  4455. @mock.patch.object(manager.ComputeManager,
  4456. '_do_rebuild_instance_with_claim')
  4457. @mock.patch('nova.compute.utils.notify_about_instance_rebuild')
  4458. @mock.patch.object(manager.ComputeManager, '_notify_about_instance_usage')
  4459. def _test_rebuild_ex(self, instance, exc,
  4460. mock_notify_about_instance_usage,
  4461. mock_notify, mock_rebuild, mock_set,
  4462. recreate=False, scheduled_node=None):
  4463. mock_rebuild.side_effect = exc
  4464. self.compute.rebuild_instance(self.context, instance, None, None, None,
  4465. None, None, None, recreate,
  4466. False, False, None, scheduled_node, {},
  4467. None)
  4468. mock_set.assert_called_once_with(None, 'failed')
  4469. mock_notify_about_instance_usage.assert_called_once_with(
  4470. mock.ANY, instance, 'rebuild.error', fault=mock_rebuild.side_effect
  4471. )
  4472. mock_notify.assert_called_once_with(
  4473. mock.ANY, instance, 'fake-mini', phase='error', exception=exc,
  4474. bdms=None, tb=mock.ANY)
  4475. def test_rebuild_deleting(self):
  4476. instance = fake_instance.fake_instance_obj(self.context)
  4477. ex = exception.UnexpectedDeletingTaskStateError(
  4478. instance_uuid=instance.uuid, expected='expected', actual='actual')
  4479. self._test_rebuild_ex(instance, ex)
  4480. def test_rebuild_notfound(self):
  4481. instance = fake_instance.fake_instance_obj(self.context)
  4482. ex = exception.InstanceNotFound(instance_id=instance.uuid)
  4483. self._test_rebuild_ex(instance, ex)
  4484. @mock.patch('nova.compute.utils.add_instance_fault_from_exc')
  4485. @mock.patch.object(manager.ComputeManager,
  4486. '_error_out_instance_on_exception')
  4487. def test_rebuild_driver_error_same_host(self, mock_error, mock_aiffe):
  4488. instance = fake_instance.fake_instance_obj(self.context)
  4489. ex = test.TestingException('foo')
  4490. rt = self._mock_rt()
  4491. self.assertRaises(test.TestingException,
  4492. self._test_rebuild_ex, instance, ex)
  4493. self.assertFalse(
  4494. rt.delete_allocation_for_evacuated_instance.called)
  4495. @mock.patch('nova.context.RequestContext.elevated')
  4496. @mock.patch('nova.compute.utils.add_instance_fault_from_exc')
  4497. @mock.patch.object(manager.ComputeManager,
  4498. '_error_out_instance_on_exception')
  4499. def test_rebuild_driver_error_evacuate(self, mock_error, mock_aiffe,
  4500. mock_elevated):
  4501. mock_elevated.return_value = self.context
  4502. instance = fake_instance.fake_instance_obj(self.context)
  4503. instance.system_metadata = {}
  4504. ex = test.TestingException('foo')
  4505. rt = self._mock_rt()
  4506. self.assertRaises(test.TestingException,
  4507. self._test_rebuild_ex, instance, ex,
  4508. recreate=True, scheduled_node='foo')
  4509. delete_alloc = rt.delete_allocation_for_evacuated_instance
  4510. delete_alloc.assert_called_once_with(self.context, instance, 'foo',
  4511. node_type='destination')
  4512. @mock.patch('nova.objects.Instance.save')
  4513. @mock.patch('nova.compute.utils.add_instance_fault_from_exc')
  4514. @mock.patch('nova.compute.resource_tracker.ResourceTracker.'
  4515. 'delete_allocation_for_evacuated_instance')
  4516. def test_rebuild_compute_resources_unavailable(self, mock_delete_alloc,
  4517. mock_add_fault,
  4518. mock_save):
  4519. """Tests that when the rebuild_claim fails with
  4520. ComputeResourcesUnavailable the vm_state on the instance remains
  4521. unchanged.
  4522. """
  4523. instance = fake_instance.fake_instance_obj(self.context)
  4524. instance.vm_state = vm_states.ACTIVE
  4525. ex = exception.ComputeResourcesUnavailable(reason='out of foo')
  4526. self.assertRaises(exception.BuildAbortException,
  4527. self._test_rebuild_ex, instance, ex)
  4528. # Make sure the instance vm_state did not change.
  4529. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  4530. mock_delete_alloc.assert_called_once()
  4531. mock_save.assert_called()
  4532. mock_add_fault.assert_called_once()
  4533. @mock.patch('nova.context.RequestContext.elevated')
  4534. @mock.patch('nova.objects.instance.Instance.drop_migration_context')
  4535. @mock.patch('nova.objects.instance.Instance.apply_migration_context')
  4536. @mock.patch('nova.objects.instance.Instance.mutated_migration_context')
  4537. @mock.patch('nova.objects.BlockDeviceMappingList.get_by_instance_uuid')
  4538. @mock.patch('nova.network.neutronv2.api.API.'
  4539. 'setup_instance_network_on_host')
  4540. @mock.patch('nova.network.neutronv2.api.API.setup_networks_on_host')
  4541. @mock.patch('nova.objects.instance.Instance.save')
  4542. @mock.patch('nova.compute.utils.notify_about_instance_rebuild')
  4543. @mock.patch('nova.compute.utils.notify_about_instance_usage')
  4544. @mock.patch('nova.compute.utils.notify_usage_exists')
  4545. @mock.patch('nova.objects.instance.Instance.image_meta',
  4546. new_callable=mock.PropertyMock)
  4547. @mock.patch('nova.compute.manager.ComputeManager.'
  4548. '_validate_instance_group_policy')
  4549. @mock.patch('nova.compute.manager.ComputeManager._set_migration_status')
  4550. @mock.patch('nova.compute.resource_tracker.ResourceTracker.rebuild_claim')
  4551. def test_evacuate_late_server_group_policy_check(
  4552. self, mock_rebuild_claim, mock_set_migration_status,
  4553. mock_validate_policy, mock_image_meta, mock_notify_exists,
  4554. mock_notify_legacy, mock_notify, mock_instance_save,
  4555. mock_setup_networks, mock_setup_intance_network, mock_get_bdms,
  4556. mock_mutate_migration, mock_appy_migration, mock_drop_migration,
  4557. mock_context_elevated):
  4558. self.flags(api_servers=['http://localhost/image/v2'], group='glance')
  4559. instance = fake_instance.fake_instance_obj(self.context)
  4560. instance.trusted_certs = None
  4561. instance.info_cache = None
  4562. elevated_context = mock.Mock()
  4563. mock_context_elevated.return_value = elevated_context
  4564. request_spec = objects.RequestSpec()
  4565. request_spec.scheduler_hints = {'group': [uuids.group]}
  4566. with mock.patch.object(self.compute, 'network_api'):
  4567. self.compute.rebuild_instance(
  4568. self.context, instance, None, None, None, None, None,
  4569. None, recreate=True, on_shared_storage=None,
  4570. preserve_ephemeral=False, migration=None,
  4571. scheduled_node='fake-node',
  4572. limits={}, request_spec=request_spec)
  4573. mock_validate_policy.assert_called_once_with(
  4574. elevated_context, instance, {'group': [uuids.group]})
  4575. @mock.patch('nova.compute.utils.add_instance_fault_from_exc')
  4576. @mock.patch('nova.compute.resource_tracker.ResourceTracker.'
  4577. 'delete_allocation_for_evacuated_instance')
  4578. @mock.patch('nova.context.RequestContext.elevated')
  4579. @mock.patch('nova.objects.instance.Instance.save')
  4580. @mock.patch('nova.compute.utils.notify_about_instance_rebuild')
  4581. @mock.patch('nova.compute.utils.notify_about_instance_usage')
  4582. @mock.patch('nova.compute.manager.ComputeManager.'
  4583. '_validate_instance_group_policy')
  4584. @mock.patch('nova.compute.manager.ComputeManager._set_migration_status')
  4585. @mock.patch('nova.compute.resource_tracker.ResourceTracker.rebuild_claim')
  4586. def test_evacuate_late_server_group_policy_check_fails(
  4587. self, mock_rebuild_claim, mock_set_migration_status,
  4588. mock_validate_policy, mock_notify_legacy, mock_notify,
  4589. mock_instance_save, mock_context_elevated, mock_delete_allocation,
  4590. mock_instance_fault):
  4591. instance = fake_instance.fake_instance_obj(self.context)
  4592. instance.info_cache = None
  4593. instance.system_metadata = {}
  4594. instance.vm_state = vm_states.ACTIVE
  4595. elevated_context = mock.Mock()
  4596. mock_context_elevated.return_value = elevated_context
  4597. request_spec = objects.RequestSpec()
  4598. request_spec.scheduler_hints = {'group': [uuids.group]}
  4599. exc = exception.RescheduledException(
  4600. instance_uuid=instance.uuid, reason='policy violation')
  4601. mock_validate_policy.side_effect = exc
  4602. self.assertRaises(
  4603. exception.BuildAbortException, self.compute.rebuild_instance,
  4604. self.context, instance, None, None, None, None, None, None,
  4605. recreate=True, on_shared_storage=None, preserve_ephemeral=False,
  4606. migration=None, scheduled_node='fake-node', limits={},
  4607. request_spec=request_spec)
  4608. mock_validate_policy.assert_called_once_with(
  4609. elevated_context, instance, {'group': [uuids.group]})
  4610. mock_delete_allocation.assert_called_once_with(
  4611. elevated_context, instance, 'fake-node', node_type='destination')
  4612. mock_notify.assert_called_once_with(
  4613. elevated_context, instance, 'fake-mini', bdms=None, exception=exc,
  4614. phase='error', tb=mock.ANY)
  4615. # Make sure the instance vm_state did not change.
  4616. self.assertEqual(vm_states.ACTIVE, instance.vm_state)
  4617. def test_rebuild_node_not_updated_if_not_recreate(self):
  4618. node = uuidutils.generate_uuid() # ironic node uuid
  4619. instance = fake_instance.fake_instance_obj(self.context, node=node)
  4620. instance.migration_context = None
  4621. with test.nested(
  4622. mock.patch.object(self.compute, '_get_compute_info'),
  4623. mock.patch.object(self.compute, '_do_rebuild_instance_with_claim'),
  4624. mock.patch.object(objects.Instance, 'save'),
  4625. mock.patch.object(self.compute, '_set_migration_status'),
  4626. ) as (mock_get, mock_rebuild, mock_save, mock_set):
  4627. self.compute.rebuild_instance(self.context, instance, None, None,
  4628. None, None, None, None, False,
  4629. False, False, None, None, {}, None)
  4630. self.assertFalse(mock_get.called)
  4631. self.assertEqual(node, instance.node)
  4632. mock_set.assert_called_once_with(None, 'done')
  4633. def test_rebuild_node_updated_if_recreate(self):
  4634. dead_node = uuidutils.generate_uuid()
  4635. img_sys_meta = {'image_hw_numa_nodes': 1}
  4636. instance = fake_instance.fake_instance_obj(self.context,
  4637. node=dead_node)
  4638. instance.system_metadata = img_sys_meta
  4639. instance.migration_context = None
  4640. mock_rt = self._mock_rt()
  4641. with test.nested(
  4642. mock.patch.object(self.compute, '_get_compute_info'),
  4643. mock.patch.object(self.compute, '_do_rebuild_instance'),
  4644. mock.patch.object(objects.Instance, 'save'),
  4645. mock.patch.object(self.compute, '_set_migration_status'),
  4646. ) as (mock_get, mock_rebuild, mock_save, mock_set):
  4647. mock_get.return_value.hypervisor_hostname = 'new-node'
  4648. self.compute.rebuild_instance(self.context, instance, None, None,
  4649. None, None, None, None, True,
  4650. False, False, None, None, {}, None)
  4651. mock_get.assert_called_once_with(mock.ANY, self.compute.host)
  4652. self.assertEqual('new-node', instance.node)
  4653. mock_set.assert_called_once_with(None, 'done')
  4654. # Make sure the rebuild_claim was called with the proper image_meta
  4655. # from the instance.
  4656. mock_rt.rebuild_claim.assert_called_once()
  4657. self.assertIn('image_meta', mock_rt.rebuild_claim.call_args[1])
  4658. actual_image_meta = mock_rt.rebuild_claim.call_args[1][
  4659. 'image_meta'].properties
  4660. self.assertIn('hw_numa_nodes', actual_image_meta)
  4661. self.assertEqual(1, actual_image_meta.hw_numa_nodes)
  4662. @mock.patch.object(compute_utils, 'notify_about_instance_rebuild')
  4663. @mock.patch.object(compute_utils, 'notify_usage_exists')
  4664. @mock.patch.object(compute_utils, 'notify_about_instance_action')
  4665. @mock.patch.object(objects.ImageMeta, 'from_instance')
  4666. @mock.patch.object(objects.Instance, 'save', return_value=None)
  4667. def test_rebuild_nw_updated_if_recreate(self,
  4668. mock_save,
  4669. mock_image_ref,
  4670. mock_notify,
  4671. mock_notify_exists,
  4672. mock_notify_rebuild):
  4673. with test.nested(
  4674. mock.patch.object(self.compute,
  4675. '_notify_about_instance_usage'),
  4676. mock.patch.object(self.compute.network_api,
  4677. 'setup_networks_on_host'),
  4678. mock.patch.object(self.compute.network_api,
  4679. 'setup_instance_network_on_host'),
  4680. mock.patch.object(self.compute.network_api,
  4681. 'get_instance_nw_info'),
  4682. mock.patch.object(self.compute,
  4683. '_get_instance_block_device_info',
  4684. return_value='fake-bdminfo'),
  4685. mock.patch.object(self.compute, '_check_trusted_certs'),
  4686. ) as(
  4687. mock_notify_usage,
  4688. mock_setup,
  4689. mock_setup_inst,
  4690. mock_get_nw_info,
  4691. mock_get_blk,
  4692. mock_check_trusted_certs
  4693. ):
  4694. self.flags(group="glance", api_servers="http://127.0.0.1:9292")
  4695. instance = fake_instance.fake_instance_obj(self.context)
  4696. orig_vif = fake_network_cache_model.new_vif(
  4697. {'profile': {"pci_slot": "0000:01:00.1"}})
  4698. orig_nw_info = network_model.NetworkInfo([orig_vif])
  4699. new_vif = fake_network_cache_model.new_vif(
  4700. {'profile': {"pci_slot": "0000:02:00.1"}})
  4701. new_nw_info = network_model.NetworkInfo([new_vif])
  4702. info_cache = objects.InstanceInfoCache(network_info=orig_nw_info,
  4703. instance_uuid=instance.uuid)
  4704. instance.info_cache = info_cache
  4705. instance.task_state = task_states.REBUILDING
  4706. instance.migration_context = None
  4707. instance.numa_topology = None
  4708. instance.pci_requests = None
  4709. instance.pci_devices = None
  4710. orig_image_ref = None
  4711. image_meta = instance.image_meta
  4712. injected_files = []
  4713. new_pass = None
  4714. orig_sys_metadata = None
  4715. bdms = []
  4716. recreate = True
  4717. on_shared_storage = None
  4718. preserve_ephemeral = None
  4719. mock_get_nw_info.return_value = new_nw_info
  4720. self.compute._do_rebuild_instance(self.context, instance,
  4721. orig_image_ref, image_meta,
  4722. injected_files, new_pass,
  4723. orig_sys_metadata, bdms,
  4724. recreate, on_shared_storage,
  4725. preserve_ephemeral, {}, {},
  4726. self.allocations,
  4727. mock.sentinel.mapping)
  4728. mock_notify_usage.assert_has_calls(
  4729. [mock.call(self.context, instance, "rebuild.start",
  4730. extra_usage_info=mock.ANY),
  4731. mock.call(self.context, instance, "rebuild.end",
  4732. network_info=new_nw_info,
  4733. extra_usage_info=mock.ANY)])
  4734. self.assertTrue(mock_image_ref.called)
  4735. self.assertTrue(mock_save.called)
  4736. self.assertTrue(mock_notify_exists.called)
  4737. mock_setup.assert_called_once_with(self.context, instance,
  4738. mock.ANY)
  4739. mock_setup_inst.assert_called_once_with(
  4740. self.context, instance, mock.ANY, mock.ANY,
  4741. provider_mappings=mock.sentinel.mapping)
  4742. mock_get_nw_info.assert_called_once_with(self.context, instance)
  4743. def test_rebuild_default_impl(self):
  4744. def _detach(context, bdms):
  4745. # NOTE(rpodolyaka): check that instance has been powered off by
  4746. # the time we detach block devices, exact calls arguments will be
  4747. # checked below
  4748. self.assertTrue(mock_power_off.called)
  4749. self.assertFalse(mock_destroy.called)
  4750. def _attach(context, instance, bdms):
  4751. return {'block_device_mapping': 'shared_block_storage'}
  4752. def _spawn(context, instance, image_meta, injected_files,
  4753. admin_password, allocations, network_info=None,
  4754. block_device_info=None):
  4755. self.assertEqual(block_device_info['block_device_mapping'],
  4756. 'shared_block_storage')
  4757. with test.nested(
  4758. mock.patch.object(self.compute.driver, 'destroy',
  4759. return_value=None),
  4760. mock.patch.object(self.compute.driver, 'spawn',
  4761. side_effect=_spawn),
  4762. mock.patch.object(objects.Instance, 'save',
  4763. return_value=None),
  4764. mock.patch.object(self.compute, '_power_off_instance',
  4765. return_value=None)
  4766. ) as(
  4767. mock_destroy,
  4768. mock_spawn,
  4769. mock_save,
  4770. mock_power_off
  4771. ):
  4772. instance = fake_instance.fake_instance_obj(self.context)
  4773. instance.migration_context = None
  4774. instance.numa_topology = None
  4775. instance.pci_requests = None
  4776. instance.pci_devices = None
  4777. instance.device_metadata = None
  4778. instance.task_state = task_states.REBUILDING
  4779. instance.save(expected_task_state=[task_states.REBUILDING])
  4780. self.compute._rebuild_default_impl(self.context,
  4781. instance,
  4782. None,
  4783. [],
  4784. admin_password='new_pass',
  4785. bdms=[],
  4786. allocations={},
  4787. detach_block_devices=_detach,
  4788. attach_block_devices=_attach,
  4789. network_info=None,
  4790. evacuate=False,
  4791. block_device_info=None,
  4792. preserve_ephemeral=False)
  4793. self.assertTrue(mock_save.called)
  4794. self.assertTrue(mock_spawn.called)
  4795. mock_destroy.assert_called_once_with(
  4796. self.context, instance,
  4797. network_info=None, block_device_info=None)
  4798. mock_power_off.assert_called_once_with(
  4799. self.context, instance, clean_shutdown=True)
  4800. def test_do_rebuild_instance_check_trusted_certs(self):
  4801. """Tests the scenario that we're rebuilding an instance with
  4802. trusted_certs on a host that does not support trusted certs so
  4803. a BuildAbortException is raised.
  4804. """
  4805. instance = self._trusted_certs_setup_instance()
  4806. instance.system_metadata = {}
  4807. with mock.patch.dict(self.compute.driver.capabilities,
  4808. supports_trusted_certs=False):
  4809. ex = self.assertRaises(
  4810. exception.BuildAbortException,
  4811. self.compute._do_rebuild_instance,
  4812. self.context, instance, instance.image_ref,
  4813. instance.image_meta, injected_files=[], new_pass=None,
  4814. orig_sys_metadata={}, bdms=objects.BlockDeviceMapping(),
  4815. evacuate=False, on_shared_storage=None,
  4816. preserve_ephemeral=False, migration=objects.Migration(),
  4817. request_spec=objects.RequestSpec(),
  4818. allocations=self.allocations,
  4819. request_group_resource_providers_mapping=mock.sentinel.mapping)
  4820. self.assertIn('Trusted image certificates provided on host',
  4821. six.text_type(ex))
  4822. @mock.patch.object(utils, 'last_completed_audit_period',
  4823. return_value=(0, 0))
  4824. @mock.patch.object(time, 'time', side_effect=[10, 20, 21])
  4825. @mock.patch.object(objects.InstanceList, 'get_by_host', return_value=[])
  4826. @mock.patch.object(objects.BandwidthUsage, 'get_by_instance_uuid_and_mac')
  4827. @mock.patch.object(db, 'bw_usage_update')
  4828. def test_poll_bandwidth_usage(self, bw_usage_update, get_by_uuid_mac,
  4829. get_by_host, time, last_completed_audit):
  4830. bw_counters = [{'uuid': uuids.instance, 'mac_address': 'fake-mac',
  4831. 'bw_in': 1, 'bw_out': 2}]
  4832. usage = objects.BandwidthUsage()
  4833. usage.bw_in = 3
  4834. usage.bw_out = 4
  4835. usage.last_ctr_in = 0
  4836. usage.last_ctr_out = 0
  4837. self.flags(bandwidth_poll_interval=1)
  4838. get_by_uuid_mac.return_value = usage
  4839. _time = timeutils.utcnow()
  4840. bw_usage_update.return_value = {'uuid': uuids.instance, 'mac': '',
  4841. 'start_period': _time, 'last_refreshed': _time, 'bw_in': 0,
  4842. 'bw_out': 0, 'last_ctr_in': 0, 'last_ctr_out': 0, 'deleted': 0,
  4843. 'created_at': _time, 'updated_at': _time, 'deleted_at': _time}
  4844. with mock.patch.object(self.compute.driver,
  4845. 'get_all_bw_counters', return_value=bw_counters):
  4846. self.compute._poll_bandwidth_usage(self.context)
  4847. get_by_uuid_mac.assert_called_once_with(self.context,
  4848. uuids.instance, 'fake-mac',
  4849. start_period=0, use_slave=True)
  4850. # NOTE(sdague): bw_usage_update happens at some time in
  4851. # the future, so what last_refreshed is irrelevant.
  4852. bw_usage_update.assert_called_once_with(self.context,
  4853. uuids.instance,
  4854. 'fake-mac', 0, 4, 6, 1, 2,
  4855. last_refreshed=mock.ANY)
  4856. def test_reverts_task_state_instance_not_found(self):
  4857. # Tests that the reverts_task_state decorator in the compute manager
  4858. # will not trace when an InstanceNotFound is raised.
  4859. instance = objects.Instance(uuid=uuids.instance, task_state="FAKE")
  4860. instance_update_mock = mock.Mock(
  4861. side_effect=exception.InstanceNotFound(instance_id=instance.uuid))
  4862. self.compute._instance_update = instance_update_mock