MIN_VLAN_TAG = 1
MAX_VLAN_TAG = 4094
-MAX_VXLAN_VNI = 16777215
+
+# For GRE Tunnel
+MIN_GRE_ID = 1
+MAX_GRE_ID = 2 ** 32 - 1
+
+# For VXLAN Tunnel
+MIN_VXLAN_VNI = 1
+MAX_VXLAN_VNI = 2 ** 24 - 1
+
FLOODING_ENTRY = ['00:00:00:00:00:00', '0.0.0.0']
EXT_NS_COMP = '_backward_comp_e_ns'
super(NetworkVlanRangeError, self).__init__(**kwargs)
+class NetworkTunnelRangeError(NeutronException):
+ message = _("Invalid network Tunnel range: "
+ "'%(tunnel_range)s' - %(error)s")
+
+ def __init__(self, **kwargs):
+ # Convert tunnel_range tuple to 'start:end' format for display
+ if isinstance(kwargs['tunnel_range'], tuple):
+ kwargs['tunnel_range'] = "%d:%d" % kwargs['tunnel_range']
+ super(NetworkTunnelRangeError, self).__init__(**kwargs)
+
+
class NetworkVxlanPortRangeError(NeutronException):
message = _("Invalid network VXLAN port range: '%(vxlan_range)s'")
return q_const.MIN_VLAN_TAG <= vlan <= q_const.MAX_VLAN_TAG
+def is_valid_gre_id(gre_id):
+ return q_const.MIN_GRE_ID <= gre_id <= q_const.MAX_GRE_ID
+
+
+def is_valid_vxlan_vni(vni):
+ return q_const.MIN_VXLAN_VNI <= vni <= q_const.MAX_VXLAN_VNI
+
+
def get_random_mac(base_mac):
mac = [int(base_mac[0], 16), int(base_mac[1], 16),
int(base_mac[2], 16), random.randint(0x00, 0xff),
from neutron.common import exceptions as n_exc
from neutron.common import utils
-from neutron.plugins.common import constants
+from neutron.plugins.common import constants as p_const
+
+
+def verify_tunnel_range(tunnel_range, tunnel_type):
+ """Raise an exception for invalid tunnel range or malformed range."""
+ mappings = {p_const.TYPE_GRE: utils.is_valid_gre_id,
+ p_const.TYPE_VXLAN: utils.is_valid_vxlan_vni}
+ if tunnel_type in mappings:
+ for ident in tunnel_range:
+ if not mappings[tunnel_type](ident):
+ raise n_exc.NetworkTunnelRangeError(
+ tunnel_range=tunnel_range,
+ error=_("%(id)s is not a valid %(type)s identifier") %
+ {'id': ident, 'type': tunnel_type})
+ if tunnel_range[1] < tunnel_range[0]:
+ raise n_exc.NetworkTunnelRangeError(
+ tunnel_range=tunnel_range,
+ error=_("End of tunnel range is less "
+ "than start of tunnel range"))
def verify_vlan_range(vlan_range):
def in_pending_status(status):
- return status in (constants.PENDING_CREATE,
- constants.PENDING_UPDATE,
- constants.PENDING_DELETE)
+ return status in (p_const.PENDING_CREATE,
+ p_const.PENDING_UPDATE,
+ p_const.PENDING_DELETE)
import sqlalchemy as sa
from sqlalchemy import sql
+from neutron.common import exceptions as exc
from neutron.db import api as db_api
from neutron.db import model_base
from neutron.openstack.common.gettextutils import _LE
return p_const.TYPE_GRE
def initialize(self):
- self._initialize(cfg.CONF.ml2_type_gre.tunnel_id_ranges)
+ try:
+ self._initialize(cfg.CONF.ml2_type_gre.tunnel_id_ranges)
+ except exc.NetworkTunnelRangeError:
+ LOG.exception(_("Failed to parse tunnel_id_ranges. "
+ "Service terminated!"))
+ raise SystemExit()
def sync_allocations(self):
from neutron.common import exceptions as exc
from neutron.common import topics
+from neutron.openstack.common.gettextutils import _LI
from neutron.openstack.common.gettextutils import _LW
from neutron.openstack.common import log
+from neutron.plugins.common import utils as plugin_utils
from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2.drivers import helpers
def _initialize(self, raw_tunnel_ranges):
self.tunnel_ranges = []
- self._parse_tunnel_ranges(raw_tunnel_ranges,
- self.tunnel_ranges,
- self.get_type())
+ self._parse_tunnel_ranges(raw_tunnel_ranges, self.tunnel_ranges)
self.sync_allocations()
- def _parse_tunnel_ranges(self, tunnel_ranges, current_range, tunnel_type):
+ def _parse_tunnel_ranges(self, tunnel_ranges, current_range):
for entry in tunnel_ranges:
entry = entry.strip()
try:
tun_min, tun_max = entry.split(':')
tun_min = tun_min.strip()
tun_max = tun_max.strip()
- current_range.append((int(tun_min), int(tun_max)))
+ tunnel_range = int(tun_min), int(tun_max)
except ValueError as ex:
- LOG.error(_("Invalid tunnel ID range: '%(range)s' - %(e)s. "
- "Agent terminated!"),
- {'range': tunnel_ranges, 'e': ex})
- LOG.info(_("%(type)s ID ranges: %(range)s"),
- {'type': tunnel_type, 'range': current_range})
+ raise exc.NetworkTunnelRangeError(tunnel_range=entry, error=ex)
+ plugin_utils.verify_tunnel_range(tunnel_range, self.get_type())
+ current_range.append(tunnel_range)
+ LOG.info(_LI("%(type)s ID ranges: %(range)s"),
+ {'type': self.get_type(), 'range': current_range})
def is_partial_segment(self, segment):
return segment.get(api.SEGMENTATION_ID) is None
import sqlalchemy as sa
from sqlalchemy import sql
+from neutron.common import exceptions as exc
from neutron.db import api as db_api
from neutron.db import model_base
from neutron.openstack.common.gettextutils import _LE
return p_const.TYPE_VXLAN
def initialize(self):
- self._initialize(cfg.CONF.ml2_type_vxlan.vni_ranges)
+ try:
+ self._initialize(cfg.CONF.ml2_type_vxlan.vni_ranges)
+ except exc.NetworkTunnelRangeError:
+ LOG.exception(_("Failed to parse vni_ranges. "
+ "Service terminated!"))
+ raise SystemExit()
def sync_allocations(self):
import mock
import testtools
+from neutron.common import constants
from neutron.common import exceptions as n_exc
from neutron.common import utils
+from neutron.plugins.common import constants as p_const
from neutron.plugins.common import utils as plugin_utils
from neutron.tests import base
self.assertEqual(self.parse(['']), {})
+class TestParseTunnelRangesMixin(object):
+ TUN_MIN = None
+ TUN_MAX = None
+ TYPE = None
+ _err_prefix = "Invalid network Tunnel range: '%d:%d' - "
+ _err_suffix = "%s is not a valid %s identifier"
+ _err_range = "End of tunnel range is less than start of tunnel range"
+
+ def _build_invalid_tunnel_range_msg(self, t_range_tuple, n):
+ bad_id = t_range_tuple[n - 1]
+ return (self._err_prefix % t_range_tuple) + (self._err_suffix
+ % (bad_id, self.TYPE))
+
+ def _build_range_reversed_msg(self, t_range_tuple):
+ return (self._err_prefix % t_range_tuple) + self._err_range
+
+ def _verify_range(self, tunnel_range):
+ return plugin_utils.verify_tunnel_range(tunnel_range, self.TYPE)
+
+ def _check_range_valid_ranges(self, tunnel_range):
+ self.assertIsNone(self._verify_range(tunnel_range))
+
+ def _check_range_invalid_ranges(self, bad_range, which):
+ expected_msg = self._build_invalid_tunnel_range_msg(bad_range, which)
+ err = self.assertRaises(n_exc.NetworkTunnelRangeError,
+ self._verify_range, bad_range)
+ self.assertEqual(expected_msg, str(err))
+
+ def _check_range_reversed(self, bad_range):
+ err = self.assertRaises(n_exc.NetworkTunnelRangeError,
+ self._verify_range, bad_range)
+ expected_msg = self._build_range_reversed_msg(bad_range)
+ self.assertEqual(expected_msg, str(err))
+
+ def test_range_tunnel_id_valid(self):
+ self._check_range_valid_ranges((self.TUN_MIN, self.TUN_MAX))
+
+ def test_range_tunnel_id_invalid(self):
+ self._check_range_invalid_ranges((-1, self.TUN_MAX), 1)
+ self._check_range_invalid_ranges((self.TUN_MIN,
+ self.TUN_MAX + 1), 2)
+ self._check_range_invalid_ranges((self.TUN_MIN - 1,
+ self.TUN_MAX + 1), 1)
+
+ def test_range_tunnel_id_reversed(self):
+ self._check_range_reversed((self.TUN_MAX, self.TUN_MIN))
+
+
+class TestGreTunnelRangeVerifyValid(TestParseTunnelRangesMixin,
+ base.BaseTestCase):
+ TUN_MIN = constants.MIN_GRE_ID
+ TUN_MAX = constants.MAX_GRE_ID
+ TYPE = p_const.TYPE_GRE
+
+
+class TestVxlanTunnelRangeVerifyValid(TestParseTunnelRangesMixin,
+ base.BaseTestCase):
+ TUN_MIN = constants.MIN_VXLAN_VNI
+ TUN_MAX = constants.MAX_VXLAN_VNI
+ TYPE = p_const.TYPE_VXLAN
+
+
class UtilTestParseVlanRanges(base.BaseTestCase):
_err_prefix = "Invalid network VLAN range: '"
_err_too_few = "' - 'need more than 2 values to unpack'"