0ed7e3a3f9
Allows 'external' keyword in addition to 'any' keyword in BigSwitch router rules support. Closes-Bug: #1226814 Change-Id: Ia92513e89b0c95c5b3b98921e126fdd21cc4f405
145 lines
4.6 KiB
Python
145 lines
4.6 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: Kevin Benton, Big Switch Networks, Inc.
|
|
|
|
from neutron.api.v2 import attributes as attr
|
|
from neutron.common import exceptions as qexception
|
|
from neutron.openstack.common import log as logging
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
# Router Rules Exceptions
|
|
class InvalidRouterRules(qexception.InvalidInput):
|
|
message = _("Invalid format for router rules: %(rule)s, %(reason)s")
|
|
|
|
|
|
class RulesExhausted(qexception.BadRequest):
|
|
message = _("Unable to complete rules update for %(router_id)s. "
|
|
"The number of rules exceeds the maximum %(quota)s.")
|
|
|
|
|
|
def convert_to_valid_router_rules(data):
|
|
"""
|
|
Validates and converts router rules to the appropriate data structure
|
|
Example argument = [{'source': 'any', 'destination': 'any',
|
|
'action':'deny'},
|
|
{'source': '1.1.1.1/32', 'destination': 'external',
|
|
'action':'permit',
|
|
'nexthops': ['1.1.1.254', '1.1.1.253']}
|
|
]
|
|
"""
|
|
V4ANY = '0.0.0.0/0'
|
|
CIDRALL = ['any', 'external']
|
|
if not isinstance(data, list):
|
|
emsg = _("Invalid data format for router rule: '%s'") % data
|
|
LOG.debug(emsg)
|
|
raise qexception.InvalidInput(error_message=emsg)
|
|
_validate_uniquerules(data)
|
|
rules = []
|
|
expected_keys = ['source', 'destination', 'action']
|
|
for rule in data:
|
|
rule['nexthops'] = rule.get('nexthops', [])
|
|
if not isinstance(rule['nexthops'], list):
|
|
rule['nexthops'] = rule['nexthops'].split('+')
|
|
|
|
src = V4ANY if rule['source'] in CIDRALL else rule['source']
|
|
dst = V4ANY if rule['destination'] in CIDRALL else rule['destination']
|
|
|
|
errors = [attr._verify_dict_keys(expected_keys, rule, False),
|
|
attr._validate_subnet(dst),
|
|
attr._validate_subnet(src),
|
|
_validate_nexthops(rule['nexthops']),
|
|
_validate_action(rule['action'])]
|
|
errors = [m for m in errors if m]
|
|
if errors:
|
|
LOG.debug(errors)
|
|
raise qexception.InvalidInput(error_message=errors)
|
|
rules.append(rule)
|
|
return rules
|
|
|
|
|
|
def _validate_nexthops(nexthops):
|
|
seen = []
|
|
for ip in nexthops:
|
|
msg = attr._validate_ip_address(ip)
|
|
if ip in seen:
|
|
msg = _("Duplicate nexthop in rule '%s'") % ip
|
|
seen.append(ip)
|
|
if msg:
|
|
return msg
|
|
|
|
|
|
def _validate_action(action):
|
|
if action not in ['permit', 'deny']:
|
|
return _("Action must be either permit or deny."
|
|
" '%s' was provided") % action
|
|
|
|
|
|
def _validate_uniquerules(rules):
|
|
pairs = []
|
|
for r in rules:
|
|
if 'source' not in r or 'destination' not in r:
|
|
continue
|
|
pairs.append((r['source'], r['destination']))
|
|
|
|
if len(set(pairs)) != len(pairs):
|
|
error = _("Duplicate router rules (src,dst) found '%s'") % pairs
|
|
LOG.debug(error)
|
|
raise qexception.InvalidInput(error_message=error)
|
|
|
|
|
|
class Routerrule(object):
|
|
|
|
@classmethod
|
|
def get_name(cls):
|
|
return "Neutron Router Rule"
|
|
|
|
@classmethod
|
|
def get_alias(cls):
|
|
return "router_rules"
|
|
|
|
@classmethod
|
|
def get_description(cls):
|
|
return "Router rule configuration for L3 router"
|
|
|
|
@classmethod
|
|
def get_namespace(cls):
|
|
return "http://docs.openstack.org/ext/neutron/routerrules/api/v1.0"
|
|
|
|
@classmethod
|
|
def get_updated(cls):
|
|
return "2013-05-23T10:00:00-00:00"
|
|
|
|
def get_extended_resources(self, version):
|
|
if version == "2.0":
|
|
return EXTENDED_ATTRIBUTES_2_0
|
|
else:
|
|
return {}
|
|
|
|
# Attribute Map
|
|
EXTENDED_ATTRIBUTES_2_0 = {
|
|
'routers': {
|
|
'router_rules': {'allow_post': False, 'allow_put': True,
|
|
'convert_to': convert_to_valid_router_rules,
|
|
'is_visible': True,
|
|
'default': attr.ATTR_NOT_SPECIFIED},
|
|
}
|
|
}
|