def _test_it(): with task_manager.TaskManager(self.context, 'node-id') as task: task.spawn_after(spawn_mock, 1, 2, foo='bar', cat='meow') task.release_resources = task_release_mock raise exception.IronicException('foo')
def _command(self, node, method, params, wait=False): """Sends command to agent. :param node: A Node object. :param method: A string represents the command to be executed by agent. :param params: A dictionary containing params used to form the request body. :param wait: True to wait for the command to finish executing, False otherwise. :raises: IronicException when failed to issue the request or there was a malformed response from the agent. :raises: AgentAPIError when agent failed to execute specified command. :returns: A dict containing command result from agent, see get_commands_status for a sample. """ url = self._get_command_url(node) body = self._get_command_body(method, params) request_params = {'wait': str(wait).lower()} agent_token = node.driver_internal_info.get('agent_secret_token') if agent_token: request_params['agent_token'] = agent_token LOG.debug('Executing agent command %(method)s for node %(node)s', { 'node': node.uuid, 'method': method }) try: response = self.session.post(url, params=request_params, data=body, timeout=CONF.agent.command_timeout) except (requests.ConnectionError, requests.Timeout) as e: msg = (_('Failed to connect to the agent running on node %(node)s ' 'for invoking command %(method)s. Error: %(error)s') % { 'node': node.uuid, 'method': method, 'error': e }) LOG.error(msg) raise exception.AgentConnectionFailed(reason=msg) except requests.RequestException as e: msg = (_('Error invoking agent command %(method)s for node ' '%(node)s. Error: %(error)s') % { 'method': method, 'node': node.uuid, 'error': e }) LOG.error(msg) raise exception.IronicException(msg) # TODO(russellhaering): real error handling try: result = response.json() except ValueError: msg = _('Unable to decode response as JSON.\n' 'Request URL: %(url)s\nRequest body: "%(body)s"\n' 'Response status code: %(code)s\n' 'Response: "%(response)s"') % ({ 'response': response.text, 'body': body, 'url': url, 'code': response.status_code }) LOG.error(msg) raise exception.IronicException(msg) LOG.debug( 'Agent command %(method)s for node %(node)s returned ' 'result %(res)s, error %(error)s, HTTP status code %(code)d', { 'node': node.uuid, 'method': method, 'res': result.get('command_result'), 'error': result.get('command_error'), 'code': response.status_code }) if response.status_code >= http_client.BAD_REQUEST: LOG.error( 'Agent command %(method)s for node %(node)s failed. ' 'Expected 2xx HTTP status code, got %(code)d.', { 'method': method, 'node': node.uuid, 'code': response.status_code }) raise exception.AgentAPIError(node=node.uuid, status=response.status_code, error=result.get('faultstring')) return result
def test_do_next_deploy_step_execute_ironic_exception(self): self._do_next_deploy_step_execute_fail( exception.IronicException('foo'), False)
def ironicexception(self): raise exception.IronicException("Fake!")
def test_collect_ramdisk_logs_storage_command_fail( self, mock_collect, mock_store): mock_collect.side_effect = exception.IronicException('boom') self.assertIsNone(driver_utils.collect_ramdisk_logs(self.node)) self.assertFalse(mock_store.called)
def fail(self, context, message): assert isinstance(context, ir_ctx.RequestContext) raise exception.IronicException(message)
def execute(*cmd, **kwargs): """Helper method to execute command with optional retry. If you add a run_as_root=True command, don't forget to add the corresponding filter to etc/nova/rootwrap.d ! :param cmd: Passed to subprocess.Popen. :param process_input: Send to opened process. :param check_exit_code: Single bool, int, or list of allowed exit codes. Defaults to [0]. Raise exception.ProcessExecutionError unless program exits with one of these code. :param delay_on_retry: True | False. Defaults to True. If set to True, wait a short amount of time before retrying. :param attempts: How many times to retry cmd. :param run_as_root: True | False. Defaults to False. If set to True, the command is run with rootwrap. :raises exception.IronicException: on receiving unknown arguments :raises exception.ProcessExecutionError: :returns: a tuple, (stdout, stderr) from the spawned process, or None if the command fails. """ process_input = kwargs.pop('process_input', None) check_exit_code = kwargs.pop('check_exit_code', [0]) ignore_exit_code = False if isinstance(check_exit_code, bool): ignore_exit_code = not check_exit_code check_exit_code = [0] elif isinstance(check_exit_code, int): check_exit_code = [check_exit_code] delay_on_retry = kwargs.pop('delay_on_retry', True) attempts = kwargs.pop('attempts', 1) run_as_root = kwargs.pop('run_as_root', False) shell = kwargs.pop('shell', False) if len(kwargs): raise exception.IronicException( _('Got unknown keyword args ' 'to utils.execute: %r') % kwargs) if run_as_root and os.geteuid() != 0: cmd = ['sudo', 'nova-rootwrap', CONF.rootwrap_config] + list(cmd) cmd = map(str, cmd) while attempts > 0: attempts -= 1 try: LOG.debug(_('Running cmd (subprocess): %s'), ' '.join(cmd)) _PIPE = subprocess.PIPE # pylint: disable=E1101 if os.name == 'nt': preexec_fn = None close_fds = False else: preexec_fn = _subprocess_setup close_fds = True obj = subprocess.Popen(cmd, stdin=_PIPE, stdout=_PIPE, stderr=_PIPE, close_fds=close_fds, preexec_fn=preexec_fn, shell=shell) result = None if process_input is not None: result = obj.communicate(process_input) else: result = obj.communicate() obj.stdin.close() # pylint: disable=E1101 _returncode = obj.returncode # pylint: disable=E1101 LOG.debug(_('Result was %s') % _returncode) if not ignore_exit_code and _returncode not in check_exit_code: (stdout, stderr) = result raise exception.ProcessExecutionError(exit_code=_returncode, stdout=stdout, stderr=stderr, cmd=' '.join(cmd)) return result except exception.ProcessExecutionError: if not attempts: raise else: LOG.debug(_('%r failed. Retrying.'), cmd) if delay_on_retry: greenthread.sleep(random.randint(20, 200) / 100.0) finally: # NOTE(termie): this appears to be necessary to let the subprocess # call clean something up in between calls, without # it two execute calls in a row hangs the second one greenthread.sleep(0)
def _command(self, node, method, params, wait=False, command_timeout_factor=1): """Sends command to agent. :param node: A Node object. :param method: A string represents the command to be executed by agent. :param params: A dictionary containing params used to form the request body. :param wait: True to wait for the command to finish executing, False otherwise. :param command_timeout_factor: An integer, default 1, by which to multiply the [agent]command_timeout value. This is intended for use with extremely long running commands to the agent ramdisk where a general timeout value should not be extended in all cases. :raises: IronicException when failed to issue the request or there was a malformed response from the agent. :raises: AgentAPIError when agent failed to execute specified command. :returns: A dict containing command result from agent, see get_commands_status for a sample. """ url = self._get_command_url(node) body = self._get_command_body(method, params) request_params = {'wait': str(wait).lower()} agent_token = node.driver_internal_info.get('agent_secret_token') if agent_token: request_params['agent_token'] = agent_token LOG.debug('Executing agent command %(method)s for node %(node)s', { 'node': node.uuid, 'method': method }) try: response = self.session.post(url, params=request_params, data=body, timeout=CONF.agent.command_timeout * command_timeout_factor) except (requests.ConnectionError, requests.Timeout) as e: msg = (_('Failed to connect to the agent running on node %(node)s ' 'for invoking command %(method)s. Error: %(error)s') % { 'node': node.uuid, 'method': method, 'error': e }) LOG.error(msg) raise exception.AgentConnectionFailed(reason=msg) except requests.RequestException as e: msg = (_('Error invoking agent command %(method)s for node ' '%(node)s. Error: %(error)s') % { 'method': method, 'node': node.uuid, 'error': e }) LOG.error(msg) raise exception.IronicException(msg) # TODO(russellhaering): real error handling try: result = response.json() except ValueError: msg = _('Unable to decode response as JSON.\n' 'Request URL: %(url)s\nRequest body: "%(body)s"\n' 'Response status code: %(code)s\n' 'Response: "%(response)s"') % ({ 'response': response.text, 'body': body, 'url': url, 'code': response.status_code }) LOG.error(msg) raise exception.IronicException(msg) error = result.get('command_error') exc_type = None if error: # if an error, we should see if a type field exists. This type # field may signal an exception that is compatability based. exc_type = error.get('type') LOG.debug( 'Agent command %(method)s for node %(node)s returned ' 'result %(res)s, error %(error)s, HTTP status code %(code)d', { 'node': node.uuid, 'method': method, 'res': result.get('command_result'), 'error': error, 'code': response.status_code }) if response.status_code >= http_client.BAD_REQUEST: LOG.error( 'Agent command %(method)s for node %(node)s failed. ' 'Expected 2xx HTTP status code, got %(code)d.', { 'method': method, 'node': node.uuid, 'code': response.status_code }) raise exception.AgentAPIError(node=node.uuid, status=response.status_code, error=result.get('faultstring')) if exc_type == 'TypeError': LOG.error( 'Agent command %(method)s for node %(node)s failed. ' 'Internal %(exc_type)s error detected: Error %(error)s', { 'method': method, 'node': node.uuid, 'exc_type': exc_type, 'error': error }) raise exception.AgentAPIError(node=node.uuid, status=error.get('code'), error=result.get('faultstring')) return result
def _command(self, node, method, params, wait=False, poll=False): """Sends command to agent. :param node: A Node object. :param method: A string represents the command to be executed by agent. :param params: A dictionary containing params used to form the request body. :param wait: True to wait for the command to finish executing, False otherwise. :param poll: Whether to poll the command until completion. Provides a better alternative to `wait` for long-running commands. :raises: IronicException when failed to issue the request or there was a malformed response from the agent. :raises: AgentAPIError when agent failed to execute specified command. :raises: AgentInProgress when the command fails to execute as the agent is presently executing the prior command. :returns: A dict containing command result from agent, see get_commands_status for a sample. """ assert not (wait and poll) url = self._get_command_url(node) body = self._get_command_body(method, params) request_params = {'wait': str(wait).lower()} agent_token = node.driver_internal_info.get('agent_secret_token') if agent_token: request_params['agent_token'] = agent_token LOG.debug('Executing agent command %(method)s for node %(node)s', { 'node': node.uuid, 'method': method }) try: response = self.session.post(url, params=request_params, data=body, verify=self._get_verify(node), timeout=CONF.agent.command_timeout) except (requests.ConnectionError, requests.Timeout) as e: msg = (_('Failed to connect to the agent running on node %(node)s ' 'for invoking command %(method)s. Error: %(error)s') % { 'node': node.uuid, 'method': method, 'error': e }) LOG.error(msg) raise exception.AgentConnectionFailed(reason=msg) except requests.RequestException as e: msg = (_('Error invoking agent command %(method)s for node ' '%(node)s. Error: %(error)s') % { 'method': method, 'node': node.uuid, 'error': e }) LOG.error(msg) raise exception.IronicException(msg) # TODO(russellhaering): real error handling try: result = response.json() except ValueError: msg = _('Unable to decode response as JSON.\n' 'Request URL: %(url)s\nRequest body: "%(body)s"\n' 'Response status code: %(code)s\n' 'Response: "%(response)s"') % ({ 'response': response.text, 'body': body, 'url': url, 'code': response.status_code }) LOG.error(msg) raise exception.IronicException(msg) error = result.get('command_error') LOG.debug( 'Agent command %(method)s for node %(node)s returned ' 'result %(res)s, error %(error)s, HTTP status code %(code)d', { 'node': node.uuid, 'method': method, 'res': result.get('command_result'), 'error': error, 'code': response.status_code }) if response.status_code >= http_client.BAD_REQUEST: faultstring = result.get('faultstring') if 'agent_token' in faultstring: LOG.error( 'Agent command %(method)s for node %(node)s ' 'failed. Expected 2xx HTTP status code, got ' '%(code)d. Error suggests an older ramdisk ' 'which does not support ``agent_token``. ' 'This is a fatal error.', { 'method': method, 'node': node.uuid, 'code': response.status_code }) else: LOG.error( 'Agent command %(method)s for node %(node)s failed. ' 'Expected 2xx HTTP status code, got %(code)d.', { 'method': method, 'node': node.uuid, 'code': response.status_code }) if (response.status_code == http_client.CONFLICT or 'agent is busy' in faultstring.lower()): # HTTP 409 check as an explicit check of if the agent # is already busy. # NOTE(TheJulia): The agent sends upper case A as of # late victoria, but lower case the entire message # for compatability with pre-late victoria agents # which returns HTTP 409. raise exception.AgentInProgress(node=node.uuid, command=method, error=faultstring) raise exception.AgentAPIError(node=node.uuid, status=response.status_code, error=faultstring) self._raise_if_typeerror(result, node, method) if poll: result = self._wait_for_command(node, method) return result
def _lock_again(u): with task_manager.acquire(u): raise exception.IronicException("Acquired lock twice.")
def test___str__non_string(self): exc = exception.IronicException(42) self.assertEqual("42", exc.__str__()) self.assertEqual(u"42", exc.__unicode__())