<commit_before>#########
# Copyright (c) 2015 GigaSpaces Technologies Ltd. All rights reserved
#
# 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.

import os
import sys
import logging

from fabric import Connection

from cloudify._compat import reraise
from cloudify.utils import CommandExecutionResponse
from cloudify.utils import setup_logger
from cloudify.exceptions import CommandExecutionException
from cloudify.exceptions import CommandExecutionError

from cloudify_agent.installer import exceptions
from cloudify_agent.api import utils as api_utils

from cloudify_rest_client.utils import is_kerberos_env

DEFAULT_REMOTE_EXECUTION_PORT = 22
RSA_PRIVATE_KEY_PREFIX = '-----BEGIN RSA PRIVATE KEY-----'

COMMON_ENV = {
    'forward_agent': True,
}


class FabricRunner(object):

    def __init__(self,
                 logger=None,
                 host=None,
                 user=None,
                 key=None,
                 port=None,
                 password=<PASSWORD>,
                 validate_connection=True,
                 fabric_env=None,
                 tmpdir=None):

        # logger
        self.logger = logger or setup_logger('fabric_runner')

        # silence paramiko
        logging.getLogger('paramiko.transport').setLevel(logging.WARNING)

        # connection details
        self.port = port or DEFAULT_REMOTE_EXECUTION_PORT
        self.password = password
        self.user = user
        self.host = host
        self.key = key
        self.tmpdir = tmpdir

        # fabric environment
        self.env = self._set_env()
        self.env.update(fabric_env or {})
        self._connection = None

        self._validate_ssh_config()
        if validate_connection:
            self.validate_connection()

    def _validate_ssh_config(self):
        if not self.host:
            raise exceptions.AgentInstallerConfigurationError('Missing host')
        if not self.user:
            raise exceptions.AgentInstallerConfigurationError('Missing user')
        if not is_kerberos_env() and not self.password and not self.key:
            raise exceptions.AgentInstallerConfigurationError(
                'Must specify either key or password')

    def _set_env(self):
        env = {
            'host': self.host,
            'port': self.port,
            'user': self.user,
            'connect_kwargs': {}
        }
        if self.key:
            if self.key.startswith(RSA_PRIVATE_KEY_PREFIX):
                env['connect_kwargs']['key'] = self.key
            else:
                env['connect_kwargs']['key_filename'] = self.key
        if self.password:
            env['connect_kwargs']['password'] = <PASSWORD>
        if is_kerberos_env():
            # For GSSAPI, the fabric env just needs to have
            # gss_auth and gss_kex set to True
            env['gss_auth'] = True
            env['gss_kex'] = True

        env.update(COMMON_ENV)<commit_msg>Fabric runner: pass private key correctly (#611)<commit_after>#########
# Copyright (c) 2015 GigaSpaces Technologies Ltd. All rights reserved
#
# 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.

import os
import sys
import logging

from fabric import Connection
from paramiko import RSAKey, ECDSAKey, Ed25519Key, SSHException

from cloudify._compat import reraise, StringIO
from cloudify.utils import CommandExecutionResponse
from cloudify.utils import setup_logger
from cloudify.exceptions import CommandExecutionException
from cloudify.exceptions import CommandExecutionError

from cloudify_agent.installer import exceptions
from cloudify_agent.api import utils as api_utils

from cloudify_rest_client.utils import is_kerberos_env

DEFAULT_REMOTE_EXECUTION_PORT = 22
PRIVATE_KEY_PREFIX = '-----BEGIN'

COMMON_ENV = {
    'forward_agent': True,
}


class FabricRunner(object):

    def __init__(self,
                 logger=None,
                 host=None,
                 user=None,
                 key=None,
                 port=None,
                 password=<PASSWORD>,
                 validate_connection=True,
                 fabric_env=None,
                 tmpdir=None):

        # logger
        self.logger = logger or setup_logger('fabric_runner')

        # silence paramiko
        logging.getLogger('paramiko.transport').setLevel(logging.WARNING)

        # connection details
        self.port = port or DEFAULT_REMOTE_EXECUTION_PORT
        self.password = password
        self.user = user
        self.host = host
        self.key = key
        self.tmpdir = tmpdir

        # fabric environment
        self.env = self._set_env()
        self.env.update(fabric_env or {})
        self._connection = None

        self._validate_ssh_config()
        if validate_connection:
            self.validate_connection()

    def _validate_ssh_config(self):
        if not self.host:
            raise exceptions.AgentInstallerConfigurationError('Missing host')
        if not self.user:
            raise exceptions.AgentInstallerConfigurationError('Missing user')
        if not is_kerberos_env() and not self.password and not self.key:
            raise exceptions.AgentInstallerConfigurationError(
                'Must specify either key or password')

    def _load_private_key(self, key_contents):
        """Load the private key and return a paramiko PKey subclass.

        :param key_contents: the contents of a keyfile, as a string starting
            with "---BEGIN"
        :return: A paramiko PKey subclass - RSA, ECDSA or Ed25519
        """
        for cls in (RSAKey, ECDSAKey, Ed25519Key):
            try:
                return cls.from_private_key(StringIO(key_contents))
            except SSHException:
                continue
        raise exceptions.AgentInstallerConfigurationError(
            'Could not load the private key as an '
            'RSA, ECDSA, or Ed25519 key'
        )

    def _set_env(self):
        env = {
            'host': self.host,
            'port': self.port,
            'user': self.user,
            'connect_kwargs': {}
        }
        if self.key:
            if self.key.startswith(PRIVATE_KEY_PREFIX):
                env['connect_kwargs']['pkey'] = \
                    self._load_private_key(self.key)
            else:
                env['connect_kwargs']['key_filename'] = self.key
        if self.password:
            env['connect_kwargs']['password'] = <PASSWORD>
        if is_kerberos_env():
            # For GSSAPI, the fabric env just needs to have
            # gss_auth and gss_kex set to True
            env['gss_auth'] = True
            env['gss_kex'] = True

        env.update(COMMON_ENV)
{'fraction_non_alphanumeric': 0.07889209766342872, 'fraction_numerical': 0.005644526122341822, 'mean_word_length': 2.3460693895476505, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '247930', 'n_tokens_mistral': 2079, 'n_tokens_neox': 1982, 'n_words': 606}