#  Copyright 2014 Cloudbase Solutions Srl
#
#    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.

import ddt
import mock

from os_win import exceptions
from os_win.tests import test_base
from os_win.utils import _wqlutils
from os_win.utils.network import networkutils


@ddt.ddt
class NetworkUtilsTestCase(test_base.OsWinBaseTestCase):
    """Unit tests for the Hyper-V NetworkUtils class."""

    _FAKE_VSWITCH_NAME = "fake_vswitch_name"
    _FAKE_PORT_NAME = "fake_port_name"
    _FAKE_JOB_PATH = 'fake_job_path'
    _FAKE_RET_VAL = 0
    _FAKE_RES_PATH = "fake_res_path"
    _FAKE_VSWITCH = "fake_vswitch"
    _FAKE_VLAN_ID = "fake_vlan_id"
    _FAKE_CLASS_NAME = "fake_class_name"
    _FAKE_ELEMENT_NAME = "fake_element_name"
    _FAKE_HYPERV_VM_STATE = 'fake_hyperv_state'

    _FAKE_ACL_ACT = 'fake_acl_action'
    _FAKE_ACL_DIR = 'fake_acl_dir'
    _FAKE_ACL_TYPE = 'fake_acl_type'
    _FAKE_LOCAL_PORT = 'fake_local_port'
    _FAKE_PROTOCOL = 'fake_port_protocol'
    _FAKE_REMOTE_ADDR = '0.0.0.0/0'
    _FAKE_WEIGHT = 'fake_weight'

    _FAKE_BAD_INSTANCE_ID = 'bad_instance_id'
    _FAKE_INSTANCE_ID = (
        r"Microsoft:609CBAAD-BC13-4A65-AADE-AD95861FE394\\55349F56-72AB-4FA3-"
        "B5FE-6A30A511A419\\C\\776E0BA7-94A1-41C8-8F28-951F524251B5\\77A43184-"
        "5444-49BF-ABE0-2210B72ABA73")

    _MSVM_VIRTUAL_SWITCH = 'Msvm_VirtualEthernetSwitch'

    def setUp(self):
        super(NetworkUtilsTestCase, self).setUp()
        self.netutils = networkutils.NetworkUtils()
        self.netutils._conn_attr = mock.MagicMock()
        self.netutils._jobutils = mock.MagicMock()

    def test_init_caches(self):
        self.netutils._switches = {}
        self.netutils._switch_ports = {}
        self.netutils._vlan_sds = {}
        self.netutils._vsid_sds = {}
        conn = self.netutils._conn

        mock_vswitch = mock.MagicMock(ElementName=mock.sentinel.vswitch_name)
        conn.Msvm_VirtualEthernetSwitch.return_value = [mock_vswitch]

        mock_port = mock.MagicMock(ElementName=mock.sentinel.port_name)
        conn.Msvm_EthernetPortAllocationSettingData.return_value = [
            mock_port]

        mock_sd = mock.MagicMock(InstanceID=self._FAKE_INSTANCE_ID)
        mock_bad_sd = mock.MagicMock(InstanceID=self._FAKE_BAD_INSTANCE_ID)
        conn.Msvm_EthernetSwitchPortVlanSettingData.return_value = [
            mock_bad_sd, mock_sd]
        conn.Msvm_EthernetSwitchPortSecuritySettingData.return_value = [
            mock_bad_sd, mock_sd]

        self.netutils.init_caches()

        self.assertEqual({mock.sentinel.vswitch_name: mock_vswitch},
                         self.netutils._switches)
        self.assertEqual({mock.sentinel.port_name: mock_port},
                         self.netutils._switch_ports)
        self.assertEqual([mock_sd], list(self.netutils._vlan_sds.values()))
        self.assertEqual([mock_sd], list(self.netutils._vsid_sds.values()))

    def test_update_cache(self):
        self.netutils._switch_ports[mock.sentinel.other] = mock.sentinel.port
        conn = self.netutils._conn
        mock_port = mock.MagicMock(ElementName=mock.sentinel.port_name)
        conn.Msvm_EthernetPortAllocationSettingData.return_value = [
            mock_port]

        self.netutils.update_cache()

        self.assertEqual({mock.sentinel.port_name: mock_port},
                         self.netutils._switch_ports)

        # assert that other networkutils have the same cache.
        netutils = networkutils.NetworkUtils()
        self.assertEqual({mock.sentinel.port_name: mock_port},
                         netutils._switch_ports)

    def test_clear_port_sg_acls_cache(self):
        self.netutils._sg_acl_sds[mock.sentinel.port_id] = [mock.sentinel.acl]
        self.netutils.clear_port_sg_acls_cache(mock.sentinel.port_id)
        self.assertNotIn(mock.sentinel.acl, self.netutils._sg_acl_sds)

    @mock.patch.object(networkutils.NetworkUtils, '_get_vswitch_external_port')
    def test_get_vswitch_external_network_name(self, mock_get_vswitch_port):
        mock_get_vswitch_port.return_value.ElementName = (
            mock.sentinel.network_name)
        result = self.netutils.get_vswitch_external_network_name(
            mock.sentinel.vswitch_name)
        self.assertEqual(mock.sentinel.network_name, result)

    def test_get_vswitch_external_port(self):
        vswitch = mock.MagicMock(Name=mock.sentinel.vswitch_name)
        self.netutils._conn.Msvm_VirtualEthernetSwitch.return_value = [vswitch]

        conn = self.netutils._conn
        ext_port = mock.MagicMock()
        lan_endpoint_assoc1 = mock.MagicMock()
        lan_endpoint_assoc2 = mock.Mock(SystemName=mock.sentinel.vswitch_name)
        self.netutils._conn.Msvm_ExternalEthernetPort.return_value = [ext_port]
        conn.Msvm_EthernetDeviceSAPImplementation.return_value = [
            lan_endpoint_assoc1]
        conn.Msvm_ActiveConnection.return_value = [
            mock.Mock(Antecedent=lan_endpoint_assoc2)]

        result = self.netutils._get_vswitch_external_port(mock.sentinel.name)
        self.assertEqual(ext_port, result)
        conn.Msvm_EthernetDeviceSAPImplementation.assert_called_once_with(
            Antecedent=ext_port.path_.return_value)
        conn.Msvm_ActiveConnection.assert_called_once_with(
            Dependent=lan_endpoint_assoc1.Dependent.path_.return_value)

    def test_vswitch_port_needed(self):
        self.assertFalse(self.netutils.vswitch_port_needed())

    @mock.patch.object(networkutils.NetworkUtils, '_get_vnic_settings')
    def test_get_vnic_mac_address(self, mock_get_vnic_settings):
        mock_vnic = mock.MagicMock(Address=mock.sentinel.mac_address)
        mock_get_vnic_settings.return_value = mock_vnic

        actual_mac_address = self.netutils.get_vnic_mac_address(
            mock.sentinel.switch_port_name)
        self.assertEqual(mock.sentinel.mac_address, actual_mac_address)

    @ddt.data([], [mock.sentinel.nic_sd])
    def test_get_vnic_settings(self, nic_sds):
        mock_nic_sd = self.netutils._conn.Msvm_SyntheticEthernetPortSettingData
        mock_nic_sd.return_value = nic_sds

        if not nic_sds:
            self.assertRaises(exceptions.HyperVvNicNotFound,
                              self.netutils._get_vnic_settings,
                              mock.sentinel.vnic_name)
        else:
            nic_sd = self.netutils._get_vnic_settings(mock.sentinel.vnic_name)
            self.assertEqual(mock.sentinel.nic_sd, nic_sd)

        mock_nic_sd.assert_called_once_with(
            ElementName=mock.sentinel.vnic_name)

    @mock.patch.object(networkutils, 'patcher')
    @mock.patch.object(networkutils.tpool, 'execute')
    @mock.patch.object(networkutils.NetworkUtils, '_get_event_wql_query')
    def test_get_vnic_event_listener(self, mock_get_event_query,
                                     mock_execute, mock_patcher):
        event = mock.MagicMock()
        port_class = self.netutils._conn.Msvm_SyntheticEthernetPortSettingData
        wmi_event_listener = port_class.watch_for.return_value
        mock_execute.side_effect = [exceptions.x_wmi_timed_out, event]

        # callback will raise an exception in order to stop iteration in the
        # listener.
        callback = mock.MagicMock(side_effect=TypeError)

        returned_listener = self.netutils.get_vnic_event_listener(
            self.netutils.EVENT_TYPE_CREATE)
        self.assertRaises(TypeError, returned_listener, callback)

        mock_get_event_query.assert_called_once_with(
            cls=self.netutils._VNIC_SET_DATA,
            event_type=self.netutils.EVENT_TYPE_CREATE,
            timeframe=2)
        port_class.watch_for.assert_called_once_with(
            mock_get_event_query.return_value)
        mock_execute.assert_has_calls(
            [mock.call(wmi_event_listener,
                       self.netutils._VNIC_LISTENER_TIMEOUT_MS)] * 2)
        callback.assert_called_once_with(event.ElementName)

    def test_get_event_wql_query(self):
        expected = ("SELECT * FROM %(event_type)s WITHIN %(timeframe)s "
                    "WHERE TargetInstance ISA '%(class)s' AND "
                    "%(like)s" % {
                        'class': "FakeClass",
                        'event_type': self.netutils.EVENT_TYPE_CREATE,
                        'like': "TargetInstance.foo LIKE 'bar%'",
                        'timeframe': 2})

        query = self.netutils._get_event_wql_query(
            "FakeClass", self.netutils.EVENT_TYPE_CREATE, like=dict(foo="bar"))

        self.assertEqual(expected, query)

    def test_connect_vnic_to_vswitch_found(self):
        self._test_connect_vnic_to_vswitch(True)

    def test_connect_vnic_to_vswitch_not_found(self):
        self._test_connect_vnic_to_vswitch(False)

    def _test_connect_vnic_to_vswitch(self, found):
        self.netutils._get_vnic_settings = mock.MagicMock()

        if not found:
            mock_vm = mock.MagicMock()
            self.netutils._get_vm_from_res_setting_data = mock.MagicMock(
                return_value=mock_vm)
            self.netutils._add_virt_resource = mock.MagicMock()
        else:
            self.netutils._modify_virt_resource = mock.MagicMock()

        self.netutils._get_vswitch = mock.MagicMock()
        mock_port = self._mock_get_switch_port_alloc(found=found)
        mock_port.HostResource = []

        self.netutils.connect_vnic_to_vswitch(self._FAKE_VSWITCH_NAME,
                                              self._FAKE_PORT_NAME)

        if not found:
            mock_add_resource = self.netutils._jobutils.add_virt_resource
            mock_add_resource.assert_called_once_with(mock_port, mock_vm)
        else:
            mock_modify_resource = self.netutils._jobutils.modify_virt_resource
            mock_modify_resource.assert_called_once_with(mock_port)

    def test_connect_vnic_to_vswitch_already_connected(self):
        mock_port = self._mock_get_switch_port_alloc()
        mock_port.HostResource = [mock.sentinel.vswitch_path]

        self.netutils.connect_vnic_to_vswitch(mock.sentinel.switch_name,
                                              mock.sentinel.port_name)

        self.assertFalse(self.netutils._jobutils.modify_virt_resource.called)

    def _mock_get_switch_port_alloc(self, found=True):
        mock_port = mock.MagicMock()
        patched = mock.patch.object(
            self.netutils, '_get_switch_port_allocation',
            return_value=(mock_port, found))
        patched.start()
        self.addCleanup(patched.stop)
        return mock_port

    def test_get_vm_from_res_setting_data(self):
        fake_res_set_instance_id = "Microsoft:GUID\\SpecificData"
        fake_vm_set_instance_id = "Microsoft:GUID"
        res_setting_data = mock.Mock(InstanceID=fake_res_set_instance_id)
        conn = self.netutils._conn
        mock_setting_data = conn.Msvm_VirtualSystemSettingData.return_value

        resulted_vm = self.netutils._get_vm_from_res_setting_data(
            res_setting_data)

        conn.Msvm_VirtualSystemSettingData.assert_called_once_with(
            InstanceID=fake_vm_set_instance_id)
        conn.Msvm_ComputerSystem.assert_called_once_with(
            Name=mock_setting_data[0].ConfigurationID)
        expected_result = conn.Msvm_ComputerSystem.return_value[0]
        self.assertEqual(expected_result, resulted_vm)

    def test_remove_switch_port(self):
        mock_sw_port = self._mock_get_switch_port_alloc()
        self.netutils._switch_ports[self._FAKE_PORT_NAME] = mock_sw_port
        self.netutils._vlan_sds[mock_sw_port.InstanceID] = mock.MagicMock()
        self.netutils._jobutils.remove_virt_resource.side_effect = (
            exceptions.x_wmi)

        self.netutils.remove_switch_port(self._FAKE_PORT_NAME, False)

        self.netutils._jobutils.remove_virt_resource.assert_called_once_with(
            mock_sw_port)
        self.assertNotIn(self._FAKE_PORT_NAME, self.netutils._switch_ports)
        self.assertNotIn(mock_sw_port.InstanceID, self.netutils._vlan_sds)

    def test_get_vswitch(self):
        self.netutils._conn.Msvm_VirtualEthernetSwitch.return_value = [
            self._FAKE_VSWITCH]
        vswitch = self.netutils._get_vswitch(self._FAKE_VSWITCH_NAME)

        self.assertEqual({self._FAKE_VSWITCH_NAME: self._FAKE_VSWITCH},
                         self.netutils._switches)
        self.assertEqual(self._FAKE_VSWITCH, vswitch)

    def test_get_vswitch_not_found(self):
        self.netutils._switches = {}
        self.netutils._conn.Msvm_VirtualEthernetSwitch.return_value = []
        self.assertRaises(exceptions.HyperVException,
                          self.netutils._get_vswitch,
                          self._FAKE_VSWITCH_NAME)

    @mock.patch.object(networkutils.NetworkUtils,
                       '_create_default_setting_data')
    def _check_set_vswitch_port_vlan_id(self, mock_create_default_sd,
                                        missing_vlan=False):
        mock_port = self._mock_get_switch_port_alloc(found=True)
        old_vlan_settings = mock.MagicMock()
        if missing_vlan:
            side_effect = [old_vlan_settings, None]
        else:
            side_effect = [old_vlan_settings, old_vlan_settings]
        self.netutils._get_vlan_setting_data_from_port_alloc = mock.MagicMock(
            side_effect=side_effect)
        mock_vlan_settings = mock.MagicMock()
        mock_create_default_sd.return_value = mock_vlan_settings

        if missing_vlan:
            self.assertRaises(exceptions.HyperVException,
                              self.netutils.set_vswitch_port_vlan_id,
                              self._FAKE_VLAN_ID, self._FAKE_PORT_NAME)
        else:
            self.netutils.set_vswitch_port_vlan_id(self._FAKE_VLAN_ID,
                                                   self._FAKE_PORT_NAME)

        mock_remove_feature = self.netutils._jobutils.remove_virt_feature
        mock_remove_feature.assert_called_once_with(old_vlan_settings)
        mock_add_feature = self.netutils._jobutils.add_virt_feature
        mock_add_feature.assert_called_once_with(mock_vlan_settings, mock_port)

    def test_set_vswitch_port_vlan_id(self):
        self._check_set_vswitch_port_vlan_id()

    def test_set_vswitch_port_vlan_id_missing(self):
        self._check_set_vswitch_port_vlan_id(missing_vlan=True)

    @mock.patch.object(networkutils.NetworkUtils,
                       '_get_vlan_setting_data_from_port_alloc')
    def test_set_vswitch_port_vlan_id_already_set(self, mock_get_vlan_sd):
        self._mock_get_switch_port_alloc()
        mock_get_vlan_sd.return_value = mock.MagicMock(
            AccessVlanId=mock.sentinel.vlan_id,
            OperationMode=self.netutils._OPERATION_MODE_ACCESS)

        self.netutils.set_vswitch_port_vlan_id(mock.sentinel.vlan_id,
                                               mock.sentinel.port_name)

        mock_remove_feature = self.netutils._jobutils.remove_virt_feature
        self.assertFalse(mock_remove_feature.called)
        mock_add_feature = self.netutils._jobutils.add_virt_feature
        self.assertFalse(mock_add_feature.called)

    @mock.patch.object(networkutils.NetworkUtils,
                       '_get_security_setting_data_from_port_alloc')
    @mock.patch.object(networkutils.NetworkUtils,
                       '_create_default_setting_data')
    def _check_set_vswitch_port_vsid(self, mock_create_default_sd,
                                     mock_get_security_sd, missing_vsid=False):
        mock_port_alloc = self._mock_get_switch_port_alloc()

        mock_vsid_settings = mock.MagicMock()
        if missing_vsid:
            side_effect = [mock_vsid_settings, None]
        else:
            side_effect = [mock_vsid_settings, mock_vsid_settings]

        mock_get_security_sd.side_effect = side_effect
        mock_create_default_sd.return_value = mock_vsid_settings

        if missing_vsid:
            self.assertRaises(exceptions.HyperVException,
                              self.netutils.set_vswitch_port_vsid,
                              mock.sentinel.vsid,
                              mock.sentinel.switch_port_name)
        else:
            self.netutils.set_vswitch_port_vsid(mock.sentinel.vsid,
                                                mock.sentinel.switch_port_name)

        mock_remove_feature = self.netutils._jobutils.remove_virt_feature
        mock_remove_feature.assert_called_once_with(mock_vsid_settings)
        mock_add_feature = self.netutils._jobutils.add_virt_feature
        mock_add_feature.assert_called_once_with(mock_vsid_settings,
                                                 mock_port_alloc)

    def test_set_vswitch_port_vsid(self):
        self._check_set_vswitch_port_vsid()

    def test_set_vswitch_port_vsid_missing(self):
        self._check_set_vswitch_port_vsid(missing_vsid=True)

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    def test_set_vswitch_port_vsid_already_set(self, mock_get_elem_assoc_cls):
        self._mock_get_switch_port_alloc()

        mock_vsid_settings = mock.MagicMock(VirtualSubnetId=mock.sentinel.vsid)
        mock_get_elem_assoc_cls.return_value = (mock_vsid_settings, True)

        self.netutils.set_vswitch_port_vsid(mock.sentinel.vsid,
                                            mock.sentinel.switch_port_name)

        self.assertFalse(self.netutils._jobutils.add_virt_feature.called)

    @mock.patch.object(networkutils.NetworkUtils,
                       '_get_setting_data_from_port_alloc')
    def test_get_vlan_setting_data_from_port_alloc(self, mock_get_sd):
        mock_port = mock.MagicMock()
        result = self.netutils._get_vlan_setting_data_from_port_alloc(
            mock_port)

        self.assertEqual(mock_get_sd.return_value, result)
        mock_get_sd.assert_called_once_with(mock_port, self.netutils._vsid_sds,
                                            self.netutils._PORT_VLAN_SET_DATA)

    @mock.patch.object(networkutils.NetworkUtils,
                       '_get_setting_data_from_port_alloc')
    def test_get_security_setting_data_from_port_alloc(self, mock_get_sd):
        mock_port = mock.MagicMock()
        result = self.netutils._get_security_setting_data_from_port_alloc(
            mock_port)

        self.assertEqual(mock_get_sd.return_value, result)
        mock_get_sd.assert_called_once_with(
            mock_port, self.netutils._vsid_sds,
            self.netutils._PORT_SECURITY_SET_DATA)

    def test_get_setting_data_from_port_alloc_cached(self):
        mock_port = mock.MagicMock(InstanceID=mock.sentinel.InstanceID)
        cache = {mock_port.InstanceID: mock.sentinel.sd_object}

        result = self.netutils._get_setting_data_from_port_alloc(
            mock_port, cache, mock.sentinel.data_class)

        self.assertEqual(mock.sentinel.sd_object, result)

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    def test_get_setting_data_from_port_alloc(self, mock_get_elem_assoc_cls):
        sd_object = mock.MagicMock()
        mock_port = mock.MagicMock(InstanceID=mock.sentinel.InstanceID)
        mock_get_elem_assoc_cls.return_value = [sd_object]
        cache = {}
        result = self.netutils._get_setting_data_from_port_alloc(
            mock_port, cache, mock.sentinel.data_class)

        mock_get_elem_assoc_cls.assert_called_once_with(
            self.netutils._conn, mock.sentinel.data_class,
            element_instance_id=mock.sentinel.InstanceID)
        self.assertEqual(sd_object, result)
        self.assertEqual(sd_object, cache[mock.sentinel.InstanceID])

    def test_get_switch_port_allocation_cached(self):
        self.netutils._switch_ports[mock.sentinel.port_name] = (
            mock.sentinel.port)

        port, found = self.netutils._get_switch_port_allocation(
            mock.sentinel.port_name)

        self.assertEqual(mock.sentinel.port, port)
        self.assertTrue(found)

    @mock.patch.object(networkutils.NetworkUtils, '_get_setting_data')
    def test_get_switch_port_allocation(self, mock_get_set_data):
        mock_get_set_data.return_value = (mock.sentinel.port, True)

        port, found = self.netutils._get_switch_port_allocation(
            mock.sentinel.port_name)

        self.assertEqual(mock.sentinel.port, port)
        self.assertTrue(found)
        self.assertIn(mock.sentinel.port_name, self.netutils._switch_ports)
        mock_get_set_data.assert_called_once_with(
            self.netutils._PORT_ALLOC_SET_DATA, mock.sentinel.port_name, False)

    @mock.patch.object(networkutils.NetworkUtils, '_get_setting_data')
    def test_get_switch_port_allocation_expected(self, mock_get_set_data):
        self.netutils._switch_ports = {}
        mock_get_set_data.return_value = (None, False)

        self.assertRaises(exceptions.HyperVPortNotFoundException,
                          self.netutils._get_switch_port_allocation,
                          mock.sentinel.port_name, expected=True)
        mock_get_set_data.assert_called_once_with(
            self.netutils._PORT_ALLOC_SET_DATA, mock.sentinel.port_name, False)

    def test_get_setting_data(self):
        self.netutils._get_first_item = mock.MagicMock(return_value=None)

        mock_data = mock.MagicMock()
        self.netutils._get_default_setting_data = mock.MagicMock(
            return_value=mock_data)

        ret_val = self.netutils._get_setting_data(self._FAKE_CLASS_NAME,
                                                  self._FAKE_ELEMENT_NAME,
                                                  True)

        self.assertEqual(ret_val, (mock_data, False))

    def test_create_default_setting_data(self):
        result = self.netutils._create_default_setting_data('FakeClass')

        fake_class = self.netutils._conn.FakeClass
        self.assertEqual(fake_class.new.return_value, result)
        fake_class.new.assert_called_once_with()

    def test_add_metrics_collection_acls(self):
        mock_port = self._mock_get_switch_port_alloc()
        mock_acl = mock.MagicMock()

        with mock.patch.multiple(
            self.netutils,
            _create_default_setting_data=mock.Mock(
                return_value=mock_acl)):

            self.netutils.add_metrics_collection_acls(self._FAKE_PORT_NAME)

            mock_add_feature = self.netutils._jobutils.add_virt_feature
            actual_calls = len(mock_add_feature.mock_calls)
            self.assertEqual(4, actual_calls)
            mock_add_feature.assert_called_with(mock_acl, mock_port)

    @mock.patch.object(networkutils.NetworkUtils, '_is_port_vm_started')
    def test_is_metrics_collection_allowed_true(self, mock_is_started):
        mock_acl = mock.MagicMock()
        mock_acl.Action = self.netutils._ACL_ACTION_METER
        self._test_is_metrics_collection_allowed(
            mock_vm_started=mock_is_started,
            acls=[mock_acl, mock_acl],
            expected_result=True)

    @mock.patch.object(networkutils.NetworkUtils, '_is_port_vm_started')
    def test_test_is_metrics_collection_allowed_false(self, mock_is_started):
        self._test_is_metrics_collection_allowed(
            mock_vm_started=mock_is_started,
            acls=[],
            expected_result=False)

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    def _test_is_metrics_collection_allowed(self, mock_get_elem_assoc_cls,
                                            mock_vm_started, acls,
                                            expected_result):
        mock_port = self._mock_get_switch_port_alloc()
        mock_acl = mock.MagicMock()
        mock_acl.Action = self.netutils._ACL_ACTION_METER

        mock_get_elem_assoc_cls.return_value = acls
        mock_vm_started.return_value = True

        result = self.netutils.is_metrics_collection_allowed(
            self._FAKE_PORT_NAME)
        self.assertEqual(expected_result, result)
        mock_get_elem_assoc_cls.assert_called_once_with(
            self.netutils._conn, self.netutils._PORT_ALLOC_ACL_SET_DATA,
            element_instance_id=mock_port.InstanceID)

    def test_is_port_vm_started_true(self):
        self._test_is_port_vm_started(self.netutils._HYPERV_VM_STATE_ENABLED,
                                      True)

    def test_is_port_vm_started_false(self):
        self._test_is_port_vm_started(self._FAKE_HYPERV_VM_STATE, False)

    def _test_is_port_vm_started(self, vm_state, expected_result):
        mock_svc = self.netutils._conn.Msvm_VirtualSystemManagementService()[0]
        mock_port = mock.MagicMock()
        mock_vmsettings = mock.MagicMock()
        mock_summary = mock.MagicMock()
        mock_summary.EnabledState = vm_state
        mock_vmsettings.path_.return_value = self._FAKE_RES_PATH

        self.netutils._conn.Msvm_VirtualSystemSettingData.return_value = [
            mock_vmsettings]
        mock_svc.GetSummaryInformation.return_value = (self._FAKE_RET_VAL,
                                                       [mock_summary])

        result = self.netutils._is_port_vm_started(mock_port)
        self.assertEqual(expected_result, result)
        mock_svc.GetSummaryInformation.assert_called_once_with(
            [self.netutils._VM_SUMMARY_ENABLED_STATE],
            [self._FAKE_RES_PATH])

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    @mock.patch.object(networkutils.NetworkUtils, '_bind_security_rules')
    def test_create_security_rules(self, mock_bind, mock_get_elem_assoc_cls):
        (m_port, m_acl) = self._setup_security_rule_test(
            mock_get_elem_assoc_cls)
        fake_rule = mock.MagicMock()

        self.netutils.create_security_rules(self._FAKE_PORT_NAME, fake_rule)
        mock_bind.assert_called_once_with(m_port, fake_rule)

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    @mock.patch.object(networkutils.NetworkUtils, '_create_security_acl')
    @mock.patch.object(networkutils.NetworkUtils, '_get_new_weights')
    @mock.patch.object(networkutils.NetworkUtils, '_filter_security_acls')
    def test_bind_security_rules(self, mock_filtered_acls, mock_get_weights,
                                 mock_create_acl, mock_get_elem_assoc_cls):
        m_port = mock.MagicMock()
        m_acl = mock.MagicMock()
        mock_get_elem_assoc_cls.return_value = [m_acl]
        mock_filtered_acls.return_value = []
        mock_get_weights.return_value = [mock.sentinel.FAKE_WEIGHT]
        mock_create_acl.return_value = m_acl
        fake_rule = mock.MagicMock()

        self.netutils._bind_security_rules(m_port, [fake_rule])

        mock_create_acl.assert_called_once_with(fake_rule,
                                                mock.sentinel.FAKE_WEIGHT)
        mock_add_features = self.netutils._jobutils.add_multiple_virt_features
        mock_add_features.assert_called_once_with([m_acl], m_port)
        mock_get_elem_assoc_cls.assert_called_once_with(
            self.netutils._conn, self.netutils._PORT_EXT_ACL_SET_DATA,
            element_instance_id=m_port.InstanceID)

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    @mock.patch.object(networkutils.NetworkUtils, '_get_new_weights')
    @mock.patch.object(networkutils.NetworkUtils, '_filter_security_acls')
    def test_bind_security_rules_existent(self, mock_filtered_acls,
                                          mock_get_weights,
                                          mock_get_elem_assoc_cls):
        m_port = mock.MagicMock()
        m_acl = mock.MagicMock()
        mock_get_elem_assoc_cls.return_value = [m_acl]
        mock_filtered_acls.return_value = [m_acl]
        fake_rule = mock.MagicMock()

        self.netutils._bind_security_rules(m_port, [fake_rule])
        mock_filtered_acls.assert_called_once_with(fake_rule, [m_acl])
        mock_get_weights.assert_called_once_with([fake_rule], [m_acl])
        mock_get_elem_assoc_cls.assert_called_once_with(
            self.netutils._conn, self.netutils._PORT_EXT_ACL_SET_DATA,
            element_instance_id=m_port.InstanceID)

    def test_get_port_security_acls_cached(self):
        mock_port = mock.MagicMock(ElementName=mock.sentinel.port_name)
        self.netutils._sg_acl_sds = {
            mock.sentinel.port_name: [mock.sentinel.fake_acl]}

        acls = self.netutils._get_port_security_acls(mock_port)

        self.assertEqual([mock.sentinel.fake_acl], acls)

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    def test_get_port_security_acls(self, mock_get_elem_assoc_cls):
        self.netutils._sg_acl_sds = {}
        mock_port = mock.MagicMock()
        mock_get_elem_assoc_cls.return_value = [mock.sentinel.fake_acl]

        acls = self.netutils._get_port_security_acls(mock_port)

        self.assertEqual([mock.sentinel.fake_acl], acls)
        self.assertEqual({mock_port.ElementName: [mock.sentinel.fake_acl]},
                         self.netutils._sg_acl_sds)
        mock_get_elem_assoc_cls.assert_called_once_with(
            self.netutils._conn, self.netutils._PORT_EXT_ACL_SET_DATA,
            element_instance_id=mock_port.InstanceID)

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    @mock.patch.object(networkutils.NetworkUtils, '_filter_security_acls')
    def test_remove_security_rules(self, mock_filter, mock_get_elem_assoc_cls):
        mock_acl = self._setup_security_rule_test(mock_get_elem_assoc_cls)[1]
        fake_rule = mock.MagicMock()
        mock_filter.return_value = [mock_acl]

        self.netutils.remove_security_rules(self._FAKE_PORT_NAME, [fake_rule])

        mock_remove_features = (
            self.netutils._jobutils.remove_multiple_virt_features)
        mock_remove_features.assert_called_once_with([mock_acl])

    @mock.patch.object(_wqlutils, 'get_element_associated_class')
    def test_remove_all_security_rules(self, mock_get_elem_assoc_cls):
        mock_acl = self._setup_security_rule_test(mock_get_elem_assoc_cls)[1]
        self.netutils.remove_all_security_rules(self._FAKE_PORT_NAME)
        mock_remove_features = (
            self.netutils._jobutils.remove_multiple_virt_features)
        mock_remove_features.assert_called_once_with([mock_acl])

    @mock.patch.object(networkutils.NetworkUtils,
                       '_create_default_setting_data')
    def test_create_security_acl(self, mock_get_set_data):
        mock_acl = mock_get_set_data.return_value
        fake_rule = mock.MagicMock()
        fake_rule.to_dict.return_value = {"Action": self._FAKE_ACL_ACT}

        self.netutils._create_security_acl(fake_rule, self._FAKE_WEIGHT)
        mock_acl.set.assert_called_once_with(Action=self._FAKE_ACL_ACT)

    def _setup_security_rule_test(self, mock_get_elem_assoc_cls):
        mock_port = self._mock_get_switch_port_alloc()
        mock_acl = mock.MagicMock()
        mock_get_elem_assoc_cls.return_value = [mock_acl]

        self.netutils._filter_security_acls = mock.MagicMock(
            return_value=[mock_acl])

        return (mock_port, mock_acl)

    def test_filter_acls(self):
        mock_acl = mock.MagicMock()
        mock_acl.Action = self._FAKE_ACL_ACT
        mock_acl.Applicability = self.netutils._ACL_APPLICABILITY_LOCAL
        mock_acl.Direction = self._FAKE_ACL_DIR
        mock_acl.AclType = self._FAKE_ACL_TYPE
        mock_acl.RemoteAddress = self._FAKE_REMOTE_ADDR

        acls = [mock_acl, mock_acl]
        good_acls = self.netutils._filter_acls(
            acls, self._FAKE_ACL_ACT, self._FAKE_ACL_DIR,
            self._FAKE_ACL_TYPE, self._FAKE_REMOTE_ADDR)
        bad_acls = self.netutils._filter_acls(
            acls, self._FAKE_ACL_ACT, self._FAKE_ACL_DIR, self._FAKE_ACL_TYPE)

        self.assertEqual(acls, good_acls)
        self.assertEqual([], bad_acls)

    def test_get_new_weights_allow(self):
        actual = self.netutils._get_new_weights([mock.ANY, mock.ANY], mock.ANY)
        self.assertEqual([0, 0], actual)


class TestNetworkUtilsR2(test_base.OsWinBaseTestCase):

    def setUp(self):
        super(TestNetworkUtilsR2, self).setUp()
        self.netutils = networkutils.NetworkUtilsR2()
        self.netutils._conn_attr = mock.MagicMock()

    @mock.patch.object(networkutils.NetworkUtilsR2,
                       '_create_default_setting_data')
    def test_create_security_acl(self, mock_create_default_setting_data):
        sg_rule = mock.MagicMock()
        sg_rule.to_dict.return_value = {}

        acl = self.netutils._create_security_acl(sg_rule, mock.sentinel.weight)

        self.assertEqual(mock.sentinel.weight, acl.Weight)

    def test_get_new_weights_no_acls_deny(self):
        mock_rule = mock.MagicMock(Action=self.netutils._ACL_ACTION_DENY)
        actual = self.netutils._get_new_weights([mock_rule], [])
        self.assertEqual([1], actual)

    def test_get_new_weights_no_acls_allow(self):
        mock_rule = mock.MagicMock(Action=self.netutils._ACL_ACTION_ALLOW)
        actual = self.netutils._get_new_weights([mock_rule, mock_rule], [])

        expected = [self.netutils._MAX_WEIGHT - 1,
                    self.netutils._MAX_WEIGHT - 2]
        self.assertEqual(expected, actual)

    def test_get_new_weights_deny(self):
        mock_rule = mock.MagicMock(Action=self.netutils._ACL_ACTION_DENY)
        mockacl1 = mock.MagicMock(Action=self.netutils._ACL_ACTION_DENY,
                                  Weight=1)
        mockacl2 = mock.MagicMock(Action=self.netutils._ACL_ACTION_DENY,
                                  Weight=3)

        actual = self.netutils._get_new_weights([mock_rule, mock_rule],
                                                [mockacl1, mockacl2])

        self.assertEqual([2, 4], actual)

    def test_get_new_weights_allow(self):
        mock_rule = mock.MagicMock(Action=self.netutils._ACL_ACTION_ALLOW)
        mockacl = mock.MagicMock(Action=self.netutils._ACL_ACTION_ALLOW,
                                 Weight=self.netutils._MAX_WEIGHT - 3)

        actual = self.netutils._get_new_weights([mock_rule, mock_rule],
                                                [mockacl])

        expected = [self.netutils._MAX_WEIGHT - 4,
                    self.netutils._MAX_WEIGHT - 5]
        self.assertEqual(expected, actual)

    def test_get_new_weights_search_available(self):
        mock_rule = mock.MagicMock(Action=self.netutils._ACL_ACTION_ALLOW)
        mockacl1 = mock.MagicMock(Action=self.netutils._ACL_ACTION_ALLOW,
                                  Weight=self.netutils._REJECT_ACLS_COUNT + 1)
        mockacl2 = mock.MagicMock(Action=self.netutils._ACL_ACTION_ALLOW,
                                  Weight=self.netutils._MAX_WEIGHT - 1)

        actual = self.netutils._get_new_weights([mock_rule],
                                                [mockacl1, mockacl2])

        self.assertEqual([self.netutils._MAX_WEIGHT - 2], actual)
