diff options
49 files changed, 712 insertions, 264 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/dhcp_agent.py b/neutron/agent/dhcp_agent.py index 5a501faa91..c8ea89b021 100644 --- a/neutron/agent/dhcp_agent.py +++ b/neutron/agent/dhcp_agent.py @@ -227,9 +227,10 @@ class DhcpAgent(manager.Manager): self.conf, network) for subnet in network.subnets: - if subnet.enable_dhcp and subnet.ip_version == 4: + if subnet.enable_dhcp: if self.call_driver('enable', network): - if self.conf.use_namespaces and enable_metadata: + if (subnet.ip_version == 4 and self.conf.use_namespaces + and enable_metadata): self.enable_isolated_metadata_proxy(network) enable_metadata = False # Don't trigger twice self.cache.put(network) 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/api/v2/attributes.py b/neutron/api/v2/attributes.py index 83471f946e..21486dbd39 100644 --- a/neutron/api/v2/attributes.py +++ b/neutron/api/v2/attributes.py @@ -540,8 +540,8 @@ def convert_to_list(data): return [data] -HOSTNAME_PATTERN = ("(?=^.{1,254}$)(^(?:(?!\d+\.|-)[a-zA-Z0-9_\-]" - "{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)") +HOSTNAME_PATTERN = ("(?=^.{1,254}$)(^(?:(?!\d+.|-)[a-zA-Z0-9_\-]{1,62}" + "[a-zA-Z0-9]\.?)+(?:[a-zA-Z]{2,})$)") HEX_ELEM = '[0-9A-Fa-f]' UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}', diff --git a/neutron/common/ipv6_utils.py b/neutron/common/ipv6_utils.py index 96e1ef2344..ec9168ea36 100644 --- a/neutron/common/ipv6_utils.py +++ b/neutron/common/ipv6_utils.py @@ -20,6 +20,7 @@ import os import netaddr +from neutron.common import constants from neutron.openstack.common.gettextutils import _LI from neutron.openstack.common import log @@ -61,3 +62,9 @@ def is_enabled(): if not _IS_IPV6_ENABLED: LOG.info(_LI("IPv6 is not enabled on this system.")) return _IS_IPV6_ENABLED + + +def is_slaac_subnet(subnet): + """Check if subnet uses SLAAC addressing.""" + return (subnet['ipv6_address_mode'] == constants.IPV6_SLAAC + or subnet['ipv6_address_mode'] == constants.DHCPV6_STATELESS) diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index efb6a61615..70827c58af 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -177,12 +177,6 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, subnet_id=subnet_id).delete() @staticmethod - def _check_if_subnet_uses_eui64(subnet): - """Check if ipv6 address will be calculated via EUI64.""" - return (subnet['ipv6_address_mode'] == constants.IPV6_SLAAC - or subnet['ipv6_address_mode'] == constants.DHCPV6_STATELESS) - - @staticmethod def _store_ip_allocation(context, ip_address, network_id, subnet_id, port_id): LOG.debug("Allocated IP %(ip_address)s " @@ -394,7 +388,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, return True return False - def _test_fixed_ips_for_port(self, context, network_id, fixed_ips): + def _test_fixed_ips_for_port(self, context, network_id, fixed_ips, + device_owner): """Test fixed IPs for port. Check that configured subnets are valid prior to allocating any @@ -449,7 +444,9 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, msg = _('IP address %s is not a valid IP for the defined ' 'subnet') % fixed['ip_address'] raise n_exc.InvalidInput(error_message=msg) - if self._check_if_subnet_uses_eui64(subnet): + if (ipv6_utils.is_slaac_subnet(subnet) and device_owner not in + (constants.DEVICE_OWNER_ROUTER_INTF, + constants.DEVICE_OWNER_DVR_INTERFACE)): msg = (_("IPv6 address %(address)s can not be directly " "assigned to a port on subnet %(id)s with " "%(mode)s address mode") % @@ -481,7 +478,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, else: subnet = self._get_subnet(context, fixed['subnet_id']) if (subnet['ip_version'] == 6 and - self._check_if_subnet_uses_eui64(subnet)): + ipv6_utils.is_slaac_subnet(subnet)): prefix = subnet['cidr'] ip_address = ipv6_utils.get_ipv6_addr_by_EUI64( prefix, mac_address) @@ -496,7 +493,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, return ips def _update_ips_for_port(self, context, network_id, port_id, original_ips, - new_ips, mac_address): + new_ips, mac_address, device_owner): """Add or remove IPs from the port.""" ips = [] # These ips are still on the port and haven't been removed @@ -517,7 +514,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, prev_ips.append(original_ip) # Check if the IP's to add are OK - to_add = self._test_fixed_ips_for_port(context, network_id, new_ips) + to_add = self._test_fixed_ips_for_port(context, network_id, new_ips, + device_owner) for ip in original_ips: LOG.debug(_("Port update. Hold %s"), ip) NeutronDbPluginV2._delete_ip_allocation(context, @@ -544,7 +542,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, if fixed_configured: configured_ips = self._test_fixed_ips_for_port(context, p["network_id"], - p['fixed_ips']) + p['fixed_ips'], + p['device_owner']) ips = self._allocate_fixed_ips(context, configured_ips, p['mac_address']) @@ -560,7 +559,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, else: v6.append(subnet) for subnet in v6: - if self._check_if_subnet_uses_eui64(subnet): + if ipv6_utils.is_slaac_subnet(subnet): #(dzyu) If true, calculate an IPv6 address # by mac address and prefix, then remove this # subnet from the array of subnets that will be passed @@ -776,7 +775,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, # id together should be equal to 128. Currently neutron supports # EUI64 interface id only, thus limiting the prefix # length to be 64 only. - if self._check_if_subnet_uses_eui64(subnet): + if ipv6_utils.is_slaac_subnet(subnet): if netaddr.IPNetwork(subnet['cidr']).prefixlen != 64: msg = _('Invalid CIDR %s for IPv6 address mode. ' 'OpenStack uses the EUI-64 address format, ' @@ -1084,7 +1083,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, s = subnet['subnet'] if s['gateway_ip'] is attributes.ATTR_NOT_SPECIFIED: - s['gateway_ip'] = str(netaddr.IPAddress(net.first + 1)) + if s['ip_version'] == 6 and ipv6_utils.is_slaac_subnet(s): + s['gateway_ip'] = None + else: + s['gateway_ip'] = str(netaddr.IPAddress(net.first + 1)) if s['allocation_pools'] == attributes.ATTR_NOT_SPECIFIED: s['allocation_pools'] = self._allocate_pools_for_subnet(context, s) @@ -1396,7 +1398,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, added_ips, prev_ips = self._update_ips_for_port( context, port["network_id"], id, original["fixed_ips"], p['fixed_ips'], - original['mac_address']) + original['mac_address'], port['device_owner']) # Update ips if necessary for ip in added_ips: diff --git a/neutron/db/l3_db.py b/neutron/db/l3_db.py index 0f8a56c0ef..cd4be99253 100644 --- a/neutron/db/l3_db.py +++ b/neutron/db/l3_db.py @@ -21,6 +21,7 @@ from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.api.v2 import attributes from neutron.common import constants as l3_constants from neutron.common import exceptions as n_exc +from neutron.common import ipv6_utils from neutron.common import rpc as n_rpc from neutron.common import utils from neutron.db import model_base @@ -169,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) @@ -188,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) @@ -459,15 +462,20 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase): def _add_interface_by_subnet(self, context, router, subnet_id, owner): subnet = self._core_plugin._get_subnet(context, subnet_id) - if not subnet['gateway_ip']: + if (not subnet['gateway_ip'] + and not ipv6_utils.is_slaac_subnet(subnet)): msg = _('Subnet for router interface must have a gateway IP') raise n_exc.BadRequest(resource='router', msg=msg) self._check_for_dup_router_subnet(context, router, subnet['network_id'], subnet_id, subnet['cidr']) - fixed_ip = {'ip_address': subnet['gateway_ip'], - 'subnet_id': subnet['id']} + if subnet['gateway_ip']: + fixed_ip = {'ip_address': subnet['gateway_ip'], + 'subnet_id': subnet['id']} + else: + fixed_ip = {'subnet_id': subnet['id']} + return self._core_plugin.create_port(context, { 'port': {'tenant_id': subnet['tenant_id'], 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/bigswitch/plugin.py b/neutron/plugins/bigswitch/plugin.py index e2e051d751..b52dd3bf5e 100644 --- a/neutron/plugins/bigswitch/plugin.py +++ b/neutron/plugins/bigswitch/plugin.py @@ -365,11 +365,12 @@ class NeutronRestProxyV2Base(db_base_plugin_v2.NeutronDbPluginV2, cfg_vif_type = override port[portbindings.VIF_TYPE] = cfg_vif_type + sg_enabled = sg_rpc.is_firewall_enabled() port[portbindings.VIF_DETAILS] = { # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases, - portbindings.OVS_HYBRID_PLUG: True + portbindings.OVS_HYBRID_PLUG: sg_enabled } return port diff --git a/neutron/plugins/bigswitch/servermanager.py b/neutron/plugins/bigswitch/servermanager.py index 0a86ff4374..37169140c1 100644 --- a/neutron/plugins/bigswitch/servermanager.py +++ b/neutron/plugins/bigswitch/servermanager.py @@ -383,7 +383,8 @@ class ServerPool(object): a given path. ''' try: - cert = ssl.get_server_certificate((server, port)) + cert = ssl.get_server_certificate((server, port), + ssl_version=ssl.PROTOCOL_TLSv1) except Exception as e: raise cfg.Error(_('Could not retrieve initial ' 'certificate from controller %(server)s. ' @@ -472,6 +473,13 @@ class ServerPool(object): 'data': ret[3]}) active_server.failed = True + # A failure on a delete means the object is gone from Neutron but not + # from the controller. Set the consistency hash to a bad value to + # trigger a sync on the next check. + # NOTE: The hash must have a comma in it otherwise it will be ignored + # by the backend. + if action == 'DELETE': + hash_handler.put_hash('INCONSISTENT,INCONSISTENT') # All servers failed, reset server list and try again next time LOG.error(_('ServerProxy: %(action)s failure for all servers: ' '%(server)r'), @@ -577,12 +585,12 @@ class ServerPool(object): def rest_create_floatingip(self, tenant_id, floatingip): resource = FLOATINGIPS_PATH % (tenant_id, floatingip['id']) errstr = _("Unable to create floating IP: %s") - self.rest_action('PUT', resource, errstr=errstr) + self.rest_action('PUT', resource, floatingip, errstr=errstr) def rest_update_floatingip(self, tenant_id, floatingip, oldid): resource = FLOATINGIPS_PATH % (tenant_id, oldid) errstr = _("Unable to update floating IP: %s") - self.rest_action('PUT', resource, errstr=errstr) + self.rest_action('PUT', resource, floatingip, errstr=errstr) def rest_delete_floatingip(self, tenant_id, oldid): resource = FLOATINGIPS_PATH % (tenant_id, oldid) @@ -637,8 +645,9 @@ class HTTPSConnectionWithValidation(httplib.HTTPSConnection): if self.combined_cert: self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, cert_reqs=ssl.CERT_REQUIRED, - ca_certs=self.combined_cert) + ca_certs=self.combined_cert, + ssl_version=ssl.PROTOCOL_TLSv1) else: - self.sock = ssl.wrap_socket(sock, self.key_file, - self.cert_file, - cert_reqs=ssl.CERT_NONE) + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, + cert_reqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLSv1) diff --git a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py index 236822d217..16c53660f5 100644 --- a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py +++ b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py @@ -847,8 +847,6 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, LOG.debug(_('_send_delete_port_request: %s'), port['id']) n1kvclient = n1kv_client.Client() n1kvclient.delete_n1kv_port(vm_network['name'], port['id']) - if vm_network['port_count'] == 0: - n1kvclient.delete_vm_network(vm_network['name']) def _get_segmentation_id(self, context, id): """ 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/drivers/l2pop/mech_driver.py b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py index a98a27fd7d..c85acb1c73 100644 --- a/neutron/plugins/ml2/drivers/l2pop/mech_driver.py +++ b/neutron/plugins/ml2/drivers/l2pop/mech_driver.py @@ -38,32 +38,18 @@ class L2populationMechanismDriver(api.MechanismDriver, LOG.debug(_("Experimental L2 population driver")) self.rpc_ctx = n_context.get_admin_context_without_session() self.migrated_ports = {} - self.remove_fdb_entries = {} def _get_port_fdb_entries(self, port): return [[port['mac_address'], ip['ip_address']] for ip in port['fixed_ips']] - def delete_port_precommit(self, context): - port = context.current - agent_host = context.host - - if port['id'] not in self.remove_fdb_entries: - self.remove_fdb_entries[port['id']] = {} - - self.remove_fdb_entries[port['id']][agent_host] = ( - self._update_port_down(context, port, agent_host, 1)) - def delete_port_postcommit(self, context): port = context.current agent_host = context.host - if port['id'] in self.remove_fdb_entries: - for agent_host in list(self.remove_fdb_entries[port['id']]): - self.L2populationAgentNotify.remove_fdb_entries( - self.rpc_ctx, - self.remove_fdb_entries[port['id']][agent_host]) - self.remove_fdb_entries[port['id']].pop(agent_host, 0) - self.remove_fdb_entries.pop(port['id'], 0) + + fdb_entries = self._update_port_down(context, port, agent_host) + self.L2populationAgentNotify.remove_fdb_entries(self.rpc_ctx, + fdb_entries) def _get_diff_ips(self, orig, port): orig_ips = set([ip['ip_address'] for ip in orig['fixed_ips']]) @@ -260,8 +246,7 @@ class L2populationMechanismDriver(api.MechanismDriver, self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx, other_fdb_entries) - def _update_port_down(self, context, port, agent_host, - agent_active_ports_count_for_flooding=0): + def _update_port_down(self, context, port, agent_host): port_infos = self._get_port_infos(context, port, agent_host) if not port_infos: return @@ -277,7 +262,7 @@ class L2populationMechanismDriver(api.MechanismDriver, {'segment_id': segment['segmentation_id'], 'network_type': segment['network_type'], 'ports': {agent_ip: []}}} - if agent_active_ports == agent_active_ports_count_for_flooding: + if agent_active_ports == 0: # Agent is removing its last activated port in this network, # other agents needs to be notified to delete their flooding entry. other_fdb_entries[network_id]['ports'][agent_ip].append( diff --git a/neutron/plugins/ml2/drivers/mech_ofagent.py b/neutron/plugins/ml2/drivers/mech_ofagent.py index 012800183f..3b7b7e27ed 100644 --- a/neutron/plugins/ml2/drivers/mech_ofagent.py +++ b/neutron/plugins/ml2/drivers/mech_ofagent.py @@ -19,6 +19,7 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.agent import securitygroups_rpc from neutron.common import constants from neutron.extensions import portbindings from neutron.openstack.common import log @@ -40,11 +41,13 @@ class OfagentMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """ def __init__(self): + sg_enabled = securitygroups_rpc.is_firewall_enabled() + vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled, + portbindings.OVS_HYBRID_PLUG: sg_enabled} super(OfagentMechanismDriver, self).__init__( constants.AGENT_TYPE_OFA, portbindings.VIF_TYPE_OVS, - {portbindings.CAP_PORT_FILTER: True, - portbindings.OVS_HYBRID_PLUG: True}) + vif_details) def check_segment_for_agent(self, segment, agent): bridge_mappings = agent['configurations'].get('bridge_mappings', {}) diff --git a/neutron/plugins/ml2/drivers/mech_openvswitch.py b/neutron/plugins/ml2/drivers/mech_openvswitch.py index 0565b97301..995abb6632 100644 --- a/neutron/plugins/ml2/drivers/mech_openvswitch.py +++ b/neutron/plugins/ml2/drivers/mech_openvswitch.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.agent import securitygroups_rpc from neutron.common import constants from neutron.extensions import portbindings from neutron.openstack.common import log @@ -33,11 +34,13 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """ def __init__(self): + sg_enabled = securitygroups_rpc.is_firewall_enabled() + vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled, + portbindings.OVS_HYBRID_PLUG: sg_enabled} super(OpenvswitchMechanismDriver, self).__init__( constants.AGENT_TYPE_OVS, portbindings.VIF_TYPE_OVS, - {portbindings.CAP_PORT_FILTER: True, - portbindings.OVS_HYBRID_PLUG: True}) + vif_details) def check_segment_for_agent(self, segment, agent): mappings = agent['configurations'].get('bridge_mappings', {}) diff --git a/neutron/plugins/ml2/drivers/mechanism_odl.py b/neutron/plugins/ml2/drivers/mechanism_odl.py index 05ca33bb7f..e2fb229c6f 100644 --- a/neutron/plugins/ml2/drivers/mechanism_odl.py +++ b/neutron/plugins/ml2/drivers/mechanism_odl.py @@ -218,10 +218,7 @@ class OpenDaylightMechanismDriver(api.MechanismDriver): ctx.reraise = False key = collection_name[:-1] if len(to_be_synced) == 1 else ( collection_name) - - # 400 errors are returned if an object exists, which we ignore. - self.sendjson('post', collection_name, {key: to_be_synced}, - [requests.codes.bad_request]) + self.sendjson('post', collection_name, {key: to_be_synced}) @utils.synchronized('odl-sync-full') def sync_full(self, context): @@ -276,9 +273,7 @@ class OpenDaylightMechanismDriver(api.MechanismDriver): attr_filter = self.update_object_map[object_type] resource = context.current.copy() attr_filter(resource, context) - # 400 errors are returned if an object exists, which we ignore. - self.sendjson(method, urlpath, {object_type[:-1]: resource}, - [requests.codes.bad_request]) + self.sendjson(method, urlpath, {object_type[:-1]: resource}) except Exception: with excutils.save_and_reraise_exception(): self.out_of_sync = True @@ -291,7 +286,7 @@ class OpenDaylightMechanismDriver(api.MechanismDriver): for sg in port['security_groups']] port['security_groups'] = groups - def sendjson(self, method, urlpath, obj, ignorecodes=[]): + def sendjson(self, method, urlpath, obj): """Send json to the OpenDaylight controller.""" headers = {'Content-Type': 'application/json'} data = jsonutils.dumps(obj, indent=2) if obj else None @@ -301,10 +296,6 @@ class OpenDaylightMechanismDriver(api.MechanismDriver): r = requests.request(method, url=url, headers=headers, data=data, auth=self.auth, timeout=self.timeout) - - # ignorecodes contains a list of HTTP error codes to ignore. - if r.status_code in ignorecodes: - return r.raise_for_status() def bind_port(self, context): diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index d744f19fe1..d29deda6ce 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -990,7 +990,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, port = self._make_port_dict(port_db) network = self.get_network(context, port['network_id']) - mech_context = None + bound_mech_contexts = [] device_owner = port['device_owner'] if device_owner == const.DEVICE_OWNER_DVR_INTERFACE: bindings = db.get_dvr_port_bindings(context.session, id) @@ -998,6 +998,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, mech_context = driver_context.DvrPortContext( self, context, port, network, bind) self.mechanism_manager.delete_port_precommit(mech_context) + bound_mech_contexts.append(mech_context) else: mech_context = driver_context.PortContext(self, context, port, network, binding) @@ -1005,6 +1006,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, router_info = l3plugin.dvr_deletens_if_no_port(context, id) removed_routers += router_info self.mechanism_manager.delete_port_precommit(mech_context) + bound_mech_contexts.append(mech_context) self._delete_port_security_group_bindings(context, id) if l3plugin: router_ids = l3plugin.disassociate_floatingips( @@ -1029,12 +1031,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, {'id': router['router_id'], 'agent': router['agent_id']}) try: - # for both normal and DVR Interface ports, only one invocation of - # delete_port_postcommit. We use gather/scatter technique for DVR - # interface ports, where the bindings are gathered in - # delete_port_precommit() call earlier and scattered as l2pop - # rules to cloud nodes in delete_port_postcommit() here - if mech_context: + # Note that DVR Interface ports will have bindings on + # multiple hosts, and so will have multiple mech_contexts, + # while other ports typically have just one. + for mech_context in bound_mech_contexts: self.mechanism_manager.delete_port_postcommit(mech_context) except ml2_exc.MechanismDriverError: # TODO(apech) - One or more mechanism driver failed to @@ -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/plugins/mlnx/common/comm_utils.py b/neutron/plugins/mlnx/common/comm_utils.py index 834b5a2cb1..f426908f28 100644 --- a/neutron/plugins/mlnx/common/comm_utils.py +++ b/neutron/plugins/mlnx/common/comm_utils.py @@ -35,7 +35,6 @@ class RetryDecorator(object): :param retries: number of times to try before giving up :raises: exceptionToCheck """ - sleep_fn = time.sleep def __init__(self, exceptionToCheck, interval=cfg.CONF.ESWITCH.request_timeout / 1000, @@ -56,7 +55,7 @@ class RetryDecorator(object): except self.exc: LOG.debug(_("Request timeout - call again after " "%s seconds"), sleep_interval) - RetryDecorator.sleep_fn(sleep_interval) + time.sleep(sleep_interval) num_of_iter -= 1 sleep_interval *= self.backoff_rate diff --git a/neutron/plugins/nec/nec_plugin.py b/neutron/plugins/nec/nec_plugin.py index 7d26b1b59b..373dfdf24f 100644 --- a/neutron/plugins/nec/nec_plugin.py +++ b/neutron/plugins/nec/nec_plugin.py @@ -421,15 +421,11 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2, self._cleanup_ofc_tenant(context, tenant_id) def _get_base_binding_dict(self): - binding = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, - portbindings.VIF_DETAILS: { - # TODO(rkukura): Replace with new VIF security details - portbindings.CAP_PORT_FILTER: - 'security-group' in self.supported_extension_aliases, - portbindings.OVS_HYBRID_PLUG: True - } - } + sg_enabled = sg_rpc.is_firewall_enabled() + vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled, + portbindings.OVS_HYBRID_PLUG: sg_enabled} + binding = {portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, + portbindings.VIF_DETAILS: vif_details} return binding def _extend_port_dict_binding_portinfo(self, port_res, portinfo): diff --git a/neutron/plugins/ryu/ryu_neutron_plugin.py b/neutron/plugins/ryu/ryu_neutron_plugin.py index 8a18228362..c387518f8a 100644 --- a/neutron/plugins/ryu/ryu_neutron_plugin.py +++ b/neutron/plugins/ryu/ryu_neutron_plugin.py @@ -107,15 +107,7 @@ class RyuNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, def __init__(self, configfile=None): super(RyuNeutronPluginV2, self).__init__() - self.base_binding_dict = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, - portbindings.VIF_DETAILS: { - # TODO(rkukura): Replace with new VIF security details - portbindings.CAP_PORT_FILTER: - 'security-group' in self.supported_extension_aliases, - portbindings.OVS_HYBRID_PLUG: True - } - } + self.base_binding_dict = self._get_base_binding_dict() portbindings_base.register_port_dict_function() self.tunnel_key = db_api_v2.TunnelKey( cfg.CONF.OVS.tunnel_key_min, cfg.CONF.OVS.tunnel_key_max) @@ -134,6 +126,14 @@ class RyuNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, # register known all network list on startup self._create_all_tenant_network() + def _get_base_binding_dict(self): + sg_enabled = sg_rpc.is_firewall_enabled() + vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled, + portbindings.OVS_HYBRID_PLUG: sg_enabled} + binding = {portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, + portbindings.VIF_DETAILS: vif_details} + return binding + def _setup_rpc(self): self.service_topics = {svc_constants.CORE: topics.PLUGIN, svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN} diff --git a/neutron/plugins/vmware/dbexts/db.py b/neutron/plugins/vmware/dbexts/db.py index 6326460046..37dd6a77f7 100644 --- a/neutron/plugins/vmware/dbexts/db.py +++ b/neutron/plugins/vmware/dbexts/db.py @@ -33,10 +33,11 @@ def get_network_bindings(session, network_id): all()) -def get_network_bindings_by_vlanid(session, vlan_id): +def get_network_bindings_by_vlanid_and_physical_net(session, vlan_id, + phy_uuid): session = session or db.get_session() return (session.query(models.TzNetworkBinding). - filter_by(vlan_id=vlan_id). + filter_by(vlan_id=vlan_id, phy_uuid=phy_uuid). all()) diff --git a/neutron/plugins/vmware/plugins/base.py b/neutron/plugins/vmware/plugins/base.py index 3797891771..773e8b818e 100644 --- a/neutron/plugins/vmware/plugins/base.py +++ b/neutron/plugins/vmware/plugins/base.py @@ -756,10 +756,15 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin, for segment in segments: network_type = segment.get(pnet.NETWORK_TYPE) physical_network = segment.get(pnet.PHYSICAL_NETWORK) + physical_network_set = attr.is_attr_set(physical_network) segmentation_id = segment.get(pnet.SEGMENTATION_ID) network_type_set = attr.is_attr_set(network_type) segmentation_id_set = attr.is_attr_set(segmentation_id) + # If the physical_network_uuid isn't passed in use the default one. + if not physical_network_set: + physical_network = cfg.CONF.default_tz_uuid + err_msg = None if not network_type_set: err_msg = _("%s required") % pnet.NETWORK_TYPE @@ -782,8 +787,11 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin, 'max_id': constants.MAX_VLAN_TAG}) else: # Verify segment is not already allocated - bindings = nsx_db.get_network_bindings_by_vlanid( - context.session, segmentation_id) + bindings = ( + nsx_db.get_network_bindings_by_vlanid_and_physical_net( + context.session, segmentation_id, + physical_network) + ) if bindings: raise n_exc.VlanIdInUse( vlan_id=segmentation_id, diff --git a/neutron/tests/unit/_test_extension_portbindings.py b/neutron/tests/unit/_test_extension_portbindings.py index 4f72f58664..54e4c67d71 100644 --- a/neutron/tests/unit/_test_extension_portbindings.py +++ b/neutron/tests/unit/_test_extension_portbindings.py @@ -29,19 +29,27 @@ class PortBindingsTestCase(test_db_plugin.NeutronDbPluginV2TestCase): # VIF_TYPE must be overridden according to plugin vif_type VIF_TYPE = portbindings.VIF_TYPE_OTHER - # The plugin supports the port security feature such as - # security groups and anti spoofing. - HAS_PORT_FILTER = False + # VIF_DETAILS must be overridden according to plugin vif_details + VIF_DETAILS = None def _check_response_portbindings(self, port): self.assertEqual(port[portbindings.VIF_TYPE], self.VIF_TYPE) - vif_details = port[portbindings.VIF_DETAILS] # REVISIT(rkukura): Consider reworking tests to enable ML2 to bind + if self.VIF_TYPE not in [portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED]: - # TODO(rkukura): Replace with new VIF security details - self.assertEqual(vif_details[portbindings.CAP_PORT_FILTER], - self.HAS_PORT_FILTER) + # NOTE(r-mibu): The following six lines are just for backward + # compatibility. In this class, HAS_PORT_FILTER has been replaced + # by VIF_DETAILS which can be set expected vif_details to check, + # but all replacement of HAS_PORT_FILTER in successor has not been + # completed. + if self.VIF_DETAILS is None: + expected = getattr(self, 'HAS_PORT_FILTER', False) + vif_details = port[portbindings.VIF_DETAILS] + port_filter = vif_details[portbindings.CAP_PORT_FILTER] + self.assertEqual(expected, port_filter) + return + self.assertEqual(self.VIF_DETAILS, port[portbindings.VIF_DETAILS]) def _check_response_no_portbindings(self, port): self.assertIn('status', port) diff --git a/neutron/tests/unit/bigswitch/test_servermanager.py b/neutron/tests/unit/bigswitch/test_servermanager.py index 43723fe8f9..2d239ea4c5 100644 --- a/neutron/tests/unit/bigswitch/test_servermanager.py +++ b/neutron/tests/unit/bigswitch/test_servermanager.py @@ -23,6 +23,7 @@ from neutron import context from neutron import manager from neutron.openstack.common import importutils from neutron.openstack.common import jsonutils +from neutron.plugins.bigswitch.db import consistency_db as cdb from neutron.plugins.bigswitch import servermanager from neutron.tests.unit.bigswitch import test_restproxy_plugin as test_rp @@ -71,7 +72,8 @@ class ServerManagerTests(test_rp.BigSwitchProxyPluginV2TestCase): pl.servers._get_combined_cert_for_server, *('example.org', 443) ) - sslgetmock.assert_has_calls([mock.call(('example.org', 443))]) + sslgetmock.assert_has_calls([mock.call( + ('example.org', 443), ssl_version=ssl.PROTOCOL_TLSv1)]) def test_consistency_watchdog_stops_with_0_polling_interval(self): pl = manager.NeutronManager.get_plugin() @@ -410,6 +412,18 @@ class ServerManagerTests(test_rp.BigSwitchProxyPluginV2TestCase): sleep_call_count = rest_call_count - 1 tmock.assert_has_calls(sleep_call * sleep_call_count) + def test_delete_failure_sets_bad_hash(self): + pl = manager.NeutronManager.get_plugin() + hash_handler = cdb.HashHandler() + with mock.patch( + SERVERMANAGER + '.ServerProxy.rest_call', + return_value=(httplib.INTERNAL_SERVER_ERROR, 0, 0, 0) + ): + # a failed delete call should put a bad hash in the DB + pl.servers.rest_call('DELETE', '/', '', None, []) + self.assertEqual('INCONSISTENT,INCONSISTENT', + hash_handler.read_for_update()) + def test_conflict_triggers_sync(self): pl = manager.NeutronManager.get_plugin() with mock.patch( @@ -444,13 +458,17 @@ class ServerManagerTests(test_rp.BigSwitchProxyPluginV2TestCase): def test_floating_calls(self): pl = manager.NeutronManager.get_plugin() with mock.patch(SERVERMANAGER + '.ServerPool.rest_action') as ramock: - pl.servers.rest_create_floatingip('tenant', {'id': 'somefloat'}) - pl.servers.rest_update_floatingip('tenant', {'name': 'myfl'}, 'id') + body1 = {'id': 'somefloat'} + body2 = {'name': 'myfl'} + pl.servers.rest_create_floatingip('tenant', body1) + pl.servers.rest_update_floatingip('tenant', body2, 'id') pl.servers.rest_delete_floatingip('tenant', 'oldid') ramock.assert_has_calls([ mock.call('PUT', '/tenants/tenant/floatingips/somefloat', + body1, errstr=u'Unable to create floating IP: %s'), mock.call('PUT', '/tenants/tenant/floatingips/id', + body2, errstr=u'Unable to update floating IP: %s'), mock.call('DELETE', '/tenants/tenant/floatingips/oldid', errstr=u'Unable to delete floating IP: %s') @@ -465,7 +483,8 @@ class ServerManagerTests(test_rp.BigSwitchProxyPluginV2TestCase): ('www.example.org', 443), 90, '127.0.0.1' )]) self.wrap_mock.assert_has_calls([mock.call( - self.socket_mock(), None, None, cert_reqs=ssl.CERT_NONE + self.socket_mock(), None, None, cert_reqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLSv1 )]) self.assertEqual(con.sock, self.wrap_mock()) @@ -480,7 +499,8 @@ class ServerManagerTests(test_rp.BigSwitchProxyPluginV2TestCase): )]) self.wrap_mock.assert_has_calls([mock.call( self.socket_mock(), None, None, ca_certs='SOMECERTS.pem', - cert_reqs=ssl.CERT_REQUIRED + cert_reqs=ssl.CERT_REQUIRED, + ssl_version=ssl.PROTOCOL_TLSv1 )]) self.assertEqual(con.sock, self.wrap_mock()) @@ -500,7 +520,8 @@ class ServerManagerTests(test_rp.BigSwitchProxyPluginV2TestCase): ('www.example.org', 443), 90, '127.0.0.1' )]) self.wrap_mock.assert_has_calls([mock.call( - self.socket_mock(), None, None, cert_reqs=ssl.CERT_NONE + self.socket_mock(), None, None, cert_reqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLSv1 )]) # _tunnel() doesn't take any args tunnel_mock.assert_has_calls([mock.call()]) diff --git a/neutron/tests/unit/bigswitch/test_ssl.py b/neutron/tests/unit/bigswitch/test_ssl.py index 6a30744236..f921a4165e 100644 --- a/neutron/tests/unit/bigswitch/test_ssl.py +++ b/neutron/tests/unit/bigswitch/test_ssl.py @@ -13,6 +13,7 @@ # under the License. import contextlib import os +import ssl import mock from oslo.config import cfg @@ -106,7 +107,8 @@ class TestSslSticky(test_ssl_certificate_base): self.getcacerts_m.assert_has_calls([mock.call(self.ca_certs_path)]) # cert should have been fetched via SSL lib self.sslgetcert_m.assert_has_calls( - [mock.call((self.servername, 443))] + [mock.call((self.servername, 443), + ssl_version=ssl.PROTOCOL_TLSv1)] ) # cert should have been recorded diff --git a/neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py b/neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py index f926a49200..09d0e942e1 100644 --- a/neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py +++ b/neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py @@ -808,6 +808,24 @@ class TestN1kvPorts(test_plugin.TestPortsV2, # for network object clean up to succeed. client_patch.stop() + def test_delete_last_port_vmnetwork_cleanup(self): + """Test whether VMNetwork is cleaned up from db on last port delete.""" + db_session = db.get_session() + with self.port() as port: + pt = port['port'] + self.assertIsNotNone(n1kv_db_v2. + get_vm_network(db_session, + pt['n1kv:profile_id'], + pt['network_id'])) + req = self.new_delete_request('ports', port['port']['id']) + req.get_response(self.api) + # Verify VMNetwork is cleaned up from the database on port delete. + self.assertRaises(c_exc.VMNetworkNotFound, + n1kv_db_v2.get_vm_network, + db_session, + pt['n1kv:profile_id'], + pt['network_id']) + class TestN1kvPolicyProfiles(N1kvPluginTestCase): def setUp(self): 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_mech_agent.py b/neutron/tests/unit/ml2/_test_mech_agent.py index a42eca0c2a..71aeecf13c 100644 --- a/neutron/tests/unit/ml2/_test_mech_agent.py +++ b/neutron/tests/unit/ml2/_test_mech_agent.py @@ -120,7 +120,7 @@ class AgentMechanismBaseTestCase(base.BaseTestCase): # The following must be overridden for the specific mechanism # driver being tested: VIF_TYPE = None - CAP_PORT_FILTER = None + VIF_DETAILS = None AGENT_TYPE = None AGENTS = None AGENTS_DEAD = None @@ -136,8 +136,17 @@ class AgentMechanismBaseTestCase(base.BaseTestCase): self.assertEqual(context._bound_vif_type, self.VIF_TYPE) vif_details = context._bound_vif_details self.assertIsNotNone(vif_details) - self.assertEqual(vif_details[portbindings.CAP_PORT_FILTER], - self.CAP_PORT_FILTER) + # NOTE(r-mibu): The following five lines are just for backward + # compatibility. In this class, HAS_PORT_FILTER has been replaced + # by VIF_DETAILS which can be set expected vif_details to check, + # but all replacement of HAS_PORT_FILTER in successor has not been + # completed. + if self.VIF_DETAILS is None: + expected = getattr(self, 'CAP_PORT_FILTER', None) + port_filter = vif_details[portbindings.CAP_PORT_FILTER] + self.assertEqual(expected, port_filter) + return + self.assertEqual(self.VIF_DETAILS, vif_details) class AgentMechanismGenericTestCase(AgentMechanismBaseTestCase): diff --git a/neutron/tests/unit/ml2/drivers/test_l2population.py b/neutron/tests/unit/ml2/drivers/test_l2population.py index cfe76d3a57..85d8b37a73 100644 --- a/neutron/tests/unit/ml2/drivers/test_l2population.py +++ b/neutron/tests/unit/ml2/drivers/test_l2population.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import contextlib import mock from neutron.common import constants @@ -24,6 +25,7 @@ from neutron.extensions import providernet as pnet from neutron import manager from neutron.openstack.common import timeutils from neutron.plugins.ml2 import config as config +from neutron.plugins.ml2.drivers.l2pop import mech_driver as l2pop_mech_driver from neutron.plugins.ml2 import managers from neutron.plugins.ml2 import rpc from neutron.tests.unit import test_db_plugin as test_plugin @@ -794,3 +796,17 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase): self.mock_fanout.assert_called_with( mock.ANY, expected, topic=self.fanout_topic) + + def test_delete_port_invokes_update_device_down(self): + l2pop_mech = l2pop_mech_driver.L2populationMechanismDriver() + l2pop_mech.L2PopulationAgentNotify = mock.Mock() + l2pop_mech.rpc_ctx = mock.Mock() + with contextlib.nested( + mock.patch.object(l2pop_mech, + '_update_port_down', + return_value=None), + mock.patch.object(l2pop_mech.L2PopulationAgentNotify, + 'remove_fdb_entries')) as (upd_port_down, + rem_fdb_entries): + l2pop_mech.delete_port_postcommit(mock.Mock()) + self.assertTrue(upd_port_down.called)
\ No newline at end of file diff --git a/neutron/tests/unit/ml2/drivers/test_ofagent_mech.py b/neutron/tests/unit/ml2/drivers/test_ofagent_mech.py index 3187f296b4..8c62e1e79f 100644 --- a/neutron/tests/unit/ml2/drivers/test_ofagent_mech.py +++ b/neutron/tests/unit/ml2/drivers/test_ofagent_mech.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo.config import cfg + from neutron.common import constants from neutron.extensions import portbindings from neutron.plugins.ml2.drivers import mech_ofagent @@ -21,7 +23,8 @@ from neutron.tests.unit.ml2 import _test_mech_agent as base class OfagentMechanismBaseTestCase(base.AgentMechanismBaseTestCase): VIF_TYPE = portbindings.VIF_TYPE_OVS - CAP_PORT_FILTER = True + VIF_DETAILS = {portbindings.CAP_PORT_FILTER: True, + portbindings.OVS_HYBRID_PLUG: True} AGENT_TYPE = constants.AGENT_TYPE_OFA GOOD_MAPPINGS = {'fake_physical_network': 'fake_interface'} @@ -49,6 +52,17 @@ class OfagentMechanismBaseTestCase(base.AgentMechanismBaseTestCase): self.driver.initialize() +class OfagentMechanismSGDisabledBaseTestCase(OfagentMechanismBaseTestCase): + VIF_DETAILS = {portbindings.CAP_PORT_FILTER: False, + portbindings.OVS_HYBRID_PLUG: False} + + def setUp(self): + cfg.CONF.set_override('enable_security_group', + False, + group='SECURITYGROUP') + super(OfagentMechanismSGDisabledBaseTestCase, self).setUp() + + class OfagentMechanismGenericTestCase(OfagentMechanismBaseTestCase, base.AgentMechanismGenericTestCase): pass @@ -74,12 +88,19 @@ class OfagentMechanismGreTestCase(OfagentMechanismBaseTestCase, pass +class OfagentMechanismSGDisabledLocalTestCase( + OfagentMechanismSGDisabledBaseTestCase, + base.AgentMechanismLocalTestCase): + pass + + # The following tests are for deprecated "bridge_mappings". # TODO(yamamoto): Remove them. class OfagentMechanismPhysBridgeTestCase(base.AgentMechanismBaseTestCase): VIF_TYPE = portbindings.VIF_TYPE_OVS - CAP_PORT_FILTER = True + VIF_DETAILS = {portbindings.CAP_PORT_FILTER: True, + portbindings.OVS_HYBRID_PLUG: True} AGENT_TYPE = constants.AGENT_TYPE_OFA GOOD_MAPPINGS = {'fake_physical_network': 'fake_bridge'} diff --git a/neutron/tests/unit/ml2/test_mech_openvswitch.py b/neutron/tests/unit/ml2/test_mech_openvswitch.py index b1af1b7fae..456d6f02cc 100644 --- a/neutron/tests/unit/ml2/test_mech_openvswitch.py +++ b/neutron/tests/unit/ml2/test_mech_openvswitch.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo.config import cfg + from neutron.common import constants from neutron.extensions import portbindings from neutron.plugins.ml2.drivers import mech_openvswitch @@ -21,7 +23,8 @@ from neutron.tests.unit.ml2 import _test_mech_agent as base class OpenvswitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase): VIF_TYPE = portbindings.VIF_TYPE_OVS - CAP_PORT_FILTER = True + VIF_DETAILS = {portbindings.CAP_PORT_FILTER: True, + portbindings.OVS_HYBRID_PLUG: True} AGENT_TYPE = constants.AGENT_TYPE_OVS GOOD_MAPPINGS = {'fake_physical_network': 'fake_bridge'} @@ -49,6 +52,18 @@ class OpenvswitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase): self.driver.initialize() +class OpenvswitchMechanismSGDisabledBaseTestCase( + OpenvswitchMechanismBaseTestCase): + VIF_DETAILS = {portbindings.CAP_PORT_FILTER: False, + portbindings.OVS_HYBRID_PLUG: False} + + def setUp(self): + cfg.CONF.set_override('enable_security_group', + False, + group='SECURITYGROUP') + super(OpenvswitchMechanismSGDisabledBaseTestCase, self).setUp() + + class OpenvswitchMechanismGenericTestCase(OpenvswitchMechanismBaseTestCase, base.AgentMechanismGenericTestCase): pass @@ -72,3 +87,9 @@ class OpenvswitchMechanismVlanTestCase(OpenvswitchMechanismBaseTestCase, class OpenvswitchMechanismGreTestCase(OpenvswitchMechanismBaseTestCase, base.AgentMechanismGreTestCase): pass + + +class OpenvswitchMechanismSGDisabledLocalTestCase( + OpenvswitchMechanismSGDisabledBaseTestCase, + base.AgentMechanismLocalTestCase): + pass diff --git a/neutron/tests/unit/ml2/test_mechanism_odl.py b/neutron/tests/unit/ml2/test_mechanism_odl.py index 8d87eb5a61..5a93429f5a 100644 --- a/neutron/tests/unit/ml2/test_mechanism_odl.py +++ b/neutron/tests/unit/ml2/test_mechanism_odl.py @@ -286,18 +286,17 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): 'delete', **kwargs) def test_create_network_postcommit(self): - for status_code in (requests.codes.created, - requests.codes.bad_request): - self._test_create_resource_postcommit('network', status_code) - self._test_create_resource_postcommit( - 'network', requests.codes.unauthorized, - requests.exceptions.HTTPError) + self._test_create_resource_postcommit('network', + requests.codes.created) + for status_code in (requests.codes.bad_request, + requests.codes.unauthorized): + self._test_create_resource_postcommit( + 'network', status_code, requests.exceptions.HTTPError) def test_create_subnet_postcommit(self): - for status_code in (requests.codes.created, - requests.codes.bad_request): - self._test_create_resource_postcommit('subnet', status_code) - for status_code in (requests.codes.unauthorized, + self._test_create_resource_postcommit('subnet', requests.codes.created) + for status_code in (requests.codes.bad_request, + requests.codes.unauthorized, requests.codes.forbidden, requests.codes.not_found, requests.codes.conflict, @@ -306,10 +305,9 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): 'subnet', status_code, requests.exceptions.HTTPError) def test_create_port_postcommit(self): - for status_code in (requests.codes.created, - requests.codes.bad_request): - self._test_create_resource_postcommit('port', status_code) - for status_code in (requests.codes.unauthorized, + self._test_create_resource_postcommit('port', requests.codes.created) + for status_code in (requests.codes.bad_request, + requests.codes.unauthorized, requests.codes.forbidden, requests.codes.not_found, requests.codes.conflict, @@ -319,19 +317,17 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): 'port', status_code, requests.exceptions.HTTPError) def test_update_network_postcommit(self): - for status_code in (requests.codes.ok, - requests.codes.bad_request): - self._test_update_resource_postcommit('network', status_code) - for status_code in (requests.codes.forbidden, + self._test_update_resource_postcommit('network', requests.codes.ok) + for status_code in (requests.codes.bad_request, + requests.codes.forbidden, requests.codes.not_found): self._test_update_resource_postcommit( 'network', status_code, requests.exceptions.HTTPError) def test_update_subnet_postcommit(self): - for status_code in (requests.codes.ok, - requests.codes.bad_request): - self._test_update_resource_postcommit('subnet', status_code) - for status_code in (requests.codes.unauthorized, + self._test_update_resource_postcommit('subnet', requests.codes.ok) + for status_code in (requests.codes.bad_request, + requests.codes.unauthorized, requests.codes.forbidden, requests.codes.not_found, requests.codes.not_implemented): @@ -339,10 +335,9 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): 'subnet', status_code, requests.exceptions.HTTPError) def test_update_port_postcommit(self): - for status_code in (requests.codes.ok, - requests.codes.bad_request): - self._test_update_resource_postcommit('port', status_code) - for status_code in (requests.codes.unauthorized, + self._test_update_resource_postcommit('port', requests.codes.ok) + for status_code in (requests.codes.bad_request, + requests.codes.unauthorized, requests.codes.forbidden, requests.codes.not_found, requests.codes.conflict, 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/mlnx/test_mlnx_comm_utils.py b/neutron/tests/unit/mlnx/test_mlnx_comm_utils.py index bb8bdcced7..49f2eaceea 100644 --- a/neutron/tests/unit/mlnx/test_mlnx_comm_utils.py +++ b/neutron/tests/unit/mlnx/test_mlnx_comm_utils.py @@ -29,8 +29,7 @@ class WrongException(Exception): class TestRetryDecorator(base.BaseTestCase): def setUp(self): super(TestRetryDecorator, self).setUp() - self.sleep_fn_p = mock.patch.object(comm_utils.RetryDecorator, - 'sleep_fn') + self.sleep_fn_p = mock.patch("time.sleep") self.sleep_fn = self.sleep_fn_p.start() def test_no_retry_required(self): diff --git a/neutron/tests/unit/nec/test_portbindings.py b/neutron/tests/unit/nec/test_portbindings.py index 9dc61ed54c..a2ceb321b3 100644 --- a/neutron/tests/unit/nec/test_portbindings.py +++ b/neutron/tests/unit/nec/test_portbindings.py @@ -28,7 +28,8 @@ from neutron.tests.unit import test_security_groups_rpc as test_sg_rpc class TestNecPortBinding(test_bindings.PortBindingsTestCase, test_nec_plugin.NecPluginV2TestCase): VIF_TYPE = portbindings.VIF_TYPE_OVS - HAS_PORT_FILTER = True + VIF_DETAILS = {portbindings.CAP_PORT_FILTER: True, + portbindings.OVS_HYBRID_PLUG: True} ENABLE_SG = True FIREWALL_DRIVER = test_sg_rpc.FIREWALL_HYBRID_DRIVER @@ -41,7 +42,8 @@ class TestNecPortBinding(test_bindings.PortBindingsTestCase, class TestNecPortBindingNoSG(TestNecPortBinding): - HAS_PORT_FILTER = False + VIF_DETAILS = {portbindings.CAP_PORT_FILTER: False, + portbindings.OVS_HYBRID_PLUG: False} ENABLE_SG = False FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER 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/plumgrid/test_plumgrid_plugin.py b/neutron/tests/unit/plumgrid/test_plumgrid_plugin.py index ff275205b1..b40883976c 100644 --- a/neutron/tests/unit/plumgrid/test_plumgrid_plugin.py +++ b/neutron/tests/unit/plumgrid/test_plumgrid_plugin.py @@ -81,6 +81,7 @@ class TestPlumgridPluginSubnetsV2(test_plugin.TestSubnetsV2, _unsupported = ( 'test_create_subnet_default_gw_conflict_allocation_pool_returns_409', 'test_create_subnet_defaults', 'test_create_subnet_gw_values', + 'test_create_subnet_ipv6_gw_values', 'test_update_subnet_gateway_in_allocation_pool_returns_409', 'test_update_subnet_allocation_pools', 'test_update_subnet_allocation_pools_invalid_pool_for_cidr') diff --git a/neutron/tests/unit/test_attributes.py b/neutron/tests/unit/test_attributes.py index 2fb268d07a..f8cb462b3e 100644 --- a/neutron/tests/unit/test_attributes.py +++ b/neutron/tests/unit/test_attributes.py @@ -281,6 +281,7 @@ class TestAttributes(base.BaseTestCase): ['www.hostname.com', 'www.hostname.com'], ['77.hostname.com'], ['1000.0.0.1'], + ['111111111111111111111111111111111111111111111111111111111111'], # noqa None] for ns in ns_pools: diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py index 74292fac07..24eab08440 100644 --- a/neutron/tests/unit/test_db_plugin.py +++ b/neutron/tests/unit/test_db_plugin.py @@ -1419,6 +1419,22 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s self.assertEqual(res.status_int, webob.exc.HTTPClientError.code) + def test_requested_fixed_ip_address_v6_slaac_router_iface(self): + with self.subnet(gateway_ip='fe80::1', + cidr='fe80::/64', + ip_version=6, + ipv6_address_mode=constants.IPV6_SLAAC) as subnet: + kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], + 'ip_address': 'fe80::1'}]} + net_id = subnet['subnet']['network_id'] + device_owner = constants.DEVICE_OWNER_ROUTER_INTF + res = self._create_port(self.fmt, net_id=net_id, + device_owner=device_owner, **kwargs) + port = self.deserialize(self.fmt, res) + self.assertEqual(len(port['port']['fixed_ips']), 1) + self.assertEqual(port['port']['fixed_ips'][0]['ip_address'], + 'fe80::1') + def test_requested_subnet_id_v6_slaac(self): with self.subnet(gateway_ip='fe80::1', cidr='2607:f0d0:1002:51::/64', @@ -2830,6 +2846,38 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): self._test_create_subnet(expected=expected, gateway_ip=gateway) + def test_create_subnet_ipv6_gw_values(self): + cidr = '2001::/64' + # Gateway is last IP in IPv6 DHCPv6 stateful subnet + gateway = '2001::ffff:ffff:ffff:fffe' + allocation_pools = [{'start': '2001::1', + 'end': '2001::ffff:ffff:ffff:fffd'}] + expected = {'gateway_ip': gateway, + 'cidr': cidr, + 'allocation_pools': allocation_pools} + self._test_create_subnet(expected=expected, gateway_ip=gateway, + cidr=cidr, ip_version=6, + ipv6_ra_mode=constants.DHCPV6_STATEFUL, + ipv6_address_mode=constants.DHCPV6_STATEFUL) + # Gateway is first IP in IPv6 DHCPv6 stateful subnet + gateway = '2001::1' + allocation_pools = [{'start': '2001::2', + 'end': '2001::ffff:ffff:ffff:fffe'}] + expected = {'gateway_ip': gateway, + 'cidr': cidr, + 'allocation_pools': allocation_pools} + self._test_create_subnet(expected=expected, gateway_ip=gateway, + cidr=cidr, ip_version=6, + ipv6_ra_mode=constants.DHCPV6_STATEFUL, + ipv6_address_mode=constants.DHCPV6_STATEFUL) + # Gateway not specified for IPv6 SLAAC subnet + expected = {'gateway_ip': None, + 'cidr': cidr} + self._test_create_subnet(expected=expected, + cidr=cidr, ip_version=6, + ipv6_ra_mode=constants.IPV6_SLAAC, + ipv6_address_mode=constants.IPV6_SLAAC) + def test_create_subnet_gw_outside_cidr_returns_400(self): with self.network() as network: self._create_subnet(self.fmt, diff --git a/neutron/tests/unit/test_dhcp_agent.py b/neutron/tests/unit/test_dhcp_agent.py index c6f98643fe..5e02428b0d 100644 --- a/neutron/tests/unit/test_dhcp_agent.py +++ b/neutron/tests/unit/test_dhcp_agent.py @@ -64,6 +64,12 @@ fake_subnet3 = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', network_id='12345678-1234-5678-1234567890ab', cidr='192.168.1.1/24', enable_dhcp=True)) +fake_ipv6_subnet = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', + network_id='12345678-1234-5678-1234567890ab', + cidr='2001:0db8::1:0:0:1/128', enable_dhcp=True, + tenant_id=fake_tenant_id, + gateway_ip='2001:0db8::1:0:0:1', ip_version=6)) + fake_meta_subnet = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', network_id='12345678-1234-5678-1234567890ab', cidr='169.254.169.252/30', @@ -104,6 +110,12 @@ fake_network = dhcp.NetModel(True, dict(id='12345678-1234-5678-1234567890ab', subnets=[fake_subnet1, fake_subnet2], ports=[fake_port1])) +fake_network_ipv6 = dhcp.NetModel(True, dict( + id='12345678-1234-5678-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', + admin_state_up=True, + subnets=[fake_ipv6_subnet])) + isolated_network = dhcp.NetModel( True, dict( id='12345678-1234-5678-1234567890ab', @@ -555,6 +567,9 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): def test_enable_dhcp_helper(self): self._enable_dhcp_helper(fake_network) + def test_enable_dhcp_helper_ipv6_network(self): + self._enable_dhcp_helper(fake_network_ipv6) + def test_enable_dhcp_helper_down_network(self): self.plugin.get_network_info.return_value = fake_down_network self.dhcp.enable_dhcp_helper(fake_down_network.id) 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/neutron/tests/unit/vmware/extensions/test_providernet.py b/neutron/tests/unit/vmware/extensions/test_providernet.py index f6057f1451..6f27011b26 100644 --- a/neutron/tests/unit/vmware/extensions/test_providernet.py +++ b/neutron/tests/unit/vmware/extensions/test_providernet.py @@ -47,6 +47,15 @@ class TestProvidernet(test_nsx_plugin.NsxPluginV2TestCase): self.assertEqual(net['network'][pnet.SEGMENTATION_ID], 411) self.assertEqual(net['network'][pnet.PHYSICAL_NETWORK], 'physnet1') + # Test that we can create another provider network using the same + # vlan_id on another physical network. + data['network'][pnet.PHYSICAL_NETWORK] = 'physnet2' + network_req = self.new_create_request('networks', data, self.fmt) + net = self.deserialize(self.fmt, network_req.get_response(self.api)) + self.assertEqual(net['network'][pnet.NETWORK_TYPE], 'vlan') + self.assertEqual(net['network'][pnet.SEGMENTATION_ID], 411) + self.assertEqual(net['network'][pnet.PHYSICAL_NETWORK], 'physnet2') + class TestMultiProviderNetworks(test_nsx_plugin.NsxPluginV2TestCase): @@ -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 |