diff -Nru neutron-7.0.4/AUTHORS neutron-7.1.1/AUTHORS --- neutron-7.0.4/AUTHORS 2016-03-29 17:48:06.000000000 +0000 +++ neutron-7.1.1/AUTHORS 2016-06-10 01:44:41.000000000 +0000 @@ -24,6 +24,7 @@ Alessio Ababilov Alessio Ababilov Alex Holden +Alex Oughton Alexander Ignatov Alexander Maretskiy Alexei Kornienko @@ -102,6 +103,7 @@ ChuckC Clark Boylan Claudiu Belu +Clayton O'Neill Clint Byrum Cyril Roelandt Cyril Roelandt @@ -131,6 +133,7 @@ Dirk Mueller Divya ChanneGowda Dmitry Ratushnyy +Dmitry Sutyagin Dongcan Ye Doug Hellmann Doug Hellmann @@ -181,6 +184,7 @@ He Yongli Hemanth Ravi Henry Gessau +Henry Gessau HenryGessau HenryVIII Hiroaki KAWAI @@ -313,6 +317,7 @@ Marga Millet Margaret Frances Mark McClain +Mark McClain Mark McClain Mark McLoughlin Mark T. Voelker @@ -353,6 +358,7 @@ Mr. Bojangles Mukul Murali Birru +NGUYEN TUONG THANH Nachi Ueno Nachi Ueno Nader Lahouti @@ -397,6 +403,7 @@ Ramanjaneya Ramu Ramamurthy Rawlin Peters +Rawlin Peters Ray Chen Rich Curran Rick Clark @@ -516,6 +523,7 @@ Sumit Naiksatam Sushil Kumar Swaminathan Vasudevan +Swaminathan Vasudevan Sylvain Afchain Takaaki Suzuki Takashi NATSUME @@ -555,6 +563,7 @@ Wei Wang WeiHu Weidong Shao +Wenxin Wang Wlodzimierz Borkowski Wu Wenxiang Xiaolin Zhang @@ -691,8 +700,10 @@ whitekid xchenum yangxurong +yaowei yuyangbj zengfagao zhhuabj zhiyuan_cai +zoukeke@cmss.chinamobile.com Édouard Thuleau diff -Nru neutron-7.0.4/bin/neutron-rootwrap-xen-dom0 neutron-7.1.1/bin/neutron-rootwrap-xen-dom0 --- neutron-7.0.4/bin/neutron-rootwrap-xen-dom0 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/bin/neutron-rootwrap-xen-dom0 2016-06-10 01:43:08.000000000 +0000 @@ -113,11 +113,14 @@ try: session = XenAPI.Session(url) session.login_with_password(username, password) - host = session.xenapi.session.get_this_host(session.handle) - result = session.xenapi.host.call_plugin( - host, 'netwrap', 'run_command', - {'cmd': json.dumps(user_args), 'cmd_input': json.dumps(cmd_input)}) - return json.loads(result) + try: + host = session.xenapi.session.get_this_host(session.handle) + result = session.xenapi.host.call_plugin( + host, 'netwrap', 'run_command', + {'cmd': json.dumps(user_args), 'cmd_input': json.dumps(cmd_input)}) + return json.loads(result) + finally: + session.xenapi.session.logout() except Exception as e: traceback.print_exc() sys.exit(RC_XENAPI_ERROR) diff -Nru neutron-7.0.4/ChangeLog neutron-7.1.1/ChangeLog --- neutron-7.0.4/ChangeLog 2016-03-29 17:48:05.000000000 +0000 +++ neutron-7.1.1/ChangeLog 2016-06-10 01:44:41.000000000 +0000 @@ -1,22 +1,120 @@ CHANGES ======= +7.1.1 +----- + +* DVR: Rename dvr_vmarp_table_update +* DVR: Rename dvr_update_router_addvm function +* DVR: Remove get_port call from dvr_update_router_addvm +* Revert "DVR: Clear SNAT namespace when agent restarts after router move" +* Make deepcopy of update body in API layer +* fullstack: Use noop firewall +* Allow to control to use constraint env for functional jobs + +7.1.0 +----- + +* Support MTU advertisement using IPv6 RAs +* Register RA and PD config options in l3-agent +* Updated from global requirements +* Enforce UUID of port/subnet ID for router interfaces +* Restart dsnmasq on any network subnet change +* Outerjoin to networks for port ownership filter +* RBAC: Fix port query and deletion for network owner +* Use correct session in update_allocation_pools +* Add exponential back-off RPC client +* DVR: Clear SNAT namespace when agent restarts after router move +* Use admin context when removing DVR router on vm port deletion +* Clear DVR MAC on last agent deletion from host +* Updated from global requirements +* Cleanup stale OVS flows for physical bridges +* IPtables firewall prevent ICMPv6 spoofing +* Mitigate restriction for fixed ips per dhcp port +* OVS: Add mac spoofing filtering to flows +* Delete fipnamespace when external net removed on DVR +* unbreak unit test caused by c5fa665de3173f3ad82cc3e7624b5968bc52c08d +* Make agent interface plugging utilize network MTU +* Consume service plugins queues in RPC workers +* Only prevent l3 port deletion if router exists +* Don't disconnect br-int from phys br if connected +* ML2: update port's status to DOWN if its binding info has changed +* Updated from global requirements +* Fix corrupted release note in Liberty +* Iptables firewall prevent IP spoofed DHCP requests +* Switched from fixtures to mock to mock out starting RPC consumers +* DVR: Increase the link-local address pair range +* L3 agent: match format used by iptables +* Don't drop 'protocol' from client supplied security_group_rule dict +* Split the FIP Namespace delete in L3 agent for DVR +* Don't delete br-int to br-tun patch on startup +* Delete metadata_proxy for network if it is not needed +* Adds base in-tree functional testing of the dhcp agent (OVS) +* fix _validate_shared_update for dvr router ports +* add arp_responder flag to linuxbridge agent +* Switches metering agent to stateless iptables +* Fix setting peer to bridge interfaces +* Refactor and fix dummy process fixture +* Remove obsolete keepalived PID files before start +* Support Routes==2.3 +* port security: gracefully handle resources with no bindings +* Move test_extend_port_dict_no_port_security to where it belongs to +* SG protocol validation to allow numbers or names +* DVR: rebind port if ofport changes +* Linux Bridge: Add mac spoofing filtering to ebtables +* Change log level from error to warning +* Ensure metadata agent doesn't use SSL for UNIX socket +* DHCP: Downgrade 'network has been deleted' logs +* Catch DBDuplicateEntry errors in RBAC code +* Change the exception type from ValueError to IpamValueInvalid +* L3 agent: log traceback on floating ip setup failure +* Update default gateway in the fip namespace after subnet-update +* Make run_ofctl check for socket error +* Skip fullstack L3 HA test +* lb: avoid doing nova VIF work plumbing tap to qbr +* Remove test_external_network_visibility +* Check tap bridge timestamps to detect local changes + 7.0.4 ----- +* Catch DB reference errors in binding DVR ports +* Move db query to fetch down bindings under try/except +* ML2: Downgrade 'no bound segment' warning +* Close XenAPI sessions in neutron-rootwrap-xen-dom0 +* Add IPAllocation object to session info to stop GC +* Allow address pairs to be cleared with None +* Downgrade "device not found" log message +* Downgrade network not found log in DHCP RPC +* Fix branch order when upgrading to alembic milestone +* Watch for 'new' events in ovsdb monitor for ofport * Add option for nova endpoint type * Update devstack plugin for dependent packages +* Removes host file contents from DHCP agent logs +* Add VLAN tag info to port before applying SG initial setup * De-dup conntrack deletions before running them +* Optimize and refactor router delete execution * Unmarshall portinfo on update_fdb_entries calls +* radvd prefix configuration for DHCPV6_Stateful RA +* Prevent PD subnets with incorrect IPv6 modes * Avoid DuplicateOptError in functional tests +* Correct insufficient name for external process in manager log +* Add network_update RPC into SR-IOV agent * Retry port create/update on duplicate db records +* Separate the command for replace_port to delete and add +* Corrected wrong ethertype exception message * Catch PortNotFound after HA router race condition +* Add more log when dhcp agent sync_state +* Prevent binding IPv6 addresses to Neutron interfaces * Documenting network_device_mtu in agents config files * Make all tox targets constrained +* Do not remove router from dvr_snat agents on dvr port deletion * Filter HA routers without HA interface and state * Correct return values for bridge sysctl calls +* Fix port relationship for DVRPortBinding * Add tests for RPC methods/classes * Fix sanity check --no* BoolOpts +* Decomposition phase2 for MidoNet plugin * Add extension requirement in port-security api test * Fix for adding gateway with IP outside subnet * Add the rebinding chance in _bind_port_if_needed @@ -36,6 +134,7 @@ * DHCP: fix regression with DNS nameservers * Add generated port id to port dict * Protect 'show' and 'index' with Retry decorator +* Support rootwrap sysctl and conntrack commands for non-l3 nodes * Add unit test cases for linuxbridge agent when prevent_arp_spoofing is True * Rule, member updates are missed with enhanced rpc * Add relationship between port and floating ip @@ -1523,7 +1622,7 @@ * Moving VLAN Transparency support from core to extension * Re-use context session in ML2 DB get_port_binding_host * Consider all address scopes in init_l3 -* Improves the description string for the config parameter metadata_workers +* Improves the description string for the config parameter metadata_workers * Fix intermittent UT failures in test_utils * OOP cleanup: start protected method names with underscore * Enhance TESTING.rst @@ -1600,7 +1699,7 @@ * Remove neutron.tests.sub_base * Fix test case for DHCP agent interface restart * Store and log correct exception info -* Test to verify shared attribute of network +* Test to verify shared attribute of network * Enable Process Monitor by default * Reload DHCP interface when its port is updated * Don't eagerly load ranges from IPAllocationPool @@ -1627,7 +1726,7 @@ * Cisco Nexus1000V ML2 Mechanism Driver * Rename/move/remove HaRouter methods * lb-agent: use 'replace' instead of 'add' with 'bridge fdb' -* Add some useful notes in devref/db_layer.rst +* Add some useful notes in devref/db_layer.rst * Fix a usage error of joinedload + filter in l3 scheduler * Move process_ha_router_added/removed from HA agent to router * Ml2 Mechanism Driver for OVSvApp Solution @@ -3046,7 +3145,7 @@ * Proper validation for inserting firewall rule * Imported Translations from Transifex * Ensure assertion matches dict iter order in test -* Fix 500 error during router-update for dvr routers +* Fix 500 error during router-update for dvr routers * Simple refactor to stop passing around an unused parameter * Make _build_uri_path output predictable * Radware: When a pip is needed, reuse the Port @@ -4558,7 +4657,7 @@ * Vxlan / L2population support to Linuxbridge Agent * OVS agent implementation of l2-population * Add l2 population base classes -* bp: pxeboot-port, provide pxeboot on ports +* bp: pxeboot-port, provide pxeboot on ports * Fix unable to ping floating ip from internal_ip * Prevent update_port ip_address from matching address_pair * Don't need to init testr in run_tests.sh @@ -4614,7 +4713,7 @@ * Implement ML2 port binding * Refactoring for nicira plugin to support NVP DHCP/Metadata services * Adding more unit tests for the FWaaS agent -* Arista ML2 Mechanism driver +* Arista ML2 Mechanism driver * ML2 type and mech managers should use instance vars for drivers lists * LBaaS integration with service type framework * ML2 Mechanism Driver for Tail-f Network Control System (NCS) @@ -4700,7 +4799,7 @@ * Imported Translations from Transifex * Ensure nvp resources are tagged with an up-to-date version of Neutron * Revert "Refactor configuring of floating ips on a router." -* Fix case error in qpid exchange name. s/Direct/direct/ +* Fix case error in qpid exchange name. s/Direct/direct/ * Fix ML2 VXLAN TypeDriver DB migration * Bumps hacking to 0.7.0 * Imported Translations from Transifex @@ -5173,7 +5272,7 @@ * Only map nicira_nvp_plugin module if installed * Let the cover venv run individual tests * Do not attempt to kill already-dead dnsmasq -* Copy the RHEL6 eventlet workaround from Oslo +* Copy the RHEL6 eventlet workaround from Oslo * Allow admin to delete default security groups * Imported Translations from Transifex * Add support for OVS l2 agent in XS/XCP domU @@ -5455,10 +5554,6 @@ * Imported Translations from Transifex * Provide a default api_extensions_path for nvp_plugin * AttributeError: No such RPC function 'report_state' - -2013.1.g3 ---------- - * Add pagination support for xml * Sync latest install_venv_common.py with olso * Imported Translations from Transifex @@ -5781,10 +5876,6 @@ * Improve openvswitch and linuxbridge agents' parsing of mappings * Move extension.py into quantum/api * Ensure that the expiration time for leased IP is updated correctly - -grizzly-1 ---------- - * Fix context problem * bug 1057844: improve floating-ip association checks * fix broken logic of only using hasattr to check for get_x_counts @@ -5984,7 +6075,7 @@ * Improve error message when flat network already exists * Lower webob dep from v1.2.0 to v1.0.8 * Allocation pool creation should check if gateway is in subnet -* Make sure floating IPs + gateways must be on external nets +* Make sure floating IPs + gateways must be on external nets * restart dnsmasq when subnet cidr set changes * supress dhcp router opt for subnets with null gw * add rootwrap filters to wrap ip netns exec @@ -6058,10 +6149,6 @@ * Use a common constant for the port/network 'status' value * Remove unused variable * Log message missing parameter causes exception - -folsom-3 --------- - * Update README for v2 API * Fix flavor extension based on new attribute extension spec * Update the Nicira NVP plugin to support the v2 Quantum API @@ -6128,7 +6215,7 @@ * Refactor the test cases so that all the test cases are under one test class * Add quota features into quantum. Blueprint quantum-api-quotas * Assume that subclass validates value of UUID -* fix bug lp:1025526,update iniparser.py to accept empty value +* fix bug lp:1025526,update iniparser.py to accept empty value * Ensures policy file is reloaded only if updated * Provide way to specify id in models_v2 * Add validity checks to Quantum v2 resources @@ -6208,10 +6295,6 @@ * fix some pylint warnings * fix errors in database test cases * Log the exception so app loading issues can be debuged - -folsom-1 --------- - * remove unneeded import from OVS agent that break 2.4 compat * blueprint man-support and fix documentation build bug 995283 * Fix print error for linux bridge bindings bug 1001941 @@ -6274,10 +6357,6 @@ * Remove quantum CLI console script * Bug 925372: remove deprecated webob attributes (and also specify stable webob version in pip-requires) * bug 923510: avoid querying all ports for non-detail GET Network call - -essex-3 -------- - * Make tox config work * Pin versions to standard versions * bp/api-filters This changeset implements filters for core Quantum API and provides unit tests @@ -6327,10 +6406,6 @@ * Added timeout flag to ovs-vsctl to avoid infinte waiting * Add quantum.exceptions path to configed ext paths * Fix for Bug #888820 - pip-requires file support for plugins - -essex-1 -------- - * Fixing Cisco plugin after update_* change * Fix for bug 888207 * Fix for bug 877525 @@ -6421,7 +6496,7 @@ * Modified CLI to handle both core and extensions CLI * merge trunk * lp835216 client lib was not passing in kwargs when creating exceptions -* lp834694 fix integrity error when deleting network with unattached ports. Add unit test +* lp834694 fix integrity error when deleting network with unattached ports. Add unit test * Minor fix in delete_port * merging changes from cisco consolidated branch * Fixes to support multinic @@ -6445,7 +6520,7 @@ * fix pep8 warnings * Updating common/extensions.py in order not to instantiate a QuantumManager when retrieving plugin * Cleaning pep8 -* Merging lp:~danwent/quantum/lp834491 Fixing Bug #834491: api alignment merge broke ovs plugin (Critical) +* Merging lp:~danwent/quantum/lp834491 Fixing Bug #834491: api alignment merge broke ovs plugin (Critical) * Addressing comments from Dan * Merging from quantum * merge cisco extensions branch @@ -6554,7 +6629,7 @@ * sync up with l2network exception handling for extension * merged Cisco branch's latest changes * Adding changes from Sumit's latest merge -* merge with lp:~cisco-openstack/quantum/l2network-plugin-extensions +* merge with lp:~cisco-openstack/quantum/l2network-plugin-extensions * replace exception handler by using cisco_exceptions * Raising exceptions in extension resources handling (where missing). Changing exception name to QosNotFound * Changing exception name to QosNotFound @@ -6579,7 +6654,7 @@ * Making keystone integration optional in quantum configuration * Merging bug fix for Bug 821733. Thanks lp:salvatore-orlando ! * Fixing typo -* Making the client raise the appropriate exception if needed. Also increasing the pylint score to above 8 +* Making the client raise the appropriate exception if needed. Also increasing the pylint score to above 8 * pep8 error fixed for l2network_db.py * Mering Sumit's branch with plugin support for Credentials, QoS, NovaTenant resources. Also merging latest from lp:~cisco-openstack/quantum/l2network-plugin-persistence * Merging from Sumit's branch, VIF-driver and Quantum-aware scheduler @@ -6640,7 +6715,7 @@ * Simplifying condition * FIxing missing 'output' variable @ line 243 (syntax error) * Adding automattic serialization to all requests by moving it to do_request -* added network and port models similar to quantum with following changes - - InnoDB as storage engine to allow foreign key constraints - joinedLoad operation on the queries to make use of relation between Network and Port Moved out the network and port code to make l2network contain vlanbinding, portprofile and portprofile bindings +* added network and port models similar to quantum with following changes - - InnoDB as storage engine to allow foreign key constraints - joinedLoad operation on the queries to make use of relation between Network and Port Moved out the network and port code to make l2network contain vlanbinding, portprofile and portprofile bindings * Authentication with Keystone. auth_token Middleware tweaked and imported in Quantum tree Developing Authorization middleware * Introducting cheetah Updating list_nets in CLI Writing unit tests for list_nets Stubbing out with FakeConnection now * I'm too tired @@ -6743,7 +6818,7 @@ * Vinkesh/Santhosh | Moved the stub classes in test_extensions to a separate file extension_stubs * Merged from trunk * bug802772 update exception handling in OVS plugin to use API exceptions -* merged the latest changes from plugin-framework branch - revision 39 conforming to the new cisco plugin directory structure and moving all db related modules into cisco/db folder updated db_test_plugin.py - added import of cisco constants module - added LOG.getLogger for logging component name - updated import module paths for l2network_models/db and ucs_models/db to use the new directory structure - updated (rearranged) imports section to obey openstack alphabetical placement convention updated db_conn.ini - updated database name from cisco_naas to quantum_l2network unit test cases ran successfully and pep8 checks done again +* merged the latest changes from plugin-framework branch - revision 39 conforming to the new cisco plugin directory structure and moving all db related modules into cisco/db folder updated db_test_plugin.py - added import of cisco constants module - added LOG.getLogger for logging component name - updated import module paths for l2network_models/db and ucs_models/db to use the new directory structure - updated (rearranged) imports section to obey openstack alphabetical placement convention updated db_conn.ini - updated database name from cisco_naas to quantum_l2network unit test cases ran successfully and pep8 checks done again * removing a few additional lines that aren't needed once we don't calculate port count * Adding a tests directory, this can be used for plugin-specific test cases * also remove line that computes portcount, as it is unneeded now that we don't return it @@ -6756,14 +6831,14 @@ * change default integration bridge from br100 to br-int to reflect new default for OVS vif-plugging in nova Diablo-3 release * fix bug 817826 and similar error in batch_config.py * persistence of l2network & ucs plugins using mysql - db_conn.ini - configuration details of making a connection to the database - db_test_plugin.py - contains abstraction methods for storing database values in a dict and unit test cases for DB testing - l2network_db.py - db methods for l2network models - l2network_models.py - class definitions for the l2 network tables - ucs_db.py - db methods for ucs models - ucs_models.py - class definition for the ucs tables dynamic loading of the 2nd layer plugin db's based on passed arguments Create, Delete, Get, Getall, Update database methods at - Quantum, L2Network and Ucs Unit test cases for create, delete, getall and update operations for L2Network and Ucs plugins pep8 checks done branch based off revision 34 plugin-framework -* merge Salvatore's api branch with fixes for tests. Tweaking branch to remove unwanted bin/quantum.py as part of merge +* merge Salvatore's api branch with fixes for tests. Tweaking branch to remove unwanted bin/quantum.py as part of merge * Merging in main repo updates * Updating to fix some SSL issues * Removing extra quantum.py file from source control removing unused import from quantum/api/__init__.py * Apply fix for bug #817813 Merging lp:~danwent/quantum/bug817813 * Apply fix for bug #814012 Merging lp:~danwent/quantum/bug814012 * Apply fix for bug #814517 merging lp:~tylesmit/quantum/quantum-bug-814517 -* bug 817813: default provider in plugins.ini accidentally changed. Changing it back to FakePlugin +* bug 817813: default provider in plugins.ini accidentally changed. Changing it back to FakePlugin * Changed the param name "network-name" to "net-name" since the Quantum service expects the later * Removing some legacy code from the unit tests * Adding unit tests to cover the client library @@ -6847,7 +6922,7 @@ * Santhosh/Vinkesh | Added extensions framework * merge and pep8 cleanup * Merging latest changes from parent repo - lp:network-service , Parent repo had approved merge proposal for merging lp:~santhom/network-service/quantum_testing_framework , which has now been merged into lp:network-service -* Merging pep8 and functional test related changes lp:~santhom/network-service/quantum_testing_framework branch +* Merging pep8 and functional test related changes lp:~santhom/network-service/quantum_testing_framework branch * add example to usage string for batch_config.py * Bug fixes and clean-up, including supporting libvirt * Fix typo in mysql package check @@ -6861,8 +6936,8 @@ * Fix cli.py from last merge when it got overwritten * Fixing pep8 errors removing excess debug lines * Add dependencies to README and fix whitespace -* Fix merge indentation errors -* Merged Brad's ovsplugin code +* Fix merge indentation errors +* Merged Brad's ovsplugin code * pep8 changes for quantum-framework code pieces * Update Quantum README file with instructions to launch the service and get going * Updated quantum_plugin_base with with return type dataformats as well as exceptions @@ -6906,13 +6981,13 @@ * Move plugin configuration to plugins.ini - a config file * 1) Created a DummDataPlugin in SamplePlugin module * merged salvatore's changes to local branch -* 1) Added a bare-bones framework for quantum plugins. 2) Created demo quantum plugin that conforms to QuantumPluginBase Abstract class specification. 3) Demonstrated plugin registration and invocation using the demo plugin called "QuantumEchoPlugin" 4) Created the initial file structure for a quantum CLI 5) Seeded the utils module that will contain frequently used Quantum utilities. 6) Modified the manager module to initialize and register the quantum plugin defined in a configuration file. I have hard-coded the path to plugin for now but this will move to a quantum.conf file +* 1) Added a bare-bones framework for quantum plugins. 2) Created demo quantum plugin that conforms to QuantumPluginBase Abstract class specification. 3) Demonstrated plugin registration and invocation using the demo plugin called "QuantumEchoPlugin" 4) Created the initial file structure for a quantum CLI 5) Seeded the utils module that will contain frequently used Quantum utilities. 6) Modified the manager module to initialize and register the quantum plugin defined in a configuration file. I have hard-coded the path to plugin for now but this will move to a quantum.conf file * Fixing pep8 errors * adding /bzrignore to precent checking in pyc files and that sort of stuff.. * Pushing initial started code based on Glance project and infrstructure work done by the melange team * Merging in Shweta's fixes from the review by Sumit * Minor Fix in ucs tests -* Fixing issues discussed in merge prop. The UCS Inventory clears the DB on teardown. The multiblade tests now check to see if a port exists in the db before deleting it. It checks to make sure the UCSInventory is set in the config +* Fixing issues discussed in merge prop. The UCS Inventory clears the DB on teardown. The multiblade tests now check to see if a port exists in the db before deleting it. It checks to make sure the UCSInventory is set in the config * Adding UCS inventory tests * Merging in latest changes from lp:quantum * Merging in Shweta's test changes diff -Nru neutron-7.0.4/debian/changelog neutron-7.1.1/debian/changelog --- neutron-7.0.4/debian/changelog 2016-04-20 14:46:37.000000000 +0000 +++ neutron-7.1.1/debian/changelog 2016-06-22 18:35:10.000000000 +0000 @@ -1,3 +1,23 @@ +neutron (2:7.1.1-0ubuntu1) wily; urgency=medium + + [ James Page ] + * New upstream version. + * d/p/*: Refresh. + + [ Corey Bryant ] + * d/p/fix-test-treat-devices-added-updated-no-local-interface.patch: + Mock ip_lib for failing test. + * d/rules: Use ostestr to run tests. + * d/control, d/neutron-plugin-midonet.install, d/t/midonet-plugin: + Drop MidonetInterfaceDriver as it has moved out of tree. + + [ David Della Vecchia ] + * New upstream stable point release for OpenStack Liberty (LP: #1594867). + * d/p/fix-neutron-configuration.patch: Rebased. + * d/p/drop-ryu-dep.patch: Rebased. + + -- David Della Vecchia Tue, 21 Jun 2016 12:14:50 -0400 + neutron (2:7.0.4-0ubuntu1) wily; urgency=medium * New upstream point release for OpenStack Liberty (LP: #1569502). diff -Nru neutron-7.0.4/debian/control neutron-7.1.1/debian/control --- neutron-7.0.4/debian/control 2016-04-20 14:46:37.000000000 +0000 +++ neutron-7.1.1/debian/control 2016-06-22 18:35:10.000000000 +0000 @@ -367,27 +367,6 @@ . This package provides the linuxbridge plugin agent. -Package: neutron-plugin-midonet -Architecture: all -Provides: neutron-plugin, -Depends: neutron-common (= ${source:Version}), - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, -Breaks: quantum-plugin-midonet ( << 1:2013.2~b2-0ubuntu1~ ), -Replaces: quantum-plugin-midonet ( << 1:2013.2~b2-0ubuntu1~ ), -Description: Neutron is a virtual network service for Openstack - Midonet plugin - Neutron is a virtual network service for Openstack, and a part of - Netstack. Just like OpenStack Nova provides an API to dynamically - request and configure virtual servers, Neutron provides an API to - dynamically request and configure virtual networks. These networks - connect "interfaces" from other OpenStack services (e.g., virtual NICs - from Nova VMs). The Neutron API supports extensions to provide - advanced network capabilities (e.g., QoS, ACLs, network monitoring, - etc.) - . - This package provides the Midonet plugin. - Package: neutron-plugin-mlnx Architecture: all Provides: neutron-plugin, diff -Nru neutron-7.0.4/debian/neutron-plugin-midonet.install neutron-7.1.1/debian/neutron-plugin-midonet.install --- neutron-7.0.4/debian/neutron-plugin-midonet.install 2016-04-20 14:46:37.000000000 +0000 +++ neutron-7.1.1/debian/neutron-plugin-midonet.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -etc/neutron/plugins/midonet/* etc/neutron/plugins/midonet diff -Nru neutron-7.0.4/debian/patches/drop-ryu-dep.patch neutron-7.1.1/debian/patches/drop-ryu-dep.patch --- neutron-7.0.4/debian/patches/drop-ryu-dep.patch 2016-04-20 14:46:37.000000000 +0000 +++ neutron-7.1.1/debian/patches/drop-ryu-dep.patch 2016-06-22 18:35:10.000000000 +0000 @@ -6,11 +6,11 @@ --- a/requirements.txt +++ b/requirements.txt -@@ -18,7 +18,6 @@ keystonemiddleware!=2.4.0,>=2.0.0 +@@ -18,7 +18,6 @@ netaddr!=0.7.16,>=0.7.12 python-neutronclient>=2.6.0 retrying!=1.3.0,>=1.2.3 # Apache-2.0 -ryu>=3.23.2 # Apache-2.0 SQLAlchemy<1.1.0,>=0.9.9 WebOb>=1.2.3 - python-keystoneclient!=1.8.0,>=1.6.0 + python-keystoneclient!=1.8.0,<3.0.0,>=1.6.0 diff -Nru neutron-7.0.4/debian/patches/fix-neutron-configuration.patch neutron-7.1.1/debian/patches/fix-neutron-configuration.patch --- neutron-7.0.4/debian/patches/fix-neutron-configuration.patch 2016-04-20 14:46:37.000000000 +0000 +++ neutron-7.1.1/debian/patches/fix-neutron-configuration.patch 2016-06-22 18:35:10.000000000 +0000 @@ -9,7 +9,7 @@ # Example: core_plugin = ml2 # (StrOpt) Neutron IPAM (IP address management) driver to be loaded from the -@@ -683,7 +683,7 @@ +@@ -687,7 +687,7 @@ # Use "sudo neutron-rootwrap /etc/neutron/rootwrap.conf" to use the real # root filter facility. # Change to "sudo" to skip the filtering and just run the command directly @@ -18,7 +18,7 @@ # Set to true to add comments to generated iptables rules that describe # each rule's purpose. (System must support the iptables comments module.) -@@ -731,6 +731,7 @@ +@@ -735,6 +735,7 @@ # be set in the corresponding core plugin '.ini' file. However, it is suggested # to put the [database] section and its connection attribute in this # configuration file. diff -Nru neutron-7.0.4/debian/patches/fix-test-treat-devices-added-updated-no-local-interface.patch neutron-7.1.1/debian/patches/fix-test-treat-devices-added-updated-no-local-interface.patch --- neutron-7.0.4/debian/patches/fix-test-treat-devices-added-updated-no-local-interface.patch 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/debian/patches/fix-test-treat-devices-added-updated-no-local-interface.patch 2016-06-22 18:35:10.000000000 +0000 @@ -0,0 +1,37 @@ +Description: Fix test error by mocking out call that leads to subprocess + failure. The error only exists on liberty as the test code has + since been moved and refactored. + Following is the original traceback: + + Traceback (most recent call last): + File "/<>/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py", line 425, in test_treat_devices_added_upd + agent.treat_devices_added_updated(set(['tap1'])) + ... + File "/<>/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py", line 476, in add_tap_interface + if not ip_lib.device_exists(tap_device_name): + ... + File "/<>/neutron/common/utils.py", line 199, in subprocess_popen + close_fds=close_fds, env=env) + File "/usr/lib/python2.7/dist-packages/eventlet/green/subprocess.py", line 54, in __init__ + subprocess_orig.Popen.__init__(self, args, 0, *argss, **kwds) + File "/usr/lib/python2.7/subprocess.py", line 711, in __init__ + errread, errwrite) + File "/usr/lib/python2.7/subprocess.py", line 1340, in _execute_child + raise child_exception + OSError: [Errno 2] No such file or directory + +Author: Corey Bryant +Forwarded: No (upstream is in security-fix only mode for liberty) + +--- a/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py ++++ b/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py +@@ -422,7 +422,8 @@ + agent.mgr = mock.Mock() + agent.mgr.plug_interface.return_value = False + agent.mgr.ensure_port_admin_state = mock.Mock() +- agent.treat_devices_added_updated(set(['tap1'])) ++ with mock.patch.object(ip_lib, 'device_exists', return_value=False): ++ agent.treat_devices_added_updated(set(['tap1'])) + self.assertFalse(agent.mgr.ensure_port_admin_state.called) + + def test_treat_devices_added_updated_admin_state_up_true(self): diff -Nru neutron-7.0.4/debian/patches/series neutron-7.1.1/debian/patches/series --- neutron-7.0.4/debian/patches/series 2016-04-20 14:46:37.000000000 +0000 +++ neutron-7.1.1/debian/patches/series 2016-06-22 18:35:10.000000000 +0000 @@ -1,3 +1,4 @@ +fix-test-treat-devices-added-updated-no-local-interface.patch fix-neutron-configuration.patch skip-iptest.patch drop-ryu-dep.patch diff -Nru neutron-7.0.4/debian/rules neutron-7.1.1/debian/rules --- neutron-7.0.4/debian/rules 2016-04-20 14:46:37.000000000 +0000 +++ neutron-7.1.1/debian/rules 2016-06-22 18:35:10.000000000 +0000 @@ -24,7 +24,7 @@ override_dh_auto_clean: dh_auto_clean - rm -f debian/*.upstart debian/*.init debian/*.service + rm -f debian/*.upstart debian/*.init debian/*.service .testrepository get-orig-source: uscan --verbose --force-download --rename --destdir=../build-area @@ -34,14 +34,7 @@ # Unpatch neutron configuration to fixup tests which conflict # with a core_plugin being set. patch -p1 -R < debian/patches/fix-neutron-configuration.patch - rm -rf .testrepository - testr init && \ - set -e && \ - TEMP_REZ=`mktemp -t` && \ - testr run --subunit neutron.tests.unit | tee $$TEMP_REZ | subunit2pyunit; \ - cat $$TEMP_REZ | subunit-filter -s --no-passthrough | subunit-stats; \ - rm -f $$TEMP_REZ ; \ - testr slowest; \ + ostestr # Patch configuration file after testing patch -p1 < debian/patches/fix-neutron-configuration.patch endif diff -Nru neutron-7.0.4/debian/tests/midonet-plugin neutron-7.1.1/debian/tests/midonet-plugin --- neutron-7.0.4/debian/tests/midonet-plugin 2016-04-20 14:46:37.000000000 +0000 +++ neutron-7.1.1/debian/tests/midonet-plugin 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -#!/bin/bash -#----------------------- -# Testing midonet-plugin -#----------------------- -set -e - -$(dirname $0)/test-plugin neutron-plugin-midonet /etc/neutron/plugins/midonet/midonet.ini midonet diff -Nru neutron-7.0.4/etc/l3_agent.ini neutron-7.1.1/etc/l3_agent.ini --- neutron-7.0.4/etc/l3_agent.ini 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/etc/l3_agent.ini 2016-06-10 01:43:08.000000000 +0000 @@ -58,6 +58,12 @@ # setup.cfg for entry points included with the neutron source. # prefix_delegation_driver = dibbler +# Service to handle DHCPv6 Prefix delegation. +# pd_dhcp_driver = dibbler + +# Location to store IPv6 RA config files +# ra_confs = $state_path/ra + # Indicates that this L3 agent should also handle routers that do not have # an external network gateway configured. This option should be True only # for a single agent in a Neutron deployment, and may be False for all agents diff -Nru neutron-7.0.4/etc/neutron/plugins/midonet/midonet.ini neutron-7.1.1/etc/neutron/plugins/midonet/midonet.ini --- neutron-7.0.4/etc/neutron/plugins/midonet/midonet.ini 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/etc/neutron/plugins/midonet/midonet.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ - -[midonet] -# MidoNet API server URI -# midonet_uri = http://localhost:8080/midonet-api - -# MidoNet admin username -# username = admin - -# MidoNet admin password -# password = passw0rd - -# ID of the project that MidoNet admin user belongs to -# project_id = 77777777-7777-7777-7777-777777777777 diff -Nru neutron-7.0.4/etc/neutron/plugins/ml2/linuxbridge_agent.ini neutron-7.1.1/etc/neutron/plugins/ml2/linuxbridge_agent.ini --- neutron-7.0.4/etc/neutron/plugins/ml2/linuxbridge_agent.ini 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/etc/neutron/plugins/ml2/linuxbridge_agent.ini 2016-06-10 01:43:08.000000000 +0000 @@ -40,6 +40,11 @@ # iproute2 supports unicast flooding - requires 3.11 kernel and iproute2 3.10) # l2_population = False +# (BoolOpt) Flag to disable local ARP responder which provides local responses +# instead of performing ARP broadcast into the overlay. Enabling local ARP +# responder is not fully compatible with the allowed-address-pairs extension. +# arp_responder = True + [agent] # Agent's polling interval in seconds # polling_interval = 2 diff -Nru neutron-7.0.4/etc/neutron/rootwrap.d/iptables-firewall.filters neutron-7.1.1/etc/neutron/rootwrap.d/iptables-firewall.filters --- neutron-7.0.4/etc/neutron/rootwrap.d/iptables-firewall.filters 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/etc/neutron/rootwrap.d/iptables-firewall.filters 2016-06-10 01:43:03.000000000 +0000 @@ -19,3 +19,10 @@ # "iptables", "-A", ... iptables: CommandFilter, iptables, root ip6tables: CommandFilter, ip6tables, root + +# neutron/agent/linux/iptables_manager.py +# "sysctl", "-w", ... +sysctl: CommandFilter, sysctl, root + +# neutron/agent/linux/ip_conntrack.py +conntrack: CommandFilter, conntrack, root \ No newline at end of file diff -Nru neutron-7.0.4/neutron/agent/common/ovs_lib.py neutron-7.1.1/neutron/agent/common/ovs_lib.py --- neutron-7.0.4/neutron/agent/common/ovs_lib.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/common/ovs_lib.py 2016-06-10 01:43:08.000000000 +0000 @@ -16,6 +16,7 @@ import collections import itertools import operator +import time from oslo_config import cfg from oslo_log import log as logging @@ -205,8 +206,12 @@ def replace_port(self, port_name, *interface_attr_tuples): """Replace existing port or create it, and configure port interface.""" + + # NOTE(xiaohhui): If del_port is inside the transaction, there will + # only be one command for replace_port. This will cause the new port + # not be found by system, which will lead to Bug #1519926. + self.ovsdb.del_port(port_name).execute() with self.ovsdb.transaction() as txn: - txn.add(self.ovsdb.del_port(port_name)) txn.add(self.ovsdb.add_port(self.br_name, port_name, may_exist=False)) if interface_attr_tuples: @@ -220,13 +225,24 @@ def run_ofctl(self, cmd, args, process_input=None): full_args = ["ovs-ofctl", cmd, self.br_name] + args - try: - return utils.execute(full_args, run_as_root=True, - process_input=process_input) - except Exception as e: - LOG.error(_LE("Unable to execute %(cmd)s. Exception: " - "%(exception)s"), - {'cmd': full_args, 'exception': e}) + # TODO(kevinbenton): This error handling is really brittle and only + # detects one specific type of failure. The callers of this need to + # be refactored to expect errors so we can re-raise and they can + # take appropriate action based on the type of error. + for i in range(1, 11): + try: + return utils.execute(full_args, run_as_root=True, + process_input=process_input) + except Exception as e: + if "failed to connect to socket" in str(e): + LOG.debug("Failed to connect to OVS. Retrying " + "in 1 second. Attempt: %s/10", i) + time.sleep(1) + continue + LOG.error(_LE("Unable to execute %(cmd)s. Exception: " + "%(exception)s"), + {'cmd': full_args, 'exception': e}) + break def count_flows(self): flow_list = self.run_ofctl("dump-flows", []).split("\n")[1:] diff -Nru neutron-7.0.4/neutron/agent/dhcp/agent.py neutron-7.1.1/neutron/agent/dhcp/agent.py --- neutron-7.0.4/neutron/agent/dhcp/agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/dhcp/agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -132,7 +132,7 @@ if (isinstance(e, oslo_messaging.RemoteError) and e.exc_type == 'NetworkNotFound' or isinstance(e, exceptions.NetworkNotFound)): - LOG.warning(_LW("Network %s has been deleted."), network.id) + LOG.debug("Network %s has been deleted.", network.id) else: LOG.exception(_LE('Unable to %(action)s dhcp for %(net_id)s.'), {'net_id': network.id, 'action': action}) @@ -155,6 +155,7 @@ try: active_networks = self.plugin_rpc.get_active_networks_info() + LOG.info(_LI('All active networks have been fetched through RPC.')) active_network_ids = set(network.id for network in active_networks) for deleted_id in known_network_ids - active_network_ids: try: @@ -205,7 +206,7 @@ try: network = self.plugin_rpc.get_network_info(network_id) if not network: - LOG.warn(_LW('Network %s has been deleted.'), network_id) + LOG.debug('Network %s has been deleted.', network_id) return network except Exception as e: self.schedule_resync(e, network_id) @@ -220,7 +221,10 @@ @utils.exception_logger() def safe_configure_dhcp_for_network(self, network): try: + network_id = network.get('id') + LOG.info(_LI('Starting network %s dhcp configuration'), network_id) self.configure_dhcp_for_network(network) + LOG.info(_LI('Finished network %s dhcp configuration'), network_id) except (exceptions.NetworkNotFound, RuntimeError): LOG.warn(_LW('Network %s may have been deleted and its resources ' 'may have already been disposed.'), network.id) @@ -245,6 +249,12 @@ if subnet.ip_version == 4 and subnet.enable_dhcp: self.enable_isolated_metadata_proxy(network) break + elif (self.conf.use_namespaces and not self.conf.force_metadata and + not self.conf.enable_isolated_metadata): + # In the case that the dhcp agent ran with metadata enabled, + # and dhcp agent now starts with metadata disabled, check and + # delete any metadata_proxy. + self.disable_isolated_metadata_proxy(network) def disable_dhcp_helper(self, network_id): """Disable DHCP for a network known to the agent.""" @@ -273,17 +283,18 @@ if not network: return - old_cidrs = set(s.cidr for s in old_network.subnets if s.enable_dhcp) - new_cidrs = set(s.cidr for s in network.subnets if s.enable_dhcp) - - if new_cidrs and old_cidrs == new_cidrs: + if not any(s for s in network.subnets if s.enable_dhcp): + self.disable_dhcp_helper(network.id) + return + # NOTE(kevinbenton): we don't exclude dhcp disabled subnets because + # they still change the indexes used for tags + old_cidrs = [s.cidr for s in network.subnets] + new_cidrs = [s.cidr for s in old_network.subnets] + if old_cidrs == new_cidrs: self.call_driver('reload_allocations', network) self.cache.put(network) - elif new_cidrs: - if self.call_driver('restart', network): - self.cache.put(network) - else: - self.disable_dhcp_helper(network.id) + elif self.call_driver('restart', network): + self.cache.put(network) @utils.synchronized('dhcp-agent') def network_create_end(self, context, payload): diff -Nru neutron-7.0.4/neutron/agent/l3/agent.py neutron-7.1.1/neutron/agent/l3/agent.py --- neutron-7.0.4/neutron/agent/l3/agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3/agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -163,9 +163,11 @@ 1.2 - DVR support: new L3 agent methods added. - add_arp_entry - del_arp_entry + 1.3 - fipnamespace_delete_on_ext_net - to delete fipnamespace + after the external network is removed Needed by the L3 service when dealing with DVR """ - target = oslo_messaging.Target(version='1.2') + target = oslo_messaging.Target(version='1.3') def __init__(self, host, conf=None): if conf: diff -Nru neutron-7.0.4/neutron/agent/l3/dvr_edge_router.py neutron-7.1.1/neutron/agent/l3/dvr_edge_router.py --- neutron-7.0.4/neutron/agent/l3/dvr_edge_router.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3/dvr_edge_router.py 2016-06-10 01:43:08.000000000 +0000 @@ -99,7 +99,8 @@ sn_port['fixed_ips'], sn_port['mac_address'], interface_name, - dvr_snat_ns.SNAT_INT_DEV_PREFIX) + dvr_snat_ns.SNAT_INT_DEV_PREFIX, + mtu=sn_port.get('mtu')) def _dvr_internal_network_removed(self, port): super(DvrEdgeRouter, self)._dvr_internal_network_removed(port) @@ -132,7 +133,8 @@ snat_ns.name, port['network_id'], port['id'], port['fixed_ips'], port['mac_address'], interface_name, - dvr_snat_ns.SNAT_INT_DEV_PREFIX) + dvr_snat_ns.SNAT_INT_DEV_PREFIX, + mtu=port.get('mtu')) self._external_gateway_added(ex_gw_port, gw_interface_name, snat_ns.name, preserve_ips=[]) self.snat_iptables_manager = iptables_manager.IptablesManager( @@ -195,3 +197,8 @@ LOG.error(_LE("The SNAT namespace %s does not exist for " "the router."), ns_name) super(DvrEdgeRouter, self).update_routing_table(operation, route) + + def delete(self, agent): + super(DvrEdgeRouter, self).delete(agent) + if self.snat_namespace: + self.snat_namespace.delete() diff -Nru neutron-7.0.4/neutron/agent/l3/dvr_fip_ns.py neutron-7.1.1/neutron/agent/l3/dvr_fip_ns.py --- neutron-7.0.4/neutron/agent/l3/dvr_fip_ns.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3/dvr_fip_ns.py 2016-06-10 01:43:08.000000000 +0000 @@ -31,7 +31,7 @@ ROUTER_2_FIP_DEV_PREFIX = namespaces.ROUTER_2_FIP_DEV_PREFIX # Route Table index for FIPs FIP_RT_TBL = 16 -FIP_LL_SUBNET = '169.254.30.0/23' +FIP_LL_SUBNET = '169.254.64.0/18' # Rule priority range for FIPs FIP_PR_START = 32768 FIP_PR_END = FIP_PR_START + 40000 @@ -105,28 +105,14 @@ ex_gw_port['mac_address'], bridge=self.agent_conf.external_network_bridge, namespace=ns_name, - prefix=FIP_EXT_DEV_PREFIX) + prefix=FIP_EXT_DEV_PREFIX, + mtu=ex_gw_port.get('mtu')) ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips']) self.driver.init_l3(interface_name, ip_cidrs, namespace=ns_name, clean_connections=True) - for fixed_ip in ex_gw_port['fixed_ips']: - ip_lib.send_ip_addr_adv_notif(ns_name, - interface_name, - fixed_ip['ip_address'], - self.agent_conf) - - for subnet in ex_gw_port['subnets']: - gw_ip = subnet.get('gateway_ip') - if gw_ip: - is_gateway_not_in_subnet = not ipam_utils.check_subnet_ip( - subnet.get('cidr'), gw_ip) - ipd = ip_lib.IPDevice(interface_name, - namespace=ns_name) - if is_gateway_not_in_subnet: - ipd.route.add_route(gw_ip, scope='link') - ipd.route.add_gateway(gw_ip) + self.update_gateway_port(ex_gw_port) cmd = ['sysctl', '-w', 'net.ipv4.conf.%s.proxy_arp=1' % interface_name] # TODO(Carl) mlavelle's work has self.ip_wrapper @@ -194,13 +180,53 @@ Request port creation from Plugin then creates Floating IP namespace and adds gateway port. """ - self.agent_gateway_port = agent_gateway_port - self.create() iface_name = self.get_ext_device_name(agent_gateway_port['id']) self._gateway_added(agent_gateway_port, iface_name) + def _check_for_gateway_ip_change(self, new_agent_gateway_port): + + def get_gateway_ips(gateway_port): + gw_ips = {} + if gateway_port: + for subnet in gateway_port.get('subnets', []): + gateway_ip = subnet.get('gateway_ip', None) + if gateway_ip: + ip_version = ip_lib.get_ip_version(gateway_ip) + gw_ips[ip_version] = gateway_ip + return gw_ips + + new_gw_ips = get_gateway_ips(new_agent_gateway_port) + old_gw_ips = get_gateway_ips(self.agent_gateway_port) + + return new_gw_ips != old_gw_ips + + def update_gateway_port(self, agent_gateway_port): + gateway_ip_not_changed = self.agent_gateway_port and ( + not self._check_for_gateway_ip_change(agent_gateway_port)) + self.agent_gateway_port = agent_gateway_port + if gateway_ip_not_changed: + return + + ns_name = self.get_name() + interface_name = self.get_ext_device_name(agent_gateway_port['id']) + for fixed_ip in agent_gateway_port['fixed_ips']: + ip_lib.send_ip_addr_adv_notif(ns_name, + interface_name, + fixed_ip['ip_address'], + self.agent_conf) + + ipd = ip_lib.IPDevice(interface_name, namespace=ns_name) + for subnet in agent_gateway_port['subnets']: + gw_ip = subnet.get('gateway_ip') + if gw_ip: + is_gateway_not_in_subnet = not ipam_utils.check_subnet_ip( + subnet.get('cidr'), gw_ip) + if is_gateway_not_in_subnet: + ipd.route.add_route(gw_ip, scope='link') + ipd.route.add_gateway(gw_ip) + def _internal_ns_interface_added(self, ip_cidr, interface_name, ns_name): ip_wrapper = ip_lib.IPWrapper(namespace=ns_name) @@ -231,9 +257,11 @@ self._internal_ns_interface_added(str(fip_2_rtr), fip_2_rtr_name, fip_ns_name) - if self.agent_conf.network_device_mtu: - int_dev[0].link.set_mtu(self.agent_conf.network_device_mtu) - int_dev[1].link.set_mtu(self.agent_conf.network_device_mtu) + mtu = (self.agent_conf.network_device_mtu or + ri.get_ex_gw_port().get('mtu')) + if mtu: + int_dev[0].link.set_mtu(mtu) + int_dev[1].link.set_mtu(mtu) int_dev[0].link.set_up() int_dev[1].link.set_up() diff -Nru neutron-7.0.4/neutron/agent/l3/dvr_local_router.py neutron-7.1.1/neutron/agent/l3/dvr_local_router.py --- neutron-7.0.4/neutron/agent/l3/dvr_local_router.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3/dvr_local_router.py 2016-06-10 01:43:08.000000000 +0000 @@ -139,26 +139,10 @@ self.fip_ns.local_subnets.release(self.router_id) self.rtr_fip_subnet = None ns_ip.del_veth(fip_2_rtr_name) - is_last = self.fip_ns.unsubscribe(self.router_id) - if is_last: - # TODO(Carl) I can't help but think that another router could - # come in and want to start using this namespace while this is - # destroying it. The two could end up conflicting on - # creating/destroying interfaces and such. I think I'd like a - # semaphore to sync creation/deletion of this namespace. - - # NOTE (Swami): Since we are deleting the namespace here we - # should be able to delete the floatingip agent gateway port - # for the provided external net since we don't need it anymore. - if self.fip_ns.agent_gateway_port: - LOG.debug('Removed last floatingip, so requesting the ' - 'server to delete Floatingip Agent Gateway port:' - '%s', self.fip_ns.agent_gateway_port) - self.agent.plugin_rpc.delete_agent_gateway_port( - self.agent.context, - self.fip_ns.agent_gateway_port['network_id']) - self.fip_ns.delete() - self.fip_ns = None + self.fip_ns.unsubscribe(self.router_id) + # NOTE (Swami): The fg interface and the namespace will be deleted + # when the external gateway port is removed. This will be + # initiated from the server through an RPC call. def add_floating_ip(self, fip, interface_name, device): if not self._add_fip_addr_to_device(fip, device): @@ -461,11 +445,14 @@ LOG.error(_LE("No FloatingIP agent gateway port " "returned from server for 'network-id': " "%s"), ex_gw_port['network_id']) - if is_first and fip_agent_port: + if fip_agent_port: if 'subnets' not in fip_agent_port: LOG.error(_LE('Missing subnet/agent_gateway_port')) else: - self.fip_ns.create_gateway_port(fip_agent_port) + if is_first: + self.fip_ns.create_gateway_port(fip_agent_port) + else: + self.fip_ns.update_gateway_port(fip_agent_port) if (self.fip_ns.agent_gateway_port and (self.dist_fip_count == 0 or is_first)): diff -Nru neutron-7.0.4/neutron/agent/l3/dvr.py neutron-7.1.1/neutron/agent/l3/dvr.py --- neutron-7.0.4/neutron/agent/l3/dvr.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3/dvr.py 2016-06-10 01:43:08.000000000 +0000 @@ -77,3 +77,9 @@ mac = arp_table['mac_address'] subnet_id = arp_table['subnet_id'] ri._update_arp_entry(ip, mac, subnet_id, 'delete') + + def fipnamespace_delete_on_ext_net(self, context, ext_net_id): + """Delete fip namespace after external network removed.""" + fip_ns = self.get_fip_ns(ext_net_id) + if fip_ns.agent_gateway_port and not fip_ns.destroyed: + fip_ns.delete() diff -Nru neutron-7.0.4/neutron/agent/l3/ha_router.py neutron-7.1.1/neutron/agent/l3/ha_router.py --- neutron-7.0.4/neutron/agent/l3/ha_router.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3/ha_router.py 2016-06-10 01:43:08.000000000 +0000 @@ -144,7 +144,8 @@ interface_name, self.ha_port['mac_address'], namespace=self.ns_name, - prefix=HA_DEV_PREFIX) + prefix=HA_DEV_PREFIX, + mtu=self.ha_port.get('mtu')) ip_cidrs = common_utils.fixed_ip_cidrs(self.ha_port['fixed_ips']) self.driver.init_l3(interface_name, ip_cidrs, namespace=self.ns_name, @@ -263,13 +264,13 @@ def internal_network_added(self, port): port_id = port['id'] interface_name = self.get_internal_device_name(port_id) - self.driver.plug(port['network_id'], port_id, interface_name, port['mac_address'], namespace=self.ns_name, - prefix=router.INTERNAL_DEV_PREFIX) + prefix=router.INTERNAL_DEV_PREFIX, + mtu=port.get('mtu')) self._disable_ipv6_addressing_on_interface(interface_name) for ip_cidr in common_utils.fixed_ip_cidrs(port['fixed_ips']): diff -Nru neutron-7.0.4/neutron/agent/l3/item_allocator.py neutron-7.1.1/neutron/agent/l3/item_allocator.py --- neutron-7.0.4/neutron/agent/l3/item_allocator.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3/item_allocator.py 2016-06-10 01:43:08.000000000 +0000 @@ -74,7 +74,9 @@ self.pool.update(self.remembered.values()) self.remembered.clear() if not self.pool: - # More than 256 routers on a compute node! + # The number of address pairs allocated from the + # pool depends upon the prefix length specified + # in FIP_LL_SUBNET raise RuntimeError("Cannot allocate item of type:" " %s from pool using file %s" % (self.ItemClass, self.state_file)) diff -Nru neutron-7.0.4/neutron/agent/l3/router_info.py neutron-7.1.1/neutron/agent/l3/router_info.py --- neutron-7.0.4/neutron/agent/l3/router_info.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3/router_info.py 2016-06-10 01:43:08.000000000 +0000 @@ -24,7 +24,7 @@ from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.common import utils as common_utils -from neutron.i18n import _LW +from neutron.i18n import _, _LW from neutron.ipam import utils as ipam_utils LOG = logging.getLogger(__name__) @@ -80,7 +80,8 @@ self.radvd = ra.DaemonMonitor(self.router_id, self.ns_name, process_monitor, - self.get_internal_device_name) + self.get_internal_device_name, + self.agent_conf) if self.router_namespace: self.router_namespace.create() @@ -143,11 +144,11 @@ return self.router.get(l3_constants.FLOATINGIP_KEY, []) def floating_forward_rules(self, floating_ip, fixed_ip): - return [('PREROUTING', '-d %s -j DNAT --to %s' % + return [('PREROUTING', '-d %s/32 -j DNAT --to-destination %s' % (floating_ip, fixed_ip)), - ('OUTPUT', '-d %s -j DNAT --to %s' % + ('OUTPUT', '-d %s/32 -j DNAT --to-destination %s' % (floating_ip, fixed_ip)), - ('float-snat', '-s %s -j SNAT --to %s' % + ('float-snat', '-s %s/32 -j SNAT --to-source %s' % (fixed_ip, floating_ip))] def process_floating_ip_nat_rules(self): @@ -175,8 +176,9 @@ self.process_floating_ip_nat_rules() except Exception: # TODO(salv-orlando): Less broad catching - raise n_exc.FloatingIpSetupException( - 'L3 agent failure to setup NAT for floating IPs') + msg = _('L3 agent failure to setup NAT for floating IPs') + LOG.exception(msg) + raise n_exc.FloatingIpSetupException(msg) def _add_fip_addr_to_device(self, fip, device): """Configures the floating ip address on the device. @@ -252,8 +254,9 @@ return self.process_floating_ip_addresses(interface_name) except Exception: # TODO(salv-orlando): Less broad catching - raise n_exc.FloatingIpSetupException('L3 agent failure to setup ' - 'floating IPs') + msg = _('L3 agent failure to setup floating IPs') + LOG.exception(msg) + raise n_exc.FloatingIpSetupException(msg) def put_fips_in_error_state(self): fip_statuses = {} @@ -265,7 +268,7 @@ self.router['gw_port'] = None self.router[l3_constants.INTERFACE_KEY] = [] self.router[l3_constants.FLOATINGIP_KEY] = [] - self.process(agent) + self.process_delete(agent) self.disable_radvd() if self.router_namespace: self.router_namespace.delete() @@ -289,12 +292,12 @@ def _internal_network_added(self, ns_name, network_id, port_id, fixed_ips, mac_address, - interface_name, prefix): + interface_name, prefix, mtu=None): LOG.debug("adding internal network: prefix(%s), port(%s)", prefix, port_id) self.driver.plug(network_id, port_id, interface_name, mac_address, namespace=ns_name, - prefix=prefix) + prefix=prefix, mtu=mtu) ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips) self.driver.init_router_port( @@ -319,7 +322,8 @@ fixed_ips, mac_address, interface_name, - INTERNAL_DEV_PREFIX) + INTERNAL_DEV_PREFIX, + mtu=port.get('mtu')) def internal_network_removed(self, port): interface_name = self.get_internal_device_name(port['id']) @@ -468,7 +472,8 @@ ex_gw_port['mac_address'], bridge=self.agent_conf.external_network_bridge, namespace=ns_name, - prefix=EXTERNAL_DEV_PREFIX) + prefix=EXTERNAL_DEV_PREFIX, + mtu=ex_gw_port.get('mtu')) def _get_external_gw_ips(self, ex_gw_port): gateway_ips = [] @@ -661,6 +666,27 @@ self.iptables_manager, interface_name) + def _process_external_on_delete(self, agent): + fip_statuses = {} + existing_floating_ips = self.floating_ips + try: + ex_gw_port = self.get_ex_gw_port() + self._process_external_gateway(ex_gw_port, agent.pd) + if not ex_gw_port: + return + + interface_name = self.get_external_device_interface_name( + ex_gw_port) + fip_statuses = self.configure_fip_addresses(interface_name) + + except (n_exc.FloatingIpSetupException) as e: + # All floating IPs must be put in error state + LOG.exception(e) + fip_statuses = self.put_fips_in_error_state() + finally: + agent.update_fip_statuses( + self, existing_floating_ips, fip_statuses) + def process_external(self, agent): fip_statuses = {} existing_floating_ips = self.floating_ips @@ -690,6 +716,22 @@ self, existing_floating_ips, fip_statuses) @common_utils.exception_logger() + def process_delete(self, agent): + """Process the delete of this router + + This method is the point where the agent requests that this router + be deleted. This is a separate code path from process in that it + avoids any changes to the qrouter namespace that will be removed + at the end of the operation. + + :param agent: Passes the agent in order to send RPC messages. + """ + LOG.debug("process router delete") + self._process_internal_ports(agent.pd) + agent.pd.sync_router(self.router['id']) + self._process_external_on_delete(agent) + + @common_utils.exception_logger() def process(self, agent): """Process updates to this router diff -Nru neutron-7.0.4/neutron/agent/l3_agent.py neutron-7.1.1/neutron/agent/l3_agent.py --- neutron-7.0.4/neutron/agent/l3_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/l3_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -24,6 +24,8 @@ from neutron.agent.l3 import ha from neutron.agent.linux import external_process from neutron.agent.linux import interface +from neutron.agent.linux import pd +from neutron.agent.linux import ra from neutron.agent.metadata import config as metadata_config from neutron.common import config as common_config from neutron.common import topics @@ -40,6 +42,8 @@ config.register_agent_state_opts_helper(conf) conf.register_opts(interface.OPTS) conf.register_opts(external_process.OPTS) + conf.register_opts(pd.OPTS) + conf.register_opts(ra.OPTS) def main(manager='neutron.agent.l3.agent.L3NATAgentWithStateReport'): diff -Nru neutron-7.0.4/neutron/agent/linux/bridge_lib.py neutron-7.1.1/neutron/agent/linux/bridge_lib.py --- neutron-7.0.4/neutron/agent/linux/bridge_lib.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/bridge_lib.py 2016-06-10 01:43:08.000000000 +0000 @@ -16,12 +16,21 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_log import log as logging +import os from neutron.agent.linux import ip_lib -from neutron.i18n import _LE -LOG = logging.getLogger(__name__) +# NOTE(toabctl): Don't use /sys/devices/virtual/net here because not all tap +# devices are listed here (i.e. when using Xen) +BRIDGE_FS = "/sys/class/net/" +BRIDGE_PORT_FS_FOR_DEVICE = BRIDGE_FS + "%s/brport" + + +def get_interface_bridged_time(interface): + try: + return os.stat(BRIDGE_PORT_FS_FOR_DEVICE % interface).st_mtime + except OSError: + pass class BridgeDevice(ip_lib.IPDevice): @@ -30,32 +39,6 @@ ip_wrapper = ip_lib.IPWrapper(self.namespace) return ip_wrapper.netns.execute(cmd, run_as_root=True) - def _sysctl(self, cmd): - """execute() doesn't return the exit status of the command it runs, - it returns stdout and stderr. Setting check_exit_code=True will cause - it to raise a RuntimeError if the exit status of the command is - non-zero, which in sysctl's case is an error. So we're normalizing - that into zero (success) and one (failure) here to mimic what - "echo $?" in a shell would be. - - This is all because sysctl is too verbose and prints the value you - just set on success, unlike most other utilities that print nothing. - - execute() will have dumped a message to the logs with the actual - output on failure, so it's not lost, and we don't need to print it - here. - """ - cmd = ['sysctl', '-w'] + cmd - ip_wrapper = ip_lib.IPWrapper(self.namespace) - try: - ip_wrapper.netns.execute(cmd, run_as_root=True, - check_exit_code=True) - except RuntimeError: - LOG.exception(_LE("Failed running %s"), cmd) - return 1 - - return 0 - @classmethod def addbr(cls, name, namespace=None): bridge = cls(name, namespace) @@ -76,7 +59,3 @@ def disable_stp(self): return self._brctl(['stp', self.name, 'off']) - - def disable_ipv6(self): - cmd = 'net.ipv6.conf.%s.disable_ipv6=1' % self.name - return self._sysctl([cmd]) diff -Nru neutron-7.0.4/neutron/agent/linux/dhcp.py neutron-7.1.1/neutron/agent/linux/dhcp.py --- neutron-7.0.4/neutron/agent/linux/dhcp.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/dhcp.py 2016-06-10 01:43:08.000000000 +0000 @@ -671,8 +671,7 @@ (port.mac_address, name, ip_address)) utils.replace_file(filename, buf.getvalue()) - LOG.debug('Done building host file %s with contents:\n%s', filename, - buf.getvalue()) + LOG.debug('Done building host file %s', filename) return filename def _get_client_id(self, port): @@ -1222,7 +1221,8 @@ port.id, interface_name, port.mac_address, - namespace=network.namespace) + namespace=network.namespace, + mtu=network.get('mtu')) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE('Unable to plug DHCP port for ' @@ -1293,7 +1293,7 @@ """Ensure DHCP reply packets always have correct UDP checksums.""" iptables_mgr = iptables_manager.IptablesManager(use_ipv6=False, namespace=namespace) - ipv4_rule = ('-p udp --dport %d -j CHECKSUM --checksum-fill' + ipv4_rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' % constants.DHCP_RESPONSE_PORT) iptables_mgr.ipv4['mangle'].add_rule('POSTROUTING', ipv4_rule) iptables_mgr.apply() diff -Nru neutron-7.0.4/neutron/agent/linux/external_process.py neutron-7.1.1/neutron/agent/linux/external_process.py --- neutron-7.0.4/neutron/agent/linux/external_process.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/external_process.py 2016-06-10 01:43:08.000000000 +0000 @@ -230,7 +230,7 @@ LOG.error(_LE("%(service)s for %(resource_type)s " "with uuid %(uuid)s not found. " "The process should not have died"), - {'service': pm.service, + {'service': service_id.service, 'resource_type': self._resource_type, 'uuid': service_id.uuid}) self._execute_action(service_id) @@ -247,9 +247,10 @@ action_function(service_id) def _respawn_action(self, service_id): - LOG.error(_LE("respawning %(service)s for uuid %(uuid)s"), - {'service': service_id.service, - 'uuid': service_id.uuid}) + # _LE used to avoid dropping translation for the message + LOG.warning(_LE("respawning %(service)s for uuid %(uuid)s"), + {'service': service_id.service, + 'uuid': service_id.uuid}) # noqa self._monitored_processes[service_id].enable() def _exit_action(self, service_id): diff -Nru neutron-7.0.4/neutron/agent/linux/interface.py neutron-7.1.1/neutron/agent/linux/interface.py --- neutron-7.0.4/neutron/agent/linux/interface.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/interface.py 2016-06-10 01:43:08.000000000 +0000 @@ -26,7 +26,7 @@ from neutron.common import constants as n_const from neutron.common import exceptions from neutron.common import ipv6_utils -from neutron.i18n import _LE, _LI +from neutron.i18n import _LE, _LI, _LW LOG = logging.getLogger(__name__) @@ -242,15 +242,19 @@ @abc.abstractmethod def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface only for new devices that don't exist yet.""" def plug(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): if not ip_lib.device_exists(device_name, namespace=namespace): - self.plug_new(network_id, port_id, device_name, mac_address, - bridge, namespace, prefix) + try: + self.plug_new(network_id, port_id, device_name, mac_address, + bridge, namespace, prefix, mtu) + except TypeError: + self.plug_new(network_id, port_id, device_name, mac_address, + bridge, namespace, prefix) else: LOG.info(_LI("Device %s already exists"), device_name) @@ -278,7 +282,7 @@ class NullDriver(LinuxInterfaceDriver): def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): pass def unplug(self, device_name, bridge=None, namespace=None, prefix=None): @@ -313,7 +317,7 @@ ovs.replace_port(device_name, *attrs) def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface.""" if not bridge: bridge = self.conf.ovs_integration_bridge @@ -328,6 +332,7 @@ root_dev, ns_dev = ip.add_veth(tap_name, device_name, namespace2=namespace) + root_dev.disable_ipv6() else: ns_dev = ip.device(device_name) @@ -337,11 +342,13 @@ ns_dev.link.set_address(mac_address) - if self.conf.network_device_mtu: - ns_dev.link.set_mtu(self.conf.network_device_mtu) + mtu = self.conf.network_device_mtu or mtu + if mtu: + ns_dev.link.set_mtu(mtu) if self.conf.ovs_use_veth: - root_dev.link.set_mtu(self.conf.network_device_mtu) - + root_dev.link.set_mtu(mtu) + else: + LOG.warning(_LW("No MTU configured for port %s"), port_id) # Add an interface created by ovs to the namespace. if not self.conf.ovs_use_veth and namespace: namespace_obj = ip.ensure_namespace(namespace) @@ -429,21 +436,25 @@ utils.execute(cmd, run_as_root=True) def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface.""" ip = ip_lib.IPWrapper() tap_name = self._get_tap_name(device_name, prefix) root_dev, ns_dev = ip.add_veth(tap_name, device_name) + root_dev.disable_ipv6() self._ivs_add_port(tap_name, port_id, mac_address) ns_dev = ip.device(device_name) ns_dev.link.set_address(mac_address) - if self.conf.network_device_mtu: - ns_dev.link.set_mtu(self.conf.network_device_mtu) - root_dev.link.set_mtu(self.conf.network_device_mtu) + mtu = self.conf.network_device_mtu or mtu + if mtu: + ns_dev.link.set_mtu(mtu) + root_dev.link.set_mtu(mtu) + else: + LOG.warning(_LW("No MTU configured for port %s"), port_id) if namespace: namespace_obj = ip.ensure_namespace(namespace) @@ -472,7 +483,7 @@ DEV_NAME_PREFIX = 'ns-' def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): """Plugin the interface.""" ip = ip_lib.IPWrapper() @@ -482,11 +493,15 @@ # Create ns_veth in a namespace if one is configured. root_veth, ns_veth = ip.add_veth(tap_name, device_name, namespace2=namespace) + root_veth.disable_ipv6() ns_veth.link.set_address(mac_address) - if self.conf.network_device_mtu: - root_veth.link.set_mtu(self.conf.network_device_mtu) - ns_veth.link.set_mtu(self.conf.network_device_mtu) + mtu = self.conf.network_device_mtu or mtu + if mtu: + root_veth.link.set_mtu(mtu) + ns_veth.link.set_mtu(mtu) + else: + LOG.warning(_LW("No MTU configured for port %s"), port_id) root_veth.link.set_up() ns_veth.link.set_up() diff -Nru neutron-7.0.4/neutron/agent/linux/ip_lib.py neutron-7.1.1/neutron/agent/linux/ip_lib.py --- neutron-7.0.4/neutron/agent/linux/ip_lib.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/ip_lib.py 2016-06-10 01:43:08.000000000 +0000 @@ -203,6 +203,12 @@ if self.namespace: device.link.set_netns(self.namespace) + def add_vlan(self, name, physical_interface, vlan_id): + cmd = ['add', 'link', physical_interface, 'name', name, + 'type', 'vlan', 'id', vlan_id] + self._as_root([], 'link', cmd) + return IPDevice(name, namespace=self.namespace) + def add_vxlan(self, name, vni, group=None, dev=None, ttl=None, tos=None, local=None, port=None, proxy=False): cmd = ['add', name, 'type', 'vxlan', 'id', vni] @@ -284,6 +290,37 @@ LOG.exception(_LE("Failed deleting egress connection state of" " floatingip %s"), ip_str) + def _sysctl(self, cmd): + """execute() doesn't return the exit status of the command it runs, + it returns stdout and stderr. Setting check_exit_code=True will cause + it to raise a RuntimeError if the exit status of the command is + non-zero, which in sysctl's case is an error. So we're normalizing + that into zero (success) and one (failure) here to mimic what + "echo $?" in a shell would be. + + This is all because sysctl is too verbose and prints the value you + just set on success, unlike most other utilities that print nothing. + + execute() will have dumped a message to the logs with the actual + output on failure, so it's not lost, and we don't need to print it + here. + """ + cmd = ['sysctl', '-w'] + cmd + ip_wrapper = IPWrapper(self.namespace) + try: + ip_wrapper.netns.execute(cmd, run_as_root=True, + check_exit_code=True) + except RuntimeError: + LOG.exception(_LE("Failed running %s"), cmd) + return 1 + + return 0 + + def disable_ipv6(self): + sysctl_name = re.sub(r'\.', '/', self.name) + cmd = 'net.ipv6.conf.%s.disable_ipv6=1' % sysctl_name + return self._sysctl([cmd]) + class IpCommandBase(object): COMMAND = '' @@ -870,6 +907,14 @@ return False +def vlan_in_use(segmentation_id, namespace=None): + """Return True if VLAN ID is in use by an interface, else False.""" + ip_wrapper = IPWrapper(namespace=namespace) + interfaces = ip_wrapper.netns.execute(["ip", "-d", "link", "list"], + check_exit_code=True) + return '802.1Q id %s ' % segmentation_id in interfaces + + def vxlan_in_use(segmentation_id, namespace=None): """Return True if VXLAN VNID is in use by an interface, else False.""" ip_wrapper = IPWrapper(namespace=namespace) diff -Nru neutron-7.0.4/neutron/agent/linux/iptables_firewall.py neutron-7.1.1/neutron/agent/linux/iptables_firewall.py --- neutron-7.0.4/neutron/agent/linux/iptables_firewall.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/iptables_firewall.py 2016-06-10 01:43:08.000000000 +0000 @@ -43,6 +43,7 @@ SPOOF_FILTER: 's'} DIRECTION_IP_PREFIX = {firewall.INGRESS_DIRECTION: 'source_ip_prefix', firewall.EGRESS_DIRECTION: 'dest_ip_prefix'} +ICMPV6_ALLOWED_UNSPEC_ADDR_TYPES = [131, 135, 143] IPSET_DIRECTION = {firewall.INGRESS_DIRECTION: 'src', firewall.EGRESS_DIRECTION: 'dst'} # length of all device prefixes (e.g. qvo, tap, qvb) @@ -384,21 +385,25 @@ mac_ipv4_pairs.append((mac, ip_address)) else: mac_ipv6_pairs.append((mac, ip_address)) + lla = str(ipv6_utils.get_ipv6_addr_by_EUI64( + constants.IPV6_LLA_PREFIX, mac)) + mac_ipv6_pairs.append((mac, lla)) def _spoofing_rule(self, port, ipv4_rules, ipv6_rules): - # Allow dhcp client packets - ipv4_rules += [comment_rule('-p udp -m udp --sport 68 ' - '-m udp --dport 67 ' - '-j RETURN', comment=ic.DHCP_CLIENT)] - # Drop Router Advts from the port. - ipv6_rules += [comment_rule('-p ipv6-icmp -m icmp6 --icmpv6-type %s ' - '-j DROP' % constants.ICMPV6_TYPE_RA, - comment=ic.IPV6_RA_DROP)] - ipv6_rules += [comment_rule('-p ipv6-icmp -j RETURN', - comment=ic.IPV6_ICMP_ALLOW)] - ipv6_rules += [comment_rule('-p udp -m udp --sport 546 ' - '-m udp --dport 547 ' + # Fixed rules for traffic sourced from unspecified addresses: 0.0.0.0 + # and :: + # Allow dhcp client discovery and request + ipv4_rules += [comment_rule('-s 0.0.0.0/32 -d 255.255.255.255/32 ' + '-p udp -m udp --sport 68 --dport 67 ' '-j RETURN', comment=ic.DHCP_CLIENT)] + # Allow neighbor solicitation and multicast listener discovery + # from the unspecified address for duplicate address detection + for icmp6_type in ICMPV6_ALLOWED_UNSPEC_ADDR_TYPES: + ipv6_rules += [comment_rule('-s ::/128 -d ff02::/16 ' + '-p ipv6-icmp -m icmp6 ' + '--icmpv6-type %s -j RETURN' % + icmp6_type, + comment=ic.IPV6_ICMP_ALLOW)] mac_ipv4_pairs = [] mac_ipv6_pairs = [] @@ -420,6 +425,19 @@ mac_ipv4_pairs, ipv4_rules) self._setup_spoof_filter_chain(port, self.iptables.ipv6['filter'], mac_ipv6_pairs, ipv6_rules) + # Fixed rules for traffic after source address is verified + # Allow dhcp client renewal and rebinding + ipv4_rules += [comment_rule('-p udp -m udp --sport 68 --dport 67 ' + '-j RETURN', comment=ic.DHCP_CLIENT)] + # Drop Router Advts from the port. + ipv6_rules += [comment_rule('-p ipv6-icmp -m icmp6 --icmpv6-type %s ' + '-j DROP' % constants.ICMPV6_TYPE_RA, + comment=ic.IPV6_RA_DROP)] + ipv6_rules += [comment_rule('-p ipv6-icmp -j RETURN', + comment=ic.IPV6_ICMP_ALLOW)] + ipv6_rules += [comment_rule('-p udp -m udp --sport 546 ' + '-m udp --dport 547 ' + '-j RETURN', comment=ic.DHCP_CLIENT)] def _drop_dhcp_rule(self, ipv4_rules, ipv6_rules): #Note(nati) Drop dhcp packet from VM diff -Nru neutron-7.0.4/neutron/agent/linux/keepalived.py neutron-7.1.1/neutron/agent/linux/keepalived.py --- neutron-7.0.4/neutron/agent/linux/keepalived.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/keepalived.py 2016-06-10 01:43:08.000000000 +0000 @@ -24,13 +24,14 @@ from neutron.agent.linux import utils from neutron.common import exceptions from neutron.common import utils as common_utils +from neutron.i18n import _, _LE VALID_STATES = ['MASTER', 'BACKUP'] VALID_AUTH_TYPES = ['AH', 'PASS'] HA_DEFAULT_PRIORITY = 50 PRIMARY_VIP_RANGE_SIZE = 24 # TODO(amuller): Use L3 agent constant when new constants module is introduced. -FIP_LL_SUBNET = '169.254.30.0/23' +FIP_LL_SUBNET = '169.254.64.0/18' KEEPALIVED_SERVICE_NAME = 'keepalived' GARP_MASTER_DELAY = 60 @@ -378,6 +379,18 @@ return config_path + @staticmethod + def _safe_remove_pid_file(pid_file): + try: + os.remove(pid_file) + except OSError as e: + if e.errno != errno.ENOENT: + LOG.error(_LE("Could not delete file %s, keepalived can " + "refuse to start."), pid_file) + + def get_vrrp_pid_file_name(self, base_pid_file): + return '%s-vrrp' % base_pid_file + def get_conf_on_disk(self): config_path = self.get_full_config_file_path('keepalived.conf') try: @@ -392,7 +405,7 @@ keepalived_pm = self.get_process() vrrp_pm = self._get_vrrp_process( - '%s-vrrp' % keepalived_pm.get_pid_file_name()) + self.get_vrrp_pid_file_name(keepalived_pm.get_pid_file_name())) keepalived_pm.default_cmd_callback = ( self._get_keepalived_process_callback(vrrp_pm, config_path)) @@ -436,10 +449,14 @@ # and spawn keepalived successfully. if vrrp_pm.active: vrrp_pm.disable() + + self._safe_remove_pid_file(pid_file) + self._safe_remove_pid_file(self.get_vrrp_pid_file_name(pid_file)) + cmd = ['keepalived', '-P', '-f', config_path, '-p', pid_file, - '-r', '%s-vrrp' % pid_file] + '-r', self.get_vrrp_pid_file_name(pid_file)] return cmd return callback diff -Nru neutron-7.0.4/neutron/agent/linux/ovsdb_monitor.py neutron-7.1.1/neutron/agent/linux/ovsdb_monitor.py --- neutron-7.0.4/neutron/agent/linux/ovsdb_monitor.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/ovsdb_monitor.py 2016-06-10 01:43:08.000000000 +0000 @@ -26,6 +26,7 @@ OVSDB_ACTION_INITIAL = 'initial' OVSDB_ACTION_INSERT = 'insert' OVSDB_ACTION_DELETE = 'delete' +OVSDB_ACTION_NEW = 'new' class OvsdbMonitor(async_process.AsyncProcess): @@ -86,6 +87,7 @@ def process_events(self): devices_added = [] devices_removed = [] + dev_to_ofport = {} for row in self.iter_stdout(): json = jsonutils.loads(row).get('data') for ovs_id, action, name, ofport, external_ids in json: @@ -100,8 +102,14 @@ devices_added.append(device) elif action == OVSDB_ACTION_DELETE: devices_removed.append(device) + elif action == OVSDB_ACTION_NEW: + dev_to_ofport[name] = ofport + self.new_events['added'].extend(devices_added) self.new_events['removed'].extend(devices_removed) + # update any events with ofports received from 'new' action + for event in self.new_events['added']: + event['ofport'] = dev_to_ofport.get(event['name'], event['ofport']) def start(self, block=False, timeout=5): super(SimpleInterfaceMonitor, self).start() diff -Nru neutron-7.0.4/neutron/agent/linux/pd.py neutron-7.1.1/neutron/agent/linux/pd.py --- neutron-7.0.4/neutron/agent/linux/pd.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/pd.py 2016-06-10 01:43:08.000000000 +0000 @@ -39,8 +39,6 @@ help=_('Service to handle DHCPv6 Prefix delegation.')), ] -cfg.CONF.register_opts(OPTS) - class PrefixDelegation(object): def __init__(self, context, pmon, intf_driver, notifier, pd_update_cb, diff -Nru neutron-7.0.4/neutron/agent/linux/ra.py neutron-7.1.1/neutron/agent/linux/ra.py --- neutron-7.0.4/neutron/agent/linux/ra.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/ra.py 2016-06-10 01:43:08.000000000 +0000 @@ -35,14 +35,16 @@ help=_('Location to store IPv6 RA config files')), ] -cfg.CONF.register_opts(OPTS) - CONFIG_TEMPLATE = jinja2.Template("""interface {{ interface_name }} { AdvSendAdvert on; MinRtrAdvInterval 3; MaxRtrAdvInterval 10; + {% if network_mtu >= constants.IPV6_MIN_MTU %} + AdvLinkMTU {{network_mtu}}; + {% endif %} + {% if constants.DHCPV6_STATELESS in ra_modes %} AdvOtherConfigFlag on; {% endif %} @@ -51,13 +53,21 @@ AdvManagedFlag on; {% endif %} - {% for prefix in prefixes %} + {% for prefix in auto_config_prefixes %} prefix {{ prefix }} { AdvOnLink on; AdvAutonomous on; }; {% endfor %} + + {% for prefix in stateful_config_prefixes %} + prefix {{ prefix }} + { + AdvOnLink on; + AdvAutonomous off; + }; + {% endfor %} }; """) @@ -65,18 +75,21 @@ class DaemonMonitor(object): """Manage the data and state of an radvd process.""" - def __init__(self, router_id, router_ns, process_monitor, dev_name_helper): + def __init__(self, router_id, router_ns, process_monitor, dev_name_helper, + agent_conf): self._router_id = router_id self._router_ns = router_ns self._process_monitor = process_monitor self._dev_name_helper = dev_name_helper + self._agent_conf = agent_conf def _generate_radvd_conf(self, router_ports): - radvd_conf = utils.get_conf_file_name(cfg.CONF.ra_confs, + radvd_conf = utils.get_conf_file_name(self._agent_conf.ra_confs, self._router_id, 'radvd.conf', True) buf = six.StringIO() + network_mtu = 0 for p in router_ports: subnets = p.get('subnets', []) v6_subnets = [subnet for subnet in subnets if @@ -87,12 +100,19 @@ auto_config_prefixes = [subnet['cidr'] for subnet in v6_subnets if subnet['ipv6_ra_mode'] == constants.IPV6_SLAAC or subnet['ipv6_ra_mode'] == constants.DHCPV6_STATELESS] + stateful_config_prefixes = [subnet['cidr'] for subnet in v6_subnets + if subnet['ipv6_ra_mode'] == constants.DHCPV6_STATEFUL] interface_name = self._dev_name_helper(p['id']) + if self._agent_conf.advertise_mtu: + network_mtu = p.get('mtu', 0) + buf.write('%s' % CONFIG_TEMPLATE.render( ra_modes=list(ra_modes), interface_name=interface_name, - prefixes=auto_config_prefixes, - constants=constants)) + auto_config_prefixes=auto_config_prefixes, + stateful_config_prefixes=stateful_config_prefixes, + constants=constants, + network_mtu=int(network_mtu))) utils.replace_file(radvd_conf, buf.getvalue()) return radvd_conf @@ -103,7 +123,7 @@ default_cmd_callback=callback, namespace=self._router_ns, service=RADVD_SERVICE_NAME, - conf=cfg.CONF, + conf=self._agent_conf, run_as_root=True) def _spawn_radvd(self, radvd_conf): @@ -143,7 +163,7 @@ service_name=RADVD_SERVICE_NAME) pm = self._get_radvd_process_manager() pm.disable() - utils.remove_conf_files(cfg.CONF.ra_confs, self._router_id) + utils.remove_conf_files(self._agent_conf.ra_confs, self._router_id) LOG.debug("radvd disabled for router %s", self._router_id) @property diff -Nru neutron-7.0.4/neutron/agent/linux/utils.py neutron-7.1.1/neutron/agent/linux/utils.py --- neutron-7.0.4/neutron/agent/linux/utils.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/linux/utils.py 2016-06-10 01:43:08.000000000 +0000 @@ -406,7 +406,7 @@ self._socket = None self._launcher = None self._server = None - super(UnixDomainWSGIServer, self).__init__(name) + super(UnixDomainWSGIServer, self).__init__(name, disable_ssl=True) def start(self, application, file_socket, workers, backlog, mode=None): self._socket = eventlet.listen(file_socket, diff -Nru neutron-7.0.4/neutron/agent/metadata/driver.py neutron-7.1.1/neutron/agent/metadata/driver.py --- neutron-7.0.4/neutron/agent/metadata/driver.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/agent/metadata/driver.py 2016-06-10 01:43:08.000000000 +0000 @@ -65,7 +65,7 @@ return [('PREROUTING', '-d 169.254.169.254/32 ' '-i %(interface_name)s ' '-p tcp -m tcp --dport 80 -j REDIRECT ' - '--to-port %(port)s' % + '--to-ports %(port)s' % {'interface_name': namespaces.INTERNAL_DEV_PREFIX + '+', 'port': port})] @@ -166,14 +166,6 @@ def before_router_removed(resource, event, l3_agent, **kwargs): router = kwargs['router'] proxy = l3_agent.metadata_driver - for c, r in proxy.metadata_filter_rules(proxy.metadata_port, - proxy.metadata_access_mark): - router.iptables_manager.ipv4['filter'].remove_rule(c, r) - for c, r in proxy.metadata_mangle_rules(proxy.metadata_access_mark): - router.iptables_manager.ipv4['mangle'].remove_rule(c, r) - for c, r in proxy.metadata_nat_rules(proxy.metadata_port): - router.iptables_manager.ipv4['nat'].remove_rule(c, r) - router.iptables_manager.apply() proxy.destroy_monitored_metadata_proxy(l3_agent.process_monitor, router.router['id'], diff -Nru neutron-7.0.4/neutron/api/extensions.py neutron-7.1.1/neutron/api/extensions.py --- neutron-7.0.4/neutron/api/extensions.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/api/extensions.py 2016-06-10 01:43:08.000000000 +0000 @@ -267,8 +267,9 @@ action=action, path_prefix=path_prefix, conditions=conditions) as submap: - submap.connect(path) - submap.connect("%s.:(format)" % path) + submap.connect(path_prefix + path, path) + submap.connect(path_prefix + path + "_format", + "%s.:(format)" % path) mapper.resource(resource.collection, resource.collection, controller=resource.controller, diff -Nru neutron-7.0.4/neutron/api/rpc/agentnotifiers/l3_rpc_agent_api.py neutron-7.1.1/neutron/api/rpc/agentnotifiers/l3_rpc_agent_api.py --- neutron-7.0.4/neutron/api/rpc/agentnotifiers/l3_rpc_agent_api.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/api/rpc/agentnotifiers/l3_rpc_agent_api.py 2016-06-10 01:43:08.000000000 +0000 @@ -122,15 +122,28 @@ cctxt = self.client.prepare(fanout=True) cctxt.cast(context, method, routers=router_ids) - def _notification_fanout(self, context, method, router_id): - """Fanout the deleted router to all L3 agents.""" - LOG.debug('Fanout notify agent at %(topic)s the message ' - '%(method)s on router %(router_id)s', - {'topic': topics.L3_AGENT, - 'method': method, - 'router_id': router_id}) + def _notification_fanout(self, context, method, router_id=None, **kwargs): + """Fanout the information to all L3 agents. + + This function will fanout the router_id or ext_net_id + to the L3 Agents. + """ + ext_net_id = kwargs.get('ext_net_id') + if router_id: + kwargs['router_id'] = router_id + LOG.debug('Fanout notify agent at %(topic)s the message ' + '%(method)s on router %(router_id)s', + {'topic': topics.L3_AGENT, + 'method': method, + 'router_id': router_id}) + if ext_net_id: + LOG.debug('Fanout notify agent at %(topic)s the message ' + '%(method)s for external_network %(ext_net_id)s', + {'topic': topics.L3_AGENT, + 'method': method, + 'ext_net_id': ext_net_id}) cctxt = self.client.prepare(fanout=True) - cctxt.cast(context, method, router_id=router_id) + cctxt.cast(context, method, **kwargs) def agent_updated(self, context, admin_state_up, host): self._notification_host(context, 'agent_updated', host, @@ -153,6 +166,11 @@ self._agent_notification_arp(context, 'del_arp_entry', router_id, operation, arp_table) + def delete_fipnamespace_for_ext_net(self, context, ext_net_id): + self._notification_fanout( + context, 'fipnamespace_delete_on_ext_net', + ext_net_id=ext_net_id) + def router_removed_from_agent(self, context, router_id, host): self._notification_host(context, 'router_removed_from_agent', host, payload={'router_id': router_id}) diff -Nru neutron-7.0.4/neutron/api/rpc/handlers/dhcp_rpc.py neutron-7.1.1/neutron/api/rpc/handlers/dhcp_rpc.py --- neutron-7.0.4/neutron/api/rpc/handlers/dhcp_rpc.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/api/rpc/handlers/dhcp_rpc.py 2016-06-10 01:43:08.000000000 +0000 @@ -132,7 +132,11 @@ filters = {'network_id': [network['id'] for network in networks]} ports = plugin.get_ports(context, filters=filters) filters['enable_dhcp'] = [True] - subnets = plugin.get_subnets(context, filters=filters) + # NOTE(kevinbenton): we sort these because the agent builds tags + # based on position in the list and has to restart the process if + # the order changes. + subnets = sorted(plugin.get_subnets(context, filters=filters), + key=operator.itemgetter('id')) grouped_subnets = self._group_by_network_id(subnets) grouped_ports = self._group_by_network_id(ports) @@ -153,11 +157,16 @@ try: network = plugin.get_network(context, network_id) except n_exc.NetworkNotFound: - LOG.warn(_LW("Network %s could not be found, it might have " - "been deleted concurrently."), network_id) + LOG.debug("Network %s could not be found, it might have " + "been deleted concurrently.", network_id) return filters = dict(network_id=[network_id]) - network['subnets'] = plugin.get_subnets(context, filters=filters) + # NOTE(kevinbenton): we sort these because the agent builds tags + # based on position in the list and has to restart the process if + # the order changes. + network['subnets'] = sorted( + plugin.get_subnets(context, filters=filters), + key=operator.itemgetter('id')) network['ports'] = plugin.get_ports(context, filters=filters) return network diff -Nru neutron-7.0.4/neutron/api/v2/base.py neutron-7.1.1/neutron/api/v2/base.py --- neutron-7.0.4/neutron/api/v2/base.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/api/v2/base.py 2016-06-10 01:43:08.000000000 +0000 @@ -580,7 +580,8 @@ @db_api.retry_db_errors def _update(self, request, id, body, **kwargs): - body = Controller.prepare_request_body(request.context, body, False, + body = Controller.prepare_request_body(request.context, + copy.deepcopy(body), False, self._resource, self._attr_info, allow_bulk=self._allow_bulk) action = self._plugin_handlers[self.UPDATE] @@ -707,12 +708,10 @@ network_owner = network['tenant_id'] if network_owner != resource_item['tenant_id']: - msg = _("Tenant %(tenant_id)s not allowed to " - "create %(resource)s on this network") - raise webob.exc.HTTPForbidden(msg % { - "tenant_id": resource_item['tenant_id'], - "resource": self._resource, - }) + # NOTE(kevinbenton): we raise a 404 to hide the existence of the + # network from the tenant since they don't have access to it. + msg = _('The resource could not be found.') + raise webob.exc.HTTPNotFound(msg) def create_resource(collection, resource, plugin, params, allow_bulk=False, diff -Nru neutron-7.0.4/neutron/callbacks/resources.py neutron-7.1.1/neutron/callbacks/resources.py --- neutron-7.0.4/neutron/callbacks/resources.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/callbacks/resources.py 2016-06-10 01:43:08.000000000 +0000 @@ -11,6 +11,7 @@ # under the License. # String literals representing core resources. +AGENT = 'agent' PORT = 'port' PROCESS = 'process' ROUTER = 'router' diff -Nru neutron-7.0.4/neutron/common/constants.py neutron-7.1.1/neutron/common/constants.py --- neutron-7.0.4/neutron/common/constants.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/common/constants.py 2016-06-10 01:43:08.000000000 +0000 @@ -30,6 +30,10 @@ FLOATINGIP_STATUS_DOWN = 'DOWN' FLOATINGIP_STATUS_ERROR = 'ERROR' +DEVICE_OWNER_COMPUTE_PREFIX = "compute:" +DEVICE_OWNER_NETWORK_PREFIX = "network:" +DEVICE_OWNER_NEUTRON_PREFIX = "neutron:" + DEVICE_OWNER_ROUTER_HA_INTF = "network:router_ha_interface" DEVICE_OWNER_ROUTER_INTF = "network:router_interface" DEVICE_OWNER_ROUTER_GW = "network:router_gateway" @@ -38,11 +42,11 @@ DEVICE_OWNER_DVR_INTERFACE = "network:router_interface_distributed" DEVICE_OWNER_AGENT_GW = "network:floatingip_agent_gateway" DEVICE_OWNER_ROUTER_SNAT = "network:router_centralized_snat" -DEVICE_OWNER_LOADBALANCER = "neutron:LOADBALANCER" -DEVICE_OWNER_LOADBALANCERV2 = "neutron:LOADBALANCERV2" +DEVICE_OWNER_LOADBALANCER = DEVICE_OWNER_NEUTRON_PREFIX + "LOADBALANCER" +DEVICE_OWNER_LOADBALANCERV2 = DEVICE_OWNER_NEUTRON_PREFIX + "LOADBALANCERV2" -DEVICE_OWNER_COMPUTE_PREFIX = "compute:" -DEVICE_OWNER_PREFIXES = ["network:", "neutron:"] +DEVICE_OWNER_PREFIXES = (DEVICE_OWNER_NETWORK_PREFIX, + DEVICE_OWNER_NEUTRON_PREFIX) # Collection used to identify devices owned by router interfaces. # DEVICE_OWNER_ROUTER_HA_INTF is a special case and so is not included. @@ -126,6 +130,13 @@ PROTO_NUM_ICMP_V6 = 58 PROTO_NUM_UDP = 17 +IP_PROTOCOL_MAP = {PROTO_NAME_TCP: PROTO_NUM_TCP, + PROTO_NAME_UDP: PROTO_NUM_UDP, + PROTO_NAME_ICMP: PROTO_NUM_ICMP, + PROTO_NAME_ICMP_V6: PROTO_NUM_ICMP_V6} + +IP_PROTOCOL_NUM_TO_NAME_MAP = {str(v): k for k, v in IP_PROTOCOL_MAP.items()} + # List of ICMPv6 types that should be allowed by default: # Multicast Listener Query (130), # Multicast Listener Report (131), diff -Nru neutron-7.0.4/neutron/common/rpc.py neutron-7.1.1/neutron/common/rpc.py --- neutron-7.0.4/neutron/common/rpc.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/common/rpc.py 2016-06-10 01:43:08.000000000 +0000 @@ -14,14 +14,20 @@ # License for the specific language governing permissions and limitations # under the License. +import collections +import random +import time + from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_messaging import serializer as om_serializer from oslo_service import service +from oslo_utils import excutils from neutron.common import exceptions from neutron import context +from neutron.i18n import _LE, _LW LOG = logging.getLogger(__name__) @@ -70,6 +76,7 @@ assert NOTIFIER is not None TRANSPORT.cleanup() TRANSPORT = NOTIFIER = None + _ContextWrapper.reset_timeouts() def add_extra_exmods(*args): @@ -84,13 +91,84 @@ return ALLOWED_EXMODS + EXTRA_EXMODS +class _ContextWrapper(object): + """Wraps oslo messaging contexts to set the timeout for calls. + + This intercepts RPC calls and sets the timeout value to the globally + adapting value for each method. An oslo messaging timeout results in + a doubling of the timeout value for the method on which it timed out. + There currently is no logic to reduce the timeout since busy Neutron + servers are more frequently the cause of timeouts rather than lost + messages. + """ + _METHOD_TIMEOUTS = collections.defaultdict( + lambda: TRANSPORT.conf.rpc_response_timeout) + + @classmethod + def reset_timeouts(cls): + cls._METHOD_TIMEOUTS.clear() + + def __init__(self, original_context): + self._original_context = original_context + + def __getattr__(self, name): + return getattr(self._original_context, name) + + def call(self, ctxt, method, **kwargs): + # two methods with the same name in different namespaces should + # be tracked independently + if self._original_context.target.namespace: + scoped_method = '%s.%s' % (self._original_context.target.namespace, + method) + else: + scoped_method = method + # set the timeout from the global method timeout tracker for this + # method + self._original_context.timeout = self._METHOD_TIMEOUTS[scoped_method] + try: + return self._original_context.call(ctxt, method, **kwargs) + except oslo_messaging.MessagingTimeout: + with excutils.save_and_reraise_exception(): + wait = random.uniform(0, TRANSPORT.conf.rpc_response_timeout) + LOG.error(_LE("Timeout in RPC method %(method)s. Waiting for " + "%(wait)s seconds before next attempt. If the " + "server is not down, consider increasing the " + "rpc_response_timeout option as Neutron " + "server(s) may be overloaded and unable to " + "respond quickly enough."), + {'wait': int(round(wait)), 'method': scoped_method}) + ceiling = TRANSPORT.conf.rpc_response_timeout * 10 + new_timeout = min(self._original_context.timeout * 2, ceiling) + if new_timeout > self._METHOD_TIMEOUTS[scoped_method]: + LOG.warning(_LW("Increasing timeout for %(method)s calls " + "to %(new)s seconds. Restart the agent to " + "restore it to the default value."), + {'method': scoped_method, 'new': new_timeout}) + self._METHOD_TIMEOUTS[scoped_method] = new_timeout + time.sleep(wait) + + +class BackingOffClient(oslo_messaging.RPCClient): + """An oslo messaging RPC Client that implements a timeout backoff. + + This has all of the same interfaces as oslo_messaging.RPCClient but + if the timeout parameter is not specified, the _ContextWrapper returned + will track when call timeout exceptions occur and exponentially increase + the timeout for the given call method. + """ + def prepare(self, *args, **kwargs): + ctx = super(BackingOffClient, self).prepare(*args, **kwargs) + # don't enclose Contexts that explicitly set a timeout + return _ContextWrapper(ctx) if 'timeout' not in kwargs else ctx + + def get_client(target, version_cap=None, serializer=None): assert TRANSPORT is not None serializer = RequestContextSerializer(serializer) - return oslo_messaging.RPCClient(TRANSPORT, - target, - version_cap=version_cap, - serializer=serializer) + return BackingOffClient(TRANSPORT, + target, + version_cap=version_cap, + serializer=serializer) def get_server(target, endpoints, serializer=None): diff -Nru neutron-7.0.4/neutron/db/agentschedulers_db.py neutron-7.1.1/neutron/db/agentschedulers_db.py --- neutron-7.0.4/neutron/db/agentschedulers_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/agentschedulers_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -263,14 +263,13 @@ cutoff = self.get_cutoff_time(agent_dead_limit) context = ncontext.get_admin_context() - down_bindings = ( - context.session.query(NetworkDhcpAgentBinding). - join(agents_db.Agent). - filter(agents_db.Agent.heartbeat_timestamp < cutoff, - agents_db.Agent.admin_state_up)) - dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP) - try: + down_bindings = ( + context.session.query(NetworkDhcpAgentBinding). + join(agents_db.Agent). + filter(agents_db.Agent.heartbeat_timestamp < cutoff, + agents_db.Agent.admin_state_up)) + dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP) dead_bindings = [b for b in self._filter_bindings(context, down_bindings)] agents = self.get_agents_db( diff -Nru neutron-7.0.4/neutron/db/agents_db.py neutron-7.1.1/neutron/db/agents_db.py --- neutron-7.0.4/neutron/db/agents_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/agents_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -25,6 +25,9 @@ from sqlalchemy import sql from neutron.api.v2 import attributes +from neutron.callbacks import events +from neutron.callbacks import registry +from neutron.callbacks import resources from neutron.common import constants from neutron.db import model_base from neutron.db import models_v2 @@ -165,8 +168,10 @@ return self._fields(res, fields) def delete_agent(self, context, id): + agent = self._get_agent(context, id) + registry.notify(resources.AGENT, events.BEFORE_DELETE, self, + context=context, agent=agent) with context.session.begin(subtransactions=True): - agent = self._get_agent(context, id) context.session.delete(agent) def update_agent(self, context, id, agent): diff -Nru neutron-7.0.4/neutron/db/db_base_plugin_common.py neutron-7.1.1/neutron/db/db_base_plugin_common.py --- neutron-7.0.4/neutron/db/db_base_plugin_common.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/db_base_plugin_common.py 2016-06-10 01:43:08.000000000 +0000 @@ -107,6 +107,12 @@ subnet_id=subnet_id ) context.session.add(allocated) + # NOTE(kevinbenton): We add this to the session info so the sqlalchemy + # object isn't immediately garbage collected. Otherwise when the + # fixed_ips relationship is referenced a new persistent object will be + # added to the session that will interfere with retry operations. + # See bug 1556178 for details. + context.session.info.setdefault('allocated_ips', []).append(allocated) def _make_subnet_dict(self, subnet, fields=None, context=None): res = {'id': subnet['id'], @@ -314,3 +320,21 @@ return [{'subnet_id': ip["subnet_id"], 'ip_address': ip["ip_address"]} for ip in ips] + + def _port_filter_hook(self, context, original_model, conditions): + # Apply the port filter only in non-admin and non-advsvc context + if self.model_query_scope(context, original_model): + conditions |= ( + (context.tenant_id == models_v2.Network.tenant_id) & + (models_v2.Network.id == models_v2.Port.network_id)) + return conditions + + def _port_query_hook(self, context, original_model, query): + # we need to outerjoin to networks if the model query scope + # is necessary so we can filter based on network id. without + # this the conditions in the filter hook cause the networks + # table to be added to the FROM statement so we get lots of + # duplicated rows that break the COUNT operation + if self.model_query_scope(context, original_model): + query = query.outerjoin(models_v2.Network) + return query diff -Nru neutron-7.0.4/neutron/db/db_base_plugin_v2.py neutron-7.1.1/neutron/db/db_base_plugin_v2.py --- neutron-7.0.4/neutron/db/db_base_plugin_v2.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/db_base_plugin_v2.py 2016-06-10 01:43:08.000000000 +0000 @@ -23,6 +23,7 @@ from oslo_utils import uuidutils from sqlalchemy import and_ from sqlalchemy import event +from sqlalchemy import not_ from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.api.v2 import attributes @@ -208,13 +209,9 @@ if updated['shared'] == original.shared or updated['shared']: return ports = self._model_query( - context, models_v2.Port).filter( - and_( - models_v2.Port.network_id == id, - models_v2.Port.device_owner != - constants.DEVICE_OWNER_ROUTER_GW, - models_v2.Port.device_owner != - constants.DEVICE_OWNER_FLOATINGIP)) + context, models_v2.Port).filter(models_v2.Port.network_id == id) + ports = ports.filter(not_(models_v2.Port.device_owner.startswith( + constants.DEVICE_OWNER_NETWORK_PREFIX))) subnets = self._model_query( context, models_v2.Subnet).filter( models_v2.Subnet.network_id == id) @@ -522,8 +519,7 @@ raise n_exc.BadRequest(resource='subnets', msg=reason) mode_list = [constants.IPV6_SLAAC, - constants.DHCPV6_STATELESS, - attributes.ATTR_NOT_SPECIFIED] + constants.DHCPV6_STATELESS] ra_mode = subnet.get('ipv6_ra_mode') if ra_mode not in mode_list: @@ -1339,3 +1335,10 @@ device_id=device_id) if tenant_id != router['tenant_id']: raise n_exc.DeviceIDNotOwnedByTenant(device_id=device_id) + + db_base_plugin_common.DbBasePluginCommon.register_model_query_hook( + models_v2.Port, + "port", + '_port_query_hook', + '_port_filter_hook', + None) diff -Nru neutron-7.0.4/neutron/db/dvr_mac_db.py neutron-7.1.1/neutron/db/dvr_mac_db.py --- neutron-7.0.4/neutron/db/dvr_mac_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/dvr_mac_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -20,11 +20,15 @@ import sqlalchemy as sa from sqlalchemy.orm import exc +from neutron.callbacks import events +from neutron.callbacks import registry +from neutron.callbacks import resources from neutron.common import exceptions as n_exc from neutron.common import utils from neutron.db import model_base from neutron.extensions import dvr as ext_dvr from neutron.extensions import portbindings +from neutron.i18n import _ from neutron.i18n import _LE from neutron import manager @@ -57,9 +61,36 @@ mac_address = sa.Column(sa.String(32), nullable=False, unique=True) +def _delete_mac_associated_with_agent(resource, event, trigger, context, agent, + **kwargs): + host = agent['host'] + plugin = manager.NeutronManager.get_plugin() + if [a for a in plugin.get_agents(context, filters={'host': [host]}) + if a['id'] != agent['id']]: + # there are still agents on this host, don't mess with the mac entry + # until they are all deleted. + return + try: + with context.session.begin(subtransactions=True): + entry = (context.session.query(DistributedVirtualRouterMacAddress). + filter(DistributedVirtualRouterMacAddress.host == host). + one()) + context.session.delete(entry) + except exc.NoResultFound: + return + # notify remaining agents so they cleanup flows + dvr_macs = plugin.get_dvr_mac_address_list(context) + plugin.notifier.dvr_mac_address_update(context, dvr_macs) + + class DVRDbMixin(ext_dvr.DVRMacAddressPluginBase): """Mixin class to add dvr mac address to db_plugin_base_v2.""" + def __new__(cls, *args, **kwargs): + registry.subscribe(_delete_mac_associated_with_agent, + resources.AGENT, events.BEFORE_DELETE) + return super(DVRDbMixin, cls).__new__(cls) + @property def plugin(self): try: diff -Nru neutron-7.0.4/neutron/db/ipam_backend_mixin.py neutron-7.1.1/neutron/db/ipam_backend_mixin.py --- neutron-7.0.4/neutron/db/ipam_backend_mixin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/ipam_backend_mixin.py 2016-06-10 01:43:08.000000000 +0000 @@ -26,6 +26,7 @@ from neutron.common import constants from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils +from neutron.common import utils as common_utils from neutron.db import db_base_plugin_common from neutron.db import models_v2 from neutron.i18n import _LI @@ -297,7 +298,10 @@ pool_2=r_range, subnet_cidr=subnet_cidr) - def _validate_max_ips_per_port(self, fixed_ip_list): + def _validate_max_ips_per_port(self, fixed_ip_list, device_owner): + if common_utils.is_port_trusted({'device_owner': device_owner}): + return + if len(fixed_ip_list) > cfg.CONF.max_fixed_ips_per_port: msg = _('Exceeded maximim amount of fixed ips per port') raise n_exc.InvalidInput(error_message=msg) @@ -367,9 +371,7 @@ new_ips, device_owner): """Calculate changes in IPs for the port.""" # the new_ips contain all of the fixed_ips that are to be updated - if len(new_ips) > cfg.CONF.max_fixed_ips_per_port: - msg = _('Exceeded maximum amount of fixed ips per port') - raise n_exc.InvalidInput(error_message=msg) + self._validate_max_ips_per_port(new_ips, device_owner) add_ips = [] remove_ips = [] @@ -413,8 +415,6 @@ def delete_port(self, context, port_id): query = (context.session.query(models_v2.Port). enable_eagerloads(False).filter_by(id=port_id)) - if not context.is_admin: - query = query.filter_by(tenant_id=context.tenant_id) # Use of the ORM mapper is needed for ensuring appropriate resource # tracking; otherwise SQL Alchemy events won't be triggered. # For more info check 'caveats' in doc/source/devref/quota.rst diff -Nru neutron-7.0.4/neutron/db/ipam_non_pluggable_backend.py neutron-7.1.1/neutron/db/ipam_non_pluggable_backend.py --- neutron-7.0.4/neutron/db/ipam_non_pluggable_backend.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/ipam_non_pluggable_backend.py 2016-06-10 01:43:08.000000000 +0000 @@ -273,7 +273,7 @@ not is_auto_addr_subnet): fixed_ip_set.append({'subnet_id': subnet['id']}) - self._validate_max_ips_per_port(fixed_ip_set) + self._validate_max_ips_per_port(fixed_ip_set, device_owner) return fixed_ip_set def _allocate_fixed_ips(self, context, fixed_ips, mac_address): diff -Nru neutron-7.0.4/neutron/db/ipam_pluggable_backend.py neutron-7.1.1/neutron/db/ipam_pluggable_backend.py --- neutron-7.0.4/neutron/db/ipam_pluggable_backend.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/ipam_pluggable_backend.py 2016-06-10 01:43:08.000000000 +0000 @@ -270,7 +270,7 @@ not is_auto_addr_subnet): fixed_ip_list.append({'subnet_id': subnet['id']}) - self._validate_max_ips_per_port(fixed_ip_list) + self._validate_max_ips_per_port(fixed_ip_list, device_owner) return fixed_ip_list def _update_ips_for_port(self, context, port, diff -Nru neutron-7.0.4/neutron/db/l3_agentschedulers_db.py neutron-7.1.1/neutron/db/l3_agentschedulers_db.py --- neutron-7.0.4/neutron/db/l3_agentschedulers_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/l3_agentschedulers_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -98,17 +98,20 @@ cutoff = self.get_cutoff_time(agent_dead_limit) context = n_ctx.get_admin_context() - down_bindings = ( - context.session.query(RouterL3AgentBinding). - join(agents_db.Agent). - filter(agents_db.Agent.heartbeat_timestamp < cutoff, - agents_db.Agent.admin_state_up). - outerjoin(l3_attrs_db.RouterExtraAttributes, - l3_attrs_db.RouterExtraAttributes.router_id == - RouterL3AgentBinding.router_id). - filter(sa.or_(l3_attrs_db.RouterExtraAttributes.ha == sql.false(), - l3_attrs_db.RouterExtraAttributes.ha == sql.null()))) try: + down_bindings = ( + context.session.query(RouterL3AgentBinding). + join(agents_db.Agent). + filter(agents_db.Agent.heartbeat_timestamp < cutoff, + agents_db.Agent.admin_state_up). + outerjoin(l3_attrs_db.RouterExtraAttributes, + l3_attrs_db.RouterExtraAttributes.router_id == + RouterL3AgentBinding.router_id). + filter(sa.or_(l3_attrs_db.RouterExtraAttributes.ha == + sql.false(), + l3_attrs_db.RouterExtraAttributes.ha == + sql.null()))) + agents_back_online = set() for binding in down_bindings: if binding.l3_agent_id in agents_back_online: diff -Nru neutron-7.0.4/neutron/db/l3_db.py neutron-7.1.1/neutron/db/l3_db.py --- neutron-7.0.4/neutron/db/l3_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/l3_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -518,6 +518,12 @@ if not (port_id_specified or subnet_id_specified): msg = _("Either subnet_id or port_id must be specified") raise n_exc.BadRequest(resource='router', msg=msg) + for key in ('port_id', 'subnet_id'): + if key not in interface_info: + continue + err = attributes._validate_uuid(interface_info[key]) + if err: + raise n_exc.BadRequest(resource='router', msg=err) if not for_removal: if port_id_specified and subnet_id_specified: msg = _("Cannot specify both subnet-id and port-id") @@ -1092,6 +1098,20 @@ return self._get_collection_count(context, FloatingIP, filters=filters) + def _router_exists(self, context, router_id): + try: + self.get_router(context.elevated(), router_id) + return True + except l3.RouterNotFound: + return False + + def _floating_ip_exists(self, context, floating_ip_id): + try: + self.get_floatingip(context, floating_ip_id) + return True + except l3.FloatingIPNotFound: + return False + def prevent_l3_port_deletion(self, context, port_id): """Checks to make sure a port is allowed to be deleted. @@ -1106,19 +1126,38 @@ except n_exc.PortNotFound: # non-existent ports don't need to be protected from deletion return - if port['device_owner'] in self.router_device_owners: - # Raise port in use only if the port has IP addresses - # Otherwise it's a stale port that can be removed - fixed_ips = port['fixed_ips'] - if fixed_ips: - reason = _('has device owner %s') % port['device_owner'] - raise n_exc.ServicePortInUse(port_id=port['id'], - reason=reason) - else: - LOG.debug("Port %(port_id)s has owner %(port_owner)s, but " - "no IP address, so it can be deleted", - {'port_id': port['id'], - 'port_owner': port['device_owner']}) + if port['device_owner'] not in self.router_device_owners: + return + # Raise port in use only if the port has IP addresses + # Otherwise it's a stale port that can be removed + fixed_ips = port['fixed_ips'] + if not fixed_ips: + LOG.debug("Port %(port_id)s has owner %(port_owner)s, but " + "no IP address, so it can be deleted", + {'port_id': port['id'], + 'port_owner': port['device_owner']}) + return + # NOTE(kevinbenton): we also check to make sure that the + # router still exists. It's possible for HA router interfaces + # to remain after the router is deleted if they encounter an + # error during deletion. + # Elevated context in case router is owned by another tenant + if port['device_owner'] == DEVICE_OWNER_FLOATINGIP: + if not self._floating_ip_exists(context, port['device_id']): + LOG.debug("Floating IP %(f_id)s corresponding to port " + "%(port_id)s no longer exists, allowing deletion.", + {'f_id': port['device_id'], 'port_id': port['id']}) + return + elif not self._router_exists(context, port['device_id']): + LOG.debug("Router %(router_id)s corresponding to port " + "%(port_id)s no longer exists, allowing deletion.", + {'router_id': port['device_id'], + 'port_id': port['id']}) + return + + reason = _('has device owner %s') % port['device_owner'] + raise n_exc.ServicePortInUse(port_id=port['id'], + reason=reason) def disassociate_floatingips(self, context, port_id): """Disassociate all floating IPs linked to specific port. @@ -1198,7 +1237,18 @@ for rp in qry] return interfaces - def _populate_subnets_for_ports(self, context, ports): + def _get_mtus_by_network_list(self, context, network_ids): + if not network_ids: + return {} + filters = {'network_id': network_ids} + fields = ['id', 'mtu'] + networks = self._core_plugin.get_networks(context, filters=filters, + fields=fields) + mtus_by_network = dict((network['id'], network.get('mtu', 0)) + for network in networks) + return mtus_by_network + + def _populate_mtu_and_subnets_for_ports(self, context, ports): """Populate ports with subnets. These ports already have fixed_ips populated. @@ -1224,6 +1274,7 @@ fields = ['id', 'cidr', 'gateway_ip', 'network_id', 'ipv6_ra_mode', 'subnetpool_id'] + mtus_by_network = self._get_mtus_by_network_list(context, network_ids) subnets_by_network = dict((id, []) for id in network_ids) for subnet in self._core_plugin.get_subnets(context, filters, fields): subnets_by_network[subnet['network_id']].append(subnet) @@ -1253,6 +1304,8 @@ # This subnet is not used by the port. port['extra_subnets'].append(subnet_info) + port['mtu'] = mtus_by_network.get(port['network_id'], 0) + def _process_floating_ips(self, context, routers_dict, floating_ips): for floating_ip in floating_ips: router = routers_dict.get(floating_ip['router_id']) @@ -1288,7 +1341,7 @@ context, router_ids=router_ids, active=active) ports_to_populate = [router['gw_port'] for router in routers if router.get('gw_port')] + interfaces - self._populate_subnets_for_ports(context, ports_to_populate) + self._populate_mtu_and_subnets_for_ports(context, ports_to_populate) routers_dict = dict((router['id'], router) for router in routers) self._process_floating_ips(context, routers_dict, floating_ips) self._process_interfaces(routers_dict, interfaces) diff -Nru neutron-7.0.4/neutron/db/l3_dvr_db.py neutron-7.1.1/neutron/db/l3_dvr_db.py --- neutron-7.0.4/neutron/db/l3_dvr_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/l3_dvr_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -161,7 +161,7 @@ self)._delete_current_gw_port(context, router_id, router, new_network) if (is_distributed_router(router) and - gw_ext_net_id != new_network): + gw_ext_net_id != new_network and gw_ext_net_id is not None): self.delete_csnat_router_interface_ports( context.elevated(), router) # NOTE(Swami): Delete the Floatingip agent gateway port @@ -174,6 +174,10 @@ if not ext_net_gw_ports: self.delete_floatingip_agent_gateway_port( context.elevated(), None, gw_ext_net_id) + # Send the information to all the L3 Agent hosts + # to clean up the fip namespace as it is no longer required. + self.l3_rpc_notifier.delete_fipnamespace_for_ext_net( + context, gw_ext_net_id) def _create_gw_port(self, context, router_id, router, new_network, ext_ips): @@ -309,11 +313,19 @@ subnet_ids = plugin.get_subnet_ids_on_router( context, router['id']) if subnet_ids: + binding_table = l3_dvrsched_db.CentralizedSnatL3AgentBinding + snat_binding = context.session.query(binding_table).filter_by( + router_id=router['id']).first() for l3_agent in l3_agents: - if not plugin.check_ports_exist_on_l3agent( - context, l3_agent, subnet_ids): - plugin.remove_router_from_l3_agent( - context, l3_agent['id'], router['id']) + is_this_snat_agent = ( + snat_binding and + snat_binding.l3_agent_id == l3_agent['id']) + if (is_this_snat_agent or + plugin.check_ports_exist_on_l3agent( + context, l3_agent, subnet_ids)): + continue + plugin.remove_router_from_l3_agent( + context, l3_agent['id'], router['id']) router_interface_info = self._make_router_interface_info( router['id'], port['tenant_id'], port['id'], subnets[0]['id'], [subnet['id'] for subnet in subnets]) @@ -466,7 +478,7 @@ if router.get(l3_const.SNAT_ROUTER_INTF_KEY): ports_to_populate += router[l3_const.SNAT_ROUTER_INTF_KEY] ports_to_populate += interfaces - self._populate_subnets_for_ports(context, ports_to_populate) + self._populate_mtu_and_subnets_for_ports(context, ports_to_populate) self._process_interfaces(routers_dict, interfaces) return list(routers_dict.values()) @@ -541,12 +553,13 @@ agent_port = p_utils.create_port(self._core_plugin, context, {'port': port_data}) if agent_port: - self._populate_subnets_for_ports(context, [agent_port]) + self._populate_mtu_and_subnets_for_ports(context, + [agent_port]) return agent_port msg = _("Unable to create the Agent Gateway Port") raise n_exc.BadRequest(resource='router', msg=msg) else: - self._populate_subnets_for_ports(context, [f_port]) + self._populate_mtu_and_subnets_for_ports(context, [f_port]) return f_port def _get_snat_interface_ports_for_router(self, context, router_id): @@ -586,7 +599,8 @@ context.session.add(router_port) if do_pop: - return self._populate_subnets_for_ports(context, [snat_port]) + return self._populate_mtu_and_subnets_for_ports(context, + [snat_port]) return snat_port def _create_snat_intf_ports_if_not_exists(self, context, router): @@ -599,7 +613,7 @@ port_list = self._get_snat_interface_ports_for_router( context, router.id) if port_list: - self._populate_subnets_for_ports(context, port_list) + self._populate_mtu_and_subnets_for_ports(context, port_list) return port_list port_list = [] @@ -621,7 +635,7 @@ intf['fixed_ips'][0]['subnet_id'], do_pop=False) port_list.append(snat_port) if port_list: - self._populate_subnets_for_ports(context, port_list) + self._populate_mtu_and_subnets_for_ports(context, port_list) return port_list def _generate_arp_table_and_notify_agent( @@ -642,12 +656,13 @@ notifier(context, router_id, arp_table) return - def dvr_vmarp_table_update(self, context, port_dict, action): - """Notify L3 agents of VM ARP table changes. - - When a VM goes up or down, look for one DVR router on the port's - subnet, and send the VM's ARP details to all L3 agents hosting the - router. + def update_arp_entry_for_dvr_service_port( + self, context, port_dict, action): + """Notify L3 agents of ARP table entry for dvr service port. + + When a dvr service port goes up or down, look for the DVR + router on the port's subnet, and send the ARP details to all + L3 agents hosting the router. """ # Check this is a valid VM or service port diff -Nru neutron-7.0.4/neutron/db/l3_dvrscheduler_db.py neutron-7.1.1/neutron/db/l3_dvrscheduler_db.py --- neutron-7.0.4/neutron/db/l3_dvrscheduler_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/l3_dvrscheduler_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -99,9 +99,14 @@ the state of the router and the Compute Nodes. """ - def dvr_update_router_addvm(self, context, port): - port_dict = self._core_plugin.get_port(context, port['id']) - port_host = port_dict['binding:host_id'] + def dvr_handle_new_service_port(self, context, port): + """Handle new dvr service port creation. + + When a new dvr service port is created, this function will + schedule a dvr router to new compute node if needed and notify + l3 agent on that node. + """ + port_host = port[portbindings.HOST_ID] l3_agent_on_host = (self.get_l3_agents( context, filters={'host': [port_host]}) or [None])[0] if not l3_agent_on_host: @@ -114,7 +119,7 @@ context, router_id, l3_agent_on_host['id']): self.schedule_router( context, router_id, candidates=[l3_agent_on_host]) - LOG.debug('DVR: dvr_update_router_addvm %s ', router_id) + LOG.debug('DVR: Handle new service_port on router: %s', router_id) self.l3_rpc_notifier.routers_updated_on_host( context, router_ids, port_host) @@ -153,7 +158,7 @@ def check_ports_on_host_and_subnet(self, context, host, port_id, subnet_id): - """Check if there is any dvr serviceable port on the subnet_id.""" + """Check if there are any dvr service ports on the subnet_id.""" filter_sub = {'fixed_ips': {'subnet_id': [subnet_id]}} ports = self._core_plugin.get_ports(context, filters=filter_sub) for port in ports: @@ -183,8 +188,17 @@ 'on host %(host)s', {'port': port_id, 'host': port_host}) return [] + agent = self._get_agent_by_type_and_host( + context, n_const.AGENT_TYPE_L3, port_host) removed_router_info = [] for router_id in router_ids: + snat_binding = context.session.query( + CentralizedSnatL3AgentBinding).filter_by( + router_id=router_id).filter_by( + l3_agent_id=agent.id).first() + if snat_binding: + # not removing from the agent hosting SNAT for the router + continue subnet_ids = self.get_subnet_ids_on_router(admin_context, router_id) port_exists_on_subnet = False @@ -212,9 +226,7 @@ # unbind this port from router dvr_binding['router_id'] = None dvr_binding.update(dvr_binding) - agent = self._get_agent_by_type_and_host(context, - n_const.AGENT_TYPE_L3, - port_host) + info = {'router_id': router_id, 'host': port_host, 'agent_id': str(agent.id)} removed_router_info.append(info) @@ -468,8 +480,8 @@ l3plugin = manager.NeutronManager.get_service_plugins().get( service_constants.L3_ROUTER_NAT) context = kwargs['context'] - l3plugin.dvr_update_router_addvm(context, port) - l3plugin.dvr_vmarp_table_update(context, port, "add") + l3plugin.dvr_handle_new_service_port(context, port) + l3plugin.update_arp_entry_for_dvr_service_port(context, port, "add") def _notify_port_delete(event, resource, trigger, **kwargs): @@ -478,10 +490,13 @@ removed_routers = kwargs['removed_routers'] l3plugin = manager.NeutronManager.get_service_plugins().get( service_constants.L3_ROUTER_NAT) - l3plugin.dvr_vmarp_table_update(context, port, "del") + l3plugin.update_arp_entry_for_dvr_service_port(context, port, "del") for router in removed_routers: + # we need admin context in case a tenant removes the last dvr + # serviceable port on a shared network owned by admin, where router + # is also owned by admin l3plugin.remove_router_from_l3_agent( - context, router['agent_id'], router['router_id']) + context.elevated(), router['agent_id'], router['router_id']) def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): @@ -525,10 +540,12 @@ new_port[portbindings.HOST_ID])) if (is_new_port_binding_changed and n_utils.is_dvr_serviced(new_device_owner)): - l3plugin.dvr_update_router_addvm(context, new_port) - l3plugin.dvr_vmarp_table_update(context, new_port, "add") + l3plugin.dvr_handle_new_service_port(context, new_port) + l3plugin.update_arp_entry_for_dvr_service_port( + context, new_port, "add") elif kwargs.get('mac_address_updated') or is_fixed_ips_changed: - l3plugin.dvr_vmarp_table_update(context, new_port, "add") + l3plugin.update_arp_entry_for_dvr_service_port( + context, new_port, "add") def subscribe(): diff -Nru neutron-7.0.4/neutron/db/l3_hamode_db.py neutron-7.1.1/neutron/db/l3_hamode_db.py --- neutron-7.0.4/neutron/db/l3_hamode_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/l3_hamode_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -601,7 +601,7 @@ for router in routers_dict.values(): interface = router.get(constants.HA_INTERFACE_KEY) if interface: - self._populate_subnets_for_ports(context, [interface]) + self._populate_mtu_and_subnets_for_ports(context, [interface]) # we don't want to return HA routers without HA interfaces created yet return [r for r in list(routers_dict.values()) diff -Nru neutron-7.0.4/neutron/db/migration/cli.py neutron-7.1.1/neutron/db/migration/cli.py --- neutron-7.0.4/neutron/db/migration/cli.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/migration/cli.py 2016-06-10 01:43:08.000000000 +0000 @@ -185,7 +185,12 @@ revision = 'heads' if revision in migration.NEUTRON_MILESTONES: - revisions = _find_milestone_revisions(config, revision, branch) + expand_revisions = _find_milestone_revisions(config, revision, + EXPAND_BRANCH) + contract_revisions = _find_milestone_revisions(config, revision, + CONTRACT_BRANCH) + # Expand revisions must be run before contract revisions + revisions = expand_revisions + contract_revisions else: revisions = [(revision, branch)] diff -Nru neutron-7.0.4/neutron/db/models_v2.py neutron-7.1.1/neutron/db/models_v2.py --- neutron-7.0.4/neutron/db/models_v2.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/models_v2.py 2016-06-10 01:43:08.000000000 +0000 @@ -119,7 +119,8 @@ network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"), nullable=False) fixed_ips = orm.relationship(IPAllocation, backref='port', lazy='joined', - passive_deletes='all') + cascade='all, delete-orphan') + mac_address = sa.Column(sa.String(32), nullable=False) admin_state_up = sa.Column(sa.Boolean(), nullable=False) status = sa.Column(sa.String(16), nullable=False) diff -Nru neutron-7.0.4/neutron/db/portsecurity_db_common.py neutron-7.1.1/neutron/db/portsecurity_db_common.py --- neutron-7.0.4/neutron/db/portsecurity_db_common.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/portsecurity_db_common.py 2016-06-10 01:43:08.000000000 +0000 @@ -56,6 +56,13 @@ class PortSecurityDbCommon(object): """Mixin class to add port security.""" + def _extend_port_security_dict(self, response_data, db_data): + if db_data.get('port_security') is None: + response_data[psec.PORTSECURITY] = psec.DEFAULT_PORT_SECURITY + else: + response_data[psec.PORTSECURITY] = ( + db_data['port_security'][psec.PORTSECURITY]) + def _process_network_port_security_create( self, context, network_req, network_res): with context.session.begin(subtransactions=True): @@ -81,52 +88,58 @@ query = self._model_query(context, NetworkSecurityBinding) binding = query.filter( NetworkSecurityBinding.network_id == network_id).one() + return binding.port_security_enabled except exc.NoResultFound: - raise psec.PortSecurityBindingNotFound() - return binding.port_security_enabled + # NOTE(ihrachys) the resource may have been created before port + # security extension was enabled; return default value + return psec.DEFAULT_PORT_SECURITY def _get_port_security_binding(self, context, port_id): try: query = self._model_query(context, PortSecurityBinding) binding = query.filter( PortSecurityBinding.port_id == port_id).one() + return binding.port_security_enabled except exc.NoResultFound: - raise psec.PortSecurityBindingNotFound() - return binding.port_security_enabled + # NOTE(ihrachys) the resource may have been created before port + # security extension was enabled; return default value + return psec.DEFAULT_PORT_SECURITY def _process_port_port_security_update( self, context, port_req, port_res): - if psec.PORTSECURITY in port_req: - port_security_enabled = port_req[psec.PORTSECURITY] - else: + if psec.PORTSECURITY not in port_req: return + port_security_enabled = port_req[psec.PORTSECURITY] try: query = self._model_query(context, PortSecurityBinding) port_id = port_res['id'] binding = query.filter( PortSecurityBinding.port_id == port_id).one() - binding.port_security_enabled = port_security_enabled port_res[psec.PORTSECURITY] = port_security_enabled except exc.NoResultFound: - raise psec.PortSecurityBindingNotFound() + # NOTE(ihrachys) the resource may have been created before port + # security extension was enabled; create the binding model + self._process_port_port_security_create( + context, port_req, port_res) def _process_network_port_security_update( self, context, network_req, network_res): - if psec.PORTSECURITY in network_req: - port_security_enabled = network_req[psec.PORTSECURITY] - else: + if psec.PORTSECURITY not in network_req: return + port_security_enabled = network_req[psec.PORTSECURITY] try: query = self._model_query(context, NetworkSecurityBinding) network_id = network_res['id'] binding = query.filter( NetworkSecurityBinding.network_id == network_id).one() - binding.port_security_enabled = port_security_enabled network_res[psec.PORTSECURITY] = port_security_enabled except exc.NoResultFound: - raise psec.PortSecurityBindingNotFound() + # NOTE(ihrachys) the resource may have been created before port + # security extension was enabled; create the binding model + self._process_network_port_security_create( + context, network_req, network_res) def _make_network_port_security_dict(self, port_security, fields=None): res = {'network_id': port_security['network_id'], diff -Nru neutron-7.0.4/neutron/db/portsecurity_db.py neutron-7.1.1/neutron/db/portsecurity_db.py --- neutron-7.0.4/neutron/db/portsecurity_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/portsecurity_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -29,8 +29,8 @@ def _extend_port_security_dict(self, response_data, db_data): if ('port-security' in getattr(self, 'supported_extension_aliases', [])): - psec_value = db_data['port_security'][psec.PORTSECURITY] - response_data[psec.PORTSECURITY] = psec_value + super(PortSecurityDbMixin, self)._extend_port_security_dict( + response_data, db_data) def _determine_port_security_and_has_ip(self, context, port): """Returns a tuple of booleans (port_security_enabled, has_ip). diff -Nru neutron-7.0.4/neutron/db/rbac_db_mixin.py neutron-7.1.1/neutron/db/rbac_db_mixin.py --- neutron-7.0.4/neutron/db/rbac_db_mixin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/rbac_db_mixin.py 2016-06-10 01:43:08.000000000 +0000 @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_db import exception as db_exc from sqlalchemy.orm import exc from neutron.callbacks import events @@ -43,12 +44,15 @@ raise n_exc.InvalidInput(error_message=e) dbmodel = models.get_type_model_map()[e['object_type']] tenant_id = self._get_tenant_id_for_create(context, e) - with context.session.begin(subtransactions=True): - db_entry = dbmodel(object_id=e['object_id'], - target_tenant=e['target_tenant'], - action=e['action'], - tenant_id=tenant_id) - context.session.add(db_entry) + try: + with context.session.begin(subtransactions=True): + db_entry = dbmodel(object_id=e['object_id'], + target_tenant=e['target_tenant'], + action=e['action'], + tenant_id=tenant_id) + context.session.add(db_entry) + except db_exc.DBDuplicateEntry: + raise ext_rbac.DuplicateRbacPolicy() return self._make_rbac_policy_dict(db_entry) def _make_rbac_policy_dict(self, db_entry, fields=None): diff -Nru neutron-7.0.4/neutron/db/securitygroups_db.py neutron-7.1.1/neutron/db/securitygroups_db.py --- neutron-7.0.4/neutron/db/securitygroups_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/db/securitygroups_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -37,11 +37,6 @@ LOG = logging.getLogger(__name__) -IP_PROTOCOL_MAP = {constants.PROTO_NAME_TCP: constants.PROTO_NUM_TCP, - constants.PROTO_NAME_UDP: constants.PROTO_NUM_UDP, - constants.PROTO_NAME_ICMP: constants.PROTO_NUM_ICMP, - constants.PROTO_NAME_ICMP_V6: constants.PROTO_NUM_ICMP_V6} - class SecurityGroup(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): """Represents a v2 neutron security group.""" @@ -418,7 +413,18 @@ # problems with comparing int and string in PostgreSQL. Here this # string is converted to int to give an opportunity to use it as # before. - return int(IP_PROTOCOL_MAP.get(protocol, protocol)) + return int(constants.IP_PROTOCOL_MAP.get(protocol, protocol)) + + def _get_ip_proto_name_and_num(self, protocol): + if protocol is None: + return + protocol = str(protocol) + if protocol in constants.IP_PROTOCOL_MAP: + return [protocol, str(constants.IP_PROTOCOL_MAP.get(protocol))] + elif protocol in constants.IP_PROTOCOL_NUM_TO_NAME_MAP: + return [constants.IP_PROTOCOL_NUM_TO_NAME_MAP.get(protocol), + protocol] + return [protocol, protocol] def _validate_port_range(self, rule): """Check that port_range is valid.""" @@ -525,6 +531,10 @@ value = sgr.get(key) if value: res[key] = [value] + # protocol field will get corresponding name and number + value = sgr.get('protocol') + if value: + res['protocol'] = self._get_ip_proto_name_and_num(value) return res def _check_for_duplicate_rules(self, context, security_group_rules): @@ -552,11 +562,17 @@ # is changed which cannot be because other methods are already # relying on this behavior. Therefore, we do the filtering # below to check for these corner cases. + rule_dict = security_group_rule['security_group_rule'].copy() + sg_protocol = rule_dict.pop('protocol', None) for db_rule in db_rules: - # need to remove id from db_rule for matching - id = db_rule.pop('id') - if (security_group_rule['security_group_rule'] == db_rule): - raise ext_sg.SecurityGroupRuleExists(id=id) + rule_id = db_rule.pop('id', None) + # remove protocol and match separately for number and type + db_protocol = db_rule.pop('protocol', None) + is_protocol_matching = ( + self._get_ip_proto_name_and_num(db_protocol) == + self._get_ip_proto_name_and_num(sg_protocol)) + if (is_protocol_matching and rule_dict == db_rule): + raise ext_sg.SecurityGroupRuleExists(id=rule_id) def _validate_ip_prefix(self, rule): """Check that a valid cidr was specified as remote_ip_prefix diff -Nru neutron-7.0.4/neutron/extensions/allowedaddresspairs.py neutron-7.1.1/neutron/extensions/allowedaddresspairs.py --- neutron-7.0.4/neutron/extensions/allowedaddresspairs.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/extensions/allowedaddresspairs.py 2016-06-10 01:43:08.000000000 +0000 @@ -96,6 +96,7 @@ EXTENDED_ATTRIBUTES_2_0 = { 'ports': { ADDRESS_PAIRS: {'allow_post': True, 'allow_put': True, + 'convert_to': attr.convert_none_to_empty_list, 'convert_list_to': attr.convert_kvp_list_to_dict, 'validate': {'type:validate_allowed_address_pairs': diff -Nru neutron-7.0.4/neutron/extensions/portsecurity.py neutron-7.1.1/neutron/extensions/portsecurity.py --- neutron-7.0.4/neutron/extensions/portsecurity.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/extensions/portsecurity.py 2016-06-10 01:43:08.000000000 +0000 @@ -17,6 +17,9 @@ from neutron.common import exceptions as nexception +DEFAULT_PORT_SECURITY = True + + class PortSecurityPortHasSecurityGroup(nexception.InUse): message = _("Port has security group associated. Cannot disable port " "security or ip address until security group is removed") @@ -27,16 +30,13 @@ " address in order to use security groups.") -class PortSecurityBindingNotFound(nexception.InvalidExtensionEnv): - message = _("Port does not have port security binding.") - PORTSECURITY = 'port_security_enabled' EXTENDED_ATTRIBUTES_2_0 = { 'networks': { PORTSECURITY: {'allow_post': True, 'allow_put': True, 'convert_to': attributes.convert_to_boolean, 'enforce_policy': True, - 'default': True, + 'default': DEFAULT_PORT_SECURITY, 'is_visible': True}, }, 'ports': { diff -Nru neutron-7.0.4/neutron/extensions/rbac.py neutron-7.1.1/neutron/extensions/rbac.py --- neutron-7.0.4/neutron/extensions/rbac.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/extensions/rbac.py 2016-06-10 01:43:08.000000000 +0000 @@ -32,6 +32,10 @@ "because other objects depend on it.\nDetails: %(details)s") +class DuplicateRbacPolicy(n_exc.Conflict): + message = _("An RBAC policy already exists with those values.") + + def convert_valid_object_type(otype): normalized = otype.strip().lower() if normalized in rbac_db_models.get_type_model_map(): diff -Nru neutron-7.0.4/neutron/extensions/securitygroup.py neutron-7.1.1/neutron/extensions/securitygroup.py --- neutron-7.0.4/neutron/extensions/securitygroup.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/extensions/securitygroup.py 2016-06-10 01:43:08.000000000 +0000 @@ -133,6 +133,11 @@ message = _("Error %(reason)s while attempting the operation.") +class SecurityGroupRuleInvalidEtherType(nexception.InvalidInput): + message = _("Security group rule for ethertype '%(ethertype)s' not " + "supported. Allowed values are %(values)s.") + + def convert_protocol(value): if value is None: return @@ -160,6 +165,8 @@ for ethertype in sg_supported_ethertypes: if ethertype.lower() == value.lower(): return ethertype + raise SecurityGroupRuleInvalidEtherType( + ethertype=value, values=sg_supported_ethertypes) def convert_validate_port_value(port): diff -Nru neutron-7.0.4/neutron/ipam/drivers/neutrondb_ipam/driver.py neutron-7.1.1/neutron/ipam/drivers/neutrondb_ipam/driver.py --- neutron-7.0.4/neutron/ipam/drivers/neutrondb_ipam/driver.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/ipam/drivers/neutrondb_ipam/driver.py 2016-06-10 01:43:08.000000000 +0000 @@ -19,7 +19,6 @@ from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils -from neutron.db import api as db_api from neutron.i18n import _LE from neutron.ipam import driver as ipam_base from neutron.ipam.drivers.neutrondb_ipam import db_api as ipam_db_api @@ -356,7 +355,7 @@ # Pools have already been validated in the subnet request object which # was sent to the subnet pool driver. Further validation should not be # required. - session = db_api.get_session() + session = self._context.session self.subnet_manager.delete_allocation_pools(session) self.create_allocation_pools(self.subnet_manager, session, pools, cidr) self._pools = pools diff -Nru neutron-7.0.4/neutron/ipam/exceptions.py neutron-7.1.1/neutron/ipam/exceptions.py --- neutron-7.0.4/neutron/ipam/exceptions.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/ipam/exceptions.py 2016-06-10 01:43:08.000000000 +0000 @@ -60,3 +60,9 @@ class IpAddressGenerationFailure(exceptions.Conflict): message = _("No more IP addresses available for subnet %(subnet_id)s.") + + +class IpamValueInvalid(exceptions.Conflict): + def __init__(self, message=None): + self.message = message + super(IpamValueInvalid, self).__init__() diff -Nru neutron-7.0.4/neutron/ipam/requests.py neutron-7.1.1/neutron/ipam/requests.py --- neutron-7.0.4/neutron/ipam/requests.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/ipam/requests.py 2016-06-10 01:43:08.000000000 +0000 @@ -108,14 +108,18 @@ if (gw_ip.version == 4 or (gw_ip.version == 6 and not gw_ip.is_link_local())): if self.gateway_ip not in subnet_cidr: - raise ValueError("gateway_ip is not in the subnet") + raise ipam_exc.IpamValueInvalid(_( + "gateway_ip %s is not in the subnet") % + self.gateway_ip) if self.allocation_pools: if subnet_cidr.version != self.allocation_pools[0].version: - raise ValueError("allocation_pools use the wrong ip version") + raise ipam_exc.IpamValueInvalid(_( + "allocation_pools use the wrong ip version")) for pool in self.allocation_pools: if pool not in subnet_cidr: - raise ValueError("allocation_pools are not in the subnet") + raise ipam_exc.IpamValueInvalid(_( + "allocation_pools are not in the subnet")) class AnySubnetRequest(SubnetRequest): diff -Nru neutron-7.0.4/neutron/plugins/midonet/plugin.py neutron-7.1.1/neutron/plugins/midonet/plugin.py --- neutron-7.0.4/neutron/plugins/midonet/plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/midonet/plugin.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -# Copyright (C) 2012 Midokura Japan K.K. -# Copyright (C) 2013 Midokura PTE LTD -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -from midonet.neutron import plugin - -midonet_opts = [ - cfg.StrOpt('midonet_uri', default='http://localhost:8080/midonet-api', - help=_('MidoNet API server URI.')), - cfg.StrOpt('username', default='admin', - help=_('MidoNet admin username.')), - cfg.StrOpt('password', default='passw0rd', - secret=True, - help=_('MidoNet admin password.')), - cfg.StrOpt('project_id', - default='77777777-7777-7777-7777-777777777777', - help=_('ID of the project that MidoNet admin user ' - 'belongs to.')) -] - - -cfg.CONF.register_opts(midonet_opts, "MIDONET") - - -# Derives from `object` (via at least NeutronDbPluginV2), but pylint -# can't see that without having the midonet libraries available. -# pylint: disable=super-on-old-class -class MidonetPluginV2(plugin.MidonetMixin): - - vendor_extensions = plugin.MidonetMixin.supported_extension_aliases - supported_extension_aliases = ['external-net', 'router', 'security-group', - 'agent', 'dhcp_agent_scheduler', 'binding', - 'quotas'] + vendor_extensions - - __native_bulk_support = True - - def __init__(self): - super(MidonetPluginV2, self).__init__() diff -Nru neutron-7.0.4/neutron/plugins/midonet/requirements.txt neutron-7.1.1/neutron/plugins/midonet/requirements.txt --- neutron-7.0.4/neutron/plugins/midonet/requirements.txt 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/midonet/requirements.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -networking-midonet diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/l2pop/mech_driver.py neutron-7.1.1/neutron/plugins/ml2/drivers/l2pop/mech_driver.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/l2pop/mech_driver.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/l2pop/mech_driver.py 2016-06-10 01:43:08.000000000 +0000 @@ -39,7 +39,6 @@ def initialize(self): LOG.debug("Experimental L2 population driver") self.rpc_ctx = n_context.get_admin_context_without_session() - self.migrated_ports = {} def _get_port_fdb_entries(self, port): return [l2pop_rpc.PortInfo(mac_address=port['mac_address'], @@ -126,13 +125,14 @@ self.L2populationAgentNotify.remove_fdb_entries( self.rpc_ctx, fdb_entries) elif (context.host != context.original_host - and context.status == const.PORT_STATUS_ACTIVE - and not self.migrated_ports.get(orig['id'])): - # The port has been migrated. We have to store the original - # binding to send appropriate fdb once the port will be set - # on the destination host - self.migrated_ports[orig['id']] = ( - (orig, context.original_host)) + and context.original_status == const.PORT_STATUS_ACTIVE + and context.status == const.PORT_STATUS_DOWN): + # The port has been migrated. Send notification about port + # removal from old host. + fdb_entries = self._get_agent_fdb( + context, orig, context.original_host) + self.L2populationAgentNotify.remove_fdb_entries( + self.rpc_ctx, fdb_entries) elif context.status != context.original_status: if context.status == const.PORT_STATUS_ACTIVE: self._update_port_up(context) @@ -141,16 +141,6 @@ context, port, context.host) self.L2populationAgentNotify.remove_fdb_entries( self.rpc_ctx, fdb_entries) - elif context.status == const.PORT_STATUS_BUILD: - orig = self.migrated_ports.pop(port['id'], None) - if orig: - original_port = orig[0] - original_host = orig[1] - # this port has been migrated: remove its entries from fdb - fdb_entries = self._get_agent_fdb( - context, original_port, original_host) - self.L2populationAgentNotify.remove_fdb_entries( - self.rpc_ctx, fdb_entries) def _get_port_infos(self, context, port, agent_host): if not agent_host: @@ -164,8 +154,12 @@ return agent_ip = self.get_agent_ip(agent) - - segment = context.bottom_bound_segment + # If a port has migrated, when we send remove_fdb_entries to original + # host, we should use context.original_bottom_bound_segment + if context.host == agent_host: + segment = context.bottom_bound_segment + else: + segment = context.original_bottom_bound_segment if not segment: LOG.debug("Port %(port)s updated by agent %(agent)s isn't bound " "to any segment", {'port': port['id'], 'agent': agent}) diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py neutron-7.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py 2016-06-10 01:43:08.000000000 +0000 @@ -23,6 +23,7 @@ LOG = logging.getLogger(__name__) SPOOF_CHAIN_PREFIX = 'neutronARP-' +MAC_CHAIN_PREFIX = 'neutronMAC-' def setup_arp_spoofing_protection(vif, port_details): @@ -39,6 +40,7 @@ LOG.debug("Skipping ARP spoofing rules for network owned port " "'%s'.", vif) return + _install_mac_spoofing_protection(vif, port_details, current_rules) # collect all of the addresses and cidrs that belong to the port addresses = {f['ip_address'] for f in port_details['fixed_ips']} if port_details.get('allowed_address_pairs'): @@ -72,6 +74,7 @@ for vif in vifs: if chain_exists(chain_name(vif), current_rules): ebtables(['-X', chain_name(vif)]) + _delete_mac_spoofing_protection(vifs, current_rules) def delete_unreferenced_arp_protection(current_vifs): @@ -126,6 +129,62 @@ return False +@lockutils.synchronized('ebtables') +def _install_mac_spoofing_protection(vif, port_details, current_rules): + mac_addresses = {port_details['mac_address']} + if port_details.get('allowed_address_pairs'): + mac_addresses |= {p['mac_address'] + for p in port_details['allowed_address_pairs']} + mac_addresses = list(mac_addresses) + vif_chain = _mac_chain_name(vif) + # mac filter chain for each vif which has a default deny + if not chain_exists(vif_chain, current_rules): + ebtables(['-N', vif_chain, '-P', 'DROP']) + # check if jump rule already exists, if not, install it + if not _mac_vif_jump_present(vif, current_rules): + ebtables(['-A', 'FORWARD', '-i', vif, '-j', vif_chain]) + # we can't just feed all allowed macs at once because we can exceed + # the maximum argument size. limit to 500 per rule. + for chunk in (mac_addresses[i:i + 500] + for i in range(0, len(mac_addresses), 500)): + new_rule = ['-A', vif_chain, '-i', vif, + '--among-src', ','.join(chunk), '-j', 'RETURN'] + ebtables(new_rule) + _delete_vif_mac_rules(vif, current_rules) + + +def _mac_vif_jump_present(vif, current_rules): + searches = (('-i %s' % vif), ('-j %s' % _mac_chain_name(vif))) + for line in current_rules: + if all(s in line for s in searches): + return True + return False + + +def _mac_chain_name(vif): + return '%s%s' % (MAC_CHAIN_PREFIX, vif) + + +def _delete_vif_mac_rules(vif, current_rules): + chain = _mac_chain_name(vif) + for rule in current_rules: + if '-i %s' % vif in rule and '--among-src' in rule: + ebtables(['-D', chain] + rule.split()) + + +def _delete_mac_spoofing_protection(vifs, current_rules): + # delete the jump rule and then delete the whole chain + jumps = [vif for vif in vifs + if _mac_vif_jump_present(vif, current_rules)] + for vif in jumps: + ebtables(['-D', 'FORWARD', '-i', vif, '-j', + _mac_chain_name(vif)]) + for vif in vifs: + chain = _mac_chain_name(vif) + if chain_exists(chain, current_rules): + ebtables(['-X', chain]) + + # Used to scope ebtables commands in testing NAMESPACE = None diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py neutron-7.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py 2016-06-10 01:43:08.000000000 +0000 @@ -41,6 +41,12 @@ help=_("Extension to use alongside ml2 plugin's l2population " "mechanism driver. It enables the plugin to populate " "VXLAN forwarding table.")), + cfg.BoolOpt('arp_responder', default=True, + help=_("Enable local ARP responder which provides local " + "responses instead of performing ARP broadcast into " + "the overlay. Enabling local ARP responder is not fully" + "compatible with the allowed-address-pairs extension.") + ), ] bridge_opts = [ diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py neutron-7.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -293,14 +293,19 @@ "%(physical_interface)s", {'interface': interface, 'vlan_id': vlan_id, 'physical_interface': physical_interface}) - if utils.execute(['ip', 'link', 'add', 'link', - physical_interface, - 'name', interface, 'type', 'vlan', 'id', - vlan_id], run_as_root=True): - return - if utils.execute(['ip', 'link', 'set', - interface, 'up'], run_as_root=True): - return + try: + int_vlan = self.ip.add_vlan(interface, physical_interface, + vlan_id) + except RuntimeError: + with excutils.save_and_reraise_exception() as ctxt: + if ip_lib.vlan_in_use(vlan_id): + ctxt.reraise = False + LOG.error(_LE("Unable to create VLAN interface for " + "VLAN ID %s because it is in use by " + "another interface."), vlan_id) + return + int_vlan.disable_ipv6() + int_vlan.link.set_up() LOG.debug("Done creating subinterface %s", interface) return interface @@ -320,7 +325,7 @@ if cfg.CONF.VXLAN.tos: args['tos'] = cfg.CONF.VXLAN.tos if cfg.CONF.VXLAN.l2_population: - args['proxy'] = True + args['proxy'] = cfg.CONF.VXLAN.arp_responder try: int_vxlan = self.ip.add_vxlan(interface, segmentation_id, **args) @@ -334,6 +339,7 @@ "VNI %s because it is in use by another " "interface."), segmentation_id) return None + int_vxlan.disable_ipv6() int_vxlan.link.set_up() LOG.debug("Done creating vxlan interface %s", interface) return interface @@ -459,12 +465,12 @@ network_id: network_id}) def add_tap_interface(self, network_id, network_type, physical_network, - segmentation_id, tap_device_name): + segmentation_id, tap_device_name, device_owner): """Add tap interface and handle interface missing exeptions.""" try: return self._add_tap_interface(network_id, network_type, physical_network, segmentation_id, - tap_device_name) + tap_device_name, device_owner) except Exception: with excutils.save_and_reraise_exception() as ctx: if not ip_lib.device_exists(tap_device_name): @@ -475,7 +481,7 @@ return False def _add_tap_interface(self, network_id, network_type, physical_network, - segmentation_id, tap_device_name): + segmentation_id, tap_device_name, device_owner): """Add tap interface. If a VIF has been plugged into a network, this function will @@ -501,20 +507,24 @@ return False self.ensure_tap_mtu(tap_device_name, phy_dev_name) - # Check if device needs to be added to bridge - tap_device_in_bridge = self.get_bridge_for_tap_device(tap_device_name) - if not tap_device_in_bridge: - data = {'tap_device_name': tap_device_name, - 'bridge_name': bridge_name} - LOG.debug("Adding device %(tap_device_name)s to bridge " - "%(bridge_name)s", data) - if bridge_lib.BridgeDevice(bridge_name).addif(tap_device_name): - return False + # Avoid messing with plugging devices into a bridge that the agent + # does not own + if device_owner.startswith(constants.DEVICE_OWNER_PREFIXES): + # Check if device needs to be added to bridge + if not self.get_bridge_for_tap_device(tap_device_name): + data = {'tap_device_name': tap_device_name, + 'bridge_name': bridge_name} + LOG.debug("Adding device %(tap_device_name)s to bridge " + "%(bridge_name)s", data) + if bridge_lib.BridgeDevice(bridge_name).addif(tap_device_name): + return False else: data = {'tap_device_name': tap_device_name, + 'device_owner': device_owner, 'bridge_name': bridge_name} - LOG.debug("%(tap_device_name)s already exists on bridge " - "%(bridge_name)s", data) + LOG.debug("Skip adding device %(tap_device_name)s to " + "%(bridge_name)s. It is owned by %(device_owner)s and " + "thus added elsewhere.", data) return True def ensure_tap_mtu(self, tap_dev_name, phy_dev_name): @@ -523,14 +533,14 @@ ip_lib.IPDevice(tap_dev_name).link.set_mtu(phy_dev_mtu) def add_interface(self, network_id, network_type, physical_network, - segmentation_id, port_id): + segmentation_id, port_id, device_owner): self.network_map[network_id] = NetworkSegment(network_type, physical_network, segmentation_id) tap_device_name = self.get_tap_device_name(port_id) return self.add_tap_interface(network_id, network_type, physical_network, segmentation_id, - tap_device_name) + tap_device_name, device_owner) def delete_bridge(self, bridge_name): if ip_lib.device_exists(bridge_name): @@ -609,6 +619,9 @@ device.link.delete() LOG.debug("Done deleting interface %s", interface) + def get_devices_modified_timestamps(self, devices): + return {d: bridge_lib.get_interface_bridged_time(d) for d in devices} + def get_tap_devices(self): devices = set() for device in os.listdir(BRIDGE_FS): @@ -973,12 +986,14 @@ def setup_linux_bridge(self, bridge_mappings, interface_mappings): self.br_mgr = LinuxBridgeManager(bridge_mappings, interface_mappings) - def remove_port_binding(self, network_id, physical_network, interface_id): - bridge_name = self.br_mgr.get_existing_bridge_name(physical_network) - if not bridge_name: - bridge_name = self.br_mgr.get_bridge_name(network_id) - tap_device_name = self.br_mgr.get_tap_device_name(interface_id) - return self.br_mgr.remove_interface(bridge_name, tap_device_name) + def _ensure_port_admin_state(self, port_id, admin_state_up): + LOG.debug("Setting admin_state_up to %s for port %s", + admin_state_up, port_id) + tap_name = self.br_mgr.get_tap_device_name(port_id) + if admin_state_up: + ip_lib.IPDevice(tap_name).link.set_up() + else: + ip_lib.IPDevice(tap_name).link.set_down() def process_network_devices(self, device_info): resync_a = False @@ -1022,18 +1037,58 @@ device_details['port_id']) arp_protect.setup_arp_spoofing_protection(port, device_details) + # create the networking for the port + network_type = device_details.get('network_type') + segmentation_id = device_details.get('segmentation_id') + tap_in_bridge = self.br_mgr.add_interface( + device_details['network_id'], network_type, + device_details['physical_network'], segmentation_id, + device_details['port_id'], device_details['device_owner']) + # REVISIT(scheuran): Changed the way how ports admin_state_up + # is implemented. + # + # Old lb implementation: + # - admin_state_up: ensure that tap is plugged into bridge + # - admin_state_down: remove tap from bridge + # New lb implementation: + # - admin_state_up: set tap device state to up + # - admin_state_down: set tap device stae to down + # + # However both approaches could result in races with + # nova/libvirt and therefore to an invalid system state in the + # scenario, where an instance is booted with a port configured + # with admin_state_up = False: + # + # Libvirt does the following actions in exactly + # this order (see libvirt virnetdevtap.c) + # 1) Create the tap device, set its MAC and MTU + # 2) Plug the tap into the bridge + # 3) Set the tap online + # + # Old lb implementation: + # A race could occur, if the lb agent removes the tap device + # right after step 1). Then libvirt will add it to the bridge + # again in step 2). + # New lb implementation: + # The race could occur if the lb-agent sets the taps device + # state to down right after step 2). In step 3) libvirt + # might set it to up again. + # + # This is not an issue if an instance is booted with a port + # configured with admin_state_up = True. Libvirt would just + # set the tap device up again. + # + # This refactoring is recommended for the following reasons: + # 1) An existing race with libvirt caused by the behavior of + # the old implementation. See Bug #1312016 + # 2) The new code is much more readable + if tap_in_bridge: + self._ensure_port_admin_state( + device_details['port_id'], + device_details['admin_state_up']) + # update plugin about port status if admin_state is up if device_details['admin_state_up']: - # create the networking for the port - network_type = device_details.get('network_type') - segmentation_id = device_details.get('segmentation_id') - if self.br_mgr.add_interface( - device_details['network_id'], - network_type, - device_details['physical_network'], - segmentation_id, - device_details['port_id']): - - # update plugin about port status + if tap_in_bridge: self.plugin_rpc.update_device_up(self.context, device, self.agent_id, @@ -1043,11 +1098,6 @@ device, self.agent_id, cfg.CONF.host) - else: - physical_network = device_details['physical_network'] - self.remove_port_binding(device_details['network_id'], - physical_network, - device_details['port_id']) else: LOG.info(_LI("Device %s not defined on plugin"), device) return False @@ -1075,6 +1125,17 @@ arp_protect.delete_arp_spoofing_protection(devices) return resync + @staticmethod + def _get_devices_locally_modified(timestamps, previous_timestamps): + """Returns devices with previous timestamps that do not match new. + + If a device did not have a timestamp previously, it will not be + returned because this means it is new. + """ + return {device for device, timestamp in timestamps.items() + if previous_timestamps.get(device) and + timestamp != previous_timestamps.get(device)} + def scan_devices(self, previous, sync): device_info = {} @@ -1092,12 +1153,26 @@ previous = {'added': set(), 'current': set(), 'updated': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} # clear any orphaned ARP spoofing rules (e.g. interface was # manually deleted) if self.prevent_arp_spoofing: arp_protect.delete_unreferenced_arp_protection(current_devices) + # check to see if any devices were locally modified based on their + # timestamps changing since the previous iteration. If a timestamp + # doesn't exist for a device, this calculation is skipped for that + # device. + device_info['timestamps'] = \ + self.br_mgr.get_devices_modified_timestamps(current_devices) + locally_updated = self._get_devices_locally_modified( + device_info['timestamps'], previous['timestamps']) + if locally_updated: + LOG.debug("Adding locally changed devices to updated set: %s", + locally_updated) + updated_devices |= locally_updated + if sync: # This is the first iteration, or the previous one had a problem. # Re-add all existing devices. diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py neutron-7.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -14,6 +14,7 @@ # limitations under the License. +import collections import socket import sys import time @@ -25,6 +26,7 @@ from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall +import six from neutron.agent.l2.extensions import manager as ext_manager from neutron.agent import rpc as agent_rpc @@ -48,8 +50,13 @@ # Set RPC API version to 1.0 by default. # history - # 1.1 Support Security Group RPC - target = oslo_messaging.Target(version='1.1') + # 1.1 Support Security Group RPC (works with NoopFirewallDriver) + # 1.2 Support DVR (Distributed Virtual Router) RPC (not supported) + # 1.3 Added param devices_to_update to security_groups_provider_updated + # (works with NoopFirewallDriver) + # 1.4 Added support for network_update + + target = oslo_messaging.Target(version='1.4') def __init__(self, context, agent, sg_agent): super(SriovNicSwitchRpcCallbacks, self).__init__() @@ -79,12 +86,22 @@ "skipping", {'id': port['id'], 'mac': mac, 'pci_slot': pci_slot}) + def network_update(self, context, **kwargs): + network_id = kwargs['network']['id'] + LOG.debug("network_update message received for network " + "%(network_id)s, with ports: %(ports)s", + {'network_id': network_id, + 'ports': self.agent.network_ports[network_id]}) + for port_data in self.agent.network_ports[network_id]: + self.agent.updated_devices.add(port_data['device']) + class SriovNicSwitchAgent(object): def __init__(self, physical_devices_mappings, exclude_devices, polling_interval): self.polling_interval = polling_interval + self.network_ports = collections.defaultdict(list) self.conf = cfg.CONF self.setup_eswitch_mgr(physical_devices_mappings, exclude_devices) @@ -99,7 +116,6 @@ # Stores port update notifications for processing in the main loop self.updated_devices = set() - self.mac_to_port_id_mapping = {} self.context = context.get_admin_context_without_session() self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) @@ -127,6 +143,7 @@ # Define the listening consumers for the agent consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE], + [topics.NETWORK, topics.UPDATE], [topics.SECURITY_GROUP, topics.UPDATE]] self.connection = agent_rpc.create_consumers(self.endpoints, self.topic, @@ -164,10 +181,11 @@ device_info = {} device_info['current'] = curr_devices device_info['added'] = curr_devices - registered_devices - # we don't want to process updates for devices that don't exist - device_info['updated'] = updated_devices & curr_devices # we need to clean up after devices are removed device_info['removed'] = registered_devices - curr_devices + # we don't want to process updates for devices that don't exist + device_info['updated'] = (updated_devices & curr_devices - + device_info['removed']) return device_info def _device_info_has_changes(self, device_info): @@ -230,6 +248,21 @@ else: LOG.info(_LI("No device with MAC %s defined on agent."), device) + def _update_network_ports(self, network_id, port_id, mac_pci_slot): + self._clean_network_ports(mac_pci_slot) + self.network_ports[network_id].append({ + "port_id": port_id, + "device": mac_pci_slot}) + + def _clean_network_ports(self, mac_pci_slot): + for netid, ports_list in six.iteritems(self.network_ports): + for port_data in ports_list: + if mac_pci_slot == port_data['device']: + ports_list.remove(port_data) + if ports_list == []: + self.network_ports.pop(netid) + return port_data['port_id'] + def treat_devices_added_updated(self, devices_info): try: macs_list = set([device_info[0] for device_info in devices_info]) @@ -250,13 +283,15 @@ LOG.info(_LI("Port %(device)s updated. Details: %(details)s"), {'device': device, 'details': device_details}) port_id = device_details['port_id'] - self.mac_to_port_id_mapping[device] = port_id profile = device_details['profile'] spoofcheck = device_details.get('port_security_enabled', True) self.treat_device(device, profile.get('pci_slot'), device_details['admin_state_up'], spoofcheck) + self._update_network_ports(device_details['network_id'], + port_id, + (device, profile.get('pci_slot'))) self.ext_manager.handle_port(self.context, device_details) else: LOG.info(_LI("Device with MAC %s not defined on plugin"), @@ -271,14 +306,12 @@ "PCI slot %(pci_slot)s"), {'mac': mac, 'pci_slot': pci_slot}) try: - port_id = self.mac_to_port_id_mapping.get(mac) + port_id = self._clean_network_ports(device) if port_id: - profile = {'pci_slot': pci_slot} port = {'port_id': port_id, 'device': mac, - 'profile': profile} + 'profile': {'pci_slot': pci_slot}} self.ext_manager.delete_port(self.context, port) - del self.mac_to_port_id_mapping[mac] else: LOG.warning(_LW("port_id to device with MAC " "%s not found"), mac) diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py 2016-06-10 01:43:08.000000000 +0000 @@ -66,6 +66,9 @@ # Table for ARP poison/spoofing prevention rules ARP_SPOOF_TABLE = 24 +# Table for MAC spoof filtering +MAC_SPOOF_TABLE = 25 + # type for ARP reply in ARP header ARP_REPLY = '0x2' diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py 2016-06-10 01:43:08.000000000 +0000 @@ -19,6 +19,8 @@ ** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic """ +import netaddr + from oslo_log import log as logging from ryu.lib.packet import ether_types from ryu.lib.packet import icmpv6 @@ -176,16 +178,45 @@ match=match, dest_table_id=constants.ARP_SPOOF_TABLE) + def set_allowed_macs_for_port(self, port, mac_addresses=None, + allow_all=False): + if allow_all: + self.delete_flows(table_id=constants.LOCAL_SWITCHING, in_port=port) + self.delete_flows(table_id=constants.MAC_SPOOF_TABLE, in_port=port) + return + mac_addresses = mac_addresses or [] + for address in mac_addresses: + self.install_normal( + table_id=constants.MAC_SPOOF_TABLE, priority=2, + eth_src=address, in_port=port) + # normalize so we can see if macs are the same + mac_addresses = {netaddr.EUI(mac) for mac in mac_addresses} + flows = self.dump_flows(constants.MAC_SPOOF_TABLE) + for flow in flows: + matches = dict(flow.match.items()) + if matches.get('in_port') != port: + continue + if not matches.get('eth_src'): + continue + flow_mac = matches['eth_src'] + if netaddr.EUI(flow_mac) not in mac_addresses: + self.delete_flows(table_id=constants.MAC_SPOOF_TABLE, + in_port=port, eth_src=flow_mac) + self.install_goto(table_id=constants.LOCAL_SWITCHING, + priority=9, in_port=port, + dest_table_id=constants.MAC_SPOOF_TABLE) + def install_arp_spoofing_protection(self, port, ip_addresses): # allow ARP replies as long as they match addresses that actually # belong to the port. for ip in ip_addresses: masked_ip = self._cidr_to_ryu(ip) - self.install_normal(table_id=constants.ARP_SPOOF_TABLE, - priority=2, - eth_type=ether_types.ETH_TYPE_ARP, - arp_spa=masked_ip, - in_port=port) + self.install_goto(table_id=constants.ARP_SPOOF_TABLE, + priority=2, + eth_type=ether_types.ETH_TYPE_ARP, + arp_spa=masked_ip, + in_port=port, + dest_table_id=constants.MAC_SPOOF_TABLE) # Now that the rules are ready, direct ARP traffic from the port into # the anti-spoof table. diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py 2016-03-29 17:44:55.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py 2016-06-10 01:43:03.000000000 +0000 @@ -30,7 +30,6 @@ dvr_process_next_table_id = constants.LOCAL_VLAN_TRANSLATION def setup_default_table(self): - self.delete_flows() self.install_normal() @staticmethod diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py 2016-06-10 01:43:08.000000000 +0000 @@ -18,6 +18,9 @@ * references ** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic """ + +import netaddr + from neutron.common import constants as const from neutron.plugins.common import constants as p_const from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants @@ -127,13 +130,39 @@ icmp_type=const.ICMPV6_TYPE_NA, in_port=port, actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE)) + def set_allowed_macs_for_port(self, port, mac_addresses=None, + allow_all=False): + if allow_all: + self.delete_flows(table_id=constants.LOCAL_SWITCHING, in_port=port) + self.delete_flows(table_id=constants.MAC_SPOOF_TABLE, in_port=port) + return + mac_addresses = mac_addresses or [] + for address in mac_addresses: + self.install_normal( + table_id=constants.MAC_SPOOF_TABLE, priority=2, + eth_src=address, in_port=port) + # normalize so we can see if macs are the same + mac_addresses = {netaddr.EUI(mac) for mac in mac_addresses} + flows = self.dump_flows(constants.MAC_SPOOF_TABLE).splitlines() + for flow in flows: + if 'dl_src' not in flow or 'in_port=%s' % port not in flow: + continue + flow_mac = flow.split('dl_src=')[1].split(' ')[0].split(',')[0] + if netaddr.EUI(flow_mac) not in mac_addresses: + self.delete_flows(table_id=constants.MAC_SPOOF_TABLE, + in_port=port, eth_src=flow_mac) + self.add_flow(table=constants.LOCAL_SWITCHING, + priority=9, in_port=port, + actions=("resubmit(,%s)" % constants.MAC_SPOOF_TABLE)) + def install_arp_spoofing_protection(self, port, ip_addresses): # allow ARPs as long as they match addresses that actually # belong to the port. for ip in ip_addresses: - self.install_normal( - table_id=constants.ARP_SPOOF_TABLE, priority=2, - proto='arp', arp_spa=ip, in_port=port) + self.add_flow( + table=constants.ARP_SPOOF_TABLE, priority=2, + proto='arp', arp_spa=ip, in_port=port, + actions=("resubmit(,%s)" % constants.MAC_SPOOF_TABLE)) # Now that the rules are ready, direct ARP traffic from the port into # the anti-spoof table. diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_phys.py neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_phys.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_phys.py 2016-03-29 17:44:55.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_phys.py 2016-06-10 01:43:03.000000000 +0000 @@ -30,7 +30,6 @@ dvr_process_next_table_id = constants.LOCAL_VLAN_TRANSLATION def setup_default_table(self): - self.delete_flows() self.install_normal() def provision_local_vlan(self, port, lvid, segmentation_id, distributed): diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -563,6 +563,12 @@ local_vlan_map.network_type)) return + if (port.vif_id in self.local_ports and + self.local_ports[port.vif_id].ofport != port.ofport): + LOG.info(_LI("DVR: Port %(vif)s changed port number to " + "%(ofport)s, rebinding."), + {'vif': port.vif_id, 'ofport': port.ofport}) + self.unbind_port_from_dvr(port, local_vlan_map) if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE: self._bind_distributed_router_interface_port(port, local_vlan_map, diff -Nru neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py --- neutron-7.0.4/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -822,6 +822,25 @@ self.int_br.set_db_attribute("Port", port.port_name, "other_config", port_other_config) + def _add_port_tag_info(self, need_binding_ports): + port_names = [p['vif_port'].port_name for p in need_binding_ports] + port_info = self.int_br.get_ports_attributes( + "Port", columns=["name", "tag", "other_config"], + ports=port_names, if_exists=True) + info_by_port = {x['name']: [x['tag'], x['other_config']] + for x in port_info} + for port_detail in need_binding_ports: + lvm = self.local_vlan_map.get(port_detail['network_id']) + if not lvm: + continue + port = port_detail['vif_port'] + cur_info = info_by_port.get(port.port_name) + if cur_info is not None and cur_info[0] != lvm.vlan: + other_config = cur_info[1] or {} + other_config['tag'] = lvm.vlan + self.int_br.set_db_attribute( + "Port", port.port_name, "other_config", other_config) + def _bind_devices(self, need_binding_ports): devices_up = [] devices_down = [] @@ -886,11 +905,13 @@ LOG.info(_LI("Skipping ARP spoofing rules for port '%s' because " "it has port security disabled"), vif.port_name) bridge.delete_arp_spoofing_protection(port=vif.ofport) + bridge.set_allowed_macs_for_port(port=vif.ofport, allow_all=True) return if port_details['device_owner'].startswith('network:'): LOG.debug("Skipping ARP spoofing rules for network owned port " "'%s'.", vif.port_name) bridge.delete_arp_spoofing_protection(port=vif.ofport) + bridge.set_allowed_macs_for_port(port=vif.ofport, allow_all=True) return # clear any previous flows related to this port in our ARP table bridge.delete_arp_spoofing_allow_rules(port=vif.ofport) @@ -904,6 +925,7 @@ for p in port_details['allowed_address_pairs'] if p.get('mac_address')} + bridge.set_allowed_macs_for_port(vif.ofport, mac_addresses) ipv6_addresses = {ip for ip in addresses if netaddr.IPNetwork(ip).version == 6} # Allow neighbor advertisements for LLA address. @@ -981,8 +1003,11 @@ self.int_br.set_secure_mode() self.int_br.setup_controllers(self.conf) - self.int_br.delete_port(self.conf.OVS.int_peer_patch_port) if self.conf.AGENT.drop_flows_on_start: + # Delete the patch port between br-int and br-tun if we're deleting + # the flows on br-int, so that traffic doesn't get flooded over + # while flows are missing. + self.int_br.delete_port(self.conf.OVS.int_peer_patch_port) self.int_br.delete_flows() self.int_br.setup_default_table() @@ -1110,7 +1135,10 @@ 'bridge': bridge}) sys.exit(1) br = self.br_phys_cls(bridge) + br.set_agent_uuid_stamp(self.agent_uuid_stamp) br.setup_controllers(self.conf) + if cfg.CONF.AGENT.drop_flows_on_start: + br.delete_flows() br.setup_default_table() self.phys_brs[physical_network] = br @@ -1144,12 +1172,19 @@ if int_type == 'veth': self.int_br.delete_port(int_if_name) br.delete_port(phys_if_name) - # Create patch ports without associating them in order to block - # untranslated traffic before association - int_ofport = self.int_br.add_patch_port( - int_if_name, constants.NONEXISTENT_PEER) - phys_ofport = br.add_patch_port( - phys_if_name, constants.NONEXISTENT_PEER) + + # Setup int_br to physical bridge patches. If they already + # exist we leave them alone, otherwise we create them but don't + # connect them until after the drop rules are in place. + int_ofport = self.int_br.get_port_ofport(int_if_name) + if int_ofport == ovs_lib.INVALID_OFPORT: + int_ofport = self.int_br.add_patch_port( + int_if_name, constants.NONEXISTENT_PEER) + + phys_ofport = br.get_port_ofport(phys_if_name) + if phys_ofport == ovs_lib.INVALID_OFPORT: + phys_ofport = br.add_patch_port( + phys_if_name, constants.NONEXISTENT_PEER) self.int_ofports[physical_network] = int_ofport self.phys_ofports[physical_network] = phys_ofport @@ -1169,9 +1204,9 @@ else: # associate patch ports to pass traffic self.int_br.set_db_attribute('Interface', int_if_name, - 'options:peer', phys_if_name) + 'options', {'peer': phys_if_name}) br.set_db_attribute('Interface', phys_if_name, - 'options:peer', int_if_name) + 'options', {'peer': int_if_name}) def update_stale_ofport_rules(self): # right now the ARP spoofing rules are the only thing that utilizes @@ -1190,6 +1225,7 @@ ofports_deleted = set(previous.values()) - set(current.values()) for ofport in ofports_deleted: self.int_br.delete_arp_spoofing_protection(port=ofport) + self.int_br.set_allowed_macs_for_port(port=ofport, allow_all=True) # store map for next iteration self.vifname_to_ofport_map = current @@ -1529,6 +1565,7 @@ # TODO(salv-orlando): Optimize avoiding applying filters # unnecessarily, (eg: when there are no IP address changes) added_ports = port_info.get('added', set()) + self._add_port_tag_info(need_binding_devices) if security_disabled_ports: added_ports -= set(security_disabled_ports) self.sg_agent.setup_port_filters(added_ports, @@ -1664,6 +1701,7 @@ def cleanup_stale_flows(self): bridges = [self.int_br] + bridges.extend(self.phys_brs.values()) if self.enable_tunneling: bridges.append(self.tun_br) for bridge in bridges: diff -Nru neutron-7.0.4/neutron/plugins/ml2/extensions/port_security.py neutron-7.1.1/neutron/plugins/ml2/extensions/port_security.py --- neutron-7.0.4/neutron/plugins/ml2/extensions/port_security.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/extensions/port_security.py 2016-06-10 01:43:08.000000000 +0000 @@ -40,8 +40,7 @@ def process_create_network(self, context, data, result): # Create the network extension attributes. if psec.PORTSECURITY not in data: - data[psec.PORTSECURITY] = (psec.EXTENDED_ATTRIBUTES_2_0['networks'] - [psec.PORTSECURITY]['default']) + data[psec.PORTSECURITY] = psec.DEFAULT_PORT_SECURITY self._process_network_port_security_create(context, data, result) def process_update_network(self, context, data, result): @@ -65,15 +64,6 @@ def extend_port_dict(self, session, db_data, result): self._extend_port_security_dict(result, db_data) - def _extend_port_security_dict(self, response_data, db_data): - if db_data.get('port_security') is None: - response_data[psec.PORTSECURITY] = ( - psec.EXTENDED_ATTRIBUTES_2_0['networks'] - [psec.PORTSECURITY]['default']) - else: - response_data[psec.PORTSECURITY] = ( - db_data['port_security'][psec.PORTSECURITY]) - def _determine_port_security(self, context, port): """Returns a boolean (port_security_enabled). diff -Nru neutron-7.0.4/neutron/plugins/ml2/models.py neutron-7.1.1/neutron/plugins/ml2/models.py --- neutron-7.0.4/neutron/plugins/ml2/models.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/models.py 2016-06-10 01:43:08.000000000 +0000 @@ -127,5 +127,5 @@ port = orm.relationship( models_v2.Port, backref=orm.backref("dvr_port_binding", - lazy='joined', uselist=False, + lazy='joined', cascade='delete')) diff -Nru neutron-7.0.4/neutron/plugins/ml2/plugin.py neutron-7.1.1/neutron/plugins/ml2/plugin.py --- neutron-7.0.4/neutron/plugins/ml2/plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/plugin.py 2016-06-10 01:43:08.000000000 +0000 @@ -251,6 +251,10 @@ binding.vif_details = '' db.clear_binding_levels(session, port_id, original_host) mech_context._clear_binding_levels() + port['status'] = const.PORT_STATUS_DOWN + super(Ml2Plugin, self).update_port( + mech_context._plugin_context, port_id, + {attributes.PORT: {'status': const.PORT_STATUS_DOWN}}) if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: binding.vif_type = portbindings.VIF_TYPE_UNBOUND @@ -548,10 +552,9 @@ if not segment: # REVISIT(rkukura): This should notify agent to unplug port network = mech_context.network.current - LOG.warning(_LW("In _notify_port_updated(), no bound segment for " - "port %(port_id)s on network %(network_id)s"), - {'port_id': port['id'], - 'network_id': network['id']}) + LOG.debug("In _notify_port_updated(), no bound segment for " + "port %(port_id)s on network %(network_id)s", + {'port_id': port['id'], 'network_id': network['id']}) return self.notifier.port_update(mech_context._plugin_context, port, segment[api.NETWORK_TYPE], @@ -1282,21 +1285,23 @@ binding.vif_type == portbindings.VIF_TYPE_BINDING_FAILED or router_id != device_id) if update_required: - with session.begin(subtransactions=True): - try: - orig_port = super(Ml2Plugin, self).get_port(context, id) - except exc.PortNotFound: - LOG.debug("DVR Port %s has been deleted concurrently", id) - return - if not binding: - binding = db.ensure_dvr_port_binding( - session, id, host, router_id=device_id) - network = self.get_network(context, orig_port['network_id']) - levels = db.get_binding_levels(session, id, host) - mech_context = driver_context.PortContext(self, - context, orig_port, network, - binding, levels, original_port=orig_port) - self._process_dvr_port_binding(mech_context, context, attrs) + try: + with session.begin(subtransactions=True): + orig_port = self.get_port(context, id) + if not binding: + binding = db.ensure_dvr_port_binding( + session, id, host, router_id=device_id) + network = self.get_network(context, + orig_port['network_id']) + levels = db.get_binding_levels(session, id, host) + mech_context = driver_context.PortContext(self, + context, orig_port, network, + binding, levels, original_port=orig_port) + self._process_dvr_port_binding(mech_context, context, + attrs) + except (os_db_exception.DBReferenceError, exc.PortNotFound): + LOG.debug("DVR Port %s has been deleted concurrently", id) + return self._bind_port_if_needed(mech_context) def _pre_delete_port(self, context, port_id, port_check): diff -Nru neutron-7.0.4/neutron/plugins/ml2/rpc.py neutron-7.1.1/neutron/plugins/ml2/rpc.py --- neutron-7.0.4/neutron/plugins/ml2/rpc.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/ml2/rpc.py 2016-06-10 01:43:08.000000000 +0000 @@ -76,9 +76,9 @@ host, cached_networks) if not port_context: - LOG.warning(_LW("Device %(device)s requested by agent " - "%(agent_id)s not found in database"), - {'device': device, 'agent_id': agent_id}) + LOG.debug("Device %(device)s requested by agent " + "%(agent_id)s not found in database", + {'device': device, 'agent_id': agent_id}) return {'device': device} segment = port_context.bottom_bound_segment diff -Nru neutron-7.0.4/neutron/plugins/opencontrail/contrail_plugin.py neutron-7.1.1/neutron/plugins/opencontrail/contrail_plugin.py --- neutron-7.0.4/neutron/plugins/opencontrail/contrail_plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/plugins/opencontrail/contrail_plugin.py 2016-06-10 01:43:08.000000000 +0000 @@ -20,6 +20,7 @@ from neutron.api.v2 import attributes as attr from neutron.common import exceptions as exc +from neutron.common import utils as common_utils from neutron.db import db_base_plugin_v2 from neutron.db import portbindings_base from neutron.extensions import external_net @@ -380,16 +381,18 @@ port = self._get_resource('port', context, res_id, fields) return self._make_port_dict(port, fields) - def _update_ips_for_port(self, context, original_ips, new_ips): + def _update_ips_for_port(self, context, device_owner, + original_ips, new_ips): """Add or remove IPs from the port.""" # These ips are still on the port and haven't been removed prev_ips = [] - # the new_ips contain all of the fixed_ips that are to be updated - if len(new_ips) > cfg.CONF.max_fixed_ips_per_port: - msg = _('Exceeded maximim amount of fixed ips per port') - raise exc.InvalidInput(error_message=msg) + if not common_utils.is_port_trusted({'device_owner': device_owner}): + # the new_ips contain all of the fixed_ips that are to be updated + if len(new_ips) > cfg.CONF.max_fixed_ips_per_port: + msg = _('Exceeded maximim amount of fixed ips per port') + raise exc.InvalidInput(error_message=msg) # Remove all of the intersecting elements for original_ip in original_ips[:]: @@ -423,7 +426,8 @@ if 'fixed_ips' in port['port']: original = self._get_port(context, port_id) added_ips, prev_ips = self._update_ips_for_port( - context, original['fixed_ips'], port['port']['fixed_ips']) + context, original['device_owner'], + original['fixed_ips'], port['port']['fixed_ips']) port['port']['fixed_ips'] = prev_ips + added_ips port = self._update_resource('port', context, port_id, port) diff -Nru neutron-7.0.4/neutron/policy.py neutron-7.1.1/neutron/policy.py --- neutron-7.0.4/neutron/policy.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/policy.py 2016-06-10 01:43:08.000000000 +0000 @@ -64,10 +64,13 @@ def get_resource_and_action(action, pluralized=None): - """Extract resource and action (write, read) from api operation.""" + """Return resource and enforce_attr_based_check(boolean) per + resource and action extracted from api operation. + """ data = action.split(':', 1)[0].split('_', 1) resource = pluralized or ("%ss" % data[-1]) - return (resource, data[0] != 'get') + enforce_attr_based_check = data[0] not in ('get', 'delete') + return (resource, enforce_attr_based_check) def set_rules(policies, overwrite=True): @@ -153,9 +156,9 @@ (e.g.: create_router:external_gateway_info:network_id) """ match_rule = policy.RuleCheck('rule', action) - resource, is_write = get_resource_and_action(action, pluralized) - # Attribute-based checks shall not be enforced on GETs - if is_write: + resource, enforce_attr_based_check = get_resource_and_action( + action, pluralized) + if enforce_attr_based_check: # assigning to variable with short name for improving readability res_map = attributes.RESOURCE_ATTRIBUTE_MAP if resource in res_map: diff -Nru neutron-7.0.4/neutron/service.py neutron-7.1.1/neutron/service.py --- neutron-7.0.4/neutron/service.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/service.py 2016-06-10 01:43:08.000000000 +0000 @@ -125,13 +125,20 @@ class RpcWorker(worker.NeutronWorker): """Wraps a worker to be handled by ProcessLauncher""" - def __init__(self, plugin): - self._plugin = plugin + start_listeners_method = 'start_rpc_listeners' + + def __init__(self, plugins): + self._plugins = plugins self._servers = [] def start(self): - super(RpcWorker, self).start() - self._servers = self._plugin.start_rpc_listeners() + for plugin in self._plugins: + if hasattr(plugin, self.start_listeners_method): + try: + servers = getattr(plugin, self.start_listeners_method)() + except NotImplementedError: + continue + self._servers.extend(servers) def wait(self): try: @@ -164,6 +171,8 @@ def serve_rpc(): plugin = manager.NeutronManager.get_plugin() + service_plugins = ( + manager.NeutronManager.get_service_plugins().values()) if cfg.CONF.rpc_workers < 1: cfg.CONF.set_override('rpc_workers', 1) @@ -181,7 +190,8 @@ raise NotImplementedError() try: - rpc = RpcWorker(plugin) + # passing service plugins only, because core plugin is among them + rpc = RpcWorker(service_plugins) # dispose the whole pool before os.fork, otherwise there will # be shared DB connections in child processes which may cause diff -Nru neutron-7.0.4/neutron/services/l3_router/l3_router_plugin.py neutron-7.1.1/neutron/services/l3_router/l3_router_plugin.py --- neutron-7.0.4/neutron/services/l3_router/l3_router_plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/services/l3_router/l3_router_plugin.py 2016-06-10 01:43:08.000000000 +0000 @@ -58,7 +58,6 @@ @resource_registry.tracked_resources(router=l3_db.Router, floatingip=l3_db.FloatingIP) def __init__(self): - self.setup_rpc() self.router_scheduler = importutils.import_object( cfg.CONF.router_scheduler_driver) self.start_periodic_l3_agent_status_check() @@ -66,9 +65,10 @@ if 'dvr' in self.supported_extension_aliases: l3_dvrscheduler_db.subscribe() l3_db.subscribe() + self.start_rpc_listeners() @log_helpers.log_method_call - def setup_rpc(self): + def start_rpc_listeners(self): # RPC support self.topic = topics.L3PLUGIN self.conn = n_rpc.create_connection(new=True) @@ -77,7 +77,7 @@ self.endpoints = [l3_rpc.L3RpcCallback()] self.conn.create_consumer(self.topic, self.endpoints, fanout=False) - self.conn.consume_in_threads() + return self.conn.consume_in_threads() def get_plugin_type(self): return constants.L3_ROUTER_NAT diff -Nru neutron-7.0.4/neutron/services/metering/drivers/iptables/iptables_driver.py neutron-7.1.1/neutron/services/metering/drivers/iptables/iptables_driver.py --- neutron-7.0.4/neutron/services/metering/drivers/iptables/iptables_driver.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/services/metering/drivers/iptables/iptables_driver.py 2016-06-10 01:43:08.000000000 +0000 @@ -73,6 +73,7 @@ self.iptables_manager = iptables_manager.IptablesManager( namespace=self.ns_name, binary_name=WRAP_NAME, + state_less=True, use_ipv6=ipv6_utils.is_enabled()) self.metering_labels = {} diff -Nru neutron-7.0.4/neutron/services/metering/metering_plugin.py neutron-7.1.1/neutron/services/metering/metering_plugin.py --- neutron-7.0.4/neutron/services/metering/metering_plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/services/metering/metering_plugin.py 2016-06-10 01:43:08.000000000 +0000 @@ -27,14 +27,15 @@ def __init__(self): super(MeteringPlugin, self).__init__() - self.endpoints = [metering_rpc.MeteringRpcCallbacks(self)] + self.meter_rpc = metering_rpc_agent_api.MeteringAgentNotifyAPI() + self.start_rpc_listeners() + def start_rpc_listeners(self): + self.endpoints = [metering_rpc.MeteringRpcCallbacks(self)] self.conn = n_rpc.create_connection(new=True) self.conn.create_consumer( topics.METERING_PLUGIN, self.endpoints, fanout=False) - self.conn.consume_in_threads() - - self.meter_rpc = metering_rpc_agent_api.MeteringAgentNotifyAPI() + return self.conn.consume_in_threads() def create_metering_label(self, context, metering_label): label = super(MeteringPlugin, self).create_metering_label( diff -Nru neutron-7.0.4/neutron/tests/api/admin/test_shared_network_extension.py neutron-7.1.1/neutron/tests/api/admin/test_shared_network_extension.py --- neutron-7.0.4/neutron/tests/api/admin/test_shared_network_extension.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/api/admin/test_shared_network_extension.py 2016-06-10 01:43:08.000000000 +0000 @@ -237,6 +237,15 @@ update_res['rbac_policy'].pop('target_tenant') self.assertEqual(res['policy'], update_res['rbac_policy']) + @test.idempotent_id('86c3529b-1231-40de-803c-affefefef321') + def test_duplicate_policy_error(self): + res = self._make_admin_net_and_subnet_shared_to_tenant_id( + self.client.tenant_id) + with testtools.ExpectedException(lib_exc.Conflict): + self.admin_client.create_rbac_policy( + object_type='network', object_id=res['network']['id'], + action='access_as_shared', target_tenant=self.client.tenant_id) + @test.attr(type='smoke') @test.idempotent_id('86c3529b-1231-40de-803c-afffffff3fff') def test_port_presence_prevents_network_rbac_policy_deletion(self): @@ -271,11 +280,6 @@ @test.attr(type='smoke') @test.idempotent_id('86c3529b-1231-40de-803c-beefbeefbeef') def test_tenant_can_delete_port_on_own_network(self): - # TODO(kevinbenton): make adjustments to the db lookup to - # make this work. - msg = "Non-admin cannot currently delete other's ports." - raise self.skipException(msg) - # pylint: disable=unreachable net = self.create_network() # owned by self.client self.client.create_rbac_policy( object_type='network', object_id=net['id'], diff -Nru neutron-7.0.4/neutron/tests/api/test_networks.py neutron-7.1.1/neutron/tests/api/test_networks.py --- neutron-7.0.4/neutron/tests/api/test_networks.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/api/test_networks.py 2016-06-10 01:43:08.000000000 +0000 @@ -12,7 +12,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -import itertools import netaddr import six @@ -393,29 +392,6 @@ enable_dhcp=True, **self.subnet_dict(['gateway', 'host_routes', 'dns_nameservers'])) - @test.attr(type='smoke') - @test.idempotent_id('af774677-42a9-4e4b-bb58-16fe6a5bc1ec') - def test_external_network_visibility(self): - """Verifies user can see external networks but not subnets.""" - body = self.client.list_networks(**{'router:external': True}) - networks = [network['id'] for network in body['networks']] - self.assertNotEmpty(networks, "No external networks found") - - nonexternal = [net for net in body['networks'] if - not net['router:external']] - self.assertEmpty(nonexternal, "Found non-external networks" - " in filtered list (%s)." % nonexternal) - self.assertIn(CONF.network.public_network_id, networks) - - subnets_iter = (network['subnets'] for network in body['networks']) - # subnets_iter is a list (iterator) of lists. This flattens it to a - # list of UUIDs - public_subnets_iter = itertools.chain(*subnets_iter) - body = self.client.list_subnets() - subnets = [sub['id'] for sub in body['subnets'] - if sub['id'] in public_subnets_iter] - self.assertEmpty(subnets, "Public subnets visible") - class BulkNetworkOpsTestJSON(base.BaseNetworkTest): diff -Nru neutron-7.0.4/neutron/tests/base.py neutron-7.1.1/neutron/tests/base.py --- neutron-7.0.4/neutron/tests/base.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/base.py 2016-06-10 01:43:08.000000000 +0000 @@ -65,10 +65,6 @@ return True -def fake_consume_in_threads(self): - return [] - - def get_rand_name(max_length=None, prefix='test'): """Return a random string. @@ -341,9 +337,9 @@ def setup_rpc_mocks(self): # don't actually start RPC listeners when testing - self.useFixture(fixtures.MonkeyPatch( + mock.patch( 'neutron.common.rpc.Connection.consume_in_threads', - fake_consume_in_threads)) + return_value=[]).start() self.useFixture(fixtures.MonkeyPatch( 'oslo_messaging.Notifier', fake_notifier.FakeNotifier)) diff -Nru neutron-7.0.4/neutron/tests/common/l3_test_common.py neutron-7.1.1/neutron/tests/common/l3_test_common.py --- neutron-7.0.4/neutron/tests/common/l3_test_common.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/common/l3_test_common.py 2016-06-10 01:43:08.000000000 +0000 @@ -175,7 +175,8 @@ def router_append_subnet(router, count=1, ip_version=4, - ipv6_subnet_modes=None, interface_id=None): + ipv6_subnet_modes=None, interface_id=None, + network_mtu=0): if ip_version == 6: subnet_mode_none = {'ra_mode': None, 'address_mode': None} if not ipv6_subnet_modes: @@ -235,6 +236,7 @@ mac_address.dialect = netaddr.mac_unix interfaces.append( {'id': _uuid(), + 'mtu': network_mtu, 'network_id': _uuid(), 'admin_state_up': True, 'mac_address': str(mac_address), diff -Nru neutron-7.0.4/neutron/tests/contrib/functional-testing.filters neutron-7.1.1/neutron/tests/contrib/functional-testing.filters --- neutron-7.0.4/neutron/tests/contrib/functional-testing.filters 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/contrib/functional-testing.filters 2016-06-10 01:43:08.000000000 +0000 @@ -16,3 +16,17 @@ ncbsd_kill: KillFilter, root, nc.openbsd, -9 ncat_kill: KillFilter, root, ncat, -9 ss_filter: CommandFilter, ss, root + +# enable dhclient from namespace +dhclient_filter: CommandFilter, dhclient, root +dhclient_kill: KillFilter, root, dhclient, -9 + +# Actually, dhclient is used for test dhcp-agent and runs +# in dhcp-agent namespace. If in that namespace resolv.conf file not exist +# dhclient will override system /etc/resolv.conf +# Filters below are limit functions mkdir, rm and touch +# only to create and delete file resolv.conf in the that namespace +mkdir_filter: RegExpFilter, /bin/mkdir, root, mkdir, -p, /etc/netns/qdhcp-[0-9a-z./-]+ +rm_filter: RegExpFilter, /bin/rm, root, rm, -r, /etc/netns/qdhcp-[0-9a-z./-]+ +touch_filter: RegExpFilter, /bin/touch, root, touch, /etc/netns/qdhcp-[0-9a-z./-]+/resolv.conf +touch_filter: RegExpFilter, /usr/bin/touch, root, touch, /etc/netns/qdhcp-[0-9a-z./-]+/resolv.conf diff -Nru neutron-7.0.4/neutron/tests/fullstack/resources/config.py neutron-7.1.1/neutron/tests/fullstack/resources/config.py --- neutron-7.0.4/neutron/tests/fullstack/resources/config.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/fullstack/resources/config.py 2016-06-10 01:43:08.000000000 +0000 @@ -201,8 +201,7 @@ 'integration_bridge': self._generate_integration_bridge(), }, 'securitygroup': { - 'firewall_driver': ('neutron.agent.linux.iptables_firewall.' - 'OVSHybridIptablesFirewallDriver'), + 'firewall_driver': 'noop', }, 'agent': { 'l2_population': str(self.env_desc.l2_pop), diff -Nru neutron-7.0.4/neutron/tests/fullstack/test_l3_agent.py neutron-7.1.1/neutron/tests/fullstack/test_l3_agent.py --- neutron-7.0.4/neutron/tests/fullstack/test_l3_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/fullstack/test_l3_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -15,6 +15,7 @@ import functools from oslo_utils import uuidutils +import testtools from neutron.agent.l3 import agent as l3_agent from neutron.agent.l3 import namespaces @@ -72,6 +73,7 @@ return ( agents['agents'][0]['ha_state'] != agents['agents'][1]['ha_state']) + @testtools.skip('bug/1550886') def test_ha_router(self): # TODO(amuller): Test external connectivity before and after a # failover, see: https://review.openstack.org/#/c/196393/ diff -Nru neutron-7.0.4/neutron/tests/functional/agent/l2/base.py neutron-7.1.1/neutron/tests/functional/agent/l2/base.py --- neutron-7.0.4/neutron/tests/functional/agent/l2/base.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/l2/base.py 2016-06-10 01:43:08.000000000 +0000 @@ -61,6 +61,8 @@ prefix='br-int') self.br_tun = base.get_rand_name(n_const.DEVICE_NAME_MAX_LEN, prefix='br-tun') + self.br_phys = base.get_rand_name(n_const.DEVICE_NAME_MAX_LEN, + prefix='br-phys') patch_name_len = n_const.DEVICE_NAME_MAX_LEN - len("-patch-tun") self.patch_tun = "%s-patch-tun" % self.br_int[patch_name_len:] self.patch_int = "%s-patch-int" % self.br_tun[patch_name_len:] @@ -106,7 +108,9 @@ else: tunnel_types = None local_ip = '192.168.10.1' - bridge_mappings = {'physnet': self.br_int} + bridge_mappings = {'physnet': self.br_phys} + # Physical bridges should be created prior to running + self._bridge_classes()['br_phys'](self.br_phys).create() agent = ovs_agent.OVSNeutronAgent(self._bridge_classes(), self.br_int, self.br_tun, local_ip, bridge_mappings, @@ -155,16 +159,20 @@ return {'id': uuidutils.generate_uuid(), 'tenant_id': uuidutils.generate_uuid()} - def _plug_ports(self, network, ports, agent, ip_len=24): + def _plug_ports(self, network, ports, agent, + bridge=None, namespace=None): + if namespace is None: + namespace = self.namespace for port in ports: + bridge = bridge or agent.int_br self.driver.plug( network.get('id'), port.get('id'), port.get('vif_name'), port.get('mac_address'), - agent.int_br.br_name, namespace=self.namespace) - ip_cidrs = ["%s/%s" % (port.get('fixed_ips')[0][ - 'ip_address'], ip_len)] + bridge.br_name, namespace=namespace) + ip_cidrs = ["%s/8" % (port.get('fixed_ips')[0][ + 'ip_address'])] self.driver.init_l3(port.get('vif_name'), ip_cidrs, - namespace=self.namespace) + namespace=namespace) def _unplug_ports(self, ports, agent): for port in ports: @@ -175,9 +183,9 @@ dev = {'device': port['id'], 'port_id': port['id'], 'network_id': network['id'], - 'network_type': 'vlan', - 'physical_network': 'physnet', - 'segmentation_id': 1, + 'network_type': network.get('network_type', 'vlan'), + 'physical_network': network.get('physical_network', 'physnet'), + 'segmentation_id': network.get('segmentation_id', 1), 'fixed_ips': port['fixed_ips'], 'device_owner': 'compute', 'port_security_enabled': True, @@ -279,11 +287,26 @@ timeout=timeout) def setup_agent_and_ports(self, port_dicts, create_tunnels=True, - trigger_resync=False): + trigger_resync=False, network=None): self.agent = self.create_agent(create_tunnels=create_tunnels) self.start_agent(self.agent) - self.network = self._create_test_network_dict() + self.network = network or self._create_test_network_dict() self.ports = port_dicts if trigger_resync: self._prepare_resync_trigger(self.agent) self._plug_ports(self.network, self.ports, self.agent) + + def plug_ports_to_phys_br(self, network, ports, namespace=None): + physical_network = network.get('physical_network', 'physnet') + phys_segmentation_id = network.get('segmentation_id', None) + network_type = network.get('network_type', 'flat') + + phys_br = self.agent.phys_brs[physical_network] + + self._plug_ports(network, ports, self.agent, bridge=phys_br, + namespace=namespace) + + if phys_segmentation_id and network_type == 'vlan': + for port in ports: + phys_br.set_db_attribute( + "Port", port['vif_name'], "tag", phys_segmentation_id) diff -Nru neutron-7.0.4/neutron/tests/functional/agent/linux/helpers.py neutron-7.1.1/neutron/tests/functional/agent/linux/helpers.py --- neutron-7.0.4/neutron/tests/functional/agent/linux/helpers.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/linux/helpers.py 2016-06-10 01:43:03.000000000 +0000 @@ -12,10 +12,15 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +import multiprocessing import os +import time import fixtures +from neutron.agent.linux import utils +from neutron.tests import tools + class RecursivePermDirFixture(fixtures.Fixture): """Ensure at least perms permissions on directory and ancestors.""" @@ -34,3 +39,49 @@ os.chmod(current_directory, perms | self.least_perms) previous_directory = current_directory current_directory = os.path.dirname(current_directory) + + +class AdminDirFixture(fixtures.Fixture): + """Handle directory create/delete with admin permissions required""" + + def __init__(self, directory): + super(AdminDirFixture, self).__init__() + self.directory = directory + + def _setUp(self): + # NOTE(cbrandily): Ensure we will not delete a directory existing + # before test run during cleanup. + if os.path.exists(self.directory): + tools.fail('%s already exists' % self.directory) + + create_cmd = ['mkdir', '-p', self.directory] + delete_cmd = ['rm', '-r', self.directory] + utils.execute(create_cmd, run_as_root=True) + self.addCleanup(utils.execute, delete_cmd, run_as_root=True) + + +class SleepyProcessFixture(fixtures.Fixture): + """ + Process fixture that performs time.sleep for the given number of seconds. + """ + + def __init__(self, timeout=60): + super(SleepyProcessFixture, self).__init__() + self.timeout = timeout + + @staticmethod + def yawn(seconds): + time.sleep(seconds) + + def _setUp(self): + self.process = multiprocessing.Process(target=self.yawn, + args=[self.timeout]) + self.process.start() + self.addCleanup(self.destroy) + + def destroy(self): + self.process.terminate() + + @property + def pid(self): + return self.process.pid diff -Nru neutron-7.0.4/neutron/tests/functional/agent/linux/test_bridge_lib.py neutron-7.1.1/neutron/tests/functional/agent/linux/test_bridge_lib.py --- neutron-7.0.4/neutron/tests/functional/agent/linux/test_bridge_lib.py 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/linux/test_bridge_lib.py 2016-06-10 01:43:08.000000000 +0000 @@ -0,0 +1,42 @@ +# Copyright (c) 2015 Thales Services SAS +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +from neutron.agent.linux import bridge_lib +from neutron.tests.common import net_helpers +from neutron.tests.functional import base + + +class BridgeLibTestCase(base.BaseSudoTestCase): + + def setUp(self): + super(BridgeLibTestCase, self).setUp() + self.bridge, self.port_fixture = self.create_bridge_port_fixture() + + def create_bridge_port_fixture(self): + bridge = self.useFixture( + net_helpers.LinuxBridgeFixture(namespace=None)).bridge + port_fixture = self.useFixture( + net_helpers.LinuxBridgePortFixture(bridge)) + return bridge, port_fixture + + def test_get_interface_bridged_time(self): + port = self.port_fixture.br_port + t1 = bridge_lib.get_interface_bridged_time(port) + self.bridge.delif(port) + self.bridge.addif(port) + t2 = bridge_lib.get_interface_bridged_time(port) + self.assertIsNotNone(t1) + self.assertIsNotNone(t2) + self.assertGreater(t2, t1) diff -Nru neutron-7.0.4/neutron/tests/functional/agent/linux/test_iptables.py neutron-7.1.1/neutron/tests/functional/agent/linux/test_iptables.py --- neutron-7.0.4/neutron/tests/functional/agent/linux/test_iptables.py 2016-03-29 17:44:55.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/linux/test_iptables.py 2016-06-10 01:43:08.000000000 +0000 @@ -31,7 +31,8 @@ DIRECTION_CHAIN_MAPPER = {'ingress': 'INPUT', 'egress': 'OUTPUT'} PROTOCOL_BLOCK_RULE = '-p %s -j DROP' - PROTOCOL_PORT_BLOCK_RULE = '-p %s --dport %d -j DROP' + PROTOCOL_PORT_BLOCK_RULE = ('-p %(protocol)s -m %(protocol)s ' + '--dport %(port)d -j DROP') def setUp(self): super(IptablesManagerTestCase, self).setUp() @@ -73,7 +74,8 @@ def _get_chain_and_rule(self, direction, protocol, port): chain = self.DIRECTION_CHAIN_MAPPER[direction] if port: - rule = self.PROTOCOL_PORT_BLOCK_RULE % (protocol, port) + rule = self.PROTOCOL_PORT_BLOCK_RULE % {'protocol': protocol, + 'port': port} else: rule = self.PROTOCOL_BLOCK_RULE % protocol return chain, rule diff -Nru neutron-7.0.4/neutron/tests/functional/agent/linux/test_keepalived.py neutron-7.1.1/neutron/tests/functional/agent/linux/test_keepalived.py --- neutron-7.0.4/neutron/tests/functional/agent/linux/test_keepalived.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/linux/test_keepalived.py 2016-06-10 01:43:08.000000000 +0000 @@ -20,6 +20,7 @@ from neutron.agent.linux import keepalived from neutron.agent.linux import utils from neutron.tests import base +from neutron.tests.functional.agent.linux import helpers from neutron.tests.unit.agent.linux import test_keepalived @@ -79,3 +80,33 @@ def test_keepalived_respawn_with_unexpected_exit(self): self._test_keepalived_respawns(False) + + def _test_keepalived_spawns_conflicting_pid(self, process, pid_file): + # Test the situation when keepalived PID file contains PID of an + # existing non-keepalived process. This situation can happen e.g. + # after hard node reset. + + spawn_process = helpers.SleepyProcessFixture() + self.useFixture(spawn_process) + + with open(pid_file, "w") as f_pid_file: + f_pid_file.write("%s" % spawn_process.pid) + + self.manager.spawn() + utils.wait_until_true( + lambda: process.active, + timeout=5, + sleep=0.1, + exception=RuntimeError(_("Keepalived didn't spawn"))) + + def test_keepalived_spawns_conflicting_pid_base_process(self): + process = self.manager.get_process() + pid_file = process.get_pid_file_name() + self._test_keepalived_spawns_conflicting_pid(process, pid_file) + + def test_keepalived_spawns_conflicting_pid_vrrp_subprocess(self): + process = self.manager.get_process() + pid_file = process.get_pid_file_name() + self._test_keepalived_spawns_conflicting_pid( + process, + self.manager.get_vrrp_pid_file_name(pid_file)) diff -Nru neutron-7.0.4/neutron/tests/functional/agent/linux/test_linuxbridge_arp_protect.py neutron-7.1.1/neutron/tests/functional/agent/linux/test_linuxbridge_arp_protect.py --- neutron-7.0.4/neutron/tests/functional/agent/linux/test_linuxbridge_arp_protect.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/linux/test_linuxbridge_arp_protect.py 2016-06-10 01:43:08.000000000 +0000 @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.common import utils from neutron.plugins.ml2.drivers.linuxbridge.agent import arp_protect from neutron.tests.common import machine_fixtures @@ -34,10 +35,17 @@ bridge = lbfixture.bridge self.source, self.destination, self.observer = self.useFixture( machine_fixtures.PeerMachines(bridge, amount=3)).machines + self.addCleanup(self._ensure_rules_cleaned) + + def _ensure_rules_cleaned(self): + rules = [r for r in arp_protect.ebtables(['-L']).splitlines() + if r and 'Bridge' not in r] + self.assertEqual([], rules, 'Test leaked ebtables rules') def _add_arp_protection(self, machine, addresses, extra_port_dict=None): port_dict = {'fixed_ips': [{'ip_address': a} for a in addresses], - 'device_owner': 'nobody'} + 'device_owner': 'nobody', + 'mac_address': machine.port.link.address} if extra_port_dict: port_dict.update(extra_port_dict) name = net_helpers.VethFixture.get_peer_name(machine.port.name) @@ -55,12 +63,38 @@ arping(self.source.namespace, self.destination.ip) arping(self.destination.namespace, self.source.ip) + def test_arp_correct_protection_allowed_address_pairs(self): + smac = self.source.port.link.address + port = {'mac_address': '00:11:22:33:44:55', + 'allowed_address_pairs': [{'mac_address': smac, + 'ip_address': self.source.ip}]} + # make sure a large number of allowed address pairs works + for i in range(100000): + port['allowed_address_pairs'].append( + {'mac_address': utils.get_random_mac( + 'fa:16:3e:00:00:00'.split(':')), + 'ip_address': '10.10.10.10'}) + self._add_arp_protection(self.source, ['1.2.2.2'], port) + self._add_arp_protection(self.destination, [self.destination.ip]) + arping(self.source.namespace, self.destination.ip) + arping(self.destination.namespace, self.source.ip) + def test_arp_fails_incorrect_protection(self): self._add_arp_protection(self.source, ['1.1.1.1']) self._add_arp_protection(self.destination, ['2.2.2.2']) no_arping(self.source.namespace, self.destination.ip) no_arping(self.destination.namespace, self.source.ip) + def test_arp_fails_incorrect_mac_protection(self): + # a bad mac filter on the source will prevent any traffic from it + self._add_arp_protection(self.source, [self.source.ip], + {'mac_address': '00:11:22:33:44:55'}) + no_arping(self.source.namespace, self.destination.ip) + no_arping(self.destination.namespace, self.source.ip) + # correcting it should make it work + self._add_arp_protection(self.source, [self.source.ip]) + arping(self.source.namespace, self.destination.ip) + def test_arp_protection_removal(self): self._add_arp_protection(self.source, ['1.1.1.1']) self._add_arp_protection(self.destination, ['2.2.2.2']) diff -Nru neutron-7.0.4/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py neutron-7.1.1/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py --- neutron-7.0.4/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py 2016-06-10 01:43:08.000000000 +0000 @@ -24,6 +24,7 @@ from oslo_config import cfg +from neutron.agent.common import ovs_lib from neutron.agent.linux import ovsdb_monitor from neutron.agent.linux import utils from neutron.tests import base as tests_base @@ -139,3 +140,18 @@ devices = self.monitor.get_events() self.assertTrue(devices.get('added'), 'Initial call should always be true') + + def test_get_events_includes_ofport(self): + utils.wait_until_true(lambda: self.monitor.has_updates) + self.monitor.get_events() # clear initial events + br = self.useFixture(net_helpers.OVSBridgeFixture()) + p1 = self.useFixture(net_helpers.OVSPortFixture(br.bridge)) + + def p1_event_has_ofport(): + if not self.monitor.has_updates: + return + for e in self.monitor.new_events['added']: + if (e['name'] == p1.port.name and + e['ofport'] != ovs_lib.UNASSIGNED_OFPORT): + return True + utils.wait_until_true(p1_event_has_ofport) diff -Nru neutron-7.0.4/neutron/tests/functional/agent/test_dhcp_agent.py neutron-7.1.1/neutron/tests/functional/agent/test_dhcp_agent.py --- neutron-7.0.4/neutron/tests/functional/agent/test_dhcp_agent.py 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/test_dhcp_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -0,0 +1,315 @@ +# Copyright (c) 2015 Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os.path + +import eventlet +import fixtures +import mock +import netaddr +from oslo_config import fixture as fixture_config +from oslo_utils import uuidutils + +from neutron.agent.common import config +from neutron.agent.common import ovs_lib +from neutron.agent.dhcp import agent +from neutron.agent import dhcp_agent +from neutron.agent.linux import dhcp +from neutron.agent.linux import external_process +from neutron.agent.linux import interface +from neutron.agent.linux import ip_lib +from neutron.agent.linux import utils +from neutron.common import constants +from neutron.common import utils as common_utils +from neutron.tests.common import net_helpers +from neutron.tests.functional.agent.linux import helpers +from neutron.tests.functional import base + + +class DHCPAgentOVSTestFramework(base.BaseSudoTestCase): + + _DHCP_PORT_MAC_ADDRESS = netaddr.EUI("24:77:03:7d:00:4c") + _DHCP_PORT_MAC_ADDRESS.dialect = netaddr.mac_unix + _TENANT_PORT_MAC_ADDRESS = netaddr.EUI("24:77:03:7d:00:3a") + _TENANT_PORT_MAC_ADDRESS.dialect = netaddr.mac_unix + + _IP_ADDRS = { + 4: {'addr': '192.168.10.11', + 'cidr': '192.168.10.0/24', + 'gateway': '192.168.10.1'}, + 6: {'addr': '0:0:0:0:0:ffff:c0a8:a0b', + 'cidr': '0:0:0:0:0:ffff:c0a8:a00/120', + 'gateway': '0:0:0:0:0:ffff:c0a8:a01'}, } + + def setUp(self): + super(DHCPAgentOVSTestFramework, self).setUp() + config.setup_logging() + self.conf_fixture = self.useFixture(fixture_config.Config()) + self.conf = self.conf_fixture.conf + dhcp_agent.register_options(self.conf) + + # NOTE(cbrandily): TempDir fixture creates a folder with 0o700 + # permissions but agent dir must be readable by dnsmasq user (nobody) + agent_config_dir = self.useFixture(fixtures.TempDir()).path + self.useFixture( + helpers.RecursivePermDirFixture(agent_config_dir, 0o555)) + + self.conf.set_override("dhcp_confs", agent_config_dir) + self.conf.set_override( + 'interface_driver', + 'neutron.agent.linux.interface.OVSInterfaceDriver') + self.conf.set_override('report_interval', 0, 'AGENT') + br_int = self.useFixture(net_helpers.OVSBridgeFixture()).bridge + self.conf.set_override('ovs_integration_bridge', br_int.br_name) + + self.mock_plugin_api = mock.patch( + 'neutron.agent.dhcp.agent.DhcpPluginApi').start().return_value + mock.patch('neutron.agent.rpc.PluginReportStateAPI').start() + self.agent = agent.DhcpAgentWithStateReport('localhost') + + self.ovs_driver = interface.OVSInterfaceDriver(self.conf) + + self.conf.set_override('check_child_processes_interval', 1, 'AGENT') + + def network_dict_for_dhcp(self, dhcp_enabled=True, ip_version=4): + net_id = uuidutils.generate_uuid() + subnet_dict = self.create_subnet_dict( + net_id, dhcp_enabled, ip_version) + port_dict = self.create_port_dict( + net_id, subnet_dict.id, + mac_address=str(self._DHCP_PORT_MAC_ADDRESS), + ip_version=ip_version) + port_dict.device_id = common_utils.get_dhcp_agent_device_id( + net_id, self.conf.host) + net_dict = self.create_network_dict( + net_id, [subnet_dict], [port_dict]) + return net_dict + + def create_subnet_dict(self, net_id, dhcp_enabled=True, ip_version=4): + sn_dict = dhcp.DictModel({ + "id": uuidutils.generate_uuid(), + "network_id": net_id, + "ip_version": ip_version, + "cidr": self._IP_ADDRS[ip_version]['cidr'], + "gateway_ip": (self. + _IP_ADDRS[ip_version]['gateway']), + "enable_dhcp": dhcp_enabled, + "dns_nameservers": [], + "host_routes": [], + "ipv6_ra_mode": None, + "ipv6_address_mode": None}) + if ip_version == 6: + sn_dict['ipv6_address_mode'] = constants.DHCPV6_STATEFUL + return sn_dict + + def create_port_dict(self, network_id, subnet_id, mac_address, + ip_version=4, ip_address=None): + ip_address = (self._IP_ADDRS[ip_version]['addr'] + if not ip_address else ip_address) + port_dict = dhcp.DictModel({ + "id": uuidutils.generate_uuid(), + "name": "foo", + "mac_address": mac_address, + "network_id": network_id, + "admin_state_up": True, + "device_id": uuidutils.generate_uuid(), + "device_owner": "foo", + "fixed_ips": [{"subnet_id": subnet_id, + "ip_address": ip_address}], }) + return port_dict + + def create_network_dict(self, net_id, subnets=None, ports=None): + subnets = [] if not subnets else subnets + ports = [] if not ports else ports + net_dict = dhcp.NetModel(use_namespaces=True, d={ + "id": net_id, + "subnets": subnets, + "ports": ports, + "admin_state_up": True, + "tenant_id": uuidutils.generate_uuid(), }) + return net_dict + + def get_interface_name(self, network, port): + device_manager = dhcp.DeviceManager(conf=self.conf, plugin=mock.Mock()) + return device_manager.get_interface_name(network, port) + + def configure_dhcp_for_network(self, network, dhcp_enabled=True): + self.agent.configure_dhcp_for_network(network) + self.addCleanup(self._cleanup_network, network, dhcp_enabled) + + def _cleanup_network(self, network, dhcp_enabled): + self.mock_plugin_api.release_dhcp_port.return_value = None + if dhcp_enabled: + self.agent.call_driver('disable', network) + + def assert_dhcp_resources(self, network, dhcp_enabled): + ovs = ovs_lib.BaseOVS() + port = network.ports[0] + iface_name = self.get_interface_name(network, port) + self.assertEqual(dhcp_enabled, ovs.port_exists(iface_name)) + self.assert_dhcp_namespace(network.namespace, dhcp_enabled) + self.assert_dhcp_device(network.namespace, iface_name, dhcp_enabled) + + def assert_dhcp_namespace(self, namespace, dhcp_enabled): + ip = ip_lib.IPWrapper() + self.assertEqual(dhcp_enabled, ip.netns.exists(namespace)) + + def assert_dhcp_device(self, namespace, dhcp_iface_name, dhcp_enabled): + dev = ip_lib.IPDevice(dhcp_iface_name, namespace) + self.assertEqual(dhcp_enabled, ip_lib.device_exists( + dhcp_iface_name, namespace)) + if dhcp_enabled: + self.assertEqual(self._DHCP_PORT_MAC_ADDRESS, dev.link.address) + + def _plug_port_for_dhcp_request(self, network, port): + namespace = network.namespace + vif_name = self.get_interface_name(network.id, port) + + self.ovs_driver.plug(network.id, port.id, vif_name, port.mac_address, + self.conf['ovs_integration_bridge'], + namespace=namespace) + + def _ip_list_for_vif(self, vif_name, namespace): + ip_device = ip_lib.IPDevice(vif_name, namespace) + return ip_device.addr.list(ip_version=4) + + def _get_network_port_for_allocation_test(self): + network = self.network_dict_for_dhcp() + ip_addr = netaddr.IPNetwork(network.subnets[0].cidr)[1] + port = self.create_port_dict( + network.id, network.subnets[0].id, + mac_address=str(self._TENANT_PORT_MAC_ADDRESS), + ip_address=str(ip_addr)) + return network, port + + def assert_good_allocation_for_port(self, network, port): + vif_name = self.get_interface_name(network.id, port) + self._run_dhclient(vif_name, network) + + predicate = lambda: len( + self._ip_list_for_vif(vif_name, network.namespace)) + utils.wait_until_true(predicate, 10) + + ip_list = self._ip_list_for_vif(vif_name, network.namespace) + cidr = ip_list[0].get('cidr') + ip_addr = str(netaddr.IPNetwork(cidr).ip) + self.assertEqual(port.fixed_ips[0].ip_address, ip_addr) + + def assert_bad_allocation_for_port(self, network, port): + vif_name = self.get_interface_name(network.id, port) + self._run_dhclient(vif_name, network) + # we need wait some time (10 seconds is enough) and check + # that dhclient not configured ip-address for interface + eventlet.sleep(10) + + ip_list = self._ip_list_for_vif(vif_name, network.namespace) + self.assertEqual([], ip_list) + + def _run_dhclient(self, vif_name, network): + # NOTE: Before run dhclient we should create resolv.conf file + # in namespace, where we will run dhclient for testing address + # allocation for port, otherwise, dhclient will override + # system /etc/resolv.conf + # By default, folder for dhcp-agent's namespace doesn't exist + # that's why we use AdminDirFixture for create directory + # with admin permissions in /etc/netns/ and touch resolv.conf in it. + etc_dir = '/etc/netns/%s' % network.namespace + self.useFixture(helpers.AdminDirFixture(etc_dir)) + cmd = ['touch', os.path.join(etc_dir, 'resolv.conf')] + utils.execute(cmd, run_as_root=True) + dhclient_cmd = ['dhclient', '--no-pid', '-d', '-1', vif_name] + proc = net_helpers.RootHelperProcess( + cmd=dhclient_cmd, namespace=network.namespace) + self.addCleanup(proc.wait) + self.addCleanup(proc.kill) + + def _get_metadata_proxy_process(self, network): + return external_process.ProcessManager( + self.conf, + network.id, + network.namespace) + + +class DHCPAgentOVSTestCase(DHCPAgentOVSTestFramework): + + def test_create_subnet_with_dhcp(self): + dhcp_enabled = True + for version in [4, 6]: + network = self.network_dict_for_dhcp( + dhcp_enabled, ip_version=version) + self.configure_dhcp_for_network(network=network, + dhcp_enabled=dhcp_enabled) + self.assert_dhcp_resources(network, dhcp_enabled) + + def test_agent_mtu_set_on_interface_driver(self): + network = self.network_dict_for_dhcp() + network["mtu"] = 789 + self.configure_dhcp_for_network(network=network) + port = network.ports[0] + iface_name = self.get_interface_name(network, port) + dev = ip_lib.IPDevice(iface_name, network.namespace) + self.assertEqual(789, dev.link.mtu) + + def test_good_address_allocation(self): + network, port = self._get_network_port_for_allocation_test() + network.ports.append(port) + self.configure_dhcp_for_network(network=network) + self._plug_port_for_dhcp_request(network, port) + self.assert_good_allocation_for_port(network, port) + + def test_bad_address_allocation(self): + network, port = self._get_network_port_for_allocation_test() + network.ports.append(port) + self.configure_dhcp_for_network(network=network) + bad_mac_address = netaddr.EUI(self._TENANT_PORT_MAC_ADDRESS.value + 1) + bad_mac_address.dialect = netaddr.mac_unix + port.mac_address = str(bad_mac_address) + self._plug_port_for_dhcp_request(network, port) + self.assert_bad_allocation_for_port(network, port) + + def _spawn_network_metadata_proxy(self): + network = self.network_dict_for_dhcp() + self.conf.set_override('enable_isolated_metadata', True) + self.addCleanup(self.agent.disable_isolated_metadata_proxy, network) + self.configure_dhcp_for_network(network=network) + pm = self._get_metadata_proxy_process(network) + utils.wait_until_true( + lambda: pm.active, + timeout=5, + sleep=0.01, + exception=RuntimeError("Metadata proxy didn't spawn")) + return (pm, network) + + def test_metadata_proxy_respawned(self): + pm, network = self._spawn_network_metadata_proxy() + old_pid = pm.pid + + utils.execute(['kill', '-9', old_pid], run_as_root=True) + utils.wait_until_true( + lambda: pm.active and pm.pid != old_pid, + timeout=5, + sleep=0.1, + exception=RuntimeError("Metadata proxy didn't respawn")) + + def test_stale_metadata_proxy_killed(self): + pm, network = self._spawn_network_metadata_proxy() + + self.conf.set_override('enable_isolated_metadata', False) + self.configure_dhcp_for_network(network=network) + utils.wait_until_true( + lambda: not pm.active, + timeout=5, + sleep=0.1, + exception=RuntimeError("Stale metadata proxy didn't get killed")) diff -Nru neutron-7.0.4/neutron/tests/functional/agent/test_l2_ovs_agent.py neutron-7.1.1/neutron/tests/functional/agent/test_l2_ovs_agent.py --- neutron-7.0.4/neutron/tests/functional/agent/test_l2_ovs_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/test_l2_ovs_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -106,7 +106,78 @@ self.agent.setup_integration_br() time.sleep(0.25) + def test_assert_br_int_patch_port_ofports_dont_change(self): + # When the integration bridge is setup, it should reuse the existing + # patch ports between br-int and br-tun. + self.setup_agent_and_ports(port_dicts=[], create_tunnels=True) + patch_int_ofport_before = self.agent.patch_int_ofport + patch_tun_ofport_before = self.agent.patch_tun_ofport + + self.setup_agent_and_ports(port_dicts=[], create_tunnels=True) + self.assertEqual(patch_int_ofport_before, self.agent.patch_int_ofport) + self.assertEqual(patch_tun_ofport_before, self.agent.patch_tun_ofport) + + def test_assert_br_phys_patch_port_ofports_dont_change(self): + # When the integration bridge is setup, it should reuse the existing + # patch ports between br-int and br-phys. + self.setup_agent_and_ports(port_dicts=[]) + patch_int_ofport_before = self.agent.int_ofports['physnet'] + patch_phys_ofport_before = self.agent.phys_ofports['physnet'] + + self.setup_agent_and_ports(port_dicts=[]) + self.assertEqual(patch_int_ofport_before, + self.agent.int_ofports['physnet']) + self.assertEqual(patch_phys_ofport_before, + self.agent.phys_ofports['physnet']) + + def test_assert_pings_during_br_phys_setup_not_lost_in_vlan_to_flat(self): + provider_net = self._create_test_network_dict() + provider_net['network_type'] = 'flat' + + self._test_assert_pings_during_br_phys_setup_not_lost(provider_net) + + def test_assert_pings_during_br_phys_setup_not_lost_in_vlan_to_vlan(self): + provider_net = self._create_test_network_dict() + provider_net['network_type'] = 'vlan' + provider_net['segmentation_id'] = 876 + + self._test_assert_pings_during_br_phys_setup_not_lost(provider_net) + + def _test_assert_pings_during_br_phys_setup_not_lost(self, provider_net): + # Separate namespace is needed when pinging from one port to another, + # otherwise Linux ping uses loopback instead for sending and receiving + # ping, hence ignoring flow setup. + ns_phys = self.useFixture(net_helpers.NamespaceFixture()).name + + ports = self.create_test_ports(amount=2) + port_int = ports[0] + port_phys = ports[1] + ip_int = port_int['fixed_ips'][0]['ip_address'] + ip_phys = port_phys['fixed_ips'][0]['ip_address'] + + self.setup_agent_and_ports(port_dicts=[port_int], create_tunnels=False, + network=provider_net) + + self.plug_ports_to_phys_br(provider_net, [port_phys], + namespace=ns_phys) + + # The OVS agent doesn't monitor the physical bridges, no notification + # is sent when a port is up on a physical bridge, hence waiting only + # for the ports connected to br-int + self.wait_until_ports_state([port_int], up=True) + + with net_helpers.async_ping(ns_phys, [ip_int]) as done: + while not done(): + self.agent.setup_physical_bridges(self.agent.bridge_mappings) + time.sleep(0.25) + + with net_helpers.async_ping(self.namespace, [ip_phys]) as done: + while not done(): + self.agent.setup_physical_bridges(self.agent.bridge_mappings) + time.sleep(0.25) + def test_noresync_after_port_gone(self): + '''This will test the scenario where a port is removed after listing it but before getting vif info about it. ''' diff -Nru neutron-7.0.4/neutron/tests/functional/agent/test_l3_agent.py neutron-7.1.1/neutron/tests/functional/agent/test_l3_agent.py --- neutron-7.0.4/neutron/tests/functional/agent/test_l3_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/test_l3_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -270,6 +270,12 @@ 'INPUT', metadata_port_filter)) + def _assert_iptables_rules_converged(self, router): + # if your code is failing on this line, it means you are not generating + # your iptables rules in the same format that iptables-save returns + # them. run iptables-save to see the format they should be in + self.assertFalse(router.iptables_manager.apply()) + def _assert_internal_devices(self, router): internal_devices = router.router[l3_constants.INTERFACE_KEY] self.assertTrue(len(internal_devices)) @@ -427,6 +433,27 @@ self._router_lifecycle(enable_ha=False, dual_stack=True, v6_ext_gw_with_sub=False) + def test_legacy_router_ns_rebuild(self): + router_info = self.generate_router_info(False) + router = self.manage_router(self.agent, router_info) + gw_port = router.router['gw_port'] + gw_inf_name = router.get_external_device_name(gw_port['id']) + router_ports = [gw_inf_name] + for i_port in router_info.get(l3_constants.INTERFACE_KEY, []): + interface_name = router.get_internal_device_name(i_port['id']) + router_ports.append(interface_name) + + namespaces.Namespace.delete(router.router_namespace) + + # l3 agent should be able to rebuild the ns when it is deleted + self.manage_router(self.agent, router_info) + # Assert the router ports are there in namespace + self.assertTrue( + all([ip_lib.device_exists(port, namespace=router.ns_name) + for port in router_ports])) + + self._delete_router(self.agent, router.router_id) + def test_ha_router_lifecycle(self): self._router_lifecycle(enable_ha=True) @@ -680,6 +707,7 @@ self.assertTrue(self.floating_ips_configured(router)) self._assert_snat_chains(router) self._assert_floating_ip_chains(router) + self._assert_iptables_rules_converged(router) self._assert_extra_routes(router) ip_versions = [4, 6] if (ip_version == 6 or dual_stack) else [4] self._assert_onlink_subnet_routes(router, ip_versions) @@ -1051,6 +1079,10 @@ class TestDvrRouter(L3AgentTestFramework): + def test_dvr_router_lifecycle_without_ha_with_snat_with_fips_nmtu(self): + self._dvr_router_lifecycle(enable_ha=False, enable_snat=True, + use_port_mtu=True) + def test_dvr_router_lifecycle_without_ha_without_snat_with_fips(self): self._dvr_router_lifecycle(enable_ha=False, enable_snat=False) @@ -1102,7 +1134,7 @@ self._validate_fips_for_external_network(router2, fip2_ns) def _dvr_router_lifecycle(self, enable_ha=False, enable_snat=False, - custom_mtu=2000): + custom_mtu=2000, use_port_mtu=False): '''Test dvr router lifecycle :param enable_ha: sets the ha value for the router. @@ -1114,11 +1146,18 @@ # Since by definition this is a dvr (distributed = true) # only dvr and dvr_snat are applicable self.agent.conf.agent_mode = 'dvr_snat' if enable_snat else 'dvr' - self.agent.conf.network_device_mtu = custom_mtu # We get the router info particular to a dvr router router_info = self.generate_dvr_router_info( enable_ha, enable_snat, extra_routes=True) + if use_port_mtu: + for key in ('_interfaces', '_snat_router_interfaces', + '_floatingip_agent_interfaces'): + for port in router_info[key]: + port['mtu'] = custom_mtu + router_info['gw_port']['mtu'] = custom_mtu + else: + self.agent.conf.network_device_mtu = custom_mtu # We need to mock the get_agent_gateway_port return value # because the whole L3PluginApi is mocked and we need the port @@ -1137,7 +1176,13 @@ # manage the router (create it, create namespaces, # attach interfaces, etc...) router = self.manage_router(self.agent, router_info) + if enable_ha: + device = router.router[l3_constants.INTERFACE_KEY][-1] + name = router.get_internal_device_name(device['id']) + self.assertEqual(custom_mtu, + ip_lib.IPDevice(name, router.ns_name).link.mtu) + ext_gateway_port = router_info['gw_port'] self.assertTrue(self._namespace_exists(router.ns_name)) self.assertTrue(self._metadata_proxy_exists(self.agent.conf, router)) self._assert_internal_devices(router) @@ -1154,7 +1199,7 @@ self._assert_extra_routes(router, namespace=snat_ns_name) self._delete_router(self.agent, router.router_id) - self._assert_interfaces_deleted_from_ovs() + self._assert_fip_namespace_deleted(ext_gateway_port) self._assert_router_does_not_exist(router) def generate_dvr_router_info(self, @@ -1358,7 +1403,7 @@ router1.router[l3_constants.FLOATINGIP_KEY] = [] self.manage_router(restarted_agent, router1.router) self._assert_dvr_snat_gateway(router1) - self.assertFalse(self._namespace_exists(fip_ns)) + self.assertTrue(self._namespace_exists(fip_ns)) def test_dvr_router_add_fips_on_restarted_agent(self): self.agent.conf.agent_mode = 'dvr' @@ -1517,29 +1562,11 @@ self.assertFalse(sg_device) self.assertTrue(qg_device) - def test_dvr_router_calls_delete_agent_gateway_if_last_fip(self): - """Test to validate delete fip if it is last fip managed by agent.""" - self.agent.conf.agent_mode = 'dvr_snat' - router_info = self.generate_dvr_router_info(enable_snat=True) - router1 = self.manage_router(self.agent, router_info) - floating_agent_gw_port = ( - router1.router[l3_constants.FLOATINGIP_AGENT_INTF_KEY]) - self.assertTrue(floating_agent_gw_port) - fip_ns = router1.fip_ns.get_name() - router1.fip_ns.agent_gw_port = floating_agent_gw_port - self.assertTrue(self._namespace_exists(router1.ns_name)) - self.assertTrue(self._namespace_exists(fip_ns)) - self._assert_dvr_floating_ips(router1) - self._assert_dvr_snat_gateway(router1) - router1.router[l3_constants.FLOATINGIP_KEY] = [] - rpc_mock = mock.patch.object( - self.agent.plugin_rpc, 'delete_agent_gateway_port').start() - self.agent._process_updated_router(router1.router) - self.assertTrue(rpc_mock.called) - rpc_mock.assert_called_once_with( - self.agent.context, - floating_agent_gw_port[0]['network_id']) - self.assertFalse(self._namespace_exists(fip_ns)) + def _assert_fip_namespace_deleted(self, ext_gateway_port): + ext_net_id = ext_gateway_port['network_id'] + self.agent.fipnamespace_delete_on_ext_net( + self.agent.context, ext_net_id) + self._assert_interfaces_deleted_from_ovs() def test_dvr_router_static_routes(self): """Test to validate the extra routes on dvr routers.""" diff -Nru neutron-7.0.4/neutron/tests/functional/agent/test_ovs_flows.py neutron-7.1.1/neutron/tests/functional/agent/test_ovs_flows.py --- neutron-7.0.4/neutron/tests/functional/agent/test_ovs_flows.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/agent/test_ovs_flows.py 2016-06-10 01:43:08.000000000 +0000 @@ -142,6 +142,17 @@ self.dst_p.addr.add('%s/24' % self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr, count=2) + def test_mac_spoof_blocks_wrong_mac(self): + self._setup_arp_spoof_for_port(self.src_p.name, [self.src_addr]) + self._setup_arp_spoof_for_port(self.dst_p.name, [self.dst_addr]) + self.src_p.addr.add('%s/24' % self.src_addr) + self.dst_p.addr.add('%s/24' % self.dst_addr) + net_helpers.assert_ping(self.src_namespace, self.dst_addr, count=2) + # changing the allowed mac should stop the port from working + self._setup_arp_spoof_for_port(self.src_p.name, [self.src_addr], + mac='00:11:22:33:44:55') + net_helpers.assert_no_ping(self.src_namespace, self.dst_addr, count=2) + def test_arp_spoof_doesnt_block_ipv6(self): self.src_addr = '2000::1' self.dst_addr = '2000::2' @@ -239,7 +250,7 @@ net_helpers.assert_ping(self.src_namespace, self.dst_addr, count=2) def _setup_arp_spoof_for_port(self, port, addrs, psec=True, - device_owner='nobody'): + device_owner='nobody', mac=None): vif = next( vif for vif in self.br.get_vif_ports() if vif.port_name == port) ip_addr = addrs.pop() @@ -248,6 +259,8 @@ 'device_owner': device_owner, 'allowed_address_pairs': [ dict(ip_address=ip) for ip in addrs]} + if mac: + vif.vif_mac = mac ovsagt.OVSNeutronAgent.setup_arp_spoofing_protection( self.br_int, vif, details) diff -Nru neutron-7.0.4/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py neutron-7.1.1/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py --- neutron-7.0.4/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py 2016-06-10 01:43:08.000000000 +0000 @@ -16,11 +16,15 @@ from neutron.api.v2 import attributes from neutron.common import constants as l3_const +from neutron import context from neutron.extensions import external_net from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import base as ml2_test_base +DEVICE_OWNER_COMPUTE = l3_const.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' + + class L3DvrTestCase(ml2_test_base.ML2TestFramework): def setUp(self): super(L3DvrTestCase, self).setUp() @@ -276,7 +280,7 @@ l3_notifier.routers_updated_on_host.assert_called_once_with( self.context, {router['id']}, HOST2) l3_notifier.router_removed_from_agent.assert_called_once_with( - self.context, router['id'], HOST1) + mock.ANY, router['id'], HOST1) def _test_create_floating_ip_agent_notification(self, dvr=True): with self.subnet() as ext_subnet,\ @@ -446,3 +450,131 @@ def test_delete_floating_ip_agent_notification_non_dvr(self): self._test_delete_floating_ip_agent_notification(dvr=False) + + def _test_router_remove_from_agent_on_vm_port_deletion( + self, non_admin_port=False): + + # register l3 agent in dvr mode in addition to existing dvr_snat agent + HOST = 'host1' + non_admin_tenant = 'tenant1' + dvr_agent = helpers.register_l3_agent( + host=HOST, agent_mode=l3_const.L3_AGENT_MODE_DVR) + router = self._create_router() + with self.network(shared=True) as net, \ + self.subnet(network=net) as subnet, \ + self.port(subnet=subnet, + device_owner=DEVICE_OWNER_COMPUTE, + tenant_id=non_admin_tenant, + set_context=non_admin_port) as port: + self.core_plugin.update_port( + self.context, port['port']['id'], + {'port': {'binding:host_id': HOST}}) + self.l3_plugin.add_router_interface( + self.context, router['id'], + {'subnet_id': subnet['subnet']['id']}) + + # router should be scheduled to agent on HOST + agents = self.l3_plugin.list_l3_agents_hosting_router( + self.context, router['id']) + self.assertEqual(1, len(agents['agents'])) + self.assertEqual(dvr_agent['id'], agents['agents'][0]['id']) + + notifier = self.l3_plugin.agent_notifiers[ + l3_const.AGENT_TYPE_L3] + with mock.patch.object( + notifier, 'router_removed_from_agent') as remove_mock: + ctx = context.Context( + '', non_admin_tenant) if non_admin_port else self.context + self._delete('ports', port['port']['id'], neutron_context=ctx) + # now when port is deleted the router should be unscheduled + agents = self.l3_plugin.list_l3_agents_hosting_router( + self.context, router['id']) + self.assertEqual(0, len(agents['agents'])) + remove_mock.assert_called_once_with( + mock.ANY, router['id'], HOST) + + def test_router_remove_from_agent_on_vm_port_deletion(self): + self._test_router_remove_from_agent_on_vm_port_deletion() + + def test_admin_router_remove_from_agent_on_vm_port_deletion(self): + self._test_router_remove_from_agent_on_vm_port_deletion( + non_admin_port=True) + + def test_router_is_not_removed_from_snat_agent_on_interface_removal(self): + """Check that dvr router is not removed from l3 agent hosting + SNAT for it on router interface removal + """ + router = self._create_router() + kwargs = {'arg_list': (external_net.EXTERNAL,), + external_net.EXTERNAL: True} + host = self.l3_agent['host'] + with self.subnet() as subnet,\ + self.network(**kwargs) as ext_net,\ + self.subnet(network=ext_net, cidr='20.0.0.0/24'): + self.l3_plugin._update_router_gw_info( + self.context, router['id'], + {'network_id': ext_net['network']['id']}) + self.l3_plugin.add_router_interface( + self.context, router['id'], + {'subnet_id': subnet['subnet']['id']}) + + agents = self.l3_plugin.list_l3_agents_hosting_router( + self.context, router['id']) + self.assertEqual(1, len(agents['agents'])) + csnat_agent_host = self.l3_plugin.get_snat_bindings( + self.context, [router['id']])[0]['l3_agent']['host'] + self.assertEqual(host, csnat_agent_host) + with mock.patch.object(self.l3_plugin, + '_l3_rpc_notifier') as l3_notifier: + self.l3_plugin.remove_router_interface( + self.context, router['id'], + {'subnet_id': subnet['subnet']['id']}) + agents = self.l3_plugin.list_l3_agents_hosting_router( + self.context, router['id']) + self.assertEqual(1, len(agents['agents'])) + self.assertFalse(l3_notifier.router_removed_from_agent.called) + + def test_router_is_not_removed_from_snat_agent_on_dhcp_port_deletion(self): + """Check that dvr router is not removed from l3 agent hosting + SNAT for it on DHCP port removal + """ + router = self._create_router() + kwargs = {'arg_list': (external_net.EXTERNAL,), + external_net.EXTERNAL: True} + with self.network(**kwargs) as ext_net,\ + self.subnet(network=ext_net),\ + self.subnet(cidr='20.0.0.0/24') as subnet,\ + self.port(subnet=subnet, + device_owner=l3_const.DEVICE_OWNER_DHCP) as port: + self.core_plugin.update_port( + self.context, port['port']['id'], + {'port': {'binding:host_id': self.l3_agent['host']}}) + self.l3_plugin._update_router_gw_info( + self.context, router['id'], + {'network_id': ext_net['network']['id']}) + self.l3_plugin.add_router_interface( + self.context, router['id'], + {'subnet_id': subnet['subnet']['id']}) + + # router should be scheduled to the dvr_snat l3 agent + csnat_agent_host = self.l3_plugin.get_snat_bindings( + self.context, [router['id']])[0]['l3_agent']['host'] + self.assertEqual(self.l3_agent['host'], csnat_agent_host) + agents = self.l3_plugin.list_l3_agents_hosting_router( + self.context, router['id']) + self.assertEqual(1, len(agents['agents'])) + self.assertEqual(self.l3_agent['id'], agents['agents'][0]['id']) + + notifier = self.l3_plugin.agent_notifiers[ + l3_const.AGENT_TYPE_L3] + with mock.patch.object( + notifier, 'router_removed_from_agent') as remove_mock: + self._delete('ports', port['port']['id']) + # now when port is deleted the router still has external + # gateway and should still be scheduled to the snat agent + agents = self.l3_plugin.list_l3_agents_hosting_router( + self.context, router['id']) + self.assertEqual(1, len(agents['agents'])) + self.assertEqual(self.l3_agent['id'], + agents['agents'][0]['id']) + self.assertFalse(remove_mock.called) diff -Nru neutron-7.0.4/neutron/tests/unit/agent/common/test_ovs_lib.py neutron-7.1.1/neutron/tests/unit/agent/common/test_ovs_lib.py --- neutron-7.0.4/neutron/tests/unit/agent/common/test_ovs_lib.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/common/test_ovs_lib.py 2016-06-10 01:43:08.000000000 +0000 @@ -341,6 +341,21 @@ self.br.mod_flow, **params) + def test_run_ofctl_retry_on_socket_error(self): + err = RuntimeError('failed to connect to socket') + self.execute.side_effect = [err] * 5 + with mock.patch('time.sleep') as sleep: + self.br.run_ofctl('add-flows', []) + self.assertEqual(5, sleep.call_count) + self.assertEqual(6, self.execute.call_count) + # a regular exception fails right away + self.execute.side_effect = RuntimeError('garbage') + self.execute.reset_mock() + with mock.patch('time.sleep') as sleep: + self.br.run_ofctl('add-flows', []) + self.assertEqual(0, sleep.call_count) + self.assertEqual(1, self.execute.call_count) + def test_add_tunnel_port(self): pname = "tap99" local_ip = "1.1.1.1" diff -Nru neutron-7.0.4/neutron/tests/unit/agent/dhcp/test_agent.py neutron-7.1.1/neutron/tests/unit/agent/dhcp/test_agent.py --- neutron-7.0.4/neutron/tests/unit/agent/dhcp/test_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/dhcp/test_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -312,18 +312,16 @@ network = mock.Mock() network.id = '1' self.driver.return_value.foo.side_effect = exc or Exception - with mock.patch.object(dhcp_agent.LOG, trace_level) as log: - dhcp = dhcp_agent.DhcpAgent(HOSTNAME) - with mock.patch.object(dhcp, - 'schedule_resync') as schedule_resync: - self.assertIsNone(dhcp.call_driver('foo', network)) - self.driver.assert_called_once_with(cfg.CONF, - mock.ANY, - mock.ANY, - mock.ANY, - mock.ANY) - self.assertEqual(log.call_count, 1) - self.assertEqual(expected_sync, schedule_resync.called) + dhcp = dhcp_agent.DhcpAgent(HOSTNAME) + with mock.patch.object(dhcp, + 'schedule_resync') as schedule_resync: + self.assertIsNone(dhcp.call_driver('foo', network)) + self.driver.assert_called_once_with(cfg.CONF, + mock.ANY, + mock.ANY, + mock.ANY, + mock.ANY) + self.assertEqual(expected_sync, schedule_resync.called) def test_call_driver_ip_address_generation_failure(self): error = oslo_messaging.RemoteError( @@ -619,11 +617,14 @@ mock.call.get_network_info(network.id)]) self.call_driver.assert_called_once_with('enable', network) self.cache.assert_has_calls([mock.call.put(network)]) - if is_isolated_network: + if is_isolated_network and enable_isolated_metadata: self.external_process.assert_has_calls([ self._process_manager_constructor_call(), - mock.call().enable() - ]) + mock.call().enable()]) + elif not enable_isolated_metadata: + self.external_process.assert_has_calls([ + self._process_manager_constructor_call(ns=None), + mock.call().disable()]) else: self.assertFalse(self.external_process.call_count) @@ -704,13 +705,11 @@ def test_enable_dhcp_helper_network_none(self): self.plugin.get_network_info.return_value = None - with mock.patch.object(dhcp_agent.LOG, 'warn') as log: - self.dhcp.enable_dhcp_helper('fake_id') - self.plugin.assert_has_calls( - [mock.call.get_network_info('fake_id')]) - self.assertFalse(self.call_driver.called) - self.assertTrue(log.called) - self.assertFalse(self.dhcp.schedule_resync.called) + self.dhcp.enable_dhcp_helper('fake_id') + self.plugin.assert_has_calls( + [mock.call.get_network_info('fake_id')]) + self.assertFalse(self.call_driver.called) + self.assertFalse(self.dhcp.schedule_resync.called) def test_enable_dhcp_helper_exception_during_rpc(self): self.plugin.get_network_info.side_effect = Exception @@ -727,6 +726,7 @@ def test_enable_dhcp_helper_driver_failure(self): self.plugin.get_network_info.return_value = fake_network self.call_driver.return_value = False + cfg.CONF.set_override('enable_isolated_metadata', True) self.dhcp.enable_dhcp_helper(fake_network.id) self.plugin.assert_has_calls( [mock.call.get_network_info(fake_network.id)]) @@ -889,6 +889,20 @@ self.assertTrue(log.called) self.assertTrue(self.dhcp.schedule_resync.called) + def test_subnet_create_restarts_with_dhcp_disabled(self): + payload = dict(subnet=dhcp.DictModel( + dict(network_id=fake_network.id, enable_dhcp=False, + cidr='99.99.99.0/24'))) + self.cache.get_network_by_id.return_value = fake_network + new_net = copy.deepcopy(fake_network) + new_net.subnets.append(payload['subnet']) + self.plugin.get_network_info.return_value = new_net + + self.dhcp.subnet_create_end(None, payload) + + self.cache.assert_has_calls([mock.call.put(new_net)]) + self.call_driver.assert_called_once_with('restart', new_net) + def test_subnet_update_end(self): payload = dict(subnet=dict(network_id=fake_network.id)) self.cache.get_network_by_id.return_value = fake_network @@ -1291,7 +1305,8 @@ port.id, 'tap12345678-12', 'aa:bb:cc:dd:ee:ff', - namespace=net.namespace)) + namespace=net.namespace, + mtu=None)) self.mock_driver.assert_has_calls(expected) dh._set_default_route.assert_called_once_with(net, 'tap12345678-12') @@ -1304,7 +1319,7 @@ def test_setup_calls_fill_dhcp_udp_checksums(self): self._test_setup_helper(False) - rule = ('-p udp --dport %d -j CHECKSUM --checksum-fill' + rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' % const.DHCP_RESPONSE_PORT) expected = [mock.call.add_rule('POSTROUTING', rule)] self.mangle_inst.assert_has_calls(expected) diff -Nru neutron-7.0.4/neutron/tests/unit/agent/l3/test_agent.py neutron-7.1.1/neutron/tests/unit/agent/l3/test_agent.py --- neutron-7.0.4/neutron/tests/unit/agent/l3/test_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/l3/test_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -74,11 +74,15 @@ agent_config.register_process_monitor_opts(self.conf) self.conf.register_opts(interface.OPTS) self.conf.register_opts(external_process.OPTS) + self.conf.register_opts(pd.OPTS) + self.conf.register_opts(ra.OPTS) self.conf.set_override('router_id', 'fake_id') self.conf.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') self.conf.set_override('send_arp_for_ha', 1) self.conf.set_override('state_path', '') + self.conf.set_override('ra_confs', '/tmp') + self.conf.set_override('pd_dhcp_driver', '') self.device_exists_p = mock.patch( 'neutron.agent.linux.ip_lib.device_exists') @@ -169,7 +173,8 @@ ri.radvd = ra.DaemonMonitor(router['id'], ri.ns_name, agent.process_monitor, - ri.get_internal_device_name) + ri.get_internal_device_name, + self.conf) ri.process(agent) @@ -380,7 +385,8 @@ sn_port['fixed_ips'], sn_port['mac_address'], ri._get_snat_int_device_name(sn_port['id']), - dvr_snat_ns.SNAT_INT_DEV_PREFIX) + dvr_snat_ns.SNAT_INT_DEV_PREFIX, + mtu=None) elif action == 'remove': self.device_exists.return_value = False ri.get_snat_port_for_internal_port = mock.Mock( @@ -1232,7 +1238,7 @@ return expected_calls def _process_router_ipv6_subnet_added( - self, router, ipv6_subnet_modes=None): + self, router, ipv6_subnet_modes=None, network_mtu=0): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) agent.external_gateway_added = mock.Mock() @@ -1243,7 +1249,8 @@ router, count=len(ipv6_subnet_modes), ip_version=6, - ipv6_subnet_modes=ipv6_subnet_modes) + ipv6_subnet_modes=ipv6_subnet_modes, + network_mtu=network_mtu) # Reassign the router object to RouterInfo self._process_router_instance_for_agent(agent, ri, router) return ri @@ -1263,8 +1270,7 @@ ri = self._process_router_ipv6_interface_added(router) self._assert_ri_process_enabled(ri) # Expect radvd configured without prefix - self.assertNotIn('prefix', - self.utils_replace_file.call_args[0][1].split()) + self.assertNotIn('prefix', self.utils_replace_file.call_args[0][1]) def test_process_router_ipv6_slaac_interface_added(self): router = l3_test_common.prepare_router_data() @@ -1272,8 +1278,19 @@ router, ra_mode=l3_constants.IPV6_SLAAC) self._assert_ri_process_enabled(ri) # Expect radvd configured with prefix - self.assertIn('prefix', - self.utils_replace_file.call_args[0][1].split()) + radvd_config_str = self.utils_replace_file.call_args[0][1] + self.assertIn('prefix', radvd_config_str) + self.assertIn('AdvAutonomous on', radvd_config_str) + + def test_process_router_ipv6_dhcpv6_stateful_interface_added(self): + router = l3_test_common.prepare_router_data() + ri = self._process_router_ipv6_interface_added( + router, ra_mode=l3_constants.DHCPV6_STATEFUL) + self._assert_ri_process_enabled(ri) + # Expect radvd configured with prefix + radvd_config_str = self.utils_replace_file.call_args[0][1] + self.assertIn('prefix', radvd_config_str) + self.assertIn('AdvAutonomous off', radvd_config_str) def test_process_router_ipv6_subnets_added(self): router = l3_test_common.prepare_router_data() @@ -1285,11 +1302,13 @@ {'ra_mode': l3_constants.DHCPV6_STATEFUL, 'address_mode': l3_constants.DHCPV6_STATEFUL}]) self._assert_ri_process_enabled(ri) - radvd_config = self.utils_replace_file.call_args[0][1].split() + radvd_config_str = self.utils_replace_file.call_args[0][1] # Assert we have a prefix from IPV6_SLAAC and a prefix from # DHCPV6_STATELESS on one interface - self.assertEqual(2, radvd_config.count("prefix")) - self.assertEqual(1, radvd_config.count("interface")) + self.assertEqual(3, radvd_config_str.count("prefix")) + self.assertEqual(1, radvd_config_str.count("interface")) + self.assertEqual(2, radvd_config_str.count("AdvAutonomous on")) + self.assertEqual(1, radvd_config_str.count("AdvAutonomous off")) def test_process_router_ipv6_subnets_added_to_existing_port(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) @@ -2211,7 +2230,8 @@ router['id'], namespaces.RouterNamespace._get_ns_name(router['id']), agent.process_monitor, - l3_test_common.FakeDev) + l3_test_common.FakeDev, + self.conf) radvd.enable(router['_interfaces']) cmd = execute.call_args[0][0] @@ -2225,6 +2245,24 @@ self.assertIn(_join('-p', pidfile), cmd) self.assertIn(_join('-m', 'syslog'), cmd) + def test_generate_radvd_mtu_conf(self): + router = l3_test_common.prepare_router_data() + ipv6_subnet_modes = [{'ra_mode': l3_constants.IPV6_SLAAC, + 'address_mode': l3_constants.IPV6_SLAAC}] + network_mtu = '1446' + ri = self._process_router_ipv6_subnet_added(router, + ipv6_subnet_modes, + network_mtu) + expected = "AdvLinkMTU 1446" + ri.agent_conf.set_override('advertise_mtu', False) + ri.radvd._generate_radvd_conf(router[l3_constants.INTERFACE_KEY]) + self.assertNotIn(expected, self.utils_replace_file.call_args[0][1]) + + # Verify that MTU is advertised when advertise_mtu is True + ri.agent_conf.set_override('advertise_mtu', True) + ri.radvd._generate_radvd_conf(router[l3_constants.INTERFACE_KEY]) + self.assertIn(expected, self.utils_replace_file.call_args[0][1]) + def test_generate_radvd_conf_other_and_managed_flag(self): # expected = {ra_mode: (AdvOtherConfigFlag, AdvManagedFlag), ...} expected = {l3_constants.IPV6_SLAAC: (False, False), @@ -2289,7 +2327,8 @@ ri.radvd = ra.DaemonMonitor(router['id'], ri.ns_name, agent.process_monitor, - ri.get_internal_device_name) + ri.get_internal_device_name, + self.conf) return agent, router, ri def _pd_remove_gw_interface(self, intfs, agent, router, ri): diff -Nru neutron-7.0.4/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py neutron-7.1.1/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py --- neutron-7.0.4/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py 2016-06-10 01:43:08.000000000 +0000 @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy import mock from oslo_utils import uuidutils @@ -69,65 +70,94 @@ self.assertNotIn('20.0.0.30', self.fip_ns._rule_priorities.allocations) self.assertIn(pr, self.fip_ns._rule_priorities.pool) - @mock.patch.object(ip_lib, 'IPWrapper') - @mock.patch.object(ip_lib, 'IPDevice') - @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') - @mock.patch.object(ip_lib, 'device_exists') - def test_gateway_added(self, device_exists, send_adv_notif, - IPDevice, IPWrapper): - subnet_id = _uuid() + def _get_agent_gw_port(self): + v4_subnet_id = _uuid() + v6_subnet_id = _uuid() agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', 'prefixlen': 24, - 'subnet_id': subnet_id}], - 'subnets': [{'id': subnet_id, + 'subnet_id': v4_subnet_id}, + {'ip_address': 'cafe:dead:beef::3', + 'prefixlen': 64, + 'subnet_id': v6_subnet_id}], + 'subnets': [{'id': v4_subnet_id, 'cidr': '20.0.0.0/24', - 'gateway_ip': '20.0.0.1'}], + 'gateway_ip': '20.0.0.1'}, + {'id': v6_subnet_id, + 'cidr': 'cafe:dead:beef::/64', + 'gateway_ip': 'cafe:dead:beef::1'}], 'id': _uuid(), 'network_id': self.net_id, 'mac_address': 'ca:fe:de:ad:be:ef'} - - device_exists.return_value = False - self.fip_ns._gateway_added(agent_gw_port, - mock.sentinel.interface_name) - self.assertEqual(self.driver.plug.call_count, 1) - self.assertEqual(self.driver.init_l3.call_count, 1) - send_adv_notif.assert_called_once_with(self.fip_ns.get_name(), - mock.sentinel.interface_name, - '20.0.0.30', - mock.ANY) + return agent_gw_port @mock.patch.object(ip_lib, 'IPWrapper') - @mock.patch.object(ip_lib, 'IPDevice') - @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') @mock.patch.object(ip_lib, 'device_exists') - def test_gateway_outside_subnet_added(self, device_exists, send_adv_notif, - IPDevice, IPWrapper): - device = mock.Mock() - IPDevice.return_value = device - - subnet_id = _uuid() - agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', - 'prefixlen': 24, - 'subnet_id': subnet_id}], - 'subnets': [{'id': subnet_id, - 'cidr': '20.0.0.0/24', - 'gateway_ip': '20.0.1.1'}], - 'id': _uuid(), - 'network_id': self.net_id, - 'mac_address': 'ca:fe:de:ad:be:ef'} + def test_gateway_added(self, device_exists, ip_wrapper): + agent_gw_port = self._get_agent_gw_port() device_exists.return_value = False + self.fip_ns.update_gateway_port = mock.Mock() self.fip_ns._gateway_added(agent_gw_port, mock.sentinel.interface_name) self.assertEqual(1, self.driver.plug.call_count) self.assertEqual(1, self.driver.init_l3.call_count) - send_adv_notif.assert_called_once_with(self.fip_ns.get_name(), - mock.sentinel.interface_name, - '20.0.0.30', - mock.ANY) - device.route.add_route.assert_called_once_with('20.0.1.1', - scope='link') - device.route.add_gateway.assert_called_once_with('20.0.1.1') + self.fip_ns.update_gateway_port.assert_called_once_with(agent_gw_port) + + @mock.patch.object(ip_lib, 'IPDevice') + @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') + def test_update_gateway_port(self, send_adv_notif, IPDevice): + self.fip_ns._check_for_gateway_ip_change = mock.Mock(return_value=True) + self.fip_ns.agent_gateway_port = None + agent_gw_port = self._get_agent_gw_port() + self.fip_ns.update_gateway_port(agent_gw_port) + expected = [ + mock.call(self.fip_ns.get_name(), + self.fip_ns.get_ext_device_name(agent_gw_port['id']), + agent_gw_port['fixed_ips'][0]['ip_address'], + mock.ANY), + mock.call(self.fip_ns.get_name(), + self.fip_ns.get_ext_device_name(agent_gw_port['id']), + agent_gw_port['fixed_ips'][1]['ip_address'], + mock.ANY)] + send_adv_notif.assert_has_calls(expected) + gw_ipv4 = agent_gw_port['subnets'][0]['gateway_ip'] + gw_ipv6 = agent_gw_port['subnets'][1]['gateway_ip'] + expected = [mock.call(gw_ipv4), mock.call(gw_ipv6)] + IPDevice().route.add_gateway.assert_has_calls(expected) + + @mock.patch.object(ip_lib, 'IPDevice') + @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') + def test_update_gateway_port_gateway_outside_subnet_added( + self, send_adv_notif, IPDevice): + self.fip_ns.agent_gateway_port = None + agent_gw_port = self._get_agent_gw_port() + agent_gw_port['subnets'][0]['gateway_ip'] = '20.0.1.1' + + self.fip_ns.update_gateway_port(agent_gw_port) + + IPDevice().route.add_route.assert_called_once_with('20.0.1.1', + scope='link') + + def test_check_gateway_ip_changed_no_change(self): + agent_gw_port = self._get_agent_gw_port() + self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port) + agent_gw_port['mac_address'] = 'aa:bb:cc:dd:ee:ff' + self.assertFalse(self.fip_ns._check_for_gateway_ip_change( + agent_gw_port)) + + def test_check_gateway_ip_changed_v4(self): + agent_gw_port = self._get_agent_gw_port() + self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port) + agent_gw_port['subnets'][0]['gateway_ip'] = '20.0.0.2' + self.assertTrue(self.fip_ns._check_for_gateway_ip_change( + agent_gw_port)) + + def test_check_gateway_ip_changed_v6(self): + agent_gw_port = self._get_agent_gw_port() + self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port) + agent_gw_port['subnets'][1]['gateway_ip'] = 'cafe:dead:beef::2' + self.assertTrue(self.fip_ns._check_for_gateway_ip_change( + agent_gw_port)) @mock.patch.object(iptables_manager, 'IptablesManager') @mock.patch.object(utils, 'execute') diff -Nru neutron-7.0.4/neutron/tests/unit/agent/l3/test_dvr_local_router.py neutron-7.1.1/neutron/tests/unit/agent/l3/test_dvr_local_router.py --- neutron-7.0.4/neutron/tests/unit/agent/l3/test_dvr_local_router.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/l3/test_dvr_local_router.py 2016-06-10 01:43:08.000000000 +0000 @@ -157,6 +157,19 @@ mock.Mock(), **kwargs) + def test_create_dvr_fip_interfaces_update(self): + ri = self._create_router() + fip_agent_port = {'subnets': []} + ri.get_floating_agent_gw_interface = mock.Mock( + return_value=fip_agent_port) + ri.get_floating_ips = mock.Mock(return_value=True) + ri.fip_ns = mock.Mock() + ri.fip_ns.subscribe.return_value = False + ex_gw_port = {'network_id': 'fake_net_id'} + ri.create_dvr_fip_interfaces(ex_gw_port) + ri.fip_ns.update_gateway_port.assert_called_once_with( + fip_agent_port) + def test_get_floating_ips_dvr(self): router = mock.MagicMock() router.get.return_value = [{'host': HOSTNAME}, @@ -259,18 +272,14 @@ ri.fip_ns.local_subnets.allocate.return_value = s1 _, fip_to_rtr = s1.get_pair() fip_ns = ri.fip_ns - with mock.patch.object(self.plugin_api, - 'delete_agent_gateway_port') as del_fip_gw: - ri.floating_ip_removed_dist(fip_cidr) - self.assertTrue(del_fip_gw.called) - self.assertTrue(fip_ns.destroyed) - mIPWrapper().del_veth.assert_called_once_with( - fip_ns.get_int_device_name(router['id'])) - mIPDevice().route.delete_gateway.assert_called_once_with( - str(fip_to_rtr.ip), table=16) - fip_ns.unsubscribe.assert_called_once_with(ri.router_id) - fip_ns.local_subnets.allocate.assert_called_once_with( - ri.router_id) + ri.floating_ip_removed_dist(fip_cidr) + self.assertTrue(fip_ns.destroyed) + mIPWrapper().del_veth.assert_called_once_with( + fip_ns.get_int_device_name(router['id'])) + mIPDevice().route.delete_gateway.assert_called_once_with( + str(fip_to_rtr.ip), table=16) + fip_ns.unsubscribe.assert_called_once_with(ri.router_id) + fip_ns.local_subnets.allocate.assert_called_once_with(ri.router_id) def _test_add_floating_ip(self, ri, fip, is_failure): ri._add_fip_addr_to_device = mock.Mock(return_value=is_failure) diff -Nru neutron-7.0.4/neutron/tests/unit/agent/linux/test_interface.py neutron-7.1.1/neutron/tests/unit/agent/linux/test_interface.py --- neutron-7.0.4/neutron/tests/unit/agent/linux/test_interface.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/linux/test_interface.py 2016-06-10 01:43:08.000000000 +0000 @@ -56,6 +56,23 @@ network_id = network.id +class FakeInterfaceDriverNoMtu(interface.LinuxInterfaceDriver): + # NOTE(ihrachys) this method intentially omit mtu= parameter, since that + # was the method signature before Mitaka. We should make sure the old + # signature still works. + + def __init__(self, *args, **kwargs): + super(FakeInterfaceDriverNoMtu, self).__init__(*args, **kwargs) + self.plug_called = False + + def plug_new(self, network_id, port_id, device_name, mac_address, + bridge=None, namespace=None, prefix=None): + self.plug_called = True + + def unplug(self, device_name, bridge=None, namespace=None, prefix=None): + pass + + class TestBase(base.BaseTestCase): def setUp(self): super(TestBase, self).setUp() @@ -69,6 +86,16 @@ self.device_exists = self.device_exists_p.start() +class TestABCDriverNoMtu(TestBase): + + def test_plug_with_no_mtu_works(self): + driver = FakeInterfaceDriverNoMtu(self.conf) + self.device_exists.return_value = False + driver.plug( + mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock(), mtu=9000) + self.assertTrue(driver.plug_called) + + class TestABCDriver(TestBase): def setUp(self): super(TestABCDriver, self).setUp() diff -Nru neutron-7.0.4/neutron/tests/unit/agent/linux/test_ip_lib.py neutron-7.1.1/neutron/tests/unit/agent/linux/test_ip_lib.py --- neutron-7.0.4/neutron/tests/unit/agent/linux/test_ip_lib.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/linux/test_ip_lib.py 2016-06-10 01:43:08.000000000 +0000 @@ -435,6 +435,17 @@ self.assertNotIn(mock.call().delete('ns'), ip_ns_cmd_cls.mock_calls) + def test_add_vlan(self): + retval = ip_lib.IPWrapper().add_vlan('eth0.1', 'eth0', '1') + self.assertIsInstance(retval, ip_lib.IPDevice) + self.assertEqual(retval.name, 'eth0.1') + self.execute.assert_called_once_with([], 'link', + ['add', 'link', 'eth0', + 'name', 'eth0.1', + 'type', 'vlan', 'id', '1'], + run_as_root=True, namespace=None, + log_fail_as_error=True) + def test_add_vxlan_valid_port_length(self): retval = ip_lib.IPWrapper().add_vxlan('vxlan0', 'vni0', group='group0', diff -Nru neutron-7.0.4/neutron/tests/unit/agent/linux/test_iptables_firewall.py neutron-7.1.1/neutron/tests/unit/agent/linux/test_iptables_firewall.py --- neutron-7.0.4/neutron/tests/unit/agent/linux/test_iptables_firewall.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/linux/test_iptables_firewall.py 2016-06-10 01:43:08.000000000 +0000 @@ -158,12 +158,17 @@ comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', - '-p udp -m udp --sport 68 -m udp --dport 67 -j RETURN', + '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' + '--sport 68 --dport 67 -j RETURN', comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', comment=None), mock.call.add_rule( 'ofake_dev', + '-p udp -m udp --sport 68 --dport 67 -j RETURN', + comment=None), + mock.call.add_rule( + 'ofake_dev', '-p udp -m udp --sport 67 -m udp --dport 68 -j DROP', comment=None), mock.call.add_rule( @@ -940,23 +945,28 @@ filter_inst = self.v4filter_inst dhcp_rule = [mock.call.add_rule( 'ofake_dev', - '-p udp -m udp --sport 68 -m udp --dport 67 -j RETURN', + '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' + '--sport 68 --dport 67 -j RETURN', comment=None)] if ethertype == 'IPv6': filter_inst = self.v6filter_inst - dhcp_rule = [mock.call.add_rule('ofake_dev', '-p ipv6-icmp ' - '-m icmp6 ' - '--icmpv6-type %s -j DROP' - % constants.ICMPV6_TYPE_RA, + dhcp_rule = [mock.call.add_rule('ofake_dev', + '-s ::/128 -d ff02::/16 ' + '-p ipv6-icmp -m icmp6 ' + '--icmpv6-type 131 -j RETURN', comment=None), mock.call.add_rule('ofake_dev', - '-p ipv6-icmp -j RETURN', + '-s ::/128 -d ff02::/16 ' + '-p ipv6-icmp -m icmp6 ' + '--icmpv6-type 135 -j RETURN', comment=None), - mock.call.add_rule('ofake_dev', '-p udp -m udp ' - '--sport 546 -m udp --dport 547 ' - '-j RETURN', comment=None)] + mock.call.add_rule('ofake_dev', + '-s ::/128 -d ff02::/16 ' + '-p ipv6-icmp -m icmp6 ' + '--icmpv6-type 143 -j RETURN', + comment=None)] sg = [rule] port['security_group_rules'] = sg self.firewall.prepare_port_filter(port) @@ -1019,19 +1029,39 @@ 'sfake_dev', '-s %s -m mac --mac-source FF:FF:FF:FF:FF:FF -j RETURN' % prefix, - comment=ic.PAIR_ALLOW), - mock.call.add_rule( - 'sfake_dev', '-j DROP', - comment=ic.PAIR_DROP)] + comment=ic.PAIR_ALLOW)] + + if ethertype == 'IPv6': + calls.append(mock.call.add_rule('sfake_dev', + '-s fe80::fdff:ffff:feff:ffff/128 -m mac ' + '--mac-source FF:FF:FF:FF:FF:FF -j RETURN', + comment=ic.PAIR_ALLOW)) + calls.append(mock.call.add_rule('sfake_dev', '-j DROP', + comment=ic.PAIR_DROP)) calls += dhcp_rule calls.append(mock.call.add_rule('ofake_dev', '-j $sfake_dev', comment=None)) if ethertype == 'IPv4': calls.append(mock.call.add_rule( 'ofake_dev', + '-p udp -m udp --sport 68 --dport 67 -j RETURN', + comment=None)) + calls.append(mock.call.add_rule( + 'ofake_dev', '-p udp -m udp --sport 67 -m udp --dport 68 -j DROP', comment=None)) if ethertype == 'IPv6': + calls.append(mock.call.add_rule('ofake_dev', + '-p ipv6-icmp -m icmp6 ' + '--icmpv6-type %s -j DROP' % + constants.ICMPV6_TYPE_RA, + comment=None)) + calls.append(mock.call.add_rule('ofake_dev', + '-p ipv6-icmp -j RETURN', + comment=None)) + calls.append(mock.call.add_rule('ofake_dev', '-p udp -m udp ' + '--sport 546 -m udp --dport 547 ' + '-j RETURN', comment=None)) calls.append(mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 547 -m udp --dport 546 -j DROP', @@ -1195,12 +1225,17 @@ comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', - '-p udp -m udp --sport 68 -m udp --dport 67 -j RETURN', + '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' + '--sport 68 --dport 67 -j RETURN', comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', comment=None), mock.call.add_rule( 'ofake_dev', + '-p udp -m udp --sport 68 --dport 67 -j RETURN', + comment=None), + mock.call.add_rule( + 'ofake_dev', '-p udp -m udp --sport 67 -m udp --dport 68 -j DROP', comment=None), mock.call.add_rule( @@ -1267,12 +1302,17 @@ comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', - '-p udp -m udp --sport 68 -m udp --dport 67 -j RETURN', + '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' + '--sport 68 --dport 67 -j RETURN', comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', comment=None), mock.call.add_rule( 'ofake_dev', + '-p udp -m udp --sport 68 --dport 67 -j RETURN', + comment=None), + mock.call.add_rule( + 'ofake_dev', '-p udp -m udp --sport 67 -m udp --dport 68 -j DROP', comment=None), mock.call.add_rule( @@ -1442,12 +1482,17 @@ comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', - '-p udp -m udp --sport 68 -m udp --dport 67 -j RETURN', + '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' + '--sport 68 --dport 67 -j RETURN', comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', comment=None), mock.call.add_rule( 'ofake_dev', + '-p udp -m udp --sport 68 --dport 67 -j RETURN', + comment=None), + mock.call.add_rule( + 'ofake_dev', '-p udp -m udp --sport 67 -m udp --dport 68 -j DROP', comment=None), mock.call.add_rule( @@ -1516,12 +1561,17 @@ comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', - '-p udp -m udp --sport 68 -m udp --dport 67 -j RETURN', + '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' + '--sport 68 --dport 67 -j RETURN', comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', comment=None), mock.call.add_rule( 'ofake_dev', + '-p udp -m udp --sport 68 --dport 67 -j RETURN', + comment=None), + mock.call.add_rule( + 'ofake_dev', '-p udp -m udp --sport 67 -m udp --dport 68 -j DROP', comment=None), mock.call.add_rule( @@ -1832,6 +1882,7 @@ fake_ipv4_pair.append((mac_unix, ipv4)) fake_ipv6_pair = [] fake_ipv6_pair.append((mac_unix, ipv6)) + fake_ipv6_pair.append((mac_unix, 'fe80::fdff:ffff:fe0f:ffff')) mac_ipv4_pairs = [] mac_ipv6_pairs = [] diff -Nru neutron-7.0.4/neutron/tests/unit/agent/metadata/test_driver.py neutron-7.1.1/neutron/tests/unit/agent/metadata/test_driver.py --- neutron-7.0.4/neutron/tests/unit/agent/metadata/test_driver.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/metadata/test_driver.py 2016-06-10 01:43:08.000000000 +0000 @@ -34,7 +34,7 @@ def test_metadata_nat_rules(self): rules = ('PREROUTING', '-d 169.254.169.254/32 -i qr-+ ' - '-p tcp -m tcp --dport 80 -j REDIRECT --to-port 8775') + '-p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8775') self.assertEqual( [rules], metadata_driver.MetadataDriver.metadata_nat_rules(8775)) diff -Nru neutron-7.0.4/neutron/tests/unit/agent/test_securitygroups_rpc.py neutron-7.1.1/neutron/tests/unit/agent/test_securitygroups_rpc.py --- neutron-7.0.4/neutron/tests/unit/agent/test_securitygroups_rpc.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/agent/test_securitygroups_rpc.py 2016-06-10 01:43:08.000000000 +0000 @@ -1810,14 +1810,15 @@ RETURN -I %(bn)s-i_port1 5 -m state --state INVALID -j DROP -I %(bn)s-i_port1 6 -j %(bn)s-sg-fallback --I %(bn)s-o_port1 1 -p udp -m udp --sport 68 -m udp --dport 67 \ --j RETURN +-I %(bn)s-o_port1 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 2 -j %(bn)s-s_port1 --I %(bn)s-o_port1 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_port1 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_port1 5 -j RETURN --I %(bn)s-o_port1 6 -m state --state INVALID -j DROP --I %(bn)s-o_port1 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_port1 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_port1 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_port1 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_port1 6 -j RETURN +-I %(bn)s-o_port1 7 -m state --state INVALID -j DROP +-I %(bn)s-o_port1 8 -j %(bn)s-sg-fallback -I %(bn)s-s_port1 1 -s 10.0.0.3/32 -m mac --mac-source 12:34:56:78:9A:BC \ -j RETURN -I %(bn)s-s_port1 2 -j DROP @@ -1864,14 +1865,15 @@ -I %(bn)s-i_port1 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_port1 4 -m state --state INVALID -j DROP -I %(bn)s-i_port1 5 -j %(bn)s-sg-fallback --I %(bn)s-o_port1 1 -p udp -m udp --sport 68 -m udp --dport 67 \ --j RETURN +-I %(bn)s-o_port1 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 2 -j %(bn)s-s_port1 --I %(bn)s-o_port1 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_port1 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_port1 5 -j RETURN --I %(bn)s-o_port1 6 -m state --state INVALID -j DROP --I %(bn)s-o_port1 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_port1 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_port1 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_port1 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_port1 6 -j RETURN +-I %(bn)s-o_port1 7 -m state --state INVALID -j DROP +-I %(bn)s-o_port1 8 -j %(bn)s-sg-fallback -I %(bn)s-s_port1 1 -s 10.0.0.3/32 -m mac --mac-source 12:34:56:78:9A:BC \ -j RETURN -I %(bn)s-s_port1 2 -j DROP @@ -1920,14 +1922,15 @@ -I %(bn)s-i_port1 4 -s 10.0.0.4/32 -j RETURN -I %(bn)s-i_port1 5 -m state --state INVALID -j DROP -I %(bn)s-i_port1 6 -j %(bn)s-sg-fallback --I %(bn)s-o_port1 1 -p udp -m udp --sport 68 -m udp --dport 67 \ --j RETURN +-I %(bn)s-o_port1 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 2 -j %(bn)s-s_port1 --I %(bn)s-o_port1 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_port1 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_port1 5 -j RETURN --I %(bn)s-o_port1 6 -m state --state INVALID -j DROP --I %(bn)s-o_port1 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_port1 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_port1 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_port1 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_port1 6 -j RETURN +-I %(bn)s-o_port1 7 -m state --state INVALID -j DROP +-I %(bn)s-o_port1 8 -j %(bn)s-sg-fallback -I %(bn)s-s_port1 1 -s 10.0.0.3/32 -m mac --mac-source 12:34:56:78:9A:BC \ -j RETURN -I %(bn)s-s_port1 2 -j DROP @@ -1993,20 +1996,24 @@ -I %(bn)s-i_%(port2)s 4 -m set --match-set NIPv4security_group1 src -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port1)s 1 -p udp -m udp --sport 68 -m udp --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s --I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port1)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port1)s 5 -j RETURN --I %(bn)s-o_%(port1)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port1)s 7 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port2)s 1 -p udp -m udp --sport 68 -m udp --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port1)s 6 -j RETURN +-I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s --I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port2)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port2)s 5 -j RETURN --I %(bn)s-o_%(port2)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port2)s 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port2)s 6 -j RETURN +-I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN @@ -2077,20 +2084,24 @@ -I %(bn)s-i_%(port2)s 5 -p icmp -j RETURN -I %(bn)s-i_%(port2)s 6 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 7 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port1)s 1 -p udp -m udp --sport 68 -m udp --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s --I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port1)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port1)s 5 -j RETURN --I %(bn)s-o_%(port1)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port1)s 7 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port2)s 1 -p udp -m udp --sport 68 -m udp --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port1)s 6 -j RETURN +-I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s --I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port2)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port2)s 5 -j RETURN --I %(bn)s-o_%(port2)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port2)s 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port2)s 6 -j RETURN +-I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN @@ -2159,22 +2170,24 @@ -I %(bn)s-i_%(port2)s 4 -s %(ip1)s -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port1)s 1 -p udp -m udp --sport 68 -m udp --dport 67 \ --j RETURN +-I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s --I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port1)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port1)s 5 -j RETURN --I %(bn)s-o_%(port1)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port1)s 7 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port2)s 1 -p udp -m udp --sport 68 -m udp --dport 67 \ --j RETURN +-I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port1)s 6 -j RETURN +-I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s --I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port2)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port2)s 5 -j RETURN --I %(bn)s-o_%(port2)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port2)s 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port2)s 6 -j RETURN +-I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN @@ -2242,20 +2255,24 @@ -I %(bn)s-i_%(port2)s 4 -s %(ip1)s -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port1)s 1 -p udp -m udp --sport 68 -m udp --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s --I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port1)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port1)s 5 -j RETURN --I %(bn)s-o_%(port1)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port1)s 7 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port2)s 1 -p udp -m udp --sport 68 -m udp --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port1)s 6 -j RETURN +-I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s --I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port2)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port2)s 5 -j RETURN --I %(bn)s-o_%(port2)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port2)s 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port2)s 6 -j RETURN +-I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN @@ -2326,20 +2343,24 @@ -I %(bn)s-i_%(port2)s 5 -p icmp -j RETURN -I %(bn)s-i_%(port2)s 6 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 7 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port1)s 1 -p udp -m udp --sport 68 -m udp --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s --I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port1)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port1)s 5 -j RETURN --I %(bn)s-o_%(port1)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port1)s 7 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port2)s 1 -p udp -m udp --sport 68 -m udp --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port1)s 6 -j RETURN +-I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ +--sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s --I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP --I %(bn)s-o_%(port2)s 4 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port2)s 5 -j RETURN --I %(bn)s-o_%(port2)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port2)s 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN +-I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 -m udp --dport 68 -j DROP +-I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port2)s 6 -j RETURN +-I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN @@ -2419,13 +2440,19 @@ -I %(bn)s-i_port1 6 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_port1 7 -m state --state INVALID -j DROP -I %(bn)s-i_port1 8 -j %(bn)s-sg-fallback --I %(bn)s-o_port1 1 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP --I %(bn)s-o_port1 2 -p ipv6-icmp -j RETURN --I %(bn)s-o_port1 3 -p udp -m udp --sport 546 -m udp --dport 547 -j RETURN --I %(bn)s-o_port1 4 -p udp -m udp --sport 547 -m udp --dport 546 -j DROP --I %(bn)s-o_port1 5 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_port1 6 -m state --state INVALID -j DROP --I %(bn)s-o_port1 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_port1 1 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 131 -j RETURN +-I %(bn)s-o_port1 2 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 135 -j RETURN +-I %(bn)s-o_port1 3 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 143 -j RETURN +-I %(bn)s-o_port1 4 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP +-I %(bn)s-o_port1 5 -p ipv6-icmp -j RETURN +-I %(bn)s-o_port1 6 -p udp -m udp --sport 546 -m udp --dport 547 -j RETURN +-I %(bn)s-o_port1 7 -p udp -m udp --sport 547 -m udp --dport 546 -j DROP +-I %(bn)s-o_port1 8 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_port1 9 -m state --state INVALID -j DROP +-I %(bn)s-o_port1 10 -j %(bn)s-sg-fallback -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-i_port1 -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ @@ -2489,20 +2516,32 @@ -I %(bn)s-i_%(port2)s 6 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 8 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port1)s 1 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP --I %(bn)s-o_%(port1)s 2 -p ipv6-icmp -j RETURN --I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 546 -m udp --dport 547 -j RETURN --I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 547 -m udp --dport 546 -j DROP --I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port1)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port1)s 7 -j %(bn)s-sg-fallback --I %(bn)s-o_%(port2)s 1 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP --I %(bn)s-o_%(port2)s 2 -p ipv6-icmp -j RETURN --I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 546 -m udp --dport 547 -j RETURN --I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 547 -m udp --dport 546 -j DROP --I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN --I %(bn)s-o_%(port2)s 6 -m state --state INVALID -j DROP --I %(bn)s-o_%(port2)s 7 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port1)s 1 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 131 -j RETURN +-I %(bn)s-o_%(port1)s 2 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 135 -j RETURN +-I %(bn)s-o_%(port1)s 3 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 143 -j RETURN +-I %(bn)s-o_%(port1)s 4 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP +-I %(bn)s-o_%(port1)s 5 -p ipv6-icmp -j RETURN +-I %(bn)s-o_%(port1)s 6 -p udp -m udp --sport 546 -m udp --dport 547 -j RETURN +-I %(bn)s-o_%(port1)s 7 -p udp -m udp --sport 547 -m udp --dport 546 -j DROP +-I %(bn)s-o_%(port1)s 8 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port1)s 9 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port1)s 10 -j %(bn)s-sg-fallback +-I %(bn)s-o_%(port2)s 1 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 131 -j RETURN +-I %(bn)s-o_%(port2)s 2 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 135 -j RETURN +-I %(bn)s-o_%(port2)s 3 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ +--icmpv6-type 143 -j RETURN +-I %(bn)s-o_%(port2)s 4 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP +-I %(bn)s-o_%(port2)s 5 -p ipv6-icmp -j RETURN +-I %(bn)s-o_%(port2)s 6 -p udp -m udp --sport 546 -m udp --dport 547 -j RETURN +-I %(bn)s-o_%(port2)s 7 -p udp -m udp --sport 547 -m udp --dport 546 -j DROP +-I %(bn)s-o_%(port2)s 8 -m state --state RELATED,ESTABLISHED -j RETURN +-I %(bn)s-o_%(port2)s 9 -m state --state INVALID -j DROP +-I %(bn)s-o_%(port2)s 10 -j %(bn)s-sg-fallback -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ diff -Nru neutron-7.0.4/neutron/tests/unit/api/rpc/handlers/test_dhcp_rpc.py neutron-7.1.1/neutron/tests/unit/api/rpc/handlers/test_dhcp_rpc.py --- neutron-7.0.4/neutron/tests/unit/api/rpc/handlers/test_dhcp_rpc.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/api/rpc/handlers/test_dhcp_rpc.py 2016-06-10 01:43:08.000000000 +0000 @@ -66,7 +66,7 @@ plugin_retval = [{'id': 'a'}, {'id': 'b'}] self.plugin.get_networks.return_value = plugin_retval port = {'network_id': 'a'} - subnet = {'network_id': 'b'} + subnet = {'network_id': 'b', 'id': 'c'} self.plugin.get_ports.return_value = [port] self.plugin.get_subnets.return_value = [subnet] networks = self.callbacks.get_active_networks_info(mock.Mock(), @@ -150,7 +150,7 @@ def test_get_network_info(self): network_retval = dict(id='a') - subnet_retval = mock.Mock() + subnet_retval = [dict(id='a'), dict(id='c'), dict(id='b')] port_retval = mock.Mock() self.plugin.get_network.return_value = network_retval @@ -159,7 +159,8 @@ retval = self.callbacks.get_network_info(mock.Mock(), network_id='a') self.assertEqual(retval, network_retval) - self.assertEqual(retval['subnets'], subnet_retval) + sorted_subnet_retval = [dict(id='a'), dict(id='b'), dict(id='c')] + self.assertEqual(retval['subnets'], sorted_subnet_retval) self.assertEqual(retval['ports'], port_retval) def test_update_dhcp_port_verify_port_action_port_dict(self): diff -Nru neutron-7.0.4/neutron/tests/unit/common/test_rpc.py neutron-7.1.1/neutron/tests/unit/common/test_rpc.py --- neutron-7.0.4/neutron/tests/unit/common/test_rpc.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/common/test_rpc.py 2016-06-10 01:43:08.000000000 +0000 @@ -20,6 +20,7 @@ from oslo_config import cfg import oslo_messaging as messaging from oslo_messaging import conffixture as messaging_conffixture +import testtools from neutron.common import rpc from neutron import context @@ -117,7 +118,7 @@ self.assertEqual(['foo', 'bar'], exmods) @mock.patch.object(rpc, 'RequestContextSerializer') - @mock.patch.object(messaging, 'RPCClient') + @mock.patch.object(rpc, 'BackingOffClient') def test_get_client(self, mock_client, mock_ser): rpc.TRANSPORT = mock.Mock() tgt = mock.Mock() @@ -281,6 +282,132 @@ rpc_server.wait.assert_called_once_with() +class TimeoutTestCase(base.DietTestCase): + def setUp(self): + super(TimeoutTestCase, self).setUp() + + self.messaging_conf = messaging_conffixture.ConfFixture(CONF) + self.messaging_conf.transport_driver = 'fake' + self.messaging_conf.response_timeout = 0 + self.useFixture(self.messaging_conf) + + self.addCleanup(rpc.cleanup) + rpc.init(CONF) + rpc.TRANSPORT = mock.MagicMock() + rpc.TRANSPORT._send.side_effect = messaging.MessagingTimeout + target = messaging.Target(version='1.0', topic='testing') + self.client = rpc.get_client(target) + self.call_context = mock.Mock() + self.sleep = mock.patch('time.sleep').start() + rpc.TRANSPORT.conf.rpc_response_timeout = 10 + + def test_timeout_unaffected_when_explicitly_set(self): + rpc.TRANSPORT.conf.rpc_response_timeout = 5 + ctx = self.client.prepare(topic='sandwiches', timeout=77) + with testtools.ExpectedException(messaging.MessagingTimeout): + ctx.call(self.call_context, 'create_pb_and_j') + # ensure that the timeout was not increased and the back-off sleep + # wasn't called + self.assertEqual( + 5, rpc._ContextWrapper._METHOD_TIMEOUTS['create_pb_and_j']) + self.assertFalse(self.sleep.called) + + def test_timeout_store_defaults(self): + # any method should default to the configured timeout + self.assertEqual(rpc.TRANSPORT.conf.rpc_response_timeout, + rpc._ContextWrapper._METHOD_TIMEOUTS['method_1']) + self.assertEqual(rpc.TRANSPORT.conf.rpc_response_timeout, + rpc._ContextWrapper._METHOD_TIMEOUTS['method_2']) + # a change to an existing should not affect new or existing ones + rpc._ContextWrapper._METHOD_TIMEOUTS['method_2'] = 7000 + self.assertEqual(rpc.TRANSPORT.conf.rpc_response_timeout, + rpc._ContextWrapper._METHOD_TIMEOUTS['method_1']) + self.assertEqual(rpc.TRANSPORT.conf.rpc_response_timeout, + rpc._ContextWrapper._METHOD_TIMEOUTS['method_3']) + + def test_method_timeout_sleep(self): + rpc.TRANSPORT.conf.rpc_response_timeout = 2 + for i in range(100): + with testtools.ExpectedException(messaging.MessagingTimeout): + self.client.call(self.call_context, 'method_1') + # sleep value should always be between 0 and configured timeout + self.assertGreaterEqual(self.sleep.call_args_list[0][0][0], 0) + self.assertLessEqual(self.sleep.call_args_list[0][0][0], 2) + self.sleep.reset_mock() + + def test_method_timeout_increases_on_timeout_exception(self): + rpc._ContextWrapper._METHOD_TIMEOUTS['method_1'] = 1 + for i in range(5): + with testtools.ExpectedException(messaging.MessagingTimeout): + self.client.call(self.call_context, 'method_1') + + # we only care to check the timeouts sent to the transport + timeouts = [call[1]['timeout'] + for call in rpc.TRANSPORT._send.call_args_list] + self.assertEqual([1, 2, 4, 8, 16], timeouts) + + def test_method_timeout_10x_config_ceiling(self): + rpc.TRANSPORT.conf.rpc_response_timeout = 10 + # 5 doublings should max out at the 10xdefault ceiling + for i in range(5): + with testtools.ExpectedException(messaging.MessagingTimeout): + self.client.call(self.call_context, 'method_1') + self.assertEqual(10 * rpc.TRANSPORT.conf.rpc_response_timeout, + rpc._ContextWrapper._METHOD_TIMEOUTS['method_1']) + with testtools.ExpectedException(messaging.MessagingTimeout): + self.client.call(self.call_context, 'method_1') + self.assertEqual(10 * rpc.TRANSPORT.conf.rpc_response_timeout, + rpc._ContextWrapper._METHOD_TIMEOUTS['method_1']) + + def test_timeout_unchanged_on_other_exception(self): + rpc._ContextWrapper._METHOD_TIMEOUTS['method_1'] = 1 + rpc.TRANSPORT._send.side_effect = ValueError + with testtools.ExpectedException(ValueError): + self.client.call(self.call_context, 'method_1') + rpc.TRANSPORT._send.side_effect = messaging.MessagingTimeout + with testtools.ExpectedException(messaging.MessagingTimeout): + self.client.call(self.call_context, 'method_1') + timeouts = [call[1]['timeout'] + for call in rpc.TRANSPORT._send.call_args_list] + self.assertEqual([1, 1], timeouts) + + def test_timeouts_for_methods_tracked_independently(self): + rpc._ContextWrapper._METHOD_TIMEOUTS['method_1'] = 1 + rpc._ContextWrapper._METHOD_TIMEOUTS['method_2'] = 1 + for method in ('method_1', 'method_1', 'method_2', + 'method_1', 'method_2'): + with testtools.ExpectedException(messaging.MessagingTimeout): + self.client.call(self.call_context, method) + timeouts = [call[1]['timeout'] + for call in rpc.TRANSPORT._send.call_args_list] + self.assertEqual([1, 2, 1, 4, 2], timeouts) + + def test_timeouts_for_namespaces_tracked_independently(self): + rpc._ContextWrapper._METHOD_TIMEOUTS['ns1.method'] = 1 + rpc._ContextWrapper._METHOD_TIMEOUTS['ns2.method'] = 1 + for ns in ('ns1', 'ns2'): + self.client.target.namespace = ns + for i in range(4): + with testtools.ExpectedException(messaging.MessagingTimeout): + self.client.call(self.call_context, 'method') + timeouts = [call[1]['timeout'] + for call in rpc.TRANSPORT._send.call_args_list] + self.assertEqual([1, 2, 4, 8, 1, 2, 4, 8], timeouts) + + def test_method_timeout_increases_with_prepare(self): + rpc._ContextWrapper._METHOD_TIMEOUTS['method_1'] = 1 + ctx = self.client.prepare(version='1.4') + with testtools.ExpectedException(messaging.MessagingTimeout): + ctx.call(self.call_context, 'method_1') + with testtools.ExpectedException(messaging.MessagingTimeout): + ctx.call(self.call_context, 'method_1') + + # we only care to check the timeouts sent to the transport + timeouts = [call[1]['timeout'] + for call in rpc.TRANSPORT._send.call_args_list] + self.assertEqual([1, 2], timeouts) + + class TestConnection(base.DietTestCase): def setUp(self): super(TestConnection, self).setUp() diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_agentschedulers_db.py neutron-7.1.1/neutron/tests/unit/db/test_agentschedulers_db.py --- neutron-7.0.4/neutron/tests/unit/db/test_agentschedulers_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_agentschedulers_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -683,6 +683,16 @@ self._take_down_agent_and_run_reschedule(L3_HOSTA) # Value error self._take_down_agent_and_run_reschedule(L3_HOSTA) # Exception + def test_router_rescheduler_catches_exceptions_on_fetching_bindings(self): + with mock.patch('neutron.context.get_admin_context') as get_ctx: + mock_ctx = mock.Mock() + get_ctx.return_value = mock_ctx + mock_ctx.session.query.side_effect = db_exc.DBError() + plugin = manager.NeutronManager.get_service_plugins().get( + service_constants.L3_ROUTER_NAT) + # check that no exception is raised + plugin.reschedule_routers_from_down_agents() + def test_router_rescheduler_iterates_after_reschedule_failure(self): plugin = manager.NeutronManager.get_service_plugins().get( service_constants.L3_ROUTER_NAT) diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_allowedaddresspairs_db.py neutron-7.1.1/neutron/tests/unit/db/test_allowedaddresspairs_db.py --- neutron-7.0.4/neutron/tests/unit/db/test_allowedaddresspairs_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_allowedaddresspairs_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -100,7 +100,7 @@ def test_create_port_allowed_address_pairs_bad_format(self): with self.network() as net: - bad_values = [False, True, None, 1.1, 1] + bad_values = [False, True, 1.1, 1] for value in bad_values: self._create_port( self.fmt, net['network']['id'], @@ -305,7 +305,13 @@ res = req.get_response(self.api) self.assertEqual(400, res.status_int) - def test_create_port_remove_allowed_address_pairs(self): + def test_create_port_remove_allowed_address_pairs_with_list(self): + self._test_create_port_remove_allowed_address_pairs([]) + + def test_create_port_remove_allowed_address_pairs_with_none(self): + self._test_create_port_remove_allowed_address_pairs(None) + + def _test_create_port_remove_allowed_address_pairs(self, update_value): with self.network() as net: address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}] @@ -313,7 +319,7 @@ arg_list=(addr_pair.ADDRESS_PAIRS,), allowed_address_pairs=address_pairs) port = self.deserialize(self.fmt, res) - update_port = {'port': {addr_pair.ADDRESS_PAIRS: []}} + update_port = {'port': {addr_pair.ADDRESS_PAIRS: update_value}} req = self.new_update_request('ports', update_port, port['port']['id']) port = self.deserialize(self.fmt, req.get_response(self.api)) diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_db_base_plugin_v2.py neutron-7.1.1/neutron/tests/unit/db/test_db_base_plugin_v2.py --- neutron-7.0.4/neutron/tests/unit/db/test_db_base_plugin_v2.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_db_base_plugin_v2.py 2016-06-10 01:43:08.000000000 +0000 @@ -956,6 +956,25 @@ self.assertEqual(expected_error, data['NeutronError']['type']) self.assertEqual(msg, data['NeutronError']['message']) + def _test_create_port_with_too_many_fixed_ips(self, error='InvalidInput'): + with self.network() as network: + with self.subnet(network=network, cidr='10.0.0.0/24') as subnet: + fixed_ips = [{'subnet_id': subnet['subnet']['id'], + 'ip_address': '10.0.0.%s' % id} + for id in range(3, + cfg.CONF.max_fixed_ips_per_port + 4)] + res = self._create_port(self.fmt, + network['network']['id'], + webob.exc.HTTPBadRequest.code, + fixed_ips=fixed_ips, + set_context=True) + data = self.deserialize(self.fmt, res) + self.assertEqual(error, + data['NeutronError']['type']) + + def test_create_port_with_too_many_fixed_ips(self): + self._test_create_port_with_too_many_fixed_ips() + def test_create_ports_bulk_native(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk port create") @@ -997,6 +1016,15 @@ ports = self.deserialize(self.fmt, res) self.assertEqual(len(ports['ports']), 0) + def test_get_ports_count(self): + with self.port(), self.port(), self.port(), self.port() as p: + tenid = p['port']['tenant_id'] + ctx = context.Context(user_id=None, tenant_id=tenid, + is_admin=False) + pl = manager.NeutronManager.get_plugin() + count = pl.get_ports_count(ctx, filters={'tenant_id': [tenid]}) + self.assertEqual(4, count) + def test_create_ports_bulk_emulated_plugin_failure(self): real_has_attr = hasattr @@ -1085,6 +1113,29 @@ self._test_list_resources('port', [port2], neutron_context=n_context) + def test_list_ports_for_network_owner(self): + with self.network(tenant_id='tenant_1') as network: + with self.subnet(network) as subnet: + with self.port(subnet, tenant_id='tenant_1') as port1,\ + self.port(subnet, tenant_id='tenant_2') as port2: + # network owner request, should return all ports + port_res = self._list_ports( + 'json', set_context=True, tenant_id='tenant_1') + port_list = self.deserialize('json', port_res)['ports'] + port_ids = [p['id'] for p in port_list] + self.assertEqual(2, len(port_list)) + self.assertIn(port1['port']['id'], port_ids) + self.assertIn(port2['port']['id'], port_ids) + + # another tenant request, only return ports belong to it + port_res = self._list_ports( + 'json', set_context=True, tenant_id='tenant_2') + port_list = self.deserialize('json', port_res)['ports'] + port_ids = [p['id'] for p in port_list] + self.assertEqual(1, len(port_list)) + self.assertNotIn(port1['port']['id'], port_ids) + self.assertIn(port2['port']['id'], port_ids) + def test_list_ports_with_sort_native(self): if self._skip_native_sorting: self.skipTest("Skip test for not implemented sorting feature") @@ -1190,6 +1241,16 @@ self._show('ports', port['port']['id'], expected_code=webob.exc.HTTPNotFound.code) + def test_delete_port_by_network_owner(self): + with self.network(tenant_id='tenant_1') as network: + with self.subnet(network) as subnet: + with self.port(subnet, tenant_id='tenant_2') as port: + self._delete( + 'ports', port['port']['id'], + neutron_context=context.Context('', 'tenant_1')) + self._show('ports', port['port']['id'], + expected_code=webob.exc.HTTPNotFound.code) + def test_update_port(self): with self.port() as port: data = {'port': {'admin_state_up': False}} @@ -1245,6 +1306,32 @@ # sub-classes for plugins/drivers that support mac address update # override this method + def test_update_dhcp_port_with_exceeding_fixed_ips(self): + """ + Max fixed ips per port is configured in configuration file + by max_fixed_ips_per_port parameter. + + DHCP port is not restricted by this parameter. + """ + with self.subnet() as subnet: + updated_fixed_ips = [{'subnet_id': subnet['subnet']['id'], + 'ip_address': '10.0.0.%s' % id} + for id in range(3, + cfg.CONF.max_fixed_ips_per_port + 4)] + host_arg = None or {} + arg_list = None or [] + with self.port(device_owner=constants.DEVICE_OWNER_DHCP, + subnet=subnet, arg_list=arg_list, + **host_arg) as port: + data = {'port': {'fixed_ips': updated_fixed_ips}} + req = self.new_update_request('ports', + data, port['port']['id']) + res = req.get_response(self.api) + self.assertEqual(res.status_int, webob.exc.HTTPOk.code) + result = self.deserialize(self.fmt, res) + for fixed_ip in updated_fixed_ips: + self.assertIn(fixed_ip, result['port']['fixed_ips']) + def test_update_port_mac_ip(self): with self.subnet() as subnet: updated_fixed_ips = [{'subnet_id': subnet['subnet']['id'], @@ -2903,6 +2990,39 @@ res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) + def _test_create_subnet_V6_pd_modes(self, ra_addr_mode, expect_fail=False): + cfg.CONF.set_override('default_ipv6_subnet_pool', + constants.IPV6_PD_POOL_ID) + with self.network() as network: + data = {'subnet': {'network_id': network['network']['id'], + 'ip_version': '6', + 'tenant_id': network['network']['tenant_id']}} + if ra_addr_mode: + data['subnet']['ipv6_ra_mode'] = ra_addr_mode + data['subnet']['ipv6_address_mode'] = ra_addr_mode + subnet_req = self.new_create_request('subnets', data) + res = subnet_req.get_response(self.api) + if expect_fail: + self.assertEqual(webob.exc.HTTPClientError.code, + res.status_int) + else: + subnet = self.deserialize(self.fmt, res)['subnet'] + self.assertEqual(constants.IPV6_PD_POOL_ID, + subnet['subnetpool_id']) + + def test_create_subnet_V6_pd_slaac(self): + self._test_create_subnet_V6_pd_modes('slaac') + + def test_create_subnet_V6_pd_stateless(self): + self._test_create_subnet_V6_pd_modes('dhcpv6-stateless') + + def test_create_subnet_V6_pd_statefull(self): + self._test_create_subnet_V6_pd_modes('dhcpv6-statefull', + expect_fail=True) + + def test_create_subnet_V6_pd_no_mode(self): + self._test_create_subnet_V6_pd_modes(None, expect_fail=True) + def test_create_2_subnets_overlapping_cidr_allowed_returns_200(self): cidr_1 = '10.0.0.0/23' cidr_2 = '10.0.0.0/24' @@ -4055,6 +4175,16 @@ res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertIsNone(data['subnet']['gateway_ip']) + def test_subnet_usable_after_update(self): + with self.subnet() as subnet: + data = {'subnet': {'name': 'newname'}} + req = self.new_update_request('subnets', data, + subnet['subnet']['id']) + res = self.deserialize(self.fmt, req.get_response(self.api)) + self.assertEqual(data['subnet']['name'], res['subnet']['name']) + with self.port(subnet=subnet): + pass + def test_update_subnet(self): with self.subnet() as subnet: data = {'subnet': {'gateway_ip': '10.0.0.1'}} @@ -5295,6 +5425,23 @@ res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['subnetpool']['default_quota'], 1) + def test_allocate_subnet_bad_gateway(self): + with self.network() as network: + sp = self._test_create_subnetpool(['10.10.0.0/8'], + tenant_id=self._tenant_id, + name=self._POOL_NAME, + default_prefixlen='24') + + # Request a subnet allocation (no CIDR) + data = {'subnet': {'network_id': network['network']['id'], + 'subnetpool_id': sp['subnetpool']['id'], + 'prefixlen': 32, + 'ip_version': 4, + 'tenant_id': network['network']['tenant_id']}} + req = self.new_create_request('subnets', data) + result = req.get_response(self.api) + self.assertEqual(409, result.status_int) + def test_allocate_any_subnet_with_prefixlen(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_dvr_mac_db.py neutron-7.1.1/neutron/tests/unit/db/test_dvr_mac_db.py --- neutron-7.0.4/neutron/tests/unit/db/test_dvr_mac_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_dvr_mac_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -16,9 +16,13 @@ import mock from oslo_config import cfg +from neutron.callbacks import events +from neutron.callbacks import registry +from neutron.callbacks import resources from neutron import context from neutron.db import dvr_mac_db from neutron.extensions import dvr +from neutron import manager from neutron.tests.unit.plugins.ml2 import test_plugin @@ -80,6 +84,34 @@ dvr_mac_db.DistributedVirtualRouterMacAddress).count() self.assertFalse(count) + def test_mac_not_cleared_on_agent_delete_event_with_remaining_agents(self): + plugin = manager.NeutronManager.get_plugin() + self._create_dvr_mac_entry('host_1', 'mac_1') + self._create_dvr_mac_entry('host_2', 'mac_2') + agent1 = {'host': 'host_1', 'id': 'a1'} + agent2 = {'host': 'host_1', 'id': 'a2'} + with mock.patch.object(plugin, 'get_agents', return_value=[agent2]): + with mock.patch.object(plugin, 'notifier') as notifier: + registry.notify(resources.AGENT, events.BEFORE_DELETE, self, + context=self.ctx, agent=agent1) + mac_list = self.mixin.get_dvr_mac_address_list(self.ctx) + self.assertEqual(2, len(mac_list)) + self.assertFalse(notifier.dvr_mac_address_update.called) + + def test_mac_cleared_on_agent_delete_event(self): + plugin = manager.NeutronManager.get_plugin() + self._create_dvr_mac_entry('host_1', 'mac_1') + self._create_dvr_mac_entry('host_2', 'mac_2') + agent = {'host': 'host_1', 'id': 'a1'} + with mock.patch.object(plugin, 'notifier') as notifier: + registry.notify(resources.AGENT, events.BEFORE_DELETE, self, + context=self.ctx, agent=agent) + mac_list = self.mixin.get_dvr_mac_address_list(self.ctx) + self.assertEqual(1, len(mac_list)) + self.assertEqual('host_2', mac_list[0]['host']) + notifier.dvr_mac_address_update.assert_called_once_with( + self.ctx, mac_list) + def test_get_dvr_mac_address_list(self): self._create_dvr_mac_entry('host_1', 'mac_1') self._create_dvr_mac_entry('host_2', 'mac_2') diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_l3_db.py neutron-7.1.1/neutron/tests/unit/db/test_l3_db.py --- neutron-7.0.4/neutron/tests/unit/db/test_l3_db.py 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_l3_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -0,0 +1,107 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +import testtools + +from neutron.common import exceptions as n_exc +from neutron.db import l3_db +from neutron.extensions import l3 +from neutron import manager +from neutron.tests import base + + +class TestL3_NAT_dbonly_mixin(base.BaseTestCase): + def setUp(self): + super(TestL3_NAT_dbonly_mixin, self).setUp() + self.db = l3_db.L3_NAT_dbonly_mixin() + + @mock.patch.object(manager.NeutronManager, 'get_plugin') + def test_prevent_l3_port_deletion_port_not_found(self, gp): + # port not found doesn't prevent + gp.return_value.get_port.side_effect = n_exc.PortNotFound(port_id='1') + self.db.prevent_l3_port_deletion(None, None) + + @mock.patch.object(manager.NeutronManager, 'get_plugin') + def test_prevent_l3_port_device_owner_not_router(self, gp): + # ignores other device owners + gp.return_value.get_port.return_value = {'device_owner': 'cat'} + self.db.prevent_l3_port_deletion(None, None) + + @mock.patch.object(manager.NeutronManager, 'get_plugin') + def test_prevent_l3_port_no_fixed_ips(self, gp): + # without fixed IPs is allowed + gp.return_value.get_port.return_value = { + 'device_owner': 'network:router_interface', 'fixed_ips': [], + 'id': 'f' + } + self.db.prevent_l3_port_deletion(None, None) + + @mock.patch.object(manager.NeutronManager, 'get_plugin') + def test_prevent_l3_port_no_router(self, gp): + # without router is allowed + gp.return_value.get_port.return_value = { + 'device_owner': 'network:router_interface', + 'device_id': '44', 'id': 'f', + 'fixed_ips': [{'ip_address': '1.1.1.1', 'subnet_id': '4'}]} + self.db.get_router = mock.Mock() + self.db.get_router.side_effect = l3.RouterNotFound(router_id='44') + self.db.prevent_l3_port_deletion(mock.Mock(), None) + + @mock.patch.object(manager.NeutronManager, 'get_plugin') + def test_prevent_l3_port_existing_router(self, gp): + gp.return_value.get_port.return_value = { + 'device_owner': 'network:router_interface', + 'device_id': 'some_router', 'id': 'f', + 'fixed_ips': [{'ip_address': '1.1.1.1', 'subnet_id': '4'}]} + self.db.get_router = mock.Mock() + with testtools.ExpectedException(n_exc.ServicePortInUse): + self.db.prevent_l3_port_deletion(mock.Mock(), None) + + @mock.patch.object(manager.NeutronManager, 'get_plugin') + def test_prevent_l3_port_existing_floating_ip(self, gp): + gp.return_value.get_port.return_value = { + 'device_owner': 'network:floatingip', + 'device_id': 'some_flip', 'id': 'f', + 'fixed_ips': [{'ip_address': '1.1.1.1', 'subnet_id': '4'}]} + self.db.get_floatingip = mock.Mock() + with testtools.ExpectedException(n_exc.ServicePortInUse): + self.db.prevent_l3_port_deletion(mock.Mock(), None) + + def test__populate_ports_for_subnets_none(self): + """Basic test that the method runs correctly with no ports""" + ports = [] + with mock.patch.object(manager.NeutronManager, 'get_plugin') as get_p: + get_p().get_networks.return_value = [] + self.db._populate_mtu_and_subnets_for_ports(mock.sentinel.context, + ports) + self.assertEqual([], ports) + + def test__populate_ports_for_subnets(self): + ports = [{'network_id': 'net_id', + 'id': 'port_id', + 'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id}]}] + with mock.patch.object(manager.NeutronManager, 'get_plugin') as get_p: + get_p().get_networks.return_value = [{'id': 'net_id', 'mtu': 1446}] + self.db._populate_mtu_and_subnets_for_ports(mock.sentinel.context, + ports) + self.assertEqual([{'extra_subnets': [], + 'fixed_ips': [{'subnet_id': + mock.sentinel.subnet_id}], + 'id': 'port_id', + 'mtu': 1446, + 'network_id': 'net_id', + 'subnets': []}], + ports) diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_l3_dvr_db.py neutron-7.1.1/neutron/tests/unit/db/test_l3_dvr_db.py --- neutron-7.0.4/neutron/tests/unit/db/test_l3_dvr_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_l3_dvr_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -193,6 +193,7 @@ plugin = mock.Mock() gp.return_value = plugin plugin.get_port.return_value = port + self.mixin._router_exists = mock.Mock(return_value=True) self.assertRaises(exceptions.ServicePortInUse, self.mixin.prevent_l3_port_deletion, self.ctx, @@ -202,6 +203,7 @@ port = { 'id': 'my_port_id', 'fixed_ips': mock.ANY, + 'device_id': 'r_id', 'device_owner': l3_const.DEVICE_OWNER_AGENT_GW } self._test_prepare_direct_delete_dvr_internal_ports(port) @@ -210,6 +212,7 @@ port = { 'id': 'my_port_id', 'fixed_ips': mock.ANY, + 'device_id': 'r_id', 'device_owner': l3_const.DEVICE_OWNER_ROUTER_SNAT } self._test_prepare_direct_delete_dvr_internal_ports(port) @@ -301,16 +304,19 @@ 'foo_host') def _setup_delete_current_gw_port_deletes_fip_agent_gw_port( - self, port=None): - gw_port_db = { - 'id': 'my_gw_id', - 'network_id': 'ext_net_id', - 'device_owner': l3_const.DEVICE_OWNER_ROUTER_GW - } + self, port=None, gw_port=True): router = mock.MagicMock() router.extra_attributes.distributed = True - router['gw_port_id'] = gw_port_db['id'] - router.gw_port = gw_port_db + if gw_port: + gw_port_db = { + 'id': 'my_gw_id', + 'network_id': 'ext_net_id', + 'device_owner': l3_const.DEVICE_OWNER_ROUTER_GW + } + router.gw_port = gw_port_db + else: + router.gw_port = None + with mock.patch.object(manager.NeutronManager, 'get_plugin') as gp,\ mock.patch.object(l3_dvr_db.l3_db.L3_NAT_db_mixin, '_delete_current_gw_port'),\ @@ -322,24 +328,28 @@ 'delete_csnat_router_interface_ports') as del_csnat_port,\ mock.patch.object( self.mixin, - 'delete_floatingip_agent_gateway_port') as del_agent_gw_port: + 'delete_floatingip_agent_gateway_port') as del_agent_gw_port,\ + mock.patch.object( + self.mixin.l3_rpc_notifier, + 'delete_fipnamespace_for_ext_net') as del_fip: plugin = mock.Mock() gp.return_value = plugin plugin.get_ports.return_value = port grtr.return_value = router self.mixin._delete_current_gw_port( self.ctx, router['id'], router, 'ext_network_id') - return router, plugin, del_csnat_port, del_agent_gw_port + return router, plugin, del_csnat_port, del_agent_gw_port, del_fip - def test_delete_current_gw_port_deletes_fip_agent_gw_port(self): - rtr, plugin, d_csnat_port, d_agent_gw_port = ( + def test_delete_current_gw_port_deletes_fip_agent_gw_port_and_fipnamespace( + self): + rtr, plugin, d_csnat_port, d_agent_gw_port, del_fip = ( self._setup_delete_current_gw_port_deletes_fip_agent_gw_port()) self.assertTrue(d_csnat_port.called) self.assertTrue(d_agent_gw_port.called) d_csnat_port.assert_called_once_with( mock.ANY, rtr) - d_agent_gw_port.assert_called_once_with( - mock.ANY, None, 'ext_net_id') + d_agent_gw_port.assert_called_once_with(mock.ANY, None, 'ext_net_id') + del_fip.assert_called_once_with(mock.ANY, 'ext_net_id') def test_delete_current_gw_port_never_calls_delete_fip_agent_gw_port(self): port = [{ @@ -352,14 +362,23 @@ 'network_id': 'ext_net_id', 'device_owner': l3_const.DEVICE_OWNER_ROUTER_GW }] - rtr, plugin, d_csnat_port, d_agent_gw_port = ( + rtr, plugin, d_csnat_port, d_agent_gw_port, del_fip = ( self._setup_delete_current_gw_port_deletes_fip_agent_gw_port( port=port)) self.assertTrue(d_csnat_port.called) self.assertFalse(d_agent_gw_port.called) + self.assertFalse(del_fip.called) d_csnat_port.assert_called_once_with( mock.ANY, rtr) + def test_delete_current_gw_port_never_calls_delete_fipnamespace(self): + rtr, plugin, d_csnat_port, d_agent_gw_port, del_fip = ( + self._setup_delete_current_gw_port_deletes_fip_agent_gw_port( + gw_port=False)) + self.assertFalse(d_csnat_port.called) + self.assertFalse(d_agent_gw_port.called) + self.assertFalse(del_fip.called) + def _floatingip_on_port_test_setup(self, hostid): router = {'id': 'foo_router_id', 'distributed': True} floatingip = { @@ -460,47 +479,6 @@ fip, floatingip, router)) self.assertFalse(create_fip.called) - def test_remove_router_interface_delete_router_l3agent_binding(self): - interface_info = {'subnet_id': '123'} - router = mock.MagicMock() - router.extra_attributes.distributed = True - plugin = mock.MagicMock() - plugin.get_l3_agents_hosting_routers = mock.Mock( - return_value=[mock.MagicMock()]) - plugin.get_subnet_ids_on_router = mock.Mock( - return_value=interface_info) - plugin.check_ports_exist_on_l3agent = mock.Mock( - return_value=False) - plugin.remove_router_from_l3_agent = mock.Mock( - return_value=None) - with mock.patch.object(self.mixin, '_get_router') as grtr,\ - mock.patch.object(self.mixin, '_get_device_owner') as gdev,\ - mock.patch.object(self.mixin, - '_remove_interface_by_subnet') as rmintf,\ - mock.patch.object( - self.mixin, - 'delete_csnat_router_interface_ports') as delintf,\ - mock.patch.object(manager.NeutronManager, - 'get_service_plugins') as gplugin,\ - mock.patch.object(self.mixin, - '_make_router_interface_info') as mkintf,\ - mock.patch.object(self.mixin, - 'notify_router_interface_action') as notify: - grtr.return_value = router - gdev.return_value = mock.Mock() - rmintf.return_value = (mock.MagicMock(), mock.MagicMock()) - mkintf.return_value = mock.Mock() - gplugin.return_value = {plugin_const.L3_ROUTER_NAT: plugin} - delintf.return_value = None - notify.return_value = None - - self.mixin.manager = manager - self.mixin.remove_router_interface( - self.ctx, mock.Mock(), interface_info) - self.assertTrue(plugin.get_l3_agents_hosting_routers.called) - self.assertTrue(plugin.check_ports_exist_on_l3agent.called) - self.assertTrue(plugin.remove_router_from_l3_agent.called) - def test_remove_router_interface_csnat_ports_removal(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} @@ -564,7 +542,8 @@ mock_notify.assert_called_once_with( 'router', 'before_update', self.mixin, **kwargs) - def _test_dvr_vmarp_table_update(self, device_owner, action): + def _test_update_arp_entry_for_dvr_service_port( + self, device_owner, action): with mock.patch.object(manager.NeutronManager, 'get_plugin') as gp,\ mock.patch.object(self.mixin, '_get_router') as grtr: plugin = mock.Mock() @@ -592,21 +571,22 @@ plugin.get_ports.return_value = [port, dvr_port] grtr.return_value = dvr_router dvr_router.extra_attributes.distributed = True - self.mixin.dvr_vmarp_table_update(self.ctx, port, action) + self.mixin.update_arp_entry_for_dvr_service_port( + self.ctx, port, action) if action == 'add': self.assertEqual(3, l3_notify.add_arp_entry.call_count) elif action == 'del': self.assertTrue(3, l3_notify.del_arp_entry.call_count) - def test_dvr_vmarp_table_update_with_service_port_added(self): + def test_update_arp_entry_for_dvr_service_port_added(self): action = 'add' device_owner = l3_const.DEVICE_OWNER_LOADBALANCER - self._test_dvr_vmarp_table_update(device_owner, action) + self._test_update_arp_entry_for_dvr_service_port(device_owner, action) - def test_dvr_vmarp_table_update_with_service_port_deleted(self): + def test_update_arp_entry_for_dvr_service_port_deleted(self): action = 'del' device_owner = l3_const.DEVICE_OWNER_LOADBALANCER - self._test_dvr_vmarp_table_update(device_owner, action) + self._test_update_arp_entry_for_dvr_service_port(device_owner, action) def test_add_router_interface_csnat_ports_failure(self): router_dict = {'name': 'test_router', 'admin_state_up': True, diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_migration.py neutron-7.1.1/neutron/tests/unit/db/test_migration.py --- neutron-7.0.4/neutron/tests/unit/db/test_migration.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_migration.py 2016-06-10 01:43:08.000000000 +0000 @@ -303,6 +303,24 @@ 'sql': False}] ) + @mock.patch('alembic.script.ScriptDirectory.walk_revisions') + def test_upgrade_milestone_expand_before_contract(self, walk_mock): + c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)] + c_revs[1].module.neutron_milestone = [migration.LIBERTY] + e_revs = [FakeRevision(labels={cli.EXPAND_BRANCH}) for r in range(5)] + e_revs[3].module.neutron_milestone = [migration.LIBERTY] + walk_mock.return_value = c_revs + e_revs + self._main_test_helper( + ['prog', '--subproject', 'neutron', 'upgrade', 'liberty'], + 'upgrade', + [{'desc': cli.EXPAND_BRANCH, + 'revision': e_revs[3].revision, + 'sql': False}, + {'desc': cli.CONTRACT_BRANCH, + 'revision': c_revs[1].revision, + 'sql': False}] + ) + def assert_command_fails(self, command): # Avoid cluttering stdout with argparse error messages mock.patch('argparse.ArgumentParser._print_message').start() diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_portsecurity_db_common.py neutron-7.1.1/neutron/tests/unit/db/test_portsecurity_db_common.py --- neutron-7.0.4/neutron/tests/unit/db/test_portsecurity_db_common.py 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_portsecurity_db_common.py 2016-06-10 01:43:03.000000000 +0000 @@ -0,0 +1,76 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from sqlalchemy.orm import exc + +from neutron.db import portsecurity_db_common as pdc +from neutron.extensions import portsecurity as psec +from neutron.tests import base + + +common = pdc.PortSecurityDbCommon + + +class PortSecurityDbCommonTestCase(base.BaseTestCase): + + def setUp(self): + super(PortSecurityDbCommonTestCase, self).setUp() + self.common = common() + + def _test__get_security_binding_no_binding(self, getter): + port_sec_enabled = True + req = {psec.PORTSECURITY: port_sec_enabled} + res = {} + with mock.patch.object( + self.common, '_model_query', + create=True, + side_effect=exc.NoResultFound): + val = getter(req, res) + self.assertEqual(port_sec_enabled, val) + + def test__get_port_security_binding_no_binding(self): + self._test__get_security_binding_no_binding( + self.common._get_port_security_binding) + + def test__get_network_security_binding_no_binding(self): + self._test__get_security_binding_no_binding( + self.common._get_network_security_binding) + + def _test__process_security_update_no_binding(self, creator, updater): + req = {psec.PORTSECURITY: False} + res = {} + context = mock.Mock() + with mock.patch.object( + self.common, '_model_query', + create=True, + side_effect=exc.NoResultFound): + updater(context, req, res) + creator.assert_called_with(context, req, res) + + @mock.patch.object(common, '_process_port_port_security_create') + def test__process_port_port_security_update_no_binding(self, creator): + self._test__process_security_update_no_binding( + creator, + self.common._process_port_port_security_update) + + @mock.patch.object(common, '_process_network_port_security_create') + def test__process_network_port_security_update_no_binding(self, creator): + self._test__process_security_update_no_binding( + creator, + self.common._process_network_port_security_update) + + def test__extend_port_security_dict_no_port_security(self): + for db_data in ({'port_security': None, 'name': 'net1'}, {}): + response_data = {} + self.common._extend_port_security_dict(response_data, db_data) + self.assertTrue(response_data[psec.PORTSECURITY]) diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_portsecurity_db.py neutron-7.1.1/neutron/tests/unit/db/test_portsecurity_db.py --- neutron-7.0.4/neutron/tests/unit/db/test_portsecurity_db.py 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_portsecurity_db.py 2016-06-10 01:43:03.000000000 +0000 @@ -0,0 +1,47 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock + +from neutron.db import portsecurity_db as pd +from neutron.db import portsecurity_db_common as pdc +from neutron.tests import base + +common = pdc.PortSecurityDbCommon + + +class FakePlugin(pd.PortSecurityDbMixin): + + supported_extension_aliases = ['port-security'] + + +class PortSecurityDbMixinTestCase(base.BaseTestCase): + + def setUp(self): + super(PortSecurityDbMixinTestCase, self).setUp() + self.plugin = FakePlugin() + + @mock.patch.object(common, '_extend_port_security_dict') + def test__extend_port_security_dict_relies_on_common(self, extend): + response = mock.Mock() + dbdata = mock.Mock() + self.plugin._extend_port_security_dict(response, dbdata) + extend.assert_called_once_with(response, dbdata) + + @mock.patch.object(common, '_extend_port_security_dict') + def test__extend_port_security_dict_ignored_if_extension_disabled(self, + extend): + response = mock.Mock() + dbdata = mock.Mock() + self.plugin.supported_extension_aliases = [] + self.plugin._extend_port_security_dict(response, dbdata) + self.assertFalse(extend.called) diff -Nru neutron-7.0.4/neutron/tests/unit/db/test_securitygroups_db.py neutron-7.1.1/neutron/tests/unit/db/test_securitygroups_db.py --- neutron-7.0.4/neutron/tests/unit/db/test_securitygroups_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/db/test_securitygroups_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -16,6 +16,7 @@ from neutron.callbacks import exceptions from neutron.callbacks import registry +from neutron.common import constants from neutron import context from neutron.db import common_db_mixin from neutron.db import securitygroups_db @@ -72,6 +73,19 @@ self.mixin.create_security_group_rule( self.ctx, mock.MagicMock()) + def test__check_for_duplicate_rules_in_db_does_not_drop_protocol(self): + with mock.patch.object(self.mixin, 'get_security_group_rules', + return_value=[mock.Mock()]): + context = mock.Mock() + rule_dict = { + 'security_group_rule': {'protocol': None, + 'tenant_id': 'fake', + 'security_group_id': 'fake', + 'direction': 'fake'} + } + self.mixin._check_for_duplicate_rules_in_db(context, rule_dict) + self.assertIn('protocol', rule_dict['security_group_rule']) + def test_delete_security_group_rule_in_use(self): with mock.patch.object(registry, "notify") as mock_notify: mock_notify.side_effect = exceptions.CallbackFailure(Exception()) @@ -83,3 +97,15 @@ with testtools.ExpectedException( securitygroup.SecurityGroupRuleNotFound): self.mixin.delete_security_group_rule(self.ctx, 'foo_rule') + + def test_get_ip_proto_name_and_num(self): + protocols = [constants.PROTO_NAME_UDP, str(constants.PROTO_NUM_TCP), + 'blah', '111'] + protocol_names_nums = ( + [[constants.PROTO_NAME_UDP, str(constants.PROTO_NUM_UDP)], + [constants.PROTO_NAME_TCP, str(constants.PROTO_NUM_TCP)], + ['blah', 'blah'], ['111', '111']]) + + for i, protocol in enumerate(protocols): + self.assertEqual(protocol_names_nums[i], + self.mixin._get_ip_proto_name_and_num(protocol)) diff -Nru neutron-7.0.4/neutron/tests/unit/extensions/test_external_net.py neutron-7.1.1/neutron/tests/unit/extensions/test_external_net.py --- neutron-7.0.4/neutron/tests/unit/extensions/test_external_net.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/extensions/test_external_net.py 2016-06-10 01:43:08.000000000 +0000 @@ -19,6 +19,7 @@ import testtools from webob import exc +from neutron.common import constants from neutron import context from neutron.db import models_v2 from neutron.extensions import external_net as external_net @@ -115,6 +116,23 @@ res = req.get_response(self.api) self.assertEqual(exc.HTTPForbidden.code, res.status_int) + def test_update_network_external_net_with_ports_set_not_shared(self): + with self.network(router__external=True, shared=True) as ext_net,\ + self.subnet(network=ext_net) as ext_subnet, \ + self.port(subnet=ext_subnet, + tenant_id='', + device_owner=constants.DEVICE_OWNER_ROUTER_SNAT): + data = {'network': {'shared': False}} + req = self.new_update_request('networks', + data, + ext_net['network']['id']) + res = req.get_response(self.api) + self.assertEqual(exc.HTTPOk.code, res.status_int) + ctx = context.Context(None, None, is_admin=True) + plugin = manager.NeutronManager.get_plugin() + result = plugin.get_networks(ctx) + self.assertFalse(result[0]['shared']) + def test_network_filter_hook_admin_context(self): plugin = manager.NeutronManager.get_plugin() ctx = context.Context(None, None, is_admin=True) diff -Nru neutron-7.0.4/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py neutron-7.1.1/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py --- neutron-7.0.4/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py 2016-06-10 01:43:08.000000000 +0000 @@ -16,6 +16,7 @@ import mock from oslo_config import cfg +from oslo_db import exception as db_exc from oslo_serialization import jsonutils from oslo_utils import uuidutils import testscenarios @@ -389,6 +390,19 @@ expected_code=expected_code, neutron_context=neutron_context) + def test_router_gateway_set_retry(self): + with self.router() as r, self.subnet() as s: + ext_net_id = s['subnet']['network_id'] + self._set_net_external(ext_net_id) + with mock.patch.object( + l3_db.L3_NAT_dbonly_mixin, '_validate_gw_info', + side_effect=[db_exc.RetryRequest(None), ext_net_id]): + self._set_router_external_gateway(r['router']['id'], + ext_net_id) + res = self._show('routers', r['router']['id'])['router'] + self.assertEqual(ext_net_id, + res['external_gateway_info']['network_id']) + def test_router_create_with_gwinfo_invalid_ext_ip(self): with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) diff -Nru neutron-7.0.4/neutron/tests/unit/extensions/test_l3.py neutron-7.1.1/neutron/tests/unit/extensions/test_l3.py --- neutron-7.0.4/neutron/tests/unit/extensions/test_l3.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/extensions/test_l3.py 2016-06-10 01:43:08.000000000 +0000 @@ -379,9 +379,9 @@ tenant_id=None, msg=None): interface_data = {} - if subnet_id: + if subnet_id is not None: interface_data.update({'subnet_id': subnet_id}) - if port_id: + if port_id is not None: interface_data.update({'port_id': port_id}) req = self.new_action_request('routers', interface_data, router_id, @@ -960,6 +960,20 @@ # nsx metadata access case self.assertIn(payload['tenant_id'], [stid, ''], msg) + def test_router_add_interface_bad_values(self): + with self.router() as r: + exp_code = exc.HTTPBadRequest.code + self._router_interface_action('add', + r['router']['id'], + False, + None, + expected_code=exp_code) + self._router_interface_action('add', + r['router']['id'], + None, + False, + expected_code=exp_code) + def test_router_add_interface_subnet(self): fake_notifier.reset() with self.router() as r: diff -Nru neutron-7.0.4/neutron/tests/unit/extensions/test_portsecurity.py neutron-7.1.1/neutron/tests/unit/extensions/test_portsecurity.py --- neutron-7.0.4/neutron/tests/unit/extensions/test_portsecurity.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/extensions/test_portsecurity.py 2016-06-10 01:43:08.000000000 +0000 @@ -23,7 +23,6 @@ from neutron.extensions import portsecurity as psec from neutron.extensions import securitygroup as ext_sg from neutron import manager -from neutron.plugins.ml2.extensions import port_security from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import test_securitygroup @@ -400,15 +399,3 @@ '', 'not_network_owner') res = req.get_response(self.api) self.assertEqual(res.status_int, exc.HTTPForbidden.code) - - def test_extend_port_dict_no_port_security(self): - """Test _extend_port_security_dict won't crash - if port_security item is None - """ - for db_data in ({'port_security': None, 'name': 'net1'}, {}): - response_data = {} - - driver = port_security.PortSecurityExtensionDriver() - driver._extend_port_security_dict(response_data, db_data) - - self.assertTrue(response_data[psec.PORTSECURITY]) diff -Nru neutron-7.0.4/neutron/tests/unit/extensions/test_securitygroup.py neutron-7.1.1/neutron/tests/unit/extensions/test_securitygroup.py --- neutron-7.0.4/neutron/tests/unit/extensions/test_securitygroup.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/extensions/test_securitygroup.py 2016-06-10 01:43:08.000000000 +0000 @@ -954,6 +954,40 @@ self.deserialize(self.fmt, res) self.assertEqual(res.status_int, webob.exc.HTTPConflict.code) + def test_create_security_group_rule_duplicate_rules_proto_name_num(self): + name = 'webservers' + description = 'my webservers' + with self.security_group(name, description) as sg: + security_group_id = sg['security_group']['id'] + with self.security_group_rule(security_group_id): + rule = self._build_security_group_rule( + sg['security_group']['id'], 'ingress', + const.PROTO_NAME_TCP, '22', '22') + self._create_security_group_rule(self.fmt, rule) + rule = self._build_security_group_rule( + sg['security_group']['id'], 'ingress', + const.PROTO_NUM_TCP, '22', '22') + res = self._create_security_group_rule(self.fmt, rule) + self.deserialize(self.fmt, res) + self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) + + def test_create_security_group_rule_duplicate_rules_proto_num_name(self): + name = 'webservers' + description = 'my webservers' + with self.security_group(name, description) as sg: + security_group_id = sg['security_group']['id'] + with self.security_group_rule(security_group_id): + rule = self._build_security_group_rule( + sg['security_group']['id'], 'ingress', + const.PROTO_NUM_UDP, '50', '100') + self._create_security_group_rule(self.fmt, rule) + rule = self._build_security_group_rule( + sg['security_group']['id'], 'ingress', + const.PROTO_NAME_UDP, '50', '100') + res = self._create_security_group_rule(self.fmt, rule) + self.deserialize(self.fmt, res) + self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) + def test_create_security_group_rule_min_port_greater_max(self): name = 'webservers' description = 'my webservers' @@ -1558,3 +1592,11 @@ def test_convert_numeric_protocol_to_string(self): self.assertIsInstance(ext_sg.convert_protocol(2), str) + + +class TestConvertEtherType(base.BaseTestCase): + def test_convert_unsupported_ethertype(self): + for val in ['ip', 'ip4', 'ip6', '']: + self.assertRaises(ext_sg.SecurityGroupRuleInvalidEtherType, + ext_sg.convert_ethertype_to_case_insensitive, + val) diff -Nru neutron-7.0.4/neutron/tests/unit/ipam/test_requests.py neutron-7.1.1/neutron/tests/unit/ipam/test_requests.py --- neutron-7.0.4/neutron/tests/unit/ipam/test_requests.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/ipam/test_requests.py 2016-06-10 01:43:08.000000000 +0000 @@ -134,7 +134,7 @@ def test_subnet_request_bad_gateway(self): cfg.CONF.set_override('force_gateway_on_subnet', True) - self.assertRaises(ValueError, + self.assertRaises(ipam_exc.IpamValueInvalid, ipam_req.AnySubnetRequest, self.tenant_id, self.subnet_id, @@ -153,7 +153,7 @@ def test_subnet_request_allocation_pool_wrong_version(self): pools = [netaddr.IPRange('0.0.0.4', '0.0.0.5')] - self.assertRaises(ValueError, + self.assertRaises(ipam_exc.IpamValueInvalid, ipam_req.AnySubnetRequest, self.tenant_id, self.subnet_id, @@ -163,7 +163,7 @@ def test_subnet_request_allocation_pool_not_in_net(self): pools = [netaddr.IPRange('0.0.0.64', '0.0.0.128')] - self.assertRaises(ValueError, + self.assertRaises(ipam_exc.IpamValueInvalid, ipam_req.AnySubnetRequest, self.tenant_id, self.subnet_id, @@ -185,7 +185,7 @@ def test_subnet_request_bad_gateway(self): cfg.CONF.set_override('force_gateway_on_subnet', True) - self.assertRaises(ValueError, + self.assertRaises(ipam_exc.IpamValueInvalid, ipam_req.SpecificSubnetRequest, self.tenant_id, self.subnet_id, diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py 2016-06-10 01:43:08.000000000 +0000 @@ -767,6 +767,7 @@ device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: + tunnel_ip = '20.0.0.1' p1 = port1['port'] device1 = 'tap' + p1['id'] self.callbacks.update_device_up( @@ -774,22 +775,21 @@ agent_id=HOST, device=device1) if twice: + tunnel_ip = '20.0.0.4' self._update_and_check_portbinding(p1['id'], HOST_4) - self._update_and_check_portbinding(p1['id'], HOST_2) + self.callbacks.update_device_up(self.adminContext, + agent_id=HOST_4, + device=device1) + self.mock_fanout.reset_mock() - # NOTE(yamamoto): see bug #1441488 - self.adminContext.session.expire_all() - self.callbacks.get_device_details( - self.adminContext, - device=device1, - agent_id=HOST_2) + self._update_and_check_portbinding(p1['id'], HOST_2) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] expected = {p1['network_id']: {'ports': - {'20.0.0.1': [constants.FLOODING_ENTRY, - l2pop_rpc.PortInfo( - p1['mac_address'], - p1_ips[0])]}, + {tunnel_ip: [constants.FLOODING_ENTRY, + l2pop_rpc.PortInfo( + p1['mac_address'], + p1_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -52,6 +52,9 @@ def __init__(self): self.link = FakeIpLinkCommand() + def disable_ipv6(self): + pass + def get_linuxbridge_manager(bridge_mappings, interface_mappings): with mock.patch.object(ip_lib.IPWrapper, 'get_device_by_ip', @@ -144,6 +147,7 @@ def test_treat_devices_removed_with_existed_device(self): agent = self.agent + agent._ensure_port_admin_state = mock.Mock() devices = [DEVICE_1] with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd,\ @@ -208,11 +212,22 @@ agent.treat_devices_removed(devices) de_arp.assert_called_with(devices) + def test__get_devices_locally_modified(self): + new_ts = {1: 1000, 2: 2000, 3: 3000} + old_ts = {1: 10, 2: 2000, 4: 900} + # 3 and 4 are not returned because 3 is a new device and 4 is a + # removed device + self.assertEqual( + set([1]), + self.agent._get_devices_locally_modified(new_ts, old_ts)) + def _test_scan_devices(self, previous, updated, - fake_current, expected, sync): + fake_current, expected, sync, + fake_ts_current=None): self.agent.br_mgr = mock.Mock() self.agent.br_mgr.get_tap_devices.return_value = fake_current - + self.agent.br_mgr.get_devices_modified_timestamps.return_value = ( + fake_ts_current or {}) self.agent.updated_devices = updated results = self.agent.scan_devices(previous, sync) self.assertEqual(expected, results) @@ -221,28 +236,49 @@ previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} fake_current = set([1, 2]) updated = set() expected = {'current': set([1, 2]), 'updated': set(), 'added': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) + def test_scan_devices_timestamp_triggers_updated(self): + previous = {'current': set([1, 2]), + 'updated': set(), + 'added': set(), + 'removed': set(), + 'timestamps': {2: 600}} + fake_current = set([1, 2]) + updated = set() + expected = {'current': set([1, 2]), + 'updated': set([2]), + 'added': set(), + 'removed': set(), + 'timestamps': {2: 1000}} + + self._test_scan_devices(previous, updated, fake_current, expected, + sync=False, fake_ts_current={2: 1000}) + def test_scan_devices_added_removed(self): previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} fake_current = set([2, 3]) updated = set() expected = {'current': set([2, 3]), 'updated': set(), 'added': set([3]), - 'removed': set([1])} + 'removed': set([1]), + 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) @@ -251,13 +287,15 @@ previous = {'current': set([2, 3]), 'updated': set(), 'added': set(), - 'removed': set([1])} + 'removed': set([1]), + 'timestamps': {}} fake_current = set([2, 3]) updated = set() expected = {'current': set([2, 3]), 'updated': set(), 'added': set([2, 3]), - 'removed': set([1])} + 'removed': set([1]), + 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=True) @@ -266,7 +304,8 @@ previous = {'current': set([2, 3]), 'updated': set(), 'added': set(), - 'removed': set([1])} + 'removed': set([1]), + 'timestamps': {}} # Device 2 disappeared. fake_current = set([3]) updated = set() @@ -274,7 +313,8 @@ expected = {'current': set([3]), 'updated': set(), 'added': set([3]), - 'removed': set([1, 2])} + 'removed': set([1, 2]), + 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=True) @@ -283,13 +323,15 @@ previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} fake_current = set([1, 2]) updated = set([1]) expected = {'current': set([1, 2]), 'updated': set([1]), 'added': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) @@ -298,13 +340,15 @@ previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} fake_current = set([1, 2]) updated = set([3]) expected = {'current': set([1, 2]), 'updated': set(), 'added': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) @@ -313,13 +357,15 @@ previous = {'current': set([1, 2]), 'updated': set([1]), 'added': set(), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} fake_current = set([1, 2]) updated = set([2]) expected = {'current': set([1, 2]), 'updated': set([1, 2]), 'added': set([1, 2]), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=True) @@ -332,7 +378,8 @@ expected = {'current': set([1, 2]), 'updated': set(), 'added': set([1, 2]), - 'removed': set()} + 'removed': set(), + 'timestamps': {}} with mock.patch.object(arp_protect, 'delete_unreferenced_arp_protection') as de_arp: self._test_scan_devices(previous, updated, fake_current, expected, @@ -359,7 +406,7 @@ 'tap4'])) agent.treat_devices_removed.assert_called_with(set(['tap1'])) - def test_treat_devices_added_updated_admin_state_up_true(self): + def test_treat_devices_added_updated_no_local_interface(self): agent = self.agent mock_details = {'device': 'dev123', 'port_id': 'port123', @@ -367,38 +414,40 @@ 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, - 'physical_network': 'physnet1'} + 'physical_network': 'physnet1', + 'device_owner': constants.DEVICE_OWNER_NETWORK_PREFIX} + agent.ext_manager = mock.Mock() agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] - agent.br_mgr = mock.Mock() - agent.br_mgr.add_interface.return_value = True - resync_needed = agent.treat_devices_added_updated(set(['tap1'])) - - self.assertFalse(resync_needed) - agent.br_mgr.add_interface.assert_called_with('net123', 'vlan', - 'physnet1', 100, - 'port123') - self.assertTrue(agent.plugin_rpc.update_device_up.called) + agent.mgr = mock.Mock() + agent.mgr.plug_interface.return_value = False + agent.mgr.ensure_port_admin_state = mock.Mock() + agent.treat_devices_added_updated(set(['tap1'])) + self.assertFalse(agent.mgr.ensure_port_admin_state.called) - def test_treat_devices_added_updated_admin_state_up_false(self): + def test_treat_devices_added_updated_admin_state_up_true(self): agent = self.agent mock_details = {'device': 'dev123', 'port_id': 'port123', 'network_id': 'net123', - 'admin_state_up': False, + 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, - 'physical_network': 'physnet1'} + 'physical_network': 'physnet1', + 'device_owner': constants.DEVICE_OWNER_NETWORK_PREFIX} agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] - agent.remove_port_binding = mock.Mock() + agent.br_mgr = mock.Mock() + agent.br_mgr.add_interface.return_value = True + agent._ensure_port_admin_state = mock.Mock() resync_needed = agent.treat_devices_added_updated(set(['tap1'])) self.assertFalse(resync_needed) - agent.remove_port_binding.assert_called_with('net123', - 'physnet1', - 'port123') - self.assertFalse(agent.plugin_rpc.update_device_up.called) + agent.br_mgr.add_interface.assert_called_with( + 'net123', 'vlan', 'physnet1', + 100, 'port123', + constants.DEVICE_OWNER_NETWORK_PREFIX) + self.assertTrue(agent.plugin_rpc.update_device_up.called) def test_treat_devices_added_updated_prevent_arp_spoofing_true(self): agent = self.agent @@ -444,6 +493,23 @@ self.agent._report_state() self.assertTrue(self.agent.fullsync) + def _test_ensure_port_admin_state(self, admin_state): + port_id = 'fake_id' + with mock.patch.object(ip_lib, 'IPDevice') as dev_mock: + self.agent._ensure_port_admin_state(port_id, admin_state) + + tap_name = self.agent.br_mgr.get_tap_device_name(port_id) + self.assertEqual(admin_state, + dev_mock(tap_name).link.set_up.called) + self.assertNotEqual(admin_state, + dev_mock(tap_name).link.set_down.called) + + def test_ensure_port_admin_state_up(self): + self._test_ensure_port_admin_state(True) + + def test_ensure_port_admin_state_down(self): + self._test_ensure_port_admin_state(False) + class TestLinuxBridgeManager(base.BaseTestCase): def setUp(self): @@ -729,16 +795,16 @@ de_fn.return_value = True self.assertEqual(self.lbm.ensure_vlan("eth0", "1"), "eth0.1") de_fn.return_value = False - with mock.patch.object(utils, 'execute') as exec_fn: - exec_fn.return_value = False - self.assertEqual(self.lbm.ensure_vlan("eth0", "1"), "eth0.1") - # FIXME(kevinbenton): validate the params to the exec_fn calls - self.assertEqual(exec_fn.call_count, 2) - exec_fn.return_value = True - self.assertIsNone(self.lbm.ensure_vlan("eth0", "1")) - self.assertEqual(exec_fn.call_count, 3) + vlan_dev = FakeIpDevice() + with mock.patch.object(vlan_dev, 'disable_ipv6') as dv6_fn,\ + mock.patch.object(self.lbm.ip, 'add_vlan', + return_value=vlan_dev) as add_vlan_fn: + retval = self.lbm.ensure_vlan("eth0", "1") + self.assertEqual("eth0.1", retval) + add_vlan_fn.assert_called_with('eth0.1', 'eth0', '1') + dv6_fn.assert_called_once_with() - def test_ensure_vxlan(self): + def test_ensure_vxlan(self, expected_proxy=True): seg_id = "12345678" self.lbm.local_int = 'eth0' self.lbm.vxlan_mode = lconst.VXLAN_MCAST @@ -746,21 +812,27 @@ de_fn.return_value = True self.assertEqual(self.lbm.ensure_vxlan(seg_id), "vxlan-" + seg_id) de_fn.return_value = False - with mock.patch.object(self.lbm.ip, - 'add_vxlan') as add_vxlan_fn: - add_vxlan_fn.return_value = FakeIpDevice() - self.assertEqual(self.lbm.ensure_vxlan(seg_id), - "vxlan-" + seg_id) + vxlan_dev = FakeIpDevice() + with mock.patch.object(vxlan_dev, 'disable_ipv6') as dv6_fn,\ + mock.patch.object(self.lbm.ip, 'add_vxlan', + return_value=vxlan_dev) as add_vxlan_fn: + retval = self.lbm.ensure_vxlan(seg_id) + self.assertEqual("vxlan-" + seg_id, retval) add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id, group="224.0.0.1", dev=self.lbm.local_int) + dv6_fn.assert_called_once_with() cfg.CONF.set_override('l2_population', 'True', 'VXLAN') self.assertEqual(self.lbm.ensure_vxlan(seg_id), "vxlan-" + seg_id) add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id, group="224.0.0.1", dev=self.lbm.local_int, - proxy=True) + proxy=expected_proxy) + + def test_ensure_vxlan_arp_responder_disabled(self): + cfg.CONF.set_override('arp_responder', False, 'VXLAN') + self.test_ensure_vxlan(expected_proxy=False) def test_update_interface_ip_details(self): gwdict = dict(gateway='1.1.1.1', @@ -877,22 +949,31 @@ self.assertFalse(self.lbm.add_tap_interface("123", p_const.TYPE_VLAN, "physnet1", None, - "tap1")) + "tap1", "foo")) @mock.patch.object(ip_lib, "device_exists", return_value=True) def test_add_tap_interface_with_other_error(self, exists): with mock.patch.object(self.lbm, "_add_tap_interface", side_effect=RuntimeError("No more fuel")): self.assertRaises(RuntimeError, self.lbm.add_tap_interface, "123", - p_const.TYPE_VLAN, "physnet1", None, "tap1") + p_const.TYPE_VLAN, "physnet1", None, "tap1", + "foo") + + def test_add_tap_interface_owner_other(self): + with mock.patch.object(ip_lib, "device_exists"): + with mock.patch.object(self.lbm, "ensure_local_bridge"): + self.assertTrue(self.lbm.add_tap_interface("123", + p_const.TYPE_LOCAL, + "physnet1", None, + "tap1", "foo")) - def test_add_tap_interface(self): + def _test_add_tap_interface(self, dev_owner_prefix): with mock.patch.object(ip_lib, "device_exists") as de_fn: de_fn.return_value = False self.assertFalse( self.lbm.add_tap_interface("123", p_const.TYPE_VLAN, - "physnet1", "1", "tap1") - ) + "physnet1", "1", "tap1", + dev_owner_prefix)) de_fn.return_value = True bridge_device = mock.Mock() @@ -906,14 +987,16 @@ self.assertTrue(self.lbm.add_tap_interface("123", p_const.TYPE_LOCAL, "physnet1", None, - "tap1")) + "tap1", + dev_owner_prefix)) en_fn.assert_called_with("123", "brq123") self.lbm.bridge_mappings = {"physnet1": "brq999"} self.assertTrue(self.lbm.add_tap_interface("123", p_const.TYPE_LOCAL, "physnet1", None, - "tap1")) + "tap1", + dev_owner_prefix)) en_fn.assert_called_with("123", "brq999") get_br.return_value = False @@ -921,8 +1004,8 @@ self.assertFalse(self.lbm.add_tap_interface("123", p_const.TYPE_LOCAL, "physnet1", None, - "tap1")) - + "tap1", + dev_owner_prefix)) with mock.patch.object(self.lbm, "ensure_physical_in_bridge") as ens_fn,\ mock.patch.object(self.lbm, @@ -933,21 +1016,31 @@ self.assertFalse(self.lbm.add_tap_interface("123", p_const.TYPE_VLAN, "physnet1", "1", - "tap1")) + "tap1", + dev_owner_prefix)) ens_fn.return_value = "eth0.1" get_br.return_value = "brq123" self.lbm.add_tap_interface("123", p_const.TYPE_VLAN, - "physnet1", "1", "tap1") + "physnet1", "1", "tap1", + dev_owner_prefix) en_mtu_fn.assert_called_once_with("tap1", "eth0.1") bridge_device.addif.assert_called_once_with("tap1") + def test_add_tap_interface_owner_network(self): + self._test_add_tap_interface(constants.DEVICE_OWNER_NETWORK_PREFIX) + + def test_add_tap_interface_owner_neutron(self): + self._test_add_tap_interface(constants.DEVICE_OWNER_NEUTRON_PREFIX) + def test_add_interface(self): with mock.patch.object(self.lbm, "add_tap_interface") as add_tap: self.lbm.add_interface("123", p_const.TYPE_VLAN, "physnet-1", - "1", "234") + "1", "234", + constants.DEVICE_OWNER_NETWORK_PREFIX) add_tap.assert_called_with("123", p_const.TYPE_VLAN, "physnet-1", - "1", "tap234") + "1", "tap234", + constants.DEVICE_OWNER_NETWORK_PREFIX) def test_delete_bridge(self): bridge_device = mock.Mock() diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -147,6 +147,17 @@ 'removed': set(['1'])} self.mock_scan_devices(expected, mock_current, registered, updated) + def test_scan_devices_updated_and_removed(self): + registered = set(['1', '2']) + # '1' is in removed and updated tuple + updated = set(['1']) + mock_current = set(['2', '3']) + expected = {'current': set(['2', '3']), + 'updated': set(), + 'added': set(['3']), + 'removed': set(['1'])} + self.mock_scan_devices(expected, mock_current, registered, updated) + def test_scan_devices_new_updates(self): registered = set(['1']) updated = set(['2']) @@ -188,6 +199,56 @@ 'mac4'])) agent.treat_devices_removed.assert_called_with(set(['mac1'])) + def test_treat_devices_added_updated_and_removed(self): + agent = self.agent + MAC1 = 'aa:bb:cc:dd:ee:ff' + SLOT1 = '1:2:3.0' + MAC2 = 'aa:bb:cc:dd:ee:fe' + SLOT2 = '1:3:3.0' + mac_pci_slot_device1 = (MAC1, SLOT1) + mac_pci_slot_device2 = (MAC2, SLOT2) + mock_device1_details = {'device': MAC1, + 'port_id': 'port123', + 'network_id': 'net123', + 'admin_state_up': True, + 'network_type': 'vlan', + 'segmentation_id': 100, + 'profile': {'pci_slot': SLOT1}, + 'physical_network': 'physnet1', + 'port_security_enabled': False} + mock_device2_details = {'device': MAC2, + 'port_id': 'port124', + 'network_id': 'net123', + 'admin_state_up': True, + 'network_type': 'vlan', + 'segmentation_id': 100, + 'profile': {'pci_slot': SLOT2}, + 'physical_network': 'physnet1', + 'port_security_enabled': False} + agent.plugin_rpc = mock.Mock() + agent.plugin_rpc.get_devices_details_list.return_value = ( + [mock_device1_details]) + agent.treat_devices_added_updated(set([MAC1])) + self.assertEqual({'net123': [{'port_id': 'port123', + 'device': mac_pci_slot_device1}]}, + agent.network_ports) + agent.plugin_rpc.get_devices_details_list.return_value = ( + [mock_device2_details]) + # add the second device and check the network_ports dict + agent.treat_devices_added_updated(set([MAC2])) + self.assertEqual( + {'net123': [{'port_id': 'port123', + 'device': mac_pci_slot_device1}, {'port_id': 'port124', + 'device': mac_pci_slot_device2}]}, + agent.network_ports) + with mock.patch.object(agent.plugin_rpc, + "update_device_down"): + agent.treat_devices_removed([mac_pci_slot_device2]) + # remove the second device and check the network_ports dict + self.assertEqual({'net123': [{'port_id': 'port123', + 'device': mac_pci_slot_device1}]}, + agent.network_ports) + def test_treat_devices_added_updated_admin_state_up_true(self): agent = self.agent mock_details = {'device': 'aa:bb:cc:dd:ee:ff', @@ -265,6 +326,35 @@ self.assertFalse(resync_needed) self.assertFalse(agent.plugin_rpc.update_device_up.called) + def test_update_and_clean_network_ports(self): + network_id1 = 'network_id1' + network_id2 = 'network_id2' + + port_id1 = 'port_id1' + port_id2 = 'port_id2' + mac_slot_1 = ('mac1', 'slot1') + mac_slot_2 = ('mac2', 'slot2') + + self.agent.network_ports[network_id1] = [{'port_id': port_id1, + 'device': mac_slot_1}, {'port_id': port_id2, 'device': mac_slot_2}] + + self.agent._update_network_ports(network_id2, port_id1, mac_slot_1) + + self.assertEqual({network_id1: [{'port_id': port_id2, + 'device': mac_slot_2}], network_id2: [ + {'port_id': port_id1, 'device': mac_slot_1}]}, + self.agent.network_ports) + + cleaned_port_id = self.agent._clean_network_ports(mac_slot_1) + self.assertEqual(cleaned_port_id, port_id1) + + self.assertEqual({network_id1: [{'port_id': port_id2, + 'device': mac_slot_2}]}, + self.agent.network_ports) + + cleaned_port_id = self.agent._clean_network_ports(mac_slot_2) + self.assertEqual({}, self.agent.network_ports) + class FakeAgent(object): def __init__(self): @@ -299,3 +389,20 @@ kwargs = {'context': self.context, 'port': port} self.sriov_rpc_callback.port_update(**kwargs) self.assertEqual(set(), self.agent.updated_devices) + + def test_network_update(self): + TEST_NETWORK_ID1 = "n1" + TEST_NETWORK_ID2 = "n2" + TEST_PORT_ID1 = 'p1' + TEST_PORT_ID2 = 'p2' + network1 = {'id': TEST_NETWORK_ID1} + port1 = {'id': TEST_PORT_ID1, 'network_id': TEST_NETWORK_ID1} + port2 = {'id': TEST_PORT_ID2, 'network_id': TEST_NETWORK_ID2} + self.agent.network_ports = { + TEST_NETWORK_ID1: [{'port_id': port1['id'], + 'device': ('mac1', 'slot1')}], + TEST_NETWORK_ID2: [{'port_id': port2['id'], + 'device': ('mac2', 'slot2')}]} + kwargs = {'context': self.context, 'network': network1} + self.sriov_rpc_callback.network_update(**kwargs) + self.assertEqual(set([('mac1', 'slot1')]), self.agent.updated_devices) diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py 2016-06-10 01:43:08.000000000 +0000 @@ -346,9 +346,7 @@ call._send_msg(ofpp.OFPFlowMod(dp, cookie=0, instructions=[ - ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ - ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0), - ]), + ofpp.OFPInstructionGotoTable(table_id=25), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, @@ -360,9 +358,7 @@ call._send_msg(ofpp.OFPFlowMod(dp, cookie=0, instructions=[ - ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ - ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0), - ]), + ofpp.OFPInstructionGotoTable(table_id=25), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py 2016-06-10 01:43:08.000000000 +0000 @@ -38,7 +38,6 @@ self.br.setup_default_table() (dp, ofp, ofpp) = self._get_dp() expected = [ - call.delete_flows(), call._send_msg(ofpp.OFPFlowMod(dp, cookie=0, instructions=[ diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py 2016-06-10 01:43:08.000000000 +0000 @@ -215,10 +215,10 @@ ip_addresses = ['192.0.2.1', '192.0.2.2/32'] self.br.install_arp_spoofing_protection(port, ip_addresses) expected = [ - call.add_flow(proto='arp', actions='normal', + call.add_flow(proto='arp', actions='resubmit(,25)', arp_spa='192.0.2.1', priority=2, table=24, in_port=8888), - call.add_flow(proto='arp', actions='normal', + call.add_flow(proto='arp', actions='resubmit(,25)', arp_spa='192.0.2.2/32', priority=2, table=24, in_port=8888), call.add_flow(priority=10, table=0, in_port=8888, diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_phys.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_phys.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_phys.py 2016-03-29 17:44:55.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_phys.py 2016-06-10 01:43:03.000000000 +0000 @@ -37,7 +37,6 @@ def test_setup_default_table(self): self.br.setup_default_table() expected = [ - call.delete_flows(), call.add_flow(priority=0, table=0, actions='normal'), ] self.assertEqual(expected, self.mock.mock_calls) diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py 2016-06-10 01:43:08.000000000 +0000 @@ -421,6 +421,40 @@ vif_port_set, registered_ports, port_tags_dict=port_tags_dict) self.assertEqual(expected, actual) + def test_add_port_tag_info(self): + self.agent.local_vlan_map["net1"] = mock.Mock() + self.agent.local_vlan_map["net1"].vlan = "1" + ovs_db_list = [{'name': 'tap1', + 'tag': [], + 'other_config': {'segmentation_id': '1'}}, + {'name': 'tap2', + 'tag': [], + 'other_config': {}}, + {'name': 'tap3', + 'tag': [], + 'other_config': None}] + vif_port1 = mock.Mock() + vif_port1.port_name = 'tap1' + vif_port2 = mock.Mock() + vif_port2.port_name = 'tap2' + vif_port3 = mock.Mock() + vif_port3.port_name = 'tap3' + port_details = [ + {'network_id': 'net1', 'vif_port': vif_port1}, + {'network_id': 'net1', 'vif_port': vif_port2}, + {'network_id': 'net1', 'vif_port': vif_port3}] + with mock.patch.object(self.agent, 'int_br') as int_br: + int_br.get_ports_attributes.return_value = ovs_db_list + self.agent._add_port_tag_info(port_details) + set_db_attribute_calls = \ + [mock.call.set_db_attribute("Port", "tap1", + "other_config", {"segmentation_id": "1", "tag": "1"}), + mock.call.set_db_attribute("Port", "tap2", + "other_config", {"tag": "1"}), + mock.call.set_db_attribute("Port", "tap3", + "other_config", {"tag": "1"})] + int_br.assert_has_calls(set_db_attribute_calls, any_order=True) + def test_bind_devices(self): devices_up = ['tap1'] devices_down = ['tap2'] @@ -841,9 +875,12 @@ parent.attach_mock(int_br, 'int_br') phys_br.add_patch_port.return_value = "phy_ofport" int_br.add_patch_port.return_value = "int_ofport" + phys_br.get_port_ofport.return_value = ovs_lib.INVALID_OFPORT + int_br.get_port_ofport.return_value = ovs_lib.INVALID_OFPORT self.agent.setup_physical_bridges({"physnet1": "br-eth"}) expected_calls = [ mock.call.phys_br_cls('br-eth'), + mock.call.phys_br.set_agent_uuid_stamp(mock.ANY), mock.call.phys_br.setup_controllers(mock.ANY), mock.call.phys_br.setup_default_table(), mock.call.int_br.db_get_val('Interface', 'int-br-eth', @@ -851,18 +888,20 @@ # Have to use __getattr__ here to avoid mock._Call.__eq__ # method being called mock.call.int_br.db_get_val().__getattr__('__eq__')('veth'), + mock.call.int_br.get_port_ofport('int-br-eth'), mock.call.int_br.add_patch_port('int-br-eth', constants.NONEXISTENT_PEER), + mock.call.phys_br.get_port_ofport('phy-br-eth'), mock.call.phys_br.add_patch_port('phy-br-eth', constants.NONEXISTENT_PEER), mock.call.int_br.drop_port(in_port='int_ofport'), mock.call.phys_br.drop_port(in_port='phy_ofport'), mock.call.int_br.set_db_attribute('Interface', 'int-br-eth', - 'options:peer', - 'phy-br-eth'), + 'options', + {'peer': 'phy-br-eth'}), mock.call.phys_br.set_db_attribute('Interface', 'phy-br-eth', - 'options:peer', - 'int-br-eth'), + 'options', + {'peer': 'int-br-eth'}), ] parent.assert_has_calls(expected_calls) self.assertEqual(self.agent.int_ofports["physnet1"], @@ -921,25 +960,30 @@ parent.attach_mock(int_br, 'int_br') phys_br.add_patch_port.return_value = "phy_ofport" int_br.add_patch_port.return_value = "int_ofport" + phys_br.get_port_ofport.return_value = ovs_lib.INVALID_OFPORT + int_br.get_port_ofport.return_value = ovs_lib.INVALID_OFPORT self.agent.setup_physical_bridges({"physnet1": "br-eth"}) expected_calls = [ mock.call.phys_br_cls('br-eth'), + mock.call.phys_br.set_agent_uuid_stamp(mock.ANY), mock.call.phys_br.setup_controllers(mock.ANY), mock.call.phys_br.setup_default_table(), mock.call.int_br.delete_port('int-br-eth'), mock.call.phys_br.delete_port('phy-br-eth'), + mock.call.int_br.get_port_ofport('int-br-eth'), mock.call.int_br.add_patch_port('int-br-eth', constants.NONEXISTENT_PEER), + mock.call.phys_br.get_port_ofport('phy-br-eth'), mock.call.phys_br.add_patch_port('phy-br-eth', constants.NONEXISTENT_PEER), mock.call.int_br.drop_port(in_port='int_ofport'), mock.call.phys_br.drop_port(in_port='phy_ofport'), mock.call.int_br.set_db_attribute('Interface', 'int-br-eth', - 'options:peer', - 'phy-br-eth'), + 'options', + {'peer': 'phy-br-eth'}), mock.call.phys_br.set_db_attribute('Interface', 'phy-br-eth', - 'options:peer', - 'int-br-eth'), + 'options', + {'peer': 'int-br-eth'}), ] parent.assert_has_calls(expected_calls) self.assertEqual(self.agent.int_ofports["physnet1"], @@ -2092,6 +2136,58 @@ def test_port_bound_for_dvr_with_csnat_ports(self): self._setup_for_dvr_test() + int_br, tun_br = self._port_bound_for_dvr_with_csnat_ports() + lvid = self.agent.local_vlan_map[self._net_uuid].vlan + expected_on_int_br = [ + mock.call.install_dvr_to_src_mac( + network_type='vxlan', + gateway_mac='aa:bb:cc:11:22:33', + dst_mac=self._port.vif_mac, + dst_port=self._port.ofport, + vlan_tag=lvid, + ), + ] + self._expected_port_bound(self._port, lvid, is_dvr=False) + self.assertEqual(expected_on_int_br, int_br.mock_calls) + expected_on_tun_br = [ + mock.call.provision_local_vlan( + network_type='vxlan', + lvid=lvid, + segmentation_id=None, + distributed=True, + ), + ] + self.assertEqual(expected_on_tun_br, tun_br.mock_calls) + + def test_port_bound_for_dvr_with_csnat_ports_ofport_change(self): + self._setup_for_dvr_test() + self._port_bound_for_dvr_with_csnat_ports() + # simulate a replug + self._port.ofport = 12 + int_br, tun_br = self._port_bound_for_dvr_with_csnat_ports() + lvid = self.agent.local_vlan_map[self._net_uuid].vlan + expected_on_int_br = [ + mock.call.delete_dvr_to_src_mac( + network_type='vxlan', + dst_mac=self._port.vif_mac, + vlan_tag=lvid, + ), + mock.call.install_dvr_to_src_mac( + network_type='vxlan', + gateway_mac='aa:bb:cc:11:22:33', + dst_mac=self._port.vif_mac, + dst_port=self._port.ofport, + vlan_tag=lvid, + ), + ] + self._expected_port_bound(self._port, lvid, is_dvr=False) + self.assertEqual(expected_on_int_br, int_br.mock_calls) + # a local vlan was already provisioned so there should be no new + # calls to tunbr + self.assertEqual([], tun_br.mock_calls) + # make sure ofport was updated + self.assertEqual(12, + self.agent.dvr_agent.local_ports[self._port.vif_id].ofport) + + def _port_bound_for_dvr_with_csnat_ports(self): int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) int_br.set_db_attribute.return_value = True @@ -2117,26 +2213,7 @@ None, None, self._fixed_ips, n_const.DEVICE_OWNER_ROUTER_SNAT, False) - lvid = self.agent.local_vlan_map[self._net_uuid].vlan - expected_on_int_br = [ - mock.call.install_dvr_to_src_mac( - network_type='vxlan', - gateway_mac='aa:bb:cc:11:22:33', - dst_mac=self._port.vif_mac, - dst_port=self._port.ofport, - vlan_tag=lvid, - ), - ] + self._expected_port_bound(self._port, lvid, is_dvr=False) - self.assertEqual(expected_on_int_br, int_br.mock_calls) - expected_on_tun_br = [ - mock.call.provision_local_vlan( - network_type='vxlan', - lvid=lvid, - segmentation_id=None, - distributed=True, - ), - ] - self.assertEqual(expected_on_tun_br, tun_br.mock_calls) + return int_br, tun_br def test_treat_devices_removed_for_dvr_interface(self): self._test_treat_devices_removed_for_dvr_interface() diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py 2016-06-10 01:43:08.000000000 +0000 @@ -81,6 +81,7 @@ self.INT_BRIDGE = 'integration_bridge' self.TUN_BRIDGE = 'tunnel_bridge' self.MAP_TUN_BRIDGE = 'tun_br_map' + self.AUX_BRIDGE = 'ancillary_bridge' self.NET_MAPPING = {'net1': self.MAP_TUN_BRIDGE} self.INT_OFPORT = 11111 self.TUN_OFPORT = 22222 @@ -104,6 +105,8 @@ self.br_tun_cls('br-tun')), self.MAP_TUN_BRIDGE: mock.create_autospec( self.br_phys_cls('br-phys')), + self.AUX_BRIDGE: mock.create_autospec( + ovs_lib.OVSBridge('br-aux')), } self.ovs_int_ofports = { 'patch-tun': self.TUN_OFPORT, @@ -122,11 +125,18 @@ self.mock_tun_bridge_cls = mock.patch(self._BR_TUN_CLASS, autospec=True).start() self.mock_tun_bridge_cls.side_effect = lookup_br + self.mock_aux_bridge_cls = mock.patch( + 'neutron.agent.common.ovs_lib.OVSBridge', + autospec=True).start() + self.mock_aux_bridge_cls.side_effect = lookup_br self.mock_int_bridge = self.ovs_bridges[self.INT_BRIDGE] + self.mock_int_bridge.br_name = self.INT_BRIDGE self.mock_int_bridge.add_port.return_value = self.MAP_TUN_INT_OFPORT self.mock_int_bridge.add_patch_port.side_effect = ( lambda tap, peer: self.ovs_int_ofports[tap]) + self.mock_int_bridge.get_port_ofport.return_value = ( + ovs_lib.INVALID_OFPORT) self.mock_int_bridge.get_vif_ports.return_value = [] self.mock_int_bridge.get_ports_attributes.return_value = [] self.mock_int_bridge.db_get_val.return_value = {} @@ -137,8 +147,11 @@ self.MAP_TUN_PHY_OFPORT) self.mock_map_tun_bridge.add_patch_port.return_value = ( self.MAP_TUN_PHY_OFPORT) + self.mock_map_tun_bridge.get_port_ofport.return_value = ( + ovs_lib.INVALID_OFPORT) self.mock_tun_bridge = self.ovs_bridges[self.TUN_BRIDGE] + self.mock_tun_bridge.br_name = self.TUN_BRIDGE self.mock_tun_bridge.add_port.return_value = self.INT_OFPORT self.mock_tun_bridge.add_patch_port.return_value = self.INT_OFPORT @@ -155,7 +168,13 @@ 'get_bridges').start() self.get_bridges.return_value = [self.INT_BRIDGE, self.TUN_BRIDGE, - self.MAP_TUN_BRIDGE] + self.MAP_TUN_BRIDGE, + self.AUX_BRIDGE] + self.get_bridge_external_bridge_id = mock.patch.object( + ovs_lib.BaseOVS, + 'get_bridge_external_bridge_id').start() + self.get_bridge_external_bridge_id.side_effect = ( + lambda bridge: bridge if bridge in self.ovs_bridges else None) self.execute = mock.patch('neutron.agent.common.utils.execute').start() @@ -181,18 +200,21 @@ mock.call.create(), mock.call.set_secure_mode(), mock.call.setup_controllers(mock.ANY), - mock.call.delete_port('patch-tun'), mock.call.setup_default_table(), ] self.mock_map_tun_bridge_expected = [ + mock.call.set_agent_uuid_stamp(mock.ANY), mock.call.setup_controllers(mock.ANY), mock.call.setup_default_table(), + mock.call.get_port_ofport('phy-%s' % self.MAP_TUN_BRIDGE), mock.call.add_patch_port('phy-%s' % self.MAP_TUN_BRIDGE, - constants.NONEXISTENT_PEER), ] + constants.NONEXISTENT_PEER), + ] self.mock_int_bridge_expected += [ mock.call.db_get_val('Interface', 'int-%s' % self.MAP_TUN_BRIDGE, 'type'), + mock.call.get_port_ofport('int-%s' % self.MAP_TUN_BRIDGE), mock.call.add_patch_port('int-%s' % self.MAP_TUN_BRIDGE, constants.NONEXISTENT_PEER), ] @@ -201,13 +223,17 @@ mock.call.drop_port(in_port=self.MAP_TUN_INT_OFPORT), mock.call.set_db_attribute( 'Interface', 'int-%s' % self.MAP_TUN_BRIDGE, - 'options:peer', 'phy-%s' % self.MAP_TUN_BRIDGE), + 'options', {'peer': 'phy-%s' % self.MAP_TUN_BRIDGE}), ] self.mock_map_tun_bridge_expected += [ mock.call.drop_port(in_port=self.MAP_TUN_PHY_OFPORT), mock.call.set_db_attribute( 'Interface', 'phy-%s' % self.MAP_TUN_BRIDGE, - 'options:peer', 'int-%s' % self.MAP_TUN_BRIDGE), + 'options', {'peer': 'int-%s' % self.MAP_TUN_BRIDGE}), + ] + + self.mock_aux_bridge = self.ovs_bridges[self.AUX_BRIDGE] + self.mock_aux_bridge_expected = [ ] self.mock_tun_bridge_expected = [ @@ -280,6 +306,8 @@ self.mock_map_tun_bridge_expected) self._verify_mock_call(self.mock_tun_bridge, self.mock_tun_bridge_expected) + self._verify_mock_call(self.mock_aux_bridge, + self.mock_aux_bridge_expected) self._verify_mock_call(self.device_exists, self.device_exists_expected) self._verify_mock_call(self.ipdevice, self.ipdevice_expected) self._verify_mock_call(self.ipwrapper, self.ipwrapper_expected) @@ -511,8 +539,16 @@ self.mock_int_bridge_expected += [ mock.call.check_canary_table(), + mock.call.cleanup_flows(), mock.call.check_canary_table() ] + self.mock_tun_bridge_expected += [ + mock.call.cleanup_flows() + ] + self.mock_map_tun_bridge_expected += [ + mock.call.cleanup_flows() + ] + # No cleanup is expected on ancillary bridge self.ovs_bridges[self.INT_BRIDGE].check_canary_table.return_value = \ constants.OVS_NORMAL @@ -522,22 +558,29 @@ 'scan_ports') as scan_ports,\ mock.patch.object( self.mod_agent.OVSNeutronAgent, + 'scan_ancillary_ports') as scan_ancillary_ports,\ + mock.patch.object( + self.mod_agent.OVSNeutronAgent, 'process_network_ports') as process_network_ports,\ + mock.patch.object( + self.mod_agent.OVSNeutronAgent, + 'process_ancillary_network_ports') as process_anc_ports,\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'tunnel_sync'),\ mock.patch.object(time, 'sleep'),\ mock.patch.object( self.mod_agent.OVSNeutronAgent, - 'update_stale_ofport_rules') as update_stale,\ - mock.patch.object( - self.mod_agent.OVSNeutronAgent, - 'cleanup_stale_flows') as cleanup: + 'update_stale_ofport_rules') as update_stale: log_exception.side_effect = Exception( 'Fake exception to get out of the loop') scan_ports.side_effect = [reply2, reply3] + scan_ancillary_ports.return_value = { + 'current': set([]), 'added': set([]), 'removed': set([]), + } update_stale.return_value = [] process_network_ports.side_effect = [ False, Exception('Fake exception to get out of the loop')] + process_anc_ports.return_value = False n_agent = self._build_agent() @@ -566,7 +609,6 @@ 'added': set([])}, False) ]) - cleanup.assert_called_once_with() self.assertTrue(update_stale.called) self._verify_mock_calls() @@ -601,11 +643,12 @@ mock.call.create(), mock.call.set_secure_mode(), mock.call.setup_controllers(mock.ANY), - mock.call.delete_port('patch-tun'), + mock.call.setup_default_table(), ] self.mock_map_tun_bridge_expected = [ + mock.call.set_agent_uuid_stamp(mock.ANY), mock.call.setup_controllers(mock.ANY), mock.call.setup_default_table(), mock.call.add_port(self.intb), @@ -623,6 +666,10 @@ mock.call.drop_port(in_port=self.MAP_TUN_PHY_OFPORT), ] + self.mock_aux_bridge = self.ovs_bridges[self.AUX_BRIDGE] + self.mock_aux_bridge_expected = [ + ] + self.mock_tun_bridge_expected = [ mock.call.set_agent_uuid_stamp(mock.ANY), mock.call.bridge_exists(mock.ANY), diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/extensions/test_port_security.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/extensions/test_port_security.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/extensions/test_port_security.py 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/extensions/test_port_security.py 2016-06-10 01:43:03.000000000 +0000 @@ -0,0 +1,39 @@ +# Copyright (c) 2015 OpenStack Foundation. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock + +from neutron.extensions import portsecurity as psec +from neutron.plugins.ml2.extensions import port_security +from neutron.tests.unit.plugins.ml2 import test_plugin + + +class TestML2ExtensionPortSecurity(test_plugin.Ml2PluginV2TestCase): + def _test_extend_dict_no_port_security(self, func): + """Test extend_*_dict won't crash if port_security item is None.""" + for db_data in ({'port_security': None, 'name': 'net1'}, {}): + response_data = {} + session = mock.Mock() + + driver = port_security.PortSecurityExtensionDriver() + getattr(driver, func)(session, db_data, response_data) + + self.assertTrue(response_data[psec.PORTSECURITY]) + + def test_extend_port_dict_no_port_security(self): + self._test_extend_dict_no_port_security('extend_port_dict') + + def test_extend_network_dict_no_port_security(self): + self._test_extend_dict_no_port_security('extend_network_dict') diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/test_db.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/test_db.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/test_db.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/test_db.py 2016-06-10 01:43:08.000000000 +0000 @@ -13,10 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import warnings + import mock from oslo_utils import uuidutils from sqlalchemy.orm import query +from neutron.common import constants from neutron import context from neutron.db import db_base_plugin_v2 from neutron.db import l3_db @@ -385,3 +388,34 @@ network_id, port_id_1, router.id, 'foo_host_id_2') ports = ml2_db.get_dvr_port_bindings(self.ctx.session, 'foo_port_id') self.assertEqual(2, len(ports)) + + def test_dvr_port_binding_deleted_by_port_deletion(self): + with self.ctx.session.begin(subtransactions=True): + self.ctx.session.add(models_v2.Network(id='network_id')) + device_owner = constants.DEVICE_OWNER_DVR_INTERFACE + port = models_v2.Port( + id='port_id', + network_id='network_id', + mac_address='00:11:22:33:44:55', + admin_state_up=True, + status=constants.PORT_STATUS_ACTIVE, + device_id='device_id', + device_owner=device_owner) + self.ctx.session.add(port) + binding_kwarg = { + 'port_id': 'port_id', + 'host': 'host', + 'vif_type': portbindings.VIF_TYPE_UNBOUND, + 'vnic_type': portbindings.VNIC_NORMAL, + 'router_id': 'router_id', + 'status': constants.PORT_STATUS_DOWN + } + self.ctx.session.add(models.DVRPortBinding(**binding_kwarg)) + binding_kwarg['host'] = 'another-host' + self.ctx.session.add(models.DVRPortBinding(**binding_kwarg)) + with warnings.catch_warnings(record=True) as warning_list: + with self.ctx.session.begin(subtransactions=True): + self.ctx.session.delete(port) + self.assertEqual([], warning_list) + ports = ml2_db.get_dvr_port_bindings(self.ctx.session, 'port_id') + self.assertEqual(0, len(ports)) diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/test_ext_portsecurity.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/test_ext_portsecurity.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/test_ext_portsecurity.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/test_ext_portsecurity.py 2016-06-10 01:43:08.000000000 +0000 @@ -34,8 +34,6 @@ def test_create_net_port_security_default(self): _core_plugin = manager.NeutronManager.get_plugin() admin_ctx = context.get_admin_context() - _default_value = (psec.EXTENDED_ATTRIBUTES_2_0['networks'] - [psec.PORTSECURITY]['default']) args = {'network': {'name': 'test', 'tenant_id': '', @@ -48,7 +46,7 @@ finally: if network: _core_plugin.delete_network(admin_ctx, network['id']) - self.assertEqual(_default_value, _value) + self.assertEqual(psec.DEFAULT_PORT_SECURITY, _value) def test_create_port_with_secgroup_none_and_port_security_false(self): if self._skip_security_group: diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/test_plugin.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/test_plugin.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/test_plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/test_plugin.py 2016-06-10 01:43:08.000000000 +0000 @@ -1171,6 +1171,18 @@ mech_context._binding.router_id) self.assertEqual(host_id, mech_context._binding.host) + def test_update_dvr_port_binding_on_concurrent_port_delete(self): + plugin = manager.NeutronManager.get_plugin() + with self.port() as port: + port = { + 'id': port['port']['id'], + portbindings.HOST_ID: 'foo_host', + } + with mock.patch.object(plugin, 'get_port', new=plugin.delete_port): + res = plugin.update_dvr_port_binding( + self.context, 'foo_port_id', {'port': port}) + self.assertIsNone(res) + def test_update_dvr_port_binding_on_non_existent_port(self): plugin = manager.NeutronManager.get_plugin() port = { @@ -1855,7 +1867,9 @@ def test_create_port_rpc_outside_transaction(self): with mock.patch.object(ml2_plugin.Ml2Plugin, '__init__') as init,\ mock.patch.object(base_plugin.NeutronDbPluginV2, - 'create_port') as db_create_port: + 'create_port') as db_create_port, \ + mock.patch.object(base_plugin.NeutronDbPluginV2, + 'update_port'): init.return_value = None new_port = mock.MagicMock() diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/ml2/test_port_binding.py neutron-7.1.1/neutron/tests/unit/plugins/ml2/test_port_binding.py --- neutron-7.0.4/neutron/tests/unit/plugins/ml2/test_port_binding.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/ml2/test_port_binding.py 2016-06-10 01:43:08.000000000 +0000 @@ -20,6 +20,7 @@ from neutron.extensions import portbindings from neutron import manager from neutron.plugins.ml2 import config as config +from neutron.plugins.ml2 import driver_context from neutron.plugins.ml2 import models as ml2_models from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin @@ -178,6 +179,39 @@ def test_update_from_host_to_empty_binding_notifies_agent(self): self._test_update_port_binding('host-ovs-no_filter', '') + def test_process_binding_port_host_id_changed(self): + ctx = context.get_admin_context() + plugin = manager.NeutronManager.get_plugin() + host_id = {portbindings.HOST_ID: 'host1'} + with self.port(**host_id) as port: + # Since the port is DOWN at first + # It's necessary to make its status ACTIVE for this test + plugin.update_port_status(ctx, port['port']['id'], + const.PORT_STATUS_ACTIVE) + + attrs = port['port'] + attrs['status'] = const.PORT_STATUS_ACTIVE + original_port = attrs.copy() + attrs['binding:host_id'] = 'host2' + updated_port = attrs.copy() + network = {'id': attrs['network_id']} + binding = ml2_models.PortBinding( + port_id=original_port['id'], + host=original_port['binding:host_id'], + vnic_type=original_port['binding:vnic_type'], + profile=original_port['binding:profile'], + vif_type=original_port['binding:vif_type'], + vif_details=original_port['binding:vif_details']) + levels = 1 + mech_context = driver_context.PortContext( + plugin, ctx, updated_port, network, binding, levels, + original_port=original_port) + + plugin._process_port_binding(mech_context, port['port']) + self.assertEqual(const.PORT_STATUS_DOWN, updated_port['status']) + port_dict = plugin.get_port(ctx, port['port']['id']) + self.assertEqual(const.PORT_STATUS_DOWN, port_dict['status']) + def test_dvr_binding(self): ctx = context.get_admin_context() with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port: diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/oneconvergence/test_nvsd_plugin.py neutron-7.1.1/neutron/tests/unit/plugins/oneconvergence/test_nvsd_plugin.py --- neutron-7.0.4/neutron/tests/unit/plugins/oneconvergence/test_nvsd_plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/oneconvergence/test_nvsd_plugin.py 2016-06-10 01:43:08.000000000 +0000 @@ -35,7 +35,7 @@ _plugin_name = PLUGIN_NAME def setUp(self): - if 'v6' in self._testMethodName: + if 'v6' in self._testMethodName.lower(): self.skipTest("NVSD Plugin does not support IPV6.") def mocked_oneconvergence_init(self): diff -Nru neutron-7.0.4/neutron/tests/unit/plugins/opencontrail/test_contrail_plugin.py neutron-7.1.1/neutron/tests/unit/plugins/opencontrail/test_contrail_plugin.py --- neutron-7.0.4/neutron/tests/unit/plugins/opencontrail/test_contrail_plugin.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/plugins/opencontrail/test_contrail_plugin.py 2016-06-10 01:43:08.000000000 +0000 @@ -249,6 +249,11 @@ class TestContrailPortsV2(test_plugin.TestPortsV2, ContrailPluginTestCase): + + def test_create_port_with_too_many_fixed_ips(self): + self._test_create_port_with_too_many_fixed_ips( + error='ContrailBadRequestError') + def test_create_port_public_network_with_invalid_ip_no_subnet_id(self): super(TestContrailPortsV2, self). \ test_create_port_public_network_with_invalid_ip_no_subnet_id( diff -Nru neutron-7.0.4/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py neutron-7.1.1/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py --- neutron-7.0.4/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py 2016-06-10 01:43:08.000000000 +0000 @@ -332,6 +332,14 @@ # just make sure that no exception is raised self.remove_networks_from_down_agents() + def test_reschedule_network_catches_exceptions_on_fetching_bindings(self): + with mock.patch('neutron.context.get_admin_context') as get_ctx: + mock_ctx = mock.Mock() + get_ctx.return_value = mock_ctx + mock_ctx.session.query.side_effect = Exception() + # just make sure that no exception is raised + self.remove_networks_from_down_agents() + def test_reschedule_doesnt_occur_if_no_agents(self): agents = self._create_and_set_agents_down(['host-a', 'host-b'], 2) self._test_schedule_bind_network([agents[0]], self.network_id) diff -Nru neutron-7.0.4/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py neutron-7.1.1/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py --- neutron-7.0.4/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py 2016-06-10 01:43:08.000000000 +0000 @@ -904,8 +904,10 @@ return_value={'L3_ROUTER_NAT': l3plugin}): l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', plugin, **kwargs) - self.assertFalse(l3plugin.dvr_vmarp_table_update.called) - self.assertFalse(l3plugin.dvr_update_router_addvm.called) + self.assertFalse( + l3plugin.update_arp_entry_for_dvr_service_port.called) + self.assertFalse( + l3plugin.dvr_handle_new_service_port.called) self.assertFalse(l3plugin.remove_router_from_l3_agent.called) self.assertFalse(l3plugin.dvr_deletens_if_no_port.called) @@ -923,9 +925,10 @@ return_value={'L3_ROUTER_NAT': l3plugin}): l3_dvrscheduler_db._notify_l3_agent_new_port( 'port', 'after_create', mock.ANY, **kwargs) - l3plugin.dvr_vmarp_table_update.assert_called_once_with( - self.adminContext, kwargs.get('port'), 'add') - l3plugin.dvr_update_router_addvm.assert_called_once_with( + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, kwargs.get('port'), 'add') + l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, kwargs.get('port')) def test__notify_l3_agent_new_port_no_action(self): @@ -942,8 +945,10 @@ return_value={'L3_ROUTER_NAT': l3plugin}): l3_dvrscheduler_db._notify_l3_agent_new_port( 'port', 'after_create', mock.ANY, **kwargs) - self.assertFalse(l3plugin.dvr_vmarp_table_update.called) - self.assertFalse(l3plugin.dvr_update_router_addvm.called) + self.assertFalse( + l3plugin.update_arp_entry_for_dvr_service_port.called) + self.assertFalse( + l3plugin.dvr_handle_new_service_port.called) def test__notify_l3_agent_update_port_no_action(self): kwargs = { @@ -964,8 +969,10 @@ l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) - self.assertFalse(l3plugin.dvr_vmarp_table_update.called) - self.assertFalse(l3plugin.dvr_update_router_addvm.called) + self.assertFalse( + l3plugin.update_arp_entry_for_dvr_service_port.called) + self.assertFalse( + l3plugin.dvr_handle_new_service_port.called) self.assertFalse(l3plugin.remove_router_from_l3_agent.called) self.assertFalse(l3plugin.dvr_deletens_if_no_port.called) @@ -989,9 +996,10 @@ l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) - l3plugin.dvr_vmarp_table_update.assert_called_once_with( - self.adminContext, kwargs.get('port'), 'add') - self.assertFalse(l3plugin.dvr_update_router_addvm.called) + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, kwargs.get('port'), 'add') + self.assertFalse(l3plugin.dvr_handle_new_service_port.called) def test__notify_l3_agent_update_port_with_port_binding_change(self): kwargs = { @@ -1016,9 +1024,10 @@ l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) l3plugin.remove_router_from_l3_agent.assert_called_once_with( - self.adminContext, 'foo_agent', 'foo_id') - self.assertEqual(2, l3plugin.dvr_vmarp_table_update.call_count) - l3plugin.dvr_update_router_addvm.assert_called_once_with( + mock.ANY, 'foo_agent', 'foo_id') + self.assertEqual( + 2, l3plugin.update_arp_entry_for_dvr_service_port.call_count) + l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, kwargs.get('port')) def test__notify_l3_agent_update_port_removing_routers(self): @@ -1055,13 +1064,16 @@ l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', plugin, **kwargs) - self.assertEqual(1, l3plugin.dvr_vmarp_table_update.call_count) - l3plugin.dvr_vmarp_table_update.assert_called_once_with( - self.adminContext, mock.ANY, 'del') + self.assertEqual( + 1, l3plugin.update_arp_entry_for_dvr_service_port.call_count) + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, mock.ANY, 'del') - self.assertFalse(l3plugin.dvr_update_router_addvm.called) + self.assertFalse( + l3plugin.dvr_handle_new_service_port.called) l3plugin.remove_router_from_l3_agent.assert_called_once_with( - self.adminContext, 'foo_agent', 'foo_id') + mock.ANY, 'foo_agent', 'foo_id') def test__notify_port_delete(self): plugin = manager.NeutronManager.get_plugin() @@ -1082,12 +1094,13 @@ } l3_dvrscheduler_db._notify_port_delete( 'port', 'after_delete', plugin, **kwargs) - l3plugin.dvr_vmarp_table_update.assert_called_once_with( - self.adminContext, mock.ANY, 'del') + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, mock.ANY, 'del') l3plugin.remove_router_from_l3_agent.assert_called_once_with( - self.adminContext, 'foo_agent', 'foo_id') + mock.ANY, 'foo_agent', 'foo_id') - def test_dvr_update_router_addvm(self): + def test_dvr_handle_new_service_port(self): port = { 'id': 'port1', 'device_id': 'abcd', @@ -1131,13 +1144,11 @@ return_value=dvr_ports),\ mock.patch('neutron.api.rpc.agentnotifiers.l3_rpc_agent_api' '.L3AgentNotifyAPI'),\ - mock.patch( - 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2.get_port', - return_value=port),\ mock.patch.object( self.dut, 'get_l3_agents', return_value=[agent_on_host]) as get_l3_agents: - self.dut.dvr_update_router_addvm(self.adminContext, port) + self.dut.dvr_handle_new_service_port( + self.adminContext, port) get_l3_agents.assert_called_once_with( self.adminContext, filters={'host': [port['binding:host_id']]}) @@ -1341,61 +1352,6 @@ mock_get_port_binding_host.assert_called_once_with( self.adminContext.session, vm_port_id) - def test_dvr_deletens_if_no_ports_no_removeable_routers(self): - # A VM port is deleted, but the router can't be unscheduled from the - # compute node because there is another VM port present. - vm_tenant_id = 'tenant-1' - my_context = n_context.Context('user-1', vm_tenant_id, is_admin=False) - shared_subnet_id = '80947d4a-fbc8-484b-9f92-623a6bfcf3e0', - vm_port_host = 'compute-node-1' - - dvr_port = self._create_port( - 'dvr-router', 'admin-tenant', vm_port_host, - shared_subnet_id, '10.10.10.1', - device_owner=constants.DEVICE_OWNER_DVR_INTERFACE) - - deleted_vm_port = self._create_port( - 'deleted-vm', vm_tenant_id, vm_port_host, - shared_subnet_id, '10.10.10.3', - status='INACTIVE') - deleted_vm_port_id = deleted_vm_port['id'] - - running_vm_port = self._create_port( - 'running-vn', 'tenant-2', vm_port_host, - shared_subnet_id, '10.10.10.33') - - fakePortDB = FakePortDB([running_vm_port, deleted_vm_port, dvr_port]) - - vm_port_binding = { - 'port_id': deleted_vm_port_id, - 'host': vm_port_host - } - - with mock.patch.object(my_context, - 'elevated', - return_value=self.adminContext),\ - mock.patch( - 'neutron.plugins.ml2.db.get_port_binding_host', - return_value=vm_port_host) as mock_get_port_binding_host,\ - mock.patch('neutron.db.db_base_plugin_v2.NeutronDbPluginV2.' - 'get_port', side_effect=fakePortDB.get_port),\ - mock.patch('neutron.db.db_base_plugin_v2.NeutronDbPluginV2.' - 'get_ports', side_effect=fakePortDB.get_ports) as\ - mock_get_ports,\ - mock.patch('neutron.plugins.ml2.db.' - 'get_dvr_port_binding_by_host', - return_value=vm_port_binding) as\ - mock_get_dvr_port_binding_by_host: - - routers = self.dut.dvr_deletens_if_no_port( - my_context, deleted_vm_port_id) - self.assertEqual([], routers) - - mock_get_port_binding_host.assert_called_once_with( - self.adminContext.session, deleted_vm_port_id) - self.assertTrue(mock_get_ports.called) - self.assertFalse(mock_get_dvr_port_binding_by_host.called) - def _test_dvr_deletens_if_no_ports_delete_routers(self, vm_tenant, router_tenant): diff -Nru neutron-7.0.4/neutron/tests/unit/services/metering/drivers/test_iptables.py neutron-7.1.1/neutron/tests/unit/services/metering/drivers/test_iptables.py --- neutron-7.0.4/neutron/tests/unit/services/metering/drivers/test_iptables.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/services/metering/drivers/test_iptables.py 2016-06-10 01:43:08.000000000 +0000 @@ -106,6 +106,15 @@ self.metering = iptables_driver.IptablesMeteringDriver('metering', cfg.CONF) + def test_create_stateless_iptables_manager(self): + routers = TEST_ROUTERS[:1] + self.metering.add_metering_label(None, routers) + self.iptables_cls.assert_called_with( + binary_name=mock.ANY, + namespace=mock.ANY, + state_less=True, + use_ipv6=mock.ANY) + def test_add_metering_label(self): routers = TEST_ROUTERS[:1] diff -Nru neutron-7.0.4/neutron/tests/unit/test_wsgi.py neutron-7.1.1/neutron/tests/unit/test_wsgi.py --- neutron-7.0.4/neutron/tests/unit/test_wsgi.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/tests/unit/test_wsgi.py 2016-06-10 01:43:08.000000000 +0000 @@ -175,6 +175,28 @@ server.stop() + def test_disable_ssl(self): + CONF.set_default('use_ssl', True) + + greetings = 'Hello, World!!!' + + def hello_world(env, start_response): + if env['PATH_INFO'] != '/': + start_response('404 Not Found', + [('Content-Type', 'text/plain')]) + return ['Not Found\r\n'] + start_response('200 OK', [('Content-Type', 'text/plain')]) + return [greetings] + + server = wsgi.Server("test_app", disable_ssl=True) + server.start(hello_world, 0, host="127.0.0.1") + + response = open_no_proxy('http://127.0.0.1:%d/' % server.port) + + self.assertEqual(greetings.encode('utf-8'), response.read()) + + server.stop() + @mock.patch.object(wsgi, 'eventlet') @mock.patch.object(wsgi, 'loggers') def test__run(self, logging_mock, eventlet_mock): diff -Nru neutron-7.0.4/neutron/wsgi.py neutron-7.1.1/neutron/wsgi.py --- neutron-7.0.4/neutron/wsgi.py 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/neutron/wsgi.py 2016-06-10 01:43:08.000000000 +0000 @@ -105,9 +105,10 @@ class WorkerService(worker.NeutronWorker): """Wraps a worker to be handled by ProcessLauncher""" - def __init__(self, service, application): + def __init__(self, service, application, disable_ssl=False): self._service = service self._application = application + self._disable_ssl = disable_ssl self._server = None def start(self): @@ -118,7 +119,7 @@ # errors on service restart. # Duplicate a socket object to keep a file descriptor usable. dup_sock = self._service._socket.dup() - if CONF.use_ssl: + if CONF.use_ssl and not self._disable_ssl: dup_sock = self._service.wrap_ssl(dup_sock) self._server = self._service.pool.spawn(self._service._run, self._application, @@ -141,10 +142,11 @@ class Server(object): """Server class to manage multiple WSGI sockets and applications.""" - def __init__(self, name, num_threads=1000): + def __init__(self, name, num_threads=1000, disable_ssl=False): # Raise the default from 8192 to accommodate large tokens eventlet.wsgi.MAX_HEADER_LINE = CONF.max_header_line self.num_threads = num_threads + self.disable_ssl = disable_ssl # Pool for a greenthread in which wsgi server will be running self.pool = eventlet.GreenPool(1) self.name = name @@ -152,7 +154,7 @@ # A value of 0 is converted to None because None is what causes the # wsgi server to wait forever. self.client_socket_timeout = CONF.client_socket_timeout or None - if CONF.use_ssl: + if CONF.use_ssl and not self.disable_ssl: self._check_ssl_settings() def _get_socket(self, host, port, backlog): @@ -246,7 +248,7 @@ self._launch(application, workers) def _launch(self, application, workers=0): - service = WorkerService(self, application) + service = WorkerService(self, application, self.disable_ssl) if workers < 1: # The API service should run in the current process. self._server = service diff -Nru neutron-7.0.4/neutron.egg-info/entry_points.txt neutron-7.1.1/neutron.egg-info/entry_points.txt --- neutron-7.0.4/neutron.egg-info/entry_points.txt 2016-03-29 17:48:06.000000000 +0000 +++ neutron-7.1.1/neutron.egg-info/entry_points.txt 2016-06-10 01:44:41.000000000 +0000 @@ -37,7 +37,6 @@ bigswitch = neutron.plugins.bigswitch.plugin:NeutronRestProxyV2 brocade = neutron.plugins.brocade.NeutronPlugin:BrocadePluginV2 embrane = neutron.plugins.embrane.plugins.embrane_ml2_plugin:EmbraneMl2Plugin -midonet = neutron.plugins.midonet.plugin:MidonetPluginV2 ml2 = neutron.plugins.ml2.plugin:Ml2Plugin nuage = neutron.plugins.nuage.plugin:NuagePlugin oneconvergence = neutron.plugins.oneconvergence.plugin:OneConvergencePluginV2 diff -Nru neutron-7.0.4/neutron.egg-info/pbr.json neutron-7.1.1/neutron.egg-info/pbr.json --- neutron-7.0.4/neutron.egg-info/pbr.json 2016-03-29 17:48:06.000000000 +0000 +++ neutron-7.1.1/neutron.egg-info/pbr.json 2016-06-10 01:44:41.000000000 +0000 @@ -1 +1 @@ -{"is_release": true, "git_version": "b80fa23"} \ No newline at end of file +{"git_version": "e6341a8", "is_release": true} \ No newline at end of file diff -Nru neutron-7.0.4/neutron.egg-info/PKG-INFO neutron-7.1.1/neutron.egg-info/PKG-INFO --- neutron-7.0.4/neutron.egg-info/PKG-INFO 2016-03-29 17:48:06.000000000 +0000 +++ neutron-7.1.1/neutron.egg-info/PKG-INFO 2016-06-10 01:44:41.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: neutron -Version: 7.0.4 +Version: 7.1.1 Summary: OpenStack Networking Home-page: http://www.openstack.org/ Author: OpenStack diff -Nru neutron-7.0.4/neutron.egg-info/requires.txt neutron-7.1.1/neutron.egg-info/requires.txt --- neutron-7.0.4/neutron.egg-info/requires.txt 2016-03-29 17:48:06.000000000 +0000 +++ neutron-7.1.1/neutron.egg-info/requires.txt 2016-06-10 01:44:41.000000000 +0000 @@ -1,6 +1,7 @@ pbr>=1.6 Paste PasteDeploy>=1.5.0 +Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3 debtcollector>=0.3.0 eventlet>=0.17.4 pecan>=1.0.0 @@ -15,7 +16,7 @@ ryu>=3.23.2 SQLAlchemy<1.1.0,>=0.9.9 WebOb>=1.2.3 -python-keystoneclient!=1.8.0,>=1.6.0 +python-keystoneclient!=1.8.0,<3.0.0,>=1.6.0 alembic>=0.8.0 six>=1.9.0 stevedore>=1.5.0 @@ -36,10 +37,7 @@ python-novaclient!=2.33.0,>=2.28.1 [:(python_version!='2.7')] -Routes!=2.0,>=1.12.3 - -[:(python_version=='2.7')] -Routes!=2.0,!=2.1,>=1.12.3 +Routes!=2.0,!=2.3.0,>=1.12.3 [:(sys_platform=='win32')] pywin32 diff -Nru neutron-7.0.4/neutron.egg-info/SOURCES.txt neutron-7.1.1/neutron.egg-info/SOURCES.txt --- neutron-7.0.4/neutron.egg-info/SOURCES.txt 2016-03-29 17:48:07.000000000 +0000 +++ neutron-7.1.1/neutron.egg-info/SOURCES.txt 2016-06-10 01:44:42.000000000 +0000 @@ -95,7 +95,6 @@ etc/neutron/plugins/brocade/vyatta/vrouter.ini etc/neutron/plugins/cisco/cisco_vpn_agent.ini etc/neutron/plugins/embrane/heleos_conf.ini -etc/neutron/plugins/midonet/midonet.ini etc/neutron/plugins/ml2/linuxbridge_agent.ini etc/neutron/plugins/ml2/ml2_conf.ini etc/neutron/plugins/ml2/ml2_conf_brocade.ini @@ -584,9 +583,6 @@ neutron/plugins/hyperv/agent/config.py neutron/plugins/hyperv/agent/l2_agent.py neutron/plugins/hyperv/agent/security_groups_driver.py -neutron/plugins/midonet/__init__.py -neutron/plugins/midonet/plugin.py -neutron/plugins/midonet/requirements.txt neutron/plugins/ml2/README neutron/plugins/ml2/__init__.py neutron/plugins/ml2/config.py @@ -864,6 +860,7 @@ neutron/tests/functional/test_server.py neutron/tests/functional/test_service.py neutron/tests/functional/agent/__init__.py +neutron/tests/functional/agent/test_dhcp_agent.py neutron/tests/functional/agent/test_l2_lb_agent.py neutron/tests/functional/agent/test_l2_ovs_agent.py neutron/tests/functional/agent/test_l3_agent.py @@ -881,6 +878,7 @@ neutron/tests/functional/agent/linux/helpers.py neutron/tests/functional/agent/linux/simple_daemon.py neutron/tests/functional/agent/linux/test_async_process.py +neutron/tests/functional/agent/linux/test_bridge_lib.py neutron/tests/functional/agent/linux/test_dhcp.py neutron/tests/functional/agent/linux/test_interface.py neutron/tests/functional/agent/linux/test_ip_lib.py @@ -1080,9 +1078,12 @@ neutron/tests/unit/db/test_ipam_backend_mixin.py neutron/tests/unit/db/test_ipam_non_pluggable_backend.py neutron/tests/unit/db/test_ipam_pluggable_backend.py +neutron/tests/unit/db/test_l3_db.py neutron/tests/unit/db/test_l3_dvr_db.py neutron/tests/unit/db/test_l3_hamode_db.py neutron/tests/unit/db/test_migration.py +neutron/tests/unit/db/test_portsecurity_db.py +neutron/tests/unit/db/test_portsecurity_db_common.py neutron/tests/unit/db/test_securitygroups_db.py neutron/tests/unit/db/metering/__init__.py neutron/tests/unit/db/metering/test_metering_db.py @@ -1220,6 +1221,7 @@ neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/test_mech_openvswitch.py neutron/tests/unit/plugins/ml2/extensions/__init__.py neutron/tests/unit/plugins/ml2/extensions/fake_extension.py +neutron/tests/unit/plugins/ml2/extensions/test_port_security.py neutron/tests/unit/plugins/oneconvergence/__init__.py neutron/tests/unit/plugins/oneconvergence/test_nvsd_agent.py neutron/tests/unit/plugins/oneconvergence/test_nvsd_plugin.py @@ -1265,12 +1267,16 @@ releasenotes/notes/713a5eb2833311e5-start-using-reno.yaml releasenotes/notes/add-port-rebinding-chance-33178b9abacf5804.yaml releasenotes/notes/agent-scheduling-3404da3fbe863366.yaml +releasenotes/notes/clear-allowed-address-pairs-with-none-4757bcca78076c9e.yaml releasenotes/notes/deprecated-features-96c51d46f3498fad.yaml releasenotes/notes/deprecated-plugins-and-drivers-292865277ee1d182.yaml releasenotes/notes/dvr-ovs-agent-6052a8d60fddde22.yaml +releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml releasenotes/notes/ipv6-prefix-delegation-a2a342e5e34534c7.yaml releasenotes/notes/lbaas-v2-1628fe34700148f6.yaml +releasenotes/notes/linuxbridge_vxlan_arp_responder-e9ea91552e1b62a7.yaml releasenotes/notes/macvtap_assigned_vf_check-f4d07660ffd82a24.yaml +releasenotes/notes/mtu-selection-and-advertisement-ab29f9ec43140224.yaml releasenotes/notes/neutron-rbac-516b499743b57d1f.yaml releasenotes/notes/ovs-agent-6137f5f82e10135c.yaml releasenotes/notes/performance-considerations-a76dd11d35f1df6a.yaml diff -Nru neutron-7.0.4/PKG-INFO neutron-7.1.1/PKG-INFO --- neutron-7.0.4/PKG-INFO 2016-03-29 17:48:07.000000000 +0000 +++ neutron-7.1.1/PKG-INFO 2016-06-10 01:44:42.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: neutron -Version: 7.0.4 +Version: 7.1.1 Summary: OpenStack Networking Home-page: http://www.openstack.org/ Author: OpenStack diff -Nru neutron-7.0.4/releasenotes/notes/clear-allowed-address-pairs-with-none-4757bcca78076c9e.yaml neutron-7.1.1/releasenotes/notes/clear-allowed-address-pairs-with-none-4757bcca78076c9e.yaml --- neutron-7.0.4/releasenotes/notes/clear-allowed-address-pairs-with-none-4757bcca78076c9e.yaml 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/releasenotes/notes/clear-allowed-address-pairs-with-none-4757bcca78076c9e.yaml 2016-06-10 01:43:03.000000000 +0000 @@ -0,0 +1,9 @@ +--- +prelude: > + Allowed address pairs can now be cleared by passing + None in addition to an empty list. This is to make + it possible to use the --action=clear option with + the neutron client. + neutron port-update --allowed-address-pairs action=clear +fixes: + - Fixes bug 1537734 diff -Nru neutron-7.0.4/releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml neutron-7.1.1/releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml --- neutron-7.0.4/releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml 2016-06-10 01:43:03.000000000 +0000 @@ -0,0 +1,20 @@ +--- +features: + - Use the value of the network 'mtu' attribute for the MTU + of virtual network interfaces such as veth pairs, patch + ports, and tap devices involving a particular network. + - Enable end-to-end support for arbitrary MTUs including + jumbo frames between instances and provider networks by + moving MTU disparities between flat or VLAN networks and + overlay networks from layer-2 devices to layer-3 devices + that support path MTU discovery (PMTUD). +upgrade: + - Does not change MTU for existing virtual network interfaces. + - Actions that create virtual network interfaces on an existing + network with the 'mtu' attribute containing a value greater + than zero could cause issues for network traffic traversing + existing and new virtual network interfaces. +fixes: + - Explicitly configure MTU of virtual network interfaces + rather than using default values or incorrect values that + do not account for overlay protocol overhead. diff -Nru neutron-7.0.4/releasenotes/notes/linuxbridge_vxlan_arp_responder-e9ea91552e1b62a7.yaml neutron-7.1.1/releasenotes/notes/linuxbridge_vxlan_arp_responder-e9ea91552e1b62a7.yaml --- neutron-7.0.4/releasenotes/notes/linuxbridge_vxlan_arp_responder-e9ea91552e1b62a7.yaml 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/releasenotes/notes/linuxbridge_vxlan_arp_responder-e9ea91552e1b62a7.yaml 2016-06-10 01:43:08.000000000 +0000 @@ -0,0 +1,7 @@ +--- +fixes: + - The Linuxbridge agent now supports the ability to toggle the local ARP + responder when L2Population is enabled. This ensures compatibility with + the allowed-address-pairs extension. + Closes `bug 1445089 `__. + diff -Nru neutron-7.0.4/releasenotes/notes/mtu-selection-and-advertisement-ab29f9ec43140224.yaml neutron-7.1.1/releasenotes/notes/mtu-selection-and-advertisement-ab29f9ec43140224.yaml --- neutron-7.0.4/releasenotes/notes/mtu-selection-and-advertisement-ab29f9ec43140224.yaml 1970-01-01 00:00:00.000000000 +0000 +++ neutron-7.1.1/releasenotes/notes/mtu-selection-and-advertisement-ab29f9ec43140224.yaml 2016-06-10 01:43:03.000000000 +0000 @@ -0,0 +1,9 @@ +--- +prelude: > + Support for MTU selection and advertisement. +features: + - When advertise_mtu is set in the config, Neutron supports + advertising the LinkMTU using Router Advertisements. +other: + - For details please read `Blueprint mtu-selection-and-advertisement + `_. diff -Nru neutron-7.0.4/requirements.txt neutron-7.1.1/requirements.txt --- neutron-7.0.4/requirements.txt 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/requirements.txt 2016-06-10 01:43:08.000000000 +0000 @@ -5,8 +5,8 @@ Paste PasteDeploy>=1.5.0 -Routes!=2.0,!=2.1,>=1.12.3;python_version=='2.7' -Routes!=2.0,>=1.12.3;python_version!='2.7' +Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3;python_version=='2.7' # MIT +Routes!=2.0,!=2.3.0,>=1.12.3;python_version!='2.7' # MIT debtcollector>=0.3.0 # Apache-2.0 eventlet>=0.17.4 pecan>=1.0.0 @@ -21,7 +21,7 @@ ryu>=3.23.2 # Apache-2.0 SQLAlchemy<1.1.0,>=0.9.9 WebOb>=1.2.3 -python-keystoneclient!=1.8.0,>=1.6.0 +python-keystoneclient!=1.8.0,<3.0.0,>=1.6.0 alembic>=0.8.0 six>=1.9.0 stevedore>=1.5.0 # Apache-2.0 diff -Nru neutron-7.0.4/setup.cfg neutron-7.1.1/setup.cfg --- neutron-7.0.4/setup.cfg 2016-03-29 17:48:07.000000000 +0000 +++ neutron-7.1.1/setup.cfg 2016-06-10 01:44:42.000000000 +0000 @@ -52,7 +52,6 @@ etc/neutron/plugins/cisco = etc/neutron/plugins/cisco/cisco_vpn_agent.ini etc/neutron/plugins/embrane = etc/neutron/plugins/embrane/heleos_conf.ini - etc/neutron/plugins/midonet = etc/neutron/plugins/midonet/midonet.ini etc/neutron/plugins/ml2 = etc/neutron/plugins/bigswitch/restproxy.ini etc/neutron/plugins/ml2/linuxbridge_agent.ini @@ -105,7 +104,6 @@ bigswitch = neutron.plugins.bigswitch.plugin:NeutronRestProxyV2 brocade = neutron.plugins.brocade.NeutronPlugin:BrocadePluginV2 embrane = neutron.plugins.embrane.plugins.embrane_ml2_plugin:EmbraneMl2Plugin - midonet = neutron.plugins.midonet.plugin:MidonetPluginV2 ml2 = neutron.plugins.ml2.plugin:Ml2Plugin nuage = neutron.plugins.nuage.plugin:NuagePlugin oneconvergence = neutron.plugins.oneconvergence.plugin:OneConvergencePluginV2 @@ -204,6 +202,6 @@ [egg_info] tag_build = -tag_svn_revision = 0 tag_date = 0 +tag_svn_revision = 0 diff -Nru neutron-7.0.4/test-requirements.txt neutron-7.1.1/test-requirements.txt --- neutron-7.0.4/test-requirements.txt 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/test-requirements.txt 2016-06-10 01:43:08.000000000 +0000 @@ -3,7 +3,7 @@ # process, which may cause wedges in the gate later. hacking<0.11,>=0.10.0 -cliff>=1.14.0 # Apache-2.0 +cliff!=1.16.0,!=1.17.0,>=1.14.0 # Apache-2.0 coverage>=3.6 fixtures>=1.3.1 mock>=1.2 diff -Nru neutron-7.0.4/tools/configure_for_func_testing.sh neutron-7.1.1/tools/configure_for_func_testing.sh --- neutron-7.0.4/tools/configure_for_func_testing.sh 2016-03-29 17:45:04.000000000 +0000 +++ neutron-7.1.1/tools/configure_for_func_testing.sh 2016-06-10 01:43:08.000000000 +0000 @@ -19,6 +19,7 @@ # Control variable used to determine whether to execute this script # directly or allow the gate_hook to import. IS_GATE=${IS_GATE:-False} +USE_CONSTRAINT_ENV=${USE_CONSTRAINT_ENV:-True} if [[ "$IS_GATE" != "True" ]] && [[ "$#" -lt 1 ]]; then @@ -51,7 +52,7 @@ # when sourcing. VENV=${VENV:-dsvm-functional} # If executed in the gate, run in a constrained env -if [[ "$IS_GATE" == "True" ]] +if [[ "$IS_GATE" == "True" && "$USE_CONSTRAINT_ENV" == "True" ]] then VENV=$VENV-constraints fi @@ -220,6 +221,11 @@ if is_ubuntu; then install_package netcat-openbsd + install_package isc-dhcp-client + elif is_fedora; then + install_package dhclient + else + exit_distro_not_supported "installing dhclient package" fi # Installing python-openvswitch from packages is a stop-gap while