diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/ports.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/ports.py index cb2ebd5ac8..2db4f4df92 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/ports.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/ports.py @@ -17,12 +17,15 @@ import logging from sqlalchemy.orm import exc -from vmware_nsx._i18n import _LI +from vmware_nsx._i18n import _LI, _LW from vmware_nsx.common import exceptions as nsx_exc +from vmware_nsx.db import db as nsx_db from vmware_nsx.db import nsx_models -from vmware_nsx.nsxlib.v3 import client as nsx_client -from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster -from vmware_nsx.nsxlib.v3 import resources as nsx_resources +from vmware_nsx.nsxlib.v3 import client +from vmware_nsx.nsxlib.v3 import cluster +from vmware_nsx.nsxlib.v3 import resources +from vmware_nsx.plugins.nsx_v3 import plugin +from vmware_nsx.services.qos.nsx_v3 import utils as qos_utils from vmware_nsx.shell.admin.plugins.common import constants from vmware_nsx.shell.admin.plugins.common import formatters from vmware_nsx.shell.admin.plugins.common import utils as admin_utils @@ -30,11 +33,21 @@ from vmware_nsx.shell import nsxadmin as shell from neutron.callbacks import registry from neutron import context as neutron_context +from neutron.db import allowedaddresspairs_db as addr_pair_db from neutron.db import db_base_plugin_v2 +from neutron.db import portsecurity_db +from neutron.extensions import allowedaddresspairs +from neutron_lib import constants as const LOG = logging.getLogger(__name__) +class PortsPlugin(db_base_plugin_v2.NeutronDbPluginV2, + portsecurity_db.PortSecurityDbMixin, + addr_pair_db.AllowedAddressPairsMixin): + pass + + def get_port_nsx_id(session, neutron_id): # get the nsx port id from the DB mapping try: @@ -46,21 +59,55 @@ def get_port_nsx_id(session, neutron_id): pass -def get_port_client(): - _api_cluster = nsx_cluster.NSXClusteredAPI() - _nsx_client = nsx_client.NSX3Client(_api_cluster) - return nsx_resources.LogicalPort(_nsx_client) +def get_port_and_profile_clients(): + _api_cluster = cluster.NSXClusteredAPI() + _nsx_client = client.NSX3Client(_api_cluster) + return (resources.LogicalPort(_nsx_client), + resources.SwitchingProfile(_nsx_client)) + + +def get_dhcp_profile_id(profile_client): + profiles = profile_client.find_by_display_name( + plugin.NSX_V3_DHCP_PROFILE_NAME) + if profiles and len(profiles) == 1: + return profiles[0]['id'] + LOG.warning(_LW("Could not find DHCP profile on backend")) + + +def get_spoofguard_profile_id(profile_client): + profiles = profile_client.find_by_display_name( + plugin.NSX_V3_PSEC_PROFILE_NAME) + if profiles and len(profiles) == 1: + return profiles[0]['id'] + LOG.warning(_LW("Could not find Spoof Guard profile on backend")) + + +def add_profile_mismatch(problems, neutron_id, nsx_id, prf_id, title): + msg = (_LI('Wrong %(title)s profile %(prf_id)s') % {'title': title, + 'prf_id': prf_id}) + problems.append({'neutron_id': neutron_id, + 'nsx_id': nsx_id, + 'error': msg}) @admin_utils.output_header def list_missing_ports(resource, event, trigger, **kwargs): """List neutron ports that are missing the NSX backend port + And ports with wrong switch profiles """ - plugin = db_base_plugin_v2.NeutronDbPluginV2() + plugin = PortsPlugin() admin_cxt = neutron_context.get_admin_context() neutron_ports = plugin.get_ports(admin_cxt) - port_client = get_port_client() - ports = [] + port_client, profile_client = get_port_and_profile_clients() + + # get pre-defined profile ids + dhcp_profile_id = get_dhcp_profile_id(profile_client) + dhcp_profile_key = resources.SwitchingProfileTypes.SWITCH_SECURITY + spoofguard_profile_id = get_spoofguard_profile_id(profile_client) + spoofguard_profile_key = resources.SwitchingProfileTypes.SPOOF_GUARD + qos_profile_key = resources.SwitchingProfileTypes.QOS + + problems = [] for port in neutron_ports: neutron_id = port['id'] # get the network nsx id from the mapping table @@ -70,19 +117,56 @@ def list_missing_ports(resource, event, trigger, **kwargs): pass else: try: - port_client.get(nsx_id) + nsx_port = port_client.get(nsx_id) except nsx_exc.ResourceNotFound: - ports.append({'name': port['name'], - 'neutron_id': neutron_id, - 'nsx_id': nsx_id}) - if len(ports) > 0: - title = _LI("Found %d internal ports missing from the NSX " - "manager:") % len(ports) + problems.append({'neutron_id': neutron_id, + 'nsx_id': nsx_id, + 'error': _LI('Missing from backend')}) + + # Port found on backend! + # Check that it has all the expected switch profiles. + # create a dictionary of the current profiles: + profiles_dict = {} + for prf in nsx_port['switching_profile_ids']: + profiles_dict[prf['key']] = prf['value'] + + # DHCP port: neutron dhcp profile should be attached + if port.get('device_owner') == const.DEVICE_OWNER_DHCP: + prf_id = profiles_dict[dhcp_profile_key] + if prf_id != dhcp_profile_id: + add_profile_mismatch(problems, neutron_id, nsx_id, + prf_id, "DHCP security") + + # Port with QoS policy: a matching profile should be attached + qos_policy_id = qos_utils.get_port_policy_id(admin_cxt, + neutron_id) + if qos_policy_id: + qos_profile_id = nsx_db.get_switch_profile_by_qos_policy( + admin_cxt.session, qos_policy_id) + prf_id = profiles_dict[qos_profile_key] + if prf_id != qos_profile_id: + add_profile_mismatch(problems, neutron_id, nsx_id, + prf_id, "QoS") + + # Port with security & fixed ips/address pairs: + # neutron spoofguard profile should be attached + port_sec, has_ip = plugin._determine_port_security_and_has_ip( + admin_cxt, port) + addr_pair = port.get(allowedaddresspairs.ADDRESS_PAIRS) + if port_sec and (has_ip or addr_pair): + prf_id = profiles_dict[spoofguard_profile_key] + if prf_id != spoofguard_profile_id: + add_profile_mismatch(problems, neutron_id, nsx_id, + prf_id, "Spoof Guard") + + if len(problems) > 0: + title = _LI("Found internal ports misconfiguration on the " + "NSX manager:") LOG.info(formatters.output_formatter( - title, ports, - ['name', 'neutron_id', 'nsx_id'])) + title, problems, + ['neutron_id', 'nsx_id', 'error'])) else: - LOG.info(_LI("All internal ports exist on the NSX manager")) + LOG.info(_LI("All internal ports verified on the NSX manager")) registry.subscribe(list_missing_ports,