# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.
""" BVT tests for routers
"""
#Import Local Modules
import marvin
from marvin.cloudstackTestCase import *
from marvin.cloudstackAPI import *
from marvin.sshClient import SshClient
from marvin.integration.lib.utils import *
from marvin.integration.lib.base import *
from marvin.integration.lib.common import *
from nose.plugins.attrib import attr
#Import System modules
import time


_multiprocess_shared_ = True
class Services:
    """Test router Services
    """

    def __init__(self):
        self.services = {
                         "service_offering": {
                                    "name": "Tiny Instance",
                                    "displaytext": "Tiny Instance",
                                    "cpunumber": 1,
                                    "cpuspeed": 100, # in MHz
                                    "memory": 128, # In MBs
                                    },
                        "virtual_machine":
                                    {
                                        "displayname": "Test VM",
                                        "username": "root",
                                        "password": "password",
                                        "ssh_port": 22,
                                        "hypervisor": 'XenServer',
                                        "privateport": 22,
                                        "publicport": 22,
                                        "protocol": 'TCP',
                                },
                        "account": {
                                        "email": "test@test.com",
                                        "firstname": "Test",
                                        "lastname": "User",
                                        "username": "testuser",
                                        "password": "password",
                                        },
                         "ostype": "CentOS 5.3 (64-bit)",
                         "sleep": 60,
                         "timeout": 10,
                        }


class TestRouterServices(cloudstackTestCase):

    @classmethod
    def setUpClass(cls):

        cls.api_client = super(
                               TestRouterServices,
                               cls
                               ).getClsTestClient().getApiClient()
        cls.services = Services().services
        # Get Zone, Domain and templates
        cls.domain = get_domain(cls.api_client, cls.services)
        cls.zone = get_zone(cls.api_client, cls.services)
        cls.services['mode'] = cls.zone.networktype
        template = get_template(
                            cls.api_client,
                            cls.zone.id,
                            cls.services["ostype"]
                            )
        cls.services["virtual_machine"]["zoneid"] = cls.zone.id

        #Create an account, network, VM and IP addresses
        cls.account = Account.create(
                                     cls.api_client,
                                     cls.services["account"],
                                     domainid=cls.domain.id
                                     )
        cls.service_offering = ServiceOffering.create(
                                            cls.api_client,
                                            cls.services["service_offering"]
                                            )
        cls.vm_1 = VirtualMachine.create(
                                    cls.api_client,
                                    cls.services["virtual_machine"],
                                    templateid=template.id,
                                    accountid=cls.account.name,
                                    domainid=cls.account.domainid,
                                    serviceofferingid=cls.service_offering.id
                                    )
        cls.cleanup = [
                       cls.account,
                       cls.service_offering
                       ]
        return

    @classmethod
    def tearDownClass(cls):
        try:
            cls.api_client = super(
                                   TestRouterServices,
                                   cls
                                   ).getClsTestClient().getApiClient()
            #Clean up, terminate the created templates
            cleanup_resources(cls.api_client, cls.cleanup)

        except Exception as e:
            raise Exception("Warning: Exception during cleanup : %s" % e)
        return

    def setUp(self):
        self.apiclient = self.testClient.getApiClient()
        return

    @attr(tags = ["advanced", "basic", "sg", "smoke"])
    def test_01_router_internal_basic(self):
        """Test router internal basic zone
        """
        # Validate the following
        # 1. Router only does dhcp
        # 2. Verify that ports 67 (DHCP) and 53 (DNS) are open on UDP
        #    by checking status of dnsmasq process

        # Find router associated with user account
        if self.zone.networktype == "Basic":
            list_router_response = list_routers(
                self.apiclient,
                listall="true"
            )
        else:
            list_router_response = list_routers(
                self.apiclient,
                account=self.account.name,
                domainid=self.account.domainid
            )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        router = list_router_response[0]

        hosts = list_hosts(
                           self.apiclient,
                           zoneid=router.zoneid,
                           type='Routing',
                           state='Up',
                           id=router.hostid
                           )
        self.assertEqual(
                            isinstance(hosts, list),
                            True,
                            "Check list host returns a valid list"
                        )
        host = hosts[0]

        self.debug("Router ID: %s, state: %s" % (router.id, router.state))

        self.assertEqual(
                            router.state,
                            'Running',
                            "Check list router response for router state"
                        )

        if self.apiclient.hypervisor.lower() == 'vmware':
               result = get_process_status(
                                   self.apiclient.connection.mgtSvr,
                                   22,
                               self.apiclient.connection.user,
                               self.apiclient.connection.passwd,
                               router.linklocalip,
                               "service dnsmasq status",
                                   hypervisor=self.apiclient.hypervisor
                               )
        else:
            try:
                host.user, host.passwd = get_host_credentials(self.config, host.ipaddress)
                result = get_process_status(
                                    host.ipaddress,
                                    22,
                                    host.user,
                                    host.passwd,
                                    router.linklocalip,
                                    "service dnsmasq status"
                                    )

            except KeyError:
                self.skipTest("Marvin configuration has no host credentials to check router services")
        res = str(result)
        self.debug("Dnsmasq process status: %s" % res)

        self.assertEqual(
            res.count("running"),
            1,
            "Check dnsmasq service is running or not"
        )
        return





    @attr(tags = ["advanced", "smoke"])
    def test_02_router_internal_adv(self):
        """Test router internal advanced zone
        """
        # Validate the following
        # 1. Router does dhcp, dns, gateway, LB, PF, FW
        # 2. verify that dhcp, dns ports are open on UDP
        # 3. dnsmasq, haproxy processes should be running

        # Find router associated with user account
        list_router_response = list_routers(
                                    self.apiclient,
                                    account=self.account.name,
                                    domainid=self.account.domainid
                                    )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        
        router = list_router_response[0]

        hosts = list_hosts(
                           self.apiclient,
                           zoneid=router.zoneid,
                           type='Routing',
                           state='Up',
                           id=router.hostid
                           )
        self.assertEqual(
                            isinstance(hosts, list),
                            True,
                            "Check list response returns a valid list"
                        )
        host = hosts[0]

        self.debug("Router ID: %s, state: %s" % (router.id, router.state))
        self.assertEqual(
                            router.state,
                            'Running',
                            "Check list router response for router state"
                        )

        if self.apiclient.hypervisor.lower() == 'vmware':
           result = get_process_status(
                           self.apiclient.connection.mgtSvr,
                               22,
                           self.apiclient.connection.user,
                           self.apiclient.connection.passwd,
                           router.linklocalip,
                           "service dnsmasq status",
                               hypervisor=self.apiclient.hypervisor
                           )
        else:
            try:
                host.user, host.passwd = get_host_credentials(self.config, host.ipaddress)
                result = get_process_status(
                                    host.ipaddress,
                                    22,
                                    host.user,
                                    host.passwd,
                                    router.linklocalip,
                                    "service dnsmasq status"
                                    )
            except KeyError:
                self.skipTest("Marvin configuration has no host credentials to check router services")
        res = str(result)
        self.debug("Dnsmasq process status: %s" % res)
        
        self.assertEqual(
                            res.count("running"),
                            1,
                            "Check dnsmasq service is running or not"
                        )

        if self.apiclient.hypervisor.lower() == 'vmware':
           result = get_process_status(
                               self.apiclient.connection.mgtSvr,
                           22,
                           self.apiclient.connection.user,
                           self.apiclient.connection.passwd,
                           router.linklocalip,
                           "service haproxy status",
                           hypervisor=self.apiclient.hypervisor
                           )
        else:
            try:
                host.user, host.passwd = get_host_credentials(self.config, host.ipaddress)
                result = get_process_status(
                                    host.ipaddress,
                                    22,
                                    host.user,
                                    host.passwd,
                                    router.linklocalip,
                                    "service haproxy status"
                                    )
            except KeyError:
                self.skipTest("Marvin configuration has no host credentials to check router services")
        res = str(result)
        self.assertEqual(
                            res.count("running"),
                            1,
                            "Check haproxy service is running or not"
                        )
        self.debug("Haproxy process status: %s" % res)
        return

    @attr(tags = ["advanced", "advancedns", "smoke"])
    def test_03_restart_network_cleanup(self):
        """Test restart network
        """

        # Validate the following
        # 1. When cleanup = true, router is destroyed and a new one created
        # 2. New router will have new publicIp and linkLocalIp and
        #    all it's services should resume

        # Find router associated with user account
        list_router_response = list_routers(
                                    self.apiclient,
                                    account=self.account.name,
                                    domainid=self.account.domainid
                                    )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        router = list_router_response[0]

        #Store old values before restart
        old_linklocalip = router.linklocalip

        timeout = 10
        # Network should be in Implemented or Setup stage before restart
        while True:
            networks = list_networks(
                                 self.apiclient,
                                 account=self.account.name,
                                 domainid=self.account.domainid
                                 )
            self.assertEqual(
                            isinstance(networks, list),
                            True,
                            "Check list response returns a valid list"
                        )
            network = networks[0]
            if network.state in ["Implemented", "Setup"]:
                break
            elif timeout == 0:
                break
            else:
                time.sleep(self.services["sleep"])
                timeout = timeout - 1

        self.debug(
            "Restarting network with ID: %s, Network state: %s" % (
                                                                network.id,
                                                                network.state
                                                                ))
        cmd = restartNetwork.restartNetworkCmd()
        cmd.id = network.id
        cmd.cleanup = True
        self.apiclient.restartNetwork(cmd)

        # Get router details after restart
        list_router_response = list_routers(
                                    self.apiclient,
                                    account=self.account.name,
                                    domainid=self.account.domainid
                                    )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        router = list_router_response[0]

        self.assertNotEqual(
                            router.linklocalip,
                            old_linklocalip,
                            "Check link-local IP after restart"
                        )
        return

    @attr(tags = ["advanced", "advancedns", "smoke"])
    def test_04_restart_network_wo_cleanup(self):
        """Test restart network without cleanup
        """

        # Validate the following
        # 1. When cleanup = false, router is restarted and
        #    all services inside the router are restarted
        # 2. check 'uptime' to see if the actual restart happened

        timeout = 10
        # Network should be in Implemented or Setup stage before restart
        while True:
            networks = list_networks(
                                 self.apiclient,
                                 account=self.account.name,
                                 domainid=self.account.domainid
                                 )
            self.assertEqual(
                            isinstance(networks, list),
                            True,
                            "Check list response returns a valid list"
                        )
            network = networks[0]
            if network.state in ["Implemented", "Setup"]:
                break
            elif timeout == 0:
                break
            else:
                time.sleep(self.services["sleep"])
                timeout = timeout - 1

        self.debug(
            "Restarting network with ID: %s, Network state: %s" % (
                                                                network.id,
                                                                network.state
                                                                ))
        cmd = restartNetwork.restartNetworkCmd()
        cmd.id = network.id
        cmd.cleanup = False
        self.apiclient.restartNetwork(cmd)

        # Get router details after restart
        list_router_response = list_routers(
                                    self.apiclient,
                                    account=self.account.name,
                                    domainid=self.account.domainid
                                    )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        router = list_router_response[0]

        hosts = list_hosts(
                           self.apiclient,
                           zoneid=router.zoneid,
                           type='Routing',
                           state='Up',
                           id=router.hostid
                           )
        self.assertEqual(
                            isinstance(hosts, list),
                            True,
                            "Check list response returns a valid list"
                        )
        host = hosts[0]

        if self.apiclient.hypervisor.lower() == 'vmware':
           res = get_process_status(
                           self.apiclient.connection.mgtSvr,
                           22,
                           self.apiclient.connection.user,
                           self.apiclient.connection.passwd,
                           router.linklocalip,
                           "uptime",
                           hypervisor=self.apiclient.hypervisor
                           )
        else:
            try:
                host.user, host.passwd = get_host_credentials(self.config, host.ipaddress)
                res = get_process_status(
                                host.ipaddress,
                                22,
                                host.user,
                                host.passwd,
                                router.linklocalip,
                                "uptime"
                                )
            except KeyError:
                self.skipTest("Marvin configuration has no host credentials to check router services")
        
        # res = 12:37:14 up 1 min,  0 users,  load average: 0.61, 0.22, 0.08
        # Split result to check the uptime
        result = res[0].split()
        self.debug("Router Uptime: %s" % result)
        self.assertEqual(
                            str(result[1]),
                            'up',
                            "Check router is running or not"
                        )
        if str(result[3]) == "min,":
               self.assertEqual(
                                (int(result[2]) < 3),
                                True,
                                "Check uptime is less than 3 mins or not"
                                )
        else:
            self.assertEqual(
                                str(result[3]),
                                'sec,',
                                "Check uptime is in seconds"
                                )
        return

    @attr(tags = ["advanced", "advancedns", "smoke"])
    def test_05_router_basic(self):
        """Test router basic setup
        """

        # Validate the following:
        # 1. verify that listRouters returned a 'Running' router
        # 2. router will have dns same as that seen in listZones
        # 3. router will have a guestIP and a linkLocalIp"

        list_router_response = list_routers(
                                        self.apiclient,
                                        account=self.account.name,
                                        domainid=self.account.domainid
                                        )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        self.assertNotEqual(
                             len(list_router_response),
                             0,
                             "Check list router response"
                             )
        for router in list_router_response:
            self.assertEqual(
                            router.state,
                            'Running',
                            "Check list router response for router state"
                        )

            zones = list_zones(
                               self.apiclient,
                               id=router.zoneid
                               )
            self.assertEqual(
                            isinstance(zones, list),
                            True,
                            "Check list response returns a valid list"
                        )
            zone = zones[0]

            self.assertEqual(
                            router.dns1,
                            zone.dns1,
                            "Compare DNS1 of router and zone"
                        )
            self.assertEqual(
                            router.dns2,
                            zone.dns2,
                            "Compare DNS2 of router and zone"
                        )
            self.assertEqual(
                            hasattr(router, 'guestipaddress'),
                            True,
                            "Check whether router has guest IP field"
                            )

            self.assertEqual(
                            hasattr(router, 'linklocalip'),
                            True,
                            "Check whether router has link local IP field"
                            )
        return

    @attr(tags = ["advanced", "advancedns", "smoke"])
    def test_06_router_advanced(self):
        """Test router advanced setup
        """

        # Validate the following
        # 1. verify that listRouters returned a 'Running' router
        # 2. router will have dns and gateway as in listZones, listVlanIpRanges
        # 3. router will have guest,public and linklocal IPs

        list_router_response = list_routers(
                                    self.apiclient,
                                    account=self.account.name,
                                    domainid=self.account.domainid
                                    )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        self.assertNotEqual(
                             len(list_router_response),
                             0,
                             "Check list router response"
                             )
        for router in list_router_response:
            self.assertEqual(
                            router.state,
                            'Running',
                            "Check list router response for router state"
                        )

            zones = list_zones(
                               self.apiclient,
                               id=router.zoneid
                               )
            self.assertEqual(
                            isinstance(zones, list),
                            True,
                            "Check list response returns a valid list"
                        )
            zone = zones[0]

            self.assertEqual(
                            router.dns1,
                            zone.dns1,
                            "Compare DNS1 of router and zone"
                        )
            self.assertEqual(
                            router.dns2,
                            zone.dns2,
                            "Compare DNS2 of router and zone"
                        )
            self.assertEqual(
                            hasattr(router, 'guestipaddress'),
                            True,
                            "Check whether router has guest IP field"
                            )

            self.assertEqual(
                            hasattr(router, 'linklocalip'),
                            True,
                            "Check whether router has link local IP field"
                            )

            #Fetch corresponding ip ranges information from listVlanIpRanges
            ipranges_response = list_vlan_ipranges(
                                                   self.apiclient,
                                                   zoneid=router.zoneid
                                                   )
            self.assertEqual(
                            isinstance(ipranges_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
            iprange = ipranges_response[0]
            self.assertEqual(
                            router.gateway,
                            iprange.gateway,
                            "Check gateway with that of corresponding IP range"
                            )
        return

    @attr(tags = ["advanced", "advancedns", "smoke"])
    def test_07_stop_router(self):
        """Test stop router
        """

        # Validate the following
        # 1. listRouter should report the router for the account as stopped

        list_router_response = list_routers(
                                    self.apiclient,
                                    account=self.account.name,
                                    domainid=self.account.domainid
                                    )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        router = list_router_response[0]
        
        self.debug("Stopping the router with ID: %s" % router.id)
        #Stop the router
        cmd = stopRouter.stopRouterCmd()
        cmd.id = router.id
        self.apiclient.stopRouter(cmd)

        #List routers to check state of router
        router_response = list_routers(
                                    self.apiclient,
                                    id=router.id
                                    )
        self.assertEqual(
                            isinstance(router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        #List router should have router in stopped state
        self.assertEqual(
                            router_response[0].state,
                            'Stopped',
                            "Check list router response for router state"
                        )
        return

    @attr(tags = ["advanced", "advancedns", "smoke"])
    def test_08_start_router(self):
        """Test start router
        """

        # Validate the following
        # 1. listRouter should report the router for the account as stopped

        list_router_response = list_routers(
                                    self.apiclient,
                                    account=self.account.name,
                                    domainid=self.account.domainid
                                    )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        router = list_router_response[0]

        self.debug("Starting the router with ID: %s" % router.id)

        #Start the router
        cmd = startRouter.startRouterCmd()
        cmd.id = router.id
        self.apiclient.startRouter(cmd)

        #List routers to check state of router
        router_response = list_routers(
                                    self.apiclient,
                                    id=router.id
                                    )
        self.assertEqual(
                            isinstance(router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        #List router should have router in running state
        self.assertEqual(
                            router_response[0].state,
                            'Running',
                            "Check list router response for router state"
                        )
        return

    @attr(tags = ["advanced", "advancedns", "smoke"])
    def test_09_reboot_router(self):
        """Test reboot router
        """

        # Validate the following
        # 1. listRouter should report the router for the account as stopped

        list_router_response = list_routers(
                                    self.apiclient,
                                    account=self.account.name,
                                    domainid=self.account.domainid
                                    )
        self.assertEqual(
                            isinstance(list_router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        router = list_router_response[0]

        public_ip = router.publicip

        self.debug("Rebooting the router with ID: %s" % router.id)
        
        #Reboot the router
        cmd = rebootRouter.rebootRouterCmd()
        cmd.id = router.id
        self.apiclient.rebootRouter(cmd)

        #List routers to check state of router
        router_response = list_routers(
                                    self.apiclient,
                                    id=router.id
                                    )
        self.assertEqual(
                            isinstance(router_response, list),
                            True,
                            "Check list response returns a valid list"
                        )
        #List router should have router in running state and same public IP
        self.assertEqual(
                            router_response[0].state,
                            'Running',
                            "Check list router response for router state"
                        )

        self.assertEqual(
                            router_response[0].publicip,
                            public_ip,
                            "Check list router response for router public IP"
                        )
        return
