Merge pull request #474 from morgabra/port_network_plugin
add port-level network_plugin
This commit is contained in:
@@ -45,7 +45,10 @@ EXTENDED_ATTRIBUTES_2_0 = {
|
|||||||
"vlan_id": {
|
"vlan_id": {
|
||||||
"allow_post": False,
|
"allow_post": False,
|
||||||
"allow_put": False,
|
"allow_put": False,
|
||||||
"is_visible": True}}}
|
"is_visible": True},
|
||||||
|
"network_plugin": {"allow_post": True, "allow_put": False,
|
||||||
|
"enforce_policy": True,
|
||||||
|
"is_visible": False, "default": ''}}}
|
||||||
|
|
||||||
|
|
||||||
class Ports_quark(object):
|
class Ports_quark(object):
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
"""add_port_network_plugin
|
||||||
|
|
||||||
|
Revision ID: 374c1bdb4480
|
||||||
|
Revises: 4da4444d7706
|
||||||
|
Create Date: 2015-10-20 12:08:24.780056
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '374c1bdb4480'
|
||||||
|
down_revision = '4da4444d7706'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('quark_ports', sa.Column('network_plugin',
|
||||||
|
sa.String(length=36), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('quark_ports', 'network_plugin')
|
||||||
@@ -1 +1 @@
|
|||||||
4da4444d7706
|
374c1bdb4480
|
||||||
@@ -430,6 +430,8 @@ class Port(BASEV2, models.HasTenant, models.HasId, IsHazTags):
|
|||||||
bridge = sa.Column(sa.String(255))
|
bridge = sa.Column(sa.String(255))
|
||||||
associations = orm.relationship(PortIpAssociation, backref="port")
|
associations = orm.relationship(PortIpAssociation, backref="port")
|
||||||
|
|
||||||
|
network_plugin = sa.Column(sa.String(36), nullable=True)
|
||||||
|
|
||||||
@declarative.declared_attr
|
@declarative.declared_attr
|
||||||
def ip_addresses(cls):
|
def ip_addresses(cls):
|
||||||
primaryjoin = cls.id == port_ip_association_table.c.port_id
|
primaryjoin = cls.id == port_ip_association_table.c.port_id
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ from quark.drivers import optimized_nvp_driver as optnvp
|
|||||||
from quark.drivers.registry_base import DriverRegistryBase
|
from quark.drivers.registry_base import DriverRegistryBase
|
||||||
from quark.drivers import unmanaged
|
from quark.drivers import unmanaged
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DriverRegistry(DriverRegistryBase):
|
class DriverRegistry(DriverRegistryBase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -28,5 +32,45 @@ class DriverRegistry(DriverRegistryBase):
|
|||||||
optnvp.OptimizedNVPDriver.get_name(): optnvp.OptimizedNVPDriver(),
|
optnvp.OptimizedNVPDriver.get_name(): optnvp.OptimizedNVPDriver(),
|
||||||
unmanaged.UnmanagedDriver.get_name(): unmanaged.UnmanagedDriver()})
|
unmanaged.UnmanagedDriver.get_name(): unmanaged.UnmanagedDriver()})
|
||||||
|
|
||||||
|
# You may optionally specify a port-level driver name that will
|
||||||
|
# be used intead of the underlying network driver. This map determines
|
||||||
|
# which drivers are allowed to be used in this way.
|
||||||
|
# example: {"MY_DRIVER": ["MY_OTHER_DRIVER"]}
|
||||||
|
# The above example would allow ports created with "MY_DRIVER"
|
||||||
|
# specified to be used with networks that use "MY_OTHER_DRIVER",
|
||||||
|
# but *not* the inverse.
|
||||||
|
# Note that drivers are automatically compatible with themselves.
|
||||||
|
self.port_driver_compat_map = {}
|
||||||
|
|
||||||
|
def get_driver(self, net_driver, port_driver=None):
|
||||||
|
LOG.info("Selecting driver for net_driver:%s "
|
||||||
|
"port_driver:%s" % (net_driver, port_driver))
|
||||||
|
|
||||||
|
if port_driver:
|
||||||
|
|
||||||
|
# Check port_driver is valid driver
|
||||||
|
if port_driver not in self.drivers:
|
||||||
|
raise Exception("Driver %s is not registered." % port_driver)
|
||||||
|
|
||||||
|
# Net drivers are compatible with themselves
|
||||||
|
if port_driver == net_driver:
|
||||||
|
LOG.info("Selecting port_driver:%s" % (port_driver))
|
||||||
|
return self.drivers[port_driver]
|
||||||
|
|
||||||
|
# Check port_driver is compatible with the given net_driver
|
||||||
|
allowed = self.port_driver_compat_map.get(port_driver, [])
|
||||||
|
if net_driver not in allowed:
|
||||||
|
raise Exception("Port driver %s not allowed for "
|
||||||
|
"underlying network driver %s."
|
||||||
|
% (port_driver, net_driver))
|
||||||
|
|
||||||
|
LOG.info("Selecting port_driver:%s" % (port_driver))
|
||||||
|
return self.drivers[port_driver]
|
||||||
|
|
||||||
|
elif net_driver in self.drivers:
|
||||||
|
LOG.info("Selecting net_driver:%s" % (net_driver))
|
||||||
|
return self.drivers[net_driver]
|
||||||
|
|
||||||
|
raise Exception("Driver %s is not registered." % net_driver)
|
||||||
|
|
||||||
DRIVER_REGISTRY = DriverRegistry()
|
DRIVER_REGISTRY = DriverRegistry()
|
||||||
|
|||||||
@@ -43,6 +43,19 @@ def _raise_if_unauthorized(tenant_id, net):
|
|||||||
raise exceptions.NotAuthorized()
|
raise exceptions.NotAuthorized()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_net_driver(network, port=None):
|
||||||
|
port_driver = None
|
||||||
|
if port and port.get("network_plugin"):
|
||||||
|
port_driver = port.get("network_plugin")
|
||||||
|
|
||||||
|
try:
|
||||||
|
return registry.DRIVER_REGISTRY.get_driver(
|
||||||
|
network["network_plugin"], port_driver=port_driver)
|
||||||
|
except Exception as e:
|
||||||
|
raise exceptions.BadRequest(resource="ports",
|
||||||
|
msg="invalid network_plugin: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
def split_and_validate_requested_subnets(context, net_id, segment_id,
|
def split_and_validate_requested_subnets(context, net_id, segment_id,
|
||||||
fixed_ips):
|
fixed_ips):
|
||||||
subnets = []
|
subnets = []
|
||||||
@@ -90,7 +103,7 @@ def create_port(context, port):
|
|||||||
port_attrs = port["port"]
|
port_attrs = port["port"]
|
||||||
|
|
||||||
admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up",
|
admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up",
|
||||||
"use_forbidden_mac_range"]
|
"use_forbidden_mac_range", "network_plugin"]
|
||||||
utils.filter_body(context, port_attrs, admin_only=admin_only)
|
utils.filter_body(context, port_attrs, admin_only=admin_only)
|
||||||
|
|
||||||
port_attrs = port["port"]
|
port_attrs = port["port"]
|
||||||
@@ -144,7 +157,7 @@ def create_port(context, port):
|
|||||||
|
|
||||||
ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"])
|
ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"])
|
||||||
|
|
||||||
net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"])
|
net_driver = _get_net_driver(net, port=port_attrs)
|
||||||
|
|
||||||
# TODO(anyone): security groups are not currently supported on port create.
|
# TODO(anyone): security groups are not currently supported on port create.
|
||||||
# Please see JIRA:NCP-801
|
# Please see JIRA:NCP-801
|
||||||
@@ -285,7 +298,7 @@ def update_port(context, id, port):
|
|||||||
|
|
||||||
admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up",
|
admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up",
|
||||||
"device_id"]
|
"device_id"]
|
||||||
always_filter = ["network_id", "backend_key"]
|
always_filter = ["network_id", "backend_key", "network_plugin"]
|
||||||
utils.filter_body(context, port_dict, admin_only=admin_only,
|
utils.filter_body(context, port_dict, admin_only=admin_only,
|
||||||
always_filter=always_filter)
|
always_filter=always_filter)
|
||||||
|
|
||||||
@@ -380,8 +393,9 @@ def update_port(context, id, port):
|
|||||||
port_dict["addresses"] = port_db["ip_addresses"]
|
port_dict["addresses"] = port_db["ip_addresses"]
|
||||||
port_dict["addresses"].extend(addresses)
|
port_dict["addresses"].extend(addresses)
|
||||||
|
|
||||||
net_driver = registry.DRIVER_REGISTRY.get_driver(
|
# NOTE(morgabra) Updating network_plugin on port objects is explicitly
|
||||||
port_db.network["network_plugin"])
|
# disallowed in the api, so we use whatever exists in the db.
|
||||||
|
net_driver = _get_net_driver(port_db.network, port=port_db)
|
||||||
|
|
||||||
# TODO(anyone): What do we want to have happen here if this fails? Is it
|
# TODO(anyone): What do we want to have happen here if this fails? Is it
|
||||||
# ok to continue to keep the IPs but fail to apply security
|
# ok to continue to keep the IPs but fail to apply security
|
||||||
@@ -520,8 +534,7 @@ def delete_port(context, id):
|
|||||||
ipam_driver.deallocate_ips_by_port(
|
ipam_driver.deallocate_ips_by_port(
|
||||||
context, port, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)
|
context, port, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)
|
||||||
|
|
||||||
net_driver = registry.DRIVER_REGISTRY.get_driver(
|
net_driver = _get_net_driver(port.network, port=port)
|
||||||
port.network["network_plugin"])
|
|
||||||
net_driver.delete_port(context, backend_key, device_id=port["device_id"],
|
net_driver.delete_port(context, backend_key, device_id=port["device_id"],
|
||||||
mac_address=port["mac_address"])
|
mac_address=port["mac_address"])
|
||||||
|
|
||||||
@@ -531,8 +544,7 @@ def delete_port(context, id):
|
|||||||
|
|
||||||
def _diag_port(context, port, fields):
|
def _diag_port(context, port, fields):
|
||||||
p = v._make_port_dict(port)
|
p = v._make_port_dict(port)
|
||||||
net_driver = registry.DRIVER_REGISTRY.get_driver(
|
net_driver = _get_net_driver(port.network, port=port)
|
||||||
port.network["network_plugin"])
|
|
||||||
if 'config' in fields:
|
if 'config' in fields:
|
||||||
p.update(net_driver.diag_port(
|
p.update(net_driver.diag_port(
|
||||||
context, port["backend_key"], get_status='status' in fields))
|
context, port["backend_key"], get_status='status' in fields))
|
||||||
|
|||||||
@@ -1132,11 +1132,49 @@ class TestPortDiagnose(test_quark_plugin.TestQuarkPlugin):
|
|||||||
self.plugin.diagnose_port(self.context, 1, [])
|
self.plugin.diagnose_port(self.context, 1, [])
|
||||||
|
|
||||||
|
|
||||||
class TestPortBadNetworkPlugin(test_quark_plugin.TestQuarkPlugin):
|
class TestPortNetworkPlugin(test_quark_plugin.TestQuarkPlugin):
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def _stubs(self, network=None, addr=None, mac=None, compat_map=None):
|
||||||
|
network["ipam_strategy"] = "ANY"
|
||||||
|
|
||||||
|
# Mock out the driver registry
|
||||||
|
foo_driver = mock.Mock()
|
||||||
|
foo_driver.create_port.return_value = {"uuid": 1}
|
||||||
|
bar_driver = mock.Mock()
|
||||||
|
bar_driver.create_port.return_value = {"uuid": 1}
|
||||||
|
drivers = {"FOO": foo_driver,
|
||||||
|
"BAR": bar_driver}
|
||||||
|
compat_map = compat_map or {}
|
||||||
|
|
||||||
|
with contextlib.nested(
|
||||||
|
mock.patch("quark.db.api.port_create"),
|
||||||
|
mock.patch("quark.db.api.network_find"),
|
||||||
|
mock.patch("quark.ipam.QuarkIpam.allocate_ip_address"),
|
||||||
|
mock.patch("quark.ipam.QuarkIpam.allocate_mac_address"),
|
||||||
|
mock.patch("oslo_utils.uuidutils.generate_uuid"),
|
||||||
|
mock.patch("quark.plugin_views._make_port_dict"),
|
||||||
|
mock.patch("quark.db.api.port_count_all"),
|
||||||
|
mock.patch("neutron.quota.QuotaEngine.limit_check"),
|
||||||
|
mock.patch("quark.plugin_modules.ports.registry."
|
||||||
|
"DRIVER_REGISTRY.drivers",
|
||||||
|
new_callable=mock.PropertyMock(return_value=drivers)),
|
||||||
|
mock.patch("quark.plugin_modules.ports.registry."
|
||||||
|
"DRIVER_REGISTRY.port_driver_compat_map",
|
||||||
|
new_callable=mock.PropertyMock(return_value=compat_map))
|
||||||
|
) as (port_create, net_find, alloc_ip, alloc_mac, gen_uuid, make_port,
|
||||||
|
port_count, limit_check, _, _):
|
||||||
|
net_find.return_value = network
|
||||||
|
alloc_ip.return_value = addr
|
||||||
|
alloc_mac.return_value = mac
|
||||||
|
gen_uuid.return_value = 1
|
||||||
|
port_count.return_value = 0
|
||||||
|
yield port_create, alloc_mac, net_find
|
||||||
|
|
||||||
def test_create_port_with_bad_network_plugin_fails(self):
|
def test_create_port_with_bad_network_plugin_fails(self):
|
||||||
network_dict = dict(id=1)
|
network_dict = dict(id=1, tenant_id=self.context.tenant_id)
|
||||||
port_name = "foobar"
|
port_name = "foobar"
|
||||||
mac = dict(address="AA:BB:CC:DD:EE:FF")
|
mac = dict(address="AA:BB:CC:DD:EE:FF")
|
||||||
|
ip = dict()
|
||||||
port = dict(port=dict(mac_address=mac["address"], network_id=1,
|
port = dict(port=dict(mac_address=mac["address"], network_id=1,
|
||||||
tenant_id=self.context.tenant_id, device_id=2,
|
tenant_id=self.context.tenant_id, device_id=2,
|
||||||
name=port_name))
|
name=port_name))
|
||||||
@@ -1147,20 +1185,153 @@ class TestPortBadNetworkPlugin(test_quark_plugin.TestQuarkPlugin):
|
|||||||
port_model.update(port)
|
port_model.update(port)
|
||||||
port_models = port_model
|
port_models = port_model
|
||||||
|
|
||||||
with contextlib.nested(
|
with self._stubs(network=network, addr=ip,
|
||||||
mock.patch("quark.db.api.port_create"),
|
mac=mac) as (port_create, alloc_mac, net_find):
|
||||||
mock.patch("quark.db.api.network_find"),
|
|
||||||
mock.patch("quark.ipam.QuarkIpam.allocate_ip_address"),
|
|
||||||
mock.patch("quark.ipam.QuarkIpam.allocate_mac_address"),
|
|
||||||
) as (port_create, net_find, alloc_ip, alloc_mac):
|
|
||||||
port_create.return_value = port_models
|
port_create.return_value = port_models
|
||||||
net_find.return_value = network
|
|
||||||
alloc_ip.return_value = {}
|
|
||||||
alloc_mac.return_value = mac
|
|
||||||
|
|
||||||
with self.assertRaises(Exception): # noqa
|
exc = "Driver FAIL is not registered."
|
||||||
|
with self.assertRaisesRegexp(exceptions.BadRequest, exc):
|
||||||
self.plugin.create_port(self.context, port)
|
self.plugin.create_port(self.context, port)
|
||||||
|
|
||||||
|
def test_create_port_with_bad_port_network_plugin_fails(self):
|
||||||
|
network_dict = dict(id=1, tenant_id=self.context.tenant_id)
|
||||||
|
port_name = "foobar"
|
||||||
|
mac = dict(address="AA:BB:CC:DD:EE:FF")
|
||||||
|
ip = dict()
|
||||||
|
port = dict(port=dict(mac_address=mac["address"], network_id=1,
|
||||||
|
tenant_id=self.context.tenant_id, device_id=2,
|
||||||
|
name=port_name, network_plugin="FAIL"))
|
||||||
|
network = models.Network()
|
||||||
|
network.update(network_dict)
|
||||||
|
network["network_plugin"] = "FOO"
|
||||||
|
port_model = models.Port()
|
||||||
|
port_model.update(port)
|
||||||
|
port_models = port_model
|
||||||
|
|
||||||
|
with self._stubs(network=network, addr=ip,
|
||||||
|
mac=mac) as (port_create, alloc_mac, net_find):
|
||||||
|
port_create.return_value = port_models
|
||||||
|
|
||||||
|
exc = "Driver FAIL is not registered."
|
||||||
|
admin_ctx = self.context.elevated()
|
||||||
|
with self.assertRaisesRegexp(exceptions.BadRequest, exc):
|
||||||
|
self.plugin.create_port(admin_ctx, port)
|
||||||
|
|
||||||
|
def test_create_port_with_incompatable_port_network_plugin_fails(self):
|
||||||
|
network_dict = dict(id=1, tenant_id=self.context.tenant_id)
|
||||||
|
port_name = "foobar"
|
||||||
|
mac = dict(address="AA:BB:CC:DD:EE:FF")
|
||||||
|
ip = dict()
|
||||||
|
port = dict(port=dict(mac_address=mac["address"], network_id=1,
|
||||||
|
tenant_id=self.context.tenant_id, device_id=2,
|
||||||
|
name=port_name, network_plugin="BAR"))
|
||||||
|
network = models.Network()
|
||||||
|
network.update(network_dict)
|
||||||
|
network["network_plugin"] = "FOO"
|
||||||
|
port_model = models.Port()
|
||||||
|
port_model.update(port)
|
||||||
|
port_models = port_model
|
||||||
|
|
||||||
|
with self._stubs(network=network, addr=ip,
|
||||||
|
mac=mac) as (port_create, alloc_mac, net_find):
|
||||||
|
port_create.return_value = port_models
|
||||||
|
|
||||||
|
exc = ("Port driver BAR not allowed for underlying network "
|
||||||
|
"driver FOO.")
|
||||||
|
admin_ctx = self.context.elevated()
|
||||||
|
with self.assertRaisesRegexp(exceptions.BadRequest, exc):
|
||||||
|
self.plugin.create_port(admin_ctx, port)
|
||||||
|
|
||||||
|
def test_create_port_with_port_network_plugin(self):
|
||||||
|
network = dict(id=1, tenant_id=self.context.tenant_id,
|
||||||
|
network_plugin="FOO")
|
||||||
|
mac = dict(address="AA:BB:CC:DD:EE:FF")
|
||||||
|
port_name = "foobar"
|
||||||
|
ip = dict()
|
||||||
|
|
||||||
|
port = dict(port=dict(mac_address=mac["address"], network_id=1,
|
||||||
|
tenant_id=self.context.tenant_id, device_id=2,
|
||||||
|
name=port_name, device_owner="quark_tests",
|
||||||
|
bridge="quark_bridge", admin_state_up=False))
|
||||||
|
|
||||||
|
expected_mac = "DE:AD:BE:EF:00:00"
|
||||||
|
expected_bridge = "new_bridge"
|
||||||
|
expected_device_owner = "new_device_owner"
|
||||||
|
expected_admin_state = "new_state"
|
||||||
|
expected_network_plugin = "FOO"
|
||||||
|
|
||||||
|
port_create_dict = {}
|
||||||
|
port_create_dict["port"] = port["port"].copy()
|
||||||
|
port_create_dict["port"]["mac_address"] = expected_mac
|
||||||
|
port_create_dict["port"]["device_owner"] = expected_device_owner
|
||||||
|
port_create_dict["port"]["bridge"] = expected_bridge
|
||||||
|
port_create_dict["port"]["admin_state_up"] = expected_admin_state
|
||||||
|
port_create_dict["port"]["network_plugin"] = expected_network_plugin
|
||||||
|
|
||||||
|
admin_ctx = self.context.elevated()
|
||||||
|
with self._stubs(network=network, addr=ip,
|
||||||
|
mac=mac) as (port_create, alloc_mac, net_find):
|
||||||
|
self.plugin.create_port(admin_ctx, port_create_dict)
|
||||||
|
|
||||||
|
alloc_mac.assert_called_once_with(
|
||||||
|
admin_ctx, network["id"], 1,
|
||||||
|
cfg.CONF.QUARK.ipam_reuse_after,
|
||||||
|
mac_address=expected_mac, use_forbidden_mac_range=False)
|
||||||
|
|
||||||
|
port_create.assert_called_once_with(
|
||||||
|
admin_ctx, bridge=expected_bridge, uuid=1, name="foobar",
|
||||||
|
admin_state_up=expected_admin_state, network_id=1,
|
||||||
|
tenant_id="fake", id=1, device_owner=expected_device_owner,
|
||||||
|
mac_address=mac["address"], device_id=2, backend_key=1,
|
||||||
|
security_groups=[], addresses=[],
|
||||||
|
network_plugin=expected_network_plugin)
|
||||||
|
|
||||||
|
def test_create_port_with_compatable_port_network_plugin(self):
|
||||||
|
network = dict(id=1, tenant_id=self.context.tenant_id,
|
||||||
|
network_plugin="FOO")
|
||||||
|
mac = dict(address="AA:BB:CC:DD:EE:FF")
|
||||||
|
port_name = "foobar"
|
||||||
|
ip = dict()
|
||||||
|
|
||||||
|
port = dict(port=dict(mac_address=mac["address"], network_id=1,
|
||||||
|
tenant_id=self.context.tenant_id, device_id=2,
|
||||||
|
name=port_name, device_owner="quark_tests",
|
||||||
|
bridge="quark_bridge", admin_state_up=False))
|
||||||
|
|
||||||
|
expected_mac = "DE:AD:BE:EF:00:00"
|
||||||
|
expected_bridge = "new_bridge"
|
||||||
|
expected_device_owner = "new_device_owner"
|
||||||
|
expected_admin_state = "new_state"
|
||||||
|
expected_network_plugin = "BAR"
|
||||||
|
|
||||||
|
port_create_dict = {}
|
||||||
|
port_create_dict["port"] = port["port"].copy()
|
||||||
|
port_create_dict["port"]["mac_address"] = expected_mac
|
||||||
|
port_create_dict["port"]["device_owner"] = expected_device_owner
|
||||||
|
port_create_dict["port"]["bridge"] = expected_bridge
|
||||||
|
port_create_dict["port"]["admin_state_up"] = expected_admin_state
|
||||||
|
port_create_dict["port"]["network_plugin"] = expected_network_plugin
|
||||||
|
|
||||||
|
compat_map = {"BAR": ["FOO"]}
|
||||||
|
admin_ctx = self.context.elevated()
|
||||||
|
with self._stubs(network=network, addr=ip, mac=mac,
|
||||||
|
compat_map=compat_map) as (port_create, alloc_mac,
|
||||||
|
net_find):
|
||||||
|
self.plugin.create_port(admin_ctx, port_create_dict)
|
||||||
|
|
||||||
|
alloc_mac.assert_called_once_with(
|
||||||
|
admin_ctx, network["id"], 1,
|
||||||
|
cfg.CONF.QUARK.ipam_reuse_after,
|
||||||
|
mac_address=expected_mac, use_forbidden_mac_range=False)
|
||||||
|
|
||||||
|
port_create.assert_called_once_with(
|
||||||
|
admin_ctx, bridge=expected_bridge, uuid=1, name="foobar",
|
||||||
|
admin_state_up=expected_admin_state, network_id=1,
|
||||||
|
tenant_id="fake", id=1, device_owner=expected_device_owner,
|
||||||
|
mac_address=mac["address"], device_id=2, backend_key=1,
|
||||||
|
security_groups=[], addresses=[],
|
||||||
|
network_plugin=expected_network_plugin)
|
||||||
|
|
||||||
|
|
||||||
class TestQuarkPortCreateFiltering(test_quark_plugin.TestQuarkPlugin):
|
class TestQuarkPortCreateFiltering(test_quark_plugin.TestQuarkPlugin):
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
@@ -1202,6 +1373,7 @@ class TestQuarkPortCreateFiltering(test_quark_plugin.TestQuarkPlugin):
|
|||||||
port_create_dict["port"]["device_owner"] = "ignored"
|
port_create_dict["port"]["device_owner"] = "ignored"
|
||||||
port_create_dict["port"]["bridge"] = "ignored"
|
port_create_dict["port"]["bridge"] = "ignored"
|
||||||
port_create_dict["port"]["admin_state_up"] = "ignored"
|
port_create_dict["port"]["admin_state_up"] = "ignored"
|
||||||
|
port_create_dict["port"]["network_plugin"] = "ignored"
|
||||||
|
|
||||||
with self._stubs(network=network, addr=ip,
|
with self._stubs(network=network, addr=ip,
|
||||||
mac=mac) as (port_create, alloc_mac, net_find):
|
mac=mac) as (port_create, alloc_mac, net_find):
|
||||||
@@ -1231,6 +1403,7 @@ class TestQuarkPortCreateFiltering(test_quark_plugin.TestQuarkPlugin):
|
|||||||
expected_bridge = "new_bridge"
|
expected_bridge = "new_bridge"
|
||||||
expected_device_owner = "new_device_owner"
|
expected_device_owner = "new_device_owner"
|
||||||
expected_admin_state = "new_state"
|
expected_admin_state = "new_state"
|
||||||
|
expected_network_plugin = "BASE"
|
||||||
|
|
||||||
port_create_dict = {}
|
port_create_dict = {}
|
||||||
port_create_dict["port"] = port["port"].copy()
|
port_create_dict["port"] = port["port"].copy()
|
||||||
@@ -1238,6 +1411,7 @@ class TestQuarkPortCreateFiltering(test_quark_plugin.TestQuarkPlugin):
|
|||||||
port_create_dict["port"]["device_owner"] = expected_device_owner
|
port_create_dict["port"]["device_owner"] = expected_device_owner
|
||||||
port_create_dict["port"]["bridge"] = expected_bridge
|
port_create_dict["port"]["bridge"] = expected_bridge
|
||||||
port_create_dict["port"]["admin_state_up"] = expected_admin_state
|
port_create_dict["port"]["admin_state_up"] = expected_admin_state
|
||||||
|
port_create_dict["port"]["network_plugin"] = expected_network_plugin
|
||||||
|
|
||||||
admin_ctx = self.context.elevated()
|
admin_ctx = self.context.elevated()
|
||||||
with self._stubs(network=network, addr=ip,
|
with self._stubs(network=network, addr=ip,
|
||||||
@@ -1254,7 +1428,8 @@ class TestQuarkPortCreateFiltering(test_quark_plugin.TestQuarkPlugin):
|
|||||||
admin_state_up=expected_admin_state, network_id=1,
|
admin_state_up=expected_admin_state, network_id=1,
|
||||||
tenant_id="fake", id=1, device_owner=expected_device_owner,
|
tenant_id="fake", id=1, device_owner=expected_device_owner,
|
||||||
mac_address=mac["address"], device_id=2, backend_key=1,
|
mac_address=mac["address"], device_id=2, backend_key=1,
|
||||||
security_groups=[], addresses=[])
|
security_groups=[], addresses=[],
|
||||||
|
network_plugin=expected_network_plugin)
|
||||||
|
|
||||||
|
|
||||||
class TestQuarkPortUpdateFiltering(test_quark_plugin.TestQuarkPlugin):
|
class TestQuarkPortUpdateFiltering(test_quark_plugin.TestQuarkPlugin):
|
||||||
@@ -1274,7 +1449,8 @@ class TestQuarkPortUpdateFiltering(test_quark_plugin.TestQuarkPlugin):
|
|||||||
new_port["port"] = {
|
new_port["port"] = {
|
||||||
"mac_address": "DD:EE:FF:00:00:00", "device_owner": "new_owner",
|
"mac_address": "DD:EE:FF:00:00:00", "device_owner": "new_owner",
|
||||||
"bridge": "new_bridge", "admin_state_up": False, "device_id": 3,
|
"bridge": "new_bridge", "admin_state_up": False, "device_id": 3,
|
||||||
"network_id": 10, "backend_key": 1234, "name": "new_name"}
|
"network_id": 10, "backend_key": 1234, "name": "new_name",
|
||||||
|
"network_plugin": "BASE"}
|
||||||
|
|
||||||
with self._stubs() as (port_find, port_update):
|
with self._stubs() as (port_find, port_update):
|
||||||
self.plugin.update_port(self.context, 1, new_port)
|
self.plugin.update_port(self.context, 1, new_port)
|
||||||
@@ -1289,7 +1465,8 @@ class TestQuarkPortUpdateFiltering(test_quark_plugin.TestQuarkPlugin):
|
|||||||
new_port["port"] = {
|
new_port["port"] = {
|
||||||
"mac_address": "DD:EE:FF:00:00:00", "device_owner": "new_owner",
|
"mac_address": "DD:EE:FF:00:00:00", "device_owner": "new_owner",
|
||||||
"bridge": "new_bridge", "admin_state_up": False, "device_id": 3,
|
"bridge": "new_bridge", "admin_state_up": False, "device_id": 3,
|
||||||
"network_id": 10, "backend_key": 1234, "name": "new_name"}
|
"network_id": 10, "backend_key": 1234, "name": "new_name",
|
||||||
|
"network_plugin": "BASE"}
|
||||||
|
|
||||||
admin_ctx = self.context.elevated()
|
admin_ctx = self.context.elevated()
|
||||||
with self._stubs() as (port_find, port_update):
|
with self._stubs() as (port_find, port_update):
|
||||||
|
|||||||
98
quark/tests/test_driver_registry.py
Normal file
98
quark/tests/test_driver_registry.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Copyright 2013 Openstack Foundation
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from quark.drivers import registry
|
||||||
|
from quark.drivers import registry_base
|
||||||
|
from quark.tests import test_base
|
||||||
|
|
||||||
|
|
||||||
|
class FakeBaseRegistry(registry_base.DriverRegistryBase):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.drivers = {"test_driver_1": 1,
|
||||||
|
"test_driver_2": 2}
|
||||||
|
|
||||||
|
|
||||||
|
class FakeNetDriverRegistry(registry.DriverRegistry):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.drivers = {"test_driver_1": 1,
|
||||||
|
"test_driver_2": 2,
|
||||||
|
"test_driver_3": 3}
|
||||||
|
|
||||||
|
self.port_driver_compat_map = {
|
||||||
|
"test_driver_2": ["test_driver_1"],
|
||||||
|
"test_driver_3": ["test_driver_1",
|
||||||
|
"test_driver_2"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestRegistryBase(test_base.TestBase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.registry = FakeBaseRegistry()
|
||||||
|
|
||||||
|
def test_get_valid(self):
|
||||||
|
driver = self.registry.get_driver("test_driver_1")
|
||||||
|
self.assertEqual(driver, 1)
|
||||||
|
|
||||||
|
driver = self.registry.get_driver("test_driver_2")
|
||||||
|
self.assertEqual(driver, 2)
|
||||||
|
|
||||||
|
def test_get_invalid(self):
|
||||||
|
exc = "Driver does_not_exist is not registered."
|
||||||
|
with self.assertRaisesRegexp(Exception, exc):
|
||||||
|
self.registry.get_driver("does_not_exist")
|
||||||
|
|
||||||
|
|
||||||
|
class TestDriverRegistry(TestRegistryBase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.registry = FakeNetDriverRegistry()
|
||||||
|
|
||||||
|
def test_get_port_driver(self):
|
||||||
|
driver = self.registry.get_driver(
|
||||||
|
"test_driver_1", port_driver="test_driver_1")
|
||||||
|
self.assertEqual(driver, 1)
|
||||||
|
|
||||||
|
driver = self.registry.get_driver(
|
||||||
|
"test_driver_2", port_driver="test_driver_2")
|
||||||
|
self.assertEqual(driver, 2)
|
||||||
|
|
||||||
|
def test_get_invalid_port_driver(self):
|
||||||
|
exc = "Driver does_not_exist is not registered."
|
||||||
|
with self.assertRaisesRegexp(Exception, exc):
|
||||||
|
self.registry.get_driver(
|
||||||
|
"test_driver_1", port_driver="does_not_exist")
|
||||||
|
|
||||||
|
def test_get_compatable_port_driver(self):
|
||||||
|
driver = self.registry.get_driver(
|
||||||
|
"test_driver_1", port_driver="test_driver_2")
|
||||||
|
self.assertEqual(driver, 2)
|
||||||
|
|
||||||
|
driver = self.registry.get_driver(
|
||||||
|
"test_driver_1", port_driver="test_driver_3")
|
||||||
|
self.assertEqual(driver, 3)
|
||||||
|
|
||||||
|
driver = self.registry.get_driver(
|
||||||
|
"test_driver_2", port_driver="test_driver_3")
|
||||||
|
self.assertEqual(driver, 3)
|
||||||
|
|
||||||
|
def test_get_incompatable_port_driver(self):
|
||||||
|
exc = ("Port driver test_driver_2 not allowed for "
|
||||||
|
"underlying network driver test_driver_3.")
|
||||||
|
with self.assertRaisesRegexp(Exception, exc):
|
||||||
|
self.registry.get_driver(
|
||||||
|
"test_driver_3", port_driver="test_driver_2")
|
||||||
Reference in New Issue
Block a user