
This change replaces remaining occurences of the notify method with calls to the publish method. As NSX admin utilities heavily rely on callbacks, this change also ensures that all callbacks are now accepting event payloads rather thank kwargs. Change-Id: I0450fff486898d6ab74086b7952dc27134cb77e2
360 lines
15 KiB
Python
360 lines
15 KiB
Python
# Copyright 2015 VMware, 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 pprint
|
|
|
|
from neutron_lib import context as n_context
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
|
|
from vmware_nsx.shell.admin.plugins.common import constants
|
|
import vmware_nsx.shell.admin.plugins.common.utils as admin_utils
|
|
from vmware_nsx.shell.admin.plugins.nsxv.resources import utils
|
|
import vmware_nsx.shell.resources as shell
|
|
|
|
from neutron_lib.callbacks import registry
|
|
from neutron_lib import exceptions as nl_exc
|
|
|
|
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
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
nsxv = utils.get_nsxv_client()
|
|
neutron_db = utils.NeutronDbClient()
|
|
|
|
|
|
def nsx_get_static_bindings_by_edge(edge_id):
|
|
nsx_dhcp_static_bindings = set()
|
|
|
|
try:
|
|
nsx_dhcp_bindings = nsxv.query_dhcp_configuration(edge_id)
|
|
except exceptions.ResourceNotFound:
|
|
LOG.error("Edge %s was not found", edge_id)
|
|
return
|
|
|
|
# nsx_dhcp_bindings[0] contains response headers;
|
|
# nsx_dhcp_bindings[1] contains response payload
|
|
sbindings = nsx_dhcp_bindings[1].get('staticBindings').get(
|
|
'staticBindings')
|
|
|
|
for binding in sbindings:
|
|
nsx_dhcp_static_bindings.add(
|
|
(edge_id, binding.get('macAddress').lower(),
|
|
binding.get('bindingId').lower()))
|
|
|
|
return nsx_dhcp_static_bindings
|
|
|
|
|
|
def neutron_get_static_bindings_by_edge(edge_id):
|
|
neutron_db_dhcp_bindings = set()
|
|
for binding in nsxv_db.get_dhcp_static_bindings_by_edge(
|
|
neutron_db.context.session, edge_id):
|
|
neutron_db_dhcp_bindings.add(
|
|
(binding.edge_id, binding.mac_address.lower(),
|
|
binding.binding_id.lower()))
|
|
return neutron_db_dhcp_bindings
|
|
|
|
|
|
@admin_utils.output_header
|
|
@admin_utils.unpack_payload
|
|
def list_missing_dhcp_bindings(resource, event, trigger, **kwargs):
|
|
"""List missing DHCP bindings from NSXv backend.
|
|
|
|
Missing DHCP bindings are those that exist in Neutron DB;
|
|
but are not present on corresponding NSXv Edge.
|
|
"""
|
|
for (edge_id, count) in nsxv_db.get_nsxv_dhcp_bindings_count_per_edge(
|
|
neutron_db.context.session):
|
|
LOG.info("%s", "=" * 60)
|
|
LOG.info("For edge: %s", edge_id)
|
|
nsx_dhcp_static_bindings = nsx_get_static_bindings_by_edge(edge_id)
|
|
if nsx_dhcp_static_bindings is None:
|
|
continue
|
|
neutron_dhcp_static_bindings = \
|
|
neutron_get_static_bindings_by_edge(edge_id)
|
|
LOG.info("# of DHCP bindings in Neutron DB: %s",
|
|
len(neutron_dhcp_static_bindings))
|
|
LOG.info("# of DHCP bindings on NSXv backend: %s",
|
|
len(nsx_dhcp_static_bindings))
|
|
missing = neutron_dhcp_static_bindings - nsx_dhcp_static_bindings
|
|
if not missing:
|
|
LOG.info("No missing DHCP bindings found.")
|
|
LOG.info("Neutron DB and NSXv backend are in sync")
|
|
else:
|
|
LOG.info("Missing DHCP bindings:")
|
|
LOG.info("%s", pprint.pformat(missing))
|
|
|
|
|
|
@admin_utils.output_header
|
|
@admin_utils.unpack_payload
|
|
def nsx_update_dhcp_edge_binding(resource, event, trigger, **kwargs):
|
|
"""Resync DHCP bindings on NSXv Edge"""
|
|
if not kwargs.get('property'):
|
|
LOG.error("Need to specify edge-id parameter")
|
|
return
|
|
properties = admin_utils.parse_multi_keyval_opt(kwargs['property'])
|
|
edge_id = properties.get('edge-id')
|
|
if not edge_id:
|
|
LOG.error("Need to specify edge-id parameter")
|
|
return
|
|
LOG.info("Updating NSXv Edge: %s", edge_id)
|
|
# Need to create a plugin object; so that we are able to
|
|
# do neutron list-ports.
|
|
with utils.NsxVPluginWrapper() as plugin:
|
|
nsxv_manager = vcns_driver.VcnsDriver(
|
|
edge_utils.NsxVCallbacks(plugin))
|
|
edge_manager = edge_utils.EdgeManager(nsxv_manager, plugin)
|
|
try:
|
|
edge_manager.update_dhcp_service_config(
|
|
neutron_db.context, edge_id)
|
|
except exceptions.ResourceNotFound:
|
|
LOG.error("Edge %s not found", edge_id)
|
|
|
|
|
|
def delete_old_dhcp_edge(context, old_edge_id, bindings):
|
|
LOG.info("Deleting the old DHCP edge: %s", old_edge_id)
|
|
with locking.LockManager.get_lock(old_edge_id):
|
|
# Delete from NSXv backend
|
|
# 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("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.clean_edge_router_binding(context.session, old_edge_id)
|
|
nsxv_db.clean_edge_vnic_binding(context.session, old_edge_id)
|
|
except Exception as e:
|
|
LOG.warning("Failed to delete the old edge %(id)s from the "
|
|
"DB : %(e)s", {'id': old_edge_id, 'e': e})
|
|
|
|
|
|
def recreate_network_dhcp(context, plugin, edge_manager, old_edge_id, net_id):
|
|
"""Handle the DHCP edge recreation of a network
|
|
"""
|
|
LOG.info("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)
|
|
if old_edge_id:
|
|
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("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("Creating network %s DHCP address group", net_id)
|
|
address_groups = plugin._create_network_dhcp_address_group(
|
|
context, net_id)
|
|
plugin.edge_manager.update_dhcp_edge_service(
|
|
context, net_id, address_groups=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("Network %(net_id)s was moved to edge %(edge_id)s",
|
|
{'net_id': net_id, 'edge_id': new_binding['edge_id']})
|
|
else:
|
|
LOG.error("Network %(net_id)s was not moved to a new edge",
|
|
{'net_id': net_id})
|
|
|
|
|
|
@admin_utils.output_header
|
|
@admin_utils.unpack_payload
|
|
def nsx_recreate_dhcp_edge(resource, event, trigger, **kwargs):
|
|
"""Recreate a dhcp edge with all the networks on a new NSXv edge"""
|
|
usage_msg = ("Need to specify edge-id or net-id parameter")
|
|
if not kwargs.get('property'):
|
|
LOG.error(usage_msg)
|
|
return
|
|
|
|
# input validation
|
|
properties = admin_utils.parse_multi_keyval_opt(kwargs['property'])
|
|
old_edge_id = properties.get('edge-id')
|
|
if not old_edge_id:
|
|
# if the net-id property exist - recreate the edge for this network
|
|
net_id = properties.get('net-id')
|
|
if net_id:
|
|
nsx_recreate_dhcp_edge_by_net_id(net_id)
|
|
return
|
|
LOG.error(usage_msg)
|
|
return
|
|
LOG.info("ReCreating NSXv Edge: %s", old_edge_id)
|
|
|
|
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("Edge %(edge_id)s is not a DHCP edge",
|
|
{'edge_id': old_edge_id})
|
|
return
|
|
|
|
# init the plugin and edge manager
|
|
cfg.CONF.set_override('core_plugin',
|
|
'vmware_nsx.shell.admin.plugins.nsxv.resources'
|
|
'.utils.NsxVPluginWrapper')
|
|
with utils.NsxVPluginWrapper() as plugin:
|
|
nsxv_manager = vcns_driver.VcnsDriver(
|
|
edge_utils.NsxVCallbacks(plugin))
|
|
edge_manager = edge_utils.EdgeManager(nsxv_manager, plugin)
|
|
|
|
# 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]
|
|
|
|
# Delete the old edge
|
|
delete_old_dhcp_edge(context, old_edge_id, bindings)
|
|
|
|
# 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)
|
|
|
|
|
|
def nsx_recreate_dhcp_edge_by_net_id(net_id):
|
|
"""Recreate a dhcp edge for a specific network without an edge"""
|
|
LOG.info("ReCreating NSXv Edge for network: %s", net_id)
|
|
|
|
context = n_context.get_admin_context()
|
|
|
|
# init the plugin and edge manager
|
|
cfg.CONF.set_override('core_plugin',
|
|
'vmware_nsx.shell.admin.plugins.nsxv.resources'
|
|
'.utils.NsxVPluginWrapper')
|
|
with utils.NsxVPluginWrapper() as plugin:
|
|
nsxv_manager = vcns_driver.VcnsDriver(edge_utils.NsxVCallbacks(plugin))
|
|
edge_manager = edge_utils.EdgeManager(nsxv_manager, plugin)
|
|
|
|
# verify that there is no DHCP edge for this network at the moment
|
|
resource_id = (nsxv_constants.DHCP_EDGE_PREFIX + net_id)[:36]
|
|
router_binding = nsxv_db.get_nsxv_router_binding(
|
|
context.session, resource_id)
|
|
if router_binding:
|
|
# make sure there is no real edge
|
|
if router_binding['edge_id']:
|
|
edge_id = router_binding['edge_id']
|
|
try:
|
|
nsxv_manager.vcns.get_edge(edge_id)
|
|
except exceptions.ResourceNotFound:
|
|
# No edge on backend
|
|
LOG.info("Edge %s does not exist on the NSX", edge_id)
|
|
else:
|
|
LOG.warning("Network %(net_id)s already has a dhcp edge: "
|
|
"%(edge_id)s",
|
|
{'edge_id': edge_id,
|
|
'net_id': net_id})
|
|
return
|
|
# delete this old entry
|
|
nsxv_db.delete_nsxv_router_binding(context.session, resource_id)
|
|
|
|
# Verify that the network exists on neutron
|
|
try:
|
|
plugin.get_network(context, net_id)
|
|
except nl_exc.NetworkNotFound:
|
|
LOG.error("Network %s does not exist", net_id)
|
|
return
|
|
recreate_network_dhcp(context, plugin, edge_manager,
|
|
None, net_id)
|
|
|
|
|
|
@admin_utils.output_header
|
|
@admin_utils.unpack_payload
|
|
def nsx_redistribute_dhcp_edges(resource, event, trigger, **kwargs):
|
|
"""If any of the DHCP networks are on a conflicting edge move them"""
|
|
context = n_context.get_admin_context()
|
|
with utils.NsxVPluginWrapper() as plugin:
|
|
nsxv_manager = vcns_driver.VcnsDriver(
|
|
edge_utils.NsxVCallbacks(plugin))
|
|
edge_manager = edge_utils.EdgeManager(nsxv_manager, plugin)
|
|
# go over all DHCP subnets
|
|
networks = plugin.get_networks(context)
|
|
for network in networks:
|
|
network_id = network['id']
|
|
# Check if the network has a related DHCP edge
|
|
resource_id = (nsxv_constants.DHCP_EDGE_PREFIX + network_id)[:36]
|
|
dhcp_edge_binding = nsxv_db.get_nsxv_router_binding(
|
|
context.session, resource_id)
|
|
if not dhcp_edge_binding:
|
|
continue
|
|
LOG.info("Checking network %s", network_id)
|
|
edge_id = dhcp_edge_binding['edge_id']
|
|
availability_zone = plugin.get_network_az_by_net_id(
|
|
context, network['id'])
|
|
filters = {'network_id': [network_id], 'enable_dhcp': [True]}
|
|
subnets = plugin.get_subnets(context, filters=filters)
|
|
for subnet in subnets:
|
|
(conflict_edge_ids,
|
|
available_edge_ids) = edge_manager._get_used_edges(
|
|
context, subnet, availability_zone)
|
|
if edge_id in conflict_edge_ids:
|
|
# move the DHCP to another edge
|
|
LOG.info("Network %(net)s on DHCP edge %(edge)s is "
|
|
"conflicting with another network and will be "
|
|
"moved",
|
|
{'net': network_id, 'edge': edge_id})
|
|
edge_manager.remove_network_from_dhcp_edge(
|
|
context, network_id, edge_id)
|
|
edge_manager.create_dhcp_edge_service(
|
|
context, network_id, subnet)
|
|
break
|
|
|
|
|
|
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)
|
|
registry.subscribe(nsx_redistribute_dhcp_edges,
|
|
constants.DHCP_BINDING,
|
|
shell.Operations.NSX_REDISTRIBUTE.value)
|