Enhanced support for policy services
- Move some NSX services code to utils so it can be reused by the policy api consumers - Add IPProtocolServiceEntry support Change-Id: I50b5415c05a8a0f5b2432fa797c7e18f244a19e7
This commit is contained in:
parent
b74dc645cb
commit
a10e22a67c
@ -635,6 +635,126 @@ class TestPolicyIcmpService(NsxPolicyLibTestCase):
|
||||
update_call, expected_def, expected_dict)
|
||||
|
||||
|
||||
class TestPolicyIPProtocolService(NsxPolicyLibTestCase):
|
||||
|
||||
def setUp(self, *args, **kwargs):
|
||||
super(TestPolicyIPProtocolService, self).setUp()
|
||||
self.resourceApi = self.policy_lib.ip_protocol_service
|
||||
|
||||
def test_create(self):
|
||||
name = 's1'
|
||||
description = 'desc'
|
||||
protocol_number = 2
|
||||
with mock.patch.object(self.policy_api,
|
||||
"create_with_parent") as api_call:
|
||||
self.resourceApi.create_or_overwrite(
|
||||
name,
|
||||
description=description,
|
||||
protocol_number=protocol_number,
|
||||
tenant=TEST_TENANT)
|
||||
exp_srv_def = policy_defs.ServiceDef(service_id=mock.ANY,
|
||||
name=name,
|
||||
description=description,
|
||||
tenant=TEST_TENANT)
|
||||
exp_entry_def = policy_defs.IPProtocolServiceEntryDef(
|
||||
service_id=mock.ANY,
|
||||
name=name,
|
||||
description=description,
|
||||
protocol_number=protocol_number,
|
||||
tenant=TEST_TENANT)
|
||||
self.assert_called_with_defs(
|
||||
api_call, [exp_srv_def, exp_entry_def])
|
||||
|
||||
def test_delete(self):
|
||||
id = '111'
|
||||
with mock.patch.object(self.policy_api, "delete") as api_call,\
|
||||
mock.patch.object(self.policy_api, "get") as get_call:
|
||||
self.resourceApi.delete(id, tenant=TEST_TENANT)
|
||||
expected_def = policy_defs.ServiceDef(service_id=id,
|
||||
tenant=TEST_TENANT)
|
||||
self.assert_called_with_def(get_call, expected_def)
|
||||
self.assert_called_with_def(api_call, expected_def)
|
||||
|
||||
def test_get(self):
|
||||
id = '111'
|
||||
with mock.patch.object(self.policy_api, "get") as api_call:
|
||||
self.resourceApi.get(id, tenant=TEST_TENANT)
|
||||
expected_def = policy_defs.ServiceDef(service_id=id,
|
||||
tenant=TEST_TENANT)
|
||||
self.assert_called_with_def(api_call, expected_def)
|
||||
|
||||
def test_get_by_name(self):
|
||||
name = 's1'
|
||||
with mock.patch.object(
|
||||
self.policy_api, "list",
|
||||
return_value={'results': [{'display_name': name}]}) as api_call:
|
||||
obj = self.resourceApi.get_by_name(name, tenant=TEST_TENANT)
|
||||
self.assertIsNotNone(obj)
|
||||
expected_def = policy_defs.ServiceDef(tenant=TEST_TENANT)
|
||||
self.assert_called_with_def(api_call, expected_def)
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.policy_api, "list") as api_call:
|
||||
self.resourceApi.list(tenant=TEST_TENANT)
|
||||
expected_def = policy_defs.ServiceDef(tenant=TEST_TENANT)
|
||||
self.assert_called_with_def(api_call, expected_def)
|
||||
|
||||
def test_update(self):
|
||||
id = '111'
|
||||
name = 'new_name'
|
||||
description = 'new desc'
|
||||
with mock.patch.object(self.policy_api, "get",
|
||||
return_value={}) as get_call,\
|
||||
mock.patch.object(self.policy_api,
|
||||
"create_or_update") as update_call:
|
||||
self.resourceApi.update(id,
|
||||
name=name,
|
||||
description=description,
|
||||
tenant=TEST_TENANT)
|
||||
expected_def = policy_defs.ServiceDef(service_id=id,
|
||||
tenant=TEST_TENANT)
|
||||
expected_dict = {'display_name': name,
|
||||
'description': description}
|
||||
self.assert_called_with_def(get_call, expected_def)
|
||||
self.assert_called_with_def_and_dict(
|
||||
update_call, expected_def, expected_dict)
|
||||
|
||||
def test_update_all(self):
|
||||
id = '111'
|
||||
name = 'newName'
|
||||
description = 'new desc'
|
||||
protocol_number = 3
|
||||
service_entry_id = '222'
|
||||
service_entry = {'id': service_entry_id}
|
||||
|
||||
with mock.patch.object(
|
||||
self.policy_api, "get",
|
||||
return_value={'service_entries': [service_entry]}) as get_call,\
|
||||
mock.patch.object(self.policy_api,
|
||||
"create_or_update") as update_call,\
|
||||
mock.patch.object(self.policy_api, "list",
|
||||
return_value={'results': []}):
|
||||
self.resourceApi.update(id,
|
||||
name=name,
|
||||
description=description,
|
||||
protocol_number=protocol_number,
|
||||
tenant=TEST_TENANT)
|
||||
# get will be called for the entire service
|
||||
expected_def = policy_defs.ServiceDef(service_id=id,
|
||||
tenant=TEST_TENANT)
|
||||
self.assert_called_with_def(get_call, expected_def)
|
||||
|
||||
expected_dict = {'display_name': name,
|
||||
'description': description,
|
||||
'service_entries': [{
|
||||
'id': service_entry_id,
|
||||
'display_name': name,
|
||||
'description': description,
|
||||
'protocol_number': protocol_number}]}
|
||||
self.assert_called_with_def_and_dict(
|
||||
update_call, expected_def, expected_dict)
|
||||
|
||||
|
||||
class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
|
||||
|
||||
def setUp(self, *args, **kwargs):
|
||||
|
@ -383,6 +383,9 @@ class NsxPolicyLib(NsxLibBase):
|
||||
self.service = policy_resources.NsxPolicyL4ServiceApi(self.policy_api)
|
||||
self.icmp_service = policy_resources.NsxPolicyIcmpServiceApi(
|
||||
self.policy_api)
|
||||
self.ip_protocol_service = (
|
||||
policy_resources.NsxPolicyIPProtocolServiceApi(
|
||||
self.policy_api))
|
||||
self.network = policy_resources.NsxPolicyNetworkApi(self.policy_api)
|
||||
self.segment = policy_resources.NsxPolicySegmentApi(self.policy_api)
|
||||
self.comm_map = policy_resources.NsxPolicyCommunicationMapApi(
|
||||
|
@ -441,6 +441,38 @@ class IcmpServiceEntryDef(ServiceEntryDef):
|
||||
body=body, **kwargs)
|
||||
|
||||
|
||||
class IPProtocolServiceEntryDef(ServiceEntryDef):
|
||||
def __init__(self,
|
||||
service_id=None,
|
||||
service_entry_id=None,
|
||||
name=None,
|
||||
description=None,
|
||||
protocol_number=None,
|
||||
tenant=policy_constants.POLICY_INFRA_TENANT):
|
||||
super(IPProtocolServiceEntryDef, self).__init__()
|
||||
self.tenant = tenant
|
||||
self.id = service_entry_id
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.protocol_number = protocol_number
|
||||
self.parent_ids = (tenant, service_id)
|
||||
|
||||
def get_obj_dict(self):
|
||||
body = super(IPProtocolServiceEntryDef, self).get_obj_dict()
|
||||
body['resource_type'] = 'IPProtocolServiceEntry'
|
||||
body['protocol_number'] = self.protocol_number
|
||||
return body
|
||||
|
||||
def update_attributes_in_body(self, **kwargs):
|
||||
# Fix params that need special conversions
|
||||
body = self._get_body_from_kwargs(**kwargs)
|
||||
if 'body' in kwargs:
|
||||
del kwargs['body']
|
||||
|
||||
super(IPProtocolServiceEntryDef, self).update_attributes_in_body(
|
||||
body=body, **kwargs)
|
||||
|
||||
|
||||
class CommunicationMapDef(ResourceDef):
|
||||
def __init__(self,
|
||||
map_id=None,
|
||||
|
@ -435,6 +435,52 @@ class NsxPolicyIcmpServiceApi(NsxPolicyServiceBase):
|
||||
icmp_code=icmp_code)
|
||||
|
||||
|
||||
class NsxPolicyIPProtocolServiceApi(NsxPolicyServiceBase):
|
||||
"""NSX Policy Service with a single IPProtocol service entry.
|
||||
|
||||
Note the nsx-policy backend supports multiple service entries per service.
|
||||
At this point this is not supported here.
|
||||
"""
|
||||
@property
|
||||
def entry_def(self):
|
||||
return policy_defs.IPProtocolServiceEntryDef
|
||||
|
||||
def create_or_overwrite(self, name, service_id=None, description=None,
|
||||
protocol_number=None,
|
||||
tenant=policy_constants.POLICY_INFRA_TENANT):
|
||||
service_id = self._init_obj_uuid(service_id)
|
||||
# service name cannot contain spaces or slashes
|
||||
name = self._canonize_name(name)
|
||||
service_def = policy_defs.ServiceDef(service_id=service_id,
|
||||
name=name,
|
||||
description=description,
|
||||
tenant=tenant)
|
||||
# NOTE(asarfaty) We set the service entry display name (which is also
|
||||
# used as the id) to be the same as the service name. In case we
|
||||
# support multiple service entries, we need the name to be unique.
|
||||
entry_def = policy_defs.IPProtocolServiceEntryDef(
|
||||
service_id=service_id,
|
||||
name=name,
|
||||
description=description,
|
||||
protocol_number=protocol_number,
|
||||
tenant=tenant)
|
||||
|
||||
return self.policy_api.create_with_parent(service_def, entry_def)
|
||||
|
||||
def _update_service_entry(self, service_id, srv_entry,
|
||||
name=None, description=None,
|
||||
protocol_number=None,
|
||||
tenant=policy_constants.POLICY_INFRA_TENANT):
|
||||
entry_id = srv_entry['id']
|
||||
entry_def = policy_defs.IPProtocolServiceEntryDef(
|
||||
service_id=service_id,
|
||||
service_entry_id=entry_id,
|
||||
tenant=tenant)
|
||||
entry_def.update_attributes_in_body(body=srv_entry, name=name,
|
||||
description=description,
|
||||
protocol_number=protocol_number)
|
||||
|
||||
|
||||
class NsxPolicyNetworkApi(NsxPolicyResourceBase):
|
||||
"""NSX Network API """
|
||||
@property
|
||||
|
@ -24,7 +24,6 @@ from oslo_log import log
|
||||
from oslo_log import versionutils
|
||||
from oslo_utils import excutils
|
||||
|
||||
from vmware_nsxlib.v3 import constants
|
||||
from vmware_nsxlib.v3 import exceptions
|
||||
from vmware_nsxlib.v3 import nsx_constants as consts
|
||||
from vmware_nsxlib.v3 import utils
|
||||
@ -291,34 +290,13 @@ class NsxLibFirewallSection(utils.NsxLibApiBase):
|
||||
else consts.OUT
|
||||
)
|
||||
|
||||
def _get_l4_protocol_name(self, protocol_number):
|
||||
if protocol_number is None:
|
||||
return
|
||||
protocol_number = constants.IP_PROTOCOL_MAP.get(protocol_number,
|
||||
protocol_number)
|
||||
try:
|
||||
protocol_number = int(protocol_number)
|
||||
except ValueError:
|
||||
raise exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=protocol_number,
|
||||
arg_name='protocol')
|
||||
if protocol_number == 6:
|
||||
return consts.TCP
|
||||
elif protocol_number == 17:
|
||||
return consts.UDP
|
||||
elif protocol_number == 1:
|
||||
return consts.ICMPV4
|
||||
else:
|
||||
return protocol_number
|
||||
|
||||
def get_nsservice(self, resource_type, **properties):
|
||||
service = {'resource_type': resource_type}
|
||||
service.update(properties)
|
||||
return {'service': service}
|
||||
|
||||
def _decide_service(self, sg_rule):
|
||||
l4_protocol = self._get_l4_protocol_name(sg_rule['protocol'])
|
||||
l4_protocol = utils.get_l4_protocol_name(sg_rule['protocol'])
|
||||
|
||||
if l4_protocol in [consts.TCP, consts.UDP]:
|
||||
# If port_range_min is not specified then we assume all ports are
|
||||
@ -343,30 +321,8 @@ class NsxLibFirewallSection(utils.NsxLibApiBase):
|
||||
icmp_code = sg_rule['port_range_max']
|
||||
icmp_strict = self.nsxlib.feature_supported(
|
||||
consts.FEATURE_ICMP_STRICT)
|
||||
if icmp_type:
|
||||
if (icmp_strict and icmp_type not in
|
||||
constants.IPV4_ICMP_STRICT_TYPES):
|
||||
raise exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=icmp_type,
|
||||
arg_name='icmp_type')
|
||||
if icmp_type not in constants.IPV4_ICMP_TYPES:
|
||||
raise exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=icmp_type,
|
||||
arg_name='icmp_type')
|
||||
if (icmp_code and icmp_strict and icmp_code not in constants.
|
||||
IPV4_ICMP_STRICT_TYPES[icmp_type]):
|
||||
raise exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=icmp_code,
|
||||
arg_name='icmp_code for this icmp_type')
|
||||
if (icmp_code and icmp_code not in
|
||||
constants.IPV4_ICMP_TYPES[icmp_type]):
|
||||
raise exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=icmp_code,
|
||||
arg_name='icmp_code for this icmp_type')
|
||||
utils.validate_dhcp_params(icmp_type, icmp_code, icmp_version=4,
|
||||
strict=icmp_strict)
|
||||
|
||||
return self.get_nsservice(
|
||||
consts.ICMP_TYPE_NSSERVICE,
|
||||
|
@ -24,7 +24,9 @@ import tenacity
|
||||
from tenacity import _utils as tenacity_utils
|
||||
|
||||
from vmware_nsxlib._i18n import _
|
||||
from vmware_nsxlib.v3 import constants
|
||||
from vmware_nsxlib.v3 import exceptions as nsxlib_exceptions
|
||||
from vmware_nsxlib.v3 import nsx_constants
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -521,3 +523,57 @@ class NsxLibApiBase(object):
|
||||
'tag': project_name[:MAX_TAG_LEN]},
|
||||
{'scope': 'os-api-version',
|
||||
'tag': self.nsxlib_config.plugin_ver}]
|
||||
|
||||
|
||||
# Some utilities for services translations & validations
|
||||
# both for the nsx manager & policy manager
|
||||
def validate_dhcp_params(icmp_type, icmp_code, icmp_version=4, strict=False):
|
||||
if icmp_version != 4:
|
||||
# ICMPv6 is currently not supported
|
||||
return
|
||||
if icmp_type:
|
||||
if (strict and icmp_type not in
|
||||
constants.IPV4_ICMP_STRICT_TYPES):
|
||||
raise nsxlib_exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=icmp_type,
|
||||
arg_name='icmp_type')
|
||||
if icmp_type not in constants.IPV4_ICMP_TYPES:
|
||||
raise nsxlib_exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=icmp_type,
|
||||
arg_name='icmp_type')
|
||||
if (icmp_code and strict and icmp_code not in
|
||||
constants.IPV4_ICMP_STRICT_TYPES[icmp_type]):
|
||||
raise nsxlib_exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=icmp_code,
|
||||
arg_name='icmp_code for this icmp_type')
|
||||
if (icmp_code and icmp_code not in
|
||||
constants.IPV4_ICMP_TYPES[icmp_type]):
|
||||
raise nsxlib_exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=icmp_code,
|
||||
arg_name='icmp_code for this icmp_type')
|
||||
|
||||
|
||||
def get_l4_protocol_name(protocol_number):
|
||||
if protocol_number is None:
|
||||
return
|
||||
protocol_number = constants.IP_PROTOCOL_MAP.get(protocol_number,
|
||||
protocol_number)
|
||||
try:
|
||||
protocol_number = int(protocol_number)
|
||||
except ValueError:
|
||||
raise nsxlib_exceptions.InvalidInput(
|
||||
operation='create_rule',
|
||||
arg_val=protocol_number,
|
||||
arg_name='protocol')
|
||||
if protocol_number == 6:
|
||||
return nsx_constants.TCP
|
||||
elif protocol_number == 17:
|
||||
return nsx_constants.UDP
|
||||
elif protocol_number == 1:
|
||||
return nsx_constants.ICMPV4
|
||||
else:
|
||||
return protocol_number
|
||||
|
Loading…
x
Reference in New Issue
Block a user