86118f6692
This patch refactors the current codebase to be more modular and testable. The main changes are: - pull out all of the profile setup logic from the __init__ method to it's own method _init_nsx_profiles method to make testing the code easier. - refactors the nsxlib.v3 code to break out all neutron related exceptions and cleans up the interface so we can make nsxlib.v3 it's own standalone library eventually. To improve: - Currently we have nsxlib.v3.dfw_api and nsxlib.v3.firewall, we should refactor this code and merge them into one file. - refactor nsxlib to section of each api component to it's own subclass. For example, nsxlib().port.create() rather than nsxlib().create_port(). I think this would be most useful for the security group/firewall integration as there are many methods there that are needed to interface with nsx as the security group feature is requires the most orchestration with nsx. Breaking them into a sub class will make things more easy to understand. Change-Id: If2fe1e014b78703ff0a9cdff1e4e8d45f3a4a16d
293 lines
12 KiB
Python
293 lines
12 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 logging
|
|
|
|
from neutron.callbacks import registry
|
|
from neutron import context as neutron_context
|
|
from neutron.db import common_db_mixin as common_db
|
|
from neutron.db import securitygroups_db
|
|
|
|
from vmware_nsx.common import utils
|
|
from vmware_nsx.db import db as nsx_db
|
|
from vmware_nsx.db import nsx_models
|
|
from vmware_nsx.extensions import providersecuritygroup as provider_sg
|
|
from vmware_nsx.extensions import securitygrouplogging as sg_logging
|
|
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
|
|
from vmware_nsx.shell.admin.plugins.nsxv3.resources import ports
|
|
from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils as v3_utils
|
|
from vmware_nsx.shell import resources as shell
|
|
from vmware_nsx._i18n import _LE, _LW
|
|
from vmware_nsx.nsxlib.v3 import dfw_api as firewall
|
|
from vmware_nsx.nsxlib.v3 import security
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class NeutronSecurityGroupApi(securitygroups_db.SecurityGroupDbMixin,
|
|
common_db.CommonDbMixin):
|
|
def __init__(self):
|
|
super(NeutronSecurityGroupApi, self)
|
|
self.context = neutron_context.get_admin_context()
|
|
|
|
def get_security_groups(self):
|
|
return super(NeutronSecurityGroupApi,
|
|
self).get_security_groups(self.context)
|
|
|
|
def delete_security_group(self, sg_id):
|
|
return super(NeutronSecurityGroupApi,
|
|
self).delete_security_group(self.context, sg_id)
|
|
|
|
def get_nsgroup_id(self, sg_id):
|
|
return nsx_db.get_nsx_security_group_id(
|
|
self.context.session, sg_id)
|
|
|
|
def get_port_security_groups(self, port_id):
|
|
secgroups_bindings = self._get_port_security_group_bindings(
|
|
self.context, {'port_id': [port_id]})
|
|
return [b['security_group_id'] for b in secgroups_bindings]
|
|
|
|
def get_ports_in_security_group(self, security_group_id):
|
|
secgroups_bindings = self._get_port_security_group_bindings(
|
|
self.context, {'security_group_id': [security_group_id]})
|
|
return [b['port_id'] for b in secgroups_bindings]
|
|
|
|
def delete_security_group_section_mapping(self, sg_id):
|
|
fw_mapping = self.context.session.query(
|
|
nsx_models.NeutronNsxFirewallSectionMapping).filter_by(
|
|
neutron_id=sg_id).one_or_none()
|
|
if fw_mapping:
|
|
with self.context.session.begin(subtransactions=True):
|
|
self.context.session.delete(fw_mapping)
|
|
|
|
def delete_security_group_backend_mapping(self, sg_id):
|
|
sg_mapping = self.context.session.query(
|
|
nsx_models.NeutronNsxSecurityGroupMapping).filter_by(
|
|
neutron_id=sg_id).one_or_none()
|
|
if sg_mapping:
|
|
with self.context.session.begin(subtransactions=True):
|
|
self.context.session.delete(sg_mapping)
|
|
|
|
def get_security_groups_mappings(self):
|
|
q = self.context.session.query(
|
|
securitygroups_db.SecurityGroup.name,
|
|
securitygroups_db.SecurityGroup.id,
|
|
nsx_models.NeutronNsxFirewallSectionMapping.nsx_id,
|
|
nsx_models.NeutronNsxSecurityGroupMapping.nsx_id).join(
|
|
nsx_models.NeutronNsxFirewallSectionMapping,
|
|
nsx_models.NeutronNsxSecurityGroupMapping).all()
|
|
sg_mappings = [{'name': mapp[0],
|
|
'id': mapp[1],
|
|
'section-id': mapp[2],
|
|
'nsx-securitygroup-id': mapp[3]}
|
|
for mapp in q]
|
|
return sg_mappings
|
|
|
|
def get_logical_port_id(self, port_id):
|
|
mapping = self.context.session.query(
|
|
nsx_models.NeutronNsxPortMapping).filter_by(
|
|
neutron_id=port_id).one_or_none()
|
|
if mapping:
|
|
return mapping.nsx_id
|
|
|
|
|
|
neutron_sg = NeutronSecurityGroupApi()
|
|
neutron_db = v3_utils.NeutronDbClient()
|
|
nsxlib = v3_utils.get_connected_nsxlib()
|
|
|
|
|
|
def _log_info(resource, data, attrs=['display_name', 'id']):
|
|
LOG.info(formatters.output_formatter(resource, data, attrs))
|
|
|
|
|
|
@admin_utils.list_handler(constants.SECURITY_GROUPS)
|
|
@admin_utils.output_header
|
|
def list_security_groups_mappings(resource, event, trigger, **kwargs):
|
|
sg_mappings = neutron_sg.get_security_groups_mappings()
|
|
_log_info(constants.SECURITY_GROUPS,
|
|
sg_mappings,
|
|
attrs=['name', 'id', 'section-id', 'nsx-securitygroup-id'])
|
|
return bool(sg_mappings)
|
|
|
|
|
|
@admin_utils.list_handler(constants.FIREWALL_SECTIONS)
|
|
@admin_utils.output_header
|
|
def nsx_list_dfw_sections(resource, event, trigger, **kwargs):
|
|
fw_sections = nsxlib.list_sections()
|
|
_log_info(constants.FIREWALL_SECTIONS, fw_sections)
|
|
return bool(fw_sections)
|
|
|
|
|
|
@admin_utils.list_handler(constants.FIREWALL_NSX_GROUPS)
|
|
@admin_utils.output_header
|
|
def nsx_list_security_groups(resource, event, trigger, **kwargs):
|
|
nsx_secgroups = nsxlib.list_nsgroups()
|
|
_log_info(constants.FIREWALL_NSX_GROUPS, nsx_secgroups)
|
|
return bool(nsx_secgroups)
|
|
|
|
|
|
def _find_missing_security_groups():
|
|
nsx_secgroups = nsxlib.list_nsgroups()
|
|
sg_mappings = neutron_sg.get_security_groups_mappings()
|
|
missing_secgroups = {}
|
|
for sg_db in sg_mappings:
|
|
for nsx_sg in nsx_secgroups:
|
|
if nsx_sg['id'] == sg_db['nsx-securitygroup-id']:
|
|
break
|
|
else:
|
|
missing_secgroups[sg_db['id']] = sg_db
|
|
return missing_secgroups
|
|
|
|
|
|
@admin_utils.list_mismatches_handler(constants.FIREWALL_NSX_GROUPS)
|
|
@admin_utils.output_header
|
|
def list_missing_security_groups(resource, event, trigger, **kwargs):
|
|
sgs_with_missing_nsx_group = _find_missing_security_groups()
|
|
missing_securitgroups_info = [
|
|
{'securitygroup-name': sg['name'],
|
|
'securitygroup-id': sg['id'],
|
|
'nsx-securitygroup-id':
|
|
sg['nsx-securitygroup-id']}
|
|
for sg in sgs_with_missing_nsx_group.values()]
|
|
_log_info(constants.FIREWALL_NSX_GROUPS, missing_securitgroups_info,
|
|
attrs=['securitygroup-name', 'securitygroup-id',
|
|
'nsx-securitygroup-id'])
|
|
return bool(missing_securitgroups_info)
|
|
|
|
|
|
def _find_missing_sections():
|
|
fw_sections = nsxlib.list_sections()
|
|
sg_mappings = neutron_sg.get_security_groups_mappings()
|
|
missing_sections = {}
|
|
for sg_db in sg_mappings:
|
|
for fw_section in fw_sections:
|
|
if fw_section['id'] == sg_db['section-id']:
|
|
break
|
|
else:
|
|
missing_sections[sg_db['id']] = sg_db
|
|
return missing_sections
|
|
|
|
|
|
@admin_utils.list_mismatches_handler(constants.FIREWALL_SECTIONS)
|
|
@admin_utils.output_header
|
|
def list_missing_firewall_sections(resource, event, trigger, **kwargs):
|
|
sgs_with_missing_section = _find_missing_sections()
|
|
missing_sections_info = [{'securitygroup-name': sg['name'],
|
|
'securitygroup-id': sg['id'],
|
|
'section-id': sg['section-id']}
|
|
for sg in sgs_with_missing_section.values()]
|
|
_log_info(constants.FIREWALL_SECTIONS, missing_sections_info,
|
|
attrs=['securitygroup-name', 'securitygroup-id', 'section-id'])
|
|
return bool(missing_sections_info)
|
|
|
|
|
|
@admin_utils.fix_mismatches_handler(constants.SECURITY_GROUPS)
|
|
@admin_utils.output_header
|
|
def fix_security_groups(resource, event, trigger, **kwargs):
|
|
context_ = neutron_context.get_admin_context()
|
|
plugin = v3_utils.NsxV3PluginWrapper()
|
|
inconsistent_secgroups = _find_missing_sections()
|
|
inconsistent_secgroups.update(_find_missing_security_groups())
|
|
|
|
for sg_id, sg in inconsistent_secgroups.items():
|
|
secgroup = plugin.get_security_group(context_, sg_id)
|
|
nsxlib.delete_section(sg['section-id'])
|
|
nsxlib.delete_nsgroup(sg['nsx-securitygroup-id'])
|
|
neutron_sg.delete_security_group_section_mapping(sg_id)
|
|
neutron_sg.delete_security_group_backend_mapping(sg_id)
|
|
nsgroup, fw_section = (
|
|
plugin._create_security_group_backend_resources(secgroup))
|
|
nsxlib.save_sg_mappings(
|
|
context_.session, sg_id, nsgroup['id'], fw_section['id'])
|
|
# If version > 1.1 then we use dynamic criteria tags, and the port
|
|
# should already have them.
|
|
if not utils.is_nsx_version_1_1_0(plugin._nsx_version):
|
|
members = []
|
|
for port_id in neutron_db.get_ports_in_security_group(sg_id):
|
|
lport_id = neutron_db.get_logical_port_id(port_id)
|
|
members.append(lport_id)
|
|
nsxlib.add_nsgroup_members(
|
|
nsgroup['id'], firewall.LOGICAL_PORT, members)
|
|
|
|
for rule in secgroup['security_group_rules']:
|
|
rule_mapping = (context_.session.query(
|
|
nsx_models.NeutronNsxRuleMapping).filter_by(
|
|
neutron_id=rule['id']).one())
|
|
with context_.session.begin(subtransactions=True):
|
|
context_.session.delete(rule_mapping)
|
|
action = (firewall.DROP
|
|
if secgroup.get(provider_sg.PROVIDER)
|
|
else firewall.ALLOW)
|
|
rules = nsxlib.create_firewall_rules(
|
|
context_, fw_section['id'], nsgroup['id'],
|
|
secgroup.get(sg_logging.LOGGING, False), action,
|
|
secgroup['security_group_rules'])
|
|
nsxlib.save_sg_rule_mappings(context_.session, rules['rules'])
|
|
# Add nsgroup to a nested group
|
|
plugin.nsgroup_manager.add_nsgroup(nsgroup['id'])
|
|
|
|
|
|
def _update_ports_dynamic_criteria_tags():
|
|
port_client, _ = ports.get_port_and_profile_clients()
|
|
for port in neutron_db.get_ports():
|
|
secgroups = neutron_sg.get_port_security_groups(port['id'])
|
|
# Nothing to do with ports that are not associated with any sec-group.
|
|
if not secgroups:
|
|
continue
|
|
|
|
_, lport_id = neutron_db.get_lswitch_and_lport_id(port['id'])
|
|
lport = port_client.get(lport_id)
|
|
criteria_tags = nsxlib.get_lport_tags_for_security_groups(secgroups)
|
|
lport['tags'] = utils.update_v3_tags(
|
|
lport.get('tags', []), criteria_tags)
|
|
port_client._client.update(lport_id, body=lport)
|
|
|
|
|
|
def _update_security_group_dynamic_criteria():
|
|
secgroups = neutron_sg.get_security_groups()
|
|
for sg in secgroups:
|
|
nsgroup_id = neutron_sg.get_nsgroup_id(sg['id'])
|
|
membership_criteria = nsxlib.get_nsgroup_port_tag_expression(
|
|
security.PORT_SG_SCOPE, sg['id'])
|
|
try:
|
|
# We want to add the dynamic criteria and remove all direct members
|
|
# they will be added by the manager using the new criteria.
|
|
nsxlib.update_nsgroup(nsgroup_id,
|
|
membership_criteria=membership_criteria,
|
|
members=[])
|
|
except Exception as e:
|
|
LOG.warning(_LW("Failed to update membership criteria for nsgroup "
|
|
"%(nsgroup_id)s, request to backend returned "
|
|
"with error: %(error)s"),
|
|
{'nsgroup_id': nsgroup_id, 'error': str(e)})
|
|
|
|
|
|
@admin_utils.output_header
|
|
def migrate_nsgroups_to_dynamic_criteria(resource, event, trigger, **kwargs):
|
|
if not utils.is_nsx_version_1_1_0(nsxlib.get_version()):
|
|
LOG.error(_LE("Dynamic criteria grouping feature isn't supported by "
|
|
"this NSX version."))
|
|
return
|
|
# First, we add the criteria tags for all ports.
|
|
_update_ports_dynamic_criteria_tags()
|
|
# Update security-groups with dynamic criteria and remove direct members.
|
|
_update_security_group_dynamic_criteria()
|
|
|
|
|
|
registry.subscribe(migrate_nsgroups_to_dynamic_criteria,
|
|
constants.FIREWALL_NSX_GROUPS,
|
|
shell.Operations.MIGRATE_TO_DYNAMIC_CRITERIA.value)
|