diff --git a/neutron/tests/fullstack/resources/client.py b/neutron/tests/fullstack/resources/client.py index 513a4e61d5b..07176e59165 100644 --- a/neutron/tests/fullstack/resources/client.py +++ b/neutron/tests/fullstack/resources/client.py @@ -301,3 +301,16 @@ class ClientFixture(fixtures.Fixture): spec.update(kwargs) return self._create_resource(resource_type, spec) + + def create_network_log(self, tenant_id, resource_type, + enabled=True, **kwargs): + + spec = {'project_id': tenant_id, + 'resource_type': resource_type, + 'enabled': enabled} + spec.update(kwargs) + + net_log = self.client.create_network_log({'log': spec}) + self.addCleanup( + _safe_method(self.client.delete_network_log), net_log['log']['id']) + return net_log diff --git a/neutron/tests/fullstack/resources/config.py b/neutron/tests/fullstack/resources/config.py index 2e2523ec9e3..ac71532bfc6 100644 --- a/neutron/tests/fullstack/resources/config.py +++ b/neutron/tests/fullstack/resources/config.py @@ -23,6 +23,7 @@ from neutron.plugins.ml2.extensions import qos as qos_ext from neutron.tests.common import config_fixtures from neutron.tests.common.exclusive_resources import port from neutron.tests.common import helpers as c_helpers +from neutron.tests.fullstack import base as fullstack_base PHYSICAL_NETWORK_NAME = "physnet1" @@ -154,7 +155,7 @@ class ML2ConfigFixture(ConfigFixture): class OVSConfigFixture(ConfigFixture): - def __init__(self, env_desc, host_desc, temp_dir, local_ip): + def __init__(self, env_desc, host_desc, temp_dir, local_ip, **kwargs): super(OVSConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='openvswitch_agent.ini') @@ -189,6 +190,14 @@ class OVSConfigFixture(ConfigFixture): if env_desc.qos: self.config['agent']['extensions'] = 'qos' + if env_desc.log: + self.config['agent']['extensions'] = 'log' + test_name = kwargs.get("test_name") + self.config.update({ + 'network_log': { + 'local_output_log_base': + self._generate_temp_log_file(test_name)} + }) def _setUp(self): if self.config['ovs']['of_interface'] == 'native': @@ -214,6 +223,13 @@ class OVSConfigFixture(ConfigFixture): def _generate_tun_peer(self): return utils.get_rand_device_name(prefix='patch-int') + def _generate_temp_log_file(self, test_name): + log_dir_path = fullstack_base.DEFAULT_LOG_DIR + '/' + test_name + if not os.path.exists(log_dir_path): + os.mkdir(log_dir_path, 0o755) + return '%s/%s.log' % (log_dir_path, + utils.get_rand_name(prefix="test-sg-")) + def get_br_int_name(self): return self.config.ovs.integration_bridge diff --git a/neutron/tests/fullstack/resources/environment.py b/neutron/tests/fullstack/resources/environment.py index 15ff2f6d231..68451fc55fa 100644 --- a/neutron/tests/fullstack/resources/environment.py +++ b/neutron/tests/fullstack/resources/environment.py @@ -39,10 +39,11 @@ class EnvironmentDescription(object): service_plugins='router', arp_responder=False, agent_down_time=75, router_scheduler=None, global_mtu=common_const.DEFAULT_NETWORK_MTU, - debug_iptables=False): + debug_iptables=False, log=False): self.network_type = network_type self.l2_pop = l2_pop self.qos = qos + self.log = log self.network_range = None self.mech_drivers = mech_drivers self.arp_responder = arp_responder @@ -53,6 +54,8 @@ class EnvironmentDescription(object): self.debug_iptables = debug_iptables if self.qos: self.service_plugins += ',qos' + if self.log: + self.service_plugins += ',log' @property def tunneling_enabled(self): @@ -134,7 +137,7 @@ class Host(fixtures.Fixture): def setup_host_with_ovs_agent(self): agent_cfg_fixture = config.OVSConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, - self.local_ip) + self.local_ip, test_name=self.test_name) self.useFixture(agent_cfg_fixture) if self.env_desc.tunneling_enabled: diff --git a/neutron/tests/fullstack/test_logging.py b/neutron/tests/fullstack/test_logging.py new file mode 100644 index 00000000000..0567c5fde78 --- /dev/null +++ b/neutron/tests/fullstack/test_logging.py @@ -0,0 +1,192 @@ +# Copyright 2018 Fujitsu Limited +# +# 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 re + +from neutron_lib import constants +from oslo_utils import uuidutils + +from neutron.common import utils +from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( + constants as ovs_const) +from neutron.tests.common import net_helpers +from neutron.tests.fullstack import base +from neutron.tests.fullstack.resources import environment +from neutron.tests.fullstack.resources import machine +from neutron.tests.unit import testlib_api + +load_tests = testlib_api.module_load_tests + + +class BaseLoggingTestCase(base.BaseFullStackTestCase): + of_interface = None + number_of_hosts = 1 + + def setUp(self): + host_desc = [ + environment.HostDescription( + of_interface=self.of_interface, + l2_agent_type=constants.AGENT_TYPE_OVS, + firewall_driver='openvswitch', + dhcp_agent=True) for _ in range(self.number_of_hosts)] + env_desc = environment.EnvironmentDescription( + mech_drivers='openvswitch', log=True) + env = environment.Environment(env_desc, host_desc) + super(BaseLoggingTestCase, self).setUp(env) + + self.tenant_id = uuidutils.generate_uuid() + self.network = self.safe_client.create_network( + self.tenant_id, 'network-test') + self.subnet = self.safe_client.create_subnet( + self.tenant_id, self.network['id'], + cidr='10.0.0.0/24', + gateway_ip='10.0.0.1', + name='subnet-test') + + def assert_no_connection(self, *args, **kwargs): + netcat = net_helpers.NetcatTester(*args, **kwargs) + try: + utils.wait_until_true(netcat.test_no_connectivity) + finally: + netcat.stop_processes() + + def _wait_for_log_rules_applied(self, vm, table, actions): + + def _is_log_flow_set(table, actions): + flows = vm.bridge.dump_flows_for_table(table) + flows_list = flows.splitlines() + pattern = re.compile( + r"^.* table=%s.* actions=%s" % (table, actions)) + for flow in flows_list: + if pattern.match(flow.strip()): + return True + return False + utils.wait_until_true(lambda: _is_log_flow_set(table, actions)) + + def _check_log(self, log_id, action, regex_str=None): + + config = self.environment.hosts[0].ovs_agent.agent_config + + def _is_log_event(log_id, action, regex_str): + regex_p = re.compile( + r"^.*action=%s.* log_resource_ids=\[[^\]]*%s" % ( + action, log_id) + ".*" + regex_str if regex_str else "") + + with open(config.network_log.local_output_log_base) as f: + for line in f.readlines(): + if regex_p.match(line): + return True + return False + + utils.wait_until_true(lambda: _is_log_event(log_id, action, regex_str)) + + +class TestLogging(BaseLoggingTestCase): + + scenarios = [ + ('ovs-openflow-native', { + 'of_interface': 'native'})] + + def _create_network_log(self, resource_type, + resource_id=None, target_id=None): + return self.safe_client.create_network_log( + tenant_id=self.tenant_id, + name='test-log', + resource_type=resource_type, + resource_id=resource_id, + target_id=target_id) + + def _prepare_vms(self): + + sgs = [self.safe_client.create_security_group(self.tenant_id) + for i in range(2)] + + port1 = self.safe_client.create_port( + self.tenant_id, self.network['id'], + self.environment.hosts[0].hostname, + security_groups=[sgs[0]['id']]) + + port2 = self.safe_client.create_port( + self.tenant_id, self.network['id'], + self.environment.hosts[0].hostname, + security_groups=[sgs[1]['id']]) + + # insert security-group-rules allow icmp + self.safe_client.create_security_group_rule( + self.tenant_id, sgs[0]['id'], + direction=constants.INGRESS_DIRECTION, + ethertype=constants.IPv4, + protocol=constants.PROTO_NAME_ICMP) + + # insert security-group-rules allow icmp + self.safe_client.create_security_group_rule( + self.tenant_id, sgs[1]['id'], + direction=constants.INGRESS_DIRECTION, + ethertype=constants.IPv4, + protocol=constants.PROTO_NAME_ICMP) + + vm1 = self.useFixture( + machine.FakeFullstackMachine( + self.environment.hosts[0], + self.network['id'], + self.tenant_id, + self.safe_client, + neutron_port=port1)) + + vm2 = self.useFixture( + machine.FakeFullstackMachine( + self.environment.hosts[0], + self.network['id'], + self.tenant_id, + self.safe_client, + neutron_port=port2)) + return machine.FakeFullstackMachinesList([vm1, vm2]) + + def test_logging(self): + vms = self._prepare_vms() + + vms.block_until_all_boot() + + sg_log = self._create_network_log(resource_type='security_group') + log_id = sg_log['log']['id'] + for vm in vms: + self._wait_for_log_rules_applied( + vm, ovs_const.ACCEPTED_EGRESS_TRAFFIC_TABLE, + actions=r"resubmit\(,%d\),CONTROLLER:65535" % ( + ovs_const.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)) + self._wait_for_log_rules_applied( + vm, ovs_const.ACCEPTED_INGRESS_TRAFFIC_TABLE, + actions="CONTROLLER:65535") + self._wait_for_log_rules_applied( + vm, ovs_const.DROPPED_TRAFFIC_TABLE, + actions="CONTROLLER:65535") + + # ping all vm + vms.ping_all() + + # check log accept packets for icmp + self._check_log(log_id=log_id, action='ACCEPT') + + # Try to connect from VM1 to VM2 via ssh + self.assert_no_connection( + vms[0].namespace, vms[1].namespace, vms[1].ip, 22, + net_helpers.NetcatTester.TCP) + + # Try to ssh from VM2 to VM1 via ssh + self.assert_no_connection( + vms[1].namespace, vms[0].namespace, vms[0].ip, 22, + net_helpers.NetcatTester.TCP) + + # check log drop packets for ssh + self._check_log(log_id=log_id, action='DROP', regex_str="dst_port=22")