armando-migliaccio 2516c13654 Fix 'null' response on router-interface-remove operation
To avoid a 'null' response body, the delete operation returns a response
that is consistent with its add counterpart, i.e. we return the router
id with details about the interface being affected by the operation, as
well as the tenant id.

A unit test is added to ensure that the right body is returned and minor
adjustments have been made to the plugins affected by the change.

Long-term, a delete operation should really return 204 w/o a body, but
this requires some major rework of the WSGI handling within Quantum.
This is an interim solution that deals with an 'ugly' response body,
whilst keeping backward compatibility.

Fixes bug 1173284

Change-Id: Icaab87ad0c8561c0690c8f0a14db815d8886bc71
2013-04-30 16:50:56 -07:00

1093 lines
47 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (C) 2012 Midokura Japan K.K.
# Copyright (C) 2013 Midokura PTE LTD
# 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.
#
# @author: Takaaki Suzuki, Midokura Japan KK
# @author: Tomoe Sugihara, Midokura Japan KK
# @author: Ryu Ishimoto, Midokura Japan KK
from midonetclient import api
from oslo.config import cfg
from webob import exc as w_exc
from quantum.common import exceptions as q_exc
from quantum.db import api as db
from quantum.db import db_base_plugin_v2
from quantum.db import l3_db
from quantum.db import models_v2
from quantum.db import securitygroups_db
from quantum.extensions import securitygroup as ext_sg
from quantum.openstack.common import log as logging
from quantum.plugins.midonet import config # noqa
from quantum.plugins.midonet import midonet_lib
LOG = logging.getLogger(__name__)
OS_TENANT_ROUTER_RULE_KEY = 'OS_TENANT_ROUTER_RULE'
OS_FLOATING_IP_RULE_KEY = 'OS_FLOATING_IP'
SNAT_RULE = 'SNAT'
SNAT_RULE_PROPERTY = {OS_TENANT_ROUTER_RULE_KEY: SNAT_RULE}
class MidonetResourceNotFound(q_exc.NotFound):
message = _('MidoNet %(resource_type)s %(id)s could not be found')
class MidonetPluginException(q_exc.QuantumException):
message = _("%(msg)s")
class MidonetPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
l3_db.L3_NAT_db_mixin,
securitygroups_db.SecurityGroupDbMixin):
supported_extension_aliases = ['router', 'security-group']
def __init__(self):
# Read config values
midonet_conf = cfg.CONF.MIDONET
midonet_uri = midonet_conf.midonet_uri
admin_user = midonet_conf.username
admin_pass = midonet_conf.password
admin_project_id = midonet_conf.project_id
provider_router_id = midonet_conf.provider_router_id
metadata_router_id = midonet_conf.metadata_router_id
mode = midonet_conf.mode
self.mido_api = api.MidonetApi(midonet_uri, admin_user,
admin_pass,
project_id=admin_project_id)
# get MidoNet provider router and metadata router
if provider_router_id and metadata_router_id:
self.provider_router = self.mido_api.get_router(provider_router_id)
self.metadata_router = self.mido_api.get_router(metadata_router_id)
# for dev purpose only
elif mode == 'dev':
msg = _('No provider router and metadata device ids found. '
'But skipping because running in dev env.')
LOG.debug(msg)
else:
msg = _('provider_router_id and metadata_router_id '
'should be configured in the plugin config file')
LOG.exception(msg)
raise MidonetPluginException(msg=msg)
self.chain_manager = midonet_lib.ChainManager(self.mido_api)
self.pg_manager = midonet_lib.PortGroupManager(self.mido_api)
self.rule_manager = midonet_lib.RuleManager(self.mido_api)
db.configure_db()
def create_subnet(self, context, subnet):
"""Create Quantum subnet.
Creates a Quantum subnet and a DHCP entry in MidoNet bridge.
"""
LOG.debug(_("MidonetPluginV2.create_subnet called: subnet=%r"), subnet)
if subnet['subnet']['ip_version'] == 6:
raise q_exc.NotImplementedError(
_("MidoNet doesn't support IPv6."))
net = super(MidonetPluginV2, self).get_network(
context, subnet['subnet']['network_id'], fields=None)
if net['subnets']:
raise q_exc.NotImplementedError(
_("MidoNet doesn't support multiple subnets "
"on the same network."))
session = context.session
with session.begin(subtransactions=True):
sn_entry = super(MidonetPluginV2, self).create_subnet(context,
subnet)
try:
bridge = self.mido_api.get_bridge(sn_entry['network_id'])
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge',
id=sn_entry['network_id'])
gateway_ip = subnet['subnet']['gateway_ip']
network_address, prefix = subnet['subnet']['cidr'].split('/')
bridge.add_dhcp_subnet().default_gateway(gateway_ip).subnet_prefix(
network_address).subnet_length(prefix).create()
# If the network is external, link the bridge to MidoNet provider
# router
self._extend_network_dict_l3(context, net)
if net['router:external']:
gateway_ip = sn_entry['gateway_ip']
network_address, length = sn_entry['cidr'].split('/')
# create a interior port in the MidoNet provider router
in_port = self.provider_router.add_interior_port()
pr_port = in_port.port_address(gateway_ip).network_address(
network_address).network_length(length).create()
# create a interior port in the bridge, then link
# it to the provider router.
br_port = bridge.add_interior_port().create()
pr_port.link(br_port.get_id())
# add a route for the subnet in the provider router
self.provider_router.add_route().type(
'Normal').src_network_addr('0.0.0.0').src_network_length(
0).dst_network_addr(
network_address).dst_network_length(
length).weight(100).next_hop_port(
pr_port.get_id()).create()
LOG.debug(_("MidonetPluginV2.create_subnet exiting: sn_entry=%r"),
sn_entry)
return sn_entry
def get_subnet(self, context, id, fields=None):
"""Get Quantum subnet.
Retrieves a Quantum subnet record but also including the DHCP entry
data stored in MidoNet.
"""
LOG.debug(_("MidonetPluginV2.get_subnet called: id=%(id)s "
"fields=%(fields)s"), {'id': id, 'fields': fields})
qsubnet = super(MidonetPluginV2, self).get_subnet(context, id)
bridge_id = qsubnet['network_id']
try:
bridge = self.mido_api.get_bridge(bridge_id)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge',
id=bridge_id)
# get dhcp subnet data from MidoNet bridge.
dhcps = bridge.get_dhcp_subnets()
b_network_address = dhcps[0].get_subnet_prefix()
b_prefix = dhcps[0].get_subnet_length()
# Validate against quantum database.
network_address, prefix = qsubnet['cidr'].split('/')
if network_address != b_network_address or int(prefix) != b_prefix:
raise MidonetResourceNotFound(resource_type='DhcpSubnet',
id=qsubnet['cidr'])
LOG.debug(_("MidonetPluginV2.get_subnet exiting: qsubnet=%s"), qsubnet)
return qsubnet
def get_subnets(self, context, filters=None, fields=None):
"""List Quantum subnets.
Retrieves Quantum subnets with some fields populated by the data
stored in MidoNet.
"""
LOG.debug(_("MidonetPluginV2.get_subnets called: filters=%(filters)r, "
"fields=%(fields)r"),
{'filters': filters, 'fields': fields})
subnets = super(MidonetPluginV2, self).get_subnets(context, filters,
fields)
for sn in subnets:
if not 'network_id' in sn:
continue
try:
bridge = self.mido_api.get_bridge(sn['network_id'])
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge',
id=sn['network_id'])
# TODO(tomoe): dedupe this part.
# get dhcp subnet data from MidoNet bridge.
dhcps = bridge.get_dhcp_subnets()
b_network_address = dhcps[0].get_subnet_prefix()
b_prefix = dhcps[0].get_subnet_length()
# Validate against quantum database.
if sn.get('cidr'):
network_address, prefix = sn['cidr'].split('/')
if network_address != b_network_address or int(
prefix) != b_prefix:
raise MidonetResourceNotFound(resource_type='DhcpSubnet',
id=sn['cidr'])
LOG.debug(_("MidonetPluginV2.create_subnet exiting"))
return subnets
def delete_subnet(self, context, id):
"""Delete Quantum subnet.
Delete quantum network and its corresponding MidoNet bridge.
"""
LOG.debug(_("MidonetPluginV2.delete_subnet called: id=%s"), id)
subnet = super(MidonetPluginV2, self).get_subnet(context, id,
fields=None)
net = super(MidonetPluginV2, self).get_network(context,
subnet['network_id'],
fields=None)
bridge_id = subnet['network_id']
try:
bridge = self.mido_api.get_bridge(bridge_id)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge', id=bridge_id)
dhcp = bridge.get_dhcp_subnets()
dhcp[0].delete()
# If the network is external, clean up routes, links, ports.
self._extend_network_dict_l3(context, net)
if net['router:external']:
# Delete routes and unlink the router and the bridge.
routes = self.provider_router.get_routes()
bridge_ports_to_delete = []
for p in self.provider_router.get_peer_ports():
if p.get_device_id() == bridge.get_id():
bridge_ports_to_delete.append(p)
for p in bridge.get_peer_ports():
if p.get_device_id() == self.provider_router.get_id():
# delete the routes going to the brdge
for r in routes:
if r.get_next_hop_port() == p.get_id():
r.delete()
p.unlink()
p.delete()
# delete bridge port
map(lambda x: x.delete(), bridge_ports_to_delete)
super(MidonetPluginV2, self).delete_subnet(context, id)
LOG.debug(_("MidonetPluginV2.delete_subnet exiting"))
def create_network(self, context, network):
"""Create Quantum network.
Create a new Quantum network and its corresponding MidoNet bridge.
"""
LOG.debug(_('MidonetPluginV2.create_network called: network=%r'),
network)
if network['network']['admin_state_up'] is False:
LOG.warning(_('Ignoring admin_state_up=False for network=%r'
'Overriding with True'), network)
network['network']['admin_state_up'] = True
tenant_id = self._get_tenant_id_for_create(context, network['network'])
self._ensure_default_security_group(context, tenant_id)
session = context.session
with session.begin(subtransactions=True):
bridge = self.mido_api.add_bridge().name(
network['network']['name']).tenant_id(tenant_id).create()
# Set MidoNet bridge ID to the quantum DB entry
network['network']['id'] = bridge.get_id()
net = super(MidonetPluginV2, self).create_network(context, network)
# to handle l3 related data in DB
self._process_l3_create(context, network['network'], net['id'])
self._extend_network_dict_l3(context, net)
LOG.debug(_("MidonetPluginV2.create_network exiting: net=%r"), net)
return net
def update_network(self, context, id, network):
"""Update Quantum network.
Update an existing Quantum network and its corresponding MidoNet
bridge.
"""
LOG.debug(_("MidonetPluginV2.update_network called: id=%(id)r, "
"network=%(network)r"), {'id': id, 'network': network})
# Reject admin_state_up=False
if network['network'].get('admin_state_up') and network['network'][
'admin_state_up'] is False:
raise q_exc.NotImplementedError(_('admin_state_up=False '
'networks are not '
'supported.'))
session = context.session
with session.begin(subtransactions=True):
net = super(MidonetPluginV2, self).update_network(
context, id, network)
try:
bridge = self.mido_api.get_bridge(id)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge', id=id)
bridge.name(net['name']).update()
self._extend_network_dict_l3(context, net)
LOG.debug(_("MidonetPluginV2.update_network exiting: net=%r"), net)
return net
def get_network(self, context, id, fields=None):
"""Get Quantum network.
Retrieves a Quantum network and its corresponding MidoNet bridge.
"""
LOG.debug(_("MidonetPluginV2.get_network called: id=%(id)r, "
"fields=%(fields)r"), {'id': id, 'fields': fields})
# NOTE: Get network data with all fields (fields=None) for
# _extend_network_dict_l3() method, which needs 'id' field
qnet = super(MidonetPluginV2, self).get_network(context, id, None)
try:
self.mido_api.get_bridge(id)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge', id=id)
self._extend_network_dict_l3(context, qnet)
LOG.debug(_("MidonetPluginV2.get_network exiting: qnet=%r"), qnet)
return self._fields(qnet, fields)
def get_networks(self, context, filters=None, fields=None):
"""List quantum networks and verify that all exist in MidoNet."""
LOG.debug(_("MidonetPluginV2.get_networks called: "
"filters=%(filters)r, fields=%(fields)r"),
{'filters': filters, 'fields': fields})
# NOTE: Get network data with all fields (fields=None) for
# _extend_network_dict_l3() method, which needs 'id' field
qnets = super(MidonetPluginV2, self).get_networks(context, filters,
None)
self.mido_api.get_bridges({'tenant_id': context.tenant_id})
for n in qnets:
try:
self.mido_api.get_bridge(n['id'])
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge',
id=n['id'])
self._extend_network_dict_l3(context, n)
return [self._fields(net, fields) for net in qnets]
def delete_network(self, context, id):
"""Delete a network and its corresponding MidoNet bridge."""
LOG.debug(_("MidonetPluginV2.delete_network called: id=%r"), id)
self.mido_api.get_bridge(id).delete()
try:
super(MidonetPluginV2, self).delete_network(context, id)
except Exception:
LOG.error(_('Failed to delete quantum db, while Midonet bridge=%r'
'had been deleted'), id)
raise
def create_port(self, context, port):
"""Create a L2 port in Quantum/MidoNet."""
LOG.debug(_("MidonetPluginV2.create_port called: port=%r"), port)
is_compute_interface = False
port_data = port['port']
# get the bridge and create a port on it.
try:
bridge = self.mido_api.get_bridge(port_data['network_id'])
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge',
id=port_data['network_id'])
device_owner = port_data['device_owner']
if device_owner.startswith('compute:') or device_owner is '':
is_compute_interface = True
bridge_port = bridge.add_exterior_port().create()
elif device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF:
bridge_port = bridge.add_interior_port().create()
elif (device_owner == l3_db.DEVICE_OWNER_ROUTER_GW or
device_owner == l3_db.DEVICE_OWNER_FLOATINGIP):
# This is a dummy port to make l3_db happy.
# This will not be used in MidoNet
bridge_port = bridge.add_interior_port().create()
if bridge_port:
# set midonet port id to quantum port id and create a DB record.
port_data['id'] = bridge_port.get_id()
session = context.session
with session.begin(subtransactions=True):
port_db_entry = super(MidonetPluginV2,
self).create_port(context, port)
self._extend_port_dict_security_group(context, port_db_entry)
if is_compute_interface:
# Create a DHCP entry if needed.
if 'ip_address' in (port_db_entry['fixed_ips'] or [{}])[0]:
# get ip and mac from DB record, assuming one IP address
# at most since we only support one subnet per network now.
fixed_ip = port_db_entry['fixed_ips'][0]['ip_address']
mac = port_db_entry['mac_address']
# create dhcp host entry under the bridge.
dhcp_subnets = bridge.get_dhcp_subnets()
if dhcp_subnets:
dhcp_subnets[0].add_dhcp_host().ip_addr(
fixed_ip).mac_addr(mac).create()
LOG.debug(_("MidonetPluginV2.create_port exiting: port_db_entry=%r"),
port_db_entry)
return port_db_entry
def update_port(self, context, id, port):
"""Update port."""
LOG.debug(_("MidonetPluginV2.update_port called: id=%(id)s "
"port=%(port)r"), {'id': id, 'port': port})
return super(MidonetPluginV2, self).update_port(context, id, port)
def get_port(self, context, id, fields=None):
"""Retrieve port."""
LOG.debug(_("MidonetPluginV2.get_port called: id=%(id)s "
"fields=%(fields)r"), {'id': id, 'fields': fields})
# get the quantum port from DB.
port_db_entry = super(MidonetPluginV2, self).get_port(context,
id, fields)
self._extend_port_dict_security_group(context, port_db_entry)
# verify that corresponding port exists in MidoNet.
try:
self.mido_api.get_port(id)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Port', id=id)
LOG.debug(_("MidonetPluginV2.get_port exiting: port_db_entry=%r"),
port_db_entry)
return port_db_entry
def get_ports(self, context, filters=None, fields=None):
"""List quantum ports and verify that they exist in MidoNet."""
LOG.debug(_("MidonetPluginV2.get_ports called: filters=%(filters)s "
"fields=%(fields)r"),
{'filters': filters, 'fields': fields})
ports_db_entry = super(MidonetPluginV2, self).get_ports(context,
filters,
fields)
if ports_db_entry:
try:
for port in ports_db_entry:
self.mido_api.get_port(port['id'])
self._extend_port_dict_security_group(context, port)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Port',
id=port['id'])
return ports_db_entry
def delete_port(self, context, id, l3_port_check=True):
"""Delete a quantum port and corresponding MidoNet bridge port."""
LOG.debug(_("MidonetPluginV2.delete_port called: id=%(id)s "
"l3_port_check=%(l3_port_check)r"),
{'id': id, 'l3_port_check': l3_port_check})
# if needed, check to see if this is a port owned by
# and l3-router. If so, we should prevent deletion.
if l3_port_check:
self.prevent_l3_port_deletion(context, id)
session = context.session
with session.begin(subtransactions=True):
port_db_entry = super(MidonetPluginV2, self).get_port(context,
id, None)
bridge = self.mido_api.get_bridge(port_db_entry['network_id'])
# Clean up dhcp host entry if needed.
if 'ip_address' in (port_db_entry['fixed_ips'] or [{}])[0]:
# get ip and mac from DB record.
ip = port_db_entry['fixed_ips'][0]['ip_address']
mac = port_db_entry['mac_address']
# create dhcp host entry under the bridge.
dhcp_subnets = bridge.get_dhcp_subnets()
if dhcp_subnets:
for dh in dhcp_subnets[0].get_dhcp_hosts():
if dh.get_mac_addr() == mac and dh.get_ip_addr() == ip:
dh.delete()
self.mido_api.get_port(id).delete()
return super(MidonetPluginV2, self).delete_port(context, id)
#
# L3 APIs.
#
def create_router(self, context, router):
LOG.debug(_("MidonetPluginV2.create_router called: router=%r"), router)
if router['router']['admin_state_up'] is False:
LOG.warning(_('Ignoring admin_state_up=False for router=%r. '
'Overriding with True'), router)
router['router']['admin_state_up'] = True
tenant_id = self._get_tenant_id_for_create(context, router['router'])
session = context.session
with session.begin(subtransactions=True):
mrouter = self.mido_api.add_router().name(
router['router']['name']).tenant_id(tenant_id).create()
qrouter = super(MidonetPluginV2, self).create_router(context,
router)
chains = self.chain_manager.create_router_chains(tenant_id,
mrouter.get_id())
# set chains to in/out filters
mrouter.inbound_filter_id(
chains['in'].get_id()).outbound_filter_id(
chains['out'].get_id()).update()
# get entry from the DB and update 'id' with MidoNet router id.
qrouter_entry = self._get_router(context, qrouter['id'])
qrouter['id'] = mrouter.get_id()
qrouter_entry.update(qrouter)
# link to metadata router
in_port = self.metadata_router.add_interior_port()
mdr_port = in_port.network_address('169.254.255.0').network_length(
30).port_address('169.254.255.1').create()
tr_port = mrouter.add_interior_port().network_address(
'169.254.255.0').network_length(30).port_address(
'169.254.255.2').create()
mdr_port.link(tr_port.get_id())
# forward metadata traffic to metadata router
mrouter.add_route().type('Normal').src_network_addr(
'0.0.0.0').src_network_length(0).dst_network_addr(
'169.254.169.254').dst_network_length(32).weight(
100).next_hop_port(tr_port.get_id()).create()
LOG.debug(_("MidonetPluginV2.create_router exiting: qrouter=%r"),
qrouter)
return qrouter
def update_router(self, context, id, router):
LOG.debug(_("MidonetPluginV2.update_router called: id=%(id)s "
"router=%(router)r"), router)
if router['router'].get('admin_state_up') is False:
raise q_exc.NotImplementedError(_('admin_state_up=False '
'routers are not '
'supported.'))
op_gateway_set = False
op_gateway_clear = False
# figure out which operation it is in
if ('external_gateway_info' in router['router'] and
'network_id' in router['router']['external_gateway_info']):
op_gateway_set = True
elif ('external_gateway_info' in router['router'] and
router['router']['external_gateway_info'] == {}):
op_gateway_clear = True
qports = super(MidonetPluginV2, self).get_ports(
context, {'device_id': [id],
'device_owner': ['network:router_gateway']})
assert len(qports) == 1
qport = qports[0]
snat_ip = qport['fixed_ips'][0]['ip_address']
qport['network_id']
session = context.session
with session.begin(subtransactions=True):
qrouter = super(MidonetPluginV2, self).update_router(context, id,
router)
changed_name = router['router'].get('name')
if changed_name:
self.mido_api.get_router(id).name(changed_name).update()
tenant_router = self.mido_api.get_router(id)
if op_gateway_set:
# find a qport with the network_id for the router
qports = super(MidonetPluginV2, self).get_ports(
context, {'device_id': [id],
'device_owner': ['network:router_gateway']})
assert len(qports) == 1
qport = qports[0]
snat_ip = qport['fixed_ips'][0]['ip_address']
in_port = self.provider_router.add_interior_port()
pr_port = in_port.network_address(
'169.254.255.0').network_length(30).port_address(
'169.254.255.1').create()
# Create a port in the tenant router
tr_port = tenant_router.add_interior_port().network_address(
'169.254.255.0').network_length(30).port_address(
'169.254.255.2').create()
# Link them
pr_port.link(tr_port.get_id())
# Add a route for snat_ip to bring it down to tenant
self.provider_router.add_route().type(
'Normal').src_network_addr('0.0.0.0').src_network_length(
0).dst_network_addr(snat_ip).dst_network_length(
32).weight(100).next_hop_port(
pr_port.get_id()).create()
# Add default route to uplink in the tenant router
tenant_router.add_route().type('Normal').src_network_addr(
'0.0.0.0').src_network_length(0).dst_network_addr(
'0.0.0.0').dst_network_length(0).weight(
100).next_hop_port(tr_port.get_id()).create()
# ADD SNAT(masquerade) rules
chains = self.chain_manager.get_router_chains(
tenant_router.get_tenant_id(), tenant_router.get_id())
chains['in'].add_rule().nw_dst_address(snat_ip).nw_dst_length(
32).type('rev_snat').flow_action('accept').in_ports(
[tr_port.get_id()]).properties(
SNAT_RULE_PROPERTY).position(1).create()
nat_targets = []
nat_targets.append(
{'addressFrom': snat_ip, 'addressTo': snat_ip,
'portFrom': 1, 'portTo': 65535})
chains['out'].add_rule().type('snat').flow_action(
'accept').nat_targets(nat_targets).out_ports(
[tr_port.get_id()]).properties(
SNAT_RULE_PROPERTY).position(1).create()
if op_gateway_clear:
# delete the port that is connected to provider router
for p in tenant_router.get_ports():
if p.get_port_address() == '169.254.255.2':
peer_port_id = p.get_peer_id()
p.unlink()
self.mido_api.get_port(peer_port_id).delete()
p.delete()
# delete default route
for r in tenant_router.get_routes():
if (r.get_dst_network_addr() == '0.0.0.0' and
r.get_dst_network_length() == 0):
r.delete()
# delete SNAT(masquerade) rules
chains = self.chain_manager.get_router_chains(
tenant_router.get_tenant_id(),
tenant_router.get_id())
for r in chains['in'].get_rules():
if OS_TENANT_ROUTER_RULE_KEY in r.get_properties():
if r.get_properties()[
OS_TENANT_ROUTER_RULE_KEY] == SNAT_RULE:
r.delete()
for r in chains['out'].get_rules():
if OS_TENANT_ROUTER_RULE_KEY in r.get_properties():
if r.get_properties()[
OS_TENANT_ROUTER_RULE_KEY] == SNAT_RULE:
r.delete()
LOG.debug(_("MidonetPluginV2.update_router exiting: qrouter=%r"),
qrouter)
return qrouter
def delete_router(self, context, id):
LOG.debug(_("MidonetPluginV2.delete_router called: id=%s"), id)
mrouter = self.mido_api.get_router(id)
tenant_id = mrouter.get_tenant_id()
# unlink from metadata router and delete the interior ports
# that connect metadata router and this router.
for pp in self.metadata_router.get_peer_ports():
if pp.get_device_id() == mrouter.get_id():
mdr_port = self.mido_api.get_port(pp.get_peer_id())
pp.unlink()
pp.delete()
mdr_port.delete()
# delete corresponding chains
chains = self.chain_manager.get_router_chains(tenant_id,
mrouter.get_id())
chains['in'].delete()
chains['out'].delete()
# delete the router
mrouter.delete()
result = super(MidonetPluginV2, self).delete_router(context, id)
LOG.debug(_("MidonetPluginV2.delete_router exiting: result=%s"),
result)
return result
def get_router(self, context, id, fields=None):
LOG.debug(_("MidonetPluginV2.get_router called: id=%(id)s "
"fields=%(fields)r"), {'id': id, 'fields': fields})
qrouter = super(MidonetPluginV2, self).get_router(context, id, fields)
try:
self.mido_api.get_router(id)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Router', id=id)
LOG.debug(_("MidonetPluginV2.get_router exiting: qrouter=%r"),
qrouter)
return qrouter
def get_routers(self, context, filters=None, fields=None):
LOG.debug(_("MidonetPluginV2.get_routers called: filters=%(filters)s "
"fields=%(fields)r"),
{'filters': filters, 'fields': fields})
qrouters = super(MidonetPluginV2, self).get_routers(
context, filters, fields)
for qr in qrouters:
try:
self.mido_api.get_router(qr['id'])
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Router',
id=qr['id'])
return qrouters
def add_router_interface(self, context, router_id, interface_info):
LOG.debug(_("MidonetPluginV2.add_router_interface called: "
"router_id=%(router_id)s "
"interface_info=%(interface_info)r"),
{'router_id': router_id, 'interface_info': interface_info})
qport = super(MidonetPluginV2, self).add_router_interface(
context, router_id, interface_info)
# TODO(tomoe): handle a case with 'port' in interface_info
if 'subnet_id' in interface_info:
subnet_id = interface_info['subnet_id']
subnet = self._get_subnet(context, subnet_id)
gateway_ip = subnet['gateway_ip']
network_address, length = subnet['cidr'].split('/')
# Link the router and the bridge port.
mrouter = self.mido_api.get_router(router_id)
mrouter_port = mrouter.add_interior_port().port_address(
gateway_ip).network_address(
network_address).network_length(length).create()
mbridge_port = self.mido_api.get_port(qport['port_id'])
mrouter_port.link(mbridge_port.get_id())
# Add a route entry to the subnet
mrouter.add_route().type('Normal').src_network_addr(
'0.0.0.0').src_network_length(0).dst_network_addr(
network_address).dst_network_length(length).weight(
100).next_hop_port(mrouter_port.get_id()).create()
# add a route for the subnet in metadata router; forward
# packets destined to the subnet to the tenant router
found = False
for pp in self.metadata_router.get_peer_ports():
if pp.get_device_id() == mrouter.get_id():
mdr_port_id = pp.get_peer_id()
found = True
assert found
self.metadata_router.add_route().type(
'Normal').src_network_addr('0.0.0.0').src_network_length(
0).dst_network_addr(network_address).dst_network_length(
length).weight(100).next_hop_port(mdr_port_id).create()
LOG.debug(_("MidonetPluginV2.add_router_interface exiting: "
"qport=%r"), qport)
return qport
def remove_router_interface(self, context, router_id, interface_info):
"""Remove interior router ports."""
LOG.debug(_("MidonetPluginV2.remove_router_interface called: "
"router_id=%(router_id)s "
"interface_info=%(interface_info)r"),
{'router_id': router_id, 'interface_info': interface_info})
if 'port_id' in interface_info:
mbridge_port = self.mido_api.get_port(interface_info['port_id'])
subnet_id = self.get_port(context,
interface_info['port_id']
)['fixed_ips'][0]['subnet_id']
subnet = self._get_subnet(context, subnet_id)
if 'subnet_id' in interface_info:
subnet_id = interface_info['subnet_id']
subnet = self._get_subnet(context, subnet_id)
network_id = subnet['network_id']
# find a quantum port for the network
rport_qry = context.session.query(models_v2.Port)
ports = rport_qry.filter_by(
device_id=router_id,
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF,
network_id=network_id)
network_port = None
for p in ports:
if p['fixed_ips'][0]['subnet_id'] == subnet_id:
network_port = p
break
assert network_port
mbridge_port = self.mido_api.get_port(network_port['id'])
# get network information from subnet data
network_addr, network_length = subnet['cidr'].split('/')
network_length = int(network_length)
# Unlink the router and the bridge.
mrouter = self.mido_api.get_router(router_id)
mrouter_port = self.mido_api.get_port(mbridge_port.get_peer_id())
mrouter_port.unlink()
# Delete the route for the subnet.
found = False
for r in mrouter.get_routes():
if r.get_next_hop_port() == mrouter_port.get_id():
r.delete()
found = True
#break # commented out due to issue#314
assert found
# delete the route for the subnet in the metadata router
found = False
for r in self.metadata_router.get_routes():
if (r.get_dst_network_addr() == network_addr and
r.get_dst_network_length() == network_length):
LOG.debug(_('Deleting route=%r ...'), r)
r.delete()
found = True
break
assert found
info = super(MidonetPluginV2, self).remove_router_interface(
context, router_id, interface_info)
LOG.debug(_("MidonetPluginV2.remove_router_interface exiting"))
return info
def update_floatingip(self, context, id, floatingip):
LOG.debug(_("MidonetPluginV2.update_floatingip called: id=%(id)s "
"floatingip=%(floatingip)s "),
{'id': id, 'floatingip': floatingip})
session = context.session
with session.begin(subtransactions=True):
if floatingip['floatingip']['port_id']:
fip = super(MidonetPluginV2, self).update_floatingip(
context, id, floatingip)
router_id = fip['router_id']
floating_address = fip['floating_ip_address']
fixed_address = fip['fixed_ip_address']
tenant_router = self.mido_api.get_router(router_id)
# find the provider router port that is connected to the tenant
# of the floating ip
for p in tenant_router.get_peer_ports():
if p.get_device_id() == self.provider_router.get_id():
pr_port = p
# get the tenant router port id connected to provider router
tr_port_id = pr_port.get_peer_id()
# add a route for the floating ip to bring it to the tenant
self.provider_router.add_route().type(
'Normal').src_network_addr('0.0.0.0').src_network_length(
0).dst_network_addr(
floating_address).dst_network_length(
32).weight(100).next_hop_port(
pr_port.get_id()).create()
chains = self.chain_manager.get_router_chains(fip['tenant_id'],
fip['router_id'])
# add dnat/snat rule pair for the floating ip
nat_targets = []
nat_targets.append(
{'addressFrom': fixed_address, 'addressTo': fixed_address,
'portFrom': 0, 'portTo': 0})
floating_property = {OS_FLOATING_IP_RULE_KEY: id}
chains['in'].add_rule().nw_dst_address(
floating_address).nw_dst_length(32).type(
'dnat').flow_action('accept').nat_targets(
nat_targets).in_ports([tr_port_id]).position(
1).properties(floating_property).create()
nat_targets = []
nat_targets.append(
{'addressFrom': floating_address,
'addressTo': floating_address,
'portFrom': 0,
'portTo': 0})
chains['out'].add_rule().nw_src_address(
fixed_address).nw_src_length(32).type(
'snat').flow_action('accept').nat_targets(
nat_targets).out_ports(
[tr_port_id]).position(1).properties(
floating_property).create()
# disassociate floating IP
elif floatingip['floatingip']['port_id'] is None:
fip = super(MidonetPluginV2, self).get_floatingip(context, id)
router_id = fip['router_id']
floating_address = fip['floating_ip_address']
fixed_address = fip['fixed_ip_address']
# delete the route for this floating ip
for r in self.provider_router.get_routes():
if (r.get_dst_network_addr() == floating_address and
r.get_dst_network_length() == 32):
r.delete()
# delete snat/dnat rule pair for this floating ip
chains = self.chain_manager.get_router_chains(fip['tenant_id'],
fip['router_id'])
LOG.debug(_('chains=%r'), chains)
for r in chains['in'].get_rules():
if OS_FLOATING_IP_RULE_KEY in r.get_properties():
if r.get_properties()[OS_FLOATING_IP_RULE_KEY] == id:
LOG.debug(_('deleting rule=%r'), r)
r.delete()
break
for r in chains['out'].get_rules():
if OS_FLOATING_IP_RULE_KEY in r.get_properties():
if r.get_properties()[OS_FLOATING_IP_RULE_KEY] == id:
LOG.debug(_('deleting rule=%r'), r)
r.delete()
break
super(MidonetPluginV2, self).update_floatingip(context, id,
floatingip)
LOG.debug(_("MidonetPluginV2.update_floating_ip exiting: fip=%s"), fip)
return fip
#
# Security groups supporting methods
#
def create_security_group(self, context, security_group, default_sg=False):
"""Create chains for Quantum security group."""
LOG.debug(_("MidonetPluginV2.create_security_group called: "
"security_group=%(security_group)s "
"default_sg=%(default_sg)s "),
{'security_group': security_group, 'default_sg': default_sg})
sg = security_group.get('security_group')
tenant_id = self._get_tenant_id_for_create(context, sg)
with context.session.begin(subtransactions=True):
sg_db_entry = super(MidonetPluginV2, self).create_security_group(
context, security_group, default_sg)
# Create MidoNet chains and portgroup for the SG
sg_id = sg_db_entry['id']
sg_name = sg_db_entry['name']
self.chain_manager.create_for_sg(tenant_id, sg_id, sg_name)
self.pg_manager.create(tenant_id, sg_id, sg_name)
LOG.debug(_("MidonetPluginV2.create_security_group exiting: "
"sg_db_entry=%r"), sg_db_entry)
return sg_db_entry
def delete_security_group(self, context, id):
"""Delete chains for Quantum security group."""
LOG.debug(_("MidonetPluginV2.delete_security_group called: id=%s"), id)
with context.session.begin(subtransactions=True):
sg_db_entry = super(MidonetPluginV2, self).get_security_group(
context, id)
if not sg_db_entry:
raise ext_sg.SecurityGroupNotFound(id=id)
sg_name = sg_db_entry['name']
sg_id = sg_db_entry['id']
tenant_id = sg_db_entry['tenant_id']
if sg_name == 'default':
raise ext_sg.SecurityGroupCannotRemoveDefault()
filters = {'security_group_id': [sg_id]}
if super(MidonetPluginV2, self)._get_port_security_group_bindings(
context, filters):
raise ext_sg.SecurityGroupInUse(id=sg_id)
# Delete MidoNet Chains and portgroup for the SG
self.chain_manager.delete_for_sg(tenant_id, sg_id, sg_name)
self.pg_manager.delete(tenant_id, sg_id, sg_name)
return super(MidonetPluginV2, self).delete_security_group(
context, id)
def get_security_groups(self, context, filters=None, fields=None):
LOG.debug(_("MidonetPluginV2.get_security_groups called: "
"filters=%(filters)r fields=%(fields)r"),
{'filters': filters, 'fields': fields})
return super(MidonetPluginV2, self).get_security_groups(
context, filters, fields)
def get_security_group(self, context, id, fields=None, tenant_id=None):
LOG.debug(_("MidonetPluginV2.get_security_group called: id=%(id)s "
"fields=%(fields)r tenant_id=%(tenant_id)s"),
{'id': id, 'fields': fields, 'tenant_id': tenant_id})
return super(MidonetPluginV2, self).get_security_group(context, id,
fields)
def create_security_group_rule(self, context, security_group_rule):
LOG.debug(_("MidonetPluginV2.create_security_group_rule called: "
"security_group_rule=%(security_group_rule)r"),
{'security_group_rule': security_group_rule})
with context.session.begin(subtransactions=True):
rule_db_entry = super(
MidonetPluginV2, self).create_security_group_rule(
context, security_group_rule)
self.rule_manager.create_for_sg_rule(rule_db_entry)
LOG.debug(_("MidonetPluginV2.create_security_group_rule exiting: "
"rule_db_entry=%r"), rule_db_entry)
return rule_db_entry
def delete_security_group_rule(self, context, sgrid):
LOG.debug(_("MidonetPluginV2.delete_security_group_rule called: "
"sgrid=%s"), sgrid)
with context.session.begin(subtransactions=True):
rule_db_entry = super(MidonetPluginV2,
self).get_security_group_rule(context, sgrid)
if not rule_db_entry:
raise ext_sg.SecurityGroupRuleNotFound(id=sgrid)
self.rule_manager.delete_for_sg_rule(rule_db_entry)
return super(MidonetPluginV2,
self).delete_security_group_rule(context, sgrid)
def get_security_group_rules(self, context, filters=None, fields=None):
LOG.debug(_("MidonetPluginV2.get_security_group_rules called: "
"filters=%(filters)r fields=%(fields)r"),
{'filters': filters, 'fields': fields})
return super(MidonetPluginV2, self).get_security_group_rules(
context, filters, fields)
def get_security_group_rule(self, context, id, fields=None):
LOG.debug(_("MidonetPluginV2.get_security_group_rule called: "
"id=%(id)s fields=%(fields)r"),
{'id': id, 'fields': fields})
return super(MidonetPluginV2, self).get_security_group_rule(
context, id, fields)