def get_state(self): """Return the current mount state. _HostMountStateManager will not permit a new state object to be created while any previous state object is still in use. get_state will raise HypervisorUnavailable if the libvirt connection is currently down. :rtype: _HostMountState """ # We hold the instance lock here so that if a _HostMountState is # currently initialising we'll wait for it to complete rather than # fail. with self.cond: state = self.state if state is None: raise exception.HypervisorUnavailable() self.use_count += 1 try: LOG.debug('Got _HostMountState generation %(gen)i', {'gen': state.generation}) yield state finally: with self.cond: self.use_count -= 1 self.cond.notify_all()
def get_session(self, host=None): """Returns a connection to the LXD hypervisor This method should be used to create a connection to the LXD hypervisor via the pylxd API call. :param host: host is the LXD daemon to connect to :return: pylxd object """ try: if host: return api.API(host=host) else: return api.API() except Exception as ex: # notify the compute host that the connection failed # via an rpc call LOG.exception(_LE('Connection to LXD failed')) payload = dict(ip=CONF.host, method='_connect', reason=ex) rpc.get_notifier('compute').error(nova_context.get_admin_context, 'compute.nova_lxd.error', payload) raise exception.HypervisorUnavailable(host=CONF.host)
def _connect(uri, read_only): auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_ECHOPROMPT, libvirt.VIR_CRED_REALM, libvirt.VIR_CRED_PASSPHRASE, libvirt.VIR_CRED_NOECHOPROMPT, libvirt.VIR_CRED_EXTERNAL], LibvirtDriver._connect_auth_cb, None] try: flags = 0 if read_only: flags = libvirt.VIR_CONNECT_RO # tpool.proxy_call creates a native thread. Due to limitations # with eventlet locking we cannot use the logging API inside # the called function. return tpool.proxy_call( (libvirt.virDomain, libvirt.virConnect), libvirt.openAuth, uri, auth, flags) except libvirt.libvirtError as ex: LOG.exception(_LE("Connection to libvirt failed: %s"), ex) payload = dict(ip=LibvirtDriver.get_host_ip_addr(), method='_connect', reason=ex) rpc.get_notifier('compute').error(nova_context.get_admin_context(), 'compute.libvirt.error', payload) raise exception.HypervisorUnavailable(host=CONF.host)
def test_finish_snapshot_based_resize_at_dest_spawn_fails(self): """Negative test where the driver spawn fails on the dest host during finish_snapshot_based_resize_at_dest which triggers a rollback of the instance data in the target cell. Furthermore, the test will hard reboot the server in the source cell to recover it from ERROR status. """ # Create a volume-backed server. This is more interesting for rollback # testing to make sure the volume attachments in the target cell were # cleaned up on failure. flavors = self.api.get_flavors() server = self._create_server(flavors[0], volume_backed=True) # Now mock out the spawn method on the destination host to fail # during _finish_snapshot_based_resize_at_dest_spawn and then resize # the server. error = exception.HypervisorUnavailable(host='host2') with mock.patch.object(self.computes['host2'].driver, 'spawn', side_effect=error): flavor2 = flavors[1]['id'] body = {'resize': {'flavorRef': flavor2}} self.api.post_server_action(server['id'], body) # The server should go to ERROR state with a fault record and # the API should still be showing the server from the source cell # because the instance mapping was not updated. server = self._wait_for_server_parameter( self.admin_api, server, { 'status': 'ERROR', 'OS-EXT-STS:task_state': None }) # The migration should be in 'error' status. self._wait_for_migration_status(server, ['error']) # Assert a fault was recorded. self.assertIn('fault', server) self.assertIn('Connection to the hypervisor is broken', server['fault']['message']) # The instance in the target cell DB should have been hard-deleted. self._assert_instance_not_in_cell('cell2', server['id']) # Assert that there is only one volume attachment for the server, i.e. # the one in the target cell was deleted. self.assertEqual(1, self._count_volume_attachments(server['id']), self.cinder.volume_to_attachment) # Assert that migration-based allocations were properly reverted. self._assert_allocation_revert_on_fail(server) # Now hard reboot the server in the source cell and it should go back # to ACTIVE. self.api.post_server_action(server['id'], {'reboot': {'type': 'HARD'}}) self._wait_for_state_change(self.admin_api, server, 'ACTIVE') # Now retry the resize without the fault in the target host to make # sure things are OK (no duplicate entry errors in the target DB). self.api.post_server_action(server['id'], body) self._wait_for_state_change(self.admin_api, server, 'VERIFY_RESIZE')
def get_available_nodes(self, refresh=True): """Returns nodenames of all nodes managed by the compute service.""" LOG.debug(_("get_available_nodes")) node_list = self._get_available_nodes(refresh) # node_list is None only when exception is throwed. if node_list is None: raise nova_exc.HypervisorUnavailable(host='fc-nova-compute') else: return node_list
def test_prep_snapshot_based_resize_at_source_destroy_fails(self): """Negative test where prep_snapshot_based_resize_at_source fails destroying the guest for the non-volume backed server and asserts resources are rolled back. """ # Create a non-volume backed server for the snapshot flow. flavors = self.api.get_flavors() flavor1 = flavors[0] server = self._create_server(flavor1) # Now mock out the snapshot method on the source host to fail # during _prep_snapshot_based_resize_at_source and then resize # the server. source_host = server['OS-EXT-SRV-ATTR:host'] error = exception.HypervisorUnavailable(host=source_host) with mock.patch.object(self.computes[source_host].driver, 'destroy', side_effect=error): flavor2 = flavors[1]['id'] body = {'resize': {'flavorRef': flavor2}} self.api.post_server_action(server['id'], body) # The server should go to ERROR state with a fault record and # the API should still be showing the server from the source cell # because the instance mapping was not updated. server = self._wait_for_server_parameter( self.admin_api, server, { 'status': 'ERROR', 'OS-EXT-STS:task_state': None }) # The migration should be in 'error' status. self._wait_for_migration_status(server, ['error']) # Assert a fault was recorded. self.assertIn('fault', server) self.assertIn('Connection to the hypervisor is broken', server['fault']['message']) # The instance in the target cell DB should have been hard-deleted. self._assert_instance_not_in_cell('cell2', server['id']) # Assert that migration-based allocations were properly reverted. self._assert_allocation_revert_on_fail(server) # Now hard reboot the server in the source cell and it should go back # to ACTIVE. self.api.post_server_action(server['id'], {'reboot': {'type': 'HARD'}}) self._wait_for_state_change(self.admin_api, server, 'ACTIVE') # Now retry the resize without the fault in the target host to make # sure things are OK (no duplicate entry errors in the target DB). self.api.post_server_action(server['id'], body) self._wait_for_state_change(self.admin_api, server, 'VERIFY_RESIZE')
def get_connection(self): """Returns a connection to the hypervisor This method should be used to create and return a well configured connection to the hypervisor. :returns: a libvirt.virConnect object """ try: conn = self._get_connection() except libvirt.libvirtError as ex: LOG.exception(_("Connection to libvirt failed: %s"), ex) payload = dict(ip=CONF.my_ip, method='_connect', reason=ex) rpc.get_notifier('compute').error(nova_context.get_admin_context(), 'compute.libvirt.error', payload) raise exception.HypervisorUnavailable(host=CONF.host) return conn
def test_migrate_live_hypervisor_unavailable(self): self._test_migrate_live_failed_with_exception( exception.HypervisorUnavailable(host=""))