diff --git a/neutron/cmd/ovn/__init__.py b/neutron/cmd/ovn/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/cmd/ovn/migration_mtu.py b/neutron/cmd/ovn/migration_mtu.py new file mode 100644 index 00000000000..a5bd9d77960 --- /dev/null +++ b/neutron/cmd/ovn/migration_mtu.py @@ -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 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() diff --git a/neutron/cmd/ovn/neutron_ovn_db_sync_util.py b/neutron/cmd/ovn/neutron_ovn_db_sync_util.py new file mode 100644 index 00000000000..d0620497e2a --- /dev/null +++ b/neutron/cmd/ovn/neutron_ovn_db_sync_util.py @@ -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') diff --git a/setup.cfg b/setup.cfg index ae2a7e5fdb8..457eec936b9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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