165 lines
6.7 KiB
Python
165 lines
6.7 KiB
Python
# 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 time
|
|
|
|
from os_ken.lib.packet import arp
|
|
|
|
from dragonflow.controller.common import constants as const
|
|
from dragonflow.tests.common import constants as test_const
|
|
from dragonflow.tests.common import utils
|
|
from dragonflow.tests.fullstack import test_base
|
|
from dragonflow.tests.fullstack import test_objects as objects
|
|
|
|
|
|
class TestOVSFlowsForActivePortDectionApp(test_base.DFTestBase):
|
|
|
|
def _get_sending_arp_to_controller_flows(self, port_key, arp_op):
|
|
ovs_flows_parser = utils.OvsFlowsParser()
|
|
flows = ovs_flows_parser.dump(self.integration_bridge)
|
|
expected_in_port = "reg6=" + hex(port_key)
|
|
expected_arp_op = "arp_op=" + str(arp_op)
|
|
expected_actions = "CONTROLLER:65535," + "goto_table:" + \
|
|
str(const.L2_LOOKUP_TABLE)
|
|
flows = [flow for flow in flows
|
|
if ((expected_in_port in flow['match']) and
|
|
(expected_arp_op in flow['match']) and
|
|
('arp' in flow['match']) and
|
|
(flow['table'] == str(const.ARP_TABLE)) and
|
|
(flow['actions'] == expected_actions))]
|
|
return flows
|
|
|
|
def _get_sending_arp_reply_to_controller_flows(self, port_key):
|
|
return self._get_sending_arp_to_controller_flows(port_key,
|
|
str(arp.ARP_REPLY))
|
|
|
|
def _get_sending_gratuitous_arp_to_controller_flows(self, port_key):
|
|
return self._get_sending_arp_to_controller_flows(port_key,
|
|
str(arp.ARP_REQUEST))
|
|
|
|
def _check_sending_arp_reply_to_controller_flows(self, port_key, ip=None):
|
|
flows = self._get_sending_arp_reply_to_controller_flows(port_key)
|
|
expected_arp_tpa = '0.0.0.0'
|
|
for flow in flows:
|
|
if ip is not None:
|
|
if expected_arp_tpa not in flow['match']:
|
|
continue
|
|
expected_arp_spa = 'arp_spa=' + ip
|
|
if expected_arp_spa not in flow['match']:
|
|
continue
|
|
return True
|
|
return False
|
|
|
|
def _check_sending_gratuitous_arp_to_controller_flows(self, port_key,
|
|
ip=None):
|
|
flows = self._get_sending_gratuitous_arp_to_controller_flows(port_key)
|
|
for flow in flows:
|
|
if ip is not None:
|
|
expected_arp_spa = 'arp_spa=' + ip
|
|
expected_arp_tpa = 'arp_tpa=' + ip
|
|
if (expected_arp_spa not in flow['match']) or \
|
|
(expected_arp_tpa not in flow['match']):
|
|
continue
|
|
return True
|
|
|
|
return False
|
|
|
|
def test_sending_arp_to_controller_flows(self):
|
|
"""
|
|
Add a VM with allowed address pairs configuration. Verify related
|
|
flows is there.
|
|
"""
|
|
if not self.check_app_loaded("active_port_detection"):
|
|
self.skipTest("ActivePortDetectionApp is not enabled")
|
|
|
|
network = objects.NetworkTestObj(self.neutron, self.nb_api)
|
|
self.addCleanup(network.close)
|
|
network_id = network.create(network={'name': 'aap_test'})
|
|
subnet_obj = objects.SubnetTestObj(self.neutron, self.nb_api,
|
|
network_id)
|
|
self.addCleanup(subnet_obj.close)
|
|
|
|
subnet = {'network_id': network_id,
|
|
'cidr': '192.168.97.0/24',
|
|
'gateway_ip': '192.168.97.1',
|
|
'ip_version': 4,
|
|
'name': 'aap_test',
|
|
'enable_dhcp': True}
|
|
subnet_obj.create(subnet)
|
|
|
|
vm = objects.VMTestObj(self, self.neutron)
|
|
self.addCleanup(vm.close)
|
|
vm_id = vm.create(network=network)
|
|
|
|
vm_port_id = self.vswitch_api.get_port_id_by_vm_id(vm_id)
|
|
self.assertIsNotNone(vm_port_id)
|
|
|
|
vm_port = objects.PortTestObj(self.neutron, self.nb_api, network_id,
|
|
vm_port_id)
|
|
port_num = self.vswitch_api.get_port_ofport_by_id(vm_port_id)
|
|
self.assertIsNotNone(port_num)
|
|
vm_lport = vm_port.get_logical_port()
|
|
self.assertIsNotNone(vm_lport)
|
|
result = self._check_sending_arp_reply_to_controller_flows(
|
|
vm_lport.unique_key)
|
|
self.assertFalse(result)
|
|
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
|
vm_lport.unique_key)
|
|
self.assertFalse(result)
|
|
|
|
ip_address1 = '192.168.97.100'
|
|
mac_address1 = '1A:22:33:44:55:66'
|
|
allowed_address_pairs1 = [{'ip_address': ip_address1,
|
|
'mac_address': mac_address1}]
|
|
vm_port.update({'allowed_address_pairs': allowed_address_pairs1})
|
|
|
|
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
|
|
|
result = self._check_sending_arp_reply_to_controller_flows(
|
|
vm_lport.unique_key, ip_address1)
|
|
self.assertTrue(result)
|
|
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
|
vm_lport.unique_key, ip_address1)
|
|
self.assertTrue(result)
|
|
|
|
ip_address2 = '192.168.97.101'
|
|
allowed_address_pairs2 = [{'ip_address': ip_address2}]
|
|
vm_port.update({'allowed_address_pairs': allowed_address_pairs2})
|
|
|
|
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
|
|
|
result = self._check_sending_arp_reply_to_controller_flows(
|
|
vm_lport.unique_key, ip_address1)
|
|
self.assertFalse(result)
|
|
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
|
vm_lport.unique_key, ip_address1)
|
|
self.assertFalse(result)
|
|
result = self._check_sending_arp_reply_to_controller_flows(
|
|
vm_lport.unique_key, ip_address2)
|
|
self.assertTrue(result)
|
|
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
|
vm_lport.unique_key, ip_address2)
|
|
self.assertTrue(result)
|
|
|
|
vm.close()
|
|
|
|
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
|
|
|
result = self._check_sending_arp_reply_to_controller_flows(
|
|
vm_lport.unique_key, ip_address2)
|
|
self.assertFalse(result)
|
|
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
|
vm_lport.unique_key, ip_address2)
|
|
self.assertFalse(result)
|
|
|
|
network.close()
|