[MP] Support firewall_match for NAT rules

The nat_pass is deprecated and has been replaced by firewall_match.
This patch add support for firewall_match and stops using nat_pass
when firewall_match is specified.

Change-Id: Ibd2303cf4e182c7aea6bab57c27f36ee4c138a47
This commit is contained in:
Salvatore Orlando 2022-06-09 23:15:22 -07:00
parent 478165cb8d
commit 04c6acbd1d
5 changed files with 145 additions and 13 deletions

View File

@ -971,8 +971,10 @@ class LogicalRouterTestCase(BaseTestResource):
test_constants.FAKE_ROUTER_UUID, router_body=fake_router)
self.assertEqual(test_constants.FAKE_ROUTER_FW_SEC_UUID, section_id)
def _test_nat_rule_create(self, nsx_version, add_bypas_arg=True,
action='SNAT', expect_failure=False):
def _test_nat_rule_create(self, nsx_version, add_bypass_arg=True,
firewall_match=None,
action='SNAT', expect_failure=False,
logging=False):
router = self.get_mocked_resource()
translated_net = '1.1.1.1'
priority = 10
@ -985,9 +987,11 @@ class LogicalRouterTestCase(BaseTestResource):
'translated_network': translated_net,
'rule_priority': priority
}
if add_bypas_arg:
if add_bypass_arg and not firewall_match:
# Expect nat_pass to be sent to the backend
data['nat_pass'] = False
elif firewall_match:
data['firewall_match'] = firewall_match
# Ignoring 'bypass_firewall' with version 1.1
with mock.patch("vmware_nsxlib.v3.NsxLib.get_version",
@ -998,7 +1002,9 @@ class LogicalRouterTestCase(BaseTestResource):
translated_network=translated_net,
rule_priority=priority,
bypass_firewall=False,
display_name=display_name)
firewall_match=firewall_match,
display_name=display_name,
logging=logging)
except exceptions.InvalidInput as e:
if expect_failure:
return
@ -1014,7 +1020,11 @@ class LogicalRouterTestCase(BaseTestResource):
def test_nat_rule_create_v1(self):
# Ignoring 'bypass_firewall' with version 1.1
self._test_nat_rule_create('1.1.0', add_bypas_arg=False)
self._test_nat_rule_create('1.1.0', add_bypass_arg=False)
def test_nat_rule_create_with_logging(self):
# enable logging parameter in snat obj
self._test_nat_rule_create('1.1.0', add_bypass_arg=False, logging=True)
def test_nat_rule_create_v2(self):
# Sending 'bypass_firewall' with version 1.1
@ -1029,11 +1039,29 @@ class LogicalRouterTestCase(BaseTestResource):
self._test_nat_rule_create('2.0.0', action='NO_DNAT',
expect_failure=True)
def test_nat_rule_create_invalid(self):
def test_nat_rule_create_invalid_action(self):
# NO_DNAT is supported from 2.2 & up
self._test_nat_rule_create('2.0.0', action='INVALID',
expect_failure=True)
def test_nat_rule_create_with_fw_match(self):
self._test_nat_rule_create(
'2.0.0', action='SNAT',
firewall_match=nsx_constants.NAT_FIREWALL_MATCH_EXTERNAL)
def test_nat_rule_create_with_fw_match_and_bypass(self):
# When both firewall_match and bypass_firewall are specified,
# we only want to send firewall_match to the backend
self._test_nat_rule_create(
'2.0.0', action='SNAT',
add_bypass_arg=True,
firewall_match=nsx_constants.NAT_FIREWALL_MATCH_INTERNAL)
def test_nat_rule_create_invalid_fw_match(self):
self._test_nat_rule_create('2.0.0', action='SNAT',
firewall_match='meh',
expect_failure=True)
def test_nat_rule_list(self):
router = self.get_mocked_resource()
router.list_nat_rules(test_constants.FAKE_ROUTER_UUID)
@ -1058,6 +1086,24 @@ class LogicalRouterTestCase(BaseTestResource):
data=jsonutils.dumps(data, sort_keys=True),
headers=self.default_headers())
def test_nat_rule_update_fw_match(self):
router = self.get_mocked_resource()
rule_id = '123'
with mock.patch.object(router.client, 'get',
return_value={'id': rule_id}):
router.update_nat_rule(
test_constants.FAKE_ROUTER_UUID, rule_id,
firewall_match=nsx_constants.NAT_FIREWALL_MATCH_INTERNAL)
data = {
'id': rule_id,
'firewall_match': nsx_constants.NAT_FIREWALL_MATCH_INTERNAL}
test_client.assert_json_call(
'put', router,
('https://1.2.3.4/api/v1/logical-routers/%s/nat/rules/%s' %
(test_constants.FAKE_ROUTER_UUID, rule_id)),
data=jsonutils.dumps(data, sort_keys=True),
headers=self.default_headers())
def test_delete_nat_rule_by_gw(self):
router = self.get_mocked_resource()
rule_id = '123'

View File

@ -243,6 +243,24 @@ class TestRouter(nsxlib_testcase.NsxClientTestCase):
tags=None,
display_name=None)
def test_add_gw_snat_rule_fw_match(self):
logical_router_id = test_constants.FAKE_ROUTER_UUID
gw_ip = '1.1.1.1'
with mock.patch.object(self.nsxlib.router.nsxlib.logical_router,
'add_nat_rule') as add_api:
self.nsxlib.router.add_gw_snat_rule(
logical_router_id, gw_ip,
firewall_match=nsx_constants.NAT_FIREWALL_MATCH_EXTERNAL)
add_api.assert_called_with(
logical_router_id,
translated_network=gw_ip,
action="SNAT",
firewall_match=nsx_constants.NAT_FIREWALL_MATCH_EXTERNAL,
source_net=None,
rule_priority=nsx_router.GW_NAT_PRI,
tags=None,
display_name=None)
def test_update_router_edge_cluster(self):
logical_router_id = test_constants.FAKE_ROUTER_UUID
ec_id = test_constants.FAKE_EDGE_CLUSTER
@ -300,6 +318,15 @@ class TestRouter(nsxlib_testcase.NsxClientTestCase):
'1.1.1.1', '2.2.2.2')
self.assertEqual(add_rule.call_count, 2)
def test_add_fip_nat_rules_firewall_match(self):
with mock.patch.object(self.nsxlib.logical_router,
"add_nat_rule") as add_rule:
self.nsxlib.router.add_fip_nat_rules(
test_constants.FAKE_ROUTER_UUID,
'1.1.1.1', '2.2.2.2',
firewall_match=nsx_constants.NAT_FIREWALL_MATCH_EXTERNAL)
self.assertEqual(add_rule.call_count, 2)
def test_delete_fip_nat_rules(self):
with mock.patch.object(self.nsxlib.logical_router,
"delete_nat_rule_by_values") as del_rule:

View File

@ -599,15 +599,26 @@ class NsxLibLogicalRouter(utils.NsxLibApiBase):
arg_val=action,
arg_name='action')
def _validate_nat_rule_firewall_match(self, firewall_match):
if not firewall_match:
return
if firewall_match not in nsx_constants.NAT_FIREWALL_MATCH_VALUES:
raise exceptions.InvalidInput(
operation="Create/Update NAt rule",
arg_val=firewall_match,
arg_name='firewall_match')
def add_nat_rule(self, logical_router_id, action, translated_network,
source_net=None, dest_net=None,
enabled=True, rule_priority=None,
match_ports=None, match_protocol=None,
match_resource_type=None,
bypass_firewall=True,
firewall_match=None,
bypass_firewall=True, logging=None,
tags=None,
display_name=None):
self._validate_nat_rule_action(action)
self._validate_nat_rule_firewall_match(firewall_match)
resource = 'logical-routers/%s/nat/rules' % logical_router_id
body = {'action': action,
'enabled': enabled,
@ -626,11 +637,16 @@ class NsxLibLogicalRouter(utils.NsxLibApiBase):
'l4_protocol': match_protocol or nsx_constants.TCP}
# nat_pass parameter is supported with the router firewall feature
# this parameter is deprecated, and should never be used
if (self.nsxlib and
self.nsxlib.feature_supported(
nsx_constants.FEATURE_ROUTER_FIREWALL)):
body['nat_pass'] = bypass_firewall
elif not bypass_firewall:
# only consider nat_pass if firewall_match is not specified
if not firewall_match:
body['nat_pass'] = bypass_firewall
else:
body['firewall_match'] = firewall_match
elif not bypass_firewall or firewall_match:
LOG.error("Ignoring bypass_firewall for router %s nat rule: "
"this feature is not supported.", logical_router_id)
if tags is not None:
@ -701,6 +717,8 @@ class NsxLibLogicalRouter(utils.NsxLibApiBase):
def update_nat_rule(self, logical_router_id, nat_rule_id, **kwargs):
if 'action' in kwargs:
self._validate_nat_rule_action(kwargs['action'])
if 'firewall_match' in kwargs:
self._validate_nat_rule_firewall_match(kwargs['firewall_match'])
resource = 'logical-routers/%s/nat/rules/%s' % (
logical_router_id, nat_rule_id)
return self._update_resource(resource, kwargs, retry=True)

View File

@ -132,6 +132,15 @@ TRANSPORT_TYPE_OVERLAY = 'OVERLAY'
HOST_SWITCH_MODE_ENS = 'ENS'
HOST_SWITCH_MODE_STANDARD = 'STANDARD'
# NAT firewall match
NAT_FIREWALL_MATCH_BYPASS = 'BYPASS'
NAT_FIREWALL_MATCH_EXTERNAL = 'MATCH_EXTERNAL_ADDRESS'
NAT_FIREWALL_MATCH_INTERNAL = 'MATCH_INTERNAL_ADDRESS'
NAT_FIREWALL_MATCH_VALUES = [
NAT_FIREWALL_MATCH_BYPASS,
NAT_FIREWALL_MATCH_INTERNAL,
NAT_FIREWALL_MATCH_EXTERNAL]
# Error codes returned by the backend
ERR_CODE_OBJECT_NOT_FOUND = 202
ERR_CODE_IPAM_POOL_EXHAUSTED = 5109

View File

@ -176,13 +176,24 @@ class RouterLib(object):
skip_not_found=True, strict_mode=False)
def add_gw_snat_rule(self, logical_router_id, gw_ip, source_net=None,
bypass_firewall=True, tags=None, display_name=None):
bypass_firewall=True, firewall_match=None,
tags=None, display_name=None):
if not firewall_match:
return self.nsxlib.logical_router.add_nat_rule(
logical_router_id, action="SNAT",
translated_network=gw_ip,
source_net=source_net,
rule_priority=GW_NAT_PRI,
bypass_firewall=bypass_firewall,
tags=tags,
display_name=display_name)
# Ignore bypass_firewall if firewall_match is specified
return self.nsxlib.logical_router.add_nat_rule(
logical_router_id, action="SNAT",
translated_network=gw_ip,
source_net=source_net,
rule_priority=GW_NAT_PRI,
bypass_firewall=bypass_firewall,
firewall_match=firewall_match,
tags=tags,
display_name=display_name)
@ -224,13 +235,34 @@ class RouterLib(object):
def add_fip_nat_rules(self, logical_router_id, ext_ip, int_ip,
match_ports=None, bypass_firewall=True,
firewall_match=None,
tags=None, display_name=None):
if not firewall_match:
self.nsxlib.logical_router.add_nat_rule(
logical_router_id, action="SNAT",
translated_network=ext_ip,
source_net=int_ip,
rule_priority=FIP_NAT_PRI,
bypass_firewall=bypass_firewall,
tags=tags,
display_name=display_name)
self.nsxlib.logical_router.add_nat_rule(
logical_router_id, action="DNAT",
translated_network=int_ip,
dest_net=ext_ip,
rule_priority=FIP_NAT_PRI,
match_ports=match_ports,
bypass_firewall=bypass_firewall,
tags=tags,
display_name=display_name)
return
# Ignore bypass_firewall if firewall_match is specified
self.nsxlib.logical_router.add_nat_rule(
logical_router_id, action="SNAT",
translated_network=ext_ip,
source_net=int_ip,
rule_priority=FIP_NAT_PRI,
bypass_firewall=bypass_firewall,
firewall_match=firewall_match,
tags=tags,
display_name=display_name)
self.nsxlib.logical_router.add_nat_rule(
@ -239,7 +271,7 @@ class RouterLib(object):
dest_net=ext_ip,
rule_priority=FIP_NAT_PRI,
match_ports=match_ports,
bypass_firewall=bypass_firewall,
firewall_match=firewall_match,
tags=tags,
display_name=display_name)