]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Check network vlan ranges for correctness.
authorgessau <gessau@cisco.com>
Wed, 15 May 2013 01:57:47 +0000 (21:57 -0400)
committergessau <gessau@cisco.com>
Sat, 18 May 2013 16:57:28 +0000 (12:57 -0400)
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

14 files changed:
quantum/common/constants.py
quantum/common/exceptions.py
quantum/common/utils.py
quantum/plugins/brocade/vlanbm.py
quantum/plugins/common/utils.py
quantum/plugins/hyperv/common/constants.py
quantum/plugins/linuxbridge/lb_quantum_plugin.py
quantum/plugins/mlnx/common/constants.py
quantum/plugins/mlnx/mlnx_plugin.py
quantum/plugins/nicira/QuantumPlugin.py
quantum/plugins/openvswitch/agent/ovs_quantum_agent.py
quantum/plugins/openvswitch/ovs_quantum_plugin.py
quantum/tests/unit/nec/test_db.py
quantum/tests/unit/test_common_utils.py

index 9557b97ddc6a3a59e86e884f68d5a0467360029e..06b0c53d2c3aa44abb9fb9e11d0030e3e4feabb1 100644 (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"
index 3c98f29936a38dc438645b5f3d5235279790ac32..1cc164251a5738b56e4d78171a07fb5115830f7c 100644 (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)
index 62b46463e5c2bbb3544d2a1be883c6156f5b4207..647f38ea2eb624a1cc1b621e070ebc9f35d3c2c6 100644 (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
index 8dbf41e86c2af6c93f1e76d19b02ed98684b65e2..b4431d4951e6bdf8c5cfc3f897cb063364d9aca3 100644 (file)
 
 
 """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):
index f0b692c42894577dbbb8dc56fe99f9124f53dda9..f5651b38970debe85e9bd610d15aaeb1606dc53d 100644 (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
 
index 03330fb896297a0edda1fb6d489f06c9c74e918f..dcce7ba3b25738f32e4e04aab03c9c35851d321e 100644 (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'
index 57a83c5efd2542ba42afa328a4b7add6427765f1..9d800894f427ff56ea75fa6cd1229815605a1400 100644 (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:
index eb6c1b8ef7a2471cd287f9fceb40b6cf38420676..58b22cad608534894cc01e6504a359b4906e8932 100644 (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'
index b258ad8d15ae7f0c8f832bb6b8a2456b408db79b..d3c56481f2457da539f734bc283a32072807c4fd 100644 (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):
index 8787e1d65c1e9b6584b2d0291df6a99fd779ab1a..6ff9a1e4b84dfea731b3320a070a690e3d0f43a7 100644 (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,
index eda4c0b91da015c630992cb2ce05507c620007f0..2d078cf6d9b3dd53ff76f1ed5b8bd9beb51587a9 100644 (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 = {}
index e2bb77f1f42e4abfe372ee30133a58df75cc8bec..e8d49f8d6e3e7c209861dd964d33bd8b45f574dc 100644 (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:
index 08a2f8c3fb0e54b4719b5591b93b703d6f700030..4ae4f5c59558446e6a547941b07b9c1673af2d44 100644 (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
index 2973861bfb48944ffb039ec2dd7d0dfc9e22ed3d..21d7b136e36d0989e216fa207467f2d70dc5f555 100644 (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):