Support for NVP advanced FwaaS service
The patch adds advanced FWaaS service support for NVP with VCNS: * NVP FWaaS is an advanced Service of NVP depending on NVP advanced service router - Once an advanced router id created, one corresponding vshield edge will be deployed, and then we can configure FW service on the vshield edge * NVP FWaaS service plugin still uses FWaaS DB service logic, while finally calling vShield Edge to support FWaaS service - When firewall object is created, we will attach the object to the advanced router with routedserviceinsertion_db service * on driver part, the driver will first convert the object to VSM known object input, and then send a synchronous JSON calling to VSM, and receive the result Implements: blueprint nvp-fwaas-plugin Change-Id: Id43af8821f5c553356e3cc870993eef99ef7def3
This commit is contained in:
parent
4afee8006c
commit
db9c24a519
@ -0,0 +1,60 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""nvp fwaas plugin
|
||||
|
||||
Revision ID: 3ed8f075e38a
|
||||
Revises: 338d7508968c
|
||||
Create Date: 2013-09-13 19:14:25.509033
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3ed8f075e38a'
|
||||
down_revision = '338d7508968c'
|
||||
|
||||
# Change to ['*'] if this migration applies to all plugins
|
||||
|
||||
migration_for_plugins = [
|
||||
'neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin'
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db import migration
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
op.create_table(
|
||||
'vcns_firewall_rule_bindings',
|
||||
sa.Column('rule_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('edge_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('rule_vseid', sa.String(length=36), nullable=True),
|
||||
sa.ForeignKeyConstraint(['rule_id'], ['firewall_rules.id'], ),
|
||||
sa.PrimaryKeyConstraint('rule_id', 'edge_id')
|
||||
)
|
||||
|
||||
|
||||
def downgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
op.drop_table('vcns_firewall_rule_bindings')
|
@ -65,13 +65,34 @@ class RoutedServiceInsertionDbMixin(object):
|
||||
context, resource['resource_id'], model)
|
||||
resource[rsi.ROUTER_ID] = binding['router_id']
|
||||
|
||||
def _get_resource_router_id_binding(self, context, resource_id, model):
|
||||
def _get_resource_router_id_binding(self, context, model,
|
||||
resource_id=None,
|
||||
router_id=None):
|
||||
query = self._model_query(context, ServiceRouterBinding)
|
||||
query = query.filter(
|
||||
ServiceRouterBinding.resource_id == resource_id,
|
||||
ServiceRouterBinding.resource_type == model.__tablename__)
|
||||
if resource_id:
|
||||
query = query.filter(
|
||||
ServiceRouterBinding.resource_id == resource_id)
|
||||
if router_id:
|
||||
query = query.filter(
|
||||
ServiceRouterBinding.router_id == router_id)
|
||||
return query.first()
|
||||
|
||||
def _get_resource_router_id_bindings(self, context, model,
|
||||
resource_ids=None,
|
||||
router_ids=None):
|
||||
query = self._model_query(context, ServiceRouterBinding)
|
||||
query = query.filter(
|
||||
ServiceRouterBinding.resource_type == model.__tablename__)
|
||||
if resource_ids:
|
||||
query = query.filter(
|
||||
ServiceRouterBinding.resource_id.in_(resource_ids))
|
||||
if router_ids:
|
||||
query = query.filter(
|
||||
ServiceRouterBinding.router_id.in_(router_ids))
|
||||
return query.all()
|
||||
|
||||
def _make_resource_router_id_dict(self, resource_router_binding, model,
|
||||
fields=None):
|
||||
resource = {'resource_id': resource_router_binding['resource_id'],
|
||||
@ -82,6 +103,6 @@ class RoutedServiceInsertionDbMixin(object):
|
||||
def _delete_resource_router_id_binding(self, context, resource_id, model):
|
||||
with context.session.begin(subtransactions=True):
|
||||
binding = self._get_resource_router_id_binding(
|
||||
context, resource_id, model)
|
||||
context, model, resource_id=resource_id)
|
||||
if binding:
|
||||
context.session.delete(binding)
|
||||
|
@ -34,6 +34,12 @@ EXTENDED_ATTRIBUTES_2_0 = {
|
||||
'validate': {'type:uuid_or_none': None},
|
||||
'default': None, 'is_visible': True},
|
||||
},
|
||||
|
||||
'firewalls': {
|
||||
ROUTER_ID: {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:uuid_or_none': None},
|
||||
'default': None, 'is_visible': True},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 VMware, Inc.
|
||||
# All Rights Reserved
|
||||
@ -18,13 +18,16 @@
|
||||
|
||||
import netaddr
|
||||
from oslo.config import cfg
|
||||
from sqlalchemy.orm import exc as sa_exc
|
||||
|
||||
from neutron.common import exceptions as q_exc
|
||||
from neutron.db.firewall import firewall_db
|
||||
from neutron.db import l3_db
|
||||
from neutron.db import routedserviceinsertion_db as rsi_db
|
||||
from neutron.extensions import firewall as fw_ext
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.common import constants as service_constants
|
||||
from neutron.plugins.nicira.common import config # noqa
|
||||
from neutron.plugins.nicira.common import exceptions as nvp_exc
|
||||
from neutron.plugins.nicira.dbexts import servicerouter as sr_db
|
||||
from neutron.plugins.nicira.dbexts import vcns_db
|
||||
from neutron.plugins.nicira.dbexts import vcns_models
|
||||
@ -38,6 +41,7 @@ from neutron.plugins.nicira.vshield.common.constants import RouterStatus
|
||||
from neutron.plugins.nicira.vshield.common import exceptions
|
||||
from neutron.plugins.nicira.vshield.tasks.constants import TaskStatus
|
||||
from neutron.plugins.nicira.vshield import vcns_driver
|
||||
from sqlalchemy.orm import exc as sa_exc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -66,11 +70,15 @@ ROUTER_STATUS_LEVEL = {
|
||||
|
||||
|
||||
class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
|
||||
NeutronPlugin.NvpPluginV2):
|
||||
|
||||
NeutronPlugin.NvpPluginV2,
|
||||
rsi_db.RoutedServiceInsertionDbMixin,
|
||||
firewall_db.Firewall_db_mixin,
|
||||
):
|
||||
supported_extension_aliases = (
|
||||
NeutronPlugin.NvpPluginV2.supported_extension_aliases + [
|
||||
'service-router'
|
||||
"service-router",
|
||||
"routed-service-insertion",
|
||||
"fwaas"
|
||||
])
|
||||
|
||||
def __init__(self):
|
||||
@ -652,7 +660,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
|
||||
router = self._get_router(context, router_id)
|
||||
if router.enable_snat:
|
||||
self._update_nat_rules(context, router)
|
||||
# TODO(fank): do rollback if error, or have a dedicated thread
|
||||
# TODO(fank): do rollback on error, or have a dedicated thread
|
||||
# do sync work (rollback, re-configure, or make router down)
|
||||
self._vcns_update_static_routes(context, router=router)
|
||||
return info
|
||||
@ -664,7 +672,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
|
||||
router = self._get_router(context, router_id)
|
||||
if router.enable_snat:
|
||||
self._update_nat_rules(context, router)
|
||||
# TODO(fank): do rollback if error, or have a dedicated thread
|
||||
# TODO(fank): do rollback on error, or have a dedicated thread
|
||||
# do sync work (rollback, re-configure, or make router down)
|
||||
self._vcns_update_static_routes(context, router=router)
|
||||
return info
|
||||
@ -675,7 +683,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
|
||||
router_id = fip.get('router_id')
|
||||
if router_id and self._is_advanced_service_router(context, router_id):
|
||||
router = self._get_router(context, router_id)
|
||||
# TODO(fank): do rollback if error, or have a dedicated thread
|
||||
# TODO(fank): do rollback on error, or have a dedicated thread
|
||||
# do sync work (rollback, re-configure, or make router down)
|
||||
self._update_interface(context, router)
|
||||
self._update_nat_rules(context, router)
|
||||
@ -687,7 +695,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
|
||||
router_id = fip.get('router_id')
|
||||
if router_id and self._is_advanced_service_router(context, router_id):
|
||||
router = self._get_router(context, router_id)
|
||||
# TODO(fank): do rollback if error, or have a dedicated thread
|
||||
# TODO(fank): do rollback on error, or have a dedicated thread
|
||||
# do sync work (rollback, re-configure, or make router down)
|
||||
self._update_interface(context, router)
|
||||
self._update_nat_rules(context, router)
|
||||
@ -701,7 +709,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
|
||||
super(NvpAdvancedPlugin, self).delete_floatingip(context, id)
|
||||
if router_id and self._is_advanced_service_router(context, router_id):
|
||||
router = self._get_router(context, router_id)
|
||||
# TODO(fank): do rollback if error, or have a dedicated thread
|
||||
# TODO(fank): do rollback on error, or have a dedicated thread
|
||||
# do sync work (rollback, re-configure, or make router down)
|
||||
self._update_interface(context, router)
|
||||
self._update_nat_rules(context, router)
|
||||
@ -717,16 +725,291 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
|
||||
port_id)
|
||||
if router_id and self._is_advanced_service_router(context, router_id):
|
||||
router = self._get_router(context, router_id)
|
||||
# TODO(fank): do rollback if error, or have a dedicated thread
|
||||
# TODO(fank): do rollback on error, or have a dedicated thread
|
||||
# do sync work (rollback, re-configure, or make router down)
|
||||
self._update_interface(context, router)
|
||||
self._update_nat_rules(context, router)
|
||||
|
||||
#
|
||||
# FWaaS plugin implementation
|
||||
#
|
||||
def _firewall_set_status(
|
||||
self, context, firewall_id, status, firewall=None):
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_db = self._get_firewall(context, firewall_id)
|
||||
if status == service_constants.PENDING_UPDATE and (
|
||||
fw_db.status == service_constants.PENDING_DELETE):
|
||||
raise fw_ext.FirewallInPendingState(
|
||||
firewall_id=firewall_id, pending_state=status)
|
||||
else:
|
||||
fw_db.status = status
|
||||
if firewall:
|
||||
firewall['status'] = status
|
||||
|
||||
def _ensure_firewall_update_allowed(self, context, firewall_id):
|
||||
fwall = self.get_firewall(context, firewall_id)
|
||||
if fwall['status'] in [service_constants.PENDING_CREATE,
|
||||
service_constants.PENDING_UPDATE,
|
||||
service_constants.PENDING_DELETE]:
|
||||
raise fw_ext.FirewallInPendingState(firewall_id=firewall_id,
|
||||
pending_state=fwall['status'])
|
||||
|
||||
def _ensure_firewall_policy_update_allowed(
|
||||
self, context, firewall_policy_id):
|
||||
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
|
||||
for firewall_id in firewall_policy.get('firewall_list', []):
|
||||
self._ensure_firewall_update_allowed(context, firewall_id)
|
||||
|
||||
def _ensure_update_or_delete_firewall_rule(
|
||||
self, context, firewall_rule_id):
|
||||
fw_rule = self.get_firewall_rule(context, firewall_rule_id)
|
||||
if fw_rule.get('firewall_policy_id'):
|
||||
self._ensure_firewall_policy_update_allowed(
|
||||
context, fw_rule['firewall_policy_id'])
|
||||
|
||||
def _make_firewall_rule_list_by_policy_id(self, context, fw_policy_id):
|
||||
if not fw_policy_id:
|
||||
return None
|
||||
firewall_policy_db = self._get_firewall_policy(context, fw_policy_id)
|
||||
return [
|
||||
self._make_firewall_rule_dict(fw_rule_db)
|
||||
for fw_rule_db in firewall_policy_db['firewall_rules']
|
||||
]
|
||||
|
||||
def _get_edge_id_by_vcns_edge_binding(self, context,
|
||||
router_id):
|
||||
#Get vcns_router_binding mapping between router and edge
|
||||
router_binding = vcns_db.get_vcns_router_binding(
|
||||
context.session, router_id)
|
||||
return router_binding.edge_id
|
||||
|
||||
def _get_firewall_list_from_firewall_policy(self, context, policy_id):
|
||||
firewall_policy_db = self._get_firewall_policy(context, policy_id)
|
||||
return [
|
||||
self._make_firewall_dict(fw_db)
|
||||
for fw_db in firewall_policy_db['firewalls']
|
||||
]
|
||||
|
||||
def _get_firewall_list_from_firewall_rule(self, context, rule_id):
|
||||
rule = self._get_firewall_rule(context, rule_id)
|
||||
if not rule.firewall_policy_id:
|
||||
# The firewall rule is not associated with firewall policy yet
|
||||
return None
|
||||
|
||||
return self._get_firewall_list_from_firewall_policy(
|
||||
context, rule.firewall_policy_id)
|
||||
|
||||
def _vcns_update_firewall(self, context, fw, router_id=None, **kwargs):
|
||||
edge_id = kwargs.get('edge_id')
|
||||
if not edge_id:
|
||||
edge_id = self._get_edge_id_by_vcns_edge_binding(
|
||||
context, router_id)
|
||||
firewall_rule_list = kwargs.get('firewall_rule_list')
|
||||
if not firewall_rule_list:
|
||||
firewall_rule_list = self._make_firewall_rule_list_by_policy_id(
|
||||
context, fw['firewall_policy_id'])
|
||||
fw_with_rules = fw
|
||||
fw_with_rules['firewall_rule_list'] = firewall_rule_list
|
||||
try:
|
||||
self.vcns_driver.update_firewall(context, edge_id, fw_with_rules)
|
||||
except exceptions.VcnsApiException as e:
|
||||
self._firewall_set_status(
|
||||
context, fw['id'], service_constants.ERROR)
|
||||
msg = (_("Failed to create firewall on vShield Edge "
|
||||
"bound on router %s") % router_id)
|
||||
LOG.exception(msg)
|
||||
raise e
|
||||
|
||||
except exceptions.BadRequest as e:
|
||||
self._firewall_set_status(
|
||||
context, fw['id'], service_constants.ERROR)
|
||||
LOG.exception(_("Bad Firewall request Input"))
|
||||
raise e
|
||||
|
||||
def _vcns_delete_firewall(self, context, router_id=None, **kwargs):
|
||||
edge_id = kwargs.get('edge_id')
|
||||
if not edge_id:
|
||||
edge_id = self._get_edge_id_by_vcns_edge_binding(
|
||||
context, router_id)
|
||||
#TODO(linb):do rollback on error
|
||||
self.vcns_driver.delete_firewall(context, edge_id)
|
||||
|
||||
def create_firewall(self, context, firewall):
|
||||
LOG.debug(_("create_firewall() called"))
|
||||
router_id = firewall['firewall'].get(vcns_const.ROUTER_ID)
|
||||
if not router_id:
|
||||
msg = _("router_id is not provided!")
|
||||
LOG.error(msg)
|
||||
raise q_exc.BadRequest(resource='router', msg=msg)
|
||||
if not self._is_advanced_service_router(context, router_id):
|
||||
msg = _("router_id:%s is not an advanced router!") % router_id
|
||||
LOG.error(msg)
|
||||
raise q_exc.BadRequest(resource='router', msg=msg)
|
||||
if self._get_resource_router_id_binding(
|
||||
context, firewall_db.Firewall, router_id=router_id):
|
||||
msg = _("A firewall is already associated with the router")
|
||||
LOG.error(msg)
|
||||
raise nvp_exc.NvpServiceOverQuota(
|
||||
overs='firewall', err_msg=msg)
|
||||
|
||||
fw = super(NvpAdvancedPlugin, self).create_firewall(context, firewall)
|
||||
#Add router service insertion binding with firewall object
|
||||
res = {
|
||||
'id': fw['id'],
|
||||
'router_id': router_id
|
||||
}
|
||||
self._process_create_resource_router_id(
|
||||
context, res, firewall_db.Firewall)
|
||||
#Since there is only one firewall per edge,
|
||||
#here would be bulk configureation operation on firewall
|
||||
self._vcns_update_firewall(context, fw, router_id)
|
||||
self._firewall_set_status(
|
||||
context, fw['id'], service_constants.ACTIVE, fw)
|
||||
return fw
|
||||
|
||||
def update_firewall(self, context, id, firewall):
|
||||
LOG.debug(_("update_firewall() called"))
|
||||
self._ensure_firewall_update_allowed(context, id)
|
||||
rule_list_pre = self._make_firewall_rule_list_by_policy_id(
|
||||
context,
|
||||
self.get_firewall(context, id)['firewall_policy_id'])
|
||||
firewall['firewall']['status'] = service_constants.PENDING_UPDATE
|
||||
fw = super(NvpAdvancedPlugin, self).update_firewall(
|
||||
context, id, firewall)
|
||||
rule_list_new = self._make_firewall_rule_list_by_policy_id(
|
||||
context, fw['firewall_policy_id'])
|
||||
if rule_list_pre == rule_list_new:
|
||||
self._firewall_set_status(
|
||||
context, fw['id'], service_constants.ACTIVE, fw)
|
||||
return fw
|
||||
else:
|
||||
service_router_binding = self._get_resource_router_id_binding(
|
||||
context, firewall_db.Firewall, resource_id=id)
|
||||
self._vcns_update_firewall(
|
||||
context, fw, service_router_binding.router_id)
|
||||
self._firewall_set_status(
|
||||
context, fw['id'], service_constants.ACTIVE, fw)
|
||||
return fw
|
||||
|
||||
def delete_firewall(self, context, id):
|
||||
LOG.debug(_("delete_firewall() called"))
|
||||
self._firewall_set_status(
|
||||
context, id, service_constants.PENDING_DELETE)
|
||||
service_router_binding = self._get_resource_router_id_binding(
|
||||
context, firewall_db.Firewall, resource_id=id)
|
||||
self._vcns_delete_firewall(context, service_router_binding.router_id)
|
||||
super(NvpAdvancedPlugin, self).delete_firewall(context, id)
|
||||
self._delete_resource_router_id_binding(
|
||||
context, id, firewall_db.Firewall)
|
||||
|
||||
def update_firewall_rule(self, context, id, firewall_rule):
|
||||
LOG.debug(_("update_firewall_rule() called"))
|
||||
self._ensure_update_or_delete_firewall_rule(context, id)
|
||||
fwr_pre = self.get_firewall_rule(context, id)
|
||||
fwr = super(NvpAdvancedPlugin, self).update_firewall_rule(
|
||||
context, id, firewall_rule)
|
||||
if fwr_pre == fwr:
|
||||
return fwr
|
||||
|
||||
# check if this rule is associated with firewall
|
||||
fw_list = self._get_firewall_list_from_firewall_rule(context, id)
|
||||
if not fw_list:
|
||||
return fwr
|
||||
|
||||
for fw in fw_list:
|
||||
# get router service insertion binding with firewall id
|
||||
service_router_binding = self._get_resource_router_id_binding(
|
||||
context, firewall_db.Firewall, resource_id=fw['id'])
|
||||
edge_id = self._get_edge_id_by_vcns_edge_binding(
|
||||
context, service_router_binding.router_id)
|
||||
|
||||
#TODO(linb): do rollback on error
|
||||
self.vcns_driver.update_firewall_rule(context, id, edge_id, fwr)
|
||||
|
||||
return fwr
|
||||
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
LOG.debug(_("update_firewall_policy() called"))
|
||||
self._ensure_firewall_policy_update_allowed(context, id)
|
||||
firewall_rules_pre = self._make_firewall_rule_list_by_policy_id(
|
||||
context, id)
|
||||
fwp = super(NvpAdvancedPlugin, self).update_firewall_policy(
|
||||
context, id, firewall_policy)
|
||||
firewall_rules = self._make_firewall_rule_list_by_policy_id(
|
||||
context, id)
|
||||
if firewall_rules_pre == firewall_rules:
|
||||
return fwp
|
||||
|
||||
# check if this policy is associated with firewall
|
||||
fw_list = self._get_firewall_list_from_firewall_policy(context, id)
|
||||
if not fw_list:
|
||||
return fwp
|
||||
|
||||
for fw in fw_list:
|
||||
# Get the router_service insertion binding with firewall id
|
||||
# TODO(fank): optimized by using _get_resource_router_id_bindings
|
||||
service_router_binding = self._get_resource_router_id_binding(
|
||||
context, firewall_db.Firewall, resource_id=fw['id'])
|
||||
self._vcns_update_firewall(
|
||||
context, fw, service_router_binding.router_id)
|
||||
return fwp
|
||||
|
||||
def insert_rule(self, context, id, rule_info):
|
||||
LOG.debug(_("insert_rule() called"))
|
||||
self._ensure_firewall_policy_update_allowed(context, id)
|
||||
fwp = super(NvpAdvancedPlugin, self).insert_rule(
|
||||
context, id, rule_info)
|
||||
fwr = super(NvpAdvancedPlugin, self).get_firewall_rule(
|
||||
context, rule_info['firewall_rule_id'])
|
||||
|
||||
# check if this policy is associated with firewall
|
||||
fw_list = self._get_firewall_list_from_firewall_policy(context, id)
|
||||
if not fw_list:
|
||||
return fwp
|
||||
for fw in fw_list:
|
||||
# TODO(fank): optimized by using _get_resource_router_id_bindings
|
||||
service_router_binding = self._get_resource_router_id_binding(
|
||||
context, firewall_db.Firewall, resource_id=fw['id'])
|
||||
edge_id = self._get_edge_id_by_vcns_edge_binding(
|
||||
context, service_router_binding.router_id)
|
||||
|
||||
if rule_info.get('insert_before') or rule_info.get('insert_after'):
|
||||
#if insert_before or insert_after is set, we would call
|
||||
#VCNS insert_rule API
|
||||
#TODO(linb): do rollback on error
|
||||
self.vcns_driver.insert_rule(context, rule_info, edge_id, fwr)
|
||||
else:
|
||||
#Else we would call bulk configuration on the firewall
|
||||
self._vcns_update_firewall(context, fw, edge_id=edge_id)
|
||||
return fwp
|
||||
|
||||
def remove_rule(self, context, id, rule_info):
|
||||
LOG.debug(_("remove_rule() called"))
|
||||
self._ensure_firewall_policy_update_allowed(context, id)
|
||||
fwp = super(NvpAdvancedPlugin, self).remove_rule(
|
||||
context, id, rule_info)
|
||||
fwr = super(NvpAdvancedPlugin, self).get_firewall_rule(
|
||||
context, rule_info['firewall_rule_id'])
|
||||
|
||||
# check if this policy is associated with firewall
|
||||
fw_list = self._get_firewall_list_from_firewall_policy(context, id)
|
||||
if not fw_list:
|
||||
return fwp
|
||||
for fw in fw_list:
|
||||
# TODO(fank): optimized by using _get_resource_router_id_bindings
|
||||
service_router_binding = self._get_resource_router_id_binding(
|
||||
context, firewall_db.Firewall, resource_id=fw['id'])
|
||||
edge_id = self._get_edge_id_by_vcns_edge_binding(
|
||||
context, service_router_binding.router_id)
|
||||
#TODO(linb): do rollback on error
|
||||
self.vcns_driver.delete_firewall_rule(
|
||||
context, fwr['id'], edge_id)
|
||||
return fwp
|
||||
|
||||
|
||||
class VcnsCallbacks(object):
|
||||
"""Edge callback implementation
|
||||
|
||||
Callback functions for asynchronous tasks
|
||||
"""Edge callback implementation Callback functions for
|
||||
asynchronous tasks.
|
||||
"""
|
||||
def __init__(self, plugin):
|
||||
self.plugin = plugin
|
||||
|
@ -62,3 +62,17 @@ class MaintenanceInProgress(NvpPluginException):
|
||||
message = _("The networking backend is currently in maintenance mode and "
|
||||
"therefore unable to accept requests which modify its state. "
|
||||
"Please try later.")
|
||||
|
||||
|
||||
class NvpServicePluginException(q_exc.NeutronException):
|
||||
"""NVP Service Plugin exceptions."""
|
||||
message = _("An unexpected error happened "
|
||||
"in the NVP Service Plugin:%(err_msg)s")
|
||||
|
||||
|
||||
class NvpServiceOverQuota(q_exc.Conflict):
|
||||
message = _("Quota exceeded for Vcns resource: %(overs)s: %(err_msg)s")
|
||||
|
||||
|
||||
class NvpVcnsDriverException(NvpServicePluginException):
|
||||
message = _("Error happened in NVP VCNS Driver: %(err_msg)s")
|
||||
|
@ -15,6 +15,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
from neutron.plugins.nicira.common import exceptions as nvp_exc
|
||||
from neutron.plugins.nicira.dbexts import vcns_models
|
||||
|
||||
|
||||
@ -48,3 +51,47 @@ def delete_vcns_router_binding(session, router_id):
|
||||
binding = (session.query(vcns_models.VcnsRouterBinding).
|
||||
filter_by(router_id=router_id).one())
|
||||
session.delete(binding)
|
||||
|
||||
|
||||
#
|
||||
# Edge Firewall binding methods
|
||||
def add_vcns_edge_firewallrule_binding(session, map_info):
|
||||
with session.begin(subtransactions=True):
|
||||
binding = vcns_models.VcnsEdgeFirewallRuleBinding(
|
||||
rule_id=map_info['rule_id'],
|
||||
rule_vseid=map_info['rule_vseid'],
|
||||
edge_id=map_info['edge_id'])
|
||||
session.add(binding)
|
||||
return binding
|
||||
|
||||
|
||||
def delete_vcns_edge_firewallrule_binding(session, id):
|
||||
with session.begin(subtransactions=True):
|
||||
if not (session.query(vcns_models.VcnsEdgeFirewallRuleBinding).
|
||||
filter_by(rule_id=id).delete()):
|
||||
msg = _("Rule Resource binding with id:%s not found!") % id
|
||||
raise nvp_exc.NvpServicePluginException(err_msg=msg)
|
||||
|
||||
|
||||
def get_vcns_edge_firewallrule_binding(session, id, edge_id):
|
||||
with session.begin(subtransactions=True):
|
||||
return (session.query(vcns_models.VcnsEdgeFirewallRuleBinding).
|
||||
filter_by(rule_id=id, edge_id=edge_id).first())
|
||||
|
||||
|
||||
def get_vcns_edge_firewallrule_binding_by_vseid(
|
||||
session, edge_id, rule_vseid):
|
||||
with session.begin(subtransactions=True):
|
||||
try:
|
||||
return (session.query(vcns_models.VcnsEdgeFirewallRuleBinding).
|
||||
filter_by(edge_id=edge_id, rule_vseid=rule_vseid).one())
|
||||
except exc.NoResultFound:
|
||||
msg = _("Rule Resource binding not found!")
|
||||
raise nvp_exc.NvpServicePluginException(err_msg=msg)
|
||||
|
||||
|
||||
def cleanup_vcns_edge_firewallrule_binding(session, edge_id):
|
||||
with session.begin(subtransactions=True):
|
||||
session.query(
|
||||
vcns_models.VcnsEdgeFirewallRuleBinding).filter_by(
|
||||
edge_id=edge_id).delete()
|
||||
|
@ -36,3 +36,18 @@ class VcnsRouterBinding(model_base.BASEV2, models_v2.HasStatusDescription):
|
||||
nullable=True)
|
||||
lswitch_id = sa.Column(sa.String(36),
|
||||
nullable=False)
|
||||
|
||||
|
||||
#
|
||||
# VCNS Edge FW mapping tables
|
||||
#
|
||||
class VcnsEdgeFirewallRuleBinding(model_base.BASEV2):
|
||||
"""1:1 mapping between firewall rule and edge firewall rule_id."""
|
||||
|
||||
__tablename__ = 'vcns_firewall_rule_bindings'
|
||||
|
||||
rule_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey("firewall_rules.id"),
|
||||
primary_key=True)
|
||||
edge_id = sa.Column(sa.String(36), primary_key=True)
|
||||
rule_vseid = sa.Column(sa.String(36))
|
||||
|
@ -1,7 +1,7 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 VMware, Inc.
|
||||
# All Rights Reserved
|
||||
# Copyright 2013 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
|
||||
|
@ -29,6 +29,14 @@ class VcnsGeneralException(VcnsException):
|
||||
super(VcnsGeneralException, self).__init__()
|
||||
|
||||
|
||||
class VcnsBadRequest(exceptions.BadRequest):
|
||||
pass
|
||||
|
||||
|
||||
class VcnsNotFound(exceptions.NotFound):
|
||||
message = _('%(resource)s not found: %(msg)s')
|
||||
|
||||
|
||||
class VcnsApiException(VcnsException):
|
||||
message = _("An unknown exception %(status)s occurred: %(response)s.")
|
||||
|
||||
|
354
neutron/plugins/nicira/vshield/edge_firewall_driver.py
Normal file
354
neutron/plugins/nicira/vshield/edge_firewall_driver.py
Normal file
@ -0,0 +1,354 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 VMware, Inc
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# @author: Leon Cui, VMware
|
||||
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.common import constants
|
||||
from neutron.plugins.nicira.dbexts import vcns_db
|
||||
from neutron.plugins.nicira.vshield.common import (
|
||||
exceptions as vcns_exc)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
VSE_FWAAS_ALLOW = "accept"
|
||||
VSE_FWAAS_DENY = "deny"
|
||||
|
||||
|
||||
class EdgeFirewallDriver(db_base_plugin_v2.NeutronDbPluginV2):
|
||||
"""Implementation of driver APIs for
|
||||
Edge Firewall feature configuration
|
||||
"""
|
||||
def _convert_firewall_action(self, action):
|
||||
if action == constants.FWAAS_ALLOW:
|
||||
return VSE_FWAAS_ALLOW
|
||||
elif action == constants.FWAAS_DENY:
|
||||
return VSE_FWAAS_DENY
|
||||
else:
|
||||
msg = _("Invalid action value %s in a firewall rule") % action
|
||||
raise vcns_exc.BadRequest(resource='firewall_rule', msg=msg)
|
||||
|
||||
def _restore_firewall_action(self, action):
|
||||
if action == VSE_FWAAS_ALLOW:
|
||||
return constants.FWAAS_ALLOW
|
||||
elif action == VSE_FWAAS_DENY:
|
||||
return constants.FWAAS_DENY
|
||||
else:
|
||||
msg = (_("Invalid action value %s in "
|
||||
"a vshield firewall rule") % action)
|
||||
raise vcns_exc.BadRequest(resource='firewall_rule', msg=msg)
|
||||
|
||||
def _get_port_range_from_min_max_ports(self, min_port, max_port):
|
||||
if not min_port:
|
||||
return None
|
||||
if min_port == max_port:
|
||||
return str(min_port)
|
||||
else:
|
||||
return '%d:%d' % (min_port, max_port)
|
||||
|
||||
def _get_min_max_ports_from_range(self, port_range):
|
||||
if not port_range:
|
||||
return [None, None]
|
||||
min_port, sep, max_port = port_range.partition(":")
|
||||
if not max_port:
|
||||
max_port = min_port
|
||||
return [int(min_port), int(max_port)]
|
||||
|
||||
def _convert_firewall_rule(self, context, rule, index=None):
|
||||
vcns_rule = {
|
||||
"name": rule['name'],
|
||||
"description": rule['description'],
|
||||
"action": self._convert_firewall_action(rule['action']),
|
||||
"enabled": rule['enabled']}
|
||||
if rule.get('source_ip_address'):
|
||||
vcns_rule['source'] = {
|
||||
"ipAddress": [rule['source_ip_address']]
|
||||
}
|
||||
if rule.get('destination_ip_address'):
|
||||
vcns_rule['destination'] = {
|
||||
"ipAddress": [rule['destination_ip_address']]
|
||||
}
|
||||
service = {}
|
||||
if rule.get('source_port'):
|
||||
min_port, max_port = self._get_min_max_ports_from_range(
|
||||
rule['source_port'])
|
||||
service['sourcePort'] = [i for i in range(min_port, max_port + 1)]
|
||||
if rule.get('destination_port'):
|
||||
min_port, max_port = self._get_min_max_ports_from_range(
|
||||
rule['destination_port'])
|
||||
service['port'] = [i for i in range(min_port, max_port + 1)]
|
||||
if rule.get('protocol'):
|
||||
service['protocol'] = rule['protocol']
|
||||
if service:
|
||||
vcns_rule['application'] = {
|
||||
'service': [service]
|
||||
}
|
||||
if index:
|
||||
vcns_rule['ruleTag'] = index
|
||||
return vcns_rule
|
||||
|
||||
def _restore_firewall_rule(self, context, edge_id, response):
|
||||
rule = response
|
||||
rule_binding = vcns_db.get_vcns_edge_firewallrule_binding_by_vseid(
|
||||
context.session, edge_id, rule['ruleId'])
|
||||
service = rule['application']['service'][0]
|
||||
src_port_range = self._get_port_range_from_min_max_ports(
|
||||
service['sourcePort'][0], service['sourcePort'][-1])
|
||||
dst_port_range = self._get_port_range_from_min_max_ports(
|
||||
service['port'][0], service['port'][-1])
|
||||
return {
|
||||
'firewall_rule': {
|
||||
'name': rule['name'],
|
||||
'id': rule_binding['rule_id'],
|
||||
'description': rule['description'],
|
||||
'source_ip_address': rule['source']['ipAddress'][0],
|
||||
'destination_ip_address': rule['destination']['ipAddress'][0],
|
||||
'protocol': service['protocol'],
|
||||
'destination_port': dst_port_range,
|
||||
'source_port': src_port_range,
|
||||
'action': self._restore_firewall_action(rule['action']),
|
||||
'enabled': rule['enabled']}}
|
||||
|
||||
def _convert_firewall(self, context, firewall):
|
||||
#bulk configuration on firewall and rescheduling the rule binding
|
||||
ruleTag = 1
|
||||
vcns_rules = []
|
||||
for rule in firewall['firewall_rule_list']:
|
||||
vcns_rule = self._convert_firewall_rule(context, rule, ruleTag)
|
||||
vcns_rules.append(vcns_rule)
|
||||
ruleTag += 1
|
||||
return {
|
||||
'featureType': "firewall_4.0",
|
||||
'firewallRules': {
|
||||
'firewallRules': vcns_rules}}
|
||||
|
||||
def _restore_firewall(self, context, edge_id, response):
|
||||
res = {}
|
||||
res['firewall_rule_list'] = []
|
||||
for rule in response['firewallRules']['firewallRules']:
|
||||
rule_binding = (
|
||||
vcns_db.get_vcns_edge_firewallrule_binding_by_vseid(
|
||||
context.session, edge_id, rule['ruleId']))
|
||||
if rule_binding is None:
|
||||
continue
|
||||
service = rule['application']['service'][0]
|
||||
src_port_range = self._get_port_range_from_min_max_ports(
|
||||
service['sourcePort'][0], service['sourcePort'][-1])
|
||||
dst_port_range = self._get_port_range_from_min_max_ports(
|
||||
service['port'][0], service['port'][-1])
|
||||
item = {
|
||||
'firewall_rule': {
|
||||
'name': rule['name'],
|
||||
'id': rule_binding['rule_id'],
|
||||
'description': rule['description'],
|
||||
'source_ip_address': rule['source']['ipAddress'][0],
|
||||
'destination_ip_address': rule[
|
||||
'destination']['ipAddress'][0],
|
||||
'protocol': service['protocol'],
|
||||
'destination_port': dst_port_range,
|
||||
'source_port': src_port_range,
|
||||
'action': self._restore_firewall_action(rule['action']),
|
||||
'enabled': rule['enabled']}}
|
||||
res['firewall_rule_list'].append(item)
|
||||
return res
|
||||
|
||||
def _create_rule_id_mapping(
|
||||
self, context, edge_id, firewall, vcns_fw):
|
||||
for rule in vcns_fw['firewallRules']['firewallRules']:
|
||||
index = rule['ruleTag'] - 1
|
||||
#TODO(linb):a simple filter of the retrived rules which may be
|
||||
#created by other operations unintentionally
|
||||
if index < len(firewall['firewall_rule_list']):
|
||||
rule_vseid = rule['ruleId']
|
||||
rule_id = firewall['firewall_rule_list'][index]['id']
|
||||
map_info = {
|
||||
'rule_id': rule_id,
|
||||
'rule_vseid': rule_vseid,
|
||||
'edge_id': edge_id
|
||||
}
|
||||
vcns_db.add_vcns_edge_firewallrule_binding(
|
||||
context.session, map_info)
|
||||
|
||||
def _get_firewall(self, context, edge_id):
|
||||
try:
|
||||
return self.vcns.get_firewall(edge_id)[1]
|
||||
except vcns_exc.VcnsApiException as e:
|
||||
LOG.exception(_("Failed to get firewall with edge "
|
||||
"id: %s"), edge_id)
|
||||
raise e
|
||||
|
||||
def _get_firewall_rule_next(self, context, edge_id, rule_vseid):
|
||||
# Return the firewall rule below 'rule_vseid'
|
||||
fw_cfg = self._get_firewall(context, edge_id)
|
||||
for i in range(len(fw_cfg['firewallRules']['firewallRules'])):
|
||||
rule_cur = fw_cfg['firewallRules']['firewallRules'][i]
|
||||
if str(rule_cur['ruleId']) == rule_vseid:
|
||||
if (i + 1) == len(fw_cfg['firewallRules']['firewallRules']):
|
||||
return None
|
||||
else:
|
||||
return fw_cfg['firewallRules']['firewallRules'][i + 1]
|
||||
|
||||
def get_firewall_rule(self, context, id, edge_id):
|
||||
rule_map = vcns_db.get_vcns_edge_firewallrule_binding(
|
||||
context.session, id, edge_id)
|
||||
if rule_map is None:
|
||||
msg = _("No rule id:%s found in the edge_firewall_binding") % id
|
||||
LOG.error(msg)
|
||||
raise vcns_exc.VcnsNotFound(
|
||||
resource='vcns_firewall_rule_bindings', msg=msg)
|
||||
vcns_rule_id = rule_map.rule_vseid
|
||||
try:
|
||||
response = self.vcns.get_firewall_rule(
|
||||
edge_id, vcns_rule_id)[1]
|
||||
except vcns_exc.VcnsApiException as e:
|
||||
LOG.exception(_("Failed to get firewall rule: %(rule_id)s "
|
||||
"with edge_id: %(edge_id)s"), {
|
||||
'rule_id': id,
|
||||
'edge_id': edge_id})
|
||||
raise e
|
||||
return self._restore_firewall_rule(context, edge_id, response)
|
||||
|
||||
def get_firewall(self, context, edge_id):
|
||||
response = self._get_firewall(context, edge_id)
|
||||
return self._restore_firewall(context, edge_id, response)
|
||||
|
||||
def update_firewall(self, context, edge_id, firewall):
|
||||
fw_req = self._convert_firewall(context, firewall)
|
||||
try:
|
||||
self.vcns.update_firewall(edge_id, fw_req)
|
||||
except vcns_exc.VcnsApiException as e:
|
||||
LOG.exception(_("Failed to update firewall "
|
||||
"with edge_id: %s"), edge_id)
|
||||
raise e
|
||||
fw_res = self._get_firewall(context, edge_id)
|
||||
vcns_db.cleanup_vcns_edge_firewallrule_binding(
|
||||
context.session, edge_id)
|
||||
self._create_rule_id_mapping(context, edge_id, firewall, fw_res)
|
||||
|
||||
def delete_firewall(self, context, edge_id):
|
||||
try:
|
||||
self.vcns.delete_firewall(edge_id)
|
||||
except vcns_exc.VcnsApiException as e:
|
||||
LOG.exception(_("Failed to delete firewall "
|
||||
"with edge_id:%s"), edge_id)
|
||||
raise e
|
||||
vcns_db.cleanup_vcns_edge_firewallrule_binding(
|
||||
context.session, edge_id)
|
||||
|
||||
def update_firewall_rule(self, context, id, edge_id, firewall_rule):
|
||||
rule_map = vcns_db.get_vcns_edge_firewallrule_binding(
|
||||
context.session, id, edge_id)
|
||||
vcns_rule_id = rule_map.rule_vseid
|
||||
fwr_req = self._convert_firewall_rule(context, firewall_rule)
|
||||
try:
|
||||
self.vcns.update_firewall_rule(edge_id, vcns_rule_id, fwr_req)
|
||||
except vcns_exc.VcnsApiException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_("Failed to update firewall rule: %(rule_id)s "
|
||||
"with edge_id: %(edge_id)s"),
|
||||
{'rule_id': id,
|
||||
'edge_id': edge_id})
|
||||
|
||||
def delete_firewall_rule(self, context, id, edge_id):
|
||||
rule_map = vcns_db.get_vcns_edge_firewallrule_binding(
|
||||
context.session, id, edge_id)
|
||||
vcns_rule_id = rule_map.rule_vseid
|
||||
try:
|
||||
self.vcns.delete_firewall_rule(edge_id, vcns_rule_id)
|
||||
except vcns_exc.VcnsApiException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_("Failed to delete firewall rule: %(rule_id)s "
|
||||
"with edge_id: %(edge_id)s"),
|
||||
{'rule_id': id,
|
||||
'edge_id': edge_id})
|
||||
vcns_db.delete_vcns_edge_firewallrule_binding(
|
||||
context.session, id)
|
||||
|
||||
def _add_rule_above(self, context, ref_rule_id, edge_id, firewall_rule):
|
||||
rule_map = vcns_db.get_vcns_edge_firewallrule_binding(
|
||||
context.session, ref_rule_id, edge_id)
|
||||
ref_vcns_rule_id = rule_map.rule_vseid
|
||||
fwr_req = self._convert_firewall_rule(context, firewall_rule)
|
||||
try:
|
||||
header = self.vcns.add_firewall_rule_above(
|
||||
edge_id, ref_vcns_rule_id, fwr_req)[0]
|
||||
except vcns_exc.VcnsApiException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_("Failed to add firewall rule above: "
|
||||
"%(rule_id)s with edge_id: %(edge_id)s"),
|
||||
{'rule_id': ref_vcns_rule_id,
|
||||
'edge_id': edge_id})
|
||||
|
||||
objuri = header['location']
|
||||
fwr_vseid = objuri[objuri.rfind("/") + 1:]
|
||||
map_info = {
|
||||
'rule_id': firewall_rule['id'],
|
||||
'rule_vseid': fwr_vseid,
|
||||
'edge_id': edge_id}
|
||||
vcns_db.add_vcns_edge_firewallrule_binding(
|
||||
context.session, map_info)
|
||||
|
||||
def _add_rule_below(self, context, ref_rule_id, edge_id, firewall_rule):
|
||||
rule_map = vcns_db.get_vcns_edge_firewallrule_binding(
|
||||
context.session, ref_rule_id, edge_id)
|
||||
ref_vcns_rule_id = rule_map.rule_vseid
|
||||
fwr_vse_next = self._get_firewall_rule_next(
|
||||
context, edge_id, ref_vcns_rule_id)
|
||||
fwr_req = self._convert_firewall_rule(context, firewall_rule)
|
||||
if fwr_vse_next:
|
||||
ref_vcns_rule_id = fwr_vse_next['ruleId']
|
||||
try:
|
||||
header = self.vcns.add_firewall_rule_above(
|
||||
edge_id, int(ref_vcns_rule_id), fwr_req)[0]
|
||||
except vcns_exc.VcnsApiException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_("Failed to add firewall rule above: "
|
||||
"%(rule_id)s with edge_id: %(edge_id)s"),
|
||||
{'rule_id': ref_vcns_rule_id,
|
||||
'edge_id': edge_id})
|
||||
else:
|
||||
# append the rule at the bottom
|
||||
try:
|
||||
header = self.vcns.add_firewall_rule(
|
||||
edge_id, fwr_req)[0]
|
||||
except vcns_exc.VcnsApiException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_("Failed to append a firewall rule"
|
||||
"with edge_id: %s"), edge_id)
|
||||
|
||||
objuri = header['location']
|
||||
fwr_vseid = objuri[objuri.rfind("/") + 1:]
|
||||
map_info = {
|
||||
'rule_id': firewall_rule['id'],
|
||||
'rule_vseid': fwr_vseid,
|
||||
'edge_id': edge_id
|
||||
}
|
||||
vcns_db.add_vcns_edge_firewallrule_binding(
|
||||
context.session, map_info)
|
||||
|
||||
def insert_rule(self, context, rule_info, edge_id, fwr):
|
||||
if rule_info.get('insert_before'):
|
||||
self._add_rule_above(
|
||||
context, rule_info['insert_before'], edge_id, fwr)
|
||||
elif rule_info.get('insert_after'):
|
||||
self._add_rule_below(
|
||||
context, rule_info['insert_after'], edge_id, fwr)
|
||||
else:
|
||||
msg = _("Can't execute insert rule operation "
|
||||
"without reference rule_id")
|
||||
raise vcns_exc.BadRequest(resource='firewall_rule', msg=msg)
|
@ -28,6 +28,10 @@ HTTP_DELETE = "DELETE"
|
||||
HTTP_PUT = "PUT"
|
||||
URI_PREFIX = "/api/4.0/edges"
|
||||
|
||||
#FwaaS constants
|
||||
FIREWALL_SERVICE = "firewall/config"
|
||||
FIREWALL_RULE_RESOURCE = "rules"
|
||||
|
||||
|
||||
class Vcns(object):
|
||||
|
||||
@ -106,3 +110,69 @@ class Vcns(object):
|
||||
def delete_lswitch(self, lswitch_id):
|
||||
uri = "/api/ws.v1/lswitch/%s" % lswitch_id
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def update_firewall(self, edge_id, fw_req):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE)
|
||||
return self.do_request(HTTP_PUT, uri, fw_req)
|
||||
|
||||
def delete_firewall(self, edge_id):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE, None)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def update_firewall_rule(self, edge_id, vcns_rule_id, fwr_req):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE,
|
||||
vcns_rule_id)
|
||||
return self.do_request(HTTP_PUT, uri, fwr_req)
|
||||
|
||||
def delete_firewall_rule(self, edge_id, vcns_rule_id):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE,
|
||||
vcns_rule_id)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def add_firewall_rule_above(self, edge_id, ref_vcns_rule_id, fwr_req):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE)
|
||||
uri += "?aboveRuleId=" + ref_vcns_rule_id
|
||||
return self.do_request(HTTP_POST, uri, fwr_req)
|
||||
|
||||
def add_firewall_rule(self, edge_id, fwr_req):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE)
|
||||
return self.do_request(HTTP_POST, uri, fwr_req)
|
||||
|
||||
def get_firewall(self, edge_id):
|
||||
uri = self._build_uri_path(edge_id, FIREWALL_SERVICE)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def get_firewall_rule(self, edge_id, vcns_rule_id):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE,
|
||||
vcns_rule_id)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def _build_uri_path(self, edge_id,
|
||||
service,
|
||||
resource=None,
|
||||
resource_id=None,
|
||||
parent_resource_id=None,
|
||||
fields=None,
|
||||
relations=None,
|
||||
filters=None,
|
||||
types=None,
|
||||
is_attachment=False):
|
||||
uri_prefix = "%s/%s/%s" % (URI_PREFIX, edge_id, service)
|
||||
if resource:
|
||||
res_path = resource + (resource_id and "/%s" % resource_id or '')
|
||||
uri_path = "%s/%s" % (uri_prefix, res_path)
|
||||
else:
|
||||
uri_path = uri_prefix
|
||||
return uri_path
|
||||
|
@ -20,11 +20,13 @@ from oslo.config import cfg
|
||||
|
||||
from neutron.plugins.nicira.common import config # noqa
|
||||
from neutron.plugins.nicira.vshield import edge_appliance_driver
|
||||
from neutron.plugins.nicira.vshield import edge_firewall_driver
|
||||
from neutron.plugins.nicira.vshield.tasks import tasks
|
||||
from neutron.plugins.nicira.vshield import vcns
|
||||
|
||||
|
||||
class VcnsDriver(edge_appliance_driver.EdgeApplianceDriver):
|
||||
class VcnsDriver(edge_appliance_driver.EdgeApplianceDriver,
|
||||
edge_firewall_driver.EdgeFirewallDriver):
|
||||
def __init__(self, callbacks):
|
||||
super(VcnsDriver, self).__init__()
|
||||
|
||||
@ -40,5 +42,4 @@ class VcnsDriver(edge_appliance_driver.EdgeApplianceDriver):
|
||||
interval = cfg.CONF.vcns.task_status_check_interval
|
||||
self.task_manager = tasks.TaskManager(interval)
|
||||
self.task_manager.start()
|
||||
|
||||
self.vcns = vcns.Vcns(self.vcns_uri, self.vcns_user, self.vcns_passwd)
|
||||
|
@ -58,23 +58,25 @@ class FirewallPluginDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
||||
for k in firewall.RESOURCE_ATTRIBUTE_MAP.keys()
|
||||
)
|
||||
|
||||
def setUp(self, core_plugin=None, fw_plugin=None):
|
||||
def setUp(self, core_plugin=None, fw_plugin=None, ext_mgr=None):
|
||||
if not fw_plugin:
|
||||
fw_plugin = DB_FW_PLUGIN_KLASS
|
||||
service_plugins = {'fw_plugin_name': fw_plugin}
|
||||
|
||||
fdb.Firewall_db_mixin.supported_extension_aliases = ["fwaas"]
|
||||
super(FirewallPluginDbTestCase, self).setUp(
|
||||
ext_mgr=ext_mgr,
|
||||
service_plugins=service_plugins
|
||||
)
|
||||
|
||||
self.plugin = importutils.import_object(fw_plugin)
|
||||
ext_mgr = api_ext.PluginAwareExtensionManager(
|
||||
extensions_path,
|
||||
{constants.FIREWALL: self.plugin}
|
||||
)
|
||||
app = config.load_paste_app('extensions_test_app')
|
||||
self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr)
|
||||
if not ext_mgr:
|
||||
self.plugin = importutils.import_object(fw_plugin)
|
||||
ext_mgr = api_ext.PluginAwareExtensionManager(
|
||||
extensions_path,
|
||||
{constants.FIREWALL: self.plugin}
|
||||
)
|
||||
app = config.load_paste_app('extensions_test_app')
|
||||
self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr)
|
||||
|
||||
def _test_list_resources(self, resource, items,
|
||||
neutron_context=None,
|
||||
|
@ -22,13 +22,18 @@ from neutron.plugins.nicira import extensions
|
||||
import neutron.plugins.nicira.NeutronPlugin as plugin
|
||||
import neutron.plugins.nicira.NeutronServicePlugin as service_plugin
|
||||
import neutron.plugins.nicira.NvpApiClient as nvpapi
|
||||
from neutron.plugins.nicira.vshield.common import (
|
||||
VcnsApiClient as vcnsapi)
|
||||
from neutron.plugins.nicira.vshield import vcns
|
||||
import neutron.plugins.nicira.vshield.vcns_driver as vcnsdriver
|
||||
|
||||
nvp_plugin = plugin.NvpPluginV2
|
||||
nvp_service_plugin = service_plugin.NvpAdvancedPlugin
|
||||
api_helper = nvpapi.NVPApiHelper
|
||||
nvp_client = client.NvpApiClientEventlet
|
||||
vcns_class = vcns.Vcns
|
||||
vcns_driver = vcnsdriver.VcnsDriver
|
||||
vcns_api_helper = vcnsapi.VcnsApiHelper
|
||||
|
||||
STUBS_PATH = os.path.join(os.path.dirname(__file__), 'etc')
|
||||
NVPEXT_PATH = os.path.dirname(extensions.__file__)
|
||||
@ -38,6 +43,9 @@ SERVICE_PLUGIN_NAME = '%s.%s' % (nvp_service_plugin.__module__,
|
||||
nvp_service_plugin.__name__)
|
||||
CLIENT_NAME = '%s.%s' % (nvp_client.__module__, nvp_client.__name__)
|
||||
VCNS_NAME = '%s.%s' % (vcns_class.__module__, vcns_class.__name__)
|
||||
VCNS_DRIVER_NAME = '%s.%s' % (vcns_driver.__module__, vcns_driver.__name__)
|
||||
VCNSAPI_NAME = '%s.%s' % (vcns_api_helper.__module__,
|
||||
vcns_api_helper.__name__)
|
||||
|
||||
|
||||
def get_fake_conf(filename):
|
||||
|
@ -61,15 +61,17 @@ class ServiceRouterTestExtensionManager(object):
|
||||
|
||||
class NvpRouterTestCase(test_nicira_plugin.TestNiciraL3NatTestCase):
|
||||
|
||||
def setUp(self, plugin=None, ext_mgr=None):
|
||||
def setUp(self, plugin=None, ext_mgr=None, service_plugins=None):
|
||||
plugin = plugin or SERVICE_PLUGIN_NAME
|
||||
super(NvpRouterTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr)
|
||||
super(NvpRouterTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr,
|
||||
service_plugins=service_plugins)
|
||||
|
||||
|
||||
class ServiceRouterTestCase(NvpRouterTestCase):
|
||||
class ServiceRouterTest(test_nicira_plugin.NiciraL3NatTest):
|
||||
|
||||
def vcns_patch(self):
|
||||
instance = self.mock_vcns.start()
|
||||
self.vcns_instance = instance
|
||||
instance.return_value.deploy_edge.side_effect = self.fc2.deploy_edge
|
||||
instance.return_value.get_edge_id.side_effect = self.fc2.get_edge_id
|
||||
instance.return_value.get_edge_deploy_status.side_effect = (
|
||||
@ -93,7 +95,7 @@ class ServiceRouterTestCase(NvpRouterTestCase):
|
||||
instance.return_value.delete_lswitch.side_effect = (
|
||||
self.fc2.delete_lswitch)
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self, ext_mgr=None, service_plugins=None):
|
||||
cfg.CONF.set_override('api_extensions_path', NVPEXT_PATH)
|
||||
cfg.CONF.set_override('task_status_check_interval', 100, group="vcns")
|
||||
|
||||
@ -103,8 +105,11 @@ class ServiceRouterTestCase(NvpRouterTestCase):
|
||||
self.mock_vcns = mock.patch(VCNS_NAME, autospec=True)
|
||||
self.vcns_patch()
|
||||
|
||||
super(ServiceRouterTestCase, self).setUp(
|
||||
ext_mgr=ServiceRouterTestExtensionManager())
|
||||
ext_mgr = ext_mgr or ServiceRouterTestExtensionManager()
|
||||
super(ServiceRouterTest, self).setUp(
|
||||
plugin=SERVICE_PLUGIN_NAME,
|
||||
service_plugins=service_plugins,
|
||||
ext_mgr=ext_mgr)
|
||||
|
||||
self.fc2.set_fake_nvpapi(self.fc)
|
||||
self.addCleanup(self.fc2.reset_all)
|
||||
@ -122,7 +127,7 @@ class ServiceRouterTestCase(NvpRouterTestCase):
|
||||
raise Exception(_("Tasks not completed"))
|
||||
manager.stop()
|
||||
|
||||
super(ServiceRouterTestCase, self).tearDown()
|
||||
super(ServiceRouterTest, self).tearDown()
|
||||
|
||||
def _create_router(self, fmt, tenant_id, name=None,
|
||||
admin_state_up=None, set_context=False,
|
||||
@ -145,6 +150,9 @@ class ServiceRouterTestCase(NvpRouterTestCase):
|
||||
|
||||
return router_req.get_response(self.ext_api)
|
||||
|
||||
|
||||
class ServiceRouterTestCase(ServiceRouterTest, NvpRouterTestCase):
|
||||
|
||||
def test_router_create(self):
|
||||
name = 'router1'
|
||||
tenant_id = _uuid()
|
||||
|
564
neutron/tests/unit/nicira/test_fwaas_plugin.py
Normal file
564
neutron/tests/unit/nicira/test_fwaas_plugin.py
Normal file
@ -0,0 +1,564 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 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 contextlib
|
||||
import copy
|
||||
import webob.exc
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron import context
|
||||
from neutron.extensions import firewall
|
||||
from neutron import manager
|
||||
from neutron.openstack.common import uuidutils
|
||||
from neutron.plugins.common import constants as const
|
||||
from neutron.tests.unit.db.firewall import test_db_firewall
|
||||
from neutron.tests.unit.nicira import test_edge_router
|
||||
|
||||
_uuid = uuidutils.generate_uuid
|
||||
|
||||
FW_PLUGIN_CLASS = (
|
||||
"neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin"
|
||||
)
|
||||
|
||||
|
||||
class FirewallTestExtensionManager(
|
||||
test_edge_router.ServiceRouterTestExtensionManager):
|
||||
|
||||
def get_resources(self):
|
||||
# If l3 resources have been loaded and updated by main API
|
||||
# router, update the map in the l3 extension so it will load
|
||||
# the same attributes as the API router
|
||||
resources = super(FirewallTestExtensionManager, self).get_resources()
|
||||
firewall_attr_map = copy.deepcopy(firewall.RESOURCE_ATTRIBUTE_MAP)
|
||||
for res in firewall.RESOURCE_ATTRIBUTE_MAP.keys():
|
||||
attr_info = attributes.RESOURCE_ATTRIBUTE_MAP.get(res)
|
||||
if attr_info:
|
||||
firewall.RESOURCE_ATTRIBUTE_MAP[res] = attr_info
|
||||
fw_resources = firewall.Firewall.get_resources()
|
||||
# restore the original resources once the controllers are created
|
||||
firewall.RESOURCE_ATTRIBUTE_MAP = firewall_attr_map
|
||||
|
||||
resources.extend(fw_resources)
|
||||
|
||||
return resources
|
||||
|
||||
def get_actions(self):
|
||||
return []
|
||||
|
||||
def get_request_extensions(self):
|
||||
return []
|
||||
|
||||
|
||||
class FirewallPluginTestCase(test_db_firewall.FirewallPluginDbTestCase,
|
||||
test_edge_router.ServiceRouterTest):
|
||||
|
||||
def vcns_firewall_patch(self):
|
||||
self.vcns_instance.return_value.update_firewall.side_effect = (
|
||||
self.fc2.update_firewall)
|
||||
self.vcns_instance.return_value.delete_firewall.side_effect = (
|
||||
self.fc2.delete_firewall)
|
||||
self.vcns_instance.return_value.update_firewall_rule.side_effect = (
|
||||
self.fc2.update_firewall_rule)
|
||||
self.vcns_instance.return_value.delete_firewall_rule.side_effect = (
|
||||
self.fc2.delete_firewall_rule)
|
||||
self.vcns_instance.return_value.add_firewall_rule_above.side_effect = (
|
||||
self.fc2.add_firewall_rule_above)
|
||||
self.vcns_instance.return_value.add_firewall_rule.side_effect = (
|
||||
self.fc2.add_firewall_rule)
|
||||
self.vcns_instance.return_value.get_firewall.side_effect = (
|
||||
self.fc2.get_firewall)
|
||||
self.vcns_instance.return_value.get_firewall_rule.side_effect = (
|
||||
self.fc2.get_firewall_rule)
|
||||
|
||||
def setUp(self):
|
||||
# Save the global RESOURCE_ATTRIBUTE_MAP
|
||||
self.saved_attr_map = {}
|
||||
for resource, attrs in attributes.RESOURCE_ATTRIBUTE_MAP.iteritems():
|
||||
self.saved_attr_map[resource] = attrs.copy()
|
||||
|
||||
super(FirewallPluginTestCase, self).setUp(
|
||||
ext_mgr=FirewallTestExtensionManager(),
|
||||
fw_plugin=FW_PLUGIN_CLASS)
|
||||
self.vcns_firewall_patch()
|
||||
self.plugin = manager.NeutronManager.get_plugin()
|
||||
self.router_id = None
|
||||
|
||||
def tearDown(self):
|
||||
super(FirewallPluginTestCase, self).tearDown()
|
||||
# Restore the global RESOURCE_ATTRIBUTE_MAP
|
||||