neutron-vpnaas/neutron/services/firewall/fwaas_plugin.py
snaiksat a5a88c7ed3 Firewall as a Service (FWaaS) APIs and DB Model
Implements: blueprint quantum-fwaas

blueprint: quantum-fwaas-plugin

This is the first iteration of the FWaaS implementation and
is geared towards implementing the model that will be
required to at least address the reference implementation.

This iteration will not include implementation of the following
features:
* grouping or dynamic objects
* application/service objects

Change-Id: I57a62d6e9d3f1e6c4dd44cd5c745710a3d9e488e
2013-07-29 22:06:50 -07:00

286 lines
12 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 Big Switch Networks, 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.
#
# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc.
from oslo.config import cfg
from neutron.common import rpc as q_rpc
from neutron.common import topics
from neutron.db import api as qdbapi
from neutron.db.firewall import firewall_db
from neutron.extensions import firewall as fw_ext
from neutron.openstack.common import log as logging
from neutron.openstack.common import rpc
from neutron.openstack.common.rpc import proxy
from neutron.plugins.common import constants as const
LOG = logging.getLogger(__name__)
class FirewallCallbacks(object):
RPC_API_VERSION = '1.0'
def __init__(self, plugin):
self.plugin = plugin
def create_rpc_dispatcher(self):
return q_rpc.PluginRpcDispatcher([self])
def set_firewall_status(self, context, firewall_id, status, **kwargs):
"""Agent uses this to set a firewall's status."""
LOG.debug(_("set_firewall_status() called"))
with context.session.begin(subtransactions=True):
fw_db = self.plugin._get_firewall(context, firewall_id)
if status in (const.ACTIVE, const.INACTIVE):
fw_db.status = status
return True
else:
fw_db.status = const.ERROR
return False
def firewall_deleted(self, context, firewall_id, **kwargs):
"""Agent uses this to indicate firewall is deleted."""
LOG.debug(_("firewall_deleted() called"))
with context.session.begin(subtransactions=True):
fw_db = self.plugin._get_firewall(context, firewall_id)
if fw_db.status == const.PENDING_DELETE:
self.plugin.delete_db_firewall_object(context, firewall_id)
return True
else:
fw_db.status = const.ERROR
LOG.warn(_('Firewall %s unexpectedly deleted by agent.'),
firewall_id)
return False
def get_firewalls_for_tenant(self, context, **kwargs):
"""Agent uses this to get all firewalls and rules for a tenant."""
LOG.debug(_("get_firewalls_for_tenant() called"))
fw_list = [
self.plugin._make_firewall_dict_with_rules(context, fw['id'])
for fw in self.plugin.get_firewalls(context)
]
return fw_list
def get_firewalls_for_tenant_without_rules(self, context, **kwargs):
"""Agent uses this to get all firewalls for a tenant."""
LOG.debug(_("get_firewalls_for_tenant_without_rules() called"))
fw_list = [fw for fw in self.plugin.get_firewalls(context)]
return fw_list
class FirewallAgentApi(proxy.RpcProxy):
"""Plugin side of plugin to agent RPC API."""
API_VERSION = '1.0'
def __init__(self, topic, host):
super(FirewallAgentApi, self).__init__(topic, self.API_VERSION)
self.host = host
def create_firewall(self, context, firewall):
return self.fanout_cast(
context,
self.make_msg('create_firewall', firewall=firewall,
host=self.host),
topic=self.topic
)
def update_firewall(self, context, firewall):
return self.fanout_cast(
context,
self.make_msg('update_firewall', firewall=firewall,
host=self.host),
topic=self.topic
)
def delete_firewall(self, context, firewall):
return self.fanout_cast(
context,
self.make_msg('delete_firewall', firewall=firewall,
host=self.host),
topic=self.topic
)
class FirewallPlugin(firewall_db.Firewall_db_mixin):
"""Implementation of the Neutron Firewall Service Plugin.
This class manages the workflow of FWaaS request/response.
Most DB related works are implemented in class
firewall_db.Firewall_db_mixin.
"""
supported_extension_aliases = ["fwaas"]
def __init__(self):
"""Do the initialization for the firewall service plugin here."""
qdbapi.register_models()
self.callbacks = FirewallCallbacks(self)
self.conn = rpc.create_connection(new=True)
self.conn.create_consumer(
topics.FIREWALL_PLUGIN,
self.callbacks.create_rpc_dispatcher(),
fanout=False)
self.conn.consume_in_thread()
self.agent_rpc = FirewallAgentApi(
topics.L3_AGENT,
cfg.CONF.host
)
def _make_firewall_dict_with_rules(self, context, firewall_id):
firewall = self.get_firewall(context, firewall_id)
fw_policy_id = firewall['firewall_policy_id']
if fw_policy_id:
fw_policy = self.get_firewall_policy(context, fw_policy_id)
fw_rules_list = [self.get_firewall_rule(
context, rule_id) for rule_id in fw_policy['firewall_rules']]
firewall['firewall_rule_list'] = fw_rules_list
else:
firewall['firewall_rule_list'] = []
# FIXME(Sumit): If the size of the firewall object we are creating
# here exceeds the largest message size supported by rabbit/qpid
# then we will have a problem.
return firewall
def _rpc_update_firewall(self, context, firewall_id):
status_update = {"firewall": {"status": const.PENDING_UPDATE}}
fw = super(FirewallPlugin, self).update_firewall(context, firewall_id,
status_update)
if fw:
fw_with_rules = (
self._make_firewall_dict_with_rules(context,
firewall_id))
self.agent_rpc.update_firewall(context, fw_with_rules)
def _rpc_update_firewall_policy(self, context, firewall_policy_id):
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
if firewall_policy:
for firewall_id in firewall_policy['firewall_list']:
self._rpc_update_firewall(context, firewall_id)
def _ensure_update_firewall(self, context, firewall_id):
fwall = self.get_firewall(context, firewall_id)
if fwall['status'] in [const.PENDING_CREATE,
const.PENDING_UPDATE,
const.PENDING_DELETE]:
raise fw_ext.FirewallInPendingState(firewall_id=firewall_id,
pending_state=fwall['status'])
def _ensure_update_firewall_policy(self, context, firewall_policy_id):
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
if firewall_policy and 'firewall_list' in firewall_policy:
for firewall_id in firewall_policy['firewall_list']:
self._ensure_update_firewall(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 'firewall_policy_id' in fw_rule and fw_rule['firewall_policy_id']:
self._ensure_update_firewall_policy(context,
fw_rule['firewall_policy_id'])
def create_firewall(self, context, firewall):
LOG.debug(_("create_firewall() called"))
firewall['firewall']['status'] = const.PENDING_CREATE
fw = super(FirewallPlugin, self).create_firewall(context, firewall)
fw_with_rules = (
self._make_firewall_dict_with_rules(context, fw['id']))
self.agent_rpc.create_firewall(context, fw_with_rules)
return fw
def update_firewall(self, context, id, firewall):
LOG.debug(_("update_firewall() called"))
self._ensure_update_firewall(context, id)
firewall['firewall']['status'] = const.PENDING_UPDATE
fw = super(FirewallPlugin, self).update_firewall(context, id, firewall)
fw_with_rules = (
self._make_firewall_dict_with_rules(context, fw['id']))
self.agent_rpc.update_firewall(context, fw_with_rules)
return fw
def delete_db_firewall_object(self, context, id):
firewall = self.get_firewall(context, id)
if firewall['status'] in [const.PENDING_DELETE]:
super(FirewallPlugin, self).delete_firewall(context, id)
def delete_firewall(self, context, id):
LOG.debug(_("delete_firewall() called"))
status_update = {"firewall": {"status": const.PENDING_DELETE}}
fw = super(FirewallPlugin, self).update_firewall(context, id,
status_update)
fw_with_rules = (
self._make_firewall_dict_with_rules(context, fw['id']))
self.agent_rpc.delete_firewall(context, fw_with_rules)
def update_firewall_policy(self, context, id, firewall_policy):
LOG.debug(_("update_firewall_policy() called"))
self._ensure_update_firewall_policy(context, id)
fwp = super(FirewallPlugin,
self).update_firewall_policy(context, id, firewall_policy)
self._rpc_update_firewall_policy(context, id)
return fwp
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 = super(FirewallPlugin,
self).update_firewall_rule(context, id, firewall_rule)
firewall_policy_id = fwr['firewall_policy_id']
if firewall_policy_id:
self._rpc_update_firewall_policy(context, firewall_policy_id)
return fwr
def delete_firewall_rule(self, context, id):
LOG.debug(_("delete_firewall_rule() called"))
self._ensure_update_or_delete_firewall_rule(context, id)
fwr = self.get_firewall_rule(context, id)
firewall_policy_id = fwr['firewall_policy_id']
super(FirewallPlugin, self).delete_firewall_rule(context, id)
# At this point we have already deleted the rule in the DB,
# however it's still not deleted on the backend firewall.
# Until it gets deleted on the backend we will be setting
# the firewall in PENDING_UPDATE state. The backend firewall
# implementation is responsible for setting the appropriate
# configuration (e.g. do not allow any traffic) until the rule
# is deleted. Once the rule is deleted, the backend should put
# the firewall back in ACTIVE state. While the firewall is in
# PENDING_UPDATE state, the firewall behavior might differ based
# on the backend implementation.
if firewall_policy_id:
self._rpc_update_firewall_policy(context, firewall_policy_id)
def insert_rule(self, context, id, rule_info):
LOG.debug(_("insert_rule() called"))
self._ensure_update_firewall_policy(context, id)
fwp = super(FirewallPlugin,
self).insert_rule(context, id, rule_info)
self._rpc_update_firewall_policy(context, id)
return fwp
def remove_rule(self, context, id, rule_info):
LOG.debug(_("remove_rule() called"))
self._ensure_update_firewall_policy(context, id)
fwp = super(FirewallPlugin,
self).remove_rule(context, id, rule_info)
self._rpc_update_firewall_policy(context, id)
return fwp