Check network vlan ranges for correctness.

Check that the range beginning and end tags are valid values 1-4094.
Supply two global constants for min/max vlan tags and update all
local usage of these values to use the global constants.

Fixes: Bug #1169266
Change-Id: I054a8bebd16d95ea40414e3cecb6d24a970c730f
This commit is contained in:
gessau 2013-05-14 21:57:47 -04:00
parent 391108a014
commit b38c56f5b6
14 changed files with 171 additions and 34 deletions

View File

@ -37,6 +37,9 @@ IPv6 = 'IPv6'
UDP_PROTOCOL = 17
DHCP_RESPONSE_PORT = 68
MIN_VLAN_TAG = 1
MAX_VLAN_TAG = 4094
EXT_NS = '_extension_ns'
XML_NS_V20 = 'http://openstack.org/quantum/api/v2.0'
XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"

View File

@ -260,4 +260,10 @@ class GatewayConflictWithAllocationPools(InUse):
class NetworkVlanRangeError(QuantumException):
message = _("Invalid network VLAN range: '%(range)s' - '%(error)s'")
message = _("Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'")
def __init__(self, **kwargs):
# Convert vlan_range tuple to 'start:end' format for display
if isinstance(kwargs['vlan_range'], tuple):
kwargs['vlan_range'] = "%d:%d" % kwargs['vlan_range']
super(NetworkVlanRangeError, self).__init__(**kwargs)

View File

@ -29,6 +29,7 @@ import socket
from eventlet.green import subprocess
from oslo.config import cfg
from quantum.common import constants as q_const
from quantum.openstack.common import log as logging
@ -193,3 +194,7 @@ def is_extension_supported(plugin, ext_alias):
def log_opt_values(log):
cfg.CONF.log_opt_values(log, std_logging.DEBUG)
def is_valid_vlan_tag(vlan):
return q_const.MIN_VLAN_TAG <= vlan <= q_const.MAX_VLAN_TAG

View File

@ -21,11 +21,13 @@
"""A Vlan Bitmap class to handle allocation/de-allocation of vlan ids."""
from quantum.common import constants
from quantum.plugins.brocade.db import models as brocade_db
MIN_VLAN = 2
MAX_VLAN = 4094
MIN_VLAN = constants.MIN_VLAN_TAG + 1
MAX_VLAN = constants.MAX_VLAN_TAG
class VlanBitmap(object):

View File

@ -19,6 +19,20 @@ Common utilities and helper functions for Openstack Networking Plugins.
"""
from quantum.common import exceptions as q_exc
from quantum.common import utils
def verify_vlan_range(vlan_range):
"""Raise an exception for invalid tags or malformed range."""
for vlan_tag in vlan_range:
if not utils.is_valid_vlan_tag(vlan_tag):
raise q_exc.NetworkVlanRangeError(
vlan_range=vlan_range,
error=_("%s is not a valid VLAN tag") % vlan_tag)
if vlan_range[1] < vlan_range[0]:
raise q_exc.NetworkVlanRangeError(
vlan_range=vlan_range,
error=_("End of VLAN range is less than start of VLAN range"))
def parse_network_vlan_range(network_vlan_range):
@ -27,10 +41,11 @@ def parse_network_vlan_range(network_vlan_range):
if ':' in entry:
try:
network, vlan_min, vlan_max = entry.split(':')
vlan_min, vlan_max = int(vlan_min), int(vlan_max)
vlan_range = (int(vlan_min), int(vlan_max))
except ValueError as ex:
raise q_exc.NetworkVlanRangeError(range=entry, error=ex)
return network, (vlan_min, vlan_max)
raise q_exc.NetworkVlanRangeError(vlan_range=entry, error=ex)
verify_vlan_range(vlan_range)
return network, vlan_range
else:
return entry, None

View File

@ -21,8 +21,6 @@ TUNNEL = 'tunnel'
# Special vlan_id value in ovs_vlan_allocations table indicating flat network
FLAT_VLAN_ID = -1
VLAN_ID_MIN = 1
VLAN_ID_MAX = 4096
# Values for network_type
TYPE_LOCAL = 'local'

View File

@ -25,6 +25,7 @@ from quantum.common import constants as q_const
from quantum.common import exceptions as q_exc
from quantum.common import rpc as q_rpc
from quantum.common import topics
from quantum.common import utils
from quantum.db import agents_db
from quantum.db import agentschedulers_db
from quantum.db import api as db_api
@ -312,9 +313,11 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
if not segmentation_id_set:
msg = _("provider:segmentation_id required")
raise q_exc.InvalidInput(error_message=msg)
if segmentation_id < 1 or segmentation_id > 4094:
msg = _("provider:segmentation_id out of range "
"(1 through 4094)")
if not utils.is_valid_vlan_tag(segmentation_id):
msg = (_("provider:segmentation_id out of range "
"(%(min_id)s through %(max_id)s)") %
{'min_id': q_const.MIN_VLAN_TAG,
'max_id': q_const.MAX_VLAN_TAG})
raise q_exc.InvalidInput(error_message=msg)
elif network_type == constants.TYPE_LOCAL:
if physical_network_set:

View File

@ -17,8 +17,6 @@
LOCAL_VLAN_ID = -2
FLAT_VLAN_ID = -1
VLAN_ID_MIN = 1
VLAN_ID_MAX = 4096
# Values for network_type
TYPE_LOCAL = 'local'

View File

@ -21,8 +21,10 @@ from oslo.config import cfg
from quantum.agent import securitygroups_rpc as sg_rpc
from quantum.api.v2 import attributes
from quantum.common import constants as q_const
from quantum.common import exceptions as q_exc
from quantum.common import topics
from quantum.common import utils
from quantum.db import agents_db
from quantum.db import db_base_plugin_v2
from quantum.db import l3_db
@ -192,9 +194,11 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.QuantumDbPluginV2,
if not segmentation_id_set:
msg = _("provider:segmentation_id required")
raise q_exc.InvalidInput(error_message=msg)
if segmentation_id < 1 or segmentation_id > 4094:
msg = _("provider:segmentation_id out of range "
"(1 through 4094)")
if not utils.is_valid_vlan_tag(segmentation_id):
msg = (_("provider:segmentation_id out of range "
"(%(min_id)s through %(max_id)s)") %
{'min_id': q_const.MIN_VLAN_TAG,
'max_id': q_const.MAX_VLAN_TAG})
raise q_exc.InvalidInput(error_message=msg)
def _process_local_net(self, physical_network_set, segmentation_id_set):

View File

@ -34,6 +34,7 @@ from quantum.common import constants
from quantum.common import exceptions as q_exc
from quantum.common import rpc as q_rpc
from quantum.common import topics
from quantum.common import utils
from quantum import context as q_context
from quantum.db import agents_db
from quantum.db import agentschedulers_db
@ -697,8 +698,12 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
err_msg = _("Segmentation ID must be specified with "
"vlan network type")
elif (segmentation_id_set and
(segmentation_id < 1 or segmentation_id > 4094)):
err_msg = _("%s out of range (1 to 4094)") % segmentation_id
not utils.is_valid_vlan_tag(segmentation_id)):
err_msg = (_("%(segmentation_id)s out of range "
"(%(min_id)s through %(max_id)s)") %
{'segmentation_id': segmentation_id,
'min_id': constants.MIN_VLAN_TAG,
'max_id': constants.MAX_VLAN_TAG})
else:
# Verify segment is not already allocated
binding = nicira_db.get_network_binding_by_vlanid(
@ -708,8 +713,12 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
physical_network=physical_network)
elif network_type == NetworkTypes.L3_EXT:
if (segmentation_id_set and
(segmentation_id < 1 or segmentation_id > 4094)):
err_msg = _("%s out of range (1 to 4094)") % segmentation_id
not utils.is_valid_vlan_tag(segmentation_id)):
err_msg = (_("%(segmentation_id)s out of range "
"(%(min_id)s through %(max_id)s)") %
{'segmentation_id': segmentation_id,
'min_id': constants.MIN_VLAN_TAG,
'max_id': constants.MAX_VLAN_TAG})
else:
err_msg = _("%(net_type_param)s %(net_type_value)s not "
"supported") % {'net_type_param': pnet.NETWORK_TYPE,

View File

@ -47,7 +47,7 @@ from quantum.plugins.openvswitch.common import constants
LOG = logging.getLogger(__name__)
# A placeholder for dead vlans.
DEAD_VLAN_TAG = "4095"
DEAD_VLAN_TAG = str(q_const.MAX_VLAN_TAG + 1)
# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
@ -139,12 +139,6 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
modifying, or stripping VLAN tags as necessary.
'''
# Lower bound on available vlans.
MIN_VLAN_TAG = 1
# Upper bound on available vlans.
MAX_VLAN_TAG = 4094
# history
# 1.0 Initial version
# 1.1 Support Security Group RPC
@ -164,9 +158,8 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
:param enable_tunneling: if True enable GRE networks.
'''
self.root_helper = root_helper
self.available_local_vlans = set(
xrange(OVSQuantumAgent.MIN_VLAN_TAG,
OVSQuantumAgent.MAX_VLAN_TAG))
self.available_local_vlans = set(xrange(q_const.MIN_VLAN_TAG,
q_const.MAX_VLAN_TAG))
self.int_br = self.setup_integration_br(integ_br)
self.setup_physical_bridges(bridge_mappings)
self.local_vlan_map = {}

View File

@ -32,6 +32,7 @@ from quantum.common import constants as q_const
from quantum.common import exceptions as q_exc
from quantum.common import rpc as q_rpc
from quantum.common import topics
from quantum.common import utils
from quantum.db import agents_db
from quantum.db import agentschedulers_db
from quantum.db import db_base_plugin_v2
@ -376,9 +377,11 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
if not segmentation_id_set:
msg = _("provider:segmentation_id required")
raise q_exc.InvalidInput(error_message=msg)
if segmentation_id < 1 or segmentation_id > 4094:
msg = _("provider:segmentation_id out of range "
"(1 through 4094)")
if not utils.is_valid_vlan_tag(segmentation_id):
msg = (_("provider:segmentation_id out of range "
"(%(min_id)s through %(max_id)s)") %
{'min_id': q_const.MIN_VLAN_TAG,
'max_id': q_const.MAX_VLAN_TAG})
raise q_exc.InvalidInput(error_message=msg)
elif network_type == constants.TYPE_GRE:
if not self.enable_tunneling:

View File

@ -17,6 +17,7 @@
import random
from quantum.common import constants as q_const
from quantum.db import api as db_api
from quantum.openstack.common import uuidutils
from quantum.plugins.nec.common import exceptions as nexc
@ -47,7 +48,7 @@ class NECPluginV2DBTestBase(base.BaseTestCase):
port_id = uuidutils.generate_uuid()
datapath_id = hex(random.randint(0, 0xffffffff))
port_no = random.randint(1, 100)
vlan_id = random.randint(0, 4095)
vlan_id = random.randint(q_const.MIN_VLAN_TAG, q_const.MAX_VLAN_TAG)
mac = ':'.join(["%02x" % random.randint(0, 0xff) for x in range(6)])
none = uuidutils.generate_uuid()
return port_id, datapath_id, port_no, vlan_id, mac, none

View File

@ -68,6 +68,8 @@ class UtilTestParseVlanRanges(base.BaseTestCase):
_err_too_few = "' - 'need more than 2 values to unpack'"
_err_too_many = "' - 'too many values to unpack'"
_err_not_int = "' - 'invalid literal for int() with base 10: '%s''"
_err_bad_vlan = "' - '%s is not a valid VLAN tag'"
_err_range = "' - 'End of VLAN range is less than start of VLAN range'"
def _range_too_few_err(self, nv_range):
return self._err_prefix + nv_range + self._err_too_few
@ -78,6 +80,82 @@ class UtilTestParseVlanRanges(base.BaseTestCase):
def _vlan_not_int_err(self, nv_range, vlan):
return self._err_prefix + nv_range + (self._err_not_int % vlan)
def _nrange_invalid_vlan(self, nv_range, n):
vlan = nv_range.split(':')[n]
v_range = ':'.join(nv_range.split(':')[1:])
return self._err_prefix + v_range + (self._err_bad_vlan % vlan)
def _vrange_invalid_vlan(self, v_range_tuple, n):
vlan = v_range_tuple[n - 1]
v_range_str = '%d:%d' % v_range_tuple
return self._err_prefix + v_range_str + (self._err_bad_vlan % vlan)
def _vrange_invalid(self, v_range_tuple):
v_range_str = '%d:%d' % v_range_tuple
return self._err_prefix + v_range_str + self._err_range
class TestVlanRangeVerifyValid(UtilTestParseVlanRanges):
def verify_range(self, vlan_range):
return plugin_utils.verify_vlan_range(vlan_range)
def test_range_valid_ranges(self):
self.assertEqual(self.verify_range((1, 2)), None)
self.assertEqual(self.verify_range((1, 1999)), None)
self.assertEqual(self.verify_range((100, 100)), None)
self.assertEqual(self.verify_range((100, 200)), None)
self.assertEqual(self.verify_range((4001, 4094)), None)
self.assertEqual(self.verify_range((1, 4094)), None)
def check_one_vlan_invalid(self, bad_range, which):
expected_msg = self._vrange_invalid_vlan(bad_range, which)
err = self.assertRaises(q_exc.NetworkVlanRangeError,
self.verify_range, bad_range)
self.assertEqual(str(err), expected_msg)
def test_range_first_vlan_invalid_negative(self):
self.check_one_vlan_invalid((-1, 199), 1)
def test_range_first_vlan_invalid_zero(self):
self.check_one_vlan_invalid((0, 199), 1)
def test_range_first_vlan_invalid_limit_plus_one(self):
self.check_one_vlan_invalid((4095, 199), 1)
def test_range_first_vlan_invalid_too_big(self):
self.check_one_vlan_invalid((9999, 199), 1)
def test_range_second_vlan_invalid_negative(self):
self.check_one_vlan_invalid((299, -1), 2)
def test_range_second_vlan_invalid_zero(self):
self.check_one_vlan_invalid((299, 0), 2)
def test_range_second_vlan_invalid_limit_plus_one(self):
self.check_one_vlan_invalid((299, 4095), 2)
def test_range_second_vlan_invalid_too_big(self):
self.check_one_vlan_invalid((299, 9999), 2)
def test_range_both_vlans_invalid_01(self):
self.check_one_vlan_invalid((-1, 0), 1)
def test_range_both_vlans_invalid_02(self):
self.check_one_vlan_invalid((0, 4095), 1)
def test_range_both_vlans_invalid_03(self):
self.check_one_vlan_invalid((4095, 9999), 1)
def test_range_both_vlans_invalid_04(self):
self.check_one_vlan_invalid((9999, -1), 1)
def test_range_reversed(self):
bad_range = (95, 10)
expected_msg = self._vrange_invalid(bad_range)
err = self.assertRaises(q_exc.NetworkVlanRangeError,
self.verify_range, bad_range)
self.assertEqual(str(err), expected_msg)
class TestParseOneVlanRange(UtilTestParseVlanRanges):
def parse_one(self, cfg_entry):
@ -121,6 +199,25 @@ class TestParseOneVlanRange(UtilTestParseVlanRanges):
self.parse_one, config_str)
self.assertEqual(str(err), expected_msg)
def test_parse_one_net_and_max_range(self):
config_str = "net1:1:4094"
expected_networks = ("net1", (1, 4094))
self.assertEqual(self.parse_one(config_str), expected_networks)
def test_parse_one_net_range_bad_vlan1(self):
config_str = "net1:9000:150"
expected_msg = self._nrange_invalid_vlan(config_str, 1)
err = self.assertRaises(q_exc.NetworkVlanRangeError,
self.parse_one, config_str)
self.assertEqual(str(err), expected_msg)
def test_parse_one_net_range_bad_vlan2(self):
config_str = "net1:4000:4999"
expected_msg = self._nrange_invalid_vlan(config_str, 2)
err = self.assertRaises(q_exc.NetworkVlanRangeError,
self.parse_one, config_str)
self.assertEqual(str(err), expected_msg)
class TestParseVlanRangeList(UtilTestParseVlanRanges):
def parse_list(self, cfg_entries):