from unittest import TestCase
from lxml import etree

from pcs.lib.cib import status


class GetResourcesFailcounts(TestCase):
    def test_no_failures(self):
        status_xml = etree.fromstring("""
            <status>
                <node_state uname="node1">
                    <transient_attributes>
                        <instance_attributes>
                        </instance_attributes>
                    </transient_attributes>
                </node_state>
                <node_state uname="node2">
                    <transient_attributes>
                    </transient_attributes>
                </node_state>
                <node_state uname="node3">
                </node_state>
            </status>
        """)
        self.assertEqual(
            status.get_resources_failcounts(status_xml),
            []
        )

    def test_failures(self):
        status_xml = etree.fromstring("""
            <status>
                <node_state uname="node1">
                    <transient_attributes>
                        <instance_attributes>
                            <nvpair name="fail-count-clone:0#start_0"
                                value="INFINITY"/>
                            <nvpair name="last-failure-clone:0#start_0"
                                value="1528871936"/>
                            <nvpair name="fail-count-clone:1#start_0"
                                value="999"/>
                            <nvpair name="last-failure-clone:1#start_0"
                                value="1528871937"/>
                        </instance_attributes>
                    </transient_attributes>
                </node_state>
                <node_state uname="node2">
                    <transient_attributes>
                        <instance_attributes>
                            <nvpair name="fail-count-resource#monitor_500"
                                value="10"/>
                            <nvpair name="last-failure-resource#monitor_500"
                                value="1528871946"/>
                            <nvpair name="fail-count-no-last#stop_0"
                                value="3"/>
                            <nvpair name="last-failure-no-count#monitor_1000"
                                value="1528871956"/>
                            <nvpair name="ignored-resource#monitor_1000"
                                value="ignored"/>
                            <nvpair name="fail-count-no-int#start_0"
                                value="a few"/>
                            <nvpair name="last-failure-no-int#start_0"
                                value="an hour ago"/>
                        </instance_attributes>
                    </transient_attributes>
                </node_state>
            </status>
        """)
        self.assertEqual(
            sorted(
                status.get_resources_failcounts(status_xml),
                key=lambda x: [str(x[key]) for key in sorted(x.keys())]
            ),
            sorted([
                {
                    "node": "node1",
                    "resource": "clone",
                    "clone_id": "0",
                    "operation": "start",
                    "interval": "0",
                    "fail_count": "INFINITY",
                    "last_failure": 1528871936,
                },
                {
                    "node": "node1",
                    "resource": "clone",
                    "clone_id": "1",
                    "operation": "start",
                    "interval": "0",
                    "fail_count": 999,
                    "last_failure": 1528871937,
                },
                {
                    "node": "node2",
                    "resource": "resource",
                    "clone_id": None,
                    "operation": "monitor",
                    "interval": "500",
                    "fail_count": 10,
                    "last_failure": 1528871946,
                },
                {
                    "node": "node2",
                    "resource": "no-last",
                    "clone_id": None,
                    "operation": "stop",
                    "interval": "0",
                    "fail_count": 3,
                    "last_failure": 0,
                },
                {
                    "node": "node2",
                    "resource": "no-count",
                    "clone_id": None,
                    "operation": "monitor",
                    "interval": "1000",
                    "fail_count": 0,
                    "last_failure": 1528871956,
                },
                {
                    "node": "node2",
                    "resource": "no-int",
                    "clone_id": None,
                    "operation": "start",
                    "interval": "0",
                    "fail_count": 1,
                    "last_failure": 0,
                },
            ],
                key=lambda x: [str(x[key]) for key in sorted(x.keys())]
            )
        )

class ParseFailureName(TestCase):
    # pylint: disable=protected-access
    def test_without_clone_id(self):
        self.assertEqual(
            status._parse_failure_name("resource#monitor_1000"),
            ("resource", None, "monitor", "1000")
        )

    def test_with_clone_id(self):
        self.assertEqual(
            status._parse_failure_name("resource:2#monitor_1000"),
            ("resource", "2", "monitor", "1000")
        )

class FilterResourceFailcounts(TestCase):
    # pylint: disable=too-many-instance-attributes
    def setUp(self):
        self.fail_01 = {
            "node": "nodeA",
            "resource": "resourceA",
            "clone_id": None,
            "operation": "start",
            "interval": "0",
            "fail_count": "INFINITY",
            "last_failure": "100",
        }
        self.fail_02 = {
            "node": "nodeA",
            "resource": "resourceB",
            "clone_id": None,
            "operation": "monitor",
            "interval": "1000",
            "fail_count": "INFINITY",
            "last_failure": "100",
        }
        self.fail_03 = {
            "node": "nodeB",
            "resource": "resourceA",
            "clone_id": None,
            "operation": "monitor",
            "interval": "1000",
            "fail_count": "INFINITY",
            "last_failure": "100",
        }
        self.fail_04 = {
            "node": "nodeB",
            "resource": "resourceB",
            "clone_id": None,
            "operation": "start",
            "interval": "0",
            "fail_count": "INFINITY",
            "last_failure": "100",
        }
        self.fail_05 = {
            "node": "nodeB",
            "resource": "resourceA",
            "clone_id": None,
            "operation": "monitor",
            "interval": "500",
            "fail_count": "INFINITY",
            "last_failure": "100",
        }
        self.fail_06 = {
            "node": "nodeB",
            "resource": "resourceA",
            "clone_id": None,
            "operation": "start",
            "interval": "0",
            "fail_count": "INFINITY",
            "last_failure": "100",
        }
        self.fail_07 = {
            "node": "nodeB",
            "resource": "resourceB",
            "clone_id": None,
            "operation": "monitor",
            "interval": "1000",
            "fail_count": "INFINITY",
            "last_failure": "100",
        }
        self.failures = [
            self.fail_01, self.fail_02, self.fail_03, self.fail_04,
            self.fail_05, self.fail_06, self.fail_07
        ]

    def test_no_filter(self):
        self.assertEqual(
            status.filter_resources_failcounts(self.failures),
            self.failures
        )

    def test_no_match(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, resource="resourceX"
            ),
            []
        )

    def test_filter_by_resource(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, resource="resourceA"
            ),
            [self.fail_01, self.fail_03, self.fail_05, self.fail_06]
        )

    def test_filter_by_node(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, node="nodeA"
            ),
            [self.fail_01, self.fail_02]
        )

    def test_filter_by_operation(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, operation="monitor"
            ),
            [self.fail_02, self.fail_03, self.fail_05, self.fail_07]
        )

    def test_filter_by_operation_and_interval(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, operation="monitor", interval="500"
            ),
            [self.fail_05]
        )

    def test_filter_by_operation_and_interval_int(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, operation="monitor", interval=500
            ),
            [self.fail_05]
        )

    def test_filter_by_resource_and_node(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, resource="resourceA", node="nodeB"
            ),
            [self.fail_03, self.fail_05, self.fail_06]
        )

    def test_filter_by_resource_and_node_and_operation(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, resource="resourceA", node="nodeB",
                operation="monitor"
            ),
            [self.fail_03, self.fail_05]
        )

    def test_filter_by_resource_and_node_and_operation_and_interval(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, resource="resourceA", node="nodeB",
                operation="monitor", interval="1000"
            ),
            [self.fail_03]
        )

    def test_filter_by_node_and_operation(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures, node="nodeB", operation="monitor"
            ),
            [self.fail_03, self.fail_05, self.fail_07]
        )

    def test_filter_by_node_and_operation_and_interval(self):
        self.assertEqual(
            status.filter_resources_failcounts(
                self.failures,
                node="nodeB",
                operation="monitor",
                interval="1000",
            ),
            [self.fail_03, self.fail_07]
        )
