neutron/quantum/plugins/midonet/plugin.py

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)
# Caveat: port_db_entry is not a db model instance
sg_ids = self._get_security_groups_on_port(context, port)
self._process_port_create_security_group(context, port, sg_ids)
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)
# 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'])
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' and not context.is_admin:
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,
default_sg=False):
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, default_sg=default_sg)
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)