--- a/components/openstack/neutron/files/evs/plugin.py Mon May 16 14:46:20 2016 +0200
+++ b/components/openstack/neutron/files/evs/plugin.py Fri May 20 17:42:29 2016 -0400
@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
-# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2016, Oracle and/or its affiliates. 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
@@ -20,12 +20,17 @@
import rad.connect as radcon
import rad.bindings.com.oracle.solaris.rad.evscntl_1 as evsbind
-from oslo.config import cfg
+from oslo_concurrency import lockutils
+from oslo_config import cfg
+from oslo_db import api as oslo_db_api
+from oslo_log import log as logging
+from oslo_utils import importutils
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
from neutron.api.rpc.handlers import dhcp_rpc
from neutron.api.rpc.handlers import l3_rpc
+from neutron.api.rpc.handlers import metadata_rpc
from neutron.api.v2 import attributes
from neutron.common import constants
from neutron.common import exceptions
@@ -36,17 +41,33 @@
from neutron.db import api as db
from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db
+from neutron.db import l3_agentschedulers_db
+from neutron.db import l3_attrs_db
from neutron.db import l3_gwmode_db
-from neutron.db import model_base
+from neutron.db import models_v2
+from neutron.db import portbindings_db
from neutron.db import quota_db
+from neutron.db import securitygroups_db
from neutron.extensions import external_net
from neutron.extensions import providernet
-from neutron.openstack.common import importutils
-from neutron.openstack.common import lockutils
-from neutron.openstack.common import log as logging
from neutron.plugins.common import constants as svc_constants
+from neutron.plugins.ml2 import models
LOG = logging.getLogger(__name__)
+# Only import the vpn server code if it exists.
+try:
+ sp = cfg.CONF.service_plugins
+ vpns = 'vpnaas'
+ if vpns in sp:
+ try:
+ from neutron_vpnaas.db.vpn import vpn_db
+ LOG.debug("Loading VPNaaS service driver.")
+ except ImportError:
+ pass
+ else:
+ LOG.debug("vpnaas service_plugin not configured")
+except:
+ pass
evs_controller_opts = [
cfg.StrOpt('evs_controller', default='ssh://evsuser@localhost',
@@ -77,6 +98,7 @@
class EVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
agentschedulers_db.DhcpAgentSchedulerDbMixin,
external_net_db.External_net_db_mixin,
+ l3_agentschedulers_db.L3AgentSchedulerDbMixin,
l3_gwmode_db.L3_NAT_db_mixin):
"""Implements v2 Neutron Plug-in API specification.
@@ -141,16 +163,16 @@
_supported_extension_aliases = ["provider", "external-net", "router",
"ext-gw-mode", "quotas", "agent",
+ "l3_agent_scheduler",
"dhcp_agent_scheduler"]
def __init__(self):
- engine = db.get_engine()
- model_base.BASEV2.metadata.create_all(engine)
-
self.network_scheduler = importutils.import_object(
cfg.CONF.network_scheduler_driver
)
-
+ self.router_scheduler = importutils.import_object(
+ cfg.CONF.router_scheduler_driver
+ )
self._setup_rpc()
self._rad_connection = None
@@ -182,7 +204,8 @@
self.conn = n_rpc.create_connection(new=True)
self.endpoints = [dhcp_rpc.DhcpRpcCallback(),
l3_rpc.L3RpcCallback(),
- agents_db.AgentExtRpcCallback()]
+ agents_db.AgentExtRpcCallback(),
+ metadata_rpc.MetadataRpcCallback()]
for svc_topic in self.service_topics.values():
self.conn.create_consumer(svc_topic, self.endpoints, fanout=False)
# Consume from all consumers in a thread
@@ -259,15 +282,6 @@
IPnet associated with the EVS.
"""
- # TODO(gmoodalb): Take care of this now that we have pool.
- # Even though EVS does not support allocation pools, it is OK for an
- # user to specify --allocation-pool because allocation pool management
- # is done by neutron-server and is transparent to EVS framework.
-
- # user specified --no-gateway, and we don't support it
- if subnet['subnet']['gateway_ip'] is None:
- raise EVSOpNotSupported(_("setting --no-gateway for a subnet "
- "not supported"))
if (subnet['subnet']['host_routes'] is not
attributes.ATTR_NOT_SPECIFIED):
raise EVSOpNotSupported(_("setting --host-route for a subnet "
@@ -285,29 +299,29 @@
evsname = db_subnet['network_id']
tenantname = db_subnet['tenant_id']
proplist = ['subnet=%s' % db_subnet['cidr']]
- proplist.append('defrouter=%s' % db_subnet['gateway_ip'])
+ defrouter = db_subnet['gateway_ip']
+ if not defrouter:
+ defrouter = '0.0.0.0' if db_subnet['ip_version'] == 4 else '::'
+ proplist.append('defrouter=%s' % defrouter)
proplist.append('uuid=%s' % db_subnet['id'])
if poolstr:
proplist.append('pool=%s' % (poolstr))
self._evs_controller_addIPnet(tenantname, evsname, ipnetname,
",".join(proplist))
- # notify dhcp agent of subnet creation
- self.dhcp_agent_notifier.notify(context, {'subnet': db_subnet},
- 'subnet.create.end')
return db_subnet
def update_subnet(self, context, id, subnet):
LOG.debug(_("Updating Subnet: %s with %s") % (id, subnet))
- evs_rpccall_sync = subnet.pop('evs_rpccall_sync', False)
if (set(subnet['subnet'].keys()) - set(('enable_dhcp',
'allocation_pools',
+ 'dns_nameservers',
'ipv6_address_mode',
'ipv6_ra_mode'))):
raise EVSOpNotSupported(_("only following subnet attributes "
"enable-dhcp, allocation-pool, "
- "ipv6-address-mode, and "
- "ipv6-ra-mode can be updated"))
+ "dns-nameserver, ipv6-address-mode, "
+ "and ipv6-ra-mode can be updated"))
poolstr = self._subnet_pool_to_evs_pool(subnet['subnet'])
@@ -319,16 +333,6 @@
if poolstr:
self._evs_controller_updateIPnet(id, "pool=%s" % poolstr)
- # notify dhcp agent of subnet update
- methodname = 'subnet.update.end'
- payload = {'subnet': retval}
- if not evs_rpccall_sync:
- self.dhcp_agent_notifier.notify(context, payload, methodname)
- else:
- msg = self.dhcp_agent_notifier.make_msg(
- methodname.replace(".", "_"), payload=payload)
- self.dhcp_agent_notifier.call(context, msg,
- topic=topics.DHCP_AGENT)
return retval
def get_subnet(self, context, id, fields=None):
@@ -343,49 +347,29 @@
page_reverse)
return [self._fields(subnet, fields) for subnet in subnets]
- def _release_subnet_dhcp_port(self, context, subnet, delete_network):
- """Release any dhcp port associated with the subnet"""
- filters = dict(network_id=[subnet['network_id']])
- portlist = self.get_ports(context, filters)
-
- if delete_network:
- # One can delete a network if there is only one port that has a
- # VNIC attached to it and that port happens to be a DHCP port.
- ports_with_deviceid = [port for port in portlist
- if port['device_id'] != '']
- update_subnet = len(ports_with_deviceid) == 1
- else:
- # One can delete a subnet if there is only one port and that
- # port happens to be a DHCP port.
- update_subnet = len(portlist) == 1
- if update_subnet:
- # For IPv6 we need to first reset the IPv6 attributes
- if subnet['ip_version'] == 6:
- if (attributes.is_attr_set(subnet.get('ipv6_address_mode'))):
- subnet_update = {'subnet':
- {'ipv6_address_mode': None,
- 'ipv6_ra_mode': None
- },
- 'evs_rpccall_sync': True
- }
- self.update_subnet(context, subnet['id'], subnet_update)
- # the lone port is a dhcp port created by dhcp agent
- # it must be released before we can delete the subnet
- subnet_update = {'subnet': {'enable_dhcp': False},
- 'evs_rpccall_sync': True}
- self.update_subnet(context, subnet['id'], subnet_update)
-
@lockutils.synchronized('evs-plugin', 'neutron-')
- def _evs_controller_removeIPnet(self, tenantname, evsname, ipnetuuid):
+ def _evs_controller_removeIPnet(self, tenantname, evsname, ipnetuuid,
+ auto_created_ports):
LOG.debug(_("Removing IPnet with id: %s for tenant: %s for evs: %s") %
(ipnetuuid, tenantname, evsname))
pat = radcli.ADRGlobPattern({'name': evsname, 'tenant': tenantname})
try:
evs = self.rad_connection.get_object(evsbind.EVS(), pat)
+ if auto_created_ports:
+ LOG.debug(_("Need to remove following ports %s before "
+ "removing the IPnet") % (auto_created_ports))
+ for port in auto_created_ports:
+ try:
+ evs.removeVPort(port['id'], "force=yes")
+ except radcli.ObjectError as oe:
+ # '43' corresponds to EVS' EVS_ENOENT_VPORT error code
+ if oe.get_payload().err == 43:
+ LOG.debug(_("VPort %s could not be found") %
+ (port['id']))
evs.removeIPnet(ipnetuuid)
- except radcli.ObjectError as oe:
+ except (radcli.NotFoundError, radcli.ObjectError) as oe:
# '42' corresponds to EVS' EVS_ENOENT_IPNET error code
- if oe.get_payload().err == 42:
+ if oe.get_payload() is None or oe.get_payload().err == 42:
# EVS doesn't have that IPnet, return success to delete
# the IPnet from Neutron DB.
LOG.debug(_("IPnet could not be found in EVS."))
@@ -397,29 +381,16 @@
if not subnet:
return
- # If the subnet is dhcp_enabled, then the dhcp agent would have
- # created a port connected to this subnet. We need to remove
- # that port before we can proceed with subnet delete operation.
- # Since, there is no subnet.delete.start event, we use an another
- # approach of updating the subnet's enable_dhcp attribute to
- # False that in turn sends a subnet.udpate notification. This
- # results in DHCP agent releasing the port.
- if subnet['enable_dhcp']:
- self._release_subnet_dhcp_port(context, subnet, False)
with context.session.begin(subtransactions=True):
+ # get a list of ports automatically created by Neutron
+ auto_created_ports = context.session.query(models_v2.Port).\
+ filter(models_v2.Port.device_owner.
+ in_(db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS)).all()
# delete subnet in DB
super(EVSNeutronPluginV2, self).delete_subnet(context, id)
self._evs_controller_removeIPnet(subnet['tenant_id'],
- subnet['network_id'], id)
-
- # notify dhcp agent
- payload = {
- 'subnet': {
- 'network_id': subnet['network_id'],
- 'id': id,
- }
- }
- self.dhcp_agent_notifier.notify(context, payload, 'subnet.delete.end')
+ subnet['network_id'], id,
+ auto_created_ports)
@lockutils.synchronized('evs-plugin', 'neutron-')
def _evs_controller_createEVS(self, tenantname, evsname, propstr):
@@ -562,9 +533,9 @@
self.rad_connection.\
get_object(evsbind.EVSController()).\
deleteEVS(evsuuid, tenantname)
- except radcli.ObjectError as oe:
+ except (radcli.NotFoundError, radcli.ObjectError) as oe:
# '41' corresponds to EVS' EVS_ENOENT_EVS error code
- if oe.get_payload().err == 41:
+ if oe.get_payload() is None or oe.get_payload().err == 41:
# EVS doesn't have that EVS, return success to delete
# the EVS from Neutron DB.
LOG.debug(_("EVS could not be found in EVS backend."))
@@ -572,28 +543,35 @@
raise EVSControllerError(oe.get_payload().errmsg)
def delete_network(self, context, id):
- # Check if it is an external network and whether addresses in that
- # network are being used for floating ips
- net = self.get_network(context, id)
- if net[external_net.EXTERNAL]:
- filters = dict(network_id=[id])
- portlist = self.get_ports(context, filters)
- ports_with_deviceid = [port for port in portlist
- if port['device_id'] != '']
- if ports_with_deviceid:
+ with context.session.begin(subtransactions=True):
+ network = self._get_network(context, id)
+
+ qry_network_ports = context.session.query(models_v2.Port).\
+ filter_by(network_id=id).filter(models_v2.Port.device_owner.
+ in_(db_base_plugin_v2.
+ AUTO_DELETE_PORT_OWNERS))
+
+ auto_created_ports = qry_network_ports.all()
+ qry_network_ports.delete(synchronize_session=False)
+
+ port_in_use = context.session.query(models_v2.Port).filter_by(
+ network_id=id).first()
+
+ if port_in_use:
raise exceptions.NetworkInUse(net_id=id)
- filters = dict(network_id=[id])
- subnets = self.get_subnets(context, filters=filters)
- dhcp_subnets = [s for s in subnets if s['enable_dhcp']]
- for subnet in dhcp_subnets:
- self._release_subnet_dhcp_port(context, subnet, True)
- with context.session.begin(subtransactions=True):
- super(EVSNeutronPluginV2, self).delete_network(context, id)
- self._evs_controller_deleteEVS(net['tenant_id'], id)
- # notify dhcp agent of network deletion
- self.dhcp_agent_notifier.notify(context, {'network': {'id': id}},
- 'network.delete.end')
+ # clean up subnets
+ subnets = self._get_subnets_by_network(context, id)
+ for subnet in subnets:
+ super(EVSNeutronPluginV2, self).delete_subnet(context,
+ subnet['id'])
+ self._evs_controller_removeIPnet(subnet['tenant_id'],
+ subnet['network_id'],
+ subnet['id'],
+ auto_created_ports)
+
+ context.session.delete(network)
+ self._evs_controller_deleteEVS(network['tenant_id'], id)
@lockutils.synchronized('evs-plugin', 'neutron-')
def _evs_controller_addVPort(self, tenantname, evsname, vportname,
@@ -611,6 +589,9 @@
raise EVSControllerError(oe.get_payload().errmsg)
return vport
+ @oslo_db_api.wrap_db_retry(max_retries=db.MAX_RETRIES,
+ retry_on_request=True,
+ retry_on_deadlock=True)
def create_port(self, context, port):
"""Creates a port(VPort) for a given network(EVS).
@@ -631,12 +612,20 @@
# is not set, but EVS does not like it.
tenant_id = self._get_tenant_id_for_create(context, port['port'])
if not tenant_id:
- network = self.get_network(context, port['port']['network_id'])
+ network = self._get_network(context,
+ port['port']['network_id'])
port['port']['tenant_id'] = network['tenant_id']
# create the port in the DB
db_port = super(EVSNeutronPluginV2, self).create_port(context,
port)
-
+ # Neutron allows to create a port on a network that doesn't
+ # yet have subnet associated with it, however EVS doesn't
+ # support this.
+ if not db_port['fixed_ips']:
+ raise EVSOpNotSupported(_("creating a port on a network that "
+ "does not yet have subnet "
+ "associated with it is not "
+ "supported"))
tenantname = db_port['tenant_id']
vportname = db_port['name']
if not vportname:
@@ -649,10 +638,6 @@
self._evs_controller_addVPort(tenantname, evs_id, vportname,
",".join(proplist))
-
- # notify dhcp agent of port creation
- self.dhcp_agent_notifier.notify(context, {'port': db_port},
- 'port.create.end')
return db_port
def update_port(self, context, id, port):
@@ -676,10 +661,6 @@
LOG.debug(_("Updating port %s with %s") % (id, port))
db_port = super(EVSNeutronPluginV2, self).update_port(context,
id, port)
-
- # notify dhcp agent of port update
- self.dhcp_agent_notifier.notify(context, {'port': db_port},
- 'port.update.end')
return db_port
def get_port(self, context, id, fields=None):
@@ -694,35 +675,6 @@
page_reverse)
return [self._fields(port, fields) for port in ports]
- def notify_l3agent(self, context, port):
- """ If an L3 agent is using this port, then we need to send
- a notification to the L3 agent so that it can remove the EVS VPort
- associated with the Neutron Port. In that case, the EVS Plugin will
- only remove the Neutron port from the DB, so return False.
-
- If the port is not used by the L3 agent, then the EVS plugin
- will remove both the Neutron port and EVS VPort, so return True.
- """
-
- device_owner = port['device_owner']
- if device_owner not in [constants.DEVICE_OWNER_ROUTER_INTF,
- constants.DEVICE_OWNER_ROUTER_GW,
- constants.DEVICE_OWNER_FLOATINGIP]:
- return True
- router_id = port['device_id']
- port_update = {
- 'port': {
- 'device_id': '',
- 'device_owner': ''
- }
- }
- self.update_port(context, port['id'], port_update)
- if device_owner in [constants.DEVICE_OWNER_ROUTER_INTF,
- constants.DEVICE_OWNER_ROUTER_GW]:
- self.l3_agent_notifier.routers_updated(context, [router_id])
- return False
- return True
-
@lockutils.synchronized('evs-plugin', 'neutron-')
def _evs_controller_removeVPort(self, tenantname, evsname, vportuuid):
LOG.debug(_("Removing VPort with id: %s for tenant: %s for evs: %s") %
@@ -731,32 +683,15 @@
'tenant': tenantname})
try:
evs = self.rad_connection.get_object(evsbind.EVS(), pat)
- evs.removeVPort(vportuuid)
- except radcli.ObjectError as oe:
- # '7' corresponds to EVS' EVS_EBUSY_VPORT error code
- if oe.get_payload().err == 7:
- # It is possible that the VM is destroyed, but EVS is unaware
- # of it. So, try to reset the vport. If it succeeds, then call
- # removeVPort() again.
- LOG.debug(_("EVS VPort is busy. We will need to reset "
- "and then remove"))
- try:
- evs.resetVPort(vportuuid)
- evs.removeVPort(vportuuid)
- except:
- # we failed one of the above operations, just return
- # the original exception.
- pass
- else:
- # the reset and remove succeeded, just return.
- return
+ evs.removeVPort(vportuuid, "force=yes")
+ except (radcli.NotFoundError, radcli.ObjectError) as oe:
# '43' corresponds to EVS' EVS_ENOENT_VPORT error code
- elif oe.get_payload().err == 43:
+ if oe.get_payload() is None or oe.get_payload().err == 43:
# EVS doesn't have that VPort, return success to delete
# the VPort from Neutron DB.
LOG.debug(_("VPort could not be found in EVS."))
- return
- raise EVSControllerError(oe.get_payload().errmsg)
+ else:
+ raise EVSControllerError(oe.get_payload().errmsg)
def delete_port(self, context, id, l3_port_check=True):
if l3_port_check:
@@ -765,19 +700,8 @@
port = self.get_port(context, id)
if not port:
return
- del_vport = l3_port_check or self.notify_l3agent(context, port)
with context.session.begin(subtransactions=True):
super(EVSNeutronPluginV2, self).delete_port(context, id)
- if del_vport:
- self._evs_controller_removeVPort(port['tenant_id'],
- port['network_id'],
- port['id'])
-
- # notify dhcp agent of port deletion
- payload = {
- 'port': {
- 'network_id': port['network_id'],
- 'id': id,
- }
- }
- self.dhcp_agent_notifier.notify(context, payload, 'port.delete.end')
+ self._evs_controller_removeVPort(port['tenant_id'],
+ port['network_id'],
+ port['id'])