# -*- coding: utf-8 -*-

# Copyright 2018 Whitestack, LLC
# *************************************************************

# This file is part of OSM Monitoring module
# All Rights Reserved to Whitestack, LLC

# 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.

# For those usages not covered by the Apache License, Version 2.0 please
# contact: bdiaz@whitestack.com or glavado@whitestack.com
##
import asyncio
import logging
import os
import sys
import unittest
import uuid
from unittest.mock import patch, Mock

from kafka import KafkaProducer
from osm_common.dbmongo import DbMongo
from playhouse.db_url import connect

from osm_policy_module.common.common_db_client import CommonDbClient
from osm_policy_module.common.mon_client import MonClient
from osm_policy_module.core import database
from osm_policy_module.core.agent import PolicyModuleAgent
from osm_policy_module.core.config import Config
from osm_policy_module.core.database import (
    ScalingGroup,
    ScalingAlarm,
    ScalingPolicy,
    ScalingCriteria,
    VnfAlarm,
    AlarmAction,
    HealingAction,
)

log = logging.getLogger()
log.level = logging.INFO
stream_handler = logging.StreamHandler(sys.stdout)
log.addHandler(stream_handler)

nsr_record_mock = {
    "_id": "87776f33-b67c-417a-8119-cb08e4098951",
    "crete-time": 1535392482.0044956,
    "operational-status": "running",
    "ssh-authorized-key": None,
    "name-ref": "cirros_ns",
    "nsd": {
        "_id": "d7c8bd3c-eb39-4514-8847-19f01345524f",
        "_admin": {
            "created": 1535392246.499733,
            "userDefinedData": {},
            "usageSate": "NOT_IN_USE",
            "storage": {
                "zipfile": "package.tar.gz",
                "fs": "local",
                "path": "/app/storage/",
                "folder": "d7c8bd3c-eb39-4514-8847-19f01345524f",
                "pkg-dir": "cirros_nsd",
                "descriptor": "cirros_nsd/cirros_vdu_scaling_nsd.yaml",
            },
            "onboardingState": "ONBOARDED",
            "modified": 1535392246.499733,
            "projects_read": ["admin"],
            "operationalState": "ENABLED",
            "projects_write": ["admin"],
        },
        "id": "cirros_vdu_scaling_ns",
        "name": "cirros_vdu_scaling_ns",
        "description": "Simple NS example with a cirros_vdu_scaling_vnf",
        "designer": "OSM",
        "version": "1.0",
        "vnfd-id": ["cirros_vdu_scaling_vnf"],
        "df": [
            {
                "id": "default-df",
                "vnf-profile": [
                    {
                        "id": "1",
                        "vnfd-id": "cirros_vdu_scaling_vnf",
                        "virtual-link-connectivity": [
                            {
                                "virtual-link-profile-id": "cirros_nsd_vld1",
                                "constituent-cpd-id": [
                                    {
                                        "constituent-base-element-id": "1",
                                        "constituent-cpd-id": "eth0-ext",
                                    }
                                ],
                            }
                        ],
                    },
                    {
                        "id": "2",
                        "vnfd-id": "cirros_vdu_scaling_vnf",
                        "virtual-link-connectivity": [
                            {
                                "virtual-link-profile-id": "cirros_nsd_vld1",
                                "constituent-cpd-id": [
                                    {
                                        "constituent-base-element-id": "2",
                                        "constituent-cpd-id": "eth0-ext",
                                    }
                                ],
                            }
                        ],
                    },
                ],
            }
        ],
        "virtual-link-desc": [{"id": "cirros_nsd_vld1", "mgmt-network": "true"}],
    },
    "id": "87776f33-b67c-417a-8119-cb08e4098951",
    "config-status": "configured",
    "operational-events": [],
    "_admin": {
        "created": 1535392482.0084584,
        "projects_read": ["admin"],
        "nsState": "INSTANTIATED",
        "modified": 1535392482.0084584,
        "projects_write": ["admin"],
        "deployed": {
            "RO": {
                "vnfd_id": {
                    "cirros_vdu_scaling_vnf": "7445e347-fe2f-431a-abc2-8b9be3d093c6"
                },
                "nsd_id": "92c56cf0-f8fa-488c-9afb-9f3d78ae6bbb",
                "nsr_id": "637e12cd-c201-4c44-8ebd-70fb57a4dcee",
                "nsr_status": "BUILD",
            }
        },
    },
    "nsd-ref": "cirros_vdu_scaling_ns",
    "name": "cirros_ns",
    "resource-orchestrator": "osmopenmano",
    "instantiate_params": {
        "nsDescription": "default description",
        "nsdId": "d7c8bd3c-eb39-4514-8847-19f01345524f",
        "nsr_id": "87776f33-b67c-417a-8119-cb08e4098951",
        "nsName": "cirros_ns",
        "vimAccountId": "be48ae31-1d46-4892-a4b4-d69abd55714b",
    },
    "description": "default description",
    "constituent-vnfr-ref": [
        "0d9d06ad-3fc2-418c-9934-465e815fafe2",
        "3336eb44-77df-4c4f-9881-d2828d259864",
    ],
    "admin-status": "ENABLED",
    "detailed-status": "done",
    "datacenter": "be48ae31-1d46-4892-a4b4-d69abd55714b",
    "orchestration-progress": {},
    "short-name": "cirros_ns",
    "ns-instance-config-ref": "87776f33-b67c-417a-8119-cb08e4098951",
    "nsd-name-ref": "cirros_vdu_scaling_ns",
    "admin": {"deployed": {"RO": {"nsr_status": "ACTIVE"}}},
}

vnfr_record_mocks = [
    {
        "_id": "0d9d06ad-3fc2-418c-9934-465e815fafe2",
        "ip-address": "192.168.160.2",
        "created-time": 1535392482.0044956,
        "vim-account-id": "be48ae31-1d46-4892-a4b4-d69abd55714b",
        "vdur": [
            {
                "interfaces": [
                    {
                        "mac-address": "fa:16:3e:71:fd:b8",
                        "name": "eth0",
                        "ip-address": "192.168.160.2",
                    }
                ],
                "count-index": 0,
                "status": "ACTIVE",
                "vim-id": "63a65636-9fc8-4022-b070-980823e6266a",
                "name": "cirros_ns-1-cirros_vnfd-VM-1",
                "status-detailed": None,
                "ip-address": "192.168.160.2",
                "vdu-id-ref": "cirros_vnfd-VM",
            }
        ],
        "id": "0d9d06ad-3fc2-418c-9934-465e815fafe2",
        "vnfd-ref": "cirros_vdu_scaling_vnf",
        "vnfd-id": "63f44c41-45ee-456b-b10d-5f08fb1796e0",
        "_admin": {
            "created": 1535392482.0067868,
            "projects_read": ["admin"],
            "modified": 1535392482.0067868,
            "projects_write": ["admin"],
        },
        "nsr-id-ref": "87776f33-b67c-417a-8119-cb08e4098951",
        "member-vnf-index-ref": "1",
        "connection-point": [{"name": "eth0", "id": None, "connection-point-id": None}],
    },
    {
        "_id": "3336eb44-77df-4c4f-9881-d2828d259864",
        "ip-address": "192.168.160.10",
        "created-time": 1535392482.0044956,
        "vim-account-id": "be48ae31-1d46-4892-a4b4-d69abd55714b",
        "vdur": [
            {
                "interfaces": [
                    {
                        "mac-address": "fa:16:3e:1e:76:e8",
                        "name": "eth0",
                        "ip-address": "192.168.160.10",
                    }
                ],
                "count-index": 0,
                "status": "ACTIVE",
                "vim-id": "a154b8d3-2b10-421a-a51d-4b391d9bd366",
                "name": "cirros_ns-2-cirros_vnfd-VM-1",
                "status-detailed": None,
                "ip-address": "192.168.160.10",
                "vdu-id-ref": "cirros_vnfd-VM",
            }
        ],
        "id": "3336eb44-77df-4c4f-9881-d2828d259864",
        "vnfd-ref": "cirros_vdu_scaling_vnf",
        "vnfd-id": "63f44c41-45ee-456b-b10d-5f08fb1796e0",
        "_admin": {
            "created": 1535392482.0076294,
            "projects_read": ["admin"],
            "modified": 1535392482.0076294,
            "projects_write": ["admin"],
        },
        "nsr-id-ref": "87776f33-b67c-417a-8119-cb08e4098951",
        "member-vnf-index-ref": "2",
        "connection-point": [{"name": "eth0", "id": None, "connection-point-id": None}],
    },
]

nsd_record_mock = {
    "id": "cirros_vdu_scaling_ns",
    "name": "cirros_vdu_scaling_ns",
    "description": "Simple NS example with a cirros_vdu_scaling_vnf",
    "designer": "OSM",
    "version": "1.0",
    "vnfd-id": ["cirros_vdu_scaling_vnf"],
    "df": [
        {
            "id": "default-df",
            "vnf-profile": [
                {
                    "id": "1",
                    "vnfd-id": "cirros_vdu_scaling_vnf",
                    "virtual-link-connectivity": [
                        {
                            "virtual-link-profile-id": "cirros_nsd_vld1",
                            "constituent-cpd-id": [
                                {
                                    "constituent-base-element-id": "1",
                                    "constituent-cpd-id": "eth0-ext",
                                }
                            ],
                        }
                    ],
                },
                {
                    "id": "2",
                    "vnfd-id": "cirros_vdu_scaling_vnf",
                    "virtual-link-connectivity": [
                        {
                            "virtual-link-profile-id": "cirros_nsd_vld1",
                            "constituent-cpd-id": [
                                {
                                    "constituent-base-element-id": "2",
                                    "constituent-cpd-id": "eth0-ext",
                                }
                            ],
                        }
                    ],
                },
            ],
        }
    ],
    "virtual-link-desc": [{"id": "cirros_nsd_vld1", "mgmt-network": "true"}],
}


vnfd_record_mock = {
    "id": "cirros_vdu_scaling_vnf",
    "_id": "63f44c41-45ee-456b-b10d-5f08fb1796e0",
    "product-name": "cirros_vdu_scaling_vnf",
    "description": "Simple VNF example with a cirros and a scaling group descriptor",
    "provider": "OSM",
    "version": "1.0",
    "mgmt-cp": "eth0-ext",
    "virtual-storage-desc": [{"id": "cirros_vnfd-VM-storage", "size-of-storage": 2}],
    "virtual-compute-desc": [
        {
            "id": "cirros_vnfd-VM-compute",
            "virtual-cpu": {"num-virtual-cpu": 1},
            "virtual-memory": {"size": 0.25},
        }
    ],
    "sw-image-desc": [{"id": "cirros034", "name": "cirros034", "image": "cirros034"}],
    "vdu": [
        {
            "id": "cirros_vnfd-VM",
            "description": "cirros_vnfd-VM",
            "name": "cirros_vnfd-VM",
            "alarm": [
                {
                    "value": 20.0,
                    "actions": {
                        "insufficient-data": [{"url": "localhost:9090"}],
                        "ok": [{"url": "localhost:9090"}],
                        "alarm": [{"url": "localhost:9090"}],
                    },
                    "alarm-id": "alarm-1",
                    "operation": "LT",
                    "vnf-monitoring-param-ref": "cirros_vnf_memory_util",
                }
            ],
            "sw-image-desc": "cirros034",
            "virtual-compute-desc": "cirros_vnfd-VM-compute",
            "virtual-storage-desc": ["cirros_vnfd-VM-storage"],
            "int-cpd": [
                {
                    "id": "eth0-int",
                    "virtual-network-interface-requirement": [
                        {
                            "name": "eth0",
                            "virtual-interface": {
                                "bandwidth": "0",
                                "type": "VIRTIO",
                                "vpci": "0000:00:0a.0",
                            },
                        }
                    ],
                }
            ],
            "monitoring-parameter": [
                {
                    "id": "cirros_vnf_memory_util",
                    "name": "cirros_vnf_memory_util",
                    "performance-metric": "average_memory_utilization",
                }
            ],
        }
    ],
    "df": [
        {
            "id": "default-df",
            "vdu-profile": [
                {
                    "id": "cirros_vnfd-VM",
                    "min-number-of-instances": 1,
                    "max-number-of-instances": 10,
                    "vdu-configuration-id": "cirros_vnfd-VM-vdu-configuration",
                }
            ],
            "healing-aspect": [
                {
                    "id": "cirros_vnfd-VM-autoheal",
                    "healing-policy": [
                        {
                            "vdu-id": "cirros_vnfd-VM",
                            "event-name": "heal-alarm",
                            "recovery-type": "automatic",
                            "action-on-recovery": "REDEPLOY_ONLY",
                            "cooldown-time": 180,
                            "day1": False,
                        }
                    ],
                }
            ],
            "instantiation-level": [
                {
                    "id": "default-instantiation-level",
                    "vdu-level": [
                        {"vdu-id": "cirros_vnfd-VM", "number-of-instances": 1}
                    ],
                }
            ],
            "scaling-aspect": [
                {
                    "id": "scale_cirros_vnfd-VM",
                    "name": "scale_cirros_vnfd-VM",
                    "max-scale-level": 10,
                    "scaling-policy": [
                        {
                            "name": "auto_memory_util_above_threshold",
                            "scaling-type": "automatic",
                            "cooldown-time": 60,
                            "threshold-time": 10,
                            "scaling-criteria": [
                                {
                                    "name": "group1_memory_util_above_threshold",
                                    "vnf-monitoring-param-ref": "cirros_vnf_memory_util",
                                    "scale-out-threshold": 80,
                                    "scale-out-relational-operation": "GT",
                                    "scale-in-relational-operation": "LT",
                                    "scale-in-threshold": 20,
                                }
                            ],
                        }
                    ],
                    "aspect-delta-details": {
                        "deltas": [
                            {
                                "id": "scale_cirros_vnfd-VM-delta",
                                "vdu-delta": [
                                    {"number-of-instances": 1, "id": "cirros_vnfd-VM"}
                                ],
                            }
                        ]
                    },
                }
            ],
        }
    ],
    "ext-cpd": [
        {"id": "eth0-ext", "int-cpd": {"vdu-id": "cirros_vnfd-VM", "cpd": "eth0-int"}}
    ],
    "vdu-configuration": [
        {
            "juju": {"charm": "testmetrics", "proxy": True},
            "metrics": [{"name": "users"}],
            "id": "cirros_vnfd-VM-vdu-configuration",
        }
    ],
    "_admin": {
        "created": 1535392242.6281035,
        "modified": 1535392242.6281035,
        "storage": {
            "zipfile": "package.tar.gz",
            "pkg-dir": "cirros_vnf",
            "path": "/app/storage/",
            "folder": "63f44c41-45ee-456b-b10d-5f08fb1796e0",
            "fs": "local",
            "descriptor": "cirros_vnf/cirros_vdu_scaling_vnfd.yaml",
        },
        "usageSate": "NOT_IN_USE",
        "onboardingState": "ONBOARDED",
        "userDefinedData": {},
        "projects_read": ["admin"],
        "operationalState": "ENABLED",
        "projects_write": ["admin"],
    },
}

MODELS = [
    ScalingGroup,
    ScalingPolicy,
    ScalingCriteria,
    ScalingAlarm,
    VnfAlarm,
    AlarmAction,
    HealingAction,
]


class PolicyModuleAgentTest(unittest.TestCase):
    def setUp(self):
        super()
        database.db.initialize(connect("sqlite:///test_db.sqlite"))
        database.db.bind(MODELS)
        database.db.connect()
        database.db.drop_tables(MODELS)
        database.db.create_tables(MODELS)
        database.db.close()
        self.loop = asyncio.new_event_loop()

    def tearDown(self):
        super()
        os.remove("test_db.sqlite")

    @patch.object(DbMongo, "db_connect", Mock())
    @patch.object(KafkaProducer, "__init__")
    @patch.object(MonClient, "create_alarm")
    @patch.object(CommonDbClient, "get_vnfd")
    @patch.object(CommonDbClient, "get_nsr")
    @patch.object(CommonDbClient, "get_vnfr")
    def test_configure_scaling_groups(
        self, get_vnfr, get_nsr, get_vnfd, create_alarm, kafka_producer_init
    ):
        def _test_configure_scaling_groups_get_vnfr(*args, **kwargs):
            if "1" in args[1]:
                return vnfr_record_mocks[0]
            if "2" in args[1]:
                return vnfr_record_mocks[1]

        def assert_not_called_with(*args, **kwargs):
            try:
                create_alarm.assert_called_with(*args, **kwargs)
            except AssertionError:
                return
            raise AssertionError("Expected to not have been called.")

        async def _test_configure_scaling_groups_create_alarm(*args, **kwargs):
            return uuid.uuid4()

        kafka_producer_init.return_value = None
        get_vnfr.side_effect = _test_configure_scaling_groups_get_vnfr
        get_nsr.return_value = nsr_record_mock
        get_vnfd.return_value = vnfd_record_mock
        create_alarm.side_effect = _test_configure_scaling_groups_create_alarm
        create_alarm.assert_not_called_with = assert_not_called_with
        config = Config()
        agent = PolicyModuleAgent(config, self.loop)
        self.loop.run_until_complete(
            agent.autoscaling_service.configure_scaling_groups("test_nsr_id")
        )
        create_alarm.assert_any_call(
            metric_name="average_memory_utilization",
            ns_id="test_nsr_id",
            operation="GT",
            threshold=80,
            vdu_name="cirros_ns-1-cirros_vnfd-VM-1",
            vnf_member_index="1",
            action="scale_out",
        )
        create_alarm.assert_not_called_with(
            metric_name="average_memory_utilization",
            ns_id="test_nsr_id",
            operation="LT",
            threshold=20,
            vdu_name="cirros_ns-1-cirros_vnfd-VM-1",
            vnf_member_index="1",
            action="scale_out",
        )
        create_alarm.assert_any_call(
            metric_name="average_memory_utilization",
            ns_id="test_nsr_id",
            operation="GT",
            threshold=80,
            vdu_name="cirros_ns-2-cirros_vnfd-VM-1",
            vnf_member_index="2",
            action="scale_out",
        )
        create_alarm.assert_not_called_with(
            metric_name="average_memory_utilization",
            ns_id="test_nsr_id",
            operation="LT",
            threshold=20,
            vdu_name="cirros_ns-2-cirros_vnfd-VM-1",
            vnf_member_index="2",
            action="scale_out",
        )
        scaling_record = ScalingGroup.get()
        self.assertEqual(scaling_record.name, "scale_cirros_vnfd-VM")
        self.assertEqual(scaling_record.nsr_id, "test_nsr_id")

    @patch.object(DbMongo, "db_connect", Mock())
    @patch.object(KafkaProducer, "__init__")
    @patch.object(MonClient, "create_alarm")
    @patch.object(CommonDbClient, "get_vnfd")
    @patch.object(CommonDbClient, "get_nsr")
    @patch.object(CommonDbClient, "get_vnfr")
    def test_configure_vnf_alarms(
        self, get_vnfr, get_nsr, get_vnfd, create_alarm, kafka_producer_init
    ):
        def _test_configure_scaling_groups_get_vnfr(*args, **kwargs):
            if "1" in args[1]:
                return vnfr_record_mocks[0]
            if "2" in args[1]:
                return vnfr_record_mocks[1]

        async def _test_configure_vnf_alarms_create_alarm(*args, **kwargs):
            return uuid.uuid4()

        kafka_producer_init.return_value = None
        get_vnfr.side_effect = _test_configure_scaling_groups_get_vnfr
        get_nsr.return_value = nsr_record_mock
        get_vnfd.return_value = vnfd_record_mock
        create_alarm.side_effect = _test_configure_vnf_alarms_create_alarm
        config = Config()
        agent = PolicyModuleAgent(config, self.loop)
        self.loop.run_until_complete(
            agent.alarming_service.configure_vnf_alarms("test_nsr_id")
        )
        create_alarm.assert_any_call(
            metric_name="average_memory_utilization",
            ns_id="test_nsr_id",
            vdu_name="cirros_ns-1-cirros_vnfd-VM-1",
            vnf_member_index="1",
            threshold=20.0,
            operation="LT",
            action="{'webhook': ['localhost:9090', 'localhost:9090', 'localhost:9090']}",
        )
        create_alarm.assert_any_call(
            metric_name="average_memory_utilization",
            ns_id="test_nsr_id",
            vdu_name="cirros_ns-2-cirros_vnfd-VM-1",
            vnf_member_index="2",
            threshold=20.0,
            operation="LT",
            action="{'webhook': ['localhost:9090', 'localhost:9090', 'localhost:9090']}",
        )

    @patch.object(DbMongo, "db_connect", Mock())
    @patch.object(KafkaProducer, "__init__")
    @patch.object(MonClient, "create_alarm")
    @patch.object(CommonDbClient, "get_vnfd")
    @patch.object(CommonDbClient, "get_nsr")
    @patch.object(CommonDbClient, "get_vnfr")
    def test_configure_healing_alarms(
        self, get_vnfr, get_nsr, get_vnfd, create_alarm, kafka_producer_init
    ):
        def _test_configure_scaling_groups_get_vnfr(*args, **kwargs):
            if "1" in args[1]:
                return vnfr_record_mocks[0]
            if "2" in args[1]:
                return vnfr_record_mocks[1]

        async def _test_configure_healing_alarms_create_alarm(*args, **kwargs):
            return uuid.uuid4()

        kafka_producer_init.return_value = None
        get_vnfr.side_effect = _test_configure_scaling_groups_get_vnfr
        get_nsr.return_value = nsr_record_mock
        get_vnfd.return_value = vnfd_record_mock
        create_alarm.side_effect = _test_configure_healing_alarms_create_alarm
        config = Config()
        agent = PolicyModuleAgent(config, self.loop)
        self.loop.run_until_complete(
            agent.healing_service.configure_healing_alarms("test_nsr_id")
        )
        create_alarm.assert_any_call(
            metric_name="vm_status",
            ns_id="test_nsr_id",
            vdu_name="cirros_ns-1-cirros_vnfd-VM-1",
            vnf_member_index="1",
            threshold=1,
            operation="LT",
            statistic="AVERAGE",
        )
        create_alarm.assert_any_call(
            metric_name="vm_status",
            ns_id="test_nsr_id",
            vdu_name="cirros_ns-2-cirros_vnfd-VM-1",
            vnf_member_index="2",
            threshold=1,
            operation="LT",
            statistic="AVERAGE",
        )


if __name__ == "__main__":
    unittest.main()
