From 37660fce9cbc90c0fa1b85b15b4f15d38c21baa7 Mon Sep 17 00:00:00 2001 From: Adit Sarfaty Date: Wed, 6 Jul 2016 17:00:20 +0300 Subject: [PATCH] [Admin-utils] NSXv recreate DHCP edge This utility can be used to move all the networks from a specific DHCP edge, to another (new or existing) edge. This should work also for VDR router DHCP edge. Usage: nsxadmin -r dhcp-binding -o nsx-recreate --property edge-id= Output example: ==== [NSX] Recreate Dhcp Edge ==== ReCreating NSXv Edge: edge-222 Deleting the old DHCP edge: edge-222 Moving network a7fd0856-923e-43a6-97c7-9980e7fabd08 to a new edge Moving subnet ae9efc04-a685-497e-aab1-1dff9abacf9c to a new edge Creating network a7fd0856-923e-43a6-97c7-9980e7fabd08 DHCP address group Network a7fd0856-923e-43a6-97c7-9980e7fabd08 was moved to edge edge-228 Moving network 7a484242-0261-4888-ba77-41bb7bbd4f9d to a new edge Moving subnet 412e89ce-7c69-494d-b525-c08c8828cdfd to a new edge Moving subnet 139f7375-afb9-41dd-bdb7-c25af772a805 to a new edge Creating network 7a484242-0261-4888-ba77-41bb7bbd4f9d DHCP address group Network 7a484242-0261-4888-ba77-41bb7bbd4f9d was moved to edge edge-228 Change-Id: I97ba4abfe50d634f5ba5b137a64e021575db1ead --- doc/source/admin_util.rst | 3 + vmware_nsx/db/nsxv_db.py | 10 + vmware_nsx/plugins/nsx_v/plugin.py | 25 +-- .../plugins/nsxv/resources/dhcp_binding.py | 180 +++++++++++++++++- .../admin/plugins/nsxv/resources/utils.py | 5 + vmware_nsx/shell/resources.py | 4 +- 6 files changed, 214 insertions(+), 13 deletions(-) diff --git a/doc/source/admin_util.rst b/doc/source/admin_util.rst index 32757fd62f..46a90215c6 100644 --- a/doc/source/admin_util.rst +++ b/doc/source/admin_util.rst @@ -79,6 +79,9 @@ DHCP Bindings nsxadmin -r dhcp-binding -o nsx-update --property edge-id=edge-15 +- Recreate DHCP edge by moving all the networks to other edges:: + + nsxadmin -r dhcp-binding -o nsx-recreate --property edge-id=edge-222 Networks ~~~~~~~~ diff --git a/vmware_nsx/db/nsxv_db.py b/vmware_nsx/db/nsxv_db.py index 1ff9d53bf7..b1055b0142 100644 --- a/vmware_nsx/db/nsxv_db.py +++ b/vmware_nsx/db/nsxv_db.py @@ -596,6 +596,16 @@ def get_vdr_dhcp_binding_by_vdr(session, vdr_router_id): return None +def get_vdr_dhcp_binding_by_edge(session, edge_id): + try: + binding = session.query( + nsxv_models.NsxvVdrDhcpBinding).filter_by( + dhcp_edge_id=edge_id).one() + return binding + except exc.NoResultFound: + return None + + def delete_vdr_dhcp_binding(session, vdr_router_id): return (session.query(nsxv_models.NsxvVdrDhcpBinding). filter_by(vdr_router_id=vdr_router_id).delete()) diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index 3e03307961..70230e7162 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -1923,6 +1923,19 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, if binding: return binding['edge_id'] + def _update_dhcp_service_new_edge(self, context, resource_id): + edge_id = self._get_edge_id_by_rtr_id(context, resource_id) + if edge_id: + with locking.LockManager.get_lock(str(edge_id)): + if self.metadata_proxy_handler: + LOG.debug('Update metadata for resource %s', + resource_id) + self.metadata_proxy_handler.configure_router_edge( + resource_id, context) + + self.setup_dhcp_edge_fw_rules(context, self, + resource_id) + def _update_dhcp_service_with_subnet(self, context, subnet): network_id = subnet['network_id'] # Create DHCP port @@ -1947,17 +1960,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, context, network_id, address_groups=address_groups) if resource_id: - edge_id = self._get_edge_id_by_rtr_id(context, resource_id) - if edge_id: - with locking.LockManager.get_lock(str(edge_id)): - if self.metadata_proxy_handler: - LOG.debug('Update metadata for resource %s', - resource_id) - self.metadata_proxy_handler.configure_router_edge( - resource_id, context) - - self.setup_dhcp_edge_fw_rules(context, self, - resource_id) + self._update_dhcp_service_new_edge(context, resource_id) except Exception: with excutils.save_and_reraise_exception(): diff --git a/vmware_nsx/shell/admin/plugins/nsxv/resources/dhcp_binding.py b/vmware_nsx/shell/admin/plugins/nsxv/resources/dhcp_binding.py index 4b0b35d699..ea9efbbaa0 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv/resources/dhcp_binding.py +++ b/vmware_nsx/shell/admin/plugins/nsxv/resources/dhcp_binding.py @@ -16,15 +16,22 @@ import logging import pprint +from oslo_config import cfg + from vmware_nsx.shell.admin.plugins.common import constants import vmware_nsx.shell.admin.plugins.common.utils as admin_utils import vmware_nsx.shell.admin.plugins.nsxv.resources.utils as utils import vmware_nsx.shell.resources as shell from neutron.callbacks import registry +from neutron import context as n_context +from neutron.db import l3_db -from vmware_nsx._i18n import _LE, _LI +from vmware_nsx._i18n import _LE, _LI, _LW +from vmware_nsx.common import locking from vmware_nsx.db import nsxv_db +from vmware_nsx.plugins.nsx_v.vshield.common import ( + constants as nsxv_constants) from vmware_nsx.plugins.nsx_v.vshield.common import exceptions from vmware_nsx.plugins.nsx_v.vshield import edge_utils from vmware_nsx.plugins.nsx_v.vshield import vcns_driver @@ -116,9 +123,180 @@ def nsx_update_dhcp_edge_binding(resource, event, trigger, **kwargs): LOG.error(_LE("Edge %s not found"), edge_id) +def delete_old_dhcp_edge(context, old_edge_id, bindings): + LOG.info(_LI("Deleting the old DHCP edge: %s"), old_edge_id) + # using one of the router-ids in the bindings for the deleting + dhcp_names = [binding['router_id'] for binding in bindings] + dhcp_name = dhcp_names[0] + with locking.LockManager.get_lock(old_edge_id): + # Delete from NSXv backend + # (using the first dhcp name as the "router name") + # Note - If we will not delete the router, but free it - it will be + # immediately used as the new one, So it is better to delete it. + try: + nsxv.delete_edge(old_edge_id) + except Exception as e: + LOG.warning(_LW("Failed to delete the old edge %(id)s: %(e)s"), + {'id': old_edge_id, 'e': e}) + # Continue the process anyway + # The edge may have been already deleted at the backend + + try: + # Remove bindings from Neutron DB + nsxv_db.delete_nsxv_router_binding(context.session, dhcp_name) + nsxv_db.clean_edge_vnic_binding(context.session, old_edge_id) + except Exception as e: + LOG.warning(_LW("Failed to delete the old edge %(id)s from the " + "DB : %(e)s"), {'id': old_edge_id, 'e': e}) + + +def recreate_vdr_dhcp_edge(context, plugin, edge_manager, + old_edge_id, vdr_router_id): + """Handle the edge recreation of a VDR router DHCP. + """ + # delete the old bindings + nsxv_db.delete_vdr_dhcp_binding(context.session, vdr_router_id) + + # Add each interface port of this router to a new edge: + intf_ports = plugin._get_port_by_device_id( + context, vdr_router_id, l3_db.DEVICE_OWNER_ROUTER_INTF) + for port in intf_ports: + fixed_ips = port.get("fixed_ips", []) + if len(fixed_ips) > 0: + fixed_ip = fixed_ips[0] + subnet_id = fixed_ip['subnet_id'] + subnet = plugin.get_subnet(context, subnet_id) + do_metadata = False + for fixed_ip in fixed_ips: + if fixed_ip['ip_address'] == subnet['gateway_ip']: + do_metadata = True + + if do_metadata: + edge_manager.configure_dhcp_for_vdr_network( + context, subnet['network_id'], vdr_router_id) + + new_binding = nsxv_db.get_vdr_dhcp_binding_by_vdr( + context.session, vdr_router_id) + if new_binding: + LOG.info(_LI("VDR router %(vdr_id)s was moved to edge %(edge_id)s"), + {'vdr_id': vdr_router_id, + 'edge_id': new_binding['dhcp_edge_id']}) + else: + LOG.error(_LI("VDR router %(vdr_id)s was not moved to a new edge"), + {'vdr_id': vdr_router_id}) + + +def recreate_network_dhcp(context, plugin, edge_manager, old_edge_id, net_id): + """Handle the DHCP edge recreation of a network + """ + LOG.info(_LI("Moving network %s to a new edge"), net_id) + # delete the old binding + resource_id = (nsxv_constants.DHCP_EDGE_PREFIX + net_id)[:36] + nsxv_db.delete_nsxv_router_binding(context.session, resource_id) + + # Delete the old static binding of the networks` compute ports + port_filters = {'network_id': [net_id], + 'device_owner': ['compute:None']} + compute_ports = plugin.get_ports(context, filters=port_filters) + for port in compute_ports: + # Delete old binding from the DB + nsxv_db.delete_edge_dhcp_static_binding(context.session, + old_edge_id, port['mac_address']) + + # Go over all the subnets with DHCP + net_filters = {'network_id': [net_id], 'enable_dhcp': [True]} + subnets = plugin.get_subnets(context, filters=net_filters) + for subnet in subnets: + LOG.info(_LI("Moving subnet %s to a new edge"), subnet['id']) + # allocate / reuse the new dhcp edge + new_resource_id = edge_manager.create_dhcp_edge_service( + context, net_id, subnet) + if new_resource_id: + # also add fw rules and metadata, once for the new edge + plugin._update_dhcp_service_new_edge(context, resource_id) + + # Update the ip of the dhcp port + LOG.info(_LI("Creating network %s DHCP address group"), net_id) + address_groups = plugin._create_network_dhcp_address_group( + context, net_id) + plugin._update_dhcp_edge_service(context, net_id, address_groups) + + # find out the id of the new edge: + new_binding = nsxv_db.get_nsxv_router_binding( + context.session, resource_id) + if new_binding: + LOG.info(_LI("Network %(net_id)s was moved to edge %(edge_id)s"), + {'net_id': net_id, 'edge_id': new_binding['edge_id']}) + else: + LOG.error(_LI("Network %(net_id)s was not moved to a new edge"), + {'net_id': net_id}) + + +@admin_utils.output_header +def nsx_recreate_dhcp_edge(resource, event, trigger, **kwargs): + """Recreate a dhcp edge with all the networks n a new NSXv edge""" + if not kwargs.get('property'): + LOG.error(_LE("Need to specify edge-id parameter")) + return + + # input validation + properties = admin_utils.parse_multi_keyval_opt(kwargs['property']) + old_edge_id = properties.get('edge-id') + if not old_edge_id: + LOG.error(_LE("Need to specify edge-id parameter")) + return + LOG.info(_LI("ReCreating NSXv Edge: %s"), old_edge_id) + + # init the plugin and edge manager + cfg.CONF.set_override('core_plugin', + 'vmware_nsx.shell.admin.plugins.nsxv.resources' + '.utils.NsxVPluginWrapper') + plugin = utils.NsxVPluginWrapper() + nsxv_manager = vcns_driver.VcnsDriver(edge_utils.NsxVCallbacks(plugin)) + edge_manager = edge_utils.EdgeManager(nsxv_manager, plugin) + context = n_context.get_admin_context() + + # verify that this is a DHCP edge + bindings = nsxv_db.get_nsxv_router_bindings_by_edge( + context.session, old_edge_id) + if (not bindings or + not bindings[0]['router_id'].startswith( + nsxv_constants.DHCP_EDGE_PREFIX)): + LOG.error(_LE("Edge %(edge_id)s is not a DHCP edge"), + {'edge_id': old_edge_id}) + return + + # find the networks bound to this DHCP edge + networks_binding = nsxv_db.get_edge_vnic_bindings_by_edge( + context.session, old_edge_id) + network_ids = [binding['network_id'] for binding in networks_binding] + + # Find out the vdr router, if this is a vdr DHCP edge + vdr_binding = nsxv_db.get_vdr_dhcp_binding_by_edge( + context.session, old_edge_id) + vdr_router_id = vdr_binding['vdr_router_id'] if vdr_binding else None + + # Delete the old edge + delete_old_dhcp_edge(context, old_edge_id, bindings) + + if vdr_router_id: + # recreate the edge as a VDR DHCP edge + recreate_vdr_dhcp_edge(context, plugin, edge_manager, + old_edge_id, vdr_router_id) + else: + # This is a regular DHCP edge: + # Move all the networks to other (new or existing) edge + for net_id in network_ids: + recreate_network_dhcp(context, plugin, edge_manager, + old_edge_id, net_id) + + registry.subscribe(list_missing_dhcp_bindings, constants.DHCP_BINDING, shell.Operations.LIST.value) registry.subscribe(nsx_update_dhcp_edge_binding, constants.DHCP_BINDING, shell.Operations.NSX_UPDATE.value) +registry.subscribe(nsx_recreate_dhcp_edge, + constants.DHCP_BINDING, + shell.Operations.NSX_RECREATE.value) diff --git a/vmware_nsx/shell/admin/plugins/nsxv/resources/utils.py b/vmware_nsx/shell/admin/plugins/nsxv/resources/utils.py index 6725c80dc1..239f15dc30 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv/resources/utils.py +++ b/vmware_nsx/shell/admin/plugins/nsxv/resources/utils.py @@ -43,3 +43,8 @@ class NsxVPluginWrapper(plugin.NsxVPlugin): def _validate_config(self): pass + + def _extend_get_network_dict_provider(self, context, net): + self._extend_network_dict_provider(context, net) + # skip getting the Qos policy ID because get_object calls + # plugin init again on admin-util environment diff --git a/vmware_nsx/shell/resources.py b/vmware_nsx/shell/resources.py index f34348df26..db0f2f1c09 100644 --- a/vmware_nsx/shell/resources.py +++ b/vmware_nsx/shell/resources.py @@ -44,6 +44,7 @@ class Operations(enum.Enum): NSX_CLEAN = 'nsx-clean' NSX_UPDATE = 'nsx-update' NSX_UPDATE_SECRET = 'nsx-update-secret' + NSX_RECREATE = 'nsx-recreate' MIGRATE_TO_DYNAMIC_CRITERIA = 'migrate-to-dynamic-criteria' @@ -100,7 +101,8 @@ nsxv_resources = { Operations.CLEAN.value]), constants.DHCP_BINDING: Resource(constants.DHCP_BINDING, [Operations.LIST.value, - Operations.NSX_UPDATE.value]), + Operations.NSX_UPDATE.value, + Operations.NSX_RECREATE.value]), constants.NETWORKS: Resource(constants.NETWORKS, [Operations.LIST.value, Operations.NSX_UPDATE.value]),