Exemple #1
0
    def _command(self, node, method, params, wait=False):
        url = self._get_command_url(node)
        body = self._get_command_body(method, params)
        request_params = {'wait': str(wait).lower()}
        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)
        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
Exemple #2
0
 def _raise_if_typeerror(self, result, node, method):
     error = result.get('command_error')
     if error and error.get('type') == 'TypeError':
         LOG.error(
             'Agent command %(method)s for node %(node)s failed. '
             'Internal TypeError detected: Error %(error)s', {
                 'method': method,
                 'node': node.uuid,
                 'error': error
             })
         raise exception.AgentAPIError(node=node.uuid,
                                       status=error.get('code'),
                                       error=get_command_error(result))
 def test_finalize_rescue_fallback_restricted(self):
     self.config(require_rescue_password_hashed=True, group="conductor")
     self.client._command = mock.MagicMock(spec_set=[])
     self.node.instance_info['rescue_password'] = '******'
     self.node.instance_info['hashed_rescue_password'] = '******'
     self.client._command.side_effect = exception.AgentAPIError('blah')
     self.assertRaises(exception.InstanceRescueFailure,
                       self.client.finalize_rescue, self.node)
     self.client._command.assert_has_calls([
         mock.call(node=mock.ANY,
                   method='rescue.finalize_rescue',
                   params={
                       'rescue_password': '******',
                       'hashed': True
                   })
     ])
Exemple #4
0
    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()}
        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
Exemple #5
0
    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.
        :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
                    })
            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
Exemple #6
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