Add script which can clean patch ports between bridges
Once provider bridges are brought up, they should not send any traffic to the integration bridge until ovs-agent configures the bridges. When node is rebooted, patch ports to br-int are preserved because they are stored in ovsdb. This patch adds script which can removes such patch ports. This script can be used e.g. in some systemd unit files or ifcfg scripts and be run during the node boot process. Co-authored-by: Jakub Libosvar <jlibosva@redhat.com> Co-authored-by: Yatin Karel <ykarel@redhat.com> Co-authored-by: Bogdan Dobrelya <bdobreli@redhat.com> Change-Id: I99e33e8c9a5146dcae3ac9d98aae0284d4d80f20
This commit is contained in:
parent
8d6c301301
commit
86f6ea347a
neutron
90
neutron/cmd/destroy_patch_ports.py
Normal file
90
neutron/cmd/destroy_patch_ports.py
Normal file
@ -0,0 +1,90 @@
|
||||
# Copyright 2020 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 sys
|
||||
|
||||
from neutron_lib.plugins import utils as p_utils
|
||||
from neutron_lib.utils import helpers
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.common import config as common_config
|
||||
from neutron.conf.agent import common as agent_config
|
||||
from neutron.conf.plugins.ml2.drivers import ovs_conf
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_patch_port_names(bridge_name):
|
||||
int_if_name = p_utils.get_interface_name(
|
||||
bridge_name, prefix=constants.PEER_INTEGRATION_PREFIX)
|
||||
phys_if_name = p_utils.get_interface_name(
|
||||
bridge_name, prefix=constants.PEER_PHYSICAL_PREFIX)
|
||||
|
||||
return int_if_name, phys_if_name
|
||||
|
||||
|
||||
class PatchPortCleaner(object):
|
||||
def __init__(self, config):
|
||||
LOG.debug("Get OVS bridge mappings")
|
||||
mappings = helpers.parse_mappings(config.OVS.bridge_mappings)
|
||||
self.bridges = [ovs_lib.OVSBridge(bridge)
|
||||
for bridge in mappings.values()]
|
||||
self.int_br = ovs_lib.OVSBridge(config.OVS.integration_bridge)
|
||||
|
||||
def destroy_patch_ports(self):
|
||||
if (not self.int_br.bridge_exists(self.int_br.br_name) or
|
||||
self.flows_configured()):
|
||||
# integration bridge hasn't been created by agent yet or it's been
|
||||
# already configured by the agent
|
||||
return
|
||||
for bridge in self.bridges:
|
||||
try:
|
||||
LOG.debug("Remove patch port from bridge %s", bridge.br_name)
|
||||
self._remove_patch_ports_from_int_br(bridge)
|
||||
except Exception as e:
|
||||
LOG.error("Failed to remove patch port from bridge %s: %s",
|
||||
bridge.br_name, e)
|
||||
|
||||
def _remove_patch_ports_from_int_br(self, bridge):
|
||||
int_if_name, phys_if_name = get_patch_port_names(
|
||||
bridge.br_name)
|
||||
int_type = self.int_br.db_get_val(
|
||||
"Interface", int_if_name, "type", log_errors=False)
|
||||
if int_type == 'patch':
|
||||
self.int_br.delete_port(int_if_name)
|
||||
bridge.delete_port(phys_if_name)
|
||||
|
||||
def flows_configured(self):
|
||||
"""Return True if the integration bridge has flows already configured.
|
||||
"""
|
||||
LOG.debug("Get configured flows for integration bridge %s",
|
||||
self.int_br.br_name)
|
||||
return bool(self.int_br.dump_flows_for(table=constants.CANARY_TABLE))
|
||||
|
||||
|
||||
def main():
|
||||
common_config.init(sys.argv[1:])
|
||||
ovs_conf.register_ovs_agent_opts()
|
||||
common_config.setup_logging()
|
||||
agent_config.setup_privsep()
|
||||
port_cleaner = PatchPortCleaner(cfg.CONF)
|
||||
port_cleaner.destroy_patch_ports()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
93
neutron/tests/functional/cmd/test_destroy_patch_ports.py
Normal file
93
neutron/tests/functional/cmd/test_destroy_patch_ports.py
Normal file
@ -0,0 +1,93 @@
|
||||
# Copyright 2020 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from neutron_lib import constants as n_const
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron.cmd import destroy_patch_ports
|
||||
from neutron.common import utils
|
||||
from neutron.conf.plugins.ml2.drivers import ovs_conf
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.functional import base
|
||||
|
||||
|
||||
class TestDestroyPatchPorts(base.BaseSudoTestCase):
|
||||
def setUp(self):
|
||||
super(TestDestroyPatchPorts, self).setUp()
|
||||
self.int_br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
|
||||
bridge_mappings = {}
|
||||
self.bridges = []
|
||||
for network in ('foo', 'bar'):
|
||||
bridge = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
|
||||
self._create_patch_ports_to_int_br(bridge)
|
||||
self.bridges.append(bridge)
|
||||
bridge_mappings[network] = bridge.br_name
|
||||
self.config = self._create_config_file(bridge_mappings)
|
||||
|
||||
def _create_config_file(self, bridge_mappings):
|
||||
config = cfg.ConfigOpts()
|
||||
ovs_conf.register_ovs_agent_opts(config)
|
||||
config.set_override('integration_bridge', self.int_br.br_name, "OVS")
|
||||
config.set_override(
|
||||
'bridge_mappings',
|
||||
','.join(["%s:%s" % (net, br)
|
||||
for net, br in bridge_mappings.items()]),
|
||||
"OVS")
|
||||
|
||||
return config
|
||||
|
||||
def _create_patch_ports_to_int_br(self, bridge):
|
||||
int_if_name, phys_if_name = destroy_patch_ports.get_patch_port_names(
|
||||
bridge.br_name)
|
||||
self.int_br.add_patch_port(
|
||||
int_if_name, constants.NONEXISTENT_PEER)
|
||||
bridge.add_patch_port(
|
||||
phys_if_name, constants.NONEXISTENT_PEER)
|
||||
self.int_br.set_db_attribute(
|
||||
'Interface', int_if_name, 'options', {'peer': phys_if_name})
|
||||
bridge.set_db_attribute(
|
||||
'Interface', phys_if_name, 'options', {'peer': int_if_name})
|
||||
|
||||
def _has_patch_ports(self, bridge):
|
||||
int_if_name, phys_if_name = destroy_patch_ports.get_patch_port_names(
|
||||
bridge.br_name)
|
||||
return (bridge.port_exists(phys_if_name) and
|
||||
self.int_br.port_exists(int_if_name))
|
||||
|
||||
def _assert_has_all_ports(self):
|
||||
self.assertTrue(all(self._has_patch_ports(bridge)
|
||||
for bridge in self.bridges))
|
||||
|
||||
def test_destroy_patch_ports(self):
|
||||
self._assert_has_all_ports()
|
||||
cleaner = destroy_patch_ports.PatchPortCleaner(self.config)
|
||||
cleaner.destroy_patch_ports()
|
||||
self.assertFalse(any(self._has_patch_ports(bridge)
|
||||
for bridge in self.bridges))
|
||||
|
||||
def test_destroy_patch_ports_no_int_br(self):
|
||||
name = utils.get_rand_name(
|
||||
max_length=n_const.DEVICE_NAME_MAX_LEN)
|
||||
self.config.set_override('integration_bridge', name, "OVS")
|
||||
cleaner = destroy_patch_ports.PatchPortCleaner(self.config)
|
||||
cleaner.destroy_patch_ports()
|
||||
|
||||
def test_destroy_patch_ports_canary_flow_on_int_br(self):
|
||||
self.int_br.add_flow(table=constants.CANARY_TABLE, actions="drop")
|
||||
self._assert_has_all_ports()
|
||||
cleaner = destroy_patch_ports.PatchPortCleaner(self.config)
|
||||
cleaner.destroy_patch_ports()
|
||||
self._assert_has_all_ports()
|
Loading…
x
Reference in New Issue
Block a user