A common library that interfaces with VMware NSX.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
vmware-nsxlib/vmware_nsxlib/v3/policy/core_defs.py

1845 lines
54 KiB

# Copyright 2017 VMware, Inc.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import abc
import six
from vmware_nsxlib.v3 import utils
from vmware_nsxlib.v3.policy import constants
TENANTS_PATH_PATTERN = "%s/"
DOMAINS_PATH_PATTERN = TENANTS_PATH_PATTERN + "domains/"
IP_BLOCKS_PATH_PATTERN = TENANTS_PATH_PATTERN + "ip-blocks/"
IP_POOLS_PATH_PATTERN = TENANTS_PATH_PATTERN + "ip-pools/"
SEGMENTS_PATH_PATTERN = TENANTS_PATH_PATTERN + "segments/"
PROVIDERS_PATH_PATTERN = TENANTS_PATH_PATTERN + "providers/"
TIER0S_PATH_PATTERN = TENANTS_PATH_PATTERN + "tier-0s/"
TIER1S_PATH_PATTERN = TENANTS_PATH_PATTERN + "tier-1s/"
SERVICES_PATH_PATTERN = TENANTS_PATH_PATTERN + "services/"
ENFORCEMENT_POINT_PATTERN = (TENANTS_PATH_PATTERN +
"sites/default/enforcement-points/")
TRANSPORT_ZONE_PATTERN = ENFORCEMENT_POINT_PATTERN + "%s/transport-zones/"
EDGE_CLUSTER_PATTERN = ENFORCEMENT_POINT_PATTERN + "%s/edge-clusters/"
SEGMENT_SECURITY_PROFILES_PATH_PATTERN = (TENANTS_PATH_PATTERN +
"segment-security-profiles/")
QOS_PROFILES_PATH_PATTERN = TENANTS_PATH_PATTERN + "qos-profiles/"
SPOOFGUARD_PROFILES_PATH_PATTERN = (TENANTS_PATH_PATTERN +
"spoofguard-profiles/")
IP_DISCOVERY_PROFILES_PATH_PATTERN = (TENANTS_PATH_PATTERN +
"ip-discovery-profiles/")
MAC_DISCOVERY_PROFILES_PATH_PATTERN = (TENANTS_PATH_PATTERN +
"mac-discovery-profiles/")
IPV6_NDRA_PROFILES_PATH_PATTERN = (TENANTS_PATH_PATTERN +
"ipv6-ndra-profiles/")
WAF_PROFILES_PATH_PATTERN = (TENANTS_PATH_PATTERN +
"waf-profiles/")
CERTIFICATE_PATH_PATTERN = TENANTS_PATH_PATTERN + "certificates/"
EXCLUDE_LIST_PATH_PATTERN = (TENANTS_PATH_PATTERN +
"settings/firewall/security/exclude-list")
REALIZATION_PATH = "infra/realized-state/realized-entities?intent_path=%s"
DHCP_REALY_PATTERN = TENANTS_PATH_PATTERN + "dhcp-relay-configs/"
TIER0_LOCALE_SERVICES_PATH_PATTERN = (TIER0S_PATH_PATTERN +
"%s/locale-services/")
TIER1_LOCALE_SERVICES_PATH_PATTERN = (TIER1S_PATH_PATTERN +
"%s/locale-services/")
@six.add_metaclass(abc.ABCMeta)
class ResourceDef(object):
def __init__(self, **kwargs):
self.attrs = kwargs
# init default tenant
self.attrs['tenant'] = self.get_tenant()
self.body = {}
# As of now, for some defs (ex: services) child entry is required,
# meaning parent creation will fail without the child.
# Unfortunately in transactional API policy still fails us, even if
# child is specified as ChildEntry in same transaction.
# To provide a workaround, we need keep reference to the child and
# populate child entry inside parent clause in transactional API.
# TODO(annak): remove this if/when policy solves this
self.mandatory_child_def = None
def get_obj_dict(self):
body = self.body if self.body else {}
if self.resource_type():
body['resource_type'] = self.resource_type()
self._set_attr_if_specified(body, 'name', 'display_name')
self._set_attrs_if_specified(body, ['description', 'tags'])
resource_id = self.get_id()
if resource_id:
body['id'] = resource_id
return body
# This is needed for sake of update due to policy issue.
# Policy refuses to update without requires attributes provided,
# so we need to run an extra GET to acquire these.
# This should be removed when/if this issue is fixed on backend.
def set_obj_dict(self, obj_dict):
self.body = obj_dict
@abc.abstractproperty
def path_pattern(self):
pass
@abc.abstractproperty
def path_ids(self):
pass
@staticmethod
def resource_type():
pass
@classmethod
def resource_class(cls):
# Returns base resource type for polymorphic objects
# if not overriden, would return resource_type
return cls.resource_type()
@staticmethod
def resource_use_cache():
return False
def path_defs(self):
pass
def get_id(self):
if self.attrs and self.path_ids:
return self.attrs.get(self.path_ids[-1])
def get_attr(self, attr):
return self.attrs.get(attr)
def has_attr(self, attr):
return attr in self.attrs
def get_tenant(self):
if self.attrs.get('tenant'):
return self.attrs.get('tenant')
return constants.POLICY_INFRA_TENANT
def get_section_path(self):
path_ids = [self.get_attr(path_id) for path_id in self.path_ids[:-1]]
return self.path_pattern % (tuple(path_ids))
def get_resource_path(self):
resource_id = self.get_id()
if resource_id:
return self.get_section_path() + resource_id
return self.get_section_path()
def get_resource_full_path(self):
return '/' + self.get_resource_path()
@property
def get_last_section_dict_key(self):
last_section = self.path_pattern.split("/")[-2]
return last_section.replace('-', '_')
@staticmethod
def sub_entries_path():
pass
def _get_body_from_kwargs(self, **kwargs):
if 'body' in kwargs:
body = kwargs['body']
else:
body = {}
return body
# Helper to set attr in body if user specified it
# Can be used if body name is different than attr name
# If value is different than self.get_attr(attr), it can be set in arg
def _set_attr_if_specified(self, body, attr,
body_attr=None, value=None):
if self.has_attr(attr):
value = value if value is not None else self.get_attr(attr)
if body_attr:
# Body attr is different that attr exposed by resource def
body[body_attr] = value
else:
# Body attr is the same
body[attr] = value
# Helper to set attrs in body if user specified them
# Body name must match attr name
def _set_attrs_if_specified(self, body, attr_list):
for attr in attr_list:
self._set_attr_if_specified(body, attr)
@classmethod
def get_single_entry(cls, obj_body):
"""Return the single sub-entry from the object body.
If there are no entries, or more than 1 - return None.
"""
entries_path = cls.sub_entries_path()
if not entries_path:
# This sub class doesn't support this
return
if (entries_path not in obj_body or
len(obj_body[entries_path]) != 1):
return
return obj_body[entries_path][0]
def bodyless(self):
"""Return True if args contain only keys and meta attrs"""
meta = ['resource_type']
meta.extend(self.path_ids)
body_args = [key for key in self.attrs.keys()
if key not in meta]
return len(body_args) == 0
class TenantDef(ResourceDef):
@property
def path_pattern(self):
return TENANTS_PATH_PATTERN
@staticmethod
def resource_type():
return 'Infra'
def path_defs(self):
return ()
@property
def path_ids(self):
return ('tenant',)
def get_resource_path(self):
return 'infra/'
def get_section_path(self):
return 'infra/'
class DomainDef(ResourceDef):
@property
def path_pattern(self):
return DOMAINS_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'domain_id')
@staticmethod
def resource_type():
return 'Domain'
def path_defs(self):
return (TenantDef,)
class RouteAdvertisement(object):
types = {'static_routes': 'TIER1_STATIC_ROUTES',
'subnets': 'TIER1_CONNECTED',
'nat': 'TIER1_NAT',
'lb_vip': 'TIER1_LB_VIP',
'lb_snat': 'TIER1_LB_SNAT',
'dns_forwarder_ip': 'TIER1_DNS_FORWARDER_IP'}
def __init__(self, **kwargs):
self.attrs = kwargs
def get_obj_dict(self):
return [value for key, value in self.types.items()
if self.attrs.get(key) is True]
def set_obj_dict(self, obj_dict):
# This initializes object based on list coming from backend
# f.e. [TIER1_NAT, TIER1_LB_SNAT]
for key, value in self.types.items():
self.attrs[key] = value in obj_dict
def update(self, **kwargs):
# "None" will be passed as value when user does not specify adv type
# True/False will be passed when user wants to switch adv ON/OFF
for key, value in kwargs.items():
if value is not None:
self.attrs[key] = value
class RouterDef(ResourceDef):
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(RouterDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['failover_mode',
'force_whitelisting',
'default_rule_logging',
'disable_firewall'])
# Add dhcp relay config
# TODO(asarfaty): this can be either dhcp or dhcp relay config
if self.has_attr('dhcp_config'):
paths = None
if self.get_attr('dhcp_config'):
dhcp_conf = DhcpRelayConfigDef(
config_id=self.get_attr('dhcp_config'),
tenant=self.get_tenant())
paths = [dhcp_conf.get_resource_full_path()]
self._set_attr_if_specified(body, 'dhcp_config',
body_attr='dhcp_config_paths',
value=paths)
if self.has_attr('ipv6_ndra_profile_id'):
paths = None
if self.get_attr('ipv6_ndra_profile_id'):
ndra_profile = Ipv6NdraProfileDef(
profile_id=self.get_attr('ipv6_ndra_profile_id'),
tenant=self.get_tenant())
paths = [ndra_profile.get_resource_full_path()]
self._set_attr_if_specified(body, 'ipv6_ndra_profile_id',
body_attr='ipv6_profile_paths',
value=paths)
return body
class Tier0Def(RouterDef):
@property
def path_pattern(self):
return TIER0S_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'tier0_id')
@staticmethod
def resource_type():
return 'Tier0'
@staticmethod
def resource_use_cache():
return True
def get_obj_dict(self):
body = super(Tier0Def, self).get_obj_dict()
self._set_attrs_if_specified(body, ['ha_mode', 'transit_subnets'])
return body
class Tier1Def(RouterDef):
@property
def path_pattern(self):
return TIER1S_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'tier1_id')
@staticmethod
def resource_type():
return 'Tier1'
def get_obj_dict(self):
body = super(Tier1Def, self).get_obj_dict()
if self.has_attr('tier0'):
tier0 = self.get_attr('tier0')
tier0_path = None
if tier0:
tenant = TENANTS_PATH_PATTERN % self.get_tenant()
tier0_path = "/%stier-0s/%s" % (tenant, tier0)
self._set_attr_if_specified(body, 'tier0',
body_attr='tier0_path',
value=tier0_path)
if self.has_attr('route_advertisement'):
body['route_advertisement_types'] = self.get_attr(
'route_advertisement').get_obj_dict()
self._set_attrs_if_specified(body, ['enable_standby_relocation'])
if self.has_attr('route_advertisement_rules'):
body['route_advertisement_rules'] = [
a.get_obj_dict()
if isinstance(a, RouteAdvertisementRule) else a
for a in self.get_attr('route_advertisement_rules')]
return body
@staticmethod
def get_route_adv(obj_dict):
route_adv = RouteAdvertisement()
if 'route_advertisement_types' in obj_dict:
route_adv.set_obj_dict(obj_dict['route_advertisement_types'])
return route_adv
class RouterLocaleServiceDef(ResourceDef):
@staticmethod
def resource_type():
return 'LocaleServices'
def get_obj_dict(self):
body = super(RouterLocaleServiceDef, self).get_obj_dict()
self._set_attr_if_specified(body, 'edge_cluster_path')
return body
class Tier0LocaleServiceDef(RouterLocaleServiceDef):
@property
def path_pattern(self):
return TIER0_LOCALE_SERVICES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'tier0_id', 'service_id')
class Tier1LocaleServiceDef(RouterLocaleServiceDef):
@property
def path_pattern(self):
return TIER1_LOCALE_SERVICES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'tier1_id', 'service_id')
class Tier0InterfaceDef(ResourceDef):
@staticmethod
def resource_type():
return 'Tier0Interface'
@property
def path_pattern(self):
return TIER0_LOCALE_SERVICES_PATH_PATTERN + "%s/interfaces/"
@property
def path_ids(self):
return ('tenant', 'tier0_id', 'service_id', 'interface_id')
class Tier1InterfaceDef(ResourceDef):
@staticmethod
def resource_type():
return 'Tier1Interface'
@property
def path_pattern(self):
return TIER1_LOCALE_SERVICES_PATH_PATTERN + "%s/interfaces/"
def get_obj_dict(self):
body = super(Tier1InterfaceDef, self).get_obj_dict()
if self.has_attr('subnets'):
# subnets expected to be of type InterfaceSubnet
if self.get_attr('subnets'):
subnets = [subnet.get_obj_dict()
for subnet in self.get_attr('subnets')]
self._set_attr_if_specified(body, 'subnets',
value=subnets)
if self.has_attr('segment_id'):
path = ""
if self.get_attr('segment_id'):
tier1 = SegmentDef(segment_id=self.get_attr('segment_id'),
tenant=self.get_tenant())
path = tier1.get_resource_full_path()
self._set_attr_if_specified(body, 'segment_id',
body_attr='segment_path',
value=path)
if self.has_attr('ipv6_ndra_profile_id'):
paths = None
if self.get_attr('ipv6_ndra_profile_id'):
ndra_profile = Ipv6NdraProfileDef(
profile_id=self.get_attr('ipv6_ndra_profile_id'),
tenant=self.get_tenant())
paths = [ndra_profile.get_resource_full_path()]
self._set_attr_if_specified(body, 'ipv6_ndra_profile_id',
body_attr='ipv6_profile_paths',
value=paths)
return body
@property
def path_ids(self):
return ('tenant', 'tier1_id', 'service_id', 'interface_id')
class RouterNatRule(ResourceDef):
@staticmethod
def resource_type():
return 'PolicyNatRule'
def get_obj_dict(self):
body = super(RouterNatRule, self).get_obj_dict()
self._set_attrs_if_specified(body, ['action',
'source_network',
'destination_network',
'translated_network',
'firewall_match',
'log',
'sequence_number',
'enabled'])
return body
class Tier1NatRule(RouterNatRule):
@property
def path_pattern(self):
return TIER1S_PATH_PATTERN + "%s/nat/%s/nat-rules/"
@property
def path_ids(self):
return ('tenant', 'tier1_id', 'nat_id', 'nat_rule_id')
def path_defs(self):
return (TenantDef, Tier1Def)
class RouteAdvertisementRule(object):
def __init__(self, name, action=constants.ADV_RULE_PERMIT,
prefix_operator=constants.ADV_RULE_OPERATOR_GE,
route_advertisement_types=None,
subnets=None):
self.name = name
self.action = action
self.prefix_operator = prefix_operator
self.route_advertisement_types = route_advertisement_types
self.subnets = subnets
def get_obj_dict(self):
return {'name': self.name,
'action': self.action,
'prefix_operator': self.prefix_operator,
'route_advertisement_types': self.route_advertisement_types,
'subnets': self.subnets}
class RouterStaticRoute(ResourceDef):
@staticmethod
def resource_type():
return 'StaticRoutes'
def get_obj_dict(self):
body = super(RouterStaticRoute, self).get_obj_dict()
self._set_attrs_if_specified(body, ['network'])
# next hops
if self.has_attr('next_hop'):
next_hop = self.get_attr('next_hop')
next_hops = [{'ip_address': next_hop}]
self._set_attr_if_specified(body, 'next_hop',
body_attr='next_hops',
value=next_hops)
return body
class Tier1StaticRoute(RouterStaticRoute):
@property
def path_pattern(self):
return TIER1S_PATH_PATTERN + "%s/static-routes/"
@property
def path_ids(self):
return ('tenant', 'tier1_id', 'static_route_id')
def path_defs(self):
return (TenantDef, Tier1Def)
class Tier0StaticRoute(RouterStaticRoute):
@property
def path_pattern(self):
return TIER0S_PATH_PATTERN + "%s/static-routes/"
@property
def path_ids(self):
return ('tenant', 'tier0_id', 'static_route_id')
def path_defs(self):
return (TenantDef, Tier0Def)
class Tier0NatRule(RouterNatRule):
@property
def path_pattern(self):
return TIER0S_PATH_PATTERN + "%s/nat/%s/nat-rules/"
@property
def path_ids(self):
return ('tenant', 'tier0_id', 'nat_id', 'nat_rule_id')
def path_defs(self):
return (TenantDef, Tier0Def)
class Subnet(object):
def __init__(self, gateway_address, dhcp_ranges=None):
self.gateway_address = gateway_address
self.dhcp_ranges = dhcp_ranges
def get_obj_dict(self):
body = {'gateway_address': self.gateway_address}
if self.dhcp_ranges:
body['dhcp_ranges'] = self.dhcp_ranges
return body
class InterfaceSubnet(object):
def __init__(self, ip_addresses, prefix_len):
self.ip_addresses = ip_addresses
self.prefix_len = prefix_len
def get_obj_dict(self):
body = {'ip_addresses': self.ip_addresses,
'prefix_len': self.prefix_len}
return body
class BaseSegmentDef(ResourceDef):
def get_obj_dict(self):
body = super(BaseSegmentDef, self).get_obj_dict()
if self.has_attr('subnets'):
# Note(asarfaty): removing subnets through PATCH api is not
# supported
if self.get_attr('subnets'):
subnets = [subnet.get_obj_dict()
for subnet in self.get_attr('subnets')]
self._set_attr_if_specified(body, 'subnets',
value=subnets)
if self.has_attr('ip_pool_id'):
ip_pool_id = self.get_attr('ip_pool_id')
adv_cfg = self._get_adv_config(ip_pool_id)
self._set_attr_if_specified(body, 'ip_pool_id',
body_attr='advanced_config',
value=adv_cfg)
self._set_attrs_if_specified(body, ['domain_name', 'vlan_ids'])
return body
@staticmethod
def resource_type():
return 'Segment'
def _get_adv_config(self, ip_pool_id):
ip_pool_def = IpPoolDef(ip_pool_id=ip_pool_id)
ip_pool_path = ip_pool_def.get_resource_full_path()
return {'address_pool_paths': [ip_pool_path]}
class Tier1SegmentDef(BaseSegmentDef):
'''Tier1 segments can not move to different tier1 '''
@property
def path_pattern(self):
return TIER1S_PATH_PATTERN + "%s/segments/"
@property
def path_ids(self):
return ('tenant', 'tier1_id', 'segment_id')
def path_defs(self):
return (TenantDef, Tier1Def)
class SegmentDef(BaseSegmentDef):
'''These segments don't belong to particular tier1.
And can be attached and re-attached to different tier1s
'''
@property
def path_pattern(self):
return SEGMENTS_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'segment_id')
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(SegmentDef, self).get_obj_dict()
if self.has_attr('tier1_id'):
path = ""
if self.get_attr('tier1_id'):
tier1 = Tier1Def(tier1_id=self.get_attr('tier1_id'),
tenant=self.get_tenant())
path = tier1.get_resource_full_path()
self._set_attr_if_specified(body, 'tier1_id',
body_attr='connectivity_path',
value=path)
if self.has_attr('tier0_id'):
path = ""
if self.get_attr('tier0_id'):
tier0 = Tier0Def(tier0_id=self.get_attr('tier0_id'),
tenant=self.get_tenant())
path = tier0.get_resource_full_path()
self._set_attr_if_specified(body, 'tier0_id',
body_attr='connectivity_path',
value=path)
if self.has_attr('transport_zone_id'):
path = ""
if self.get_attr('transport_zone_id'):
tz = TransportZoneDef(
tz_id=self.get_attr('transport_zone_id'),
ep_id=constants.DEFAULT_ENFORCEMENT_POINT,
tenant=self.get_tenant())
path = tz.get_resource_full_path()
self._set_attr_if_specified(body, 'transport_zone_id',
body_attr='transport_zone_path',
value=path)
return body
class PortAddressBinding(object):
def __init__(self, ip_address, mac_address, vlan_id=None):
self.ip_address = ip_address
self.mac_address = mac_address
self.vlan_id = vlan_id
def get_obj_dict(self):
return {'ip_address': self.ip_address,
'mac_address': self.mac_address,
'vlan_id': self.vlan_id}
class SegmentPortDef(ResourceDef):
'''Infra segment port'''
@property
def path_pattern(self):
return SEGMENTS_PATH_PATTERN + "%s/ports/"
@property
def path_ids(self):
return ('tenant', 'segment_id', 'port_id')
@staticmethod
def resource_type():
return 'SegmentPort'
def path_defs(self):
return (TenantDef, SegmentDef)
def get_obj_dict(self):
body = super(SegmentPortDef, self).get_obj_dict()
address_bindings = self.get_attr('address_bindings')
if address_bindings:
body['address_bindings'] = [binding.get_obj_dict()
for binding in address_bindings]
if self.has_attr('attachment_type') or self.has_attr('vif_id'):
if (not self.get_attr('attachment_type') and
not self.get_attr('vif_id')):
# detach operation
body['attachment'] = None
else:
attachment = {}
if self.get_attr('attachment_type'):
attachment['type'] = self.get_attr('attachment_type')
if self.get_attr('vif_id'):
attachment['id'] = self.get_attr('vif_id')
self._set_attrs_if_specified(attachment,
['context_id',
'app_id',
'traffic_tag',
'allocate_addresses'])
body['attachment'] = attachment
return body
class SegmentPortBindingMapDefBase(ResourceDef):
@property
def path_ids(self):
return ('tenant', 'segment_id', 'port_id', 'map_id')
def path_defs(self):
return (TenantDef, SegmentDef, SegmentPortDef)
class SegmentPortSecProfilesBindingMapDef(SegmentPortBindingMapDefBase):
@property
def path_pattern(self):
return (SEGMENTS_PATH_PATTERN +
"%s/ports/%s/port-security-profile-binding-maps/")
@staticmethod
def resource_type():
return 'PortSecurityProfileBindingMap'
def get_obj_dict(self):
body = super(SegmentPortSecProfilesBindingMapDef, self).get_obj_dict()
if self.has_attr('segment_security_profile_id'):
path = None
if self.get_attr('segment_security_profile_id'):
profile = SegmentSecurityProfileDef(
profile_id=self.get_attr('segment_security_profile_id'),
tenant=self.get_tenant())
path = profile.get_resource_full_path()
self._set_attr_if_specified(
body, 'segment_security_profile_id',
body_attr='segment_security_profile_path',
value=path)
if self.has_attr('spoofguard_profile_id'):
path = None
if self.get_attr('spoofguard_profile_id'):
profile = SpoofguardProfileDef(
profile_id=self.get_attr('spoofguard_profile_id'),
tenant=self.get_tenant())
path = profile.get_resource_full_path()
self._set_attr_if_specified(
body, 'spoofguard_profile_id',
body_attr='spoofguard_profile_path',
value=path)
return body
class SegmentPortDiscoveryProfilesBindingMapDef(SegmentPortBindingMapDefBase):
@property
def path_pattern(self):
return (SEGMENTS_PATH_PATTERN +
"%s/ports/%s/port-discovery-profile-binding-maps/")
@staticmethod
def resource_type():
return 'PortDiscoveryProfileBindingMap'
def get_obj_dict(self):
body = super(SegmentPortDiscoveryProfilesBindingMapDef,
self).get_obj_dict()
if self.has_attr('mac_discovery_profile_id'):
path = None
if self.get_attr('mac_discovery_profile_id'):
profile = MacDiscoveryProfileDef(
profile_id=self.get_attr('mac_discovery_profile_id'),
tenant=self.get_tenant())
path = profile.get_resource_full_path()
self._set_attr_if_specified(
body, 'mac_discovery_profile_id',
body_attr='mac_discovery_profile_path',
value=path)
if self.has_attr('ip_discovery_profile_id'):
path = None
if self.get_attr('ip_discovery_profile_id'):
profile = IpDiscoveryProfileDef(
profile_id=self.get_attr('ip_discovery_profile_id'),
tenant=self.get_tenant())
path = profile.get_resource_full_path()
self._set_attr_if_specified(
body, 'ip_discovery_profile_id',
body_attr='ip_discovery_profile_path',
value=path)
return body
class SegmentPortQoSProfilesBindingMapDef(SegmentPortBindingMapDefBase):
@property
def path_pattern(self):
return (SEGMENTS_PATH_PATTERN +
"%s/ports/%s/port-qos-profile-binding-maps/")
@staticmethod
def resource_type():
return 'PortQoSProfileBindingMap'
def get_obj_dict(self):
body = super(SegmentPortQoSProfilesBindingMapDef,
self).get_obj_dict()
if self.has_attr('qos_profile_id'):
path = None
if self.get_attr('qos_profile_id'):
profile = QosProfileDef(
profile_id=self.get_attr('qos_profile_id'),
tenant=self.get_tenant())
path = profile.get_resource_full_path()
self._set_attr_if_specified(
body, 'qos_profile_id',
body_attr='qos_profile_path',
value=path)
return body
class Tier1SegmentPortDef(SegmentPortDef):
'''Tier1 segment port'''
@property
def path_pattern(self):
return TIER1S_PATH_PATTERN + "%s/segments/%s/ports/"
@property
def path_ids(self):
return ('tenant', 'tier1_id', 'segment_id', 'port_id')
def path_defs(self):
return (TenantDef, Tier1Def, SegmentDef)
class IpBlockDef(ResourceDef):
'''Infra IpBlock'''
@property
def path_pattern(self):
return IP_BLOCKS_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'ip_block_id')
@staticmethod
def resource_type():
return 'IpAddressBlock'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(IpBlockDef, self).get_obj_dict()
self._set_attr_if_specified(body, 'cidr')
return body
class IpPoolDef(ResourceDef):
'''Infra IpPool'''
@property
def path_pattern(self):
return IP_POOLS_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'ip_pool_id')
@staticmethod
def resource_type():
return 'IpAddressPool'
def path_defs(self):
return (TenantDef,)
class IpPoolAllocationDef(ResourceDef):
'''Infra IpPoolAllocation'''
@property
def path_pattern(self):
return IP_POOLS_PATH_PATTERN + "%s/ip-allocations/"
@property
def path_ids(self):
return ('tenant', 'ip_pool_id', 'ip_allocation_id')
@staticmethod
def resource_type():
return 'IpAddressAllocation'
def path_defs(self):
return (TenantDef, IpPoolDef)
def get_obj_dict(self):
body = super(IpPoolAllocationDef, self).get_obj_dict()
self._set_attr_if_specified(body, 'allocation_ip')
return body
class IpPoolSubnetDef(ResourceDef):
'''Infra IpPool Subnet'''
@property
def path_pattern(self):
return IP_POOLS_PATH_PATTERN + "%s/ip-subnets/"
@property
def path_ids(self):
return ('tenant', 'ip_pool_id', 'ip_subnet_id')
@classmethod
def resource_class(cls):
return 'IpAddressPoolSubnet'
def path_defs(self):
return (TenantDef, IpPoolDef)
class IpPoolBlockSubnetDef(IpPoolSubnetDef):
'''Infra IpPoolSubnet belonging to IpBlock'''
@staticmethod
def resource_type():
return 'IpAddressPoolBlockSubnet'
def get_obj_dict(self):
body = super(IpPoolBlockSubnetDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['auto_assign_gateway', 'size'])
if self.has_attr('ip_block_id'):
# Format the IP Block ID to its path
ip_block_id = self.get_attr('ip_block_id')
ip_block_def = IpBlockDef(ip_block_id=ip_block_id,
tenant=self.get_tenant())
ip_block_path = ip_block_def.get_resource_full_path()
self._set_attr_if_specified(
body, 'ip_block_id', body_attr='ip_block_path',
value=ip_block_path)
return body
class IpPoolStaticSubnetDef(IpPoolSubnetDef):
'''Infra IpPool static subnet'''
@staticmethod
def resource_type():
return 'IpAddressPoolStaticSubnet'
def get_obj_dict(self):
body = super(IpPoolStaticSubnetDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['cidr',
'allocation_ranges',
'gateway_ip'])
return body
class Condition(object):
def __init__(self, value, key=constants.CONDITION_KEY_TAG,
member_type=constants.CONDITION_MEMBER_PORT,
operator=constants.CONDITION_OP_EQUALS):
self.value = value
self.key = key
self.member_type = member_type
self.operator = operator
def get_obj_dict(self):
return {'resource_type': 'Condition',
'member_type': self.member_type,
'key': self.key,
'value': self.value,
'operator': self.operator}
class IPAddressExpression(object):
def __init__(self, ip_addresses):
self.ip_addresses = ip_addresses
def get_obj_dict(self):
return {'resource_type': 'IPAddressExpression',
'ip_addresses': self.ip_addresses}
class PathExpression(object):
def __init__(self, paths):
self.paths = paths
def get_obj_dict(self):
return {'resource_type': 'PathExpression',
'paths': self.paths}
class ConjunctionOperator(object):
def __init__(self, operator=constants.CONDITION_OP_AND):
self.operator = operator
def get_obj_dict(self):
return {'resource_type': 'ConjunctionOperator',
'conjunction_operator': self.operator}
class NestedExpression(object):
def __init__(self, expressions=None):
self.expressions = expressions or []
def get_obj_dict(self):
return {'resource_type': 'NestedExpression',
'expressions': [ex.get_obj_dict() for ex in self.expressions]}
class GroupDef(ResourceDef):
@property
def path_pattern(self):
return DOMAINS_PATH_PATTERN + "%s/groups/"
@property
def path_ids(self):
return ('tenant', 'domain_id', 'group_id')
@staticmethod
def resource_type():
return 'Group'
def path_defs(self):
return (TenantDef, DomainDef)
def get_obj_dict(self):
body = super(GroupDef, self).get_obj_dict()
conds = self.get_attr('conditions')
if conds:
conds = conds if isinstance(conds, list) else [conds]
if conds:
body['expression'] = [condition.get_obj_dict()
for condition in conds]
return body
class ServiceDef(ResourceDef):
def __init__(self, **kwargs):
super(ServiceDef, self).__init__(**kwargs)
self.service_entries = []
@property
def path_pattern(self):
return SERVICES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'service_id')
@staticmethod
def resource_type():
return 'Service'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(ServiceDef, self).get_obj_dict()
entries = [entry.get_obj_dict()
for entry in self.service_entries]
if entries:
body['service_entries'] = entries
return body
@staticmethod
def sub_entries_path():
return ServiceEntryDef().get_last_section_dict_key
class ServiceEntryDef(ResourceDef):
@property
def path_pattern(self):
return SERVICES_PATH_PATTERN + "%s/service-entries/"
@property
def path_ids(self):
return ('tenant', 'service_id', 'entry_id')
def path_defs(self):
return (TenantDef, ServiceDef)
@classmethod
def resource_class(cls):
return 'ServiceEntry'
class L4ServiceEntryDef(ServiceEntryDef):
@staticmethod
def resource_type():
return 'L4PortSetServiceEntry'
def get_obj_dict(self):
body = super(L4ServiceEntryDef, self).get_obj_dict()
self._set_attr_if_specified(body, 'protocol', 'l4_protocol')
self._set_attr_if_specified(body, 'dest_ports', 'destination_ports')
self._set_attr_if_specified(body, 'source_ports', 'source_ports')
return body
class IcmpServiceEntryDef(ServiceEntryDef):
@staticmethod
def resource_type():
return 'ICMPTypeServiceEntry'
def get_obj_dict(self):
body = super(IcmpServiceEntryDef, self).get_obj_dict()
if self.get_attr('version'):
body['protocol'] = 'ICMPv' + str(self.get_attr('version'))
for attr in ('icmp_type', 'icmp_code'):
# Note that icmp_type and icmp_code could be 0.
if self.get_attr(attr) is not None:
body[attr] = self.get_attr(attr)
return body
class IPProtocolServiceEntryDef(ServiceEntryDef):
@staticmethod
def resource_type():
return 'IPProtocolServiceEntry'
def get_obj_dict(self):
body = super(IPProtocolServiceEntryDef, self).get_obj_dict()
if self.get_attr('protocol_number') is not None:
# Note that protocol_number could be 0.
body['protocol_number'] = self.get_attr('protocol_number')
return body
class SecurityPolicyBaseDef(ResourceDef):
@property
def path_ids(self):
return ('tenant', 'domain_id', 'map_id')
def path_defs(self):
return (TenantDef, DomainDef)
def get_obj_dict(self):
body = super(SecurityPolicyBaseDef, self).get_obj_dict()
self._set_attr_if_specified(body, 'category')
if self.has_attr('map_sequence_number'):
seq_number = self.get_attr('map_sequence_number')
self._set_attr_if_specified(body, 'map_sequence_number',
body_attr='sequence_number',
value=seq_number)
return body
class CommunicationMapDef(SecurityPolicyBaseDef):
"""AKA security policy"""
@property
def path_pattern(self):
return (DOMAINS_PATH_PATTERN + "%s/security-policies/")
@staticmethod
def resource_type():
return 'SecurityPolicy'
@staticmethod
def sub_entries_path():
return CommunicationMapEntryDef().get_last_section_dict_key
class GatewayPolicyDef(SecurityPolicyBaseDef):
@property
def path_pattern(self):
return (DOMAINS_PATH_PATTERN + "%s/gateway-policies/")
@staticmethod
def resource_type():
return 'GatewayPolicy'
@staticmethod
def sub_entries_path():
return GatewayPolicyRuleDef().get_last_section_dict_key
class SecurityPolicyRuleBaseDef(ResourceDef):
def get_groups_path(self, domain_id, group_ids):
if not group_ids:
return [constants.ANY_GROUP]
return [GroupDef(domain_id=domain_id,
group_id=group_id,
tenant=self.get_tenant()).get_resource_full_path()
for group_id in group_ids]
def get_service_path(self, service_id):
return ServiceDef(
service_id=service_id,
tenant=self.get_tenant()).get_resource_full_path()
def get_services_path(self, service_ids):
if service_ids:
return [self.get_service_path(service_id)
for service_id in service_ids]
return [constants.ANY_SERVICE]
@property
def path_ids(self):
return ('tenant', 'domain_id', 'map_id', 'entry_id')
@staticmethod
def resource_type():
return 'Rule'
def get_obj_dict(self):
body = super(SecurityPolicyRuleBaseDef, self).get_obj_dict()
domain_id = self.get_attr('domain_id')
if self.has_attr('source_groups'):
body['source_groups'] = self.get_groups_path(
domain_id, self.get_attr('source_groups'))
if self.has_attr('dest_groups'):
body['destination_groups'] = self.get_groups_path(
domain_id, self.get_attr('dest_groups'))
self._set_attrs_if_specified(body, ['sequence_number', 'scope',
'action', 'direction', 'logged',
'ip_protocol'])
if self.has_attr('service_ids'):
service_ids = self.get_attr('service_ids')
body['services'] = self.get_services_path(service_ids)
return body
class CommunicationMapEntryDef(SecurityPolicyRuleBaseDef):
@property
def path_pattern(self):
return (DOMAINS_PATH_PATTERN +
"%s/security-policies/%s/rules/")
def path_defs(self):
return (TenantDef, DomainDef, CommunicationMapDef)
class GatewayPolicyRuleDef(SecurityPolicyRuleBaseDef):
@property
def path_pattern(self):
return (DOMAINS_PATH_PATTERN +
"%s/gateway-policies/%s/rules/")
def path_defs(self):
return (TenantDef, DomainDef, GatewayPolicyDef)
# Currently supports only NSXT
class EnforcementPointDef(ResourceDef):
@property
def path_pattern(self):
return ENFORCEMENT_POINT_PATTERN
@property
def path_ids(self):
return ('tenant', 'ep_id')
@staticmethod
def resource_type():
return 'EnforcementPoint'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(EnforcementPointDef, self).get_obj_dict()
body['id'] = self.get_id()
if 'connection_info' not in body:
body['connection_info'] = {'resource_type': 'NSXTConnectionInfo'}
info = body['connection_info']
self._set_attrs_if_specified(info,
['thumbprint', 'username', 'password',
'ip_address'])
if self.get_attr('ip_address'):
info['enforcement_point_address'] = self.get_attr('ip_address')
if self.get_attr('edge_cluster_id'):
body['connection_info']['edge_cluster_ids'] = [
self.get_attr('edge_cluster_id')]
if self.get_attr('transport_zone_id'):
body['connection_info']['transport_zone_ids'] = [
self.get_attr('transport_zone_id')]
return body
class TransportZoneDef(ResourceDef):
@property
def path_pattern(self):
return TRANSPORT_ZONE_PATTERN
@property
def path_ids(self):
return ('tenant', 'ep_id', 'tz_id')
@staticmethod
def resource_type():
return 'PolicyTransportZone'
@staticmethod
def resource_use_cache():
return True
class EdgeClusterDef(ResourceDef):
@property
def path_pattern(self):
return EDGE_CLUSTER_PATTERN
@property
def path_ids(self):
return ('tenant', 'ep_id', 'ec_id')
@staticmethod
def resource_type():
return 'PolicyEdgeCluster'
@staticmethod
def resource_use_cache():
return True
# Currently assumes one deployment point per id
class DeploymentMapDef(ResourceDef):
@property
def path_pattern(self):
return (DOMAINS_PATH_PATTERN + '%s/domain-deployment-maps/')
@property
def path_ids(self):
return ('tenant', 'domain_id', 'map_id')
@staticmethod
def resource_type():
return 'DeploymentMap'
def path_defs(self):
return (TenantDef, DomainDef)
def get_obj_dict(self):
body = super(DeploymentMapDef, self).get_obj_dict()
body['id'] = self.get_id()
ep_id = self.get_attr('ep_id')
tenant = self.get_tenant()
body['enforcement_point_path'] = EnforcementPointDef(
ep_id=ep_id,
tenant=tenant).get_resource_full_path() if ep_id else None
return body
class SegmentSecurityProfileDef(ResourceDef):
DEFAULT_PROFILE = 'default-segment-security-profile'
@property
def path_pattern(self):
return SEGMENT_SECURITY_PROFILES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'profile_id')
@staticmethod
def resource_type():
return 'SegmentSecurityProfile'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(SegmentSecurityProfileDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['bpdu_filter_enable',
'dhcp_client_block_enabled',
'dhcp_client_block_v6_enabled',
'dhcp_server_block_enabled',
'dhcp_server_block_v6_enabled',
'non_ip_traffic_block_enabled',
'ra_guard_enabled',
'rate_limits_enabled'])
return body
class QoSObjectBase(object):
keys = []
def __init__(self, **kwargs):
self.attrs = kwargs
def get_obj_dict(self):
obj_dict = {}
for key in self.attrs:
if key in self.keys:
obj_dict[key] = self.attrs[key]
return obj_dict
class QoSRateLimiter(QoSObjectBase):
INGRESS_RATE_LIMITER_TYPE = 'IngressRateLimiter'
EGRESS_RATE_LIMITER_TYPE = 'EgressRateLimiter'
INGRESS_BRD_RATE_LIMITER_TYPE = 'IngressBroadcastRateLimiter'
keys = ['resource_type',
'average_bandwidth', # Mb/s
'peak_bandwidth', # Mb/s
'burst_size', # byes
'enabled'
]
class QoSDscp(QoSObjectBase):
QOS_DSCP_TRUSTED = 'TRUSTED'
QOS_DSCP_UNTRUSTED = 'UNTRUSTED'
keys = ['mode', 'priority']
class QosProfileDef(ResourceDef):
@property
def path_pattern(self):
return QOS_PROFILES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'profile_id')
@staticmethod
def resource_type():
return 'QoSProfile'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(QosProfileDef, self).get_obj_dict()
self._set_attr_if_specified(body, 'class_of_service')
if self.has_attr('dscp'):
value = None
if self.get_attr('dscp'):
value = self.get_attr('dscp').get_obj_dict()
self._set_attr_if_specified(body, 'dscp', value=value)
if self.has_attr('shaper_configurations'):
value = None
if self.get_attr('shaper_configurations'):
value = [s.get_obj_dict()
for s in self.get_attr('shaper_configurations')]
self._set_attr_if_specified(body, 'shaper_configurations',
value=value)
return body
class SpoofguardProfileDef(ResourceDef):
DEFAULT_PROFILE = 'default-spoofguard-profile'
@property
def path_pattern(self):
return SPOOFGUARD_PROFILES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'profile_id')
@staticmethod
def resource_type():
return 'SpoofGuardProfile'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(SpoofguardProfileDef, self).get_obj_dict()
# TODO(asarfaty): add all attributes here
self._set_attr_if_specified(body, 'address_binding_whitelist')
return body
class IpDiscoveryProfileDef(ResourceDef):
DEFAULT_PROFILE = 'default-ip-discovery-profile'
@property
def path_pattern(self):
return IP_DISCOVERY_PROFILES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'profile_id')
@staticmethod
def resource_type():
return 'IPDiscoveryProfile'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(IpDiscoveryProfileDef, self).get_obj_dict()
# TODO(asarfaty): add all attributes here. currently used for read only
return body
class MacDiscoveryProfileDef(ResourceDef):
DEFAULT_PROFILE = 'default-mac-discovery-profile'
@property
def path_pattern(self):
return MAC_DISCOVERY_PROFILES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'profile_id')
@staticmethod
def resource_type():
return 'MacDiscoveryProfile'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(MacDiscoveryProfileDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['mac_change_enabled',
'mac_learning_enabled',
'unknown_unicast_flooding_enabled',
'mac_limit_policy', 'mac_limit'])
return body
class Ipv6NdraProfileDef(ResourceDef):
@property
def path_pattern(self):
return IPV6_NDRA_PROFILES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'profile_id')
@staticmethod
def resource_type():
return 'Ipv6NdraProfile'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(Ipv6NdraProfileDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['ra_mode',
'reachable_timer',
'retransmit_interval'])
# Use default settings for dns and RA for now
# TODO(annak): expose when required
body['dns_config'] = {}
body['ra_config'] = {}
return body
class DhcpRelayConfigDef(ResourceDef):
@property
def path_pattern(self):
return DHCP_REALY_PATTERN
@property
def path_ids(self):
return ('tenant', 'config_id')
@staticmethod
def resource_type():
return 'DhcpRelayConfig'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(DhcpRelayConfigDef, self).get_obj_dict()
self._set_attr_if_specified(body, 'server_addresses')
return body
class WAFProfileDef(ResourceDef):
@property
def path_pattern(self):
return WAF_PROFILES_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'profile_id')
@staticmethod
def resource_type():
return 'WAFProfile'
def path_defs(self):
return (TenantDef,)
def get_obj_dict(self):
body = super(WAFProfileDef, self).get_obj_dict()
# TODO(asarfaty): add all attributes here.
# Currently used for read only
return body
class CertificateDef(ResourceDef):
@property
def path_pattern(self):
return CERTIFICATE_PATH_PATTERN
@property
def path_ids(self):
return ('tenant', 'certificate_id')
@staticmethod
def resource_type():
return "TlsTrustData"
def get_obj_dict(self):
body = super(CertificateDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['pem_encoded', 'key_algo',
'private_key', 'passphrase'])
return body
class ExcludeListDef(ResourceDef):
@property
def path_pattern(self):
return EXCLUDE_LIST_PATH_PATTERN
@property
def path_ids(self):
# Adding dummy 2nd key to satisfy get_section_path
# This resource has no keys, since it is a single object
return ('tenant', 'Dummy')
@staticmethod
def resource_type():
return "PolicyExcludeList"
def get_obj_dict(self):
body = super(ExcludeListDef, self).get_obj_dict()
self._set_attr_if_specified(body, 'members')
return body
class NsxPolicyApi(object):
def __init__(self, client):
self.client = client
self.cache = utils.NsxLibCache(utils.DEFAULT_CACHE_AGE_SEC)
self.partial_updates = True
def disable_partial_updates(self):
self.partial_updates = False
def partial_updates_supported(self):
return self.partial_updates
def create_or_update(self, resource_def):
"""Create or update a policy object.
This api will update an existing object, or create a new one if it
doesn't exist.
The policy API supports PATCH for create/update operations
"""
path = resource_def.get_resource_path()
if resource_def.resource_use_cache():
self.cache.remove(path)
body = resource_def.get_obj_dict()
self.client.patch(path, body)
def create_with_parent(self, parent_def, resource_def):
path = parent_def.get_resource_path()
body = parent_def.get_obj_dict()
if isinstance(resource_def, list):
child_dict_key = resource_def[0].get_last_section_dict_key
body[child_dict_key] = [r.get_obj_dict() for r in resource_def]
else:
child_dict_key = resource_def.get_last_section_dict_key
body[child_dict_key] = [resource_def.get_obj_dict()]
self.client.patch(path, body)
def delete(self, resource_def):
path = resource_def.get_resource_path()
if resource_def.resource_use_cache():
self.cache.remove(path)
self.client.delete(path)
def get(self, resource_def, silent=False):
path = resource_def.get_resource_path()
if resource_def.resource_use_cache():
# try to get it from the cache
result = self.cache.get(path)
if result:
return result
# call the client
result = self.client.get(path, silent=silent)
if resource_def.resource_use_cache():
# add the result to the cache
self.cache.update(path, result)
return result
def list(self, resource_def, silent=False):
path = resource_def.get_section_path()
return self.client.list(path, silent=silent)
def get_realized_entities(self, path, silent=False):
return self.client.list(REALIZATION_PATH % path,
silent=silent)['results']
def get_realized_entity(self, path, silent=False):
# Return first realization entity if exists
# Useful for resources with single realization entity
entities = self.get_realized_entities(path, silent=silent)
if entities:
return entities[0]
def get_realized_state(self, path, silent=False):
entity = self.get_realized_entity(path, silent=silent)
if entity:
return entity['state']