<commit_before>#!/usr/bin/env python
"""Test Juju's log forwarding feature.

Log forwarding allows a controller to forward syslog from all models of a
controller to a syslog host via TCP (using SSL).

"""

from __future__ import print_function

import argparse
import logging
import os
import re
import sys
import socket
import subprocess
from textwrap import dedent

from assess_model_migration import get_bootstrap_managers
import certificates
from jujucharm import local_charm_path
from utility import (
    add_basic_testing_arguments,
    configure_logging,
    get_unit_ipaddress,
    JujuAssertionError,
    temp_dir,
)


__metaclass__ = type


log = logging.getLogger("assess_log_forward")


def assess_log_forward(bs_dummy, bs_rsyslog, upload_tools):
    """Ensure logs are forwarded after forwarding enabled after bootstrapping.

    Given 2 controllers set rsyslog and dummy:
      - setup rsyslog with secure details
      - Enable log forwarding on dummy
      - Ensure intial logs are present in the rsyslog sinks logs

    """
    with bs_rsyslog.booted_context(upload_tools):
        log.info('Bootstrapped rsyslog environment')
        rsyslog = bs_rsyslog.client
        rsyslog_details = deploy_rsyslog(rsyslog)

        update_client_config(bs_dummy.client, rsyslog_details)

        with bs_dummy.existing_booted_context(upload_tools):
            log.info('Bootstrapped dummy environment')
            dummy_client = bs_dummy.client

            unit_machine = 'rsyslog/0'
            remote_script_path = create_check_script_on_unit(
                rsyslog, unit_machine)

            ensure_enabling_log_forwarding_forwards_previous_messages(
                rsyslog, dummy_client, unit_machine, remote_script_path)
            ensure_multiple_models_forward_messages(
                rsyslog, dummy_client, unit_machine, remote_script_path)


def ensure_multiple_models_forward_messages(
        rsyslog, dummy, unit_machine, remote_check_path):
    """Assert that logs of multiple models are forwarded.

    :raises JujuAssertionError: If the expected message does not appear in the
      given timeframe.
    :raises JujuAssertionError: If the log message check fails in an unexpected
      way.
    """
    enable_log_forwarding(dummy)

    model1 = dummy.add_model('{}-{}'.format(dummy.env.environment, 'model1'))

    charm_path = local_charm_path(
        charm='dummy-source', juju_ver=model1.version)

    model1.deploy(charm_path)
    model1.wait_for_started()

    model1_check_string = get_assert_regex(model1.get_model_uuid())

    check_remote_log_for_content(
        rsyslog, unit_machine, model1_check_string, remote_check_path)


def ensure_enabling_log_forwarding_forwards_previous_messages(
        rsyslog, dummy, unit_machine, remote_check_path):
    """Assert that mention of the sources logs appear in the sinks logging.

    Given a rsyslog sink and an output source assert that logging details from
    the source appear in the sinks logging.
    Attempt a check over a period of time (10 seconds).

    :raises JujuAssertionError: If the expected message does not appear in the
      given timeframe.
    :raises JujuAssertionError: If the log message check fails in an unexpected
      way.

    """
    uuid = dummy.get_controller_model_uuid()

    enable_log_forwarding(dummy)
    check_string = get_assert_regex(uuid)

    check_remote_log_for_content(
        rsyslog, unit_machine, check_string, remote_check_path)


def check_remote_log_for_content(
        remote_machine, unit, check_string, script_path):
    try:
        remote_machine.juju(
            'ssh',
            (
                unit,
                'sudo',
                'python',
                script_path,
                check_string,
                '/var/log/syslog'))
        log.info('Check script passed on target machine.')
    except subprocess.CalledProcessError:
        # This is where a failure happened
        raise JujuAssertionError('Forwarded log message never appeared.')


def create_check_script_on_unit(client, unit_machine):
    script_path = os.path.join(os.path.dirname(__file__), 'log_check.py')
    script_dest_path = os.path.join('/tmp', os.path.basename(script_path))
    client.juju(
        'scp',
        (script_path, '{}:{}'.format(unit_machine, script_dest_path)))
    return script_dest_path


def get_assert_regex(raw_uuid, message=None):
    """Create a regex string to check syslog file.

    If message is supplied it is expected to be escaped as needed (i.e. spaces)
    no further massaging will be done to the message string.

    """
    # Maybe over simplified removing the last 8 characters
    uuid = re.escape(raw_uuid)
    short_uuid = re.escape(raw_uuid[:-8])
    date_check = '[A-Z][a-z]{,2}\ +[0-9]+\ +[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}'
    machine = 'machine-0.{}'.format(uuid)
    agent = 'jujud-machine-agent-{}'.format(short_uuid)
    message = message or '.*'

    return '"^{datecheck}\ {machine}\ {agent}\ {message}$"'.format(
        datecheck=date_check,
        machine=machine,
        agent=agent,
        message=message)


def enable_log_forwarding(client):
    client.get_controller_client().set_env_option('logforward-enabled', 'true')


def update_client_config(client, rsyslog_details):
    client.env.update_config({'logforward-enabled': False})
    client.env.update_config(rsyslog_details)<commit_msg>Set logforward-enabled on new model<commit_after>#!/usr/bin/env python
"""Test Juju's log forwarding feature.

Log forwarding allows a controller to forward syslog from all models of a
controller to a syslog host via TCP (using SSL).

"""

from __future__ import print_function

import argparse
import logging
import os
import re
import sys
import socket
import subprocess
from textwrap import dedent

from assess_model_migration import get_bootstrap_managers
import certificates
from jujucharm import local_charm_path
from utility import (
    add_basic_testing_arguments,
    configure_logging,
    get_unit_ipaddress,
    JujuAssertionError,
    temp_dir,
)


__metaclass__ = type


log = logging.getLogger("assess_log_forward")


def assess_log_forward(bs_dummy, bs_rsyslog, upload_tools):
    """Ensure logs are forwarded after forwarding enabled after bootstrapping.

    Given 2 controllers set rsyslog and dummy:
      - setup rsyslog with secure details
      - Enable log forwarding on dummy
      - Ensure intial logs are present in the rsyslog sinks logs

    """
    with bs_rsyslog.booted_context(upload_tools):
        log.info('Bootstrapped rsyslog environment')
        rsyslog = bs_rsyslog.client
        rsyslog_details = deploy_rsyslog(rsyslog)

        update_client_config(bs_dummy.client, rsyslog_details)

        with bs_dummy.existing_booted_context(upload_tools):
            log.info('Bootstrapped dummy environment')
            dummy_client = bs_dummy.client

            unit_machine = 'rsyslog/0'
            remote_script_path = create_check_script_on_unit(
                rsyslog, unit_machine)

            ensure_enabling_log_forwarding_forwards_previous_messages(
                rsyslog, dummy_client, unit_machine, remote_script_path)
            ensure_multiple_models_forward_messages(
                rsyslog, dummy_client, unit_machine, remote_script_path)


def ensure_multiple_models_forward_messages(
        rsyslog, dummy, unit_machine, remote_check_path):
    """Assert that logs of multiple models are forwarded.

    :raises JujuAssertionError: If the expected message does not appear in the
      given timeframe.
    :raises JujuAssertionError: If the log message check fails in an unexpected
      way.
    """
    model1 = dummy.add_model('{}-{}'.format(dummy.env.environment, 'model1'))

    charm_path = local_charm_path(
        charm='dummy-source', juju_ver=model1.version)

    enable_log_forwarding(model1)

    model1.deploy(charm_path)
    model1.wait_for_started()

    model1_check_string = get_assert_regex(model1.get_model_uuid())

    check_remote_log_for_content(
        rsyslog, unit_machine, model1_check_string, remote_check_path)


def ensure_enabling_log_forwarding_forwards_previous_messages(
        rsyslog, dummy, unit_machine, remote_check_path):
    """Assert that mention of the sources logs appear in the sinks logging.

    Given a rsyslog sink and an output source assert that logging details from
    the source appear in the sinks logging.
    Attempt a check over a period of time (10 seconds).

    :raises JujuAssertionError: If the expected message does not appear in the
      given timeframe.
    :raises JujuAssertionError: If the log message check fails in an unexpected
      way.

    """
    uuid = dummy.get_controller_model_uuid()

    enable_log_forwarding(dummy)
    check_string = get_assert_regex(uuid)

    check_remote_log_for_content(
        rsyslog, unit_machine, check_string, remote_check_path)


def check_remote_log_for_content(
        remote_machine, unit, check_string, script_path):
    try:
        remote_machine.juju(
            'ssh',
            (
                unit,
                'sudo',
                'python',
                script_path,
                check_string,
                '/var/log/syslog'))
        log.info('Check script passed on target machine.')
    except subprocess.CalledProcessError:
        # This is where a failure happened
        raise JujuAssertionError('Forwarded log message never appeared.')


def create_check_script_on_unit(client, unit_machine):
    script_path = os.path.join(os.path.dirname(__file__), 'log_check.py')
    script_dest_path = os.path.join('/tmp', os.path.basename(script_path))
    client.juju(
        'scp',
        (script_path, '{}:{}'.format(unit_machine, script_dest_path)))
    return script_dest_path


def get_assert_regex(raw_uuid, message=None):
    """Create a regex string to check syslog file.

    If message is supplied it is expected to be escaped as needed (i.e. spaces)
    no further massaging will be done to the message string.

    """
    # Maybe over simplified removing the last 8 characters
    uuid = re.escape(raw_uuid)
    short_uuid = re.escape(raw_uuid[:-8])
    date_check = '[A-Z][a-z]{,2}\ +[0-9]+\ +[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}'
    machine = 'machine-0.{}'.format(uuid)
    agent = 'jujud-machine-agent-{}'.format(short_uuid)
    message = message or '.*'

    return '"^{datecheck}\ {machine}\ {agent}\ {message}$"'.format(
        datecheck=date_check,
        machine=machine,
        agent=agent,
        message=message)


def enable_log_forwarding(client):
    client.set_env_option('logforward-enabled', 'true')
    client.get_controller_client().set_env_option('logforward-enabled', 'true')


def update_client_config(client, rsyslog_details):
    client.env.update_config({'logforward-enabled': False})
    client.env.update_config(rsyslog_details)
{'fraction_non_alphanumeric': 0.08338690421526311, 'fraction_numerical': 0.005601983653228029, 'mean_word_length': 3.3594875900720576, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '7042348', 'n_tokens_mistral': 3353, 'n_tokens_neox': 3207, 'n_words': 833}