#!/usr/bin/env python
"""
# 
#   Copyright 2016 RIFT.IO 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.
#

@file test_scaling.py
@author Paul Laidler (Paul.Laidler@riftio.com)
@date 07/13/2016
@brief Pingpong scaling system test
"""

import os
import pytest
import subprocess
import sys
import time
import uuid

import rift.auto.mano
import rift.auto.session
import rift.auto.descriptor

from gi.repository import (
    NsrYang,
    NsdYang,
    VnfrYang,
    RwNsrYang,
    RwNsdYang,
    RwVnfrYang,
)

@pytest.mark.setup('pingpong_nsd')
@pytest.mark.depends('launchpad')
class TestSetupPingpongNsd(object):
    def test_onboard(self, mgmt_session, descriptors):
        for descriptor in descriptors:
            rift.auto.descriptor.onboard(mgmt_session.host, descriptor)

    def test_install_sar(self, mgmt_session):
        install_cmd = 'ssh {mgmt_ip} -q -n -o BatchMode=yes -o StrictHostKeyChecking=no -- sudo yum install sysstat --assumeyes'.format(
                mgmt_ip=mgmt_session.host,
        )
        subprocess.check_call(install_cmd, shell=True)


@pytest.fixture(scope='function', params=[5,10,15,20,25])
def service_count(request):
    '''Fixture representing the number of services to test'''
    return request.param

@pytest.mark.depends('pingpong_nsd')
class TestScaling(object):
    @pytest.mark.preserve_fixture_order
    def test_scaling(self, mgmt_session, cloud_account_name, service_count):

        def start_services(mgmt_session, desired_service_count, max_attempts=3): 
            catalog = mgmt_session.proxy(NsdYang).get_config('/nsd-catalog')
            nsd = catalog.nsd[0]
            
            nsr_path = "/ns-instance-config"
            nsr = mgmt_session.proxy(RwNsrYang).get_config(nsr_path)
            service_count = len(nsr.nsr)

            attempts = 0
            while attempts < max_attempts and service_count < desired_service_count:
                attempts += 1

                for count in range(service_count, desired_service_count):
                    nsr = rift.auto.descriptor.create_nsr(
                        cloud_account_name,
                        "pingpong_%s" % str(uuid.uuid4().hex[:10]),
                        nsd.id)
                    mgmt_session.proxy(RwNsrYang).create_config('/ns-instance-config/nsr', nsr)

                ns_instance_opdata = mgmt_session.proxy(RwNsrYang).get('/ns-instance-opdata')
                for nsr in ns_instance_opdata.nsr:
                    try:
                        xpath = "/ns-instance-opdata/nsr[ns-instance-config-ref='{}']/operational-status".format(nsr.ns_instance_config_ref)
                        mgmt_session.proxy(RwNsrYang).wait_for(xpath, "running", fail_on=['failed'], timeout=180)
                        xpath = "/ns-instance-opdata/nsr[ns-instance-config-ref='{}']/config-status".format(nsr.ns_instance_config_ref)
                        mgmt_session.proxy(RwNsrYang).wait_for(xpath, "configured", fail_on=['failed'], timeout=450)
                        service_count += 1
                    except rift.auto.session.ProxyWaitForError:
                        mgmt_session.proxy(RwNsrYang).delete_config("/ns-instance-config/nsr[id='{}']".format(nsr.ns_instance_config_ref))

        def monitor_launchpad_performance(service_count, interval=30, samples=1):
            sar_cmd = "ssh {mgmt_ip} -q -n -o BatchMode=yes -o StrictHostKeyChecking=no -- sar -A {interval} {samples}".format(
                    mgmt_ip=mgmt_session.host,
                    interval=interval,
                    samples=samples
            )
            output = subprocess.check_output(sar_cmd, shell=True, stderr=subprocess.STDOUT)
            outfile = '{rift_artifacts}/scaling_{task_id}.log'.format(
                    rift_artifacts=os.environ.get('RIFT_ARTIFACTS'),
                    task_id=os.environ.get('AUTO_TASK_ID')
            )
            with open(outfile, 'a') as fh:
                message = '''
== SCALING RESULTS : {service_count} Network Services ==
{output}               
                '''.format(service_count=service_count, output=output.decode())
                fh.write(message)

        start_services(mgmt_session, service_count)
        monitor_launchpad_performance(service_count, interval=30, samples=1)

@pytest.mark.depends('pingpong_nsd')
@pytest.mark.teardown('pingpong_nsd')
class TestTeardownPingpongNsr(object):
    def test_teardown_nsr(self, mgmt_session):

        ns_instance_config = mgmt_session.proxy(RwNsrYang).get_config('/ns-instance-config')
        for nsr in ns_instance_config.nsr:
            mgmt_session.proxy(RwNsrYang).delete_config("/ns-instance-config/nsr[id='{}']".format(nsr.id))

        time.sleep(60)
        vnfr_catalog = mgmt_session.proxy(RwVnfrYang).get('/vnfr-catalog')
        assert vnfr_catalog is None or len(vnfr_catalog.vnfr) == 0

    def test_generate_plots(self):
        plot_commands = [
            ('python {rift_install}/usr/rift/systemtest/util/sarplot.py '
                    '--plot "{rift_artifacts}/scaling_cpu_{task_id}.png" '
                    '--title "CPU Utilization by network service count" '
                    '--keys CPU '
                    '--fields %usr,%idle,%sys '
                    '--key-filter CPU:all '
                    '--ylabel "CPU Utilization %" '
                    '--xlabel "Network Service Count" '
                    '--xticklabels "5,10,15,20,25" < {rift_artifacts}/scaling_{task_id}.log'
            ),
            ('python {rift_install}/usr/rift/systemtest/util/sarplot.py '
                    '--plot "{rift_artifacts}/scaling_mem_{task_id}.png" '
                    '--title "Memory Utilization by network service count" '
                    '--fields kbmemfree,kbmemused,kbbuffers,kbcached,kbcommit,kbactive,kbinact,kbdirty '
                    '--ylabel "Memory Utilization" '
                    '--xlabel "Network Service Count" '
                    '--xticklabels "5,10,15,20,25" < {rift_artifacts}/scaling_{task_id}.log'
            ),
            ('python {rift_install}/usr/rift/systemtest/util/sarplot.py '
                    '--plot "{rift_artifacts}/scaling_mempct_{task_id}.png" '
                    '--title "Memory Utilization by network service count" '
                    '--fields %memused,%commit '
                    '--ylabel "Memory Utilization %" '
                    '--xlabel "Network Service Count" '
                    '--xticklabels "5,10,15,20,25" < {rift_artifacts}/scaling_{task_id}.log'
            ),
            ('python {rift_install}/usr/rift/systemtest/util/sarplot.py '
                    '--plot "{rift_artifacts}/scaling_iface_{task_id}.png" '
                    '--title "Interface Utilization by network service count" '
                    '--keys IFACE '
                    '--fields rxpck/s,txpck/s,rxkB/s,txkB/s,rxcmp/s,txcmp/s,rxmcst/s '
                    '--key-filter IFACE:eth0 '
                    '--ylabel "Interface Utilization" '
                    '--xlabel "Network Service Count" '
                    '--xticklabels "5,10,15,20,25" < {rift_artifacts}/scaling_{task_id}.log'
            ),
            ('python {rift_install}/usr/rift/systemtest/util/sarplot.py '
                    '--plot "{rift_artifacts}/scaling_iface_err_{task_id}.png" '
                    '--title "Interface Errors by network service count" '
                    '--keys IFACE '
                    '--fields rxerr/s,txerr/s,coll/s,rxdrop/s,txdrop/s,txcarr/s,rxfram/s,rxfifo/s,txfifo/s '
                    '--key-filter IFACE:eth0 '
                    '--ylabel "Interface Errors" '
                    '--xlabel "Network Service Count" '
                    '--xticklabels "5,10,15,20,25" < {rift_artifacts}/scaling_{task_id}.log'
            ),
        ]

        for cmd in plot_commands:
            subprocess.check_call(
                    cmd.format(
                        rift_install=os.environ.get('RIFT_INSTALL'),
                        rift_artifacts=os.environ.get('RIFT_ARTIFACTS'),
                        task_id=os.environ.get('AUTO_TASK_ID')
                    ),
                    shell=True
            )

