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:
Mark McClain 2014-10-08 18:49:20 +00:00
parent d217543ea0
commit 93012915a3
4 changed files with 249 additions and 96 deletions

View File

@ -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

View File

@ -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,

View File

@ -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')

View File

@ -1 +1 @@
1680e1f0c4dc 544673ac99ab