Merge "[OVN] Migrate the OVN tools"

This commit is contained in:
Zuul 2020-02-03 18:27:24 +00:00 committed by Gerrit Code Review
commit f73f39f2cf
4 changed files with 344 additions and 0 deletions

View File

View File

@ -0,0 +1,119 @@
# Copyright 2018 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 os
import sys
from openstack import connection
# TODO(dalvarez): support also GRE
GENEVE_TO_VXLAN_OVERHEAD = 8
def get_connection():
user_domain_id = os.environ.get('OS_USER_DOMAIN_ID', 'default')
project_domain_id = os.environ.get('OS_PROJECT_DOMAIN_ID', 'default')
conn = connection.Connection(auth_url=os.environ['OS_AUTH_URL'],
project_name=os.environ['OS_PROJECT_NAME'],
username=os.environ['OS_USERNAME'],
password=os.environ['OS_PASSWORD'],
user_domain_id=user_domain_id,
project_domain_id=project_domain_id)
return conn
def verify_network_mtu():
print("Verifying the tenant network mtu's")
conn = get_connection()
success = True
for network in conn.network.networks():
if network.provider_physical_network is None and (
network.provider_network_type == 'vxlan') and (
'adapted_mtu' not in network.tags):
print("adapted_mtu tag is not set for the Network "
"[" + str(network.name) + "]")
success = False
if success:
print("All the networks are set to expected mtu value")
else:
print("Some tenant networks need to have their MTU updated to a "
"lower value.")
return success
def update_network_mtu():
print("Updating the tenant network mtu")
conn = get_connection()
for network in conn.network.networks():
try:
if network.provider_physical_network is None and (
network.provider_network_type == 'vxlan') and (
'adapted_mtu' not in network.tags):
print("Updating the mtu and the tag 'adapted_mtu"
" of the network - " + str(network.name))
new_tags = list(network.tags)
new_tags.append('adapted_mtu')
conn.network.update_network(
network,
mtu=int(network.mtu) - GENEVE_TO_VXLAN_OVERHEAD)
conn.network.set_tags(network, new_tags)
except Exception as e:
print("Exception occured while updating the MTU:" + str(e))
return False
return True
def print_usage():
print('Invalid options:')
print('Usage: %s <update|verify> mtu' % sys.argv[0])
def main():
"""Tool for updating the networks MTU's pre migration.
This lowers the MTU of the pre migration VXLAN and GRE networks. The
tool will ignore non-VXLAN/GRE networks, so if you use VLAN for tenant
networks it will be fine if you find this step not doing anything.
This step will go network by network reducing the MTU, and tagging
with adapted_mtu the networks which have been already handled.
Every time a network is updated all the existing L3/DHCP agents
connected to such network will update their internal leg MTU,
instances will start fetching the new MTU as the DHCP T1 timer
expires. As explained before, instances not obeying the DHCP T1
parameter will need to be restarted, and instances with static IP
assignment will need to be manually updated.
"""
if len(sys.argv) < 3:
print_usage()
sys.exit(1)
retval = 1
if sys.argv[1] == "update" and sys.argv[2] == "mtu":
if update_network_mtu():
retval = 0
elif sys.argv[1] == "verify" and sys.argv[2] == "mtu":
if verify_network_mtu():
retval = 0
else:
print_usage()
sys.exit(retval)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,222 @@
# Copyright 2016 Red Hat, Inc.
#
# 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 copy
from neutron_lib.agent import topics
from neutron_lib.plugins import directory
from oslo_config import cfg
from oslo_db import options as db_options
from oslo_log import log as logging
from neutron.conf.agent import securitygroups_rpc
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
from neutron import manager
from neutron import opts as neutron_options
from neutron.plugins.ml2.drivers.ovn.mech_driver import mech_driver
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import impl_idl_ovn
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_db_sync
from neutron.plugins.ml2 import plugin as ml2_plugin
LOG = logging.getLogger(__name__)
class Ml2Plugin(ml2_plugin.Ml2Plugin):
def _setup_dhcp(self):
pass
def _start_rpc_notifiers(self):
# Override the notifier so that when calling the ML2 plugin to create
# resources, it doesn't crash trying to notify subscribers.
self.notifier = AgentNotifierApi(topics.AGENT)
class OVNMechanismDriver(mech_driver.OVNMechanismDriver):
def subscribe(self):
pass
def post_fork_initialize(self, resource, event, trigger, **kwargs):
pass
@property
def ovn_client(self):
if not self._ovn_client:
self._ovn_client = ovn_client.OVNClient(self._nb_ovn, self._sb_ovn)
return self._ovn_client
# Since we are not using the ovn mechanism driver while syncing,
# we override the post and pre commit methods so that original ones are
# not called.
def create_port_precommit(self, context):
pass
def create_port_postcommit(self, context):
port = context.current
self.ovn_client.create_port(port)
def update_port_precommit(self, context):
pass
def update_port_postcommit(self, context):
port = context.current
original_port = context.original
self.ovn_client.update_port(port, original_port)
def delete_port_precommit(self, context):
pass
def delete_port_postcommit(self, context):
port = copy.deepcopy(context.current)
port['network'] = context.network.current
# FIXME(lucasagomes): PortContext does not have a session, therefore
# we need to use the _plugin_context attribute.
self.ovn_client.delete_port(context._plugin_context, port['id'],
port_object=port)
class AgentNotifierApi(object):
"""Default Agent Notifier class for ovn-db-sync-util.
This class implements empty methods so that when creating resources in
the core plugin, the original ones don't get called and don't interfere
with the syncing process.
"""
def __init__(self, topic):
self.topic = topic
self.topic_network_delete = topics.get_topic_name(topic,
topics.NETWORK,
topics.DELETE)
self.topic_port_update = topics.get_topic_name(topic,
topics.PORT,
topics.UPDATE)
self.topic_port_delete = topics.get_topic_name(topic,
topics.PORT,
topics.DELETE)
self.topic_network_update = topics.get_topic_name(topic,
topics.NETWORK,
topics.UPDATE)
def network_delete(self, context, network_id):
pass
def port_update(self, context, port, network_type, segmentation_id,
physical_network):
pass
def port_delete(self, context, port_id):
pass
def network_update(self, context, network):
pass
def security_groups_provider_updated(self, context,
devices_to_udpate=None):
pass
def setup_conf():
conf = cfg.CONF
ml2_group, ml2_opts = neutron_options.list_ml2_conf_opts()[0]
cfg.CONF.register_cli_opts(ml2_opts, ml2_group)
cfg.CONF.register_cli_opts(securitygroups_rpc.security_group_opts,
'SECURITYGROUP')
ovn_group, ovn_opts = ovn_conf.list_opts()[0]
cfg.CONF.register_cli_opts(ovn_opts, group=ovn_group)
db_group, neutron_db_opts = db_options.list_opts()[0]
cfg.CONF.register_cli_opts(neutron_db_opts, db_group)
return conf
def main():
"""Main method for syncing neutron networks and ports with ovn nb db.
This script provides a utility for syncing the OVN Northbound Database
with the Neutron database.
This script is used for the migration from ML2/OVS to ML2/OVN.
"""
conf = setup_conf()
# if no config file is passed or no configuration options are passed
# then load configuration from /etc/neutron/neutron.conf
try:
conf(project='neutron')
except TypeError:
LOG.error('Error parsing the configuration values. Please verify.')
return
logging.setup(conf, 'neutron_ovn_db_sync_util')
LOG.info('Started Neutron OVN db sync')
mode = ovn_conf.get_ovn_neutron_sync_mode()
if mode not in [ovn_db_sync.SYNC_MODE_LOG, ovn_db_sync.SYNC_MODE_REPAIR]:
LOG.error(
'Invalid sync mode : ["%s"]. Should be "log" or "repair"', mode)
return
# Validate and modify core plugin and ML2 mechanism drivers for syncing.
if (cfg.CONF.core_plugin.endswith('.Ml2Plugin') or
cfg.CONF.core_plugin == 'ml2'):
cfg.CONF.core_plugin = (
'neutron.cmd.ovn.neutron_ovn_db_sync_util.Ml2Plugin')
if not cfg.CONF.ml2.mechanism_drivers:
LOG.error('please use --config-file to specify '
'neutron and ml2 configuration file.')
return
if 'ovn' not in cfg.CONF.ml2.mechanism_drivers:
LOG.error('No "ovn" mechanism driver found : "%s".',
cfg.CONF.ml2.mechanism_drivers)
return
cfg.CONF.set_override('mechanism_drivers', ['ovn-sync'], 'ml2')
conf.service_plugins = [
'neutron.services.ovn_l3.plugin.OVNL3RouterPlugin']
else:
LOG.error('Invalid core plugin : ["%s"].', cfg.CONF.core_plugin)
return
try:
conn = impl_idl_ovn.get_connection(impl_idl_ovn.OvsdbNbOvnIdl)
ovn_api = impl_idl_ovn.OvsdbNbOvnIdl(conn)
except RuntimeError:
LOG.error('Invalid --ovn-ovn_nb_connection parameter provided.')
return
try:
sb_conn = impl_idl_ovn.get_connection(impl_idl_ovn.OvsdbSbOvnIdl)
ovn_sb_api = impl_idl_ovn.OvsdbSbOvnIdl(sb_conn)
except RuntimeError:
LOG.error('Invalid --ovn-ovn_sb_connection parameter provided.')
return
manager.init()
core_plugin = directory.get_plugin()
ovn_driver = core_plugin.mechanism_manager.mech_drivers['ovn-sync'].obj
ovn_driver._nb_ovn = ovn_api
ovn_driver._sb_ovn = ovn_sb_api
synchronizer = ovn_db_sync.OvnNbSynchronizer(
core_plugin, ovn_api, ovn_sb_api, mode, ovn_driver)
LOG.info('Sync for Northbound db started with mode : %s', mode)
synchronizer.do_sync()
LOG.info('Sync completed for Northbound db')
sb_synchronizer = ovn_db_sync.OvnSbSynchronizer(
core_plugin, ovn_sb_api, ovn_driver)
LOG.info('Sync for Southbound db started with mode : %s', mode)
sb_synchronizer.do_sync()
LOG.info('Sync completed for Southbound db')

View File

@ -55,6 +55,8 @@ console_scripts =
neutron-sanity-check = neutron.cmd.sanity_check:main
neutron-status = neutron.cmd.status:main
neutron-ovn-metadata-agent = neutron.cmd.eventlet.agents.ovn_metadata:main
neutron-ovn-migration-mtu = neutron.cmd.ovn.migration_mtu:main
neutron-ovn-db-sync-util = neutron.cmd.ovn.neutron_ovn_db_sync_util:main
neutron.core_plugins =
ml2 = neutron.plugins.ml2.plugin:Ml2Plugin
neutron.service_plugins =
@ -94,6 +96,7 @@ neutron.ml2.mechanism_drivers =
l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver
sriovnicswitch = neutron.plugins.ml2.drivers.mech_sriov.mech_driver.mech_driver:SriovNicSwitchMechanismDriver
ovn = neutron.plugins.ml2.drivers.ovn.mech_driver.mech_driver:OVNMechanismDriver
ovn-sync = neutron.cmd.ovn.neutron_ovn_db_sync_util:OVNMechanismDriver
fake_agent = neutron.tests.unit.plugins.ml2.drivers.mech_fake_agent:FakeAgentMechanismDriver
fake_agent_l3 = neutron.tests.unit.plugins.ml2.drivers.mech_fake_agent:FakeAgentMechanismDriverL3
another_fake_agent = neutron.tests.unit.plugins.ml2.drivers.mech_fake_agent:AnotherFakeAgentMechanismDriver