summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2017-06-17 06:20:10 +0000
committerGerrit Code Review <review@openstack.org>2017-06-17 06:20:10 +0000
commite82d0fe5c06ef6460069aca4710efcb709ad8ba8 (patch)
tree43f0e9e555ea5eca3f3ed66fbac6bf981dced5b9
parent7102def745beb11148e754e58126c4eb25201e12 (diff)
parent754e0e756103da9533951a6615e92104920db6bb (diff)
Merge "NSX|V3: FWaaS-v1 support"
-rw-r--r--doc/source/devstack.rst17
-rw-r--r--setup.cfg1
-rw-r--r--vmware_nsx/plugins/nsx_v3/plugin.py17
-rw-r--r--vmware_nsx/services/fwaas/common/__init__.py0
-rw-r--r--vmware_nsx/services/fwaas/common/fwaas_callbacks.py82
-rw-r--r--vmware_nsx/services/fwaas/nsx_v/fwaas_callbacks.py61
-rw-r--r--vmware_nsx/services/fwaas/nsx_v3/__init__.py0
-rw-r--r--vmware_nsx/services/fwaas/nsx_v3/edge_fwaas_driver.py284
-rw-r--r--vmware_nsx/services/fwaas/nsx_v3/fwaas_callbacks.py60
-rw-r--r--vmware_nsx/tests/unit/nsx_v3/test_fwaas_driver.py200
-rw-r--r--vmware_nsx/tests/unit/nsx_v3/test_plugin.py10
11 files changed, 674 insertions, 58 deletions
diff --git a/doc/source/devstack.rst b/doc/source/devstack.rst
index 68d962d..5dd435a 100644
--- a/doc/source/devstack.rst
+++ b/doc/source/devstack.rst
@@ -171,3 +171,20 @@ Enable trunk service and configure following flags in ``local.conf``::
171 # Trunk plugin NSXv3 driver config 171 # Trunk plugin NSXv3 driver config
172 ENABLED_SERVICES+=,q-trunk 172 ENABLED_SERVICES+=,q-trunk
173 Q_SERVICE_PLUGIN_CLASSES=trunk 173 Q_SERVICE_PLUGIN_CLASSES=trunk
174
175FWAAS (V1) Driver:
176~~~~~~~~~~~~~
177
178Add neutron-fwaas repo as an external repository and configure following flags in ``local.conf``::
179
180 [[local|localrc]]
181 enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas
182 ENABLED_SERVICES+=,q-fwaas
183
184 [[post-config|$NEUTRON_CONF]]
185 [DEFAULT]
186 service_plugins = neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin
187
188 [fwaas]
189 enabled = True
190 driver = vmware_nsxv3_edge
diff --git a/setup.cfg b/setup.cfg
index 52a87fc..c94dbaf 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -38,6 +38,7 @@ neutron.core_plugins =
38 vmware_dvs = vmware_nsx.plugin:NsxDvsPlugin 38 vmware_dvs = vmware_nsx.plugin:NsxDvsPlugin
39firewall_drivers = 39firewall_drivers =
40 vmware_nsxv_edge = vmware_nsx.services.fwaas.nsx_v.edge_fwaas_driver:EdgeFwaasDriver 40 vmware_nsxv_edge = vmware_nsx.services.fwaas.nsx_v.edge_fwaas_driver:EdgeFwaasDriver
41 vmware_nsxv3_edge = vmware_nsx.services.fwaas.nsx_v3.edge_fwaas_driver:EdgeFwaasV3Driver
41neutron.service_plugins = 42neutron.service_plugins =
42 vmware_nsxv_qos = vmware_nsx.services.qos.nsx_v.plugin:NsxVQosPlugin 43 vmware_nsxv_qos = vmware_nsx.services.qos.nsx_v.plugin:NsxVQosPlugin
43neutron.qos.notification_drivers = 44neutron.qos.notification_drivers =
diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py
index f9823b3..11e02e9 100644
--- a/vmware_nsx/plugins/nsx_v3/plugin.py
+++ b/vmware_nsx/plugins/nsx_v3/plugin.py
@@ -97,6 +97,7 @@ from vmware_nsx.extensions import providersecuritygroup as provider_sg
97from vmware_nsx.extensions import securitygrouplogging as sg_logging 97from vmware_nsx.extensions import securitygrouplogging as sg_logging
98from vmware_nsx.plugins.nsx_v3 import availability_zones as nsx_az 98from vmware_nsx.plugins.nsx_v3 import availability_zones as nsx_az
99from vmware_nsx.plugins.nsx_v3 import utils as v3_utils 99from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
100from vmware_nsx.services.fwaas.nsx_v3 import fwaas_callbacks
100from vmware_nsx.services.qos.common import utils as qos_com_utils 101from vmware_nsx.services.qos.common import utils as qos_com_utils
101from vmware_nsx.services.qos.nsx_v3 import driver as qos_driver 102from vmware_nsx.services.qos.nsx_v3 import driver as qos_driver
102from vmware_nsx.services.trunk.nsx_v3 import driver as trunk_driver 103from vmware_nsx.services.trunk.nsx_v3 import driver as trunk_driver
@@ -227,6 +228,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
227 # init profiles on nsx backend 228 # init profiles on nsx backend
228 self._init_nsx_profiles() 229 self._init_nsx_profiles()
229 230
231 # Init the FWaaS support
232 self._init_fwaas()
233
230 # Include exclude NSGroup 234 # Include exclude NSGroup
231 LOG.debug("Initializing NSX v3 Excluded Port NSGroup") 235 LOG.debug("Initializing NSX v3 Excluded Port NSGroup")
232 self._excluded_port_nsgroup = None 236 self._excluded_port_nsgroup = None
@@ -247,6 +251,10 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
247 # Register NSXv3 trunk driver to support trunk extensions 251 # Register NSXv3 trunk driver to support trunk extensions
248 self.trunk_driver = trunk_driver.NsxV3TrunkDriver.create(self) 252 self.trunk_driver = trunk_driver.NsxV3TrunkDriver.create(self)
249 253
254 def _init_fwaas(self):
255 # Bind FWaaS callbacks to the driver
256 self.fwaas_callbacks = fwaas_callbacks.Nsxv3FwaasCallbacks(self.nsxlib)
257
250 def init_availability_zones(self): 258 def init_availability_zones(self):
251 # availability zones are supported only with native dhcp 259 # availability zones are supported only with native dhcp
252 # if not - the default az will be loaded and used internally only 260 # if not - the default az will be loaded and used internally only
@@ -2776,7 +2784,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
2776 self._routerlib.add_router_link_port(nsx_router_id, new_tier0_uuid, 2784 self._routerlib.add_router_link_port(nsx_router_id, new_tier0_uuid,
2777 tags=tags) 2785 tags=tags)
2778 if add_snat_rules: 2786 if add_snat_rules:
2779 self._routerlib.add_gw_snat_rule(nsx_router_id, newaddr) 2787 self._routerlib.add_gw_snat_rule(nsx_router_id, newaddr,
2788 bypass_firewall=False)
2780 if bgp_announce: 2789 if bgp_announce:
2781 # TODO(berlin): bgp announce on new tier0 router 2790 # TODO(berlin): bgp announce on new tier0 router
2782 pass 2791 pass
@@ -3230,7 +3239,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
3230 router_id) 3239 router_id)
3231 self._routerlib.add_fip_nat_rules( 3240 self._routerlib.add_fip_nat_rules(
3232 nsx_router_id, new_fip['floating_ip_address'], 3241 nsx_router_id, new_fip['floating_ip_address'],
3233 new_fip['fixed_ip_address']) 3242 new_fip['fixed_ip_address'],
3243 bypass_firewall=False)
3234 except nsx_lib_exc.ManagerError: 3244 except nsx_lib_exc.ManagerError:
3235 with excutils.save_and_reraise_exception(): 3245 with excutils.save_and_reraise_exception():
3236 self.delete_floatingip(context, new_fip['id']) 3246 self.delete_floatingip(context, new_fip['id'])
@@ -3304,7 +3314,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
3304 router_id) 3314 router_id)
3305 self._routerlib.add_fip_nat_rules( 3315 self._routerlib.add_fip_nat_rules(
3306 nsx_router_id, new_fip['floating_ip_address'], 3316 nsx_router_id, new_fip['floating_ip_address'],
3307 new_fip['fixed_ip_address']) 3317 new_fip['fixed_ip_address'],
3318 bypass_firewall=False)
3308 except nsx_lib_exc.ManagerError: 3319 except nsx_lib_exc.ManagerError:
3309 with excutils.save_and_reraise_exception(): 3320 with excutils.save_and_reraise_exception():
3310 super(NsxV3Plugin, self).update_floatingip( 3321 super(NsxV3Plugin, self).update_floatingip(
diff --git a/vmware_nsx/services/fwaas/common/__init__.py b/vmware_nsx/services/fwaas/common/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vmware_nsx/services/fwaas/common/__init__.py
diff --git a/vmware_nsx/services/fwaas/common/fwaas_callbacks.py b/vmware_nsx/services/fwaas/common/fwaas_callbacks.py
new file mode 100644
index 0000000..5d357ce
--- /dev/null
+++ b/vmware_nsx/services/fwaas/common/fwaas_callbacks.py
@@ -0,0 +1,82 @@
1# Copyright 2017 VMware, Inc.
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.
15
16from oslo_config import cfg
17from oslo_log import log as logging
18
19from neutron.agent.l3 import router_info
20from neutron.common import config as neutron_config # noqa
21from neutron_fwaas.services.firewall.agents.l3reference \
22 import firewall_l3_agent
23from neutron_lib import context as n_context
24from neutron_lib.plugins import directory
25
26LOG = logging.getLogger(__name__)
27
28
29class NsxFwaasCallbacks(firewall_l3_agent.L3WithFWaaS):
30 """Common NSX RPC callbacks for Firewall As A Service - V1."""
31 def __init__(self):
32 # The super code needs a configuration object with the neutron host
33 # and an agent_mode, which our driver doesn't use.
34 neutron_conf = cfg.CONF
35 neutron_conf.agent_mode = 'nsx'
36 super(NsxFwaasCallbacks, self).__init__(conf=neutron_conf)
37
38 @property
39 def core_plugin(self):
40 return directory.get_plugin()
41
42 # Override functions using the agent_api that is not used by our plugin
43 def _get_router_ids_for_fw(self, context, fw, to_delete=False):
44 """Return the router_ids either from fw dict or tenant routers."""
45 if self._has_router_insertion_fields(fw):
46 # it is a new version of plugin
47 return (fw['del-router-ids'] if to_delete
48 else fw['add-router-ids'])
49 else:
50 return [router['id'] for router in
51 self._get_routers_in_project(context, fw['tenant_id'])]
52
53 def _get_routers_in_project(self, context, project_id):
54 return self.core_plugin.get_routers(
55 context,
56 filters={'project_id': [project_id]})
57
58 def _router_dict_to_obj(self, r):
59 # The callbacks expect a router-info object
60 return router_info.RouterInfo(
61 None, r['id'], router=r,
62 agent_conf=None,
63 interface_driver=None,
64 use_ipv6=False)
65
66 def _get_router_info_list_for_tenant(self, router_ids, tenant_id):
67 """Returns the list of router info objects on which to apply the fw."""
68 context = n_context.get_admin_context()
69 tenant_routers = self._get_routers_in_project(context, tenant_id)
70 return [self._router_dict_to_obj(ri) for ri in tenant_routers
71 if ri['id'] in router_ids]
72
73 def should_apply_firewall_to_router(self, context, router_id):
74 """Return True if the FWaaS rules should be added to this router."""
75 if not self.fwaas_enabled:
76 return False
77
78 if not self._get_router_firewall_id(context.elevated(), router_id):
79 # No FWaas Firewall was assigned to this router
80 return False
81
82 return True
diff --git a/vmware_nsx/services/fwaas/nsx_v/fwaas_callbacks.py b/vmware_nsx/services/fwaas/nsx_v/fwaas_callbacks.py
index 00dbaeb..98455b8 100644
--- a/vmware_nsx/services/fwaas/nsx_v/fwaas_callbacks.py
+++ b/vmware_nsx/services/fwaas/nsx_v/fwaas_callbacks.py
@@ -13,78 +13,29 @@
13# License for the specific language governing permissions and limitations 13# License for the specific language governing permissions and limitations
14# under the License. 14# under the License.
15 15
16from oslo_config import cfg
17from oslo_log import log as logging 16from oslo_log import log as logging
18 17
19from neutron.agent.l3 import router_info
20from neutron.common import config as neutron_config # noqa
21from neutron_fwaas.db.firewall import firewall_db # noqa 18from neutron_fwaas.db.firewall import firewall_db # noqa
22from neutron_fwaas.db.firewall import firewall_router_insertion_db \ 19from neutron_fwaas.db.firewall import firewall_router_insertion_db \
23 as fw_r_ins_db 20 as fw_r_ins_db
24from neutron_fwaas.services.firewall.agents.l3reference \ 21
25 import firewall_l3_agent 22from vmware_nsx.services.fwaas.common import fwaas_callbacks as com_callbacks
26from neutron_lib import context as n_context
27from neutron_lib.plugins import directory
28 23
29LOG = logging.getLogger(__name__) 24LOG = logging.getLogger(__name__)
30 25
31 26
32class NsxvFwaasCallbacks(firewall_l3_agent.L3WithFWaaS): 27class NsxvFwaasCallbacks(com_callbacks.NsxFwaasCallbacks):
33 """NSX-V RPC callbacks for Firewall As A Service - V1.""" 28 """NSX-V RPC callbacks for Firewall As A Service - V1."""
34 def __init__(self):
35 # The super code needs a configuration object with the neutron host
36 # and an agent_mode, hich our driver doesn't use.
37 neutron_conf = cfg.CONF
38 neutron_conf.agent_mode = 'nsx'
39 super(NsxvFwaasCallbacks, self).__init__(conf=neutron_conf)
40
41 @property
42 def core_plugin(self):
43 return directory.get_plugin()
44
45 # Override functions using the agent_api that is not used by our plugin
46 def _get_router_ids_for_fw(self, context, fw, to_delete=False):
47 """Return the router_ids either from fw dict or tenant routers."""
48 if self._has_router_insertion_fields(fw):
49 # it is a new version of plugin
50 return (fw['del-router-ids'] if to_delete
51 else fw['add-router-ids'])
52 else:
53 return [router['id'] for router in
54 self._get_routers_in_project(context, fw['tenant_id'])]
55
56 def _get_routers_in_project(self, context, project_id):
57 return self.core_plugin.get_routers(
58 context,
59 filters={'tenant_id': [project_id]})
60
61 def _router_dict_to_obj(self, r):
62 # The callbacks expect a router-info object
63 return router_info.RouterInfo(
64 None, r['id'], router=r,
65 agent_conf=None,
66 interface_driver=None,
67 use_ipv6=False)
68
69 def _get_router_info_list_for_tenant(self, router_ids, tenant_id):
70 """Returns the list of router info objects on which to apply the fw."""
71 context = n_context.get_admin_context()
72 tenant_routers = self._get_routers_in_project(context, tenant_id)
73 return [self._router_dict_to_obj(ri) for ri in tenant_routers
74 if ri['id'] in router_ids]
75 29
76 def should_apply_firewall_to_router(self, context, router, router_id): 30 def should_apply_firewall_to_router(self, context, router, router_id):
77 """Return True if the FWaaS rules should be added to this router.""" 31 """Return True if the FWaaS rules should be added to this router."""
78 if not self.fwaas_enabled: 32 if not super(NsxvFwaasCallbacks, self).should_apply_firewall_to_router(
79 return False 33 context, router_id):
80
81 ctx_elevated = context.elevated()
82 if not self._get_router_firewall_id(ctx_elevated, router_id):
83 # No FWaas Firewall was assigned to this router
84 return False 34 return False
85 35
86 # get all the relevant router info 36 # get all the relevant router info
87 # ("router" does not have all the fields) 37 # ("router" does not have all the fields)
38 ctx_elevated = context.elevated()
88 router_data = self.core_plugin.get_router(ctx_elevated, router['id']) 39 router_data = self.core_plugin.get_router(ctx_elevated, router['id'])
89 if not router_data: 40 if not router_data:
90 LOG.error("Couldn't read router %s data", router['id']) 41 LOG.error("Couldn't read router %s data", router['id'])
diff --git a/vmware_nsx/services/fwaas/nsx_v3/__init__.py b/vmware_nsx/services/fwaas/nsx_v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vmware_nsx/services/fwaas/nsx_v3/__init__.py
diff --git a/vmware_nsx/services/fwaas/nsx_v3/edge_fwaas_driver.py b/vmware_nsx/services/fwaas/nsx_v3/edge_fwaas_driver.py
new file mode 100644
index 0000000..d191fcb
--- /dev/null
+++ b/vmware_nsx/services/fwaas/nsx_v3/edge_fwaas_driver.py
@@ -0,0 +1,284 @@
1# Copyright 2017 VMware, Inc.
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.
15
16import netaddr
17
18from neutron_fwaas.common import exceptions
19from neutron_fwaas.services.firewall.drivers import fwaas_base
20from neutron_lib.api.definitions import constants as fwaas_consts
21from neutron_lib import context as n_context
22from neutron_lib.plugins import directory
23from oslo_log import helpers as log_helpers
24from oslo_log import log as logging
25
26from vmware_nsx.db import db as nsx_db
27from vmware_nsxlib.v3 import nsx_constants as consts
28
29LOG = logging.getLogger(__name__)
30FWAAS_DRIVER_NAME = 'Fwaas NSX-V3 driver'
31RULE_NAME_PREFIX = 'Fwaas-'
32DEFAULT_RULE_NAME = 'Default LR Layer3 Rule'
33
34
35class EdgeFwaasV3Driver(fwaas_base.FwaasDriverBase):
36 """NSX-V3 driver for Firewall As A Service - V1."""
37
38 def __init__(self):
39 LOG.debug("Loading FWaaS NsxV3Driver.")
40 super(EdgeFwaasV3Driver, self).__init__()
41
42 @property
43 def nsxlib(self):
44 return directory.get_plugin().nsxlib
45
46 @property
47 def nsx_firewall(self):
48 return self.nsxlib.firewall_section
49
50 @property
51 def nsx_router(self):
52 return self.nsxlib.logical_router
53
54 def should_apply_firewall_to_router(self, router_data):
55 """Return True if the firewall rules should be added the router
56
57 Right now the driver supports for all routers.
58 """
59 return True
60
61 @staticmethod
62 def _translate_action(fwaas_action, fwaas_rule_id):
63 """Translate FWaaS action to NSX action"""
64 if fwaas_action == fwaas_consts.FWAAS_ALLOW:
65 return consts.FW_ACTION_ALLOW
66 if fwaas_action == fwaas_consts.FWAAS_DENY:
67 return consts.FW_ACTION_DROP
68 if fwaas_action == fwaas_consts.FWAAS_REJECT:
69 # reject is not supported by the nsx router firewall
70 LOG.warning("Reject action is not supported by the NSX backend "
71 "for router firewall. Using %(action)s instead for "
72 "rule %(id)s",
73 {'action': consts.FW_ACTION_DROP,
74 'id': fwaas_rule_id})
75 return consts.FW_ACTION_DROP
76 # Unexpected action
77 LOG.error("Unsupported FWAAS action %(action)s for rule %(id)s", {
78 'action': fwaas_action, 'id': fwaas_rule_id})
79 raise exceptions.FirewallInternalDriverError(
80 driver=FWAAS_DRIVER_NAME)
81
82 def _translate_cidr(self, cidr):
83 return self.nsx_firewall.get_ip_cidr_reference(
84 cidr,
85 consts.IPV6 if netaddr.valid_ipv6(cidr) else consts.IPV4)
86
87 def _translate_addresses(self, cidrs):
88 return [self._translate_cidr(ip) for ip in cidrs]
89
90 @staticmethod
91 def _translate_protocol(fwaas_protocol):
92 """Translate FWaaS L4 protocol to NSX protocol"""
93 if fwaas_protocol.lower() == 'tcp':
94 return consts.TCP
95 if fwaas_protocol.lower() == 'udp':
96 return consts.UDP
97 if fwaas_protocol.lower() == 'icmp':
98 # This will cover icmpv6 too, when adding the rule.
99 return consts.ICMPV4
100
101 def _translate_services(self, fwaas_rule):
102 l4_protocol = self._translate_protocol(fwaas_rule['protocol'])
103 if l4_protocol in [consts.TCP, consts.UDP]:
104 source_ports = []
105 destination_ports = []
106 if fwaas_rule.get('source_port'):
107 source_ports = [fwaas_rule['source_port']]
108 if fwaas_rule.get('destination_port'):
109 destination_ports = [fwaas_rule['destination_port']]
110
111 return [self.nsx_firewall.get_nsservice(
112 consts.L4_PORT_SET_NSSERVICE,
113 l4_protocol=l4_protocol,
114 source_ports=source_ports,
115 destination_ports=destination_ports)]
116 elif l4_protocol == consts.ICMPV4:
117 # Add both icmp v4 & v6 services
118 return [
119 self.nsx_firewall.get_nsservice(
120 consts.ICMP_TYPE_NSSERVICE,
121 protocol=consts.ICMPV4),
122 self.nsx_firewall.get_nsservice(
123 consts.ICMP_TYPE_NSSERVICE,
124 protocol=consts.ICMPV6),
125 ]
126
127 def _translate_rules(self, fwaas_rules):
128 translated_rules = []
129 for rule in fwaas_rules:
130 nsx_rule = {}
131 if not rule['enabled']:
132 # skip disabled rules
133 continue
134 # Make sure the rule has a name, and it starts with the prefix
135 # (backend max name length is 255)
136 if rule.get('name'):
137 name = RULE_NAME_PREFIX + rule['name']
138 else:
139 name = RULE_NAME_PREFIX + rule['id']
140 nsx_rule['display_name'] = name[:255]
141 if rule.get('description'):
142 nsx_rule['notes'] = rule['description']
143 nsx_rule['action'] = self._translate_action(
144 rule['action'], rule['id'])
145 if rule.get('destination_ip_address'):
146 nsx_rule['destinations'] = self._translate_addresses(
147 [rule['destination_ip_address']])
148 if rule.get('source_ip_address'):
149 nsx_rule['sources'] = self._translate_addresses(
150 [rule['source_ip_address']])
151 if rule.get('protocol', 'any') != 'any':
152 nsx_rule['services'] = self._translate_services(rule)
153
154 translated_rules.append(nsx_rule)
155
156 return translated_rules
157
158 def _create_or_update_firewall(self, agent_mode, apply_list, firewall):
159 # admin state down means default block rule firewall
160 if not firewall['admin_state_up']:
161 self.apply_default_policy(agent_mode, apply_list, firewall)
162 return
163
164 context = n_context.get_admin_context()
165 rules = self._translate_rules(firewall['firewall_rule_list'])
166 # update each router using the core plugin code
167 self._update_backend_routers(context, apply_list, rules=rules)
168
169 @log_helpers.log_method_call
170 def create_firewall(self, agent_mode, apply_list, firewall):
171 """Create the Firewall with a given policy. """
172 self._create_or_update_firewall(agent_mode, apply_list, firewall)
173
174 @log_helpers.log_method_call
175 def update_firewall(self, agent_mode, apply_list, firewall):
176 """Remove previous policy and apply the new policy."""
177 self._create_or_update_firewall(agent_mode, apply_list, firewall)
178
179 @log_helpers.log_method_call
180 def delete_firewall(self, agent_mode, apply_list, firewall):
181 """Delete firewall.
182
183 Removes rules created by this instance from the backend firewall
184 And add the default allow rule.
185 """
186 context = n_context.get_admin_context()
187 self._update_backend_routers(context, apply_list, delete_fw=True)
188
189 @log_helpers.log_method_call
190 def apply_default_policy(self, agent_mode, apply_list, firewall):
191 """Apply the default policy (deny all).
192
193 The backend firewall always has this policy (=deny all) as default,
194 so we only need to delete the current rules.
195 """
196 context = n_context.get_admin_context()
197 self._update_backend_routers(context, apply_list, rules=[])
198
199 def _update_backend_routers(self, context, apply_list, rules=None,
200 delete_fw=False):
201 # update each router using the core plugin code
202 for router_info in apply_list:
203
204 # Skip unsupported routers
205 if not self.should_apply_firewall_to_router(router_info.router):
206 continue
207
208 router_id = router_info.router_id
209
210 # update the routers firewall
211 if delete_fw:
212 self._delete_nsx_router_firewall(context, router_id)
213 else:
214 self._update_nsx_router_firewall(context, router_id, rules)
215
216 def _get_backend_router_and_fw_section(self, context, router_id):
217 # find the backend router id in the DB
218 nsx_router_id = nsx_db.get_nsx_router_id(context.session, router_id)
219 if nsx_router_id is None:
220 LOG.error("Didn't find nsx router for router %s", router_id)
221 raise exceptions.FirewallInternalDriverError(
222 driver=FWAAS_DRIVER_NAME)
223
224 # get the FW section id of the backend router
225 try:
226 section_id = self.nsx_router.get_firewall_section_id(
227 nsx_router_id)
228 except Exception as e:
229 LOG.error("Failed to find router firewall section for router "
230 "%(id)s: %(e)s", {'id': router_id, 'e': e})
231 raise exceptions.FirewallInternalDriverError(
232 driver=FWAAS_DRIVER_NAME)
233 if section_id is None:
234 LOG.error("Failed to find router firewall section for router "
235 "%(id)s.", {'id': router_id})
236 raise exceptions.FirewallInternalDriverError(
237 driver=FWAAS_DRIVER_NAME)
238
239 return nsx_router_id, section_id
240
241 def _delete_nsx_router_firewall(self, context, router_id):
242 """Reset the router firewall back to it's default"""
243
244 # find the backend router and its firewall section
245 nsx_router_id, section_id = self._get_backend_router_and_fw_section(
246 context, router_id)
247
248 # Add default allow all rule
249 old_default_rule = self.nsx_firewall.get_default_rule(
250 section_id)
251 allow_all = {
252 'display_name': DEFAULT_RULE_NAME,
253 'action': consts.FW_ACTION_ALLOW,
254 'is_default': True,
255 'id': old_default_rule['id'] if old_default_rule else 0}
256
257 # Update the backend firewall section with the rules
258 self.nsx_firewall.update(section_id, rules=[allow_all])
259
260 def _update_nsx_router_firewall(self, context, router_id, rules):
261 """Update the backend router firewall section
262
263 Adding all relevant north-south rules from the FWaaS firewall
264 and the default drop all rule
265
266 Since those rules do no depend on the router gateway/interfaces/ips
267 there is no need to call this method on each router update.
268 Just when the firewall changes.
269 """
270 # find the backend router and its firewall section
271 nsx_router_id, section_id = self._get_backend_router_and_fw_section(
272 context, router_id)
273
274 # Add default drop all rule at the end
275 old_default_rule = self.nsx_firewall.get_default_rule(
276 section_id)
277 drop_all = {
278 'display_name': DEFAULT_RULE_NAME,
279 'action': consts.FW_ACTION_DROP,
280 'is_default': True,
281 'id': old_default_rule['id'] if old_default_rule else 0}
282
283 # Update the backend firewall section with the rules
284 self.nsx_firewall.update(section_id, rules=rules + [drop_all])
diff --git a/vmware_nsx/services/fwaas/nsx_v3/fwaas_callbacks.py b/vmware_nsx/services/fwaas/nsx_v3/fwaas_callbacks.py
new file mode 100644
index 0000000..d656dab
--- /dev/null
+++ b/vmware_nsx/services/fwaas/nsx_v3/fwaas_callbacks.py
@@ -0,0 +1,60 @@
1# Copyright 2017 VMware, Inc.
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.
15
16from oslo_log import log as logging
17
18from vmware_nsx.common import exceptions as nsx_exc
19from vmware_nsx.services.fwaas.common import fwaas_callbacks as com_callbacks
20from vmware_nsxlib.v3 import nsx_constants as consts
21
22LOG = logging.getLogger(__name__)
23
24
25class Nsxv3FwaasCallbacks(com_callbacks.NsxFwaasCallbacks):
26 """NSX-V3 RPC callbacks for Firewall As A Service - V1."""
27
28 def __init__(self, nsxlib):
29 super(Nsxv3FwaasCallbacks, self).__init__()
30 # Verify that the nsx backend supports FWaaS
31 if self.fwaas_enabled:
32 self.verify_backend_version(nsxlib)
33
34 def verify_backend_version(self, nsxlib):
35 if not nsxlib.feature_supported(consts.FEATURE_ROUTER_FIREWALL):
36 # router firewall is not supported
37 msg = (_("FWaaS is not supported by the NSX backend (version %s): "
38 "Router firewall is not supported") %
39 self.nsxlib.get_version())
40 raise nsx_exc.NsxPluginException(err_msg=msg)
41
42 def should_apply_firewall_to_router(self, context, router_id):
43 """Return True if the FWaaS rules should be added to this router."""
44 if not super(Nsxv3FwaasCallbacks,
45 self).should_apply_firewall_to_router(context,
46 router_id):
47 return False
48
49 # get all the relevant router info
50 ctx_elevated = context.elevated()
51 router_data = self.core_plugin.get_router(ctx_elevated, router_id)
52 if not router_data:
53 LOG.error("Couldn't read router %s data", router_id)
54 return False
55
56 # Check if the FWaaS driver supports this router
57 if not self.fwaas_driver.should_apply_firewall_to_router(router_data):
58 return False
59
60 return True
diff --git a/vmware_nsx/tests/unit/nsx_v3/test_fwaas_driver.py b/vmware_nsx/tests/unit/nsx_v3/test_fwaas_driver.py
new file mode 100644
index 0000000..3597b82
--- /dev/null
+++ b/vmware_nsx/tests/unit/nsx_v3/test_fwaas_driver.py
@@ -0,0 +1,200 @@
1# Copyright 2017 VMware, Inc.
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.
15
16import copy
17import mock
18
19from vmware_nsx.services.fwaas.nsx_v3 import edge_fwaas_driver
20from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_v3_plugin
21from vmware_nsxlib.v3 import nsx_constants as consts
22
23FAKE_FW_ID = 'fake_fw_uuid'
24FAKE_ROUTER_ID = 'fake_rtr_uuid'
25MOCK_NSX_ID = 'nsx_router_id'
26MOCK_DEFAULT_RULE_ID = 'nsx_default_rule_id'
27MOCK_SECTION_ID = 'sec_id'
28DEFAULT_RULE = {'is_default': True,
29 'display_name': edge_fwaas_driver.DEFAULT_RULE_NAME,
30 'id': MOCK_DEFAULT_RULE_ID,
31 'action': consts.FW_ACTION_DROP}
32
33
34class Nsxv3FwaasTestCase(test_v3_plugin.NsxV3PluginTestCaseMixin):
35 def setUp(self):
36 super(Nsxv3FwaasTestCase, self).setUp()
37 self.firewall = edge_fwaas_driver.EdgeFwaasV3Driver()
38
39 # Start some nsxlib/DB mocks
40 mock.patch(
41 "vmware_nsxlib.v3.core_resources.NsxLibLogicalRouter."
42 "get_firewall_section_id",
43 return_value=MOCK_SECTION_ID).start()
44
45 mock.patch(
46 "vmware_nsxlib.v3.security.NsxLibFirewallSection."
47 "get_default_rule",
48 return_value={'id': MOCK_DEFAULT_RULE_ID}).start()
49
50 mock.patch(
51 "vmware_nsx.db.db.get_nsx_router_id",
52 return_value=MOCK_NSX_ID).start()
53
54 def _default_rule(self, drop=True):
55 rule = DEFAULT_RULE
56 if drop:
57 rule['action'] = consts.FW_ACTION_DROP
58 else:
59 rule['action'] = consts.FW_ACTION_ALLOW
60 return rule
61
62 def _fake_rules_v4(self):
63 rule1 = {'enabled': True,
64 'action': 'allow',
65 'ip_version': 4,
66 'protocol': 'tcp',
67 'destination_port': '80',
68 'source_ip_address': '10.24.4.2',
69 'id': 'fake-fw-rule1',
70 'description': 'first rule'}
71 rule2 = {'enabled': True,
72 'action': 'reject',
73 'ip_version': 4,
74 'protocol': 'tcp',
75 'destination_port': '22',
76 'id': 'fake-fw-rule2'}
77 rule3 = {'enabled': True,
78 'action': 'deny',
79 'ip_version': 4,
80 'protocol': 'icmp',
81 'id': 'fake-fw-rule3'}
82 return [rule1, rule2, rule3]
83
84 def _fake_translated_rules(self):
85 # The expected translation of the rules in _fake_rules_v4
86 service1 = {'l4_protocol': 'TCP',
87 'resource_type': 'L4PortSetNSService',
88 'destination_ports': ['80'],
89 'source_ports': []}
90 rule1 = {'action': 'ALLOW',
91 'services': [{'service': service1}],
92 'sources': [{'target_id': '10.24.4.2',
93 'target_type': 'IPv4Address'}],
94 'display_name': 'Fwaas-fake-fw-rule1',
95 'notes': 'first rule'}
96 service2 = {'l4_protocol': 'TCP',
97 'resource_type': 'L4PortSetNSService',
98 'destination_ports': ['22'],
99 'source_ports': []}
100 rule2 = {'action': 'DROP', # Reject is replaced with deny
101 'services': [{'service': service2}],
102 'display_name': 'Fwaas-fake-fw-rule2'}
103 service3_1 = {'resource_type': 'ICMPTypeNSService',
104 'protocol': 'ICMPv4'}
105 service3_2 = {'resource_type': 'ICMPTypeNSService',
106 'protocol': 'ICMPv6'}
107 rule3 = {'action': 'DROP',
108 # icmp is translated to icmp v4 & v6
109 'services': [{'service': service3_1},
110 {'service': service3_2}],
111 'display_name': 'Fwaas-fake-fw-rule3'}
112 return [rule1, rule2, rule3]
113
114 def _fake_firewall_no_rule(self):
115 rule_list = []
116 fw_inst = {'id': FAKE_FW_ID,
117 'admin_state_up': True,
118 'tenant_id': 'tenant-uuid',
119 'firewall_rule_list': rule_list}
120 return fw_inst
121
122 def _fake_firewall(self, rule_list):
123 _rule_list = copy.deepcopy(rule_list)
124 for rule in _rule_list:
125 rule['position'] = str(_rule_list.index(rule))
126 fw_inst = {'id': FAKE_FW_ID,
127 'admin_state_up': True,
128 'tenant_id': 'tenant-uuid',
129 'firewall_rule_list': _rule_list}
130 return fw_inst
131
132 def _fake_firewall_with_admin_down(self, rule_list):
133 fw_inst = {'id': FAKE_FW_ID,
134 'admin_state_up': False,
135 'tenant_id': 'tenant-uuid',
136 'firewall_rule_list': rule_list}
137 return fw_inst
138
139 def _fake_apply_list(self, router_count=1):
140 apply_list = []
141 while router_count > 0:
142 router_inst = {'id': FAKE_ROUTER_ID}
143 router_info_inst = mock.Mock()
144 router_info_inst.router = router_inst
145 apply_list.append(router_info_inst)
146 router_count -= 1
147 return apply_list
148
149 def _setup_firewall_with_rules(self, func, router_count=1):
150 apply_list = self._fake_apply_list(router_count=router_count)
151 rule_list = self._fake_rules_v4()
152 firewall = self._fake_firewall(rule_list)
153 with mock.patch("vmware_nsxlib.v3.security.NsxLibFirewallSection."
154 "update") as update_fw:
155 func('nsx', apply_list, firewall)
156 self.assertEqual(router_count, update_fw.call_count)
157 update_fw.assert_called_with(
158 MOCK_SECTION_ID,
159 rules=self._fake_translated_rules() + [self._default_rule()])
160
161 def test_create_firewall_no_rules(self):
162 apply_list = self._fake_apply_list()
163 firewall = self._fake_firewall_no_rule()
164 with mock.patch("vmware_nsxlib.v3.security.NsxLibFirewallSection."
165 "update") as update_fw:
166 self.firewall.create_firewall('nsx', apply_list, firewall)
167 update_fw.assert_called_once_with(
168 MOCK_SECTION_ID,
169 rules=[self._default_rule()])
170
171 def test_create_firewall_with_rules(self):
172 self._setup_firewall_with_rules(self.firewall.create_firewall)
173
174 def test_create_firewall_with_rules_two_routers(self):
175 self._setup_firewall_with_rules(self.firewall.create_firewall,
176 router_count=2)
177
178 def test_update_firewall_with_rules(self):
179 self._setup_firewall_with_rules(self.firewall.update_firewall)
180
181 def test_delete_firewall(self):
182 apply_list = self._fake_apply_list()
183 firewall = self._fake_firewall_no_rule()
184 with mock.patch("vmware_nsxlib.v3.security.NsxLibFirewallSection."
185 "update") as update_fw:
186 self.firewall.delete_firewall('nsx', apply_list, firewall)
187 update_fw.assert_called_once_with(
188 MOCK_SECTION_ID,
189 rules=[self._default_rule(drop=False)])
190
191 def test_create_firewall_with_admin_down(self):
192 apply_list = self._fake_apply_list()
193 rule_list = self._fake_rules_v4()
194 firewall = self._fake_firewall_with_admin_down(rule_list)
195 with mock.patch("vmware_nsxlib.v3.security.NsxLibFirewallSection."
196 "update") as update_fw:
197 self.firewall.create_firewall('nsx', apply_list, firewall)
198 update_fw.assert_called_once_with(
199 MOCK_SECTION_ID,
200 rules=[self._default_rule()])
diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py
index 5adbe57..4f005e4 100644
--- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py
+++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py
@@ -149,6 +149,11 @@ def _mock_nsx_backend_calls():
149 side_effect=_return_id_key).start() 149 side_effect=_return_id_key).start()
150 150
151 mock.patch( 151 mock.patch(
152 "vmware_nsxlib.v3.core_resources.NsxLibLogicalRouter."
153 "get_firewall_section_id",
154 side_effect=_return_id_key).start()
155
156 mock.patch(
152 "vmware_nsxlib.v3.NsxLib.get_version", 157 "vmware_nsxlib.v3.NsxLib.get_version",
153 return_value='1.1.0').start() 158 return_value='1.1.0').start()
154 159
@@ -562,6 +567,11 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxV3PluginTestCaseMixin):
562 cfg.CONF.set_default('max_routes', 3) 567 cfg.CONF.set_default('max_routes', 3)
563 self.addCleanup(restore_l3_attribute_map, self._l3_attribute_map_bk) 568 self.addCleanup(restore_l3_attribute_map, self._l3_attribute_map_bk)
564 ext_mgr = ext_mgr or TestL3ExtensionManager() 569 ext_mgr = ext_mgr or TestL3ExtensionManager()
570 mock_nsx_version = mock.patch.object(nsx_plugin.utils,
571 'is_nsx_version_2_0_0',
572 new=lambda v: True)
573 mock_nsx_version.start()
574
565 super(L3NatTest, self).setUp( 575 super(L3NatTest, self).setUp(
566 plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) 576 plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins)
567 self.plugin_instance = directory.get_plugin() 577 self.plugin_instance = directory.get_plugin()