
1. Since the nsxv plugin was defined globally in one of the utilities files, it was actually initialized for all the nsxv utilities, which has some side affects (like deploying backup edges during unrelated admin utilities in case some of the pools are not full), and also takes a long time. Now the plugin is initialized only when needed. 2. When the plugin is initialized during an admin-util call, we mustn't exit while spawn jobs for creating edges are still running, or else - those edges will be in PENDING-CREATE state for ever. Initializing the plugin using "with" and adding an exit method which waits for the jobs to finish solved this issue. Change-Id: Ia1fa557a8da149f79a46b3ca49b122f991b2ca9b
384 lines
15 KiB
Python
384 lines
15 KiB
Python
# Copyright 2016 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
|
|
import xml.etree.ElementTree as et
|
|
|
|
from neutron.callbacks import registry
|
|
from neutron import context
|
|
from neutron.db.models import securitygroup as sg_models
|
|
from neutron.db import models_v2
|
|
from neutron.db import securitygroups_db
|
|
from neutron.extensions import securitygroup as ext_sg
|
|
|
|
from vmware_nsx._i18n import _LE, _LI, _LW
|
|
from vmware_nsx.db import db as nsx_db
|
|
from vmware_nsx.db import nsx_models
|
|
from vmware_nsx.db import nsxv_db
|
|
from vmware_nsx.db import nsxv_models
|
|
from vmware_nsx.extensions import securitygrouppolicy as sg_policy
|
|
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.nsxv.resources import utils
|
|
from vmware_nsx.shell import resources as shell
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class NeutronSecurityGroupDB(utils.NeutronDbClient,
|
|
securitygroups_db.SecurityGroupDbMixin):
|
|
def __init__(self):
|
|
super(NeutronSecurityGroupDB, self)
|
|
# FIXME(roeyc): context is already defined in NeutrondDbClient
|
|
self.context = context.get_admin_context()
|
|
|
|
def get_security_groups_mappings(self):
|
|
q = self.context.session.query(
|
|
sg_models.SecurityGroup.name,
|
|
sg_models.SecurityGroup.id,
|
|
nsxv_models.NsxvSecurityGroupSectionMapping.ip_section_id,
|
|
nsx_models.NeutronNsxSecurityGroupMapping.nsx_id).join(
|
|
nsxv_models.NsxvSecurityGroupSectionMapping,
|
|
nsx_models.NeutronNsxSecurityGroupMapping).all()
|
|
sg_mappings = [{'name': mapp.name,
|
|
'id': mapp.id,
|
|
'section-uri': mapp.ip_section_id,
|
|
'nsx-securitygroup-id': mapp.nsx_id}
|
|
for mapp in q]
|
|
return sg_mappings
|
|
|
|
def get_security_group(self, sg_id):
|
|
return super(NeutronSecurityGroupDB, self).get_security_group(
|
|
self.context, sg_id)
|
|
|
|
def get_security_groups(self):
|
|
return super(NeutronSecurityGroupDB,
|
|
self).get_security_groups(self.context)
|
|
|
|
def delete_security_group_section_mapping(self, sg_id):
|
|
fw_mapping = self.context.session.query(
|
|
nsxv_models.NsxvSecurityGroupSectionMapping).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_vnics_in_security_group(self, security_group_id):
|
|
with utils.NsxVPluginWrapper() as plugin:
|
|
vnics = []
|
|
query = self.context.session.query(
|
|
models_v2.Port.id, models_v2.Port.device_id
|
|
).join(sg_models.SecurityGroupPortBinding).filter_by(
|
|
security_group_id=security_group_id).all()
|
|
for p in query:
|
|
vnic_index = plugin._get_port_vnic_index(self.context, p.id)
|
|
vnic_id = plugin._get_port_vnic_id(vnic_index, p.device_id)
|
|
vnics.append(vnic_id)
|
|
return vnics
|
|
|
|
|
|
class NsxFirewallAPI(object):
|
|
def __init__(self):
|
|
self.vcns = utils.get_nsxv_client()
|
|
|
|
def list_security_groups(self):
|
|
h, secgroups = self.vcns.list_security_groups()
|
|
if not secgroups:
|
|
return []
|
|
root = et.fromstring(secgroups)
|
|
secgroups = []
|
|
for sg in root.iter('securitygroup'):
|
|
sg_id = sg.find('objectId').text
|
|
# This specific security-group is not relevant to the plugin
|
|
if sg_id == 'securitygroup-1':
|
|
continue
|
|
secgroups.append({'name': sg.find('name').text,
|
|
'id': sg_id})
|
|
return secgroups
|
|
|
|
def list_fw_sections(self):
|
|
h, firewall_config = self.vcns.get_dfw_config()
|
|
if not firewall_config:
|
|
return []
|
|
root = et.fromstring(firewall_config)
|
|
sections = []
|
|
for sec in root.iter('section'):
|
|
sec_id = sec.attrib['id']
|
|
# Don't show NSX default sections, which are not relevant to OS.
|
|
if sec_id in ['1001', '1002', '1003']:
|
|
continue
|
|
sections.append({'name': sec.attrib['name'],
|
|
'id': sec_id})
|
|
return sections
|
|
|
|
def reorder_fw_sections(self):
|
|
# read all the sections
|
|
h, firewall_config = self.vcns.get_dfw_config()
|
|
if not firewall_config:
|
|
LOG.info(_LI("No firewall sections were found."))
|
|
return
|
|
|
|
root = et.fromstring(firewall_config)
|
|
|
|
for child in root:
|
|
if str(child.tag) == 'layer3Sections':
|
|
# go over the L3 sections and reorder them.
|
|
# policy sections should come first
|
|
sections = list(child.iter('section'))
|
|
regular_sections = []
|
|
policy_sections = []
|
|
|
|
for sec in sections:
|
|
if sec.attrib.get('managedBy') == 'NSX Service Composer':
|
|
policy_sections.append(sec)
|
|
else:
|
|
regular_sections.append(sec)
|
|
child.remove(sec)
|
|
|
|
if not policy_sections:
|
|
LOG.info(_LI("No need to reorder the firewall sections."))
|
|
return
|
|
|
|
# reorder the sections to have the policy sections first
|
|
reordered_sections = policy_sections + regular_sections
|
|
child.extend(reordered_sections)
|
|
|
|
# update the new order of sections in the backend
|
|
self.vcns.update_dfw_config(et.tostring(root), h)
|
|
LOG.info(_LI("L3 Firewall sections were reordered."))
|
|
|
|
|
|
neutron_sg = NeutronSecurityGroupDB()
|
|
nsxv_firewall = NsxFirewallAPI()
|
|
|
|
|
|
def _log_info(resource, data, attrs=['name', 'id']):
|
|
LOG.info(formatters.output_formatter(resource, data, attrs))
|
|
|
|
|
|
@admin_utils.list_handler(constants.SECURITY_GROUPS)
|
|
@admin_utils.output_header
|
|
def neutron_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-uri', '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 = nsxv_firewall.list_fw_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 = nsxv_firewall.list_security_groups()
|
|
_log_info(constants.FIREWALL_NSX_GROUPS, nsx_secgroups)
|
|
return bool(nsx_secgroups)
|
|
|
|
|
|
def _find_missing_security_groups():
|
|
nsx_secgroups = nsxv_firewall.list_security_groups()
|
|
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 = nsxv_firewall.list_fw_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.get('section-uri', '').split('/')[-1]:
|
|
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-uri']}
|
|
for sg in sgs_with_missing_section.values()]
|
|
_log_info(constants.FIREWALL_SECTIONS, missing_sections_info,
|
|
attrs=['securitygroup-name', 'securitygroup-id', 'section-uri'])
|
|
return bool(missing_sections_info)
|
|
|
|
|
|
@admin_utils.list_mismatches_handler(constants.FIREWALL_SECTIONS)
|
|
@admin_utils.output_header
|
|
def reorder_firewall_sections(resource, event, trigger, **kwargs):
|
|
nsxv_firewall.reorder_fw_sections()
|
|
|
|
|
|
@admin_utils.fix_mismatches_handler(constants.SECURITY_GROUPS)
|
|
@admin_utils.output_header
|
|
def fix_security_groups(resource, event, trigger, **kwargs):
|
|
context_ = context.get_admin_context()
|
|
sgs_with_missing_section = _find_missing_sections()
|
|
sgs_with_missing_nsx_group = _find_missing_security_groups()
|
|
if not sgs_with_missing_section and not sgs_with_missing_nsx_group:
|
|
# no mismatches
|
|
return
|
|
|
|
with utils.NsxVPluginWrapper() as plugin:
|
|
# If only the fw section is missing then create it.
|
|
for sg_id in (set(sgs_with_missing_section.keys()) -
|
|
set(sgs_with_missing_nsx_group.keys())):
|
|
neutron_sg.delete_security_group_section_mapping(sg_id)
|
|
secgroup = plugin.get_security_group(context_, sg_id)
|
|
plugin._create_fw_section_for_security_group(
|
|
context_, secgroup,
|
|
sgs_with_missing_section[sg_id]['nsx-securitygroup-id'])
|
|
|
|
# If nsx security-group is missing then create both nsx security-group
|
|
# and a new fw section (remove old one).
|
|
for sg_id, sg in sgs_with_missing_nsx_group.items():
|
|
secgroup = plugin.get_security_group(context_, sg_id)
|
|
if sg_id not in sgs_with_missing_section:
|
|
plugin._delete_section(sg['section-uri'])
|
|
neutron_sg.delete_security_group_section_mapping(sg_id)
|
|
neutron_sg.delete_security_group_backend_mapping(sg_id)
|
|
plugin._process_security_group_create_backend_resources(context_,
|
|
secgroup)
|
|
nsx_id = nsx_db.get_nsx_security_group_id(context_.session, sg_id)
|
|
for vnic_id in neutron_sg.get_vnics_in_security_group(sg_id):
|
|
plugin._add_member_to_security_group(nsx_id, vnic_id)
|
|
|
|
|
|
@admin_utils.output_header
|
|
def migrate_sg_to_policy(resource, event, trigger, **kwargs):
|
|
"""Change the mode of a security group from rules to NSX policy"""
|
|
if not kwargs.get('property'):
|
|
LOG.error(_LE("Need to specify security-group-id and policy-id "
|
|
"parameters"))
|
|
return
|
|
|
|
# input validation
|
|
properties = admin_utils.parse_multi_keyval_opt(kwargs['property'])
|
|
sg_id = properties.get('security-group-id')
|
|
if not sg_id:
|
|
LOG.error(_LE("Need to specify security-group-id parameter"))
|
|
return
|
|
policy_id = properties.get('policy-id')
|
|
if not policy_id:
|
|
LOG.error(_LE("Need to specify policy-id parameter"))
|
|
return
|
|
|
|
# validate that the security group exist and contains rules and no policy
|
|
context_ = context.get_admin_context()
|
|
with utils.NsxVPluginWrapper() as plugin:
|
|
try:
|
|
secgroup = plugin.get_security_group(context_, sg_id)
|
|
except ext_sg.SecurityGroupNotFound:
|
|
LOG.error(_LE("Security group %s was not found"), sg_id)
|
|
return
|
|
if secgroup.get('policy'):
|
|
LOG.error(_LE("Security group %s already uses a policy"), sg_id)
|
|
return
|
|
|
|
# validate that the policy exists
|
|
if not plugin.nsx_v.vcns.validate_inventory(policy_id):
|
|
LOG.error(_LE("NSX policy %s was not found"), policy_id)
|
|
return
|
|
|
|
# Delete the rules from the security group
|
|
LOG.info(_LI("Deleting the rules of security group: %s"), sg_id)
|
|
for rule in secgroup.get('security_group_rules', []):
|
|
try:
|
|
plugin.delete_security_group_rule(context_, rule['id'])
|
|
except Exception as e:
|
|
LOG.warning(_LW("Failed to delete rule %(r)s from security "
|
|
"group %(sg)s: %(e)s"),
|
|
{'r': rule['id'], 'sg': sg_id, 'e': e})
|
|
# continue anyway
|
|
|
|
# Delete the security group FW section
|
|
LOG.info(_LI("Deleting the section of security group: %s"), sg_id)
|
|
try:
|
|
section_uri = plugin._get_section_uri(context_.session, sg_id)
|
|
plugin._delete_section(section_uri)
|
|
nsxv_db.delete_neutron_nsx_section_mapping(
|
|
context_.session, sg_id)
|
|
except Exception as e:
|
|
LOG.warning(_LW("Failed to delete firewall section of security "
|
|
"group %(sg)s: %(e)s"),
|
|
{'sg': sg_id, 'e': e})
|
|
# continue anyway
|
|
|
|
# bind this security group to the policy in the backend and DB
|
|
nsx_sg_id = nsx_db.get_nsx_security_group_id(context_.session, sg_id)
|
|
LOG.info(_LI("Binding the NSX security group %(nsx)s to policy "
|
|
"%(pol)s"),
|
|
{'nsx': nsx_sg_id, 'pol': policy_id})
|
|
plugin._update_nsx_security_group_policies(
|
|
policy_id, None, nsx_sg_id)
|
|
prop = plugin._get_security_group_properties(context_, sg_id)
|
|
with context_.session.begin(subtransactions=True):
|
|
prop.update({sg_policy.POLICY: policy_id})
|
|
|
|
LOG.info(_LI("Done."))
|
|
|
|
|
|
registry.subscribe(migrate_sg_to_policy,
|
|
constants.SECURITY_GROUPS,
|
|
shell.Operations.MIGRATE_TO_POLICY.value)
|
|
|
|
registry.subscribe(reorder_firewall_sections,
|
|
constants.FIREWALL_SECTIONS,
|
|
shell.Operations.NSX_REORDER.value)
|