summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/neutron/rootwrap.d/dhcp.filters2
-rw-r--r--etc/neutron/rootwrap.d/l3.filters2
-rw-r--r--neutron/agent/securitygroups_rpc.py41
-rw-r--r--neutron/api/rpc/handlers/dhcp_rpc.py8
-rw-r--r--neutron/api/rpc/handlers/securitygroups_rpc.py14
-rw-r--r--neutron/db/l3_db.py8
-rw-r--r--neutron/db/l3_hamode_db.py2
-rw-r--r--neutron/db/securitygroups_rpc_base.py61
-rw-r--r--neutron/notifiers/nova.py12
-rw-r--r--neutron/plugins/ml2/db.py72
-rw-r--r--neutron/plugins/ml2/plugin.py18
-rw-r--r--neutron/tests/unit/db/test_l3_ha_db.py33
-rw-r--r--neutron/tests/unit/ml2/test_security_group.py107
-rw-r--r--neutron/tests/unit/notifiers/test_notifiers_nova.py17
-rw-r--r--neutron/tests/unit/test_dhcp_rpc.py35
-rw-r--r--neutron/tests/unit/test_security_groups_rpc.py45
-rw-r--r--setup.cfg1
17 files changed, 365 insertions, 113 deletions
diff --git a/etc/neutron/rootwrap.d/dhcp.filters b/etc/neutron/rootwrap.d/dhcp.filters
index 26c2ffa86c..7c11d70dbd 100644
--- a/etc/neutron/rootwrap.d/dhcp.filters
+++ b/etc/neutron/rootwrap.d/dhcp.filters
@@ -23,11 +23,9 @@ dhcp_release: CommandFilter, dhcp_release, root
# metadata proxy
metadata_proxy: CommandFilter, neutron-ns-metadata-proxy, root
-metadata_proxy_quantum: CommandFilter, quantum-ns-metadata-proxy, root
# If installed from source (say, by devstack), the prefix will be
# /usr/local instead of /usr/bin.
metadata_proxy_local: CommandFilter, /usr/local/bin/neutron-ns-metadata-proxy, root
-metadata_proxy_local_quantum: CommandFilter, /usr/local/bin/quantum-ns-metadata-proxy, root
# RHEL invocation of the metadata proxy will report /usr/bin/python
kill_metadata: KillFilter, root, python, -9
kill_metadata7: KillFilter, root, python2.7, -9
diff --git a/etc/neutron/rootwrap.d/l3.filters b/etc/neutron/rootwrap.d/l3.filters
index 9a3031822a..d9e4a13c46 100644
--- a/etc/neutron/rootwrap.d/l3.filters
+++ b/etc/neutron/rootwrap.d/l3.filters
@@ -18,11 +18,9 @@ radvd: CommandFilter, radvd, root
# metadata proxy
metadata_proxy: CommandFilter, neutron-ns-metadata-proxy, root
-metadata_proxy_quantum: CommandFilter, quantum-ns-metadata-proxy, root
# If installed from source (say, by devstack), the prefix will be
# /usr/local instead of /usr/bin.
metadata_proxy_local: CommandFilter, /usr/local/bin/neutron-ns-metadata-proxy, root
-metadata_proxy_local_quantum: CommandFilter, /usr/local/bin/quantum-ns-metadata-proxy, root
# RHEL invocation of the metadata proxy will report /usr/bin/python
kill_metadata: KillFilter, root, python, -9
kill_metadata7: KillFilter, root, python2.7, -9
diff --git a/neutron/agent/securitygroups_rpc.py b/neutron/agent/securitygroups_rpc.py
index 17b544502e..3da4d347f0 100644
--- a/neutron/agent/securitygroups_rpc.py
+++ b/neutron/agent/securitygroups_rpc.py
@@ -14,11 +14,14 @@
# under the License.
#
+import functools
+
from oslo.config import cfg
from oslo import messaging
+from neutron.agent import firewall
from neutron.common import topics
-from neutron.openstack.common.gettextutils import _LW
+from neutron.openstack.common.gettextutils import _LI, _LW
from neutron.openstack.common import importutils
from neutron.openstack.common import log as logging
@@ -74,9 +77,9 @@ def _disable_extension(extension, aliases):
def disable_security_group_extension_by_config(aliases):
if not is_firewall_enabled():
- LOG.info(_('Disabled security-group extension.'))
+ LOG.info(_LI('Disabled security-group extension.'))
_disable_extension('security-group', aliases)
- LOG.info(_('Disabled allowed-address-pairs extension.'))
+ LOG.info(_LI('Disabled allowed-address-pairs extension.'))
_disable_extension('allowed-address-pairs', aliases)
@@ -187,10 +190,23 @@ class SecurityGroupAgentRpcMixin(object):
return False
return True
+ def skip_if_noopfirewall_or_firewall_disabled(func):
+ @functools.wraps(func)
+ def decorated_function(self, *args, **kwargs):
+ if (isinstance(self.firewall, firewall.NoopFirewallDriver) or
+ not is_firewall_enabled()):
+ LOG.info(_LI("Skipping method %s as firewall is disabled "
+ "or configured as NoopFirewallDriver."),
+ func.__name__)
+ else:
+ return func(self, *args, **kwargs)
+ return decorated_function
+
+ @skip_if_noopfirewall_or_firewall_disabled
def prepare_devices_filter(self, device_ids):
if not device_ids:
return
- LOG.info(_("Preparing filters for devices %s"), device_ids)
+ LOG.info(_LI("Preparing filters for devices %s"), device_ids)
if self.use_enhanced_rpc:
devices_info = self.plugin_rpc.security_group_info_for_devices(
self.context, list(device_ids))
@@ -220,15 +236,15 @@ class SecurityGroupAgentRpcMixin(object):
remote_sg_id, member_ips)
def security_groups_rule_updated(self, security_groups):
- LOG.info(_("Security group "
- "rule updated %r"), security_groups)
+ LOG.info(_LI("Security group "
+ "rule updated %r"), security_groups)
self._security_group_updated(
security_groups,
'security_groups')
def security_groups_member_updated(self, security_groups):
- LOG.info(_("Security group "
- "member updated %r"), security_groups)
+ LOG.info(_LI("Security group "
+ "member updated %r"), security_groups)
self._security_group_updated(
security_groups,
'security_group_source_groups')
@@ -249,7 +265,7 @@ class SecurityGroupAgentRpcMixin(object):
self.refresh_firewall(devices)
def security_groups_provider_updated(self):
- LOG.info(_("Provider rule updated"))
+ LOG.info(_LI("Provider rule updated"))
if self.defer_refresh_firewall:
# NOTE(salv-orlando): A 'global refresh' might not be
# necessary if the subnet for which the provider rules
@@ -261,7 +277,7 @@ class SecurityGroupAgentRpcMixin(object):
def remove_devices_filter(self, device_ids):
if not device_ids:
return
- LOG.info(_("Remove device filter for %r"), device_ids)
+ LOG.info(_LI("Remove device filter for %r"), device_ids)
with self.firewall.defer_apply():
for device_id in device_ids:
device = self.firewall.ports.get(device_id)
@@ -269,12 +285,13 @@ class SecurityGroupAgentRpcMixin(object):
continue
self.firewall.remove_port_filter(device)
+ @skip_if_noopfirewall_or_firewall_disabled
def refresh_firewall(self, device_ids=None):
- LOG.info(_("Refresh firewall rules"))
+ LOG.info(_LI("Refresh firewall rules"))
if not device_ids:
device_ids = self.firewall.ports.keys()
if not device_ids:
- LOG.info(_("No ports here to refresh firewall"))
+ LOG.info(_LI("No ports here to refresh firewall"))
return
if self.use_enhanced_rpc:
devices_info = self.plugin_rpc.security_group_info_for_devices(
diff --git a/neutron/api/rpc/handlers/dhcp_rpc.py b/neutron/api/rpc/handlers/dhcp_rpc.py
index 56016be708..58317eac4b 100644
--- a/neutron/api/rpc/handlers/dhcp_rpc.py
+++ b/neutron/api/rpc/handlers/dhcp_rpc.py
@@ -60,7 +60,7 @@ class DhcpRpcCallback(n_rpc.RpcCallback):
if action == 'create_port':
return plugin.create_port(context, port)
elif action == 'update_port':
- return plugin.update_port(context, port['id'], port['port'])
+ return plugin.update_port(context, port['id'], port)
else:
msg = _('Unrecognized action')
raise n_exc.Invalid(message=msg)
@@ -282,13 +282,11 @@ class DhcpRpcCallback(n_rpc.RpcCallback):
def update_dhcp_port(self, context, **kwargs):
"""Update the dhcp port."""
host = kwargs.get('host')
- port_id = kwargs.get('port_id')
port = kwargs.get('port')
+ port['id'] = kwargs.get('port_id')
LOG.debug(_('Update dhcp port %(port)s '
'from %(host)s.'),
{'port': port,
'host': host})
plugin = manager.NeutronManager.get_plugin()
- return self._port_action(plugin, context,
- {'id': port_id, 'port': port},
- 'update_port')
+ return self._port_action(plugin, context, port, 'update_port')
diff --git a/neutron/api/rpc/handlers/securitygroups_rpc.py b/neutron/api/rpc/handlers/securitygroups_rpc.py
index 2a748cfbcc..e4a16b2f58 100644
--- a/neutron/api/rpc/handlers/securitygroups_rpc.py
+++ b/neutron/api/rpc/handlers/securitygroups_rpc.py
@@ -36,15 +36,11 @@ class SecurityGroupServerRpcCallback(n_rpc.RpcCallback):
return manager.NeutronManager.get_plugin()
def _get_devices_info(self, devices):
- devices_info = {}
- for device in devices:
- port = self.plugin.get_port_from_device(device)
- if not port:
- continue
- if port['device_owner'].startswith('network:'):
- continue
- devices_info[port['id']] = port
- return devices_info
+ return dict(
+ (port['id'], port)
+ for port in self.plugin.get_ports_from_devices(devices)
+ if port and not port['device_owner'].startswith('network:')
+ )
def security_group_rules_for_devices(self, context, **kwargs):
"""Callback method to return security group rules for each port.
diff --git a/neutron/db/l3_db.py b/neutron/db/l3_db.py
index 83428a4075..cd4be99253 100644
--- a/neutron/db/l3_db.py
+++ b/neutron/db/l3_db.py
@@ -170,10 +170,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
return self._make_router_dict(router_db)
def _update_router_db(self, context, router_id, data, gw_info):
- """Update the DB object and related gw info, if available."""
+ """Update the DB object."""
with context.session.begin(subtransactions=True):
- if gw_info != attributes.ATTR_NOT_SPECIFIED:
- self._update_router_gw_info(context, router_id, gw_info)
router_db = self._get_router(context, router_id)
if data:
router_db.update(data)
@@ -189,6 +187,10 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
if gw_info != attributes.ATTR_NOT_SPECIFIED:
candidates = self._check_router_needs_rescheduling(
context, id, gw_info)
+ # Update the gateway outside of the DB update since it involves L2
+ # calls that don't make sense to rollback and may cause deadlocks
+ # in a transaction.
+ self._update_router_gw_info(context, id, gw_info)
else:
candidates = None
router_db = self._update_router_db(context, id, r, gw_info)
diff --git a/neutron/db/l3_hamode_db.py b/neutron/db/l3_hamode_db.py
index a0ed580850..94897dbc83 100644
--- a/neutron/db/l3_hamode_db.py
+++ b/neutron/db/l3_hamode_db.py
@@ -224,7 +224,7 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin):
'shared': False,
'admin_state_up': True,
'status': constants.NET_STATUS_ACTIVE}}
- network = self._core_plugin.create_network(context, args)
+ network = self._core_plugin.create_network(admin_ctx, args)
try:
ha_network = self._create_ha_network_tenant_binding(admin_ctx,
tenant_id,
diff --git a/neutron/db/securitygroups_rpc_base.py b/neutron/db/securitygroups_rpc_base.py
index 1dda6bb469..2f6e2b7fdb 100644
--- a/neutron/db/securitygroups_rpc_base.py
+++ b/neutron/db/securitygroups_rpc_base.py
@@ -19,6 +19,7 @@ from sqlalchemy.orm import exc
from neutron.common import constants as q_const
from neutron.common import ipv6_utils as ipv6
from neutron.common import utils
+from neutron.db import allowedaddresspairs_db as addr_pair
from neutron.db import models_v2
from neutron.db import securitygroups_db as sg_db
from neutron.extensions import securitygroup as ext_sg
@@ -39,7 +40,7 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
def get_port_from_device(self, device):
"""Get port dict from device name on an agent.
- Subclass must provide this method.
+ Subclass must provide this method or get_ports_from_devices.
:param device: device name which identifies a port on the agent side.
What is specified in "device" depends on a plugin agent implementation.
@@ -53,9 +54,18 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
- security_group_source_groups
- fixed_ips
"""
- raise NotImplementedError(_("%s must implement get_port_from_device.")
+ raise NotImplementedError(_("%s must implement get_port_from_device "
+ "or get_ports_from_devices.")
% self.__class__.__name__)
+ def get_ports_from_devices(self, devices):
+ """Bulk method of get_port_from_device.
+
+ Subclasses may override this to provide better performance for DB
+ queries, backend calls, etc.
+ """
+ return [self.get_port_from_device(device) for device in devices]
+
def create_security_group_rule(self, context, security_group_rule):
bulk_rule = {'security_group_rules': [security_group_rule]}
rule = self.create_security_group_rule_bulk_native(context,
@@ -153,8 +163,7 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
'sg_member_ips': {}}
rules_in_db = self._select_rules_for_ports(context, ports)
remote_security_group_info = {}
- for (binding, rule_in_db) in rules_in_db:
- port_id = binding['port_id']
+ for (port_id, rule_in_db) in rules_in_db:
remote_gid = rule_in_db.get('remote_group_id')
security_group_id = rule_in_db.get('security_group_id')
ethertype = rule_in_db['ethertype']
@@ -219,7 +228,7 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
sgr_sgid = sg_db.SecurityGroupRule.security_group_id
- query = context.session.query(sg_db.SecurityGroupPortBinding,
+ query = context.session.query(sg_binding_port,
sg_db.SecurityGroupRule)
query = query.join(sg_db.SecurityGroupRule,
sgr_sgid == sg_binding_sgid)
@@ -231,27 +240,32 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
if not remote_group_ids:
return ips_by_group
for remote_group_id in remote_group_ids:
- ips_by_group[remote_group_id] = []
+ ips_by_group[remote_group_id] = set()
ip_port = models_v2.IPAllocation.port_id
sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
sg_binding_sgid = sg_db.SecurityGroupPortBinding.security_group_id
+ # Join the security group binding table directly to the IP allocation
+ # table instead of via the Port table skip an unnecessary intermediary
query = context.session.query(sg_binding_sgid,
- models_v2.Port,
- models_v2.IPAllocation.ip_address)
+ models_v2.IPAllocation.ip_address,
+ addr_pair.AllowedAddressPair.ip_address)
query = query.join(models_v2.IPAllocation,
ip_port == sg_binding_port)
- query = query.join(models_v2.Port,
- ip_port == models_v2.Port.id)
+ # Outerjoin because address pairs may be null and we still want the
+ # IP for the port.
+ query = query.outerjoin(
+ addr_pair.AllowedAddressPair,
+ sg_binding_port == addr_pair.AllowedAddressPair.port_id)
query = query.filter(sg_binding_sgid.in_(remote_group_ids))
- for security_group_id, port, ip_address in query:
- ips_by_group[security_group_id].append(ip_address)
- # if there are allowed_address_pairs add them
- if getattr(port, 'allowed_address_pairs', None):
- for address_pair in port.allowed_address_pairs:
- ips_by_group[security_group_id].append(
- address_pair['ip_address'])
+ # Each allowed address pair IP record for a port beyond the 1st
+ # will have a duplicate regular IP in the query response since
+ # the relationship is 1-to-many. Dedup with a set
+ for security_group_id, ip_address, allowed_addr_ip in query:
+ ips_by_group[security_group_id].add(ip_address)
+ if allowed_addr_ip:
+ ips_by_group[security_group_id].add(allowed_addr_ip)
return ips_by_group
def _select_remote_group_ids(self, ports):
@@ -269,7 +283,8 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
def _select_dhcp_ips_for_network_ids(self, context, network_ids):
if not network_ids:
return {}
- query = context.session.query(models_v2.Port,
+ query = context.session.query(models_v2.Port.mac_address,
+ models_v2.Port.network_id,
models_v2.IPAllocation.ip_address)
query = query.join(models_v2.IPAllocation)
query = query.filter(models_v2.Port.network_id.in_(network_ids))
@@ -280,14 +295,13 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
for network_id in network_ids:
ips[network_id] = []
- for port, ip in query:
+ for mac_address, network_id, ip in query:
if (netaddr.IPAddress(ip).version == 6
and not netaddr.IPAddress(ip).is_link_local()):
- mac_address = port['mac_address']
ip = str(ipv6.get_ipv6_addr_by_EUI64(q_const.IPV6_LLA_PREFIX,
mac_address))
- if ip not in ips[port['network_id']]:
- ips[port['network_id']].append(ip)
+ if ip not in ips[network_id]:
+ ips[network_id].append(ip)
return ips
@@ -417,8 +431,7 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
def security_group_rules_for_ports(self, context, ports):
rules_in_db = self._select_rules_for_ports(context, ports)
- for (binding, rule_in_db) in rules_in_db:
- port_id = binding['port_id']
+ for (port_id, rule_in_db) in rules_in_db:
port = ports[port_id]
direction = rule_in_db['direction']
rule_dict = {
diff --git a/neutron/notifiers/nova.py b/neutron/notifiers/nova.py
index aa4f1b53e1..1db6bc097e 100644
--- a/neutron/notifiers/nova.py
+++ b/neutron/notifiers/nova.py
@@ -128,6 +128,18 @@ class Notifier(object):
if not cfg.CONF.notify_nova_on_port_data_changes:
return
+ # When neutron re-assigns floating ip from an original instance
+ # port to a new instance port without disassociate it first, an
+ # event should be sent for original instance, that will make nova
+ # know original instance's info, and update database for it.
+ if (action == 'update_floatingip'
+ and returned_obj['floatingip'].get('port_id')
+ and original_obj.get('port_id')):
+ disassociate_returned_obj = {'floatingip': {'port_id': None}}
+ event = self.create_port_changed_event(action, original_obj,
+ disassociate_returned_obj)
+ self.queue_event(event)
+
event = self.create_port_changed_event(action, original_obj,
returned_obj)
self.queue_event(event)
diff --git a/neutron/plugins/ml2/db.py b/neutron/plugins/ml2/db.py
index d8caa9384a..40e1c22e52 100644
--- a/neutron/plugins/ml2/db.py
+++ b/neutron/plugins/ml2/db.py
@@ -13,6 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import collections
+
+from sqlalchemy import or_
from sqlalchemy.orm import exc
from oslo.db import exception as db_exc
@@ -30,6 +33,9 @@ from neutron.plugins.ml2 import models
LOG = log.getLogger(__name__)
+# limit the number of port OR LIKE statements in one query
+MAX_PORTS_PER_QUERY = 500
+
def _make_segment_dict(record):
"""Make a segment dictionary out of a DB record."""
@@ -206,32 +212,64 @@ def get_port_from_device_mac(device_mac):
return qry.first()
-def get_port_and_sgs(port_id):
- """Get port from database with security group info."""
+def get_ports_and_sgs(port_ids):
+ """Get ports from database with security group info."""
+
+ # break large queries into smaller parts
+ if len(port_ids) > MAX_PORTS_PER_QUERY:
+ LOG.debug("Number of ports %(pcount)s exceeds the maximum per "
+ "query %(maxp)s. Partitioning queries.",
+ {'pcount': len(port_ids), 'maxp': MAX_PORTS_PER_QUERY})
+ return (get_ports_and_sgs(port_ids[:MAX_PORTS_PER_QUERY]) +
+ get_ports_and_sgs(port_ids[MAX_PORTS_PER_QUERY:]))
+
+ LOG.debug("get_ports_and_sgs() called for port_ids %s", port_ids)
- LOG.debug(_("get_port_and_sgs() called for port_id %s"), port_id)
+ if not port_ids:
+ # if port_ids is empty, avoid querying to DB to ask it for nothing
+ return []
+ ports_to_sg_ids = get_sg_ids_grouped_by_port(port_ids)
+ return [make_port_dict_with_security_groups(port, sec_groups)
+ for port, sec_groups in ports_to_sg_ids.iteritems()]
+
+
+def get_sg_ids_grouped_by_port(port_ids):
+ sg_ids_grouped_by_port = collections.defaultdict(list)
session = db_api.get_session()
sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
with session.begin(subtransactions=True):
+ # partial UUIDs must be individually matched with startswith.
+ # full UUIDs may be matched directly in an IN statement
+ partial_uuids = set(port_id for port_id in port_ids
+ if not uuidutils.is_uuid_like(port_id))
+ full_uuids = set(port_ids) - partial_uuids
+ or_criteria = [models_v2.Port.id.startswith(port_id)
+ for port_id in partial_uuids]
+ if full_uuids:
+ or_criteria.append(models_v2.Port.id.in_(full_uuids))
+
query = session.query(models_v2.Port,
sg_db.SecurityGroupPortBinding.security_group_id)
query = query.outerjoin(sg_db.SecurityGroupPortBinding,
models_v2.Port.id == sg_binding_port)
- query = query.filter(models_v2.Port.id.startswith(port_id))
- port_and_sgs = query.all()
- if not port_and_sgs:
- return
- port = port_and_sgs[0][0]
- plugin = manager.NeutronManager.get_plugin()
- port_dict = plugin._make_port_dict(port)
- port_dict['security_groups'] = [
- sg_id for port_, sg_id in port_and_sgs if sg_id]
- port_dict['security_group_rules'] = []
- port_dict['security_group_source_groups'] = []
- port_dict['fixed_ips'] = [ip['ip_address']
- for ip in port['fixed_ips']]
- return port_dict
+ query = query.filter(or_(*or_criteria))
+
+ for port, sg_id in query:
+ if sg_id:
+ sg_ids_grouped_by_port[port].append(sg_id)
+ return sg_ids_grouped_by_port
+
+
+def make_port_dict_with_security_groups(port, sec_groups):
+ plugin = manager.NeutronManager.get_plugin()
+ port_dict = plugin._make_port_dict(port)
+ port_dict['security_groups'] = sec_groups
+ port_dict['security_group_rules'] = []
+ port_dict['security_group_source_groups'] = []
+ port_dict['fixed_ips'] = [ip['ip_address']
+ for ip in port['fixed_ips']]
+ return port_dict
def get_port_binding_host(port_id):
diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py
index 72cf151006..d29deda6ce 100644
--- a/neutron/plugins/ml2/plugin.py
+++ b/neutron/plugins/ml2/plugin.py
@@ -1156,12 +1156,18 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
port_host = db.get_port_binding_host(port_id)
return (port_host == host)
- def get_port_from_device(self, device):
- port_id = self._device_to_port_id(device)
- port = db.get_port_and_sgs(port_id)
- if port:
- port['device'] = device
- return port
+ def get_ports_from_devices(self, devices):
+ port_ids_to_devices = dict((self._device_to_port_id(device), device)
+ for device in devices)
+ port_ids = port_ids_to_devices.keys()
+ ports = db.get_ports_and_sgs(port_ids)
+ for port in ports:
+ # map back to original requested id
+ port_id = next((port_id for port_id in port_ids
+ if port['id'].startswith(port_id)), None)
+ port['device'] = port_ids_to_devices.get(port_id)
+
+ return ports
def _device_to_port_id(self, device):
# REVISIT(rkukura): Consider calling into MechanismDrivers to
diff --git a/neutron/tests/unit/db/test_l3_ha_db.py b/neutron/tests/unit/db/test_l3_ha_db.py
index 4616612bbd..ca2ce82e86 100644
--- a/neutron/tests/unit/db/test_l3_ha_db.py
+++ b/neutron/tests/unit/db/test_l3_ha_db.py
@@ -54,19 +54,25 @@ class L3HATestFramework(testlib_api.SqlTestCase,
self.notif_m = notif_p.start()
cfg.CONF.set_override('allow_overlapping_ips', True)
- def _create_router(self, ha=True, tenant_id='tenant1', distributed=None):
+ def _create_router(self, ha=True, tenant_id='tenant1', distributed=None,
+ ctx=None):
+ if ctx is None:
+ ctx = self.admin_ctx
+ ctx.tenant_id = tenant_id
router = {'name': 'router1', 'admin_state_up': True}
if ha is not None:
router['ha'] = ha
if distributed is not None:
router['distributed'] = distributed
- return self.plugin._create_router_db(self.admin_ctx, router, tenant_id)
+ return self.plugin._create_router_db(ctx, router, tenant_id)
- def _update_router(self, router_id, ha=True, distributed=None):
+ def _update_router(self, router_id, ha=True, distributed=None, ctx=None):
+ if ctx is None:
+ ctx = self.admin_ctx
data = {'ha': ha} if ha is not None else {}
if distributed is not None:
data['distributed'] = distributed
- return self.plugin._update_router_db(self.admin_ctx, router_id,
+ return self.plugin._update_router_db(ctx, router_id,
data, None)
@@ -388,3 +394,22 @@ class L3HATestCase(L3HATestFramework):
routers_after = self.plugin.get_routers(self.admin_ctx)
self.assertEqual(routers_before, routers_after)
+
+
+class L3HAUserTestCase(L3HATestFramework):
+
+ def setUp(self):
+ super(L3HAUserTestCase, self).setUp()
+ self.user_ctx = context.Context('', _uuid())
+ self.plugin = FakeL3Plugin()
+
+ def test_create_ha_router(self):
+ self._create_router(ctx=self.user_ctx)
+
+ def test_update_router(self):
+ router = self._create_router(ctx=self.user_ctx)
+ self._update_router(router['id'], ha=False, ctx=self.user_ctx)
+
+ def test_delete_router(self):
+ router = self._create_router(ctx=self.user_ctx)
+ self.plugin.delete_router(self.user_ctx, router['id'])
diff --git a/neutron/tests/unit/ml2/test_security_group.py b/neutron/tests/unit/ml2/test_security_group.py
index 39c3cc2bae..cc8468ae23 100644
--- a/neutron/tests/unit/ml2/test_security_group.py
+++ b/neutron/tests/unit/ml2/test_security_group.py
@@ -14,11 +14,15 @@
# License for the specific language governing permissions and limitations
# under the License.
+import contextlib
+import math
import mock
from neutron.api.v2 import attributes
+from neutron.common import constants as const
from neutron.extensions import securitygroup as ext_sg
from neutron import manager
+from neutron.tests.unit import test_api_v2
from neutron.tests.unit import test_extension_security_group as test_sg
from neutron.tests.unit import test_security_groups_rpc as test_sg_rpc
@@ -55,38 +59,91 @@ class TestMl2SecurityGroups(Ml2SecurityGroupsTestCase,
plugin = manager.NeutronManager.get_plugin()
plugin.start_rpc_listeners()
- def test_security_group_get_port_from_device(self):
+ def _make_port_with_new_sec_group(self, net_id):
+ sg = self._make_security_group(self.fmt, 'name', 'desc')
+ port = self._make_port(
+ self.fmt, net_id, security_groups=[sg['security_group']['id']])
+ return port['port']
+
+ def test_security_group_get_ports_from_devices(self):
with self.network() as n:
with self.subnet(n):
- with self.security_group() as sg:
- security_group_id = sg['security_group']['id']
- res = self._create_port(self.fmt, n['network']['id'])
- port = self.deserialize(self.fmt, res)
- fixed_ips = port['port']['fixed_ips']
- data = {'port': {'fixed_ips': fixed_ips,
- 'name': port['port']['name'],
- ext_sg.SECURITYGROUPS:
- [security_group_id]}}
-
- req = self.new_update_request('ports', data,
- port['port']['id'])
- res = self.deserialize(self.fmt,
- req.get_response(self.api))
- port_id = res['port']['id']
- plugin = manager.NeutronManager.get_plugin()
- port_dict = plugin.get_port_from_device(port_id)
- self.assertEqual(port_id, port_dict['id'])
- self.assertEqual([security_group_id],
+ port1 = self._make_port_with_new_sec_group(n['network']['id'])
+ port2 = self._make_port_with_new_sec_group(n['network']['id'])
+ plugin = manager.NeutronManager.get_plugin()
+ # should match full ID and starting chars
+ ports = plugin.get_ports_from_devices(
+ [port1['id'], port2['id'][0:8]])
+ self.assertEqual(2, len(ports))
+ for port_dict in ports:
+ p = port1 if port1['id'] == port_dict['id'] else port2
+ self.assertEqual(p['id'], port_dict['id'])
+ self.assertEqual(p['security_groups'],
port_dict[ext_sg.SECURITYGROUPS])
self.assertEqual([], port_dict['security_group_rules'])
- self.assertEqual([fixed_ips[0]['ip_address']],
+ self.assertEqual([p['fixed_ips'][0]['ip_address']],
port_dict['fixed_ips'])
- self._delete('ports', port_id)
+ self._delete('ports', p['id'])
+
+ def test_security_group_get_ports_from_devices_with_bad_id(self):
+ plugin = manager.NeutronManager.get_plugin()
+ ports = plugin.get_ports_from_devices(['bad_device_id'])
+ self.assertFalse(ports)
- def test_security_group_get_port_from_device_with_no_port(self):
+ def test_security_group_no_db_calls_with_no_ports(self):
+ plugin = manager.NeutronManager.get_plugin()
+ with mock.patch(
+ 'neutron.plugins.ml2.db.get_sg_ids_grouped_by_port'
+ ) as get_mock:
+ self.assertFalse(plugin.get_ports_from_devices([]))
+ self.assertFalse(get_mock.called)
+
+ def test_large_port_count_broken_into_parts(self):
+ plugin = manager.NeutronManager.get_plugin()
+ max_ports_per_query = 5
+ ports_to_query = 73
+ for max_ports_per_query in (1, 2, 5, 7, 9, 31):
+ with contextlib.nested(
+ mock.patch('neutron.plugins.ml2.db.MAX_PORTS_PER_QUERY',
+ new=max_ports_per_query),
+ mock.patch('neutron.plugins.ml2.db.get_sg_ids_grouped_by_port',
+ return_value={}),
+ ) as (max_mock, get_mock):
+ plugin.get_ports_from_devices(
+ ['%s%s' % (const.TAP_DEVICE_PREFIX, i)
+ for i in range(ports_to_query)])
+ all_call_args = map(lambda x: x[1][0], get_mock.mock_calls)
+ last_call_args = all_call_args.pop()
+ # all but last should be getting MAX_PORTS_PER_QUERY ports
+ self.assertTrue(
+ all(map(lambda x: len(x) == max_ports_per_query,
+ all_call_args))
+ )
+ remaining = ports_to_query % max_ports_per_query
+ if remaining:
+ self.assertEqual(remaining, len(last_call_args))
+ # should be broken into ceil(total/MAX_PORTS_PER_QUERY) calls
+ self.assertEqual(
+ math.ceil(ports_to_query / float(max_ports_per_query)),
+ get_mock.call_count
+ )
+
+ def test_full_uuids_skip_port_id_lookup(self):
plugin = manager.NeutronManager.get_plugin()
- port_dict = plugin.get_port_from_device('bad_device_id')
- self.assertIsNone(port_dict)
+ # when full UUIDs are provided, the _or statement should only
+ # have one matching 'IN' critiera for all of the IDs
+ with contextlib.nested(
+ mock.patch('neutron.plugins.ml2.db.or_'),
+ mock.patch('neutron.plugins.ml2.db.db_api.get_session')
+ ) as (or_mock, sess_mock):
+ fmock = sess_mock.query.return_value.outerjoin.return_value.filter
+ # return no ports to exit the method early since we are mocking
+ # the query
+ fmock.return_value.all.return_value = []
+ plugin.get_ports_from_devices([test_api_v2._uuid(),
+ test_api_v2._uuid()])
+ # the or_ function should only have one argument
+ or_mock.assert_called_once_with(mock.ANY)
class TestMl2SecurityGroupsXML(TestMl2SecurityGroups):
diff --git a/neutron/tests/unit/notifiers/test_notifiers_nova.py b/neutron/tests/unit/notifiers/test_notifiers_nova.py
index 7972ebf55a..db9bc79c31 100644
--- a/neutron/tests/unit/notifiers/test_notifiers_nova.py
+++ b/neutron/tests/unit/notifiers/test_notifiers_nova.py
@@ -303,3 +303,20 @@ class TestNovaNotify(base.BaseTestCase):
self.nova_notifier.queue_event(mock.Mock())
self.assertFalse(self.nova_notifier._waiting_to_send)
send_events.assert_called_once_with()
+
+ def test_reassociate_floatingip_without_disassociate_event(self):
+ returned_obj = {'floatingip':
+ {'port_id': 'f5348a16-609a-4971-b0f0-4b8def5235fb'}}
+ original_obj = {'port_id': '5a39def4-3d3f-473d-9ff4-8e90064b9cc1'}
+ self.nova_notifier._waiting_to_send = True
+ self.nova_notifier.send_network_change(
+ 'update_floatingip', original_obj, returned_obj)
+ self.assertEqual(2, len(self.nova_notifier.pending_events))
+
+ returned_obj_non = {'floatingip': {'port_id': None}}
+ event_dis = self.nova_notifier.create_port_changed_event(
+ 'update_floatingip', original_obj, returned_obj_non)
+ event_assoc = self.nova_notifier.create_port_changed_event(
+ 'update_floatingip', original_obj, returned_obj)
+ self.assertEqual(self.nova_notifier.pending_events[0], event_dis)
+ self.assertEqual(self.nova_notifier.pending_events[1], event_assoc)
diff --git a/neutron/tests/unit/test_dhcp_rpc.py b/neutron/tests/unit/test_dhcp_rpc.py
index 6a2ed16d7e..2c4c5c9e6e 100644
--- a/neutron/tests/unit/test_dhcp_rpc.py
+++ b/neutron/tests/unit/test_dhcp_rpc.py
@@ -161,13 +161,44 @@ class TestDhcpRpcCallback(base.BaseTestCase):
self.plugin.assert_has_calls(expected)
return retval
+ def test_update_dhcp_port_verify_port_action_port_dict(self):
+ port = {'port': {'network_id': 'foo_network_id',
+ 'device_owner': constants.DEVICE_OWNER_DHCP,
+ 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]}
+ }
+ expected_port = {'port': {'network_id': 'foo_network_id',
+ 'device_owner': constants.DEVICE_OWNER_DHCP,
+ 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]
+ },
+ 'id': 'foo_port_id'
+ }
+
+ def _fake_port_action(plugin, context, port, action):
+ self.assertEqual(expected_port, port)
+
+ self.callbacks._port_action = _fake_port_action
+ self.callbacks.update_dhcp_port(mock.Mock(),
+ host='foo_host',
+ port_id='foo_port_id',
+ port=port)
+
def test_update_dhcp_port(self):
+ port = {'port': {'network_id': 'foo_network_id',
+ 'device_owner': constants.DEVICE_OWNER_DHCP,
+ 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]}
+ }
+ expected_port = {'port': {'network_id': 'foo_network_id',
+ 'device_owner': constants.DEVICE_OWNER_DHCP,
+ 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]
+ },
+ 'id': 'foo_port_id'
+ }
self.callbacks.update_dhcp_port(mock.Mock(),
host='foo_host',
port_id='foo_port_id',
- port=mock.Mock())
+ port=port)
self.plugin.assert_has_calls(
- mock.call.update_port(mock.ANY, 'foo_port_id', mock.ANY))
+ mock.call.update_port(mock.ANY, 'foo_port_id', expected_port))
def test_get_dhcp_port_existing(self):
port_retval = dict(id='port_id', fixed_ips=[dict(subnet_id='a')])
diff --git a/neutron/tests/unit/test_security_groups_rpc.py b/neutron/tests/unit/test_security_groups_rpc.py
index 7f20c7d9ea..3acc378b1d 100644
--- a/neutron/tests/unit/test_security_groups_rpc.py
+++ b/neutron/tests/unit/test_security_groups_rpc.py
@@ -1021,6 +1021,7 @@ class BaseSecurityGroupAgentRpcTestCase(base.BaseTestCase):
self.agent.root_helper = 'sudo'
self.agent.plugin_rpc = mock.Mock()
self.agent.init_firewall(defer_refresh_firewall=defer_refresh_firewall)
+ self.default_firewall = self.agent.firewall
self.firewall = mock.Mock()
firewall_object = firewall_base.FirewallDriver()
self.firewall.defer_apply.side_effect = firewall_object.defer_apply
@@ -1057,6 +1058,26 @@ class SecurityGroupAgentRpcTestCase(BaseSecurityGroupAgentRpcTestCase):
self.fake_device),
])
+ def test_prepare_devices_filter_with_noopfirewall(self):
+ self.agent.firewall = self.default_firewall
+ self.agent.plugin_rpc.security_group_info_for_devices = mock.Mock()
+ self.agent.plugin_rpc.security_group_rules_for_devices = mock.Mock()
+ self.agent.prepare_devices_filter(['fake_device'])
+ self.assertFalse(self.agent.plugin_rpc.
+ security_group_info_for_devices.called)
+ self.assertFalse(self.agent.plugin_rpc.
+ security_group_rules_for_devices.called)
+
+ def test_prepare_devices_filter_with_firewall_disabled(self):
+ cfg.CONF.set_override('enable_security_group', False, 'SECURITYGROUP')
+ self.agent.plugin_rpc.security_group_info_for_devices = mock.Mock()
+ self.agent.plugin_rpc.security_group_rules_for_devices = mock.Mock()
+ self.agent.prepare_devices_filter(['fake_device'])
+ self.assertFalse(self.agent.plugin_rpc.
+ security_group_info_for_devices.called)
+ self.assertFalse(self.agent.plugin_rpc.
+ security_group_rules_for_devices.called)
+
def test_security_groups_rule_updated(self):
self.agent.refresh_firewall = mock.Mock()
self.agent.prepare_devices_filter(['fake_port_id'])
@@ -1111,6 +1132,30 @@ class SecurityGroupAgentRpcTestCase(BaseSecurityGroupAgentRpcTestCase):
self.agent.refresh_firewall([])
self.assertFalse(self.firewall.called)
+ def test_refresh_firewall_with_firewall_disabled(self):
+ cfg.CONF.set_override('enable_security_group', False, 'SECURITYGROUP')
+ self.agent.plugin_rpc.security_group_info_for_devices = mock.Mock()
+ self.agent.plugin_rpc.security_group_rules_for_devices = mock.Mock()
+ self.agent.firewall.defer_apply = mock.Mock()
+ self.agent.refresh_firewall([self.fake_device])
+ self.assertFalse(self.agent.plugin_rpc.
+ security_group_info_for_devices.called)
+ self.assertFalse(self.agent.plugin_rpc.
+ security_group_rules_for_devices.called)
+ self.assertFalse(self.agent.firewall.defer_apply.called)
+
+ def test_refresh_firewall_with_noopfirewall(self):
+ self.agent.firewall = self.default_firewall
+ self.agent.plugin_rpc.security_group_info_for_devices = mock.Mock()
+ self.agent.plugin_rpc.security_group_rules_for_devices = mock.Mock()
+ self.agent.firewall.defer_apply = mock.Mock()
+ self.agent.refresh_firewall([self.fake_device])
+ self.assertFalse(self.agent.plugin_rpc.
+ security_group_info_for_devices.called)
+ self.assertFalse(self.agent.plugin_rpc.
+ security_group_rules_for_devices.called)
+ self.assertFalse(self.agent.firewall.defer_apply.called)
+
class SecurityGroupAgentEnhancedRpcTestCase(
BaseSecurityGroupAgentRpcTestCase):
diff --git a/setup.cfg b/setup.cfg
index 493940d2aa..94bf98cb34 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -141,7 +141,6 @@ neutron.core_plugins =
nuage = neutron.plugins.nuage.plugin:NuagePlugin
metaplugin = neutron.plugins.metaplugin.meta_neutron_plugin:MetaPluginV2
oneconvergence = neutron.plugins.oneconvergence.plugin:OneConvergencePluginV2
- openvswitch = neutron.plugins.openvswitch.ovs_neutron_plugin:OVSNeutronPluginV2
plumgrid = neutron.plugins.plumgrid.plumgrid_plugin.plumgrid_plugin:NeutronPluginPLUMgridV2
ryu = neutron.plugins.ryu.ryu_neutron_plugin:RyuNeutronPluginV2
vmware = neutron.plugins.vmware.plugin:NsxPlugin