Support 4-Byte AS Numbers
Now neutron_dynamic_routing supports 2 byte AS numbers only. This patch expands AS numbers constraint so that it supports 4 byte AS numbers. It expects that operators use asplain notation to set AS numbers[1]. That's backward compatible with existing 2 byte AS numbers. [1]: https://tools.ietf.org/html/rfc5396 Change-Id: I06ae0c42d983e88e1f38c501d5c85a7956f195ad Closes-Bug: #1573092
This commit is contained in:
parent
c576ffd48d
commit
972756f603
|
@ -93,7 +93,7 @@ class BgpSpeaker(model_base.BASEV2,
|
||||||
__tablename__ = 'bgp_speakers'
|
__tablename__ = 'bgp_speakers'
|
||||||
|
|
||||||
name = sa.Column(sa.String(255), nullable=False)
|
name = sa.Column(sa.String(255), nullable=False)
|
||||||
local_as = sa.Column(sa.Integer, nullable=False, autoincrement=False)
|
local_as = sa.Column(sa.BigInteger(), nullable=False, autoincrement=False)
|
||||||
advertise_floating_ip_host_routes = sa.Column(sa.Boolean, nullable=False)
|
advertise_floating_ip_host_routes = sa.Column(sa.Boolean, nullable=False)
|
||||||
advertise_tenant_networks = sa.Column(sa.Boolean, nullable=False)
|
advertise_tenant_networks = sa.Column(sa.Boolean, nullable=False)
|
||||||
peers = orm.relationship(BgpSpeakerPeerBinding,
|
peers = orm.relationship(BgpSpeakerPeerBinding,
|
||||||
|
@ -118,7 +118,7 @@ class BgpPeer(model_base.BASEV2,
|
||||||
name = sa.Column(sa.String(255), nullable=False)
|
name = sa.Column(sa.String(255), nullable=False)
|
||||||
peer_ip = sa.Column(sa.String(64),
|
peer_ip = sa.Column(sa.String(64),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
remote_as = sa.Column(sa.Integer, nullable=False, autoincrement=False)
|
remote_as = sa.Column(sa.BigInteger(), nullable=False, autoincrement=False)
|
||||||
auth_type = sa.Column(sa.String(16), nullable=False)
|
auth_type = sa.Column(sa.String(16), nullable=False)
|
||||||
password = sa.Column(sa.String(255), nullable=True)
|
password = sa.Column(sa.String(255), nullable=True)
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
4cf8bc3edb66
|
a589fdb5724c
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""change size of as number
|
||||||
|
|
||||||
|
Revision ID: a589fdb5724c
|
||||||
|
Revises: 4cf8bc3edb66
|
||||||
|
Create Date: 2017-08-31 13:50:28.324422
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'a589fdb5724c'
|
||||||
|
down_revision = '4cf8bc3edb66'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.alter_column('bgp_speakers', 'local_as', nullable=False,
|
||||||
|
type_=sa.BigInteger())
|
||||||
|
op.alter_column('bgp_peers', 'remote_as', nullable=False,
|
||||||
|
type_=sa.BigInteger())
|
|
@ -0,0 +1,72 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from neutron_lib.api import extensions as api_extensions
|
||||||
|
|
||||||
|
from neutron_dynamic_routing.extensions import bgp as bgp_ext
|
||||||
|
from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts
|
||||||
|
|
||||||
|
|
||||||
|
BGP_4BYTE_ASN_EXT_ALIAS = 'bgp_4byte_asn'
|
||||||
|
|
||||||
|
|
||||||
|
RESOURCE_ATTRIBUTE_MAP = {
|
||||||
|
'bgp-speakers': {
|
||||||
|
'local_as': {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:range': (bgp_consts.MIN_ASNUM,
|
||||||
|
bgp_consts.MAX_4BYTE_ASNUM)},
|
||||||
|
'is_visible': True, 'default': None,
|
||||||
|
'required_by_policy': False,
|
||||||
|
'enforce_policy': False}
|
||||||
|
},
|
||||||
|
'bgp-peers': {
|
||||||
|
'remote_as': {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:range': (bgp_consts.MIN_ASNUM,
|
||||||
|
bgp_consts.MAX_4BYTE_ASNUM)},
|
||||||
|
'is_visible': True, 'default': None,
|
||||||
|
'required_by_policy': False,
|
||||||
|
'enforce_policy': False}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Bgp_4byte_asn(api_extensions.ExtensionDescriptor):
|
||||||
|
"""Extension class supporting bgp 4-byte AS numbers.
|
||||||
|
"""
|
||||||
|
@classmethod
|
||||||
|
def get_name(cls):
|
||||||
|
return "BGP 4-byte AS numbers"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_alias(cls):
|
||||||
|
return BGP_4BYTE_ASN_EXT_ALIAS
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_description(cls):
|
||||||
|
return "Support bgp 4-byte AS numbers"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_updated(cls):
|
||||||
|
return "2017-09-07T00:00:00-00:00"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_resources(cls):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_extended_resources(self, version):
|
||||||
|
if version == "2.0":
|
||||||
|
return RESOURCE_ATTRIBUTE_MAP
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_required_extensions(self):
|
||||||
|
return [bgp_ext.BGP_EXT_ALIAS]
|
|
@ -28,11 +28,11 @@ def validate_as_num(param, as_num):
|
||||||
raise bgp_driver_exc.InvalidParamType(param=param,
|
raise bgp_driver_exc.InvalidParamType(param=param,
|
||||||
param_type='integer')
|
param_type='integer')
|
||||||
|
|
||||||
if not (bgp_consts.MIN_ASNUM <= as_num <= bgp_consts.MAX_ASNUM):
|
if not (bgp_consts.MIN_ASNUM <= as_num <= bgp_consts.MAX_4BYTE_ASNUM):
|
||||||
# Must be in [AS_NUM_MIN, AS_NUM_MAX] range.
|
# Must be in [AS_NUM_MIN, MAX_4BYTE_ASNUM] range.
|
||||||
allowed_range = ('[' +
|
allowed_range = ('[' +
|
||||||
str(bgp_consts.MIN_ASNUM) + '-' +
|
str(bgp_consts.MIN_ASNUM) + '-' +
|
||||||
str(bgp_consts.MAX_ASNUM) +
|
str(bgp_consts.MAX_4BYTE_ASNUM) +
|
||||||
']')
|
']')
|
||||||
raise bgp_driver_exc.InvalidParamRange(param=param,
|
raise bgp_driver_exc.InvalidParamRange(param=param,
|
||||||
range=allowed_range)
|
range=allowed_range)
|
||||||
|
|
|
@ -32,6 +32,7 @@ from neutron_dynamic_routing.api.rpc.handlers import bgp_speaker_rpc as bs_rpc
|
||||||
from neutron_dynamic_routing.db import bgp_db
|
from neutron_dynamic_routing.db import bgp_db
|
||||||
from neutron_dynamic_routing.db import bgp_dragentscheduler_db
|
from neutron_dynamic_routing.db import bgp_dragentscheduler_db
|
||||||
from neutron_dynamic_routing.extensions import bgp as bgp_ext
|
from neutron_dynamic_routing.extensions import bgp as bgp_ext
|
||||||
|
from neutron_dynamic_routing.extensions import bgp_4byte_asn
|
||||||
from neutron_dynamic_routing.extensions import bgp_dragentscheduler as dras_ext
|
from neutron_dynamic_routing.extensions import bgp_dragentscheduler as dras_ext
|
||||||
from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts
|
from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts
|
||||||
|
|
||||||
|
@ -44,7 +45,8 @@ class BgpPlugin(service_base.ServicePluginBase,
|
||||||
bgp_dragentscheduler_db.BgpDrAgentSchedulerDbMixin):
|
bgp_dragentscheduler_db.BgpDrAgentSchedulerDbMixin):
|
||||||
|
|
||||||
supported_extension_aliases = [bgp_ext.BGP_EXT_ALIAS,
|
supported_extension_aliases = [bgp_ext.BGP_EXT_ALIAS,
|
||||||
dras_ext.BGP_DRAGENT_SCHEDULER_EXT_ALIAS]
|
dras_ext.BGP_DRAGENT_SCHEDULER_EXT_ALIAS,
|
||||||
|
bgp_4byte_asn.BGP_4BYTE_ASN_EXT_ALIAS]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(BgpPlugin, self).__init__()
|
super(BgpPlugin, self).__init__()
|
||||||
|
|
|
@ -25,3 +25,4 @@ SUPPORTED_AUTH_TYPES = ['none', 'md5']
|
||||||
# Supported AS number range
|
# Supported AS number range
|
||||||
MIN_ASNUM = 1
|
MIN_ASNUM = 1
|
||||||
MAX_ASNUM = 65535
|
MAX_ASNUM = 65535
|
||||||
|
MAX_4BYTE_ASNUM = 4294967295
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
|
from tempest.common import utils
|
||||||
from tempest.lib import decorators
|
from tempest.lib import decorators
|
||||||
from tempest.lib import exceptions as lib_exc
|
from tempest.lib import exceptions as lib_exc
|
||||||
|
|
||||||
|
@ -27,9 +28,12 @@ class BgpSpeakerTestJSONNegative(test_base.BgpSpeakerTestJSONBase):
|
||||||
@decorators.attr(type=['negative', 'smoke'])
|
@decorators.attr(type=['negative', 'smoke'])
|
||||||
@decorators.idempotent_id('75e9ee2f-6efd-4320-bff7-ae24741c8b06')
|
@decorators.idempotent_id('75e9ee2f-6efd-4320-bff7-ae24741c8b06')
|
||||||
def test_create_bgp_speaker_illegal_local_asn(self):
|
def test_create_bgp_speaker_illegal_local_asn(self):
|
||||||
|
wrong_asn = 65537
|
||||||
|
if utils.is_extension_enabled('bgp_4byte_asn', 'network'):
|
||||||
|
wrong_asn = 4294967296
|
||||||
self.assertRaises(lib_exc.BadRequest,
|
self.assertRaises(lib_exc.BadRequest,
|
||||||
self.create_bgp_speaker,
|
self.create_bgp_speaker,
|
||||||
local_as='65537')
|
local_as=wrong_asn)
|
||||||
|
|
||||||
@decorators.attr(type=['negative', 'smoke'])
|
@decorators.attr(type=['negative', 'smoke'])
|
||||||
@decorators.idempotent_id('6742ec2e-382a-4453-8791-13a19b47cd13')
|
@decorators.idempotent_id('6742ec2e-382a-4453-8791-13a19b47cd13')
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from tempest.common import utils
|
||||||
|
from tempest import config
|
||||||
|
from tempest.lib import decorators
|
||||||
|
|
||||||
|
from neutron_dynamic_routing.tests.tempest.scenario import base
|
||||||
|
from neutron_dynamic_routing.tests.tempest.scenario import base_test_proto as test_base # noqa
|
||||||
|
|
||||||
|
from ryu.tests.integrated.common import docker_base as ctn_base
|
||||||
|
from ryu.tests.integrated.common import quagga
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class BgpSpeaker4byteASNTest(test_base.BgpSpeakerProtoTestBase):
|
||||||
|
|
||||||
|
RAS_MAX = 3
|
||||||
|
ip_version = 4
|
||||||
|
public_gw = '192.168.10.1'
|
||||||
|
MyScope = base.Scope(name='my-scope')
|
||||||
|
PNet = base.Net(name='', net='172.24.6.0', mask=24,
|
||||||
|
cidr='172.24.6.0/24', router=None)
|
||||||
|
PPool = base.Pool(name='test-pool-ext', prefixlen=PNet.mask,
|
||||||
|
prefixes=[PNet.net + '/8'])
|
||||||
|
PSubNet = base.SubNet(name='', cidr=PNet.cidr, mask=PNet.mask)
|
||||||
|
TPool = base.Pool(name='tenant-test-pool', prefixlen=28,
|
||||||
|
prefixes=['10.10.0.0/16'])
|
||||||
|
L_AS = base.AS(asn='4200000000', router_id='192.168.0.2', adv_net='')
|
||||||
|
ras_l = [
|
||||||
|
base.AS(asn='4210000000', router_id='192.168.0.12',
|
||||||
|
adv_net='192.168.162.0/24'),
|
||||||
|
base.AS(asn='64522', router_id='192.168.0.13',
|
||||||
|
adv_net='192.168.163.0/24'),
|
||||||
|
base.AS(asn='4230000000', router_id='192.168.0.14',
|
||||||
|
adv_net='192.168.164.0/24')
|
||||||
|
]
|
||||||
|
|
||||||
|
bgp_speaker_args = {
|
||||||
|
'local_as': L_AS.asn,
|
||||||
|
'ip_version': ip_version,
|
||||||
|
'name': 'my-bgp-speaker1',
|
||||||
|
'advertise_floating_ip_host_routes': True,
|
||||||
|
'advertise_tenant_networks': True
|
||||||
|
}
|
||||||
|
bgp_peer_args = [
|
||||||
|
{'remote_as': ras_l[0].asn,
|
||||||
|
'name': 'my-bgp-peer1',
|
||||||
|
'peer_ip': None,
|
||||||
|
'auth_type': 'none'},
|
||||||
|
{'remote_as': ras_l[1].asn,
|
||||||
|
'name': 'my-bgp-peer2',
|
||||||
|
'peer_ip': None,
|
||||||
|
'auth_type': 'none'},
|
||||||
|
{'remote_as': ras_l[2].asn,
|
||||||
|
'name': 'my-bgp-peer3',
|
||||||
|
'peer_ip': None,
|
||||||
|
'auth_type': 'none'}
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@utils.requires_ext(extension="bgp_4byte_asn", service="network")
|
||||||
|
def resource_setup(cls):
|
||||||
|
super(BgpSpeaker4byteASNTest, cls).resource_setup()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resource_setup_container(cls):
|
||||||
|
cls.brdc = ctn_base.Bridge(name='br-docker',
|
||||||
|
subnet='192.168.10.0/24',
|
||||||
|
start_ip='192.168.10.128',
|
||||||
|
end_ip='192.168.10.254',
|
||||||
|
self_ip=True,
|
||||||
|
fixed_ip=cls.public_gw + '/24',
|
||||||
|
br_type=base.BRIDGE_TYPE)
|
||||||
|
cls.bridges.append(cls.brdc)
|
||||||
|
# This is dummy container object for a dr service.
|
||||||
|
# This keeps data which passes to a quagga container.
|
||||||
|
cls.dr = ctn_base.BGPContainer(name='dr', asn=int(cls.L_AS.asn),
|
||||||
|
router_id=cls.L_AS.router_id)
|
||||||
|
cls.dr.set_addr_info(bridge='br-docker', ipv4=cls.public_gw)
|
||||||
|
# quagga container
|
||||||
|
cls.dockerimg = ctn_base.DockerImage()
|
||||||
|
cls.q_img = cls.dockerimg.create_quagga(check_exist=True)
|
||||||
|
cls.images.append(cls.q_img)
|
||||||
|
for i in range(cls.RAS_MAX):
|
||||||
|
qg = quagga.QuaggaBGPContainer(name='q' + str(i + 1),
|
||||||
|
asn=int(cls.ras_l[i].asn),
|
||||||
|
router_id=cls.ras_l[i].router_id,
|
||||||
|
ctn_image_name=cls.q_img)
|
||||||
|
cls.containers.append(qg)
|
||||||
|
cls.r_ass.append(qg)
|
||||||
|
qg.add_route(cls.ras_l[i].adv_net)
|
||||||
|
qg.run(wait=True)
|
||||||
|
cls.r_as_ip.append(cls.brdc.addif(qg))
|
||||||
|
qg.add_peer(cls.dr, bridge=cls.brdc.name,
|
||||||
|
peer_info={'passive': True})
|
||||||
|
cls.tnet_gen = cls.get_subnet(start='10.10.1.0', end='10.10.255.0',
|
||||||
|
step=256)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('9f18c931-a59e-4a27-939b-21124995ffe2')
|
||||||
|
def test_check_neighbor_established(self):
|
||||||
|
self._test_check_neighbor_established(self.ip_version)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('73466aa5-7d1d-4f9f-8fb4-4100fad2dffe')
|
||||||
|
def test_check_advertised_tenant_network(self):
|
||||||
|
self._test_check_advertised_tenant_network(self.ip_version)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('c3158328-2f69-4aa2-b1b7-5a06ab58afaf')
|
||||||
|
def test_check_advertised_multiple_tenant_network(self):
|
||||||
|
self._test_check_advertised_multiple_tenant_network(self.ip_version)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('212a3d82-ac50-43dc-b657-030b1133643e')
|
||||||
|
def test_check_neighbor_established_with_multiple_peers(self):
|
||||||
|
self._test_check_neighbor_established_with_multiple_peers(
|
||||||
|
self.ip_version)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('c72411c8-ea79-495d-bdbd-a10159642676')
|
||||||
|
def test_check_advertised_tenant_network_with_multiple_peers(self):
|
||||||
|
self._test_check_advertised_tenant_network_with_multiple_peers(
|
||||||
|
self.ip_version)
|
|
@ -191,7 +191,9 @@ class TestRyuBgpDriver(base.BaseTestCase):
|
||||||
self.assertRaises(bgp_driver_exc.InvalidParamRange,
|
self.assertRaises(bgp_driver_exc.InvalidParamRange,
|
||||||
self.ryu_bgp_driver.add_bgp_speaker, -1)
|
self.ryu_bgp_driver.add_bgp_speaker, -1)
|
||||||
self.assertRaises(bgp_driver_exc.InvalidParamRange,
|
self.assertRaises(bgp_driver_exc.InvalidParamRange,
|
||||||
self.ryu_bgp_driver.add_bgp_speaker, 65536)
|
self.ryu_bgp_driver.add_bgp_speaker, 4294967296)
|
||||||
|
# valid when enables 4 byte AS number
|
||||||
|
self.ryu_bgp_driver.add_bgp_speaker(65536)
|
||||||
|
|
||||||
def test_add_bgp_peer_with_invalid_paramtype(self):
|
def test_add_bgp_peer_with_invalid_paramtype(self):
|
||||||
# Test with an invalid asnum data-type
|
# Test with an invalid asnum data-type
|
||||||
|
@ -237,7 +239,9 @@ class TestRyuBgpDriver(base.BaseTestCase):
|
||||||
FAKE_LOCAL_AS1, FAKE_PEER_IP, -1)
|
FAKE_LOCAL_AS1, FAKE_PEER_IP, -1)
|
||||||
self.assertRaises(bgp_driver_exc.InvalidParamRange,
|
self.assertRaises(bgp_driver_exc.InvalidParamRange,
|
||||||
self.ryu_bgp_driver.add_bgp_peer,
|
self.ryu_bgp_driver.add_bgp_peer,
|
||||||
FAKE_LOCAL_AS1, FAKE_PEER_IP, 65536)
|
FAKE_LOCAL_AS1, FAKE_PEER_IP, 4294967296)
|
||||||
|
# valid when enables 4 byte AS number
|
||||||
|
self.ryu_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP, 65536)
|
||||||
|
|
||||||
def test_add_bgp_peer_without_adding_speaker(self):
|
def test_add_bgp_peer_without_adding_speaker(self):
|
||||||
self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded,
|
self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded,
|
||||||
|
|
|
@ -52,19 +52,19 @@ class TestValidateMethod(base.BaseTestCase):
|
||||||
def test_validate_as_num_with_invalid_max_range(self):
|
def test_validate_as_num_with_invalid_max_range(self):
|
||||||
allowed_range = ('\[' +
|
allowed_range = ('\[' +
|
||||||
str(bgp_consts.MIN_ASNUM) + '-' +
|
str(bgp_consts.MIN_ASNUM) + '-' +
|
||||||
str(bgp_consts.MAX_ASNUM) +
|
str(bgp_consts.MAX_4BYTE_ASNUM) +
|
||||||
'\]')
|
'\]')
|
||||||
with self.assertRaisesRegex(
|
with self.assertRaisesRegex(
|
||||||
bgp_driver_exc.InvalidParamRange,
|
bgp_driver_exc.InvalidParamRange,
|
||||||
EXC_INV_PARAMRANGE % {'param': 'local_as',
|
EXC_INV_PARAMRANGE % {'param': 'local_as',
|
||||||
'range': allowed_range}):
|
'range': allowed_range}):
|
||||||
bgp_driver_utils.validate_as_num('local_as',
|
bgp_driver_utils.validate_as_num('local_as',
|
||||||
bgp_consts.MAX_ASNUM + 1)
|
bgp_consts.MAX_4BYTE_ASNUM + 1)
|
||||||
|
|
||||||
def test_validate_as_num_with_invalid_min_range(self):
|
def test_validate_as_num_with_invalid_min_range(self):
|
||||||
allowed_range = ('\[' +
|
allowed_range = ('\[' +
|
||||||
str(bgp_consts.MIN_ASNUM) + '-' +
|
str(bgp_consts.MIN_ASNUM) + '-' +
|
||||||
str(bgp_consts.MAX_ASNUM) +
|
str(bgp_consts.MAX_4BYTE_ASNUM) +
|
||||||
'\]')
|
'\]')
|
||||||
with self.assertRaisesRegex(
|
with self.assertRaisesRegex(
|
||||||
bgp_driver_exc.InvalidParamRange,
|
bgp_driver_exc.InvalidParamRange,
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- The neutron-dynamic-routing supports 4-byte AS Numbers now.
|
Loading…
Reference in New Issue