Adit Sarfaty a7d43ec275 [Admin-Util] NSXv: fix plugin issues
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
2017-02-01 15:52:53 +02:00

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)