Add database relationship between router and ports
Add an explicit schema relationship between a router and its ports. This change ensures referential integrity among the entities and prevents orphaned ports. Change-Id: I09e8a694cdff7f64a642a39b45cbd12422132806 Closes-Bug: #1378866
This commit is contained in:
parent
d217543ea0
commit
93012915a3
@ -47,6 +47,26 @@ API_TO_DB_COLUMN_MAP = {'port_id': 'fixed_port_id'}
|
|||||||
CORE_ROUTER_ATTRS = ('id', 'name', 'tenant_id', 'admin_state_up', 'status')
|
CORE_ROUTER_ATTRS = ('id', 'name', 'tenant_id', 'admin_state_up', 'status')
|
||||||
|
|
||||||
|
|
||||||
|
class RouterPort(model_base.BASEV2):
|
||||||
|
router_id = sa.Column(
|
||||||
|
sa.String(36),
|
||||||
|
sa.ForeignKey('routers.id', ondelete="CASCADE"),
|
||||||
|
primary_key=True)
|
||||||
|
port_id = sa.Column(
|
||||||
|
sa.String(36),
|
||||||
|
sa.ForeignKey('ports.id', ondelete="CASCADE"),
|
||||||
|
primary_key=True)
|
||||||
|
# The port_type attribute is redundant as the port table already specifies
|
||||||
|
# it in DEVICE_OWNER.However, this redundancy enables more efficient
|
||||||
|
# queries on router ports, and also prevents potential error-prone
|
||||||
|
# conditions which might originate from users altering the DEVICE_OWNER
|
||||||
|
# property of router ports.
|
||||||
|
port_type = sa.Column(sa.String(255))
|
||||||
|
port = orm.relationship(
|
||||||
|
models_v2.Port,
|
||||||
|
backref=orm.backref('routerport', uselist=False, cascade="all,delete"))
|
||||||
|
|
||||||
|
|
||||||
class Router(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
class Router(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||||
"""Represents a v2 neutron router."""
|
"""Represents a v2 neutron router."""
|
||||||
|
|
||||||
@ -55,6 +75,10 @@ class Router(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
|||||||
admin_state_up = sa.Column(sa.Boolean)
|
admin_state_up = sa.Column(sa.Boolean)
|
||||||
gw_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
|
gw_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
|
||||||
gw_port = orm.relationship(models_v2.Port, lazy='joined')
|
gw_port = orm.relationship(models_v2.Port, lazy='joined')
|
||||||
|
attached_ports = orm.relationship(
|
||||||
|
RouterPort,
|
||||||
|
backref='router',
|
||||||
|
lazy='dynamic')
|
||||||
|
|
||||||
|
|
||||||
class FloatingIP(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
class FloatingIP(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||||
@ -76,6 +100,7 @@ class FloatingIP(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
|||||||
# aysnchronous backend is unavailable when the floating IP is disassociated
|
# aysnchronous backend is unavailable when the floating IP is disassociated
|
||||||
last_known_router_id = sa.Column(sa.String(36))
|
last_known_router_id = sa.Column(sa.String(36))
|
||||||
status = sa.Column(sa.String(16))
|
status = sa.Column(sa.String(16))
|
||||||
|
router = orm.relationship(Router, backref='floating_ips')
|
||||||
|
|
||||||
|
|
||||||
class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
||||||
@ -259,7 +284,13 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
router.gw_port = self._core_plugin._get_port(context.elevated(),
|
router.gw_port = self._core_plugin._get_port(context.elevated(),
|
||||||
gw_port['id'])
|
gw_port['id'])
|
||||||
|
router_port = RouterPort(
|
||||||
|
router_id=router.id,
|
||||||
|
port_id=gw_port['id'],
|
||||||
|
port_type=DEVICE_OWNER_ROUTER_GW
|
||||||
|
)
|
||||||
context.session.add(router)
|
context.session.add(router)
|
||||||
|
context.session.add(router_port)
|
||||||
|
|
||||||
def _validate_gw_info(self, context, gw_port, info):
|
def _validate_gw_info(self, context, gw_port, info):
|
||||||
network_id = info['network_id'] if info else None
|
network_id = info['network_id'] if info else None
|
||||||
@ -281,11 +312,12 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
raise l3.RouterExternalGatewayInUseByFloatingIp(
|
raise l3.RouterExternalGatewayInUseByFloatingIp(
|
||||||
router_id=router_id, net_id=router.gw_port['network_id'])
|
router_id=router_id, net_id=router.gw_port['network_id'])
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
gw_port_id = router.gw_port['id']
|
gw_port = router.gw_port
|
||||||
router.gw_port = None
|
router.gw_port = None
|
||||||
context.session.add(router)
|
context.session.add(router)
|
||||||
|
context.session.expire(gw_port)
|
||||||
self._core_plugin.delete_port(
|
self._core_plugin.delete_port(
|
||||||
admin_ctx, gw_port_id, l3_port_check=False)
|
admin_ctx, gw_port['id'], l3_port_check=False)
|
||||||
|
|
||||||
def _create_gw_port(self, context, router_id, router, new_network):
|
def _create_gw_port(self, context, router_id, router, new_network):
|
||||||
new_valid_gw_port_attachment = (
|
new_valid_gw_port_attachment = (
|
||||||
@ -295,7 +327,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
subnets = self._core_plugin._get_subnets_by_network(context,
|
subnets = self._core_plugin._get_subnets_by_network(context,
|
||||||
new_network)
|
new_network)
|
||||||
for subnet in subnets:
|
for subnet in subnets:
|
||||||
self._check_for_dup_router_subnet(context, router_id,
|
self._check_for_dup_router_subnet(context, router,
|
||||||
new_network, subnet['id'],
|
new_network, subnet['id'],
|
||||||
subnet['cidr'])
|
subnet['cidr'])
|
||||||
self._create_router_gw_port(context, router, new_network)
|
self._create_router_gw_port(context, router, new_network)
|
||||||
@ -317,11 +349,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
admin_ctx, filters={'router_id': [router_id]}):
|
admin_ctx, filters={'router_id': [router_id]}):
|
||||||
raise l3.RouterInUse(router_id=router_id)
|
raise l3.RouterInUse(router_id=router_id)
|
||||||
device_owner = self._get_device_owner(context, router)
|
device_owner = self._get_device_owner(context, router)
|
||||||
device_filter = {'device_id': [router_id],
|
if any(rp.port_type == device_owner
|
||||||
'device_owner': [device_owner]}
|
for rp in router.attached_ports.all()):
|
||||||
port_count = self._core_plugin.get_ports_count(
|
|
||||||
admin_ctx, filters=device_filter)
|
|
||||||
if port_count:
|
|
||||||
raise l3.RouterInUse(router_id=router_id)
|
raise l3.RouterInUse(router_id=router_id)
|
||||||
return router
|
return router
|
||||||
|
|
||||||
@ -335,18 +364,13 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
if vpnservice:
|
if vpnservice:
|
||||||
vpnservice.check_router_in_use(context, id)
|
vpnservice.check_router_in_use(context, id)
|
||||||
|
|
||||||
|
router_ports = router.attached_ports.all()
|
||||||
|
# Set the router's gw_port to None to avoid a constraint violation.
|
||||||
|
router.gw_port = None
|
||||||
|
for rp in router_ports:
|
||||||
|
self._core_plugin._delete_port(context.elevated(), rp.port.id)
|
||||||
context.session.delete(router)
|
context.session.delete(router)
|
||||||
|
|
||||||
# Delete the gw port after the router has been removed to
|
|
||||||
# avoid a constraint violation.
|
|
||||||
device_filter = {'device_id': [id],
|
|
||||||
'device_owner': [DEVICE_OWNER_ROUTER_GW]}
|
|
||||||
ports = self._core_plugin.get_ports(context.elevated(),
|
|
||||||
filters=device_filter)
|
|
||||||
if ports:
|
|
||||||
self._core_plugin._delete_port(context.elevated(),
|
|
||||||
ports[0]['id'])
|
|
||||||
|
|
||||||
def get_router(self, context, id, fields=None):
|
def get_router(self, context, id, fields=None):
|
||||||
router = self._get_router(context, id)
|
router = self._get_router(context, id)
|
||||||
return self._make_router_dict(router, fields)
|
return self._make_router_dict(router, fields)
|
||||||
@ -367,15 +391,13 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
return self._get_collection_count(context, Router,
|
return self._get_collection_count(context, Router,
|
||||||
filters=filters)
|
filters=filters)
|
||||||
|
|
||||||
def _check_for_dup_router_subnet(self, context, router_id,
|
def _check_for_dup_router_subnet(self, context, router,
|
||||||
network_id, subnet_id, subnet_cidr):
|
network_id, subnet_id, subnet_cidr):
|
||||||
try:
|
try:
|
||||||
rport_qry = context.session.query(models_v2.Port)
|
|
||||||
rports = rport_qry.filter_by(device_id=router_id)
|
|
||||||
# It's possible these ports are on the same network, but
|
# It's possible these ports are on the same network, but
|
||||||
# different subnets.
|
# different subnets.
|
||||||
new_ipnet = netaddr.IPNetwork(subnet_cidr)
|
new_ipnet = netaddr.IPNetwork(subnet_cidr)
|
||||||
for p in rports:
|
for p in (rp.port for rp in router.attached_ports):
|
||||||
for ip in p['fixed_ips']:
|
for ip in p['fixed_ips']:
|
||||||
if ip['subnet_id'] == subnet_id:
|
if ip['subnet_id'] == subnet_id:
|
||||||
msg = (_("Router already has a port on subnet %s")
|
msg = (_("Router already has a port on subnet %s")
|
||||||
@ -415,7 +437,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
return port_id_specified, subnet_id_specified
|
return port_id_specified, subnet_id_specified
|
||||||
|
|
||||||
def _add_interface_by_port(self, context, router_id, port_id, owner):
|
def _add_interface_by_port(self, context, router, port_id, owner):
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
port = self._core_plugin._get_port(context, port_id)
|
port = self._core_plugin._get_port(context, port_id)
|
||||||
if port['device_id']:
|
if port['device_id']:
|
||||||
@ -428,19 +450,19 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
subnet_id = fixed_ips[0]['subnet_id']
|
subnet_id = fixed_ips[0]['subnet_id']
|
||||||
subnet = self._core_plugin._get_subnet(context, subnet_id)
|
subnet = self._core_plugin._get_subnet(context, subnet_id)
|
||||||
self._check_for_dup_router_subnet(context, router_id,
|
self._check_for_dup_router_subnet(context, router,
|
||||||
port['network_id'],
|
port['network_id'],
|
||||||
subnet['id'],
|
subnet['id'],
|
||||||
subnet['cidr'])
|
subnet['cidr'])
|
||||||
port.update({'device_id': router_id, 'device_owner': owner})
|
port.update({'device_id': router.id, 'device_owner': owner})
|
||||||
return port
|
return port
|
||||||
|
|
||||||
def _add_interface_by_subnet(self, context, router_id, subnet_id, owner):
|
def _add_interface_by_subnet(self, context, router, subnet_id, owner):
|
||||||
subnet = self._core_plugin._get_subnet(context, subnet_id)
|
subnet = self._core_plugin._get_subnet(context, subnet_id)
|
||||||
if not subnet['gateway_ip']:
|
if not subnet['gateway_ip']:
|
||||||
msg = _('Subnet for router interface must have a gateway IP')
|
msg = _('Subnet for router interface must have a gateway IP')
|
||||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
self._check_for_dup_router_subnet(context, router_id,
|
self._check_for_dup_router_subnet(context, router,
|
||||||
subnet['network_id'],
|
subnet['network_id'],
|
||||||
subnet_id,
|
subnet_id,
|
||||||
subnet['cidr'])
|
subnet['cidr'])
|
||||||
@ -453,7 +475,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
'fixed_ips': [fixed_ip],
|
'fixed_ips': [fixed_ip],
|
||||||
'mac_address': attributes.ATTR_NOT_SPECIFIED,
|
'mac_address': attributes.ATTR_NOT_SPECIFIED,
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'device_id': router_id,
|
'device_id': router.id,
|
||||||
'device_owner': owner,
|
'device_owner': owner,
|
||||||
'name': ''}})
|
'name': ''}})
|
||||||
|
|
||||||
@ -468,18 +490,27 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def add_router_interface(self, context, router_id, interface_info):
|
def add_router_interface(self, context, router_id, interface_info):
|
||||||
|
router = self._get_router(context, router_id)
|
||||||
add_by_port, add_by_sub = self._validate_interface_info(interface_info)
|
add_by_port, add_by_sub = self._validate_interface_info(interface_info)
|
||||||
device_owner = self._get_device_owner(context, router_id)
|
device_owner = self._get_device_owner(context, router_id)
|
||||||
|
|
||||||
if add_by_port:
|
if add_by_port:
|
||||||
port = self._add_interface_by_port(
|
port = self._add_interface_by_port(
|
||||||
context, router_id, interface_info['port_id'], device_owner)
|
context, router, interface_info['port_id'], device_owner)
|
||||||
elif add_by_sub:
|
elif add_by_sub:
|
||||||
port = self._add_interface_by_subnet(
|
port = self._add_interface_by_subnet(
|
||||||
context, router_id, interface_info['subnet_id'], device_owner)
|
context, router, interface_info['subnet_id'], device_owner)
|
||||||
|
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
router_port = RouterPort(
|
||||||
|
port_id=port['id'],
|
||||||
|
router_id=router.id,
|
||||||
|
port_type=device_owner
|
||||||
|
)
|
||||||
|
context.session.add(router_port)
|
||||||
|
|
||||||
return self._make_router_interface_info(
|
return self._make_router_interface_info(
|
||||||
router_id, port['tenant_id'], port['id'],
|
router.id, port['tenant_id'], port['id'],
|
||||||
port['fixed_ips'][0]['subnet_id'])
|
port['fixed_ips'][0]['subnet_id'])
|
||||||
|
|
||||||
def _confirm_router_interface_not_in_use(self, context, router_id,
|
def _confirm_router_interface_not_in_use(self, context, router_id,
|
||||||
@ -494,9 +525,15 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
|
|
||||||
def _remove_interface_by_port(self, context, router_id,
|
def _remove_interface_by_port(self, context, router_id,
|
||||||
port_id, subnet_id, owner):
|
port_id, subnet_id, owner):
|
||||||
port_db = self._core_plugin._get_port(context, port_id)
|
qry = context.session.query(RouterPort)
|
||||||
if not (port_db['device_owner'] == owner and
|
qry = qry.filter_by(
|
||||||
port_db['device_id'] == router_id):
|
port_id=port_id,
|
||||||
|
router_id=router_id,
|
||||||
|
port_type=owner
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
port_db = qry.one().port
|
||||||
|
except exc.NoResultFound:
|
||||||
raise l3.RouterInterfaceNotFound(router_id=router_id,
|
raise l3.RouterInterfaceNotFound(router_id=router_id,
|
||||||
port_id=port_id)
|
port_id=port_id)
|
||||||
port_subnet_id = port_db['fixed_ips'][0]['subnet_id']
|
port_subnet_id = port_db['fixed_ips'][0]['subnet_id']
|
||||||
@ -517,11 +554,12 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
subnet = self._core_plugin._get_subnet(context, subnet_id)
|
subnet = self._core_plugin._get_subnet(context, subnet_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rport_qry = context.session.query(models_v2.Port)
|
rport_qry = context.session.query(models_v2.Port).join(RouterPort)
|
||||||
ports = rport_qry.filter_by(
|
ports = rport_qry.filter(
|
||||||
device_id=router_id,
|
RouterPort.router_id == router_id,
|
||||||
device_owner=owner,
|
RouterPort.port_type == owner,
|
||||||
network_id=subnet['network_id'])
|
models_v2.Port.network_id == subnet['network_id']
|
||||||
|
)
|
||||||
|
|
||||||
for p in ports:
|
for p in ports:
|
||||||
if p['fixed_ips'][0]['subnet_id'] == subnet_id:
|
if p['fixed_ips'][0]['subnet_id'] == subnet_id:
|
||||||
@ -570,10 +608,12 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
return self._fields(res, fields)
|
return self._fields(res, fields)
|
||||||
|
|
||||||
def _get_interface_ports_for_network(self, context, network_id):
|
def _get_interface_ports_for_network(self, context, network_id):
|
||||||
router_intf_qry = context.session.query(models_v2.Port)
|
router_intf_qry = context.session.query(RouterPort)
|
||||||
return router_intf_qry.filter_by(
|
router_intf_qry = router_intf_qry.join(models_v2.Port)
|
||||||
network_id=network_id,
|
return router_intf_qry.filter(
|
||||||
device_owner=DEVICE_OWNER_ROUTER_INTF)
|
models_v2.Port.network_id == network_id,
|
||||||
|
RouterPort.port_type == DEVICE_OWNER_ROUTER_INTF
|
||||||
|
)
|
||||||
|
|
||||||
def _get_router_for_floatingip(self, context, internal_port,
|
def _get_router_for_floatingip(self, context, internal_port,
|
||||||
internal_subnet_id,
|
internal_subnet_id,
|
||||||
@ -588,16 +628,16 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
router_intf_ports = self._get_interface_ports_for_network(
|
router_intf_ports = self._get_interface_ports_for_network(
|
||||||
context, internal_port['network_id'])
|
context, internal_port['network_id'])
|
||||||
|
|
||||||
for intf_p in router_intf_ports:
|
# This joins on port_id so is not a cross-join
|
||||||
if intf_p['fixed_ips'][0]['subnet_id'] == internal_subnet_id:
|
routerport_qry = router_intf_ports.join(models_v2.IPAllocation)
|
||||||
router_id = intf_p['device_id']
|
routerport_qry = routerport_qry.filter(
|
||||||
router_gw_qry = context.session.query(models_v2.Port)
|
models_v2.IPAllocation.subnet_id == internal_subnet_id
|
||||||
has_gw_port = router_gw_qry.filter_by(
|
)
|
||||||
network_id=external_network_id,
|
|
||||||
device_id=router_id,
|
router_port = routerport_qry.first()
|
||||||
device_owner=DEVICE_OWNER_ROUTER_GW).count()
|
|
||||||
if has_gw_port:
|
if router_port and router_port.router.gw_port:
|
||||||
return router_id
|
return router_port.router.id
|
||||||
|
|
||||||
raise l3.ExternalGatewayForFloatingIPNotFound(
|
raise l3.ExternalGatewayForFloatingIPNotFound(
|
||||||
subnet_id=internal_subnet_id,
|
subnet_id=internal_subnet_id,
|
||||||
@ -936,9 +976,16 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||||||
device_owners = device_owners or [DEVICE_OWNER_ROUTER_INTF]
|
device_owners = device_owners or [DEVICE_OWNER_ROUTER_INTF]
|
||||||
if not router_ids:
|
if not router_ids:
|
||||||
return []
|
return []
|
||||||
filters = {'device_id': router_ids,
|
qry = context.session.query(RouterPort)
|
||||||
'device_owner': device_owners}
|
qry = qry.filter(
|
||||||
interfaces = self._core_plugin.get_ports(context, filters)
|
Router.id.in_(router_ids),
|
||||||
|
RouterPort.port_type.in_(device_owners)
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO(markmcclain): This is suboptimal but was left to reduce
|
||||||
|
# changeset size since it is late in cycle
|
||||||
|
ports = [rp.port.id for rp in qry]
|
||||||
|
interfaces = self._core_plugin.get_ports(context, {'id': ports})
|
||||||
if interfaces:
|
if interfaces:
|
||||||
self._populate_subnet_for_ports(context, interfaces)
|
self._populate_subnet_for_ports(context, interfaces)
|
||||||
return interfaces
|
return interfaces
|
||||||
|
@ -81,13 +81,11 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
self, context, router_id, router_db, data, gw_info):
|
self, context, router_id, router_db, data, gw_info):
|
||||||
"""Update the model to support the dvr case of a router."""
|
"""Update the model to support the dvr case of a router."""
|
||||||
if not attributes.is_attr_set(gw_info) and data.get('distributed'):
|
if not attributes.is_attr_set(gw_info) and data.get('distributed'):
|
||||||
admin_ctx = context.elevated()
|
old_owner = l3_const.DEVICE_OWNER_ROUTER_INTF
|
||||||
filters = {'device_id': [router_id],
|
new_owner = DEVICE_OWNER_DVR_INTERFACE
|
||||||
'device_owner': [l3_const.DEVICE_OWNER_ROUTER_INTF]}
|
for rp in router_db.attached_ports.filter_by(port_type=old_owner):
|
||||||
ports = self._core_plugin.get_ports(admin_ctx, filters=filters)
|
rp.port_type = new_owner
|
||||||
for p in ports:
|
rp.port.device_owner = new_owner
|
||||||
port_db = self._core_plugin._get_port(admin_ctx, p['id'])
|
|
||||||
port_db.update({'device_owner': DEVICE_OWNER_DVR_INTERFACE})
|
|
||||||
|
|
||||||
def _update_router_db(self, context, router_id, data, gw_info):
|
def _update_router_db(self, context, router_id, data, gw_info):
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
@ -119,7 +117,7 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
router, new_network)
|
router, new_network)
|
||||||
if router.extra_attributes.distributed and router.gw_port:
|
if router.extra_attributes.distributed and router.gw_port:
|
||||||
snat_p_list = self.create_snat_intf_ports_if_not_exists(
|
snat_p_list = self.create_snat_intf_ports_if_not_exists(
|
||||||
context.elevated(), router['id'])
|
context.elevated(), router)
|
||||||
if not snat_p_list:
|
if not snat_p_list:
|
||||||
LOG.debug("SNAT interface ports not created: %s", snat_p_list)
|
LOG.debug("SNAT interface ports not created: %s", snat_p_list)
|
||||||
|
|
||||||
@ -134,12 +132,15 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
self)._get_device_owner(context, router)
|
self)._get_device_owner(context, router)
|
||||||
|
|
||||||
def _get_interface_ports_for_network(self, context, network_id):
|
def _get_interface_ports_for_network(self, context, network_id):
|
||||||
router_intf_qry = (context.session.query(models_v2.Port).
|
router_intf_qry = context.session.query(l3_db.RouterPort)
|
||||||
filter_by(network_id=network_id))
|
router_intf_qry = router_intf_qry.join(models_v2.Port)
|
||||||
return (router_intf_qry.
|
|
||||||
filter(models_v2.Port.device_owner.in_(
|
return router_intf_qry.filter(
|
||||||
[l3_const.DEVICE_OWNER_ROUTER_INTF,
|
models_v2.Port.network_id == network_id,
|
||||||
DEVICE_OWNER_DVR_INTERFACE])))
|
l3_db.RouterPort.port_type.in_(
|
||||||
|
[l3_const.DEVICE_OWNER_ROUTER_INTF, DEVICE_OWNER_DVR_INTERFACE]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
|
def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
|
||||||
previous_router_id = floatingip_db.router_id
|
previous_router_id = floatingip_db.router_id
|
||||||
@ -208,14 +209,22 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
|
|
||||||
if add_by_port:
|
if add_by_port:
|
||||||
port = self._add_interface_by_port(
|
port = self._add_interface_by_port(
|
||||||
context, router_id, interface_info['port_id'], device_owner)
|
context, router, interface_info['port_id'], device_owner)
|
||||||
elif add_by_sub:
|
elif add_by_sub:
|
||||||
port = self._add_interface_by_subnet(
|
port = self._add_interface_by_subnet(
|
||||||
context, router_id, interface_info['subnet_id'], device_owner)
|
context, router, interface_info['subnet_id'], device_owner)
|
||||||
|
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
router_port = l3_db.RouterPort(
|
||||||
|
port_id=port['id'],
|
||||||
|
router_id=router.id,
|
||||||
|
port_type=device_owner
|
||||||
|
)
|
||||||
|
context.session.add(router_port)
|
||||||
|
|
||||||
if router.extra_attributes.distributed and router.gw_port:
|
if router.extra_attributes.distributed and router.gw_port:
|
||||||
self.add_csnat_router_interface_port(
|
self.add_csnat_router_interface_port(
|
||||||
context.elevated(), router_id, port['network_id'],
|
context.elevated(), router, port['network_id'],
|
||||||
port['fixed_ips'][0]['subnet_id'])
|
port['fixed_ips'][0]['subnet_id'])
|
||||||
|
|
||||||
router_interface_info = self._make_router_interface_info(
|
router_interface_info = self._make_router_interface_info(
|
||||||
@ -257,9 +266,16 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
"""Query router interfaces that relate to list of router_ids."""
|
"""Query router interfaces that relate to list of router_ids."""
|
||||||
if not router_ids:
|
if not router_ids:
|
||||||
return []
|
return []
|
||||||
filters = {'device_id': router_ids,
|
qry = context.session.query(l3_db.RouterPort)
|
||||||
'device_owner': [DEVICE_OWNER_DVR_SNAT]}
|
qry = qry.filter(
|
||||||
interfaces = self._core_plugin.get_ports(context, filters)
|
l3_db.RouterPort.router_id.in_(router_ids),
|
||||||
|
l3_db.RouterPort.port_type == DEVICE_OWNER_DVR_SNAT
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO(markmcclain): This is suboptimal but was left to reduce
|
||||||
|
# changeset size since it is late in cycle
|
||||||
|
ports = [rp.port.id for rp in qry]
|
||||||
|
interfaces = self._core_plugin.get_ports(context, {'id': ports})
|
||||||
LOG.debug("Return the SNAT ports: %s", interfaces)
|
LOG.debug("Return the SNAT ports: %s", interfaces)
|
||||||
if interfaces:
|
if interfaces:
|
||||||
self._populate_subnet_for_ports(context, interfaces)
|
self._populate_subnet_for_ports(context, interfaces)
|
||||||
@ -447,12 +463,19 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
|
|
||||||
def get_snat_interface_ports_for_router(self, context, router_id):
|
def get_snat_interface_ports_for_router(self, context, router_id):
|
||||||
"""Return all existing snat_router_interface ports."""
|
"""Return all existing snat_router_interface ports."""
|
||||||
filters = {'device_id': [router_id],
|
# TODO(markmcclain): This is suboptimal but was left to reduce
|
||||||
'device_owner': [DEVICE_OWNER_DVR_SNAT]}
|
# changeset size since it is late in cycle
|
||||||
return self._core_plugin.get_ports(context, filters)
|
qry = context.session.query(l3_db.RouterPort)
|
||||||
|
qry = qry.filter_by(
|
||||||
|
router_id=router_id,
|
||||||
|
port_type=DEVICE_OWNER_DVR_SNAT
|
||||||
|
)
|
||||||
|
|
||||||
|
ports = [rp.port.id for rp in qry]
|
||||||
|
return self._core_plugin.get_ports(context, {'id': ports})
|
||||||
|
|
||||||
def add_csnat_router_interface_port(
|
def add_csnat_router_interface_port(
|
||||||
self, context, router_id, network_id, subnet_id, do_pop=True):
|
self, context, router, network_id, subnet_id, do_pop=True):
|
||||||
"""Add SNAT interface to the specified router and subnet."""
|
"""Add SNAT interface to the specified router and subnet."""
|
||||||
snat_port = self._core_plugin.create_port(
|
snat_port = self._core_plugin.create_port(
|
||||||
context,
|
context,
|
||||||
@ -460,19 +483,27 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
'network_id': network_id,
|
'network_id': network_id,
|
||||||
'mac_address': attributes.ATTR_NOT_SPECIFIED,
|
'mac_address': attributes.ATTR_NOT_SPECIFIED,
|
||||||
'fixed_ips': [{'subnet_id': subnet_id}],
|
'fixed_ips': [{'subnet_id': subnet_id}],
|
||||||
'device_id': router_id,
|
'device_id': router.id,
|
||||||
'device_owner': DEVICE_OWNER_DVR_SNAT,
|
'device_owner': DEVICE_OWNER_DVR_SNAT,
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'name': ''}})
|
'name': ''}})
|
||||||
if not snat_port:
|
if not snat_port:
|
||||||
msg = _("Unable to create the SNAT Interface Port")
|
msg = _("Unable to create the SNAT Interface Port")
|
||||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
elif do_pop:
|
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
router_port = l3_db.RouterPort(
|
||||||
|
port_id=snat_port['id'],
|
||||||
|
router_id=router.id,
|
||||||
|
port_type=DEVICE_OWNER_DVR_SNAT
|
||||||
|
)
|
||||||
|
context.session.add(router_port)
|
||||||
|
|
||||||
|
if do_pop:
|
||||||
return self._populate_subnet_for_ports(context, [snat_port])
|
return self._populate_subnet_for_ports(context, [snat_port])
|
||||||
return snat_port
|
return snat_port
|
||||||
|
|
||||||
def create_snat_intf_ports_if_not_exists(
|
def create_snat_intf_ports_if_not_exists(self, context, router):
|
||||||
self, context, router_id):
|
|
||||||
"""Function to return the snat interface port list.
|
"""Function to return the snat interface port list.
|
||||||
|
|
||||||
This function will return the snat interface port list
|
This function will return the snat interface port list
|
||||||
@ -480,24 +511,27 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
new ports and then return the list.
|
new ports and then return the list.
|
||||||
"""
|
"""
|
||||||
port_list = self.get_snat_interface_ports_for_router(
|
port_list = self.get_snat_interface_ports_for_router(
|
||||||
context, router_id)
|
context, router.id)
|
||||||
if port_list:
|
if port_list:
|
||||||
self._populate_subnet_for_ports(context, port_list)
|
self._populate_subnet_for_ports(context, port_list)
|
||||||
return port_list
|
return port_list
|
||||||
port_list = []
|
port_list = []
|
||||||
filters = {
|
|
||||||
'device_id': [router_id],
|
int_ports = (
|
||||||
'device_owner': [DEVICE_OWNER_DVR_INTERFACE]}
|
rp.port for rp in
|
||||||
int_ports = self._core_plugin.get_ports(context, filters)
|
router.attached_ports.filter_by(
|
||||||
|
port_type=DEVICE_OWNER_DVR_INTERFACE
|
||||||
|
)
|
||||||
|
)
|
||||||
LOG.info(_('SNAT interface port list does not exist,'
|
LOG.info(_('SNAT interface port list does not exist,'
|
||||||
' so create one: %s'), port_list)
|
' so create one: %s'), port_list)
|
||||||
for intf in int_ports:
|
for intf in int_ports:
|
||||||
if intf.get('fixed_ips'):
|
if intf.fixed_ips:
|
||||||
# Passing the subnet for the port to make sure the IP's
|
# Passing the subnet for the port to make sure the IP's
|
||||||
# are assigned on the right subnet if multiple subnet
|
# are assigned on the right subnet if multiple subnet
|
||||||
# exists
|
# exists
|
||||||
snat_port = self.add_csnat_router_interface_port(
|
snat_port = self.add_csnat_router_interface_port(
|
||||||
context, router_id, intf['network_id'],
|
context, router, intf['network_id'],
|
||||||
intf['fixed_ips'][0]['subnet_id'], do_pop=False)
|
intf['fixed_ips'][0]['subnet_id'], do_pop=False)
|
||||||
port_list.append(snat_port)
|
port_list.append(snat_port)
|
||||||
if port_list:
|
if port_list:
|
||||||
@ -539,11 +573,18 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||||||
# Each csnat router interface port is associated
|
# Each csnat router interface port is associated
|
||||||
# with a subnet, so we need to pass the subnet id to
|
# with a subnet, so we need to pass the subnet id to
|
||||||
# delete the right ports.
|
# delete the right ports.
|
||||||
device_filter = {
|
|
||||||
'device_id': [router['id']],
|
# TODO(markmcclain): This is suboptimal but was left to reduce
|
||||||
'device_owner': [DEVICE_OWNER_DVR_SNAT]}
|
# changeset size since it is late in cycle
|
||||||
|
ports = (
|
||||||
|
rp.port.id for rp in
|
||||||
|
router.attached_ports.filter_by(port_type=DEVICE_OWNER_DVR_SNAT)
|
||||||
|
)
|
||||||
|
|
||||||
c_snat_ports = self._core_plugin.get_ports(
|
c_snat_ports = self._core_plugin.get_ports(
|
||||||
context, filters=device_filter)
|
context,
|
||||||
|
filters={'id': ports}
|
||||||
|
)
|
||||||
for p in c_snat_ports:
|
for p in c_snat_ports:
|
||||||
if subnet_id is None:
|
if subnet_id is None:
|
||||||
self._core_plugin.delete_port(context,
|
self._core_plugin.delete_port(context,
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
# Copyright 2014 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""add router port relationship
|
||||||
|
|
||||||
|
Revision ID: 544673ac99ab
|
||||||
|
Revises: 1680e1f0c4dc
|
||||||
|
Create Date: 2014-01-14 11:58:13.754747
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '544673ac99ab'
|
||||||
|
down_revision = '1680e1f0c4dc'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
SQL_STATEMENT = (
|
||||||
|
"insert into routerports "
|
||||||
|
"select "
|
||||||
|
"p.device_id as router_id, p.id as port_id, p.device_owner as port_type "
|
||||||
|
"from ports p join routers r on (p.device_id=r.id) "
|
||||||
|
"where "
|
||||||
|
"(r.tenant_id=p.tenant_id AND p.device_owner='network:router_interface') "
|
||||||
|
"OR (p.tenant_id='' AND p.device_owner='network:router_gateway')"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table(
|
||||||
|
'routerports',
|
||||||
|
sa.Column('router_id', sa.String(length=36), nullable=False),
|
||||||
|
sa.Column('port_id', sa.String(length=36), nullable=False),
|
||||||
|
sa.Column('port_type', sa.String(length=255)),
|
||||||
|
sa.PrimaryKeyConstraint('router_id', 'port_id'),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
['router_id'],
|
||||||
|
['routers.id'],
|
||||||
|
ondelete='CASCADE'
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
['port_id'],
|
||||||
|
['ports.id'],
|
||||||
|
ondelete='CASCADE'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
op.execute(SQL_STATEMENT)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table('routerports')
|
@ -1 +1 @@
|
|||||||
1680e1f0c4dc
|
544673ac99ab
|
||||||
|
Loading…
x
Reference in New Issue
Block a user