<commit_before># Copyright 2014 Rackspace, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from http import client as http_client

from ironic_lib import metrics_utils
from oslo_log import log
from oslo_serialization import jsonutils
import requests
import retrying

from ironic.common import exception
from ironic.common.i18n import _
from ironic.conf import CONF

LOG = log.getLogger(__name__)

METRICS = metrics_utils.get_metrics_logger(__name__)

DEFAULT_IPA_PORTAL_PORT = 3260


class AgentClient(object):
    """Client for interacting with nodes via a REST API."""
    @METRICS.timer('AgentClient.__init__')
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({'Content-Type': 'application/json'})

    def _get_command_url(self, node):
        """Get URL endpoint for agent command request"""
        agent_url = node.driver_internal_info.get('agent_url')
        if not agent_url:
            raise exception.IronicException(_('Agent driver requires '
                                              'agent_url in '
                                              'driver_internal_info'))
        return ('%(agent_url)s/%(api_version)s/commands/' %
                {'agent_url': agent_url,
                 'api_version': CONF.agent.agent_api_version})

    def _get_command_body(self, method, params):
        """Generate command body from method and params"""
        return jsonutils.dumps({
            'name': method,
            'params': params,
        })

    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=result.get('faultstring'))

    @METRICS.timer('AgentClient._wait_for_command')
    @retrying.retry(
        retry_on_exception=(
            lambda e: isinstance(e, exception.AgentCommandTimeout)),
        stop_max_attempt_number=CONF.agent.command_wait_attempts,
        wait_fixed=CONF.agent.command_wait_interval * 1000)
    def _wait_for_command(self, node, method):
        """Wait for a command to complete.

        :param node: A Node object.
        :param method: A string represents the command executed by agent.
        """
        try:
            method = method.split('.', 1)[1]
        except IndexError:
            pass

        commands = self.get_commands_status(node)
        try:
            result = next(c for c in reversed(commands)
                          if c.get('command_name') == method)
        except StopIteration:
            LOG.debug('Command %(cmd)s is not in the executing commands list '
                      'for node %(node)s',
                      {'cmd': method, 'node': node.uuid})
            raise exception.AgentCommandTimeout(command=method, node=node.uuid)

        if result.get('command_status') == 'RUNNING':
            LOG.debug('Command %(cmd)s has not finished yet for node %(node)s',
                      {'cmd': method, 'node': node.uuid})
            raise exception.AgentCommandTimeout(command=method, node=node.uuid)
        else:
            LOG.debug('Command %(cmd)s has finished for node %(node)s with '
                      'result %(result)s',
                      {'cmd': method, 'node': node.uuid, 'result': result})
            self._raise_if_typeerror(result, node, method)
            return result<commit_msg>Trivial: clarify usage of AgentCommandTimeout in _wait_for_command<commit_after># Copyright 2014 Rackspace, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from http import client as http_client

from ironic_lib import metrics_utils
from oslo_log import log
from oslo_serialization import jsonutils
import requests
import retrying

from ironic.common import exception
from ironic.common.i18n import _
from ironic.conf import CONF

LOG = log.getLogger(__name__)

METRICS = metrics_utils.get_metrics_logger(__name__)

DEFAULT_IPA_PORTAL_PORT = 3260


class AgentClient(object):
    """Client for interacting with nodes via a REST API."""
    @METRICS.timer('AgentClient.__init__')
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({'Content-Type': 'application/json'})

    def _get_command_url(self, node):
        """Get URL endpoint for agent command request"""
        agent_url = node.driver_internal_info.get('agent_url')
        if not agent_url:
            raise exception.IronicException(_('Agent driver requires '
                                              'agent_url in '
                                              'driver_internal_info'))
        return ('%(agent_url)s/%(api_version)s/commands/' %
                {'agent_url': agent_url,
                 'api_version': CONF.agent.agent_api_version})

    def _get_command_body(self, method, params):
        """Generate command body from method and params"""
        return jsonutils.dumps({
            'name': method,
            'params': params,
        })

    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=result.get('faultstring'))

    @METRICS.timer('AgentClient._wait_for_command')
    @retrying.retry(
        retry_on_exception=(
            lambda e: isinstance(e, exception.AgentCommandTimeout)),
        stop_max_attempt_number=CONF.agent.command_wait_attempts,
        wait_fixed=CONF.agent.command_wait_interval * 1000)
    def _wait_for_command(self, node, method):
        """Wait for a command to complete.

        :param node: A Node object.
        :param method: A string represents the command executed by agent.
        :raises: AgentCommandTimeout if timeout is reached.
        """
        try:
            method = method.split('.', 1)[1]
        except IndexError:
            pass

        # NOTE(dtantsur): this function uses AgentCommandTimeout on every
        # failure, but unless the timeout is reached, the exception is caught
        # and retried by the @retry decorator above.

        commands = self.get_commands_status(node)
        try:
            result = next(c for c in reversed(commands)
                          if c.get('command_name') == method)
        except StopIteration:
            LOG.debug('Command %(cmd)s is not in the executing commands list '
                      'for node %(node)s',
                      {'cmd': method, 'node': node.uuid})
            raise exception.AgentCommandTimeout(command=method, node=node.uuid)

        if result.get('command_status') == 'RUNNING':
            LOG.debug('Command %(cmd)s has not finished yet for node %(node)s',
                      {'cmd': method, 'node': node.uuid})
            raise exception.AgentCommandTimeout(command=method, node=node.uuid)
        else:
            LOG.debug('Command %(cmd)s has finished for node %(node)s with '
                      'result %(result)s',
                      {'cmd': method, 'node': node.uuid, 'result': result})
            self._raise_if_typeerror(result, node, method)
            return result
{'fraction_non_alphanumeric': 0.09851994124957632, 'fraction_numerical': 0.004519263360072308, 'mean_word_length': 2.3657794676806083, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2350441', 'n_tokens_mistral': 2357, 'n_tokens_neox': 2279, 'n_words': 706}