Browse Source

Merge remote-tracking branch 'origin' into merge-branch

This commit merges the master neutron branch into the feature/lbaasv2
branch. This is needed to get the feature/lbaasv2 branch building again.

Change-Id: Iba59aa20adc6b369b4b9d250afee406159287ba1
changes/64/130864/2
Kyle Mestery 4 years ago
parent
commit
c089154a94
100 changed files with 2523 additions and 21158 deletions
  1. 108
    16
      .pylintrc
  2. 1
    0
      HACKING.rst
  3. 9
    0
      doc/source/devref/db_layer.rst
  4. 9
    0
      etc/neutron.conf
  5. 15
    0
      etc/neutron/plugins/cisco/cisco_cfg_agent.ini
  6. 7
    56
      etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini
  7. 3
    0
      etc/neutron/rootwrap.d/l3.filters
  8. 0
    14
      neutron/agent/__init__.py
  9. 0
    14
      neutron/agent/common/__init__.py
  10. 9
    0
      neutron/agent/common/config.py
  11. 14
    0
      neutron/agent/firewall.py
  12. 2
    2
      neutron/agent/l2population_rpc.py
  13. 122
    90
      neutron/agent/l3_agent.py
  14. 0
    14
      neutron/agent/linux/__init__.py
  15. 2
    1
      neutron/agent/linux/dhcp.py
  16. 3
    5
      neutron/agent/linux/external_process.py
  17. 53
    10
      neutron/agent/linux/interface.py
  18. 3
    2
      neutron/agent/linux/ip_lib.py
  19. 34
    0
      neutron/agent/linux/iptables_comments.py
  20. 39
    20
      neutron/agent/linux/iptables_firewall.py
  21. 24
    8
      neutron/agent/linux/iptables_manager.py
  22. 23
    23
      neutron/agent/linux/ovs_lib.py
  23. 17
    14
      neutron/agent/linux/utils.py
  24. 0
    13
      neutron/agent/metadata/__init__.py
  25. 8
    14
      neutron/agent/metadata/agent.py
  26. 2
    0
      neutron/agent/metadata/namespace_proxy.py
  27. 29
    12
      neutron/agent/securitygroups_rpc.py
  28. 3
    5
      neutron/api/rpc/handlers/dhcp_rpc.py
  29. 0
    5
      neutron/api/rpc/handlers/dvr_rpc.py
  30. 2
    2
      neutron/api/v2/attributes.py
  31. 4
    0
      neutron/api/v2/base.py
  32. 0
    14
      neutron/cmd/__init__.py
  33. 1
    5
      neutron/cmd/sanity_check.py
  34. 2
    0
      neutron/common/config.py
  35. 10
    0
      neutron/common/constants.py
  36. 4
    0
      neutron/common/exceptions.py
  37. 55
    29
      neutron/db/db_base_plugin_v2.py
  38. 0
    14
      neutron/db/firewall/__init__.py
  39. 50
    29
      neutron/db/firewall/firewall_db.py
  40. 24
    10
      neutron/db/l3_agentschedulers_db.py
  41. 121
    66
      neutron/db/l3_db.py
  42. 84
    41
      neutron/db/l3_dvr_db.py
  43. 7
    7
      neutron/db/l3_dvrscheduler_db.py
  44. 7
    1
      neutron/db/l3_gwmode_db.py
  45. 11
    10
      neutron/db/l3_hamode_db.py
  46. 0
    13
      neutron/db/loadbalancer/__init__.py
  47. 0
    13
      neutron/db/metering/__init__.py
  48. 2
    2
      neutron/db/metering/metering_db.py
  49. 1
    0
      neutron/db/migration/alembic_migrations/env.py
  50. 40
    0
      neutron/db/migration/alembic_migrations/versions/1f71e54a85e7_ml2_net_seg_model.py
  51. 78
    29
      neutron/db/migration/alembic_migrations/versions/31d7f831a591_add_constraint_for_routerid.py
  52. 10
    2
      neutron/db/migration/alembic_migrations/versions/3927f7f7c456_l3_extension_distributed_mode.py
  53. 61
    0
      neutron/db/migration/alembic_migrations/versions/44621190bc02_add_uniqueconstraint_ipavailability_ranges.py
  54. 65
    0
      neutron/db/migration/alembic_migrations/versions/544673ac99ab_add_router_port_table.py
  55. 7
    0
      neutron/db/migration/alembic_migrations/versions/884573acbf1c_unify_nsx_router_extra_attributes.py
  56. 1
    1
      neutron/db/migration/alembic_migrations/versions/HEAD
  57. 36
    0
      neutron/db/migration/alembic_migrations/versions/juno_release.py
  58. 8
    4
      neutron/db/migration/cli.py
  59. 34
    10
      neutron/db/migration/migrate_to_ml2.py
  60. 11
    2
      neutron/db/models_v2.py
  61. 11
    2
      neutron/db/securitygroups_db.py
  62. 30
    26
      neutron/db/securitygroups_rpc_base.py
  63. 0
    14
      neutron/db/vpn/__init__.py
  64. 12
    1
      neutron/db/vpn/vpn_db.py
  65. 0
    14
      neutron/debug/__init__.py
  66. 19
    6
      neutron/extensions/firewall.py
  67. 15
    1
      neutron/extensions/l3.py
  68. 7
    1
      neutron/extensions/l3_ext_gw_mode.py
  69. 1
    1
      neutron/extensions/l3_ext_ha_mode.py
  70. 2
    3
      neutron/extensions/quotasv2.py
  71. 4
    0
      neutron/extensions/vpnaas.py
  72. 12
    0
      neutron/hacking/checks.py
  73. 0
    263
      neutron/locale/de/LC_MESSAGES/neutron-log-error.po
  74. 141
    44
      neutron/locale/de/LC_MESSAGES/neutron-log-info.po
  75. 0
    156
      neutron/locale/de/LC_MESSAGES/neutron-log-warning.po
  76. 0
    263
      neutron/locale/en_AU/LC_MESSAGES/neutron-log-error.po
  77. 134
    39
      neutron/locale/en_AU/LC_MESSAGES/neutron-log-info.po
  78. 0
    156
      neutron/locale/en_AU/LC_MESSAGES/neutron-log-warning.po
  79. 0
    20
      neutron/locale/en_GB/LC_MESSAGES/neutron-log-critical.po
  80. 0
    268
      neutron/locale/en_GB/LC_MESSAGES/neutron-log-error.po
  81. 134
    39
      neutron/locale/en_GB/LC_MESSAGES/neutron-log-info.po
  82. 0
    159
      neutron/locale/en_GB/LC_MESSAGES/neutron-log-warning.po
  83. 0
    16682
      neutron/locale/en_US/LC_MESSAGES/neutron.po
  84. 0
    263
      neutron/locale/es/LC_MESSAGES/neutron-log-error.po
  85. 143
    46
      neutron/locale/es/LC_MESSAGES/neutron-log-info.po
  86. 0
    156
      neutron/locale/es/LC_MESSAGES/neutron-log-warning.po
  87. 0
    23
      neutron/locale/fr/LC_MESSAGES/neutron-log-critical.po
  88. 0
    263
      neutron/locale/fr/LC_MESSAGES/neutron-log-error.po
  89. 153
    52
      neutron/locale/fr/LC_MESSAGES/neutron-log-info.po
  90. 0
    156
      neutron/locale/fr/LC_MESSAGES/neutron-log-warning.po
  91. 0
    264
      neutron/locale/it/LC_MESSAGES/neutron-log-error.po
  92. 141
    46
      neutron/locale/it/LC_MESSAGES/neutron-log-info.po
  93. 0
    156
      neutron/locale/it/LC_MESSAGES/neutron-log-warning.po
  94. 0
    263
      neutron/locale/ja/LC_MESSAGES/neutron-log-error.po
  95. 145
    46
      neutron/locale/ja/LC_MESSAGES/neutron-log-info.po
  96. 0
    156
      neutron/locale/ja/LC_MESSAGES/neutron-log-warning.po
  97. 0
    263
      neutron/locale/ko_KR/LC_MESSAGES/neutron-log-error.po
  98. 144
    49
      neutron/locale/ko_KR/LC_MESSAGES/neutron-log-info.po
  99. 0
    156
      neutron/locale/ko_KR/LC_MESSAGES/neutron-log-warning.po
  100. 0
    0
      neutron/locale/neutron-log-error.pot

+ 108
- 16
.pylintrc View File

@@ -2,17 +2,97 @@
2 2
 [MASTER]
3 3
 # Add <file or directory> to the black list. It should be a base name, not a
4 4
 # path. You may set this option multiple times.
5
-ignore=test
5
+#
6
+# Note the 'openstack' below is intended to match only
7
+# neutron.openstack.common.  If we ever have another 'openstack'
8
+# dirname, then we'll need to expand the ignore features in pylint :/
9
+ignore=.git,tests,openstack
6 10
 
7
-[Messages Control]
8
-# NOTE(justinsb): We might want to have a 2nd strict pylintrc in future
9
-# C0111: Don't require docstrings on every method
10
-# W0511: TODOs in code comments are fine.
11
-# W0142: *args and **kwargs are fine.
12
-# W0622: Redefining id is fine.
13
-disable=C0111,W0511,W0142,W0622
11
+[MESSAGES CONTROL]
12
+# NOTE(gus): This is a long list.  A number of these are important and
13
+# should be re-enabled once the offending code is fixed (or marked
14
+# with a local disable)
15
+disable=
16
+# "F" Fatal errors that prevent further processing
17
+ import-error,
18
+# "I" Informational noise
19
+ locally-disabled,
20
+# "E" Error for important programming issues (likely bugs)
21
+ access-member-before-definition,
22
+ assignment-from-no-return,
23
+ bad-except-order,
24
+ bad-super-call,
25
+ maybe-no-member,
26
+ no-member,
27
+ no-method-argument,
28
+ no-name-in-module,
29
+ no-self-argument,
30
+ not-callable,
31
+ no-value-for-parameter,
32
+ super-on-old-class,
33
+ too-few-format-args,
34
+# "W" Warnings for stylistic problems or minor programming issues
35
+ abstract-method,
36
+ anomalous-backslash-in-string,
37
+ anomalous-unicode-escape-in-string,
38
+ arguments-differ,
39
+ attribute-defined-outside-init,
40
+ bad-builtin,
41
+ bad-indentation,
42
+ broad-except,
43
+ dangerous-default-value,
44
+ deprecated-lambda,
45
+ duplicate-key,
46
+ expression-not-assigned,
47
+ fixme,
48
+ global-statement,
49
+ global-variable-not-assigned,
50
+ logging-not-lazy,
51
+ lost-exception,
52
+ no-init,
53
+ non-parent-init-called,
54
+ pointless-string-statement,
55
+ protected-access,
56
+ redefined-builtin,
57
+ redefined-outer-name,
58
+ redefine-in-handler,
59
+ signature-differs,
60
+ star-args,
61
+ super-init-not-called,
62
+ undefined-loop-variable,
63
+ unnecessary-lambda,
64
+ unnecessary-pass,
65
+ unpacking-non-sequence,
66
+ unreachable,
67
+ unused-argument,
68
+ unused-import,
69
+ unused-variable,
70
+ useless-else-on-loop,
71
+# "C" Coding convention violations
72
+ bad-continuation,
73
+ invalid-name,
74
+ missing-docstring,
75
+ old-style-class,
76
+ superfluous-parens,
77
+# "R" Refactor recommendations
78
+ abstract-class-little-used,
79
+ abstract-class-not-used,
80
+ cyclic-import,
81
+ duplicate-code,
82
+ interface-not-implemented,
83
+ no-self-use,
84
+ too-few-public-methods,
85
+ too-many-ancestors,
86
+ too-many-arguments,
87
+ too-many-branches,
88
+ too-many-instance-attributes,
89
+ too-many-lines,
90
+ too-many-locals,
91
+ too-many-public-methods,
92
+ too-many-return-statements,
93
+ too-many-statements
14 94
 
15
-[Basic]
95
+[BASIC]
16 96
 # Variable names can be 1 to 31 characters long, with lowercase and underscores
17 97
 variable-rgx=[a-z_][a-z0-9_]{0,30}$
18 98
 
@@ -21,7 +101,7 @@ argument-rgx=[a-z_][a-z0-9_]{1,30}$
21 101
 
22 102
 # Method names should be at least 3 characters long
23 103
 # and be lowecased with underscores
24
-method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$
104
+method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$
25 105
 
26 106
 # Module names matching neutron-* are ok (files in bin/)
27 107
 module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$
@@ -29,14 +109,26 @@ module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$
29 109
 # Don't require docstrings on tests.
30 110
 no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
31 111
 
32
-[Design]
33
-max-public-methods=100
34
-min-public-methods=0
35
-max-args=6
36
-
37
-[Variables]
112
+[FORMAT]
113
+# Maximum number of characters on a single line.
114
+max-line-length=79
38 115
 
116
+[VARIABLES]
39 117
 # List of additional names supposed to be defined in builtins. Remember that
40 118
 # you should avoid to define new builtins when possible.
41 119
 # _ is used by our localization
42 120
 additional-builtins=_
121
+
122
+[CLASSES]
123
+# List of interface methods to ignore, separated by a comma.
124
+ignore-iface-methods=
125
+
126
+[IMPORTS]
127
+# Deprecated modules which should not be used, separated by a comma
128
+deprecated-modules=
129
+# should use openstack.common.jsonutils
130
+ json
131
+
132
+[REPORTS]
133
+# Tells whether to display a full report or only the messages
134
+reports=no

+ 1
- 0
HACKING.rst View File

@@ -12,6 +12,7 @@ Neutron Specific Commandments
12 12
 - [N321] Validate that jsonutils module is used instead of json
13 13
 - [N322] We do not use @authors tags in source files. We have git to track
14 14
   authorship.
15
+- [N323] assert_called_once() is not a valid method
15 16
 
16 17
 Creating Unit Tests
17 18
 -------------------

+ 9
- 0
doc/source/devref/db_layer.rst View File

@@ -1,2 +1,11 @@
1 1
 Neutron Database Layer
2 2
 ======================
3
+
4
+
5
+Testing database and models sync
6
+--------------------------------
7
+
8
+.. automodule:: neutron.tests.unit.db.test_migration
9
+
10
+.. autoclass:: _TestModelsMigrations
11
+   :members:

+ 9
- 0
etc/neutron.conf View File

@@ -251,6 +251,11 @@ lock_path = $state_path/lock
251 251
 # The uuid of the admin nova tenant
252 252
 # nova_admin_tenant_id =
253 253
 
254
+# The name of the admin nova tenant. If the uuid of the admin nova tenant
255
+# is set, this is optional.  Useful for cases where the uuid of the admin
256
+# nova tenant is not available when configuration is being done.
257
+# nova_admin_tenant_name =
258
+
254 259
 # Password for connection to nova in admin context.
255 260
 # nova_admin_password =
256 261
 
@@ -555,6 +560,10 @@ lock_path = $state_path/lock
555 560
 # Change to "sudo" to skip the filtering and just run the comand directly
556 561
 # root_helper = sudo
557 562
 
563
+# Set to true to add comments to generated iptables rules that describe
564
+# each rule's purpose. (System must support the iptables comments module.)
565
+# comment_iptables_rules = True
566
+
558 567
 # =========== items for agent management extension =============
559 568
 # seconds between nodes reporting state to server; should be less than
560 569
 # agent_down_time, best if it is half or less than agent_down_time

+ 15
- 0
etc/neutron/plugins/cisco/cisco_cfg_agent.ini View File

@@ -0,0 +1,15 @@
1
+[cfg_agent]
2
+# (IntOpt) Interval in seconds for processing of service updates.
3
+# That is when the config agent's process_services() loop executes
4
+# and it lets each service helper to process its service resources.
5
+# rpc_loop_interval = 10
6
+
7
+# (StrOpt) Period-separated module path to the routing service helper class.
8
+# routing_svc_helper_class = neutron.plugins.cisco.cfg_agent.service_helpers.routing_svc_helper.RoutingServiceHelper
9
+
10
+# (IntOpt) Timeout value in seconds for connecting to a hosting device.
11
+# device_connection_timeout = 30
12
+
13
+# (IntOpt) The time in seconds until a backlogged hosting device is
14
+# presumed dead or booted to an error state.
15
+# hosting_device_dead_timeout = 300

+ 7
- 56
etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini View File

@@ -1,52 +1,13 @@
1 1
 [ovs]
2
-# (StrOpt) Type of network to allocate for tenant networks. The
3
-# default value 'local' is useful only for single-box testing and
4
-# provides no connectivity between hosts. You MUST either change this
5
-# to 'vlan' and configure network_vlan_ranges below or change this to
6
-# 'gre' or 'vxlan' and configure tunnel_id_ranges below in order for
7
-# tenant networks to provide connectivity between hosts. Set to 'none'
8
-# to disable creation of tenant networks.
9
-#
10
-# tenant_network_type = local
11
-# Example: tenant_network_type = gre
12
-# Example: tenant_network_type = vxlan
13
-
14
-# (ListOpt) Comma-separated list of
15
-# <physical_network>[:<vlan_min>:<vlan_max>] tuples enumerating ranges
16
-# of VLAN IDs on named physical networks that are available for
17
-# allocation. All physical networks listed are available for flat and
18
-# VLAN provider network creation. Specified ranges of VLAN IDs are
19
-# available for tenant network allocation if tenant_network_type is
20
-# 'vlan'. If empty, only gre, vxlan and local networks may be created.
21
-#
22
-# network_vlan_ranges =
23
-# Example: network_vlan_ranges = physnet1:1000:2999
24
-
25 2
 # (BoolOpt) Set to True in the server and the agents to enable support
26 3
 # for GRE or VXLAN networks. Requires kernel support for OVS patch ports and
27 4
 # GRE or VXLAN tunneling.
28 5
 #
29 6
 # WARNING: This option will be deprecated in the Icehouse release, at which
30
-#          point setting tunnel_type below will be required to enable
31
-#          tunneling.
7
+#          point setting tunnel_types will be required to enable tunneling.
32 8
 #
33 9
 # enable_tunneling = False
34 10
 
35
-# (StrOpt) The type of tunnel network, if any, supported by the plugin. If
36
-# this is set, it will cause tunneling to be enabled. If this is not set and
37
-# the option enable_tunneling is set, this will default to 'gre'.
38
-#
39
-# tunnel_type =
40
-# Example: tunnel_type = gre
41
-# Example: tunnel_type = vxlan
42
-
43
-# (ListOpt) Comma-separated list of <tun_min>:<tun_max> tuples
44
-# enumerating ranges of GRE or VXLAN tunnel IDs that are available for
45
-# tenant network allocation if tenant_network_type is 'gre' or 'vxlan'.
46
-#
47
-# tunnel_id_ranges =
48
-# Example: tunnel_id_ranges = 1:1000
49
-
50 11
 # Do not change this parameter unless you have a good reason to.
51 12
 # This is the name of the OVS integration bridge. There is one per hypervisor.
52 13
 # The integration bridge acts as a virtual "patch bay". All VM VIFs are
@@ -55,7 +16,7 @@
55 16
 #
56 17
 # integration_bridge = br-int
57 18
 
58
-# Only used for the agent if tunnel_id_ranges (above) is not empty for
19
+# Only used for the agent if tunnel_id_ranges is not empty for
59 20
 # the server.  In most cases, the default value should be fine.
60 21
 #
61 22
 # tunnel_bridge = br-tun
@@ -66,7 +27,7 @@
66 27
 # Peer patch port in tunnel bridge for integration bridge
67 28
 # tun_peer_patch_port = patch-int
68 29
 
69
-# Uncomment this line for the agent if tunnel_id_ranges (above) is not
30
+# Uncomment this line for the agent if tunnel_id_ranges is not
70 31
 # empty for the server. Set local-ip to be the local IP address of
71 32
 # this hypervisor.
72 33
 #
@@ -77,8 +38,8 @@
77 38
 # bridge names to be used for flat and VLAN networks. The length of
78 39
 # bridge names should be no more than 11. Each bridge must
79 40
 # exist, and should have a physical network interface configured as a
80
-# port. All physical networks listed in network_vlan_ranges on the
81
-# server should have mappings to appropriate bridges on each agent.
41
+# port. All physical networks configured on the server should have
42
+# mappings to appropriate bridges on each agent.
82 43
 #
83 44
 # bridge_mappings =
84 45
 # Example: bridge_mappings = physnet1:br-eth1
@@ -102,10 +63,8 @@
102 63
 # (ListOpt) The types of tenant network tunnels supported by the agent.
103 64
 # Setting this will enable tunneling support in the agent. This can be set to
104 65
 # either 'gre' or 'vxlan'. If this is unset, it will default to [] and
105
-# disable tunneling support in the agent. When running the agent with the OVS
106
-# plugin, this value must be the same as "tunnel_type" in the "[ovs]" section.
107
-# When running the agent with ML2, you can specify as many values here as
108
-# your compute hosts supports.
66
+# disable tunneling support in the agent.
67
+# You can specify as many values here as your compute hosts supports.
109 68
 #
110 69
 # tunnel_types =
111 70
 # Example: tunnel_types = gre
@@ -164,25 +123,17 @@
164 123
 #
165 124
 # 1. With VLANs on eth1.
166 125
 # [ovs]
167
-# network_vlan_ranges = default:2000:3999
168
-# tunnel_id_ranges =
169 126
 # integration_bridge = br-int
170 127
 # bridge_mappings = default:br-eth1
171 128
 #
172 129
 # 2. With GRE tunneling.
173 130
 # [ovs]
174
-# network_vlan_ranges =
175
-# tunnel_id_ranges = 1:1000
176 131
 # integration_bridge = br-int
177 132
 # tunnel_bridge = br-tun
178 133
 # local_ip = 10.0.0.3
179 134
 #
180 135
 # 3. With VXLAN tunneling.
181 136
 # [ovs]
182
-# network_vlan_ranges =
183
-# tenant_network_type = vxlan
184
-# tunnel_type = vxlan
185
-# tunnel_id_ranges = 1:1000
186 137
 # integration_bridge = br-int
187 138
 # tunnel_bridge = br-tun
188 139
 # local_ip = 10.0.0.3

+ 3
- 0
etc/neutron/rootwrap.d/l3.filters View File

@@ -46,3 +46,6 @@ ip6tables-restore: CommandFilter, ip6tables-restore, root
46 46
 # Keepalived
47 47
 keepalived: CommandFilter, keepalived, root
48 48
 kill_keepalived: KillFilter, root, /usr/sbin/keepalived, -HUP, -15, -9
49
+
50
+# l3 agent to delete floatingip's conntrack state
51
+conntrack: CommandFilter, conntrack, root

+ 0
- 14
neutron/agent/__init__.py View File

@@ -1,14 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
3
-#
4
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
-#    not use this file except in compliance with the License. You may obtain
6
-#    a copy of the License at
7
-#
8
-#         http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-#    Unless required by applicable law or agreed to in writing, software
11
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
-#    License for the specific language governing permissions and limitations
14
-#    under the License.

+ 0
- 14
neutron/agent/common/__init__.py View File

@@ -1,14 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
3
-#
4
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
-#    not use this file except in compliance with the License. You may obtain
6
-#    a copy of the License at
7
-#
8
-#         http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-#    Unless required by applicable law or agreed to in writing, software
11
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
-#    License for the specific language governing permissions and limitations
14
-#    under the License.

+ 9
- 0
neutron/agent/common/config.py View File

@@ -46,6 +46,11 @@ USE_NAMESPACES_OPTS = [
46 46
                 help=_("Allow overlapping IP.")),
47 47
 ]
48 48
 
49
+IPTABLES_OPTS = [
50
+    cfg.BoolOpt('comment_iptables_rules', default=True,
51
+                help=_("Add comments to iptables rules.")),
52
+]
53
+
49 54
 
50 55
 def get_log_args(conf, log_file_name):
51 56
     cmd_args = []
@@ -92,6 +97,10 @@ def register_use_namespaces_opts_helper(conf):
92 97
     conf.register_opts(USE_NAMESPACES_OPTS)
93 98
 
94 99
 
100
+def register_iptables_opts(conf):
101
+    conf.register_opts(IPTABLES_OPTS, 'AGENT')
102
+
103
+
95 104
 def get_root_helper(conf):
96 105
     root_helper = conf.AGENT.root_helper
97 106
     if root_helper != 'sudo':

+ 14
- 0
neutron/agent/firewall.py View File

@@ -105,6 +105,14 @@ class FirewallDriver(object):
105 105
         finally:
106 106
             self.filter_defer_apply_off()
107 107
 
108
+    def update_security_group_members(self, sg_id, ips):
109
+        """Update group members in a security group."""
110
+        raise NotImplementedError()
111
+
112
+    def update_security_group_rules(self, sg_id, rules):
113
+        """Update rules in a security group."""
114
+        raise NotImplementedError()
115
+
108 116
 
109 117
 class NoopFirewallDriver(FirewallDriver):
110 118
     """Noop Firewall Driver.
@@ -134,3 +142,9 @@ class NoopFirewallDriver(FirewallDriver):
134 142
     @property
135 143
     def ports(self):
136 144
         return {}
145
+
146
+    def update_security_group_members(self, sg_id, ips):
147
+        pass
148
+
149
+    def update_security_group_rules(self, sg_id, rules):
150
+        pass

+ 2
- 2
neutron/agent/l2population_rpc.py View File

@@ -242,12 +242,12 @@ class L2populationRpcCallBackTunnelMixin(L2populationRpcCallBackMixin):
242 242
                 if agent_ip == local_ip:
243 243
                     continue
244 244
 
245
-                after = state.get('after')
245
+                after = state.get('after', [])
246 246
                 for mac, ip in after:
247 247
                     self.setup_entry_for_arp_reply(br, 'add', lvm.vlan, mac,
248 248
                                                    ip)
249 249
 
250
-                before = state.get('before')
250
+                before = state.get('before', [])
251 251
                 for mac, ip in before:
252 252
                     self.setup_entry_for_arp_reply(br, 'remove', lvm.vlan, mac,
253 253
                                                    ip)

+ 122
- 90
neutron/agent/l3_agent.py View File

@@ -22,6 +22,7 @@ eventlet.monkey_patch()
22 22
 import netaddr
23 23
 import os
24 24
 from oslo.config import cfg
25
+from oslo import messaging
25 26
 import Queue
26 27
 
27 28
 from neutron.agent.common import config
@@ -34,13 +35,15 @@ from neutron.agent.linux import ra
34 35
 from neutron.agent import rpc as agent_rpc
35 36
 from neutron.common import config as common_config
36 37
 from neutron.common import constants as l3_constants
38
+from neutron.common import exceptions as n_exc
37 39
 from neutron.common import ipv6_utils
38 40
 from neutron.common import rpc as n_rpc
39 41
 from neutron.common import topics
40 42
 from neutron.common import utils as common_utils
41
-from neutron import context
43
+from neutron import context as n_context
42 44
 from neutron import manager
43 45
 from neutron.openstack.common import excutils
46
+from neutron.openstack.common.gettextutils import _LE, _LW
44 47
 from neutron.openstack.common import importutils
45 48
 from neutron.openstack.common import log as logging
46 49
 from neutron.openstack.common import loopingcall
@@ -241,7 +244,7 @@ class LinkLocalAllocator(object):
241 244
 class RouterInfo(l3_ha_agent.RouterMixin):
242 245
 
243 246
     def __init__(self, router_id, root_helper, use_namespaces, router,
244
-                 use_ipv6=False):
247
+                 use_ipv6=False, ns_name=None):
245 248
         self.router_id = router_id
246 249
         self.ex_gw_port = None
247 250
         self._snat_enabled = None
@@ -254,7 +257,7 @@ class RouterInfo(l3_ha_agent.RouterMixin):
254 257
         self.use_namespaces = use_namespaces
255 258
         # Invoke the setter for establishing initial SNAT action
256 259
         self.router = router
257
-        self.ns_name = NS_PREFIX + router_id if use_namespaces else None
260
+        self.ns_name = ns_name
258 261
         self.iptables_manager = iptables_manager.IptablesManager(
259 262
             root_helper=root_helper,
260 263
             use_ipv6=use_ipv6,
@@ -518,31 +521,47 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
518 521
             LOG.error(msg)
519 522
             raise SystemExit(1)
520 523
 
521
-        self.context = context.get_admin_context_without_session()
524
+        self.context = n_context.get_admin_context_without_session()
522 525
         self.plugin_rpc = L3PluginApi(topics.L3PLUGIN, host)
523 526
         self.fullsync = True
524
-        self.updated_routers = set()
525
-        self.removed_routers = set()
526 527
         self.sync_progress = False
527 528
 
528 529
         # Get the list of service plugins from Neutron Server
529
-        try:
530
-            self.neutron_service_plugins = (
531
-                self.plugin_rpc.get_service_plugin_list(self.context))
532
-        except n_rpc.RemoteError as e:
533
-            LOG.warning(_('l3-agent cannot check service plugins '
534
-                          'enabled at the neutron server when startup '
535
-                          'due to RPC error. It happens when the server '
536
-                          'does not support this RPC API. If the error '
537
-                          'is UnsupportedVersion you can ignore '
538
-                          'this warning. Detail message: %s'), e)
539
-            self.neutron_service_plugins = None
530
+        # This is the first place where we contact neutron-server on startup
531
+        # so retry in case its not ready to respond.
532
+        retry_count = 5
533
+        while True:
534
+            retry_count = retry_count - 1
535
+            try:
536
+                self.neutron_service_plugins = (
537
+                    self.plugin_rpc.get_service_plugin_list(self.context))
538
+            except n_rpc.RemoteError as e:
539
+                with excutils.save_and_reraise_exception() as ctx:
540
+                    ctx.reraise = False
541
+                    LOG.warning(_LW('l3-agent cannot check service plugins '
542
+                                    'enabled at the neutron server when '
543
+                                    'startup due to RPC error. It happens '
544
+                                    'when the server does not support this '
545
+                                    'RPC API. If the error is '
546
+                                    'UnsupportedVersion you can ignore this '
547
+                                    'warning. Detail message: %s'), e)
548
+                self.neutron_service_plugins = None
549
+            except messaging.MessagingTimeout as e:
550
+                with excutils.save_and_reraise_exception() as ctx:
551
+                    if retry_count > 0:
552
+                        ctx.reraise = False
553
+                        LOG.warning(_LW('l3-agent cannot check service '
554
+                                        'plugins enabled on the neutron '
555
+                                        'server. Retrying. '
556
+                                        'Detail message: %s'), e)
557
+                        continue
558
+            break
540 559
 
541 560
         self._clean_stale_namespaces = self.conf.use_namespaces
542 561
 
543 562
         # dvr data
544 563
         self.agent_gateway_port = None
545
-        self.agent_fip_count = 0
564
+        self.fip_ns_subscribers = set()
546 565
         self.local_subnets = LinkLocalAllocator(
547 566
             os.path.join(self.conf.state_path, 'fip-linklocal-networks'),
548 567
             FIP_LL_SUBNET)
@@ -554,6 +573,15 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
554 573
         self.target_ex_net_id = None
555 574
         self.use_ipv6 = ipv6_utils.is_enabled()
556 575
 
576
+    def _fip_ns_subscribe(self, router_id):
577
+        is_first = (len(self.fip_ns_subscribers) == 0)
578
+        self.fip_ns_subscribers.add(router_id)
579
+        return is_first
580
+
581
+    def _fip_ns_unsubscribe(self, router_id):
582
+        self.fip_ns_subscribers.discard(router_id)
583
+        return len(self.fip_ns_subscribers) == 0
584
+
557 585
     def _check_config_params(self):
558 586
         """Check items in configuration files.
559 587
 
@@ -588,16 +616,22 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
588 616
                             'for namespace cleanup.'))
589 617
             return set()
590 618
 
619
+    def _get_routers_namespaces(self, router_ids):
620
+        namespaces = set(self.get_ns_name(rid) for rid in router_ids)
621
+        namespaces.update(self.get_snat_ns_name(rid) for rid in router_ids)
622
+        return namespaces
623
+
591 624
     def _cleanup_namespaces(self, router_namespaces, router_ids):
592 625
         """Destroy stale router namespaces on host when L3 agent restarts
593 626
 
594
-            This routine is called when self._clean_stale_namespaces is True.
627
+        This routine is called when self._clean_stale_namespaces is True.
595 628
 
596 629
         The argument router_namespaces is the list of all routers namespaces
597 630
         The argument router_ids is the list of ids for known routers.
598 631
         """
599
-        ns_to_ignore = set(NS_PREFIX + id for id in router_ids)
600
-        ns_to_ignore.update(SNAT_NS_PREFIX + id for id in router_ids)
632
+        # Don't destroy namespaces of routers this agent handles.
633
+        ns_to_ignore = self._get_routers_namespaces(router_ids)
634
+
601 635
         ns_to_destroy = router_namespaces - ns_to_ignore
602 636
         self._destroy_stale_router_namespaces(ns_to_destroy)
603 637
 
@@ -610,7 +644,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
610 644
         one attempt will be made to delete them.
611 645
         """
612 646
         for ns in router_namespaces:
613
-            ra.disable_ipv6_ra(ns[len(NS_PREFIX):], ns, self.root_helper)
614 647
             try:
615 648
                 self._destroy_namespace(ns)
616 649
             except RuntimeError:
@@ -620,8 +653,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
620 653
 
621 654
     def _destroy_namespace(self, ns):
622 655
         if ns.startswith(NS_PREFIX):
623
-            if self.conf.enable_metadata_proxy:
624
-                self._destroy_metadata_proxy(ns[len(NS_PREFIX):], ns)
625 656
             self._destroy_router_namespace(ns)
626 657
         elif ns.startswith(FIP_NS_PREFIX):
627 658
             self._destroy_fip_namespace(ns)
@@ -670,6 +701,10 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
670 701
         self.agent_gateway_port = None
671 702
 
672 703
     def _destroy_router_namespace(self, ns):
704
+        router_id = ns[len(NS_PREFIX):]
705
+        ra.disable_ipv6_ra(router_id, ns, self.root_helper)
706
+        if self.conf.enable_metadata_proxy:
707
+            self._destroy_metadata_proxy(router_id, ns)
673 708
         ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=ns)
674 709
         for d in ns_ip.get_devices(exclude_loopback=True):
675 710
             if d.name.startswith(INTERNAL_DEV_PREFIX):
@@ -727,9 +762,14 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
727 762
                     raise Exception(msg)
728 763
 
729 764
     def _router_added(self, router_id, router):
730
-        ri = RouterInfo(router_id, self.root_helper,
731
-                        self.conf.use_namespaces, router,
732
-                        use_ipv6=self.use_ipv6)
765
+        ns_name = (self.get_ns_name(router_id)
766
+                   if self.conf.use_namespaces else None)
767
+        ri = RouterInfo(router_id=router_id,
768
+                        root_helper=self.root_helper,
769
+                        use_namespaces=self.conf.use_namespaces,
770
+                        router=router,
771
+                        use_ipv6=self.use_ipv6,
772
+                        ns_name=ns_name)
733 773
         self.router_info[router_id] = ri
734 774
         if self.conf.use_namespaces:
735 775
             self._create_router_namespace(ri)
@@ -768,15 +808,13 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
768 808
         for c, r in self.metadata_nat_rules():
769 809
             ri.iptables_manager.ipv4['nat'].remove_rule(c, r)
770 810
         ri.iptables_manager.apply()
771
-        if self.conf.enable_metadata_proxy:
772
-            self._destroy_metadata_proxy(ri.router_id, ri.ns_name)
773 811
         del self.router_info[router_id]
774 812
         self._destroy_router_namespace(ri.ns_name)
775 813
 
776 814
     def _get_metadata_proxy_callback(self, router_id):
777 815
 
778 816
         def callback(pid_file):
779
-            metadata_proxy_socket = cfg.CONF.metadata_proxy_socket
817
+            metadata_proxy_socket = self.conf.metadata_proxy_socket
780 818
             proxy_cmd = ['neutron-ns-metadata-proxy',
781 819
                          '--pid_file=%s' % pid_file,
782 820
                          '--metadata_proxy_socket=%s' % metadata_proxy_socket,
@@ -784,7 +822,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
784 822
                          '--state_path=%s' % self.conf.state_path,
785 823
                          '--metadata_port=%s' % self.conf.metadata_port]
786 824
             proxy_cmd.extend(config.get_log_args(
787
-                cfg.CONF, 'neutron-ns-metadata-proxy-%s.log' %
825
+                self.conf, 'neutron-ns-metadata-proxy-%s.log' %
788 826
                 router_id))
789 827
             return proxy_cmd
790 828
 
@@ -948,7 +986,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
948 986
         # Process SNAT/DNAT rules for floating IPs
949 987
         fip_statuses = {}
950 988
         try:
951
-            if ex_gw_port or ri.ex_gw_port:
989
+            if ex_gw_port:
952 990
                 existing_floating_ips = ri.floating_ips
953 991
                 self.process_router_floating_ip_nat_rules(ri)
954 992
                 ri.iptables_manager.defer_apply_off()
@@ -962,7 +1000,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
962 1000
             for fip in ri.router.get(l3_constants.FLOATINGIP_KEY, []):
963 1001
                 fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ERROR
964 1002
 
965
-        if ex_gw_port or ri.ex_gw_port:
1003
+        if ex_gw_port:
966 1004
             # Identify floating IPs which were disabled
967 1005
             ri.floating_ips = set(fip_statuses.keys())
968 1006
             for fip_id in existing_floating_ips - ri.floating_ips:
@@ -1066,9 +1104,11 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1066 1104
         if ri.router['distributed']:
1067 1105
             # filter out only FIPs for this host/agent
1068 1106
             floating_ips = [i for i in floating_ips if i['host'] == self.host]
1069
-            if floating_ips and self.agent_gateway_port is None:
1070
-                self._create_agent_gateway_port(ri, floating_ips[0]
1071
-                                                ['floating_network_id'])
1107
+            if floating_ips:
1108
+                is_first = self._fip_ns_subscribe(ri.router_id)
1109
+                if is_first:
1110
+                    self._create_agent_gateway_port(ri, floating_ips[0]
1111
+                                                    ['floating_network_id'])
1072 1112
 
1073 1113
             if self.agent_gateway_port:
1074 1114
                 if floating_ips and ri.dist_fip_count == 0:
@@ -1115,6 +1155,9 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1115 1155
         else:
1116 1156
             net = netaddr.IPNetwork(ip_cidr)
1117 1157
             device.addr.delete(net.version, ip_cidr)
1158
+            self.driver.delete_conntrack_state(root_helper=self.root_helper,
1159
+                                               namespace=ri.ns_name,
1160
+                                               ip=ip_cidr)
1118 1161
             if ri.router['distributed']:
1119 1162
                 self.floating_ip_removed_dist(ri, ip_cidr)
1120 1163
 
@@ -1215,6 +1258,9 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1215 1258
     def get_fip_ns_name(self, ext_net_id):
1216 1259
         return (FIP_NS_PREFIX + ext_net_id)
1217 1260
 
1261
+    def get_ns_name(self, router_id):
1262
+        return (NS_PREFIX + router_id)
1263
+
1218 1264
     def get_snat_ns_name(self, router_id):
1219 1265
         return (SNAT_NS_PREFIX + router_id)
1220 1266
 
@@ -1384,7 +1430,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1384 1430
                 self._snat_redirect_remove(ri, p, internal_interface)
1385 1431
 
1386 1432
             if self.conf.agent_mode == 'dvr_snat' and (
1387
-                ex_gw_port['binding:host_id'] == self.host):
1433
+                ri.router['gw_port_host'] == self.host):
1388 1434
                 ns_name = self.get_snat_ns_name(ri.router['id'])
1389 1435
             else:
1390 1436
                 # not hosting agent - no work to do
@@ -1626,7 +1672,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1626 1672
                                          interface_name, floating_ip,
1627 1673
                                          distributed=True)
1628 1674
         # update internal structures
1629
-        self.agent_fip_count = self.agent_fip_count + 1
1630 1675
         ri.dist_fip_count = ri.dist_fip_count + 1
1631 1676
 
1632 1677
     def floating_ip_removed_dist(self, ri, fip_cidr):
@@ -1660,10 +1705,10 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1660 1705
             self.local_subnets.release(ri.router_id)
1661 1706
             ri.rtr_fip_subnet = None
1662 1707
             ns_ip.del_veth(fip_2_rtr_name)
1663
-        # clean up fip-namespace if this is the last FIP
1664
-        self.agent_fip_count = self.agent_fip_count - 1
1665
-        if self.agent_fip_count == 0:
1666
-            self._destroy_fip_namespace(fip_ns_name)
1708
+            is_last = self._fip_ns_unsubscribe(ri.router_id)
1709
+            # clean up fip-namespace if this is the last FIP
1710
+            if is_last:
1711
+                self._destroy_fip_namespace(fip_ns_name)
1667 1712
 
1668 1713
     def floating_forward_rules(self, floating_ip, fixed_ip):
1669 1714
         return [('PREROUTING', '-d %s -j DNAT --to %s' %
@@ -1742,51 +1787,38 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1742 1787
         LOG.debug(_('Got router added to agent :%r'), payload)
1743 1788
         self.routers_updated(context, payload)
1744 1789
 
1745
-    def _process_routers(self, routers, all_routers=False):
1746
-        pool = eventlet.GreenPool()
1790
+    def _process_router_if_compatible(self, router):
1747 1791
         if (self.conf.external_network_bridge and
1748 1792
             not ip_lib.device_exists(self.conf.external_network_bridge)):
1749 1793
             LOG.error(_("The external network bridge '%s' does not exist"),
1750 1794
                       self.conf.external_network_bridge)
1751 1795
             return
1752 1796
 
1797
+        # If namespaces are disabled, only process the router associated
1798
+        # with the configured agent id.
1799
+        if (not self.conf.use_namespaces and
1800
+            router['id'] != self.conf.router_id):
1801
+            raise n_exc.RouterNotCompatibleWithAgent(router_id=router['id'])
1802
+
1803
+        # Either ex_net_id or handle_internal_only_routers must be set
1804
+        ex_net_id = (router['external_gateway_info'] or {}).get('network_id')
1805
+        if not ex_net_id and not self.conf.handle_internal_only_routers:
1806
+            raise n_exc.RouterNotCompatibleWithAgent(router_id=router['id'])
1807
+
1808
+        # If target_ex_net_id and ex_net_id are set they must be equal
1753 1809
         target_ex_net_id = self._fetch_external_net_id()
1754
-        # if routers are all the routers we have (They are from router sync on
1755
-        # starting or when error occurs during running), we seek the
1756
-        # routers which should be removed.
1757
-        # If routers are from server side notification, we seek them
1758
-        # from subset of incoming routers and ones we have now.
1759
-        if all_routers:
1760
-            prev_router_ids = set(self.router_info)
1761
-        else:
1762
-            prev_router_ids = set(self.router_info) & set(
1763
-                [router['id'] for router in routers])
1764
-        cur_router_ids = set()
1765
-        for r in routers:
1766
-            # If namespaces are disabled, only process the router associated
1767
-            # with the configured agent id.
1768
-            if (not self.conf.use_namespaces and
1769
-                r['id'] != self.conf.router_id):
1770
-                continue
1771
-            ex_net_id = (r['external_gateway_info'] or {}).get('network_id')
1772
-            if not ex_net_id and not self.conf.handle_internal_only_routers:
1773
-                continue
1774
-            if (target_ex_net_id and ex_net_id and
1775
-                ex_net_id != target_ex_net_id):
1776
-                # Double check that our single external_net_id has not changed
1777
-                # by forcing a check by RPC.
1778
-                if (ex_net_id != self._fetch_external_net_id(force=True)):
1779
-                    continue
1780
-            cur_router_ids.add(r['id'])
1781
-            if r['id'] not in self.router_info:
1782
-                self._router_added(r['id'], r)
1783
-            ri = self.router_info[r['id']]
1784
-            ri.router = r
1785
-            pool.spawn_n(self.process_router, ri)
1786
-        # identify and remove routers that no longer exist
1787
-        for router_id in prev_router_ids - cur_router_ids:
1788
-            pool.spawn_n(self._router_removed, router_id)
1789
-        pool.waitall()
1810
+        if (target_ex_net_id and ex_net_id and ex_net_id != target_ex_net_id):
1811
+            # Double check that our single external_net_id has not changed
1812
+            # by forcing a check by RPC.
1813
+            if ex_net_id != self._fetch_external_net_id(force=True):
1814
+                raise n_exc.RouterNotCompatibleWithAgent(
1815
+                    router_id=router['id'])
1816
+
1817
+        if router['id'] not in self.router_info:
1818
+            self._router_added(router['id'], router)
1819
+        ri = self.router_info[router['id']]
1820
+        ri.router = router
1821
+        self.process_router(ri)
1790 1822
 
1791 1823
     def _process_router_update(self):
1792 1824
         for rp, update in self._queue.each_update_to_next_router():
@@ -1810,7 +1842,15 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1810 1842
                 self._router_removed(update.id)
1811 1843
                 continue
1812 1844
 
1813
-            self._process_routers([router])
1845
+            try:
1846
+                self._process_router_if_compatible(router)
1847
+            except n_exc.RouterNotCompatibleWithAgent as e:
1848
+                LOG.exception(e.msg)
1849
+                # Was the router previously handled by this agent?
1850
+                if router['id'] in self.router_info:
1851
+                    LOG.error(_LE("Removing incompatible router '%s'"),
1852
+                              router['id'])
1853
+                    self._router_removed(router['id'])
1814 1854
             LOG.debug("Finished a router update for %s", update.id)
1815 1855
             rp.fetched_and_processed(update.timestamp)
1816 1856
 
@@ -1820,12 +1860,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1820 1860
         while True:
1821 1861
             pool.spawn_n(self._process_router_update)
1822 1862
 
1823
-    def _process_router_delete(self):
1824
-        current_removed_routers = list(self.removed_routers)
1825
-        for router_id in current_removed_routers:
1826
-            self._router_removed(router_id)
1827
-            self.removed_routers.remove(router_id)
1828
-
1829 1863
     def _router_ids(self):
1830 1864
         if not self.conf.use_namespaces:
1831 1865
             return [self.conf.router_id]
@@ -1851,8 +1885,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
1851 1885
 
1852 1886
         try:
1853 1887
             router_ids = self._router_ids()
1854
-            self.updated_routers.clear()
1855
-            self.removed_routers.clear()
1856 1888
             timestamp = timeutils.utcnow()
1857 1889
             routers = self.plugin_rpc.get_routers(
1858 1890
                 context, router_ids)
@@ -1946,7 +1978,7 @@ class L3NATAgentWithStateReport(L3NATAgent):
1946 1978
                 'interface_driver': self.conf.interface_driver},
1947 1979
             'start_flag': True,
1948 1980
             'agent_type': l3_constants.AGENT_TYPE_L3}
1949
-        report_interval = cfg.CONF.AGENT.report_interval
1981
+        report_interval = self.conf.AGENT.report_interval
1950 1982
         self.use_call = True
1951 1983
         if report_interval:
1952 1984
             self.heartbeat = loopingcall.FixedIntervalLoopingCall(

+ 0
- 14
neutron/agent/linux/__init__.py View File

@@ -1,14 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
3
-#
4
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
-#    not use this file except in compliance with the License. You may obtain
6
-#    a copy of the License at
7
-#
8
-#         http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-#    Unless required by applicable law or agreed to in writing, software
11
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
-#    License for the specific language governing permissions and limitations
14
-#    under the License.

+ 2
- 1
neutron/agent/linux/dhcp.py View File

@@ -730,7 +730,8 @@ class Dnsmasq(DhcpLocalProcess):
730 730
         subnets = dict((subnet.id, subnet) for subnet in network.subnets)
731 731
 
732 732
         for port in network.ports:
733
-            if port.device_owner != constants.DEVICE_OWNER_ROUTER_INTF:
733
+            if port.device_owner not in (constants.DEVICE_OWNER_ROUTER_INTF,
734
+                                         constants.DEVICE_OWNER_DVR_INTERFACE):
734 735
                 continue
735 736
             for alloc in port.fixed_ips:
736 737
                 if subnets[alloc.subnet_id].gateway_ip == alloc.ip_address:

+ 3
- 5
neutron/agent/linux/external_process.py View File

@@ -30,14 +30,12 @@ OPTS = [
30 30
     cfg.StrOpt('external_pids',
31 31
                default='$state_path/external/pids',
32 32
                help=_('Location to store child pid files')),
33
-    cfg.BoolOpt('check_child_processes', default=False,
34
-                help=_("Periodically check child processes")),
35 33
     cfg.StrOpt('check_child_processes_action', default='respawn',
36 34
                choices=['respawn', 'exit'],
37 35
                help=_('Action to be executed when a child process dies')),
38
-    cfg.IntOpt('check_child_processes_interval', default=60,
36
+    cfg.IntOpt('check_child_processes_interval', default=0,
39 37
                help=_('Interval between checks of child process liveness '
40
-                      '(seconds)')),
38
+                      '(seconds), use 0 to disable')),
41 39
 ]
42 40
 
43 41
 
@@ -156,7 +154,7 @@ class ProcessMonitor(object):
156 154
 
157 155
         self._process_managers = {}
158 156
 
159
-        if self._config.check_child_processes:
157
+        if self._config.check_child_processes_interval:
160 158
             self._spawn_checking_thread()
161 159
 
162 160
     def enable(self, uuid, cmd_callback, namespace=None, service=None,

+ 53
- 10
neutron/agent/linux/interface.py View File

@@ -23,8 +23,10 @@ from neutron.agent.common import config
23 23
 from neutron.agent.linux import ip_lib
24 24
 from neutron.agent.linux import ovs_lib
25 25
 from neutron.agent.linux import utils
26
+from neutron.common import constants as n_const
26 27
 from neutron.common import exceptions
27 28
 from neutron.extensions import flavor
29
+from neutron.openstack.common.gettextutils import _LE
28 30
 from neutron.openstack.common import importutils
29 31
 from neutron.openstack.common import log as logging
30 32
 
@@ -71,7 +73,7 @@ class LinuxInterfaceDriver(object):
71 73
 
72 74
     # from linux IF_NAMESIZE
73 75
     DEV_NAME_LEN = 14
74
-    DEV_NAME_PREFIX = 'tap'
76
+    DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
75 77
 
76 78
     def __init__(self, conf):
77 79
         self.conf = conf
@@ -110,6 +112,9 @@ class LinuxInterfaceDriver(object):
110 112
         for ip_cidr, ip_version in previous.items():
111 113
             if ip_cidr not in preserve_ips:
112 114
                 device.addr.delete(ip_version, ip_cidr)
115
+                self.delete_conntrack_state(root_helper=self.root_helper,
116
+                                            namespace=namespace,
117
+                                            ip=ip_cidr)
113 118
 
114 119
         if gateway:
115 120
             device.route.add_gateway(gateway)
@@ -121,6 +126,43 @@ class LinuxInterfaceDriver(object):
121 126
         for route in existing_onlink_routes - new_onlink_routes:
122 127
             device.route.delete_onlink_route(route)
123 128
 
129
+    def delete_conntrack_state(self, root_helper, namespace, ip):
130
+        """Delete conntrack state associated with an IP address.
131
+
132
+        This terminates any active connections through an IP.  Call this soon
133
+        after removing the IP address from an interface so that new connections
134
+        cannot be created before the IP address is gone.
135
+
136
+        root_helper: root_helper to gain root access to call conntrack
137
+        namespace: the name of the namespace where the IP has been configured
138
+        ip: the IP address for which state should be removed.  This can be
139
+            passed as a string with or without /NN.  A netaddr.IPAddress or
140
+            netaddr.Network representing the IP address can also be passed.
141
+        """
142
+        ip_str = str(netaddr.IPNetwork(ip).ip)
143
+        ip_wrapper = ip_lib.IPWrapper(root_helper, namespace=namespace)
144
+
145
+        # Delete conntrack state for ingress traffic
146
+        # If 0 flow entries have been deleted
147
+        # conntrack -D will return 1
148
+        try:
149
+            ip_wrapper.netns.execute(["conntrack", "-D", "-d", ip_str],
150
+                                     check_exit_code=True,
151
+                                     extra_ok_codes=[1])
152
+
153
+        except RuntimeError:
154
+            LOG.exception(_LE("Failed deleting ingress connection state of"
155
+                              " floatingip %s"), ip_str)
156
+
157
+        # Delete conntrack state for egress traffic
158
+        try:
159
+            ip_wrapper.netns.execute(["conntrack", "-D", "-q", ip_str],
160
+                                     check_exit_code=True,
161
+                                     extra_ok_codes=[1])
162
+        except RuntimeError:
163
+            LOG.exception(_LE("Failed deleting egress connection state of"
164
+                              " floatingip %s"), ip_str)
165
+
124 166
     def check_bridge_exists(self, bridge):
125 167
         if not ip_lib.device_exists(bridge):
126 168
             raise exceptions.BridgeDoesNotExist(bridge=bridge)
@@ -150,7 +192,7 @@ class NullDriver(LinuxInterfaceDriver):
150 192
 class OVSInterfaceDriver(LinuxInterfaceDriver):
151 193
     """Driver for creating an internal interface on an OVS bridge."""
152 194
 
153
-    DEV_NAME_PREFIX = 'tap'
195
+    DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
154 196
 
155 197
     def __init__(self, conf):
156 198
         super(OVSInterfaceDriver, self).__init__(conf)
@@ -159,7 +201,8 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
159 201
 
160 202
     def _get_tap_name(self, dev_name, prefix=None):
161 203
         if self.conf.ovs_use_veth:
162
-            dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX, 'tap')
204
+            dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX,
205
+                                        n_const.TAP_DEVICE_PREFIX)
163 206
         return dev_name
164 207
 
165 208
     def _ovs_add_port(self, bridge, device_name, port_id, mac_address,
@@ -254,7 +297,8 @@ class MidonetInterfaceDriver(LinuxInterfaceDriver):
254 297
                                     self.root_helper,
255 298
                                     namespace=namespace):
256 299
             ip = ip_lib.IPWrapper(self.root_helper)
257
-            tap_name = device_name.replace(prefix or 'tap', 'tap')
300
+            tap_name = device_name.replace(prefix or n_const.TAP_DEVICE_PREFIX,
301
+                                           n_const.TAP_DEVICE_PREFIX)
258 302
 
259 303
             # Create ns_dev in a namespace if one is configured.
260 304
             root_dev, ns_dev = ip.add_veth(tap_name, device_name,
@@ -293,14 +337,15 @@ class MidonetInterfaceDriver(LinuxInterfaceDriver):
293 337
 class IVSInterfaceDriver(LinuxInterfaceDriver):
294 338
     """Driver for creating an internal interface on an IVS bridge."""
295 339
 
296
-    DEV_NAME_PREFIX = 'tap'
340
+    DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
297 341
 
298 342
     def __init__(self, conf):
299 343
         super(IVSInterfaceDriver, self).__init__(conf)
300 344
         self.DEV_NAME_PREFIX = 'ns-'
301 345
 
302 346
     def _get_tap_name(self, dev_name, prefix=None):
303
-        dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX, 'tap')
347
+        dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX,
348
+                                    n_const.TAP_DEVICE_PREFIX)
304 349
         return dev_name
305 350
 
306 351
     def _ivs_add_port(self, device_name, port_id, mac_address):
@@ -367,10 +412,8 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
367 412
             ip = ip_lib.IPWrapper(self.root_helper)
368 413
 
369 414
             # Enable agent to define the prefix
370
-            if prefix:
371
-                tap_name = device_name.replace(prefix, 'tap')
372
-            else:
373
-                tap_name = device_name.replace(self.DEV_NAME_PREFIX, 'tap')
415
+            tap_name = device_name.replace(prefix or self.DEV_NAME_PREFIX,
416
+                                        n_const.TAP_DEVICE_PREFIX)
374 417
             # Create ns_veth in a namespace if one is configured.
375 418
             root_veth, ns_veth = ip.add_veth(tap_name, device_name,
376 419
                                              namespace2=namespace)

+ 3
- 2
neutron/agent/linux/ip_lib.py View File

@@ -532,7 +532,8 @@ class IpNetnsCommand(IpCommandBase):
532 532
     def delete(self, name):
533 533
         self._as_root('delete', name, use_root_namespace=True)
534 534
 
535
-    def execute(self, cmds, addl_env={}, check_exit_code=True):
535
+    def execute(self, cmds, addl_env={}, check_exit_code=True,
536
+                extra_ok_codes=None):
536 537
         ns_params = []
537 538
         if self._parent.namespace:
538 539
             if not self._parent.root_helper:
@@ -546,7 +547,7 @@ class IpNetnsCommand(IpCommandBase):
546 547
         return utils.execute(
547 548
             ns_params + env_params + list(cmds),
548 549
             root_helper=self._parent.root_helper,
549
-            check_exit_code=check_exit_code)
550
+            check_exit_code=check_exit_code, extra_ok_codes=extra_ok_codes)
550 551
 
551 552
     def exists(self, name):
552 553
         output = self._parent._execute('o', 'netns', ['list'])

+ 34
- 0
neutron/agent/linux/iptables_comments.py View File

@@ -0,0 +1,34 @@
1
+#    Copyright 2014 OpenStack Foundation
2
+#
3
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+#    not use this file except in compliance with the License. You may obtain
5
+#    a copy of the License at
6
+#
7
+#         http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+#    Unless required by applicable law or agreed to in writing, software
10
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+#    License for the specific language governing permissions and limitations
13
+#    under the License.
14
+
15
+"""iptables comments"""
16
+
17
+# Do not translate these comments. These comments cannot contain a quote or
18
+# an escape character because they will end up in a call to iptables and
19
+# could interfere with other parameters.
20
+
21
+SNAT_OUT = 'Perform source NAT on outgoing traffic.'
22
+UNMATCH_DROP = 'Default drop rule for unmatched traffic.'
23
+VM_INT_SG = 'Direct traffic from the VM interface to the security group chain.'
24
+SG_TO_VM_SG = 'Jump to the VM specific chain.'
25
+INPUT_TO_SG = 'Direct incoming traffic from VM to the security group chain.'
26
+PAIR_ALLOW = 'Allow traffic from defined IP/MAC pairs.'
27
+PAIR_DROP = 'Drop traffic without an IP/MAC allow rule.'
28
+DHCP_CLIENT = 'Allow DHCP client traffic.'
29
+DHCP_SPOOF = 'Prevent DHCP Spoofing by VM.'
30
+UNMATCHED = 'Send unmatched traffic to the fallback chain.'
31
+STATELESS_DROP = 'Drop packets that are not associated with a state.'
32
+ALLOW_ASSOC = ('Direct packets associated with a known session to the RETURN '
33
+               'chain.')
34
+IPV6_RA_ALLOW = 'Allow IPv6 ICMP traffic to allow RA packets.'

+ 39
- 20
neutron/agent/linux/iptables_firewall.py View File

@@ -18,6 +18,7 @@ from oslo.config import cfg
18 18
 
19 19
 from neutron.agent import firewall
20 20
 from neutron.agent.linux import ipset_manager
21
+from neutron.agent.linux import iptables_comments as ic
21 22
 from neutron.agent.linux import iptables_manager
22 23
 from neutron.common import constants
23 24
 from neutron.common import ipv6_utils
@@ -40,6 +41,7 @@ LINUX_DEV_LEN = 14
40 41
 IPSET_CHAIN_LEN = 20
41 42
 IPSET_CHANGE_BULK_THRESHOLD = 10
42 43
 IPSET_ADD_BULK_THRESHOLD = 5
44
+comment_rule = iptables_manager.comment_rule
43 45
 
44 46
 
45 47
 class IptablesFirewallDriver(firewall.FirewallDriver):
@@ -146,9 +148,11 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
146 148
 
147 149
     def _add_fallback_chain_v4v6(self):
148 150
         self.iptables.ipv4['filter'].add_chain('sg-fallback')
149
-        self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP')
151
+        self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP',
152
+                                              comment=ic.UNMATCH_DROP)
150 153
         self.iptables.ipv6['filter'].add_chain('sg-fallback')
151
-        self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP')
154
+        self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP',
155
+                                              comment=ic.UNMATCH_DROP)
152 156
 
153 157
     def _add_chain_by_name_v4v6(self, chain_name):
154 158
         self.iptables.ipv6['filter'].add_chain(chain_name)
@@ -158,12 +162,15 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
158 162
         self.iptables.ipv4['filter'].ensure_remove_chain(chain_name)
159 163
         self.iptables.ipv6['filter'].ensure_remove_chain(chain_name)
160 164
 
161
-    def _add_rule_to_chain_v4v6(self, chain_name, ipv4_rules, ipv6_rules):
165
+    def _add_rule_to_chain_v4v6(self, chain_name, ipv4_rules, ipv6_rules,
166
+                                comment=None):
162 167
         for rule in ipv4_rules:
163
-            self.iptables.ipv4['filter'].add_rule(chain_name, rule)
168
+            self.iptables.ipv4['filter'].add_rule(chain_name, rule,
169
+                                                  comment=comment)
164 170
 
165 171
         for rule in ipv6_rules:
166
-            self.iptables.ipv6['filter'].add_rule(chain_name, rule)
172
+            self.iptables.ipv6['filter'].add_rule(chain_name, rule,
173
+                                                  comment=comment)
167 174
 
168 175
     def _get_device_name(self, port):
169 176
         return port['device']
@@ -183,17 +190,20 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
183 190
                      '-j $%s' % (self.IPTABLES_DIRECTION[direction],
184 191
                                  device,
185 192
                                  SG_CHAIN)]
186
-        self._add_rule_to_chain_v4v6('FORWARD', jump_rule, jump_rule)
193
+        self._add_rule_to_chain_v4v6('FORWARD', jump_rule, jump_rule,
194
+                                     comment=ic.VM_INT_SG)
187 195
 
188 196
         # jump to the chain based on the device
189 197
         jump_rule = ['-m physdev --%s %s --physdev-is-bridged '
190 198
                      '-j $%s' % (self.IPTABLES_DIRECTION[direction],
191 199
                                  device,
192 200
                                  chain_name)]
193
-        self._add_rule_to_chain_v4v6(SG_CHAIN, jump_rule, jump_rule)
201
+        self._add_rule_to_chain_v4v6(SG_CHAIN, jump_rule, jump_rule,
202
+                                     comment=ic.SG_TO_VM_SG)
194 203
 
195 204
         if direction == EGRESS_DIRECTION:
196
-            self._add_rule_to_chain_v4v6('INPUT', jump_rule, jump_rule)
205
+            self._add_rule_to_chain_v4v6('INPUT', jump_rule, jump_rule,
206
+                                         comment=ic.INPUT_TO_SG)
197 207
 
198 208
     def _split_sgr_by_ethertype(self, security_group_rules):
199 209
         ipv4_sg_rules = []
@@ -222,12 +232,12 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
222 232
                     # of the list after the allowed_address_pair rules.
223 233
                     table.add_rule(chain_name,
224 234
                                    '-m mac --mac-source %s -j RETURN'
225
-                                   % mac)
235
+                                   % mac, comment=ic.PAIR_ALLOW)
226 236
                 else:
227 237
                     table.add_rule(chain_name,
228 238
                                    '-m mac --mac-source %s -s %s -j RETURN'
229
-                                   % (mac, ip))
230
-            table.add_rule(chain_name, '-j DROP')
239
+                                   % (mac, ip), comment=ic.PAIR_ALLOW)
240
+            table.add_rule(chain_name, '-j DROP', comment=ic.PAIR_DROP)
231 241
             rules.append('-j $%s' % chain_name)
232 242
 
233 243
     def _build_ipv4v6_mac_ip_list(self, mac, ip_address, mac_ipv4_pairs,
@@ -239,9 +249,12 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
239 249
 
240 250
     def _spoofing_rule(self, port, ipv4_rules, ipv6_rules):
241 251
         #Note(nati) allow dhcp or RA packet
242
-        ipv4_rules += ['-p udp -m udp --sport 68 --dport 67 -j RETURN']
243
-        ipv6_rules += ['-p icmpv6 -j RETURN']
244
-        ipv6_rules += ['-p udp -m udp --sport 546 --dport 547 -j RETURN']
252
+        ipv4_rules += [comment_rule('-p udp -m udp --sport 68 --dport 67 '
253
+                                    '-j RETURN', comment=ic.DHCP_CLIENT)]
254
+        ipv6_rules += [comment_rule('-p icmpv6 -j RETURN',
255
+                                    comment=ic.IPV6_RA_ALLOW)]
256
+        ipv6_rules += [comment_rule('-p udp -m udp --sport 546 --dport 547 '
257
+                                    '-j RETURN', comment=None)]
245 258
         mac_ipv4_pairs = []
246 259
         mac_ipv6_pairs = []
247 260
 
@@ -266,8 +279,10 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
266 279
 
267 280
     def _drop_dhcp_rule(self, ipv4_rules, ipv6_rules):
268 281
         #Note(nati) Drop dhcp packet from VM
269
-        ipv4_rules += ['-p udp -m udp --sport 67 --dport 68 -j DROP']
270
-        ipv6_rules += ['-p udp -m udp --sport 547 --dport 546 -j DROP']
282
+        ipv4_rules += [comment_rule('-p udp -m udp --sport 67 --dport 68 '
283
+                                    '-j DROP', comment=ic.DHCP_SPOOF)]
284
+        ipv6_rules += [comment_rule('-p udp -m udp --sport 547 --dport 546 '
285
+                                    '-j DROP', comment=None)]
271 286
 
272 287
     def _accept_inbound_icmpv6(self):
273 288
         # Allow multicast listener, neighbor solicitation and
@@ -454,18 +469,22 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
454 469
             args += ['-j RETURN']
455 470
             iptables_rules += [' '.join(args)]
456 471
 
457
-        iptables_rules += ['-j $sg-fallback']
472
+        iptables_rules += [comment_rule('-j $sg-fallback',
473
+                                        comment=ic.UNMATCHED)]
458 474
 
459 475
         return iptables_rules
460 476
 
461 477
     def _drop_invalid_packets(self, iptables_rules):
462 478
         # Always drop invalid packets
463
-        iptables_rules += ['-m state --state ' 'INVALID -j DROP']
479
+        iptables_rules += [comment_rule('-m state --state ' 'INVALID -j DROP',
480
+                                        comment=ic.STATELESS_DROP)]
464 481
         return iptables_rules
465 482
 
466 483
     def _allow_established(self, iptables_rules):
467 484
         # Allow established connections
468
-        iptables_rules += ['-m state --state RELATED,ESTABLISHED -j RETURN']
485
+        iptables_rules += [comment_rule(
486
+            '-m state --state RELATED,ESTABLISHED -j RETURN',
487
+            comment=ic.ALLOW_ASSOC)]
469 488
         return iptables_rules
470 489
 
471 490
     def _protocol_arg(self, protocol):
@@ -560,7 +579,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
560 579
 
561 580
 
562 581
 class OVSHybridIptablesFirewallDriver(IptablesFirewallDriver):
563
-    OVS_HYBRID_TAP_PREFIX = 'tap'
582
+    OVS_HYBRID_TAP_PREFIX = constants.TAP_DEVICE_PREFIX
564 583
 
565 584
     def _port_chain_name(self, port, direction):
566 585
         return iptables_manager.get_chain_name(

+ 24
- 8
neutron/agent/linux/iptables_manager.py View File

@@ -22,6 +22,10 @@ import inspect
22 22
 import os
23 23
 import re
24 24
 
25
+from oslo.config import cfg
26
+
27
+from neutron.agent.common import config
28
+from neutron.agent.linux import iptables_comments as ic
25 29
 from neutron.agent.linux import utils as linux_utils
26 30
 from neutron.common import utils
27 31
 from neutron.openstack.common import excutils
@@ -51,6 +55,12 @@ MAX_CHAIN_LEN_NOWRAP = 28
51 55
 IPTABLES_ERROR_LINES_OF_CONTEXT = 5
52 56
 
53 57
 
58
+def comment_rule(rule, comment):
59
+    if not cfg.CONF.AGENT.comment_iptables_rules or not comment:
60
+        return rule
61
+    return '%s -m comment --comment "%s"' % (rule, comment)
62
+
63
+
54 64
 def get_chain_name(chain_name, wrap=True):
55 65
     if wrap:
56 66
         return chain_name[:MAX_CHAIN_LEN_WRAP]
@@ -67,13 +77,14 @@ class IptablesRule(object):
67 77
     """
68 78
 
69 79
     def __init__(self, chain, rule, wrap=True, top=False,
70
-                 binary_name=binary_name, tag=None):
80
+                 binary_name=binary_name, tag=None, comment=None):
71 81
         self.chain = get_chain_name(chain, wrap)
72 82
         self.rule = rule
73 83
         self.wrap = wrap
74 84
         self.top = top
75 85
         self.wrap_name = binary_name[:16]
76 86
         self.tag = tag
87
+        self.comment = comment
77 88
 
78 89
     def __eq__(self, other):
79 90
         return ((self.chain == other.chain) and
@@ -89,7 +100,7 @@ class IptablesRule(object):
89 100
             chain = '%s-%s' % (self.wrap_name, self.chain)
90 101
         else:
91 102
             chain = self.chain
92
-        return '-A %s %s' % (chain, self.rule)
103
+        return comment_rule('-A %s %s' % (chain, self.rule), self.comment)
93 104
 
94 105
 
95 106
 class IptablesTable(object):
@@ -182,7 +193,8 @@ class IptablesTable(object):
182 193
         self.rules = [r for r in self.rules
183 194
                       if jump_snippet not in r.rule]
184 195
 
185
-    def add_rule(self, chain, rule, wrap=True, top=False, tag=None):
196
+    def add_rule(self, chain, rule, wrap=True, top=False, tag=None,
197
+                 comment=None):
186 198
         """Add a rule to the table.
187 199
 
188 200
         This is just like what you'd feed to iptables, just without
@@ -202,7 +214,7 @@ class IptablesTable(object):
202 214
                 self._wrap_target_chain(e, wrap) for e in rule.split(' '))
203 215
 
204 216
         self.rules.append(IptablesRule(chain, rule, wrap, top, self.wrap_name,
205
-                                       tag))
217
+                                       tag, comment))
206 218
 
207 219
     def _wrap_target_chain(self, s, wrap):
208 220
         if s.startswith('$'):
@@ -210,7 +222,7 @@ class IptablesTable(object):
210 222
 
211 223
         return s
212 224
 
213
-    def remove_rule(self, chain, rule, wrap=True, top=False):
225
+    def remove_rule(self, chain, rule, wrap=True, top=False, comment=None):
214 226
         """Remove a rule from a chain.
215 227
 
216 228
         Note: The rule must be exactly identical to the one that was added.
@@ -225,10 +237,12 @@ class IptablesTable(object):
225 237
                     self._wrap_target_chain(e, wrap) for e in rule.split(' '))
226 238
 
227 239
             self.rules.remove(IptablesRule(chain, rule, wrap, top,
228
-                                           self.wrap_name))
240
+                                           self.wrap_name,
241
+                                           comment=comment))
229 242
             if not wrap:
230 243
                 self.remove_rules.append(IptablesRule(chain, rule, wrap, top,
231
-                                                      self.wrap_name))
244
+                                                      self.wrap_name,
245
+                                                      comment=comment))
232 246
         except ValueError:
233 247
             LOG.warn(_('Tried to remove rule that was not there:'
234 248
                        ' %(chain)r %(rule)r %(wrap)r %(top)r'),
@@ -288,6 +302,7 @@ class IptablesManager(object):
288 302
         else:
289 303
             self.execute = linux_utils.execute
290 304
 
305
+        config.register_iptables_opts(cfg.CONF)
291 306
         self.use_ipv6 = use_ipv6
292 307
         self.root_helper = root_helper
293 308
         self.namespace = namespace
@@ -351,7 +366,8 @@ class IptablesManager(object):
351 366
             # chain so that it's applied last.
352 367
             self.ipv4['nat'].add_chain('snat')
353 368
             self.ipv4['nat'].add_rule('neutron-postrouting-bottom',
354
-                                      '-j $snat', wrap=False)
369
+                                      '-j $snat', wrap=False,
370
+                                      comment=ic.SNAT_OUT)
355 371
 
356 372
             # And then we add a float-snat chain and jump to first thing in
357 373
             # the snat chain.

+ 23
- 23
neutron/agent/linux/ovs_lib.py View File

@@ -22,6 +22,7 @@ from neutron.agent.linux import ip_lib
22 22
 from neutron.agent.linux import utils
23 23
 from neutron.common import exceptions
24 24
 from neutron.openstack.common import excutils
25
+from neutron.openstack.common.gettextutils import _LI, _LW
25 26
 from neutron.openstack.common import jsonutils
26 27
 from neutron.openstack.common import log as logging
27 28
 from neutron.plugins.common import constants
@@ -401,29 +402,28 @@ class OVSBridge(BaseOVS):
401 402
             # an exeception which will be captured in this block.
402 403
             # We won't deal with the possibility of ovs-vsctl return multiple
403 404
             # rows since the interface identifier is unique
404
-            data = json_result['data'][0]
405
-            port_name = data[name_idx]
406
-            switch = get_bridge_for_iface(self.root_helper, port_name)
407
-            if switch != self.br_name:
408
-                LOG.info(_("Port: %(port_name)s is on %(switch)s,"
409
-                           " not on %(br_name)s"), {'port_name': port_name,
410
-                                                    'switch': switch,
411
-                                                    'br_name': self.br_name})
412
-                return
413
-            ofport = data[ofport_idx]
414
-            # ofport must be integer otherwise return None
415
-            if not isinstance(ofport, int) or ofport == -1:
416
-                LOG.warn(_("ofport: %(ofport)s for VIF: %(vif)s is not a "
417
-                           "positive integer"), {'ofport': ofport,
418
-                                                 'vif': port_id})
419
-                return
420
-            # Find VIF's mac address in external ids
421
-            ext_id_dict = dict((item[0], item[1]) for item in
422
-                               data[ext_ids_idx][1])
423
-            vif_mac = ext_id_dict['attached-mac']
424
-            return VifPort(port_name, ofport, port_id, vif_mac, self)
425
-        except Exception as e:
426
-            LOG.warn(_("Unable to parse interface details. Exception: %s"), e)
405
+            for data in json_result['data']:
406
+                port_name = data[name_idx]
407
+                switch = get_bridge_for_iface(self.root_helper, port_name)
408
+                if switch != self.br_name:
409
+                    continue
410
+                ofport = data[ofport_idx]
411
+                # ofport must be integer otherwise return None
412
+                if not isinstance(ofport, int) or ofport == -1:
413
+                    LOG.warn(_LW("ofport: %(ofport)s for VIF: %(vif)s is not a"
414
+                                 " positive integer"), {'ofport': ofport,
415
+                                                        'vif': port_id})
416
+                    return
417
+                # Find VIF's mac address in external ids
418
+                ext_id_dict = dict((item[0], item[1]) for item in
419
+                                   data[ext_ids_idx][1])
420
+                vif_mac = ext_id_dict['attached-mac']
421
+                return VifPort(port_name, ofport, port_id, vif_mac, self)
422
+            LOG.info(_LI("Port %(port_id)s not present in bridge %(br_name)s"),
423
+                     {'port_id': port_id, 'br_name': self.br_name})
424
+        except Exception as error:
425
+            LOG.warn(_LW("Unable to parse interface details. Exception: %s"),
426
+                     error)
427 427
             return
428 428
 
429 429
     def delete_ports(self, all_ports=False):

+ 17
- 14
neutron/agent/linux/utils.py View File

@@ -14,9 +14,9 @@
14 14
 #    under the License.
15 15
 
16 16
 import fcntl
17
+import glob
17 18
 import os
18 19
 import shlex
19
-import shutil
20 20
 import socket
21 21
 import struct
22 22
 import tempfile
@@ -58,7 +58,8 @@ def create_process(cmd, root_helper=None, addl_env=None):
58 58
 
59 59
 
60 60
 def execute(cmd, root_helper=None, process_input=None, addl_env=None,
61
-            check_exit_code=True, return_stderr=False, log_fail_as_error=True):
61
+            check_exit_code=True, return_stderr=False, log_fail_as_error=True,
62
+            extra_ok_codes=None):
62 63
     try:
63 64
         obj, cmd = create_process(cmd, root_helper=root_helper,
64 65
                                   addl_env=addl_env)
@@ -70,6 +71,10 @@ def execute(cmd, root_helper=None, process_input=None, addl_env=None,
70 71
               "Stderr: %(stderr)r") % {'cmd': cmd, 'code': obj.returncode,
71 72
                                        'stdout': _stdout, 'stderr': _stderr}
72 73
 
74
+        extra_ok_codes = extra_ok_codes or []
75
+        if obj.returncode and obj.returncode in extra_ok_codes:
76
+            obj.returncode = None
77
+
73 78
         if obj.returncode and log_fail_as_error:
74 79
             LOG.error(m)
75 80
         else:
@@ -129,19 +134,19 @@ def find_child_pids(pid):
129 134
     return [x.strip() for x in raw_pids.split('\n') if x.strip()]
130 135
 
131 136
 
132
-def _get_conf_dir(cfg_root, uuid, ensure_conf_dir):
133
-    confs_dir = os.path.abspath(os.path.normpath(cfg_root))
134
-    conf_dir = os.path.join(confs_dir, uuid)
137
+def _get_conf_base(cfg_root, uuid, ensure_conf_dir):
138
+    conf_dir = os.path.abspath(os.path.normpath(cfg_root))
139
+    conf_base = os.path.join(conf_dir, uuid)
135 140
     if ensure_conf_dir:
136 141
         if not os.path.isdir(conf_dir):
137 142
             os.makedirs(conf_dir, 0o755)
138
-    return conf_dir
143
+    return conf_base
139 144
 
140 145
 
141 146
 def get_conf_file_name(cfg_root, uuid, cfg_file, ensure_conf_dir=False):
142 147
     """Returns the file name for a given kind of config file."""
143
-    conf_dir = _get_conf_dir(cfg_root, uuid, ensure_conf_dir)
144
-    return os.path.join(conf_dir, cfg_file)
148
+    conf_base = _get_conf_base(cfg_root, uuid, ensure_conf_dir)
149
+    return "%s.%s" % (conf_base, cfg_file)
145 150
 
146 151
 
147 152
 def get_value_from_conf_file(cfg_root, uuid, cfg_file, converter=None):
@@ -163,15 +168,13 @@ def get_value_from_conf_file(cfg_root, uuid, cfg_file, converter=None):
163 168
 
164 169
 
165 170
 def remove_conf_files(cfg_root, uuid):
166
-    conf_dir = _get_conf_dir(cfg_root, uuid, False)
167
-    shutil.rmtree(conf_dir, ignore_errors=True)
171
+    conf_base = _get_conf_base(cfg_root, uuid, False)
172
+    for file_path in glob.iglob("%s.*" % conf_base):
173
+        os.unlink(file_path)
168 174
 
169 175
 
170 176
 def remove_conf_file(cfg_root, uuid, cfg_file):
171
-    """Remove a config file. Remove the directory if this is the last file."""
177
+    """Remove a config file."""
172 178
     conf_file = get_conf_file_name(cfg_root, uuid, cfg_file)
173 179
     if os.path.exists(conf_file):
174 180
         os.unlink(conf_file)
175
-        conf_dir = _get_conf_dir(cfg_root, uuid, False)
176
-        if not os.listdir(conf_dir):
177
-            shutil.rmtree(conf_dir, ignore_errors=True)

+ 0
- 13
neutron/agent/metadata/__init__.py View File

@@ -1,13 +0,0 @@
1
-# Copyright 2012 New Dream Network, LLC (DreamHost)
2
-#
3
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
-#    not use this file except in compliance with the License. You may obtain
5
-#    a copy of the License at
6
-#
7
-#         http://www.apache.org/licenses/LICENSE-2.0
8
-#
9
-#    Unless required by applicable law or agreed to in writing, software
10
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
-#    License for the specific language governing permissions and limitations
13
-#    under the License.

+ 8
- 14
neutron/agent/metadata/agent.py View File

@@ -38,7 +38,6 @@ from neutron.openstack.common.cache import cache
38 38
 from neutron.openstack.common import excutils
39 39
 from neutron.openstack.common import log as logging
40 40
 from neutron.openstack.common import loopingcall
41
-from neutron.openstack.common import service
42 41
 from neutron import wsgi
43 42
 
44 43
 LOG = logging.getLogger(__name__)
@@ -210,9 +209,10 @@ class MetadataProxyHandler(object):
210 209
             req.query_string,
211 210
             ''))
212 211
 
213
-        h = httplib2.Http(ca_certs=self.conf.auth_ca_cert,
214
-                          disable_ssl_certificate_validation=
215
-                          self.conf.nova_metadata_insecure)
212
+        h = httplib2.Http(
213
+            ca_certs=self.conf.auth_ca_cert,
214
+            disable_ssl_certificate_validation=self.conf.nova_metadata_insecure
215
+        )
216 216
         if self.conf.nova_client_cert and self.conf.nova_client_priv_key:
217 217
             h.add_certificate(self.conf.nova_client_priv_key,
218 218
                               self.conf.nova_client_cert,
@@ -232,6 +232,8 @@ class MetadataProxyHandler(object):
232 232
             )
233 233
             LOG.warn(msg)
234 234
             return webob.exc.HTTPForbidden()
235
+        elif resp.status == 400:
236
+            return webob.exc.HTTPBadRequest()
235 237
         elif resp.status == 404:
236 238
             return webob.exc.HTTPNotFound()
237 239
         elif resp.status == 409:
@@ -278,16 +280,8 @@ class UnixDomainWSGIServer(wsgi.Server):
278 280
         self._socket = eventlet.listen(file_socket,
279 281
                                        family=socket.AF_UNIX,
280 282
                                        backlog=backlog)
281
-        if workers < 1:
282
-            # For the case where only one process is required.
283
-            self._server = self.pool.spawn_n(self._run, application,
284
-                                             self._socket)
285
-        else:
286
-            # Minimize the cost of checking for child exit by extending the
287
-            # wait interval past the default of 0.01s.
288
-            self._launcher = service.ProcessLauncher(wait_interval=1.0)
289
-            self._server = WorkerService(self, application)
290
-            self._launcher.launch_service(self._server, workers=workers)
283
+
284
+        self._launch(application, workers=workers)
291 285
 
292 286
     def _run(self, application, socket):
293 287
         """Start a WSGI service in a new green thread."""

+ 2
- 0
neutron/agent/metadata/namespace_proxy.py View File

@@ -110,6 +110,8 @@ class NetworkMetadataProxyHandler(object):
110 110
             response.headers['Content-Type'] = resp['content-type']
111 111
             response.body = content
112 112
             return response
113
+        elif resp.status == 400:
114
+            return webob.exc.HTTPBadRequest()
113 115
         elif resp.status == 404:
114 116
             return webob.exc.HTTPNotFound()
115 117
         elif resp.status == 409:

+ 29
- 12
neutron/agent/securitygroups_rpc.py View File

@@ -14,11 +14,14 @@
14 14
 #    under the License.
15 15
 #
16 16
 
17
+import functools
18
+
17 19
 from oslo.config import cfg
18 20
 from oslo import messaging
19 21
 
22
+from neutron.agent import firewall
20 23
 from neutron.common import topics
21
-from neutron.openstack.common.gettextutils import _LW
24
+from neutron.openstack.common.gettextutils import _LI, _LW
22 25
 from neutron.openstack.common import importutils
23 26
 from neutron.openstack.common import log as logging
24 27
 
@@ -74,9 +77,9 @@ def _disable_extension(extension, aliases):
74 77
 
75 78
 def disable_security_group_extension_by_config(aliases):
76 79
     if not is_firewall_enabled():
77
-        LOG.info(_('Disabled security-group extension.'))
80
+        LOG.info(_LI('Disabled security-group extension.'))
78 81
         _disable_extension('security-group', aliases)
79
-        LOG.info(_('Disabled allowed-address-pairs extension.'))
82
+        LOG.info(_LI('Disabled allowed-address-pairs extension.'))
80 83
         _disable_extension('allowed-address-pairs', aliases)
81 84
 
82 85
 
@@ -187,10 +190,23 @@ class SecurityGroupAgentRpcMixin(object):
187 190
             return False
188 191
         return True
189 192
 
193
+    def skip_if_noopfirewall_or_firewall_disabled(func):
194
+        @functools.wraps(func)
195
+        def decorated_function(self, *args, **kwargs):
196
+            if (isinstance(self.firewall, firewall.NoopFirewallDriver) or
197
+                not is_firewall_enabled()):
198
+                LOG.info(_LI("Skipping method %s as firewall is disabled "
199
+                         "or configured as NoopFirewallDriver."),
200
+                         func.__name__)
201
+            else:
202
+                return func(self, *args, **kwargs)
203
+        return decorated_function
204
+
205
+    @skip_if_noopfirewall_or_firewall_disabled
190 206
     def prepare_devices_filter(self, device_ids):
191 207
         if not device_ids:
192 208
             return
193
-        LOG.info(_("Preparing filters for devices %s"), device_ids)
209
+        LOG.info(_LI("Preparing filters for devices %s"), device_ids)
194 210
         if self.use_enhanced_rpc:
195 211
             devices_info = self.plugin_rpc.security_group_info_for_devices(
196 212
                 self.context, list(device_ids))
@@ -220,15 +236,15 @@ class SecurityGroupAgentRpcMixin(object):
220 236
                 remote_sg_id, member_ips)
221 237
 
222 238
     def security_groups_rule_updated(self, security_groups):
223
-        LOG.info(_("Security group "
224
-                   "rule updated %r"), security_groups)
239
+        LOG.info(_LI("Security group "
240
+                 "rule updated %r"), security_groups)
225 241
         self._security_group_updated(
226 242
             security_groups,
227 243
             'security_groups')
228 244
 
229 245
     def security_groups_member_updated(self, security_groups):
230
-        LOG.info(_("Security group "
231
-                   "member updated %r"), security_groups)
246
+        LOG.info(_LI("Security group "
247
+                 "member updated %r"), security_groups)
232 248
         self._security_group_updated(
233 249
             security_groups,
234 250
             'security_group_source_groups')
@@ -249,7 +265,7 @@ class SecurityGroupAgentRpcMixin(object):
249 265
                 self.refresh_firewall(devices)
250 266
 
251 267
     def security_groups_provider_updated(self):
252
-        LOG.info(_("Provider rule updated"))
268
+        LOG.info(_LI("Provider rule updated"))
253 269
         if self.defer_refresh_firewall:
254 270
             # NOTE(salv-orlando): A 'global refresh' might not be
255 271
             # necessary if the subnet for which the provider rules
@@ -261,7 +277,7 @@ class SecurityGroupAgentRpcMixin(object):
261 277
     def remove_devices_filter(self, device_ids):
262 278
         if not device_ids:
263 279
             return
264
-        LOG.info(_("Remove device filter for %r"), device_ids)
280
+        LOG.info(_LI("Remove device filter for %r"), device_ids)
265 281
         with self.firewall.defer_apply():
266 282
             for device_id in device_ids:
267 283
                 device = self.firewall.ports.get(device_id)
@@ -269,12 +285,13 @@ class SecurityGroupAgentRpcMixin(object):
269 285
                     continue
270 286
                 self.firewall.remove_port_filter(device)
271 287
 
288
+    @skip_if_noopfirewall_or_firewall_disabled
272 289
     def refresh_firewall(self, device_ids=None):
273
-        LOG.info(_("Refresh firewall rules"))
290
+        LOG.info(_LI("Refresh firewall rules"))
274 291
         if not device_ids:
275 292
             device_ids = self.firewall.ports.keys()
276 293
             if not device_ids:
277
-                LOG.info(_("No ports here to refresh firewall"))
294
+                LOG.info(_LI("No ports here to refresh firewall"))
278 295
                 return
279 296
         if self.use_enhanced_rpc:
280 297
             devices_info = self.plugin_rpc.security_group_info_for_devices(

+ 3
- 5
neutron/api/rpc/handlers/dhcp_rpc.py View File

@@ -60,7 +60,7 @@ class DhcpRpcCallback(n_rpc.RpcCallback):
60 60
             if action == 'create_port':
61 61
                 return plugin.create_port(context, port)
62 62
             elif action == 'update_port':
63
-                return plugin.update_port(context, port['id'], port['port'])
63
+                return plugin.update_port(context, port['id'], port)
64 64
             else:
65 65
                 msg = _('Unrecognized action')
66 66
                 raise n_exc.Invalid(message=msg)
@@ -282,13 +282,11 @@ class DhcpRpcCallback(n_rpc.RpcCallback):
282 282
     def update_dhcp_port(self, context, **kwargs):
283 283
         """Update the dhcp port."""
284 284
         host = kwargs.get('host')
285
-        port_id = kwargs.get('port_id')
286 285
         port = kwargs.get('port')
286
+        port['id'] = kwargs.get('port_id')
287 287
         LOG.debug(_('Update dhcp port %(port)s '
288 288
                     'from %(host)s.'),
289 289
                   {'port': port,
290 290
                    'host': host})
291 291
         plugin = manager.NeutronManager.get_plugin()
292
-        return self._port_action(plugin, context,
293
-                                 {'id': port_id, 'port': port},
294
-                                 'update_port')
292
+        return self._port_action(plugin, context, port, 'update_port')

+ 0
- 5
neutron/api/rpc/handlers/dvr_rpc.py View File

@@ -115,8 +115,6 @@ class DVRAgentRpcApiMixin(object):
115 115
 class DVRAgentRpcCallbackMixin(object):
116 116
     """Agent-side RPC (implementation) for plugin-to-agent interaction."""
117 117
 
118
-    dvr_agent = None
119
-
120 118
     def dvr_mac_address_update(self, context, **kwargs):
121 119
         """Callback for dvr_mac_addresses update.
122 120
 
@@ -124,7 +122,4 @@ class DVRAgentRpcCallbackMixin(object):
124 122
         """
125 123
         dvr_macs = kwargs.get('dvr_macs', [])
126 124
         LOG.debug("dvr_macs updated on remote: %s", dvr_macs)
127
-        if not self.dvr_agent:
128
-            LOG.warn(_("DVR agent binding currently not set."))
129
-            return
130 125
         self.dvr_agent.dvr_mac_address_update(dvr_macs)

+ 2
- 2
neutron/api/v2/attributes.py View File

@@ -729,11 +729,11 @@ RESOURCE_ATTRIBUTE_MAP = {
729 729
                         'default': True,
730 730
                         'convert_to': convert_to_boolean,
731 731
                         'is_visible': True},
732
-        'ipv6_ra_mode': {'allow_post': True, 'allow_put': True,
732
+        'ipv6_ra_mode': {'allow_post': True, 'allow_put': False,
733 733
                          'default': ATTR_NOT_SPECIFIED,
734 734
                          'validate': {'type:values': constants.IPV6_MODES},
735 735
                          'is_visible': True},
736
-        'ipv6_address_mode': {'allow_post': True, 'allow_put': True,
736
+        'ipv6_address_mode': {'allow_post': True, 'allow_put': False,
737 737
                               'default': ATTR_NOT_SPECIFIED,
738 738
                               'validate': {'type:values':
739 739
                                            constants.IPV6_MODES},

+ 4
- 0
neutron/api/v2/base.py View File

@@ -513,6 +513,10 @@ class Controller(object):
513 513
                               parent_id=parent_id)
514 514
         orig_object_copy = copy.copy(orig_obj)
515 515
         orig_obj.update(body[self._resource])
516
+        # Make a list of attributes to be updated to inform the policy engine
517
+        # which attributes are set explicitly so that it can distinguish them
518
+        # from the ones that are set to their default values.
519
+        orig_obj[const.ATTRIBUTES_TO_UPDATE] = body[self._resource].keys()
516 520
         try:
517 521
             policy.enforce(request.context,
518 522
                            action,

+ 0
- 14
neutron/cmd/__init__.py View File

@@ -1,14 +0,0 @@
1
-# Copyright (c) 2013 OpenStack Foundation.
2
-# All Rights Reserved.
3
-#
4
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
-#    not use this file except in compliance with the License. You may obtain
6
-#    a copy of the License at
7
-#
8
-#         http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-#    Unless required by applicable law or agreed to in writing, software
11
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
-#    License for the specific language governing permissions and limitations
14
-#    under the License.

+ 1
- 5
neutron/cmd/sanity_check.py View File

@@ -104,11 +104,7 @@ def enable_tests_from_config():
104 104
 
105 105
 
106 106
 def all_tests_passed():
107
-    res = True
108
-    for opt in OPTS:
109
-        if cfg.CONF.get(opt.name):
110
-            res &= opt.callback()
111
-    return res
107
+    return all(opt.callback() for opt in OPTS if cfg.CONF.get(opt.name))
112 108
 
113 109
 
114 110
 def main():

+ 2
- 0
neutron/common/config.py View File

@@ -101,6 +101,8 @@ core_opts = [
101 101
                secret=True),
102 102
     cfg.StrOpt('nova_admin_tenant_id',
103 103
                help=_('The uuid of the admin nova tenant')),
104
+    cfg.StrOpt('nova_admin_tenant_name',
105
+               help=_('The name of the admin nova tenant')),
104 106
     cfg.StrOpt('nova_admin_auth_url',
105 107
                default='http://localhost:5000/v2.0',
106 108
                help=_('Authorization URL for connecting to nova in admin '

+ 10
- 0
neutron/common/constants.py View File

@@ -147,3 +147,13 @@ IPV6_LLA_PREFIX = 'fe80::/64'
147 147
 
148 148
 # Linux interface max length
149 149
 DEVICE_NAME_MAX_LEN = 15
150
+
151
+# Device names start with "tap"
152
+TAP_DEVICE_PREFIX = 'tap'
153
+
154
+ATTRIBUTES_TO_UPDATE = 'attributes_to_update'
155
+
156
+# Maximum value integer can take in MySQL and PostgreSQL
157
+# In SQLite integer can be stored in 1, 2, 3, 4, 6, or 8 bytes,
158
+# but here it will be limited by this value for consistency.
159
+DB_INTEGER_MAX_VALUE = 2 ** 31 - 1

+ 4
- 0
neutron/common/exceptions.py View File

@@ -335,3 +335,7 @@ class DeviceIDNotOwnedByTenant(Conflict):
335 335
 
336 336
 class InvalidCIDR(BadRequest):
337 337
     message = _("Invalid CIDR %(input)s given as IP prefix")
338
+
339
+
340
+class RouterNotCompatibleWithAgent(NeutronException):
341
+    message = _("Router '%(router_id)s' is not compatible with this agent")

+ 55
- 29
neutron/db/db_base_plugin_v2.py View File

@@ -449,7 +449,14 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
449 449
                     msg = _('IP address %s is not a valid IP for the defined '
450 450
                             'subnet') % fixed['ip_address']
451 451
                     raise n_exc.InvalidInput(error_message=msg)
452
-
452
+                if self._check_if_subnet_uses_eui64(subnet):
453
+                    msg = (_("IPv6 address %(address)s can not be directly "
454
+                            "assigned to a port on subnet %(id)s with "
455
+                            "%(mode)s address mode") %
456
+                           {'address': fixed['ip_address'],
457
+                            'id': subnet_id,
458
+                            'mode': subnet['ipv6_address_mode']})
459
+                    raise n_exc.InvalidInput(error_message=msg)
453 460
                 fixed_ip_set.append({'subnet_id': subnet_id,
454 461
                                      'ip_address': fixed['ip_address']})
455 462
             else:
@@ -459,7 +466,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
459 466
             raise n_exc.InvalidInput(error_message=msg)
460 467
         return fixed_ip_set
461 468
 
462
-    def _allocate_fixed_ips(self, context, fixed_ips):
469
+    def _allocate_fixed_ips(self, context, fixed_ips, mac_address):
463 470
         """Allocate IP addresses according to the configured fixed_ips."""
464 471
         ips = []
465 472
         for fixed in fixed_ips:
@@ -472,15 +479,24 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
472 479
             # Only subnet ID is specified => need to generate IP
473 480
             # from subnet
474 481
             else:
475
-                subnets = [self._get_subnet(context, fixed['subnet_id'])]
476
-                # IP address allocation
477
-                result = self._generate_ip(context, subnets)
478
-                ips.append({'ip_address': result['ip_address'],
479
-                            'subnet_id': result['subnet_id']})
482
+                subnet = self._get_subnet(context, fixed['subnet_id'])
483
+                if (subnet['ip_version'] == 6 and
484
+                        self._check_if_subnet_uses_eui64(subnet)):
485
+                    prefix = subnet['cidr']
486
+                    ip_address = ipv6_utils.get_ipv6_addr_by_EUI64(
487
+                        prefix, mac_address)
488
+                    ips.append({'ip_address': ip_address.format(),
489
+                                'subnet_id': subnet['id']})
490
+                else:
491
+                    subnets = [subnet]
492
+                    # IP address allocation
493
+                    result = self._generate_ip(context, subnets)
494
+                    ips.append({'ip_address': result['ip_address'],
495
+                                'subnet_id': result['subnet_id']})
480 496
         return ips
481 497
 
482 498
     def _update_ips_for_port(self, context, network_id, port_id, original_ips,
483
-                             new_ips):
499
+                             new_ips, mac_address):
484 500
         """Add or remove IPs from the port."""
485 501
         ips = []
486 502
         # These ips are still on the port and haven't been removed
@@ -511,7 +527,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
511 527
 
512 528
         if to_add:
513 529
             LOG.debug(_("Port update. Adding %s"), to_add)
514
-            ips = self._allocate_fixed_ips(context, to_add)
530
+            ips = self._allocate_fixed_ips(context, to_add, mac_address)
515 531
         return ips, prev_ips
516 532
 
517 533
     def _allocate_ips_for_port(self, context, port):
@@ -529,7 +545,9 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
529 545
             configured_ips = self._test_fixed_ips_for_port(context,
530 546
                                                            p["network_id"],
531 547
                                                            p['fixed_ips'])
532
-            ips = self._allocate_fixed_ips(context, configured_ips)
548
+            ips = self._allocate_fixed_ips(context,
549
+                                           configured_ips,
550
+                                           p['mac_address'])
533 551
         else:
534 552
             filter = {'network_id': [p['network_id']]}
535 553
             subnets = self.get_subnets(context, filters=filter)
@@ -548,10 +566,9 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
548 566
                     # subnet from the array of subnets that will be passed
549 567
                     # to the _generate_ip() function call, since we just
550 568
                     # generated an IP.
551
-                    mac = p['mac_address']
552 569
                     prefix = subnet['cidr']
553 570
                     ip_address = ipv6_utils.get_ipv6_addr_by_EUI64(
554
-                        prefix, mac)
571
+                        prefix, p['mac_address'])
555 572
                     if not self._check_unique_ip(
556 573
                         context, p['network_id'],
557 574
                         subnet['id'], ip_address.format()):
@@ -740,24 +757,32 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
740 757
             raise n_exc.InvalidSharedSetting(network=original.name)
741 758
 
742 759
     def _validate_ipv6_attributes(self, subnet, cur_subnet):
760
+        if cur_subnet:
761
+            self._validate_ipv6_update_dhcp(subnet, cur_subnet)
762
+            return
743 763
         ra_mode_set = attributes.is_attr_set(subnet.get('ipv6_ra_mode'))
744 764
         address_mode_set = attributes.is_attr_set(
745 765
             subnet.get('ipv6_address_mode'))
746
-        if cur_subnet:
747
-            ra_mode = (subnet['ipv6_ra_mode'] if ra_mode_set
748
-                       else cur_subnet['ipv6_ra_mode'])
749
-            addr_mode = (subnet['ipv6_address_mode'] if address_mode_set
750
-                         else cur_subnet['ipv6_address_mode'])
751
-            if ra_mode_set or address_mode_set:
752
-                # Check that updated subnet ipv6 attributes do not conflict
753
-                self._validate_ipv6_combination(ra_mode, addr_mode)
754
-            self._validate_ipv6_update_dhcp(subnet, cur_subnet)
755
-        else:
756
-            self._validate_ipv6_dhcp(ra_mode_set, address_mode_set,
757
-                                     subnet['enable_dhcp'])
758
-            if ra_mode_set and address_mode_set:
759
-                self._validate_ipv6_combination(subnet['ipv6_ra_mode'],
760
-                                                subnet['ipv6_address_mode'])
766
+        self._validate_ipv6_dhcp(ra_mode_set, address_mode_set,
767
+                                 subnet['enable_dhcp'])
768
+        if ra_mode_set and address_mode_set:
769
+            self._validate_ipv6_combination(subnet['ipv6_ra_mode'],
770
+                                            subnet['ipv6_address_mode'])
771
+        if address_mode_set:
772
+            self._validate_eui64_applicable(subnet)
773
+
774
+    def _validate_eui64_applicable(self, subnet):
775
+        # Per RFC 4862, section 5.5.3, prefix length and interface
776
+        # id together should be equal to 128. Currently neutron supports
777
+        # EUI64 interface id only, thus limiting the prefix
778
+        # length to be 64 only.
779
+        if self._check_if_subnet_uses_eui64(subnet):
780
+            if netaddr.IPNetwork(subnet['cidr']).prefixlen != 64:
781
+                msg = _('Invalid CIDR %s for IPv6 address mode. '
782
+                        'OpenStack uses the EUI-64 address format, '
783
+                        'which requires the prefix to be /64.')
784
+                raise n_exc.InvalidInput(
785
+                    error_message=(msg % subnet['cidr']))
761 786
 
762 787
     def _validate_ipv6_combination(self, ra_mode, address_mode):
763 788
         if ra_mode != address_mode:
@@ -1369,8 +1394,9 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
1369 1394
                 changed_ips = True
1370 1395
                 original = self._make_port_dict(port, process_extensions=False)
1371 1396
                 added_ips, prev_ips = self._update_ips_for_port(
1372
-                    context, port["network_id"], id, original["fixed_ips"],
1373
-                    p['fixed_ips'])
1397
+                    context, port["network_id"], id,
1398
+                    original["fixed_ips"], p['fixed_ips'],
1399
+                    original['mac_address'])
1374 1400
 
1375 1401
                 # Update ips if necessary
1376 1402
                 for ip in added_ips:

+ 0
- 14
neutron/db/firewall/__init__.py View File

@@ -1,14 +0,0 @@
1
-# Copyright 2013 OpenStack Foundation
2
-# All Rights Reserved.
3
-#
4
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
-#    not use this file except in compliance with the License. You may obtain
6
-#    a copy of the License at
7
-#
8
-#         http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-#    Unless required by applicable law or agreed to in writing, software
11
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
-#    License for the specific language governing permissions and limitations
14
-#    under the License.

+ 50
- 29
neutron/db/firewall/firewall_db.py View File

@@ -162,6 +162,13 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
162 162
                'enabled': firewall_rule['enabled']}
163 163
         return self._fields(res, fields)
164 164
 
165
+    def _check_firewall_rule_conflict(self, fwr_db, fwp_db):
166
+        if not fwr_db['shared']:
167
+            if fwr_db['tenant_id'] != fwp_db['tenant_id']:
168
+                raise firewall.FirewallRuleConflict(
169
+                    firewall_rule_id=fwr_db['id'],
170
+                    tenant_id=fwr_db['tenant_id'])
171
+
165 172
     def _set_rules_for_policy(self, context, firewall_policy_db, fwp):
166 173
         rule_id_list = fwp['firewall_rules']
167 174
         fwp_db = firewall_policy_db
@@ -180,8 +187,8 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
180 187
                     # If we find an invalid rule in the list we
181 188
                     # do not perform the update since this breaks
182 189
                     # the integrity of this list.
183
-                    raise firewall.FirewallRuleNotFound(firewall_rule_id=
184
-                                                        fwrule_id)
190
+                    raise firewall.FirewallRuleNotFound(
191
+                        firewall_rule_id=fwrule_id)
185 192
                 elif rules_dict[fwrule_id]['firewall_policy_id']:
186 193
                     if (rules_dict[fwrule_id]['firewall_policy_id'] !=
187 194
                             fwp_db['id']):
@@ -196,6 +203,8 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
196 203
                     raise firewall.FirewallRuleSharingConflict(
197 204
                         firewall_rule_id=fwrule_id,
198 205
                         firewall_policy_id=fwp_db['id'])
206
+            for fwr_db in rules_in_db:
207
+                self._check_firewall_rule_conflict(fwr_db, fwp_db)
199 208
             # New list of rules is valid so we will first reset the existing
200 209
             # list and then add each rule in order.
201 210
             # Note that the list could be empty in which case we interpret
@@ -264,14 +273,14 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
264 273
         status = (const.CREATED
265 274
             if cfg.CONF.router_distributed else const.PENDING_CREATE)
266 275
         with context.session.begin(subtransactions=True):
267
-            firewall_db = Firewall(id=uuidutils.generate_uuid(),
268
-                                   tenant_id=tenant_id,
269
-                                   name=fw['name'],
270
-                                   description=fw['description'],
271
-                                   firewall_policy_id=
272
-                                   fw['firewall_policy_id'],
273
-                                   admin_state_up=fw['admin_state_up'],
274
-                                   status=status)
276
+            firewall_db = Firewall(
277
+                id=uuidutils.generate_uuid(),
278
+                tenant_id=tenant_id,
279
+                name=fw['name'],
280
+                description=fw['description'],
281
+                firewall_policy_id=fw['firewall_policy_id'],
282
+                admin_state_up=fw['admin_state_up'],
283
+                status=status)
275 284
             context.session.add(firewall_db)
276 285
         return self._make_firewall_dict(firewall_db)
277 286
 
@@ -329,6 +338,12 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
329 338
         fwp = firewall_policy['firewall_policy']
330 339
         with context.session.begin(subtransactions=True):
331 340
             fwp_db = self._get_firewall_policy(context, id)
341
+            # check tenant ids are same for fw and fwp or not
342
+            if not fwp.get('shared', True) and fwp_db.firewalls:
343
+                for fw in fwp_db['firewalls']:
344
+                    if fwp_db['tenant_id'] != fw['tenant_id']:
345
+                        raise firewall.FirewallPolicyInUse(
346
+                            firewall_policy_id=id)
332 347
             # check any existing rules are not shared
333 348
             if 'shared' in fwp and 'firewall_rules' not in fwp:
334 349
                 self._check_unshared_rules_for_policy(fwp_db, fwp)
@@ -381,28 +396,35 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
381 396
         dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
382 397
             fwr['destination_port'])
383 398
         with context.session.begin(subtransactions=True):
384
-            fwr_db = FirewallRule(id=uuidutils.generate_uuid(),
385
-                                  tenant_id=tenant_id,
386
-                                  name=fwr['name'],
387
-                                  description=fwr['description'],
388
-                                  shared=fwr['shared'],
389
-                                  protocol=fwr['protocol'],
390
-                                  ip_version=fwr['ip_version'],
391
-                                  source_ip_address=fwr['source_ip_address'],
392
-                                  destination_ip_address=
393
-                                  fwr['destination_ip_address'],
394
-                                  source_port_range_min=src_port_min,
395
-                                  source_port_range_max=src_port_max,
396
-                                  destination_port_range_min=dst_port_min,
397
-                                  destination_port_range_max=dst_port_max,
398
-                                  action=fwr['action'],
399
-                                  enabled=fwr['enabled'])
399
+            fwr_db = FirewallRule(
400
+                id=uuidutils.generate_uuid(),
401
+                tenant_id=tenant_id,
402
+                name=fwr['name'],
403
+                description=fwr['description'],
404
+                shared=fwr['shared'],
405
+                protocol=fwr['protocol'],
406
+                ip_version=fwr['ip_version'],
407
+                source_ip_address=fwr['source_ip_address'],
408
+                destination_ip_address=fwr['destination_ip_address'],
409
+                source_port_range_min=src_port_min,
410
+                source_port_range_max=src_port_max,
411
+                destination_port_range_min=dst_port_min,
412
+                destination_port_range_max=dst_port_max,
413
+                action=fwr['action'],
414
+                enabled=fwr['enabled'])
400 415
             context.session.add(fwr_db)
401 416
         return self._make_firewall_rule_dict(fwr_db)
402 417
 
403 418
     def update_firewall_rule(self, context, id, firewall_rule):
404 419
         LOG.debug(_("update_firewall_rule() called"))
405 420
         fwr = firewall_rule['firewall_rule']
421
+        fwr_db = self._get_firewall_rule(context, id)
422
+        if fwr_db.firewall_policy_id:
423
+            fwp_db = self._get_firewall_policy(context,
424
+                                               fwr_db.firewall_policy_id)
425
+            if 'shared' in fwr and not fwr['shared']:
426
+                if fwr_db['tenant_id'] != fwp_db['tenant_id']:
427
+                    raise firewall.FirewallRuleInUse(firewall_rule_id=id)
406 428
         if 'source_port' in fwr:
407 429
             src_port_min, src_port_max = self._get_min_max_ports_from_range(
408 430
                 fwr['source_port'])
@@ -416,7 +438,6 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
416 438
             fwr['destination_port_range_max'] = dst_port_max
417 439
             del fwr['destination_port']
418 440
         with context.session.begin(subtransactions=True):
419
-            fwr_db = self._get_firewall_rule(context, id)
420 441
             protocol = fwr.get('protocol', fwr_db['protocol'])
421 442
             if not protocol:
422 443
                 sport = fwr.get('source_port_range_min',
@@ -427,8 +448,6 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
427 448
                     raise firewall.FirewallRuleWithPortWithoutProtocolInvalid()
428 449
             fwr_db.update(fwr)
429 450
             if fwr_db.firewall_policy_id:
430
-                fwp_db = self._get_firewall_policy(context,
431
-                                                   fwr_db.firewall_policy_id)
432 451
                 fwp_db.audited = False
433 452
         return self._make_firewall_rule_dict(fwr_db)
434 453
 
@@ -476,8 +495,10 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
476 495
             insert_before = False
477 496
         with context.session.begin(subtransactions=True):
478 497
             fwr_db = self._get_firewall_rule(context, firewall_rule_id)
498
+            fwp_db = self._get_firewall_policy(context, id)
479 499
             if fwr_db.firewall_policy_id:
480 500
                 raise firewall.FirewallRuleInUse(firewall_rule_id=fwr_db['id'])
501
+            self._check_firewall_rule_conflict(fwr_db, fwp_db)
481 502
             if ref_firewall_rule_id:
482 503
                 # If reference_firewall_rule_id is set, the new rule
483 504
                 # is inserted depending on the value of insert_before.

+ 24
- 10
neutron/db/l3_agentschedulers_db.py View File

@@ -26,6 +26,7 @@ from sqlalchemy.orm import joinedload
26 26
 from sqlalchemy import sql
27 27
 
28 28
 from neutron.common import constants
29
+from neutron.common import rpc as n_rpc
29 30
 from neutron.common import utils as n_utils
30 31
 from neutron import context as n_ctx
31 32
 from neutron.db import agents_db
@@ -34,7 +35,7 @@ from neutron.db import l3_attrs_db
34 35
 from neutron.db import model_base
35 36
 from neutron.extensions import l3agentscheduler
36 37
 from neutron import manager
37
-from neutron.openstack.common.gettextutils import _LI, _LW
38
+from neutron.openstack.common.gettextutils import _LE, _LI, _LW
38 39
 from neutron.openstack.common import log as logging
39 40
 from neutron.openstack.common import loopingcall
40 41
 from neutron.openstack.common import timeutils
@@ -122,15 +123,28 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase,
122 123
                       RouterL3AgentBinding.router_id).
123 124
             filter(sa.or_(l3_attrs_db.RouterExtraAttributes.ha == sql.false(),
124 125
                           l3_attrs_db.RouterExtraAttributes.ha == sql.null())))
125
-
126
-        for binding in down_bindings:
127
-            LOG.warn(_LW("Rescheduling router %(router)s from agent %(agent)s "
128
-                         "because the agent did not report to the server in "
129
-                         "the last %(dead_time)s seconds."),
130
-                     {'router': binding.router_id,
131
-                      'agent': binding.l3_agent_id,
132
-                      'dead_time': agent_dead_limit})
133
-            self.reschedule_router(context, binding.router_id)
126
+        try:
127
+            for binding in down_bindings:
128
+                LOG.warn(_LW(
129
+                    "Rescheduling router %(router)s from agent %(agent)s "
130
+                    "because the agent did not report to the server in "
131
+                    "the last %(dead_time)s seconds."),
132
+                    {'router': binding.router_id,
133
+                     'agent': binding.l3_agent_id,
134
+                     'dead_time': agent_dead_limit})
135
+                try:
136
+                    self.reschedule_router(context, binding.router_id)
137
+                except (l3agentscheduler.RouterReschedulingFailed,
138
+                        n_rpc.RemoteError):
139
+                    # Catch individual router rescheduling errors here
140
+                    # so one broken one doesn't stop the iteration.
141
+                    LOG.exception(_LE("Failed to reschedule router %s"),
142
+                                  binding.router_id)
143
+        except db_exc.DBError:
144
+            # Catch DB errors here so a transient DB connectivity issue
145
+            # doesn't stop the loopingcall.
146
+            LOG.exception(_LE("Exception encountered during router "
147
+                              "rescheduling."))
134 148
 
135 149
     def validate_agent_router_combination(self, context, agent, router):
136 150
         """Validate if the router can be correctly assigned to the agent.

+ 121
- 66
neutron/db/l3_db.py View File

@@ -47,6 +47,26 @@ API_TO_DB_COLUMN_MAP = {'port_id': 'fixed_port_id'}
47 47
 CORE_ROUTER_ATTRS = ('id', 'name', 'tenant_id', 'admin_state_up', 'status')
48 48
 
49 49
 
50
+class RouterPort(model_base.BASEV2):
51
+    router_id = sa.Column(
52
+        sa.String(36),
53
+        sa.ForeignKey('routers.id', ondelete="CASCADE"),
54
+        primary_key=True)
55
+    port_id = sa.Column(
56
+        sa.String(36),
57
+        sa.ForeignKey('ports.id', ondelete="CASCADE"),
58
+        primary_key=True)
59
+    # The port_type attribute is redundant as the port table already specifies
60
+    # it in DEVICE_OWNER.However, this redundancy enables more efficient
61
+    # queries on router ports, and also prevents potential error-prone
62
+    # conditions which might originate from users altering the DEVICE_OWNER
63
+    # property of router ports.
64
+    port_type = sa.Column(sa.String(255))
65
+    port = orm.relationship(
66
+        models_v2.Port,
67
+        backref=orm.backref('routerport', uselist=False, cascade="all,delete"))
68
+
69
+
50 70
 class Router(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
51 71
     """Represents a v2 neutron router."""
52 72
 
@@ -55,6 +75,10 @@ class Router(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
55 75
     admin_state_up = sa.Column(sa.Boolean)
56 76
     gw_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
57 77
     gw_port = orm.relationship(models_v2.Port, lazy='joined')
78
+    attached_ports = orm.relationship(
79
+        RouterPort,
80
+        backref='router',
81
+        lazy='dynamic')
58 82
 
59 83
 
60 84
 class FloatingIP(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
@@ -76,6 +100,7 @@ class FloatingIP(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
76 100
     # aysnchronous backend is unavailable when the floating IP is disassociated
77 101
     last_known_router_id = sa.Column(sa.String(36))
78 102
     status = sa.Column(sa.String(16))
103
+    router = orm.relationship(Router, backref='floating_ips')
79 104
 
80 105
 
81 106
 class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
@@ -101,7 +126,11 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
101 126
     def _make_router_dict(self, router, fields=None, process_extensions=True):
102 127
         res = dict((key, router[key]) for key in CORE_ROUTER_ATTRS)
103 128
         if router['gw_port_id']:
104
-            ext_gw_info = {'network_id': router.gw_port['network_id']}
129
+            ext_gw_info = {
130
+                'network_id': router.gw_port['network_id'],
131
+                'external_fixed_ips': [{'subnet_id': ip["subnet_id"],
132
+                                        'ip_address': ip["ip_address"]}
133
+                                       for ip in router.gw_port['fixed_ips']]}
105 134
         else:
106 135
             ext_gw_info = None
107 136
         res.update({
@@ -255,7 +284,13 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
255 284
         with context.session.begin(subtransactions=True):
256 285
             router.gw_port = self._core_plugin._get_port(context.elevated(),
257 286
                                                          gw_port['id'])
287
+            router_port = RouterPort(
288
+                router_id=router.id,
289
+                port_id=gw_port['id'],
290
+                port_type=DEVICE_OWNER_ROUTER_GW
291
+            )
258 292
             context.session.add(router)
293
+            context.session.add(router_port)
259 294
 
260 295
     def _validate_gw_info(self, context, gw_port, info):
261 296
         network_id = info['network_id'] if info else None
@@ -277,11 +312,16 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
277 312
                 raise l3.RouterExternalGatewayInUseByFloatingIp(
278 313
                     router_id=router_id, net_id=router.gw_port['network_id'])
279 314
             with context.session.begin(subtransactions=True):
280
-                gw_port_id = router.gw_port['id']
315
+                gw_port = router.gw_port
281 316
                 router.gw_port = None
282 317
                 context.session.add(router)
318
+                context.session.expire(gw_port)
319
+            vpnservice = manager.NeutronManager.get_service_plugins().get(
320
+                constants.VPN)
321
+            if vpnservice:
322
+                vpnservice.check_router_in_use(context, router_id)
283 323
             self._core_plugin.delete_port(
284
-                admin_ctx, gw_port_id, l3_port_check=False)
324
+                admin_ctx, gw_port['id'], l3_port_check=False)
285 325
 
286 326
     def _create_gw_port(self, context, router_id, router, new_network):
287 327
         new_valid_gw_port_attachment = (
@@ -291,7 +331,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):