From c7e77a7387e9aa115aa84a28d6c3f4e9eab03fa7 Mon Sep 17 00:00:00 2001 From: Qin Zhao Date: Thu, 27 Feb 2020 16:56:20 +0800 Subject: [PATCH 1/6] Support bandwidth control of listener --- .../lbaasv2/drivers/bigip/lbaas_builder.py | 33 ++++++++++++++++++- .../lbaasv2/drivers/bigip/service_adapter.py | 4 +++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/lbaas_builder.py b/f5_openstack_agent/lbaasv2/drivers/bigip/lbaas_builder.py index 1a21845c0..f21fea148 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/lbaas_builder.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/lbaas_builder.py @@ -55,6 +55,9 @@ def init_esd(self, esd): def is_esd(self, esd): return self.esd.is_esd(esd) + def is_bwc(self, desc): + return desc.startswith("vip_bwc_policy:") + def assure_service(self, service, traffic_group, all_subnet_hints): """Assure that a service is configured on the BIGIP.""" start_time = time() @@ -441,6 +444,33 @@ def _assure_l7policies_created(self, service): for l7policy in l7policies: LOG.debug("L7 debug: assuring policy: %s", l7policy) name = l7policy.get('name', None) + + desc = l7policy.get('description', None) + position = l7policy.get('position', 1) + if self.is_bwc(desc) and 'listeners' in service: + # Parse bwc policy name from L7 policy description + bwc = desc[15:] + for listener in service['listeners']: + if listener['id'] == l7policy['listener_id']: + policy_state = l7policy.get('provisioning_status') + bwc_position = listener.get('bwcPosition', None) + if policy_state == "PENDING_CREATE" or \ + policy_state == "ACTIVE": + # Only bwc l7 policy with the highest priority + # can take effect. Skip rest of bwc l7 policies + # of the same listener + if not bwc_position or bwc_position > position: + LOG.debug('Add bwc policy %s to listener %s', + bwc, listener['id']) + listener['bwcPosition'] = position + listener['bwcPolicy'] = "/Common/" + bwc + if policy_state == "PENDING_DELETE" and \ + 'bwcPolicy' not in listener: + LOG.debug('Remove bwc policy %s from listener %s', + bwc, listener['id']) + listener['bwcPolicy'] = None + continue + if not self.esd.is_esd(name): listener_id = l7policy.get('listener_id', None) if not listener_id or listener_id in listener_policy_map: @@ -488,7 +518,8 @@ def _assure_l7policies_deleted(self, service): l7policies = service['l7policies'] for l7policy in l7policies: name = l7policy.get('name', None) - if not self.esd.is_esd(name): + desc = l7policy.get('description', None) + if not self.esd.is_esd(name) and not self.is_bwc(desc): listener_id = l7policy.get('listener_id', None) if not listener_id or listener_id in listener_policy_map: continue diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py index 408345c36..aad1b11c8 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py @@ -480,6 +480,10 @@ def _map_virtual(self, loadbalancer, listener, self._add_vlan_and_snat(listener, vip) self._add_profiles_session_persistence(listener, pool, vip) + # Set bandwidth contoller + if 'bwcPolicy' in listener: + vip['bwcPolicy'] = listener.get("bwcPolicy") + existed_irules = vip.get('rules', []) irules += existed_irules vip['rules'] = irules From cd5a37acfe7629ed7e7f46f656ebe4b0b5102a9b Mon Sep 17 00:00:00 2001 From: zhangshengping Date: Fri, 13 Mar 2020 06:10:33 -0700 Subject: [PATCH 2/6] In order to support TCP IP transmission. CAUTION: TCP listener can configure http profile, but if it is configured with http profile, it may not work well with ssh protocol, since it will check the upper 7 layer info, then ssh connection will be block by http profile. fastl4 type TCP listener can not configure server or client protocol, it will throw error. Command info: SE give suggestion to configuration ESD like below: "insert_sip_tcp-option": { "lbaas_stcp": "tcp_option_28", "lbaas_irule": ["insert-clientIP-TCP-option"] } we have to change TCP to standard type to use lbaas_stcp tag (Protocol profile (Sever)) by using --description options. Command lines: neutron lbaas-listener-create --name tcp-standard --loadbalancer source-ip-lb --protocol TCP --protocol-port 24 --description "{'TCP-type':'standard'}" neutron lbaas-l7policy-create --listener tcp-standard --name insert_sip_tcp-option --action REJECT CAUTION: update description is not allow. Conflicts: f5_openstack_agent/lbaasv2/drivers/bigip/selfips.py --- .../lbaasv2/drivers/bigip/selfips.py | 9 ++- .../lbaasv2/drivers/bigip/service_adapter.py | 76 +++++++++++++++++-- .../bigip/test/test_service_adapter.py | 21 +++-- 3 files changed, 93 insertions(+), 13 deletions(-) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/selfips.py b/f5_openstack_agent/lbaasv2/drivers/bigip/selfips.py index c17c3a0d8..367958479 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/selfips.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/selfips.py @@ -71,8 +71,13 @@ def _create_bigip_selfip(self, bigip, model): err.message)) raise f5_ex.SelfIPCreationException("selfip") else: - LOG.exception("selfip creation error: %s(%s)" % - (err.message, err.response.status_code)) + # here may post 400 + LOG.error("selfip creation error message: %s" % + err.message) + LOG.error("selfip creation error status: %s" % + err.reponse.status_code) + LOG.error("selfip creation error text: %s" % + err.response.text) raise except Exception as err: LOG.error("Failed to create selfip") diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py index aad1b11c8..c70f24001 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py @@ -14,6 +14,8 @@ # limitations under the License. # +import ast + import hashlib import netaddr @@ -512,9 +514,12 @@ def _apply_l7_and_esd_policies(self, listener, policies, vip): policy_name = policy.get('name', None) esd = self.esd.get_esd(policy_name) - if esd: + + if isinstance(esd, dict): esd_composite.update(esd) + # TCP with source ip transmission will not go to + # apply_fastl4_esd logical. if listener['protocol'] == 'TCP': self._apply_fastl4_esd(vip, esd_composite) else: @@ -529,6 +534,26 @@ def get_esd(self, name): def is_esd(self, name): return self.esd.get_esd(name) is not None + def parse_descript_opts(self, listener): + extra_opts = {} + listener_id = listener.get('id') + descript = listener.get('description') + + if descript: + try: + extra_opts = ast.literal_eval(descript) + LOG.debug("listener %s get extra descript options %s" % + (listener_id, descript)) + except Exception as exc: + LOG.error("listener id: %s can not parse extra options %s" % + (listener_id, descript)) + LOG.error("exception is %s" % exc) + LOG.error( + "CAUTION: listener will show on neutron lbaas " + + "table, BUT it will not be configure on BIGIP device") + raise exc + return extra_opts + def _add_profiles_session_persistence(self, listener, pool, vip): protocol = listener.get('protocol', "") @@ -547,10 +572,24 @@ def _add_profiles_session_persistence(self, listener, pool, vip): else: virtual_type = 'standard' + extra_options = self.parse_descript_opts(listener) + + # according to the extra_options, + # some vip profile will be overwrite + if extra_options: + tcp_type = extra_options.get('TCP-type') + # add this for TCP source ip transparent + if tcp_type == 'standard' and protocol == 'TCP': + virtual_type = 'standard' + if virtual_type == 'fastl4': vip['profiles'] = ['/Common/fastL4'] + # standard type TCP listener canot have http and oneconnect profile + # or ssh lb members will be diconnected + elif virtual_type == 'standard' and protocol == 'TCP': + vip['profiles'] = [] else: - # add profiles for HTTP, HTTPS, TERMINATED_HTTPS protocols + # add profiles for HTTP, TERMINATED_HTTPS protocols vip['profiles'] = ['/Common/http', '/Common/oneconnect'] vip['fallbackPersistence'] = '' @@ -681,10 +720,35 @@ def _apply_fastl4_esd(self, vip, esd): # Application of ESD implies some type of L7 traffic routing. Add # an HTTP profile. if 'lbaas_http_profile' in esd: - vip['profiles'] = ["/Common/" + esd['lbaas_http_profile'], - "/Common/fastL4"] - else: - vip['profiles'] = ["/Common/http", "/Common/fastL4"] + if "/Common/fastL4" in vip['profiles']: + vip['profiles'] = ["/Common/" + esd['lbaas_http_profile'], + "/Common/fastL4"] + else: + vip['profiles'] = ["/Common/" + esd['lbaas_http_profile']] + + # fastL4 listener cannot configure server and client + # protocol, or it will throw errors + # start with server tcp profile + if "/Common/fastL4" not in vip['profiles']: + if 'lbaas_stcp' in esd: + # set serverside tcp profile + vip['profiles'].append({'name': esd['lbaas_stcp'], + 'partition': 'Common', + 'context': 'serverside'}) + # restrict client profile + ctcp_context = 'clientside' + else: + # no serverside profile; use client profile for both + ctcp_context = 'all' + + # must define client profile; default to tcp if not in ESD + if 'lbaas_ctcp' in esd: + ctcp_profile = esd['lbaas_ctcp'] + else: + ctcp_profile = 'tcp' + vip['profiles'].append({'name': ctcp_profile, + 'partition': 'Common', + 'context': ctcp_context}) # persistence if 'lbaas_persist' in esd: diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/test/test_service_adapter.py b/f5_openstack_agent/lbaasv2/drivers/bigip/test/test_service_adapter.py index d3f638797..05a6d79ca 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/test/test_service_adapter.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/test/test_service_adapter.py @@ -1155,7 +1155,9 @@ def test_apply_l4_esd_persist_profile(adapter): assert "policies" not in vip assert vip['persist'] == [dict(name="hash")] - assert vip['profiles'] == ["/Common/http", "/Common/fastL4"] + assert vip['profiles'] == [{'partition': 'Common', + 'context': 'all', + 'name': 'tcp'}] def test_apply_l4_esd_persist_profile_collision(adapter): adapter = ServiceModelAdapter(mock.MagicMock()) @@ -1169,7 +1171,9 @@ def test_apply_l4_esd_persist_profile_collision(adapter): assert "policies" not in vip assert vip['persist'] == [dict(name="hash")] - assert vip['profiles'] == ["/Common/http", "/Common/fastL4"] + assert vip['profiles'] == [{'partition': 'Common', + 'context': 'all', + 'name': 'tcp'}] def test_apply_l4_esd_fallback_persist_profile(adapter): adapter = ServiceModelAdapter(mock.MagicMock()) @@ -1184,7 +1188,9 @@ def test_apply_l4_esd_fallback_persist_profile(adapter): assert vip['persist'] == [dict(name="sourceip")] assert vip['fallbackPersistence'] == 'hash' - assert vip['profiles'] == ["/Common/http", "/Common/fastL4"] + assert vip['profiles'] == [{'partition': 'Common', + 'context': 'all', + 'name': 'tcp'}] def test_apply_l4_esd_fallback_persist_profile_collision(adapter): adapter = ServiceModelAdapter(mock.MagicMock()) @@ -1199,7 +1205,9 @@ def test_apply_l4_esd_fallback_persist_profile_collision(adapter): assert vip['persist'] == [dict(name="sourceip")] assert vip['fallbackPersistence'] == 'hash' - assert vip['profiles'] == ["/Common/http", "/Common/fastL4"] + assert vip['profiles'] == [{'partition': 'Common', + 'context': 'all', + 'name': 'tcp'}] def test_apply_l4_esd_http_profile(adapter): adapter = ServiceModelAdapter(mock.MagicMock()) @@ -1213,7 +1221,10 @@ def test_apply_l4_esd_http_profile(adapter): assert "persist" not in vip assert "fallbackPersistence" not in vip - assert vip['profiles'] == ["/Common/http_profile", "/Common/fastL4"] + assert vip['profiles'] == ["/Common/http_profile", + {'partition': 'Common', + 'context': 'all', + 'name': 'tcp'}] def test_apply_l4_esd_fallback_persist_profile_nopersist(adapter): adapter = ServiceModelAdapter(mock.MagicMock()) From 0c37ad9838810f8f646a0ea3b0f8c59bcb71cf9c Mon Sep 17 00:00:00 2001 From: zhangshengping Date: Fri, 11 Oct 2019 01:59:18 -0700 Subject: [PATCH 3/6] enable listeners, in different providers but with the same subnet, configure with different snat pool address. Conflicts: etc/neutron/services/f5/f5-openstack-agent.ini (cherry picked from commit 059cde518429debf5878d601c2ecdb4efab4aef7) --- .../services/f5/f5-openstack-agent.ini | 12 ++++++++- .../lbaasv2/drivers/bigip/agent_manager.py | 5 ++++ .../lbaasv2/drivers/bigip/network_service.py | 9 ++++--- .../lbaasv2/drivers/bigip/snats.py | 25 +++++++++++-------- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/etc/neutron/services/f5/f5-openstack-agent.ini b/etc/neutron/services/f5/f5-openstack-agent.ini index 5c8e02321..2c9796e42 100644 --- a/etc/neutron/services/f5/f5-openstack-agent.ini +++ b/etc/neutron/services/f5/f5-openstack-agent.ini @@ -34,7 +34,8 @@ # [DEFAULT] # Show debugging output in log (sets DEBUG log level output). -debug = True +debug = False + # The LBaaS agent will resync its state with Neutron to recover from any # transient notification or rpc errors. The interval is number of # seconds between attempts. @@ -433,6 +434,15 @@ f5_snat_mode = True # f5_global_routed_mode = True. # f5_snat_addresses_per_subnet = 1 + +# Currently this provider_name use to distinguish different +# snat pool addresses for different listeners which +# are in different providers and within the same subnet. +# (Note) +# This value must be configured with different values among +# different neutron lbaasv2 providers. +provider_name = 'f5networks' + # # This setting will cause all networks to be # defined under the common partition on the diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py index 11bf531ef..1cc8066ac 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py @@ -83,6 +83,11 @@ default=1, help=('Interface and VLAN for the VTEP overlay network') ), + cfg.StrOpt( + 'provider_name', + default='f5networks', + help=('provider_name for snat pool addresses') + ), cfg.StrOpt( 'agent_id', default=None, diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/network_service.py b/f5_openstack_agent/lbaasv2/drivers/bigip/network_service.py index 5a0f6e9b9..c6522289d 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/network_service.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/network_service.py @@ -595,7 +595,9 @@ def _assure_subnet_snats(self, assure_bigips, service, subnetinfo): subnet['id']) if len(assure_bigips): snat_addrs = self.bigip_snat_manager.get_snat_addrs( - subnetinfo, tenant_id, snats_per_subnet, lb_id) + subnetinfo, tenant_id, snats_per_subnet, lb_id, + self.conf.provider_name + ) if len(snat_addrs) != snats_per_subnet: raise f5_ex.SNATCreationException( @@ -604,7 +606,8 @@ def _assure_subnet_snats(self, assure_bigips, service, subnetinfo): (snats_per_subnet, len(snat_addrs))) for assure_bigip in assure_bigips: self.bigip_snat_manager.assure_bigip_snats( - assure_bigip, subnetinfo, snat_addrs, tenant_id) + assure_bigip, subnetinfo, snat_addrs, + tenant_id, self.conf.provider_name) def _allocate_gw_addr(self, subnetinfo): # Create a name for the port and for the IP Forwarding @@ -748,7 +751,7 @@ def _assure_delete_nets_shared(self, bigip, service, subnet_hints): deleted_names.add(gw_name) my_deleted_names, my_in_use_subnets = \ self.bigip_snat_manager.delete_bigip_snats( - bigip, subnetinfo, tenant_id) + bigip, subnetinfo, tenant_id, self.conf.provider_name) deleted_names = deleted_names.union(my_deleted_names) for in_use_subnetid in my_in_use_subnets: subnet_hints['check_for_delete_subnets'].pop( diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/snats.py b/f5_openstack_agent/lbaasv2/drivers/bigip/snats.py index e4ecf300c..359237e80 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/snats.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/snats.py @@ -65,7 +65,8 @@ def _get_snat_traffic_group(self, tenant_id): LOG.error('Invalid f5_ha_type:%s' % self.driver.conf.f5_ha_type) return '' - def get_snat_addrs(self, subnetinfo, tenant_id, snat_count, lb_id): + def get_snat_addrs(self, subnetinfo, tenant_id, snat_count, lb_id, + provider_name): # Get the ip addresses for snat """ if self.driver.conf.unlegacy_setting_placeholder: LOG.debug('setting vnic_type to normal instead of baremetal') @@ -79,7 +80,7 @@ def get_snat_addrs(self, subnetinfo, tenant_id, snat_count, lb_id): snat_name = self._get_snat_name(subnet, tenant_id) for i in range(snat_count): ip_address = "" - index_snat_name = snat_name + "_" + str(i) + index_snat_name = snat_name + "_" + provider_name + "_" + str(i) ports = self.driver.plugin_rpc.get_port_by_name( port_name=index_snat_name) if len(ports) > 0: @@ -106,7 +107,8 @@ def get_snat_addrs(self, subnetinfo, tenant_id, snat_count, lb_id): return snat_addrs - def assure_bigip_snats(self, bigip, subnetinfo, snat_addrs, tenant_id): + def assure_bigip_snats(self, bigip, subnetinfo, snat_addrs, tenant_id, + provider_name): # Ensure Snat Addresses are configured on a bigip. # Called for every bigip only in replication mode. # otherwise called once and synced. @@ -128,9 +130,11 @@ def assure_bigip_snats(self, bigip, subnetinfo, snat_addrs, tenant_id): ) snat_info['addrs'] = snat_addrs - self._assure_bigip_snats(bigip, subnetinfo, snat_info, tenant_id) + self._assure_bigip_snats(bigip, subnetinfo, snat_info, tenant_id, + provider_name) - def _assure_bigip_snats(self, bigip, subnetinfo, snat_info, tenant_id): + def _assure_bigip_snats(self, bigip, subnetinfo, snat_info, tenant_id, + provider_name): # Configure the ip addresses for snat network = subnetinfo['network'] subnet = subnetinfo['subnet'] @@ -144,7 +148,7 @@ def _assure_bigip_snats(self, bigip, subnetinfo, snat_info, tenant_id): for i, snat_address in enumerate(snat_info['addrs']): ip_address = snat_address + \ '%' + str(network['route_domain_id']) - index_snat_name = snat_name + "_" + str(i) + index_snat_name = snat_name + "_" + provider_name + "_" + str(i) snat_traffic_group = self._get_snat_traffic_group(tenant_id) # snat.create() did the following in LBaaSv1 @@ -208,7 +212,7 @@ def _assure_bigip_snats(self, bigip, subnetinfo, snat_info, tenant_id): bigip.assured_tenant_snat_subnets[tenant_id].append(subnet['id']) - def delete_bigip_snats(self, bigip, subnetinfo, tenant_id): + def delete_bigip_snats(self, bigip, subnetinfo, tenant_id, provider_name): # Assure shared snat configuration (which syncs) is deleted. # if not subnetinfo['network']: @@ -216,7 +220,8 @@ def delete_bigip_snats(self, bigip, subnetinfo, tenant_id): 'for missing network ... skipping.') return set() - return self._delete_bigip_snats(bigip, subnetinfo, tenant_id) + return self._delete_bigip_snats(bigip, subnetinfo, tenant_id, + provider_name) def _remove_assured_tenant_snat_subnet(self, bigip, tenant_id, subnet): # Remove ref for the subnet for this tenant""" @@ -239,7 +244,7 @@ def _remove_assured_tenant_snat_subnet(self, bigip, tenant_id, subnet): 'Tenant id %s does not exist in ' 'bigip.assured_tenant_snat_subnets' % tenant_id) - def _delete_bigip_snats(self, bigip, subnetinfo, tenant_id): + def _delete_bigip_snats(self, bigip, subnetinfo, tenant_id, provider_name): # Assure snats deleted in standalone mode """ subnet = subnetinfo['subnet'] network = subnetinfo['network'] @@ -256,7 +261,7 @@ def _delete_bigip_snats(self, bigip, subnetinfo, tenant_id): # Delete SNATs on traffic-group-local-only snat_name = self._get_snat_name(subnet, tenant_id) for i in range(self.driver.conf.f5_snat_addresses_per_subnet): - index_snat_name = snat_name + "_" + str(i) + index_snat_name = snat_name + "_" + provider_name + "_" + str(i) tmos_snat_name = index_snat_name if self.l3_binding: From 8e57202fc00b4ffad08abd281b73ae4b7c69138d Mon Sep 17 00:00:00 2001 From: zhangshengping Date: Fri, 11 Oct 2019 01:59:18 -0700 Subject: [PATCH 4/6] enable listeners, in different providers but with the same subnet, configure with different snat pool address. Conflicts: etc/neutron/services/f5/f5-openstack-agent.ini (cherry picked from commit 059cde518429debf5878d601c2ecdb4efab4aef7) From 104d3f0d182bc507c8b460dee613c5f0ab9264c5 Mon Sep 17 00:00:00 2001 From: zhangshengping Date: Sat, 25 Apr 2020 08:44:31 -0700 Subject: [PATCH 5/6] add sip udp listener, diameter listener and change the way of creating transparent IP TCP standard listener Add sip udp listener: neutron lbaas-listener-create --name test-udp-sip \\ --loadbalancer test-lb \\ --protocol TCP \\ --protocol-port 24 \\ --description "{'Add-profile':'SIP', 'Listener-proto':'UDP', 'Listener-type':'standard'}" neutron lbaas-l7policy-create --listener test-udp-sip --name sip_persistence --action REJECT --------------------------------------------------------------- Add daimeter listener: neutron lbaas-listener-create --name test-tcp-diameter \\ --loadbalancer test-lb \\ --protocol TCP \\ --protocol-port 1234 \\ --description "{'Add-profile':'Diameter', 'ds':'diameter-it-session', 'dr':'diameterrouter-it'}" neutron lbaas-l7policy-create --listener test-tcp-diameter --name diameter_tcp --action REJECT --------------------------------------------------------------- Change transparent IP TCP standard listener CLI to: neutron lbaas-listener-create --name test-tcp-transparent \\ --loadbalancer test-lb \\ --protocol TCP \\ --protocol-port 8899 \\ --description "{'Listener-type':'standard'}" neutron lbaas-l7policy-create --listener test-tcp-transparent --name insert_sip_tcp-option --action REJECT --------------------------------------------------------------- ESD file: cat /etc/neutron/services/f5/esd/demo.json { "sip_persistence": { "lbaas_persist": "custom_sip_persist" }, "diameter_tcp": { "lbaas_ctcp": "tcp-idle-800" }, "insert_sip_tcp-option": { "lbaas_stcp": "tcp_option_28", "lbaas_irule": ["insert-clientIP-TCP-option"] } } --- .../lbaasv2/drivers/bigip/service_adapter.py | 84 +++++++++++++++---- 1 file changed, 68 insertions(+), 16 deletions(-) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py index c70f24001..222e442a5 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py @@ -561,11 +561,6 @@ def _add_profiles_session_persistence(self, listener, pool, vip): LOG.warning("Listener protocol unrecognized: %s", listener["protocol"]) - if protocol == "UDP": - vip["ipProtocol"] = "udp" - else: - vip["ipProtocol"] = "tcp" - # if protocol is HTTPS, also use fastl4 if protocol in ['TCP', 'HTTPS', 'UDP']: virtual_type = 'fastl4' @@ -574,20 +569,47 @@ def _add_profiles_session_persistence(self, listener, pool, vip): extra_options = self.parse_descript_opts(listener) + add_sip = False + add_diameter = False # according to the extra_options, # some vip profile will be overwrite if extra_options: - tcp_type = extra_options.get('TCP-type') + listener_type = extra_options.get('Listener-type') + extra_profile = extra_options.get('Add-profile') + + other_proto = extra_options.get('Listener-proto') # add this for TCP source ip transparent - if tcp_type == 'standard' and protocol == 'TCP': + if other_proto == 'UDP': + # TCP with UDP description, it will become UDP + protocol = "UDP" + # change listener protocol here + listener["protocol"] = "UDP" + + if listener_type == 'standard': virtual_type = 'standard' + if extra_profile == 'SIP': + add_sip = True + elif extra_profile == 'Diameter': + virtual_type = "mr" + add_diameter = True + + if protocol == "UDP": + vip["ipProtocol"] = "udp" + else: + vip["ipProtocol"] = "tcp" + if virtual_type == 'fastl4': vip['profiles'] = ['/Common/fastL4'] - # standard type TCP listener canot have http and oneconnect profile - # or ssh lb members will be diconnected elif virtual_type == 'standard' and protocol == 'TCP': - vip['profiles'] = [] + vip['profiles'] = ['/Common/tcp'] + # vip['profiles'] = [] + elif virtual_type == 'standard' and protocol == 'UDP': + vip['profiles'] = ['/Common/udp'] + # vip['profiles'] = [] + elif virtual_type == 'mr' and protocol == 'TCP': + vip['profiles'] = ['/Common/tcp'] + # vip['profiles'] = ['/Common/tcp'] else: # add profiles for HTTP, TERMINATED_HTTPS protocols vip['profiles'] = ['/Common/http', '/Common/oneconnect'] @@ -624,6 +646,29 @@ def _add_profiles_session_persistence(self, listener, pool, vip): if persistence_type in ['HTTP_COOKIE', 'APP_COOKIE']: vip['profiles'] = ['/Common/http', '/Common/oneconnect'] + if add_sip: + if '/Common/sip' not in vip['profiles']: + vip['profiles'].append('/Common/sip') + + if add_diameter: + diameter_session = extra_options.get('ds') + diameter_router = extra_options.get('dr') + # tcp_profile = extra_options.get('dtcp') + + if not diameter_session: + diameter_session = '/Common/diametersession' + else: + diameter_session = '/Common/' + diameter_session + + if not diameter_router: + diameter_router = '/Common/diameterrouter' + + if diameter_session not in vip['profiles'] and \ + diameter_router not in vip['profiles']: + vip['profiles'] += [diameter_session, + diameter_router] + # tcp_profile] + def get_vlan(self, vip, bigip, network_id): if network_id in bigip.assured_networks: vip['vlans'].append( @@ -726,9 +771,7 @@ def _apply_fastl4_esd(self, vip, esd): else: vip['profiles'] = ["/Common/" + esd['lbaas_http_profile']] - # fastL4 listener cannot configure server and client - # protocol, or it will throw errors - # start with server tcp profile + # for standard type tcp listener if "/Common/fastL4" not in vip['profiles']: if 'lbaas_stcp' in esd: # set serverside tcp profile @@ -746,6 +789,12 @@ def _apply_fastl4_esd(self, vip, esd): ctcp_profile = esd['lbaas_ctcp'] else: ctcp_profile = 'tcp' + + if '/Common/tcp' in vip['profiles']: + vip['profiles'].remove('/Common/tcp') + + # if '/Common/udp' not in vip['profiles'] and \ + # '/Common/tcp' not in vip['profiles']: vip['profiles'].append({'name': ctcp_profile, 'partition': 'Common', 'context': ctcp_context}) @@ -807,9 +856,12 @@ def _apply_esd(self, vip, esd): ctcp_profile = esd['lbaas_ctcp'] else: ctcp_profile = 'tcp' - profiles.append({'name': ctcp_profile, - 'partition': 'Common', - 'context': ctcp_context}) + + # in case of udp listener cahanges back to 'tcp' + if '/Common/udp' not in vip["profiles"]: + profiles.append({'name': ctcp_profile, + 'partition': 'Common', + 'context': ctcp_context}) if 'lbaas_oneconnect_profile' in esd: profiles.remove('/Common/oneconnect') From 36f72c370a1c3970dbd906568deda89e6f992f5e Mon Sep 17 00:00:00 2001 From: zhangshengping Date: Thu, 7 May 2020 05:54:43 -0700 Subject: [PATCH 6/6] feat: add diameter healthmonitor CMCC need to add Diameter type healthmonitor for them, the lbaas healthmonitor does not include DIAMETER type, this feature to let lbaas cli use `--name diameter` to configure diameter type healthmonitor on F5 BIGIP. usage: create diameter healthmonitor on BIGIP: neutron lbaas-healthmonitor-create --pool a4b9e446-4350-4652-8d20-e6d087e784e6 --type TCP --delay 20 --timeout 30 --max-retries 1 --name diameter delete diameter healthmonitor on BIGIP (do not use diameter name to delete): neutron lbaas-healthmonitor-delete ac8f50f8-8bfb-44a8-a1d5-dd571570ef31 Conflicts: f5_openstack_agent/lbaasv2/drivers/bigip/test/conftest.py f5_openstack_agent/lbaasv2/drivers/bigip/test/mock_builder_base_class.py --- .../lbaasv2/drivers/bigip/icontrol_driver.py | 12 ++++++++++++ .../lbaasv2/drivers/bigip/pool_service.py | 4 ++++ .../lbaasv2/drivers/bigip/resource_helper.py | 3 +++ .../lbaasv2/drivers/bigip/service_adapter.py | 7 ------- .../drivers/bigip/test/mock_builder_base_class.py | 4 ++-- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py b/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py index 8d74da4bf..383bbde27 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py @@ -1609,6 +1609,10 @@ def delete_member(self, member, service): def create_health_monitor(self, health_monitor, service): """Create pool health monitor.""" LOG.debug("Creating health monitor") + monitors = service.get("healthmonitors", list()) + for mn in monitors: + if mn.get("name") == "diameter": + mn["type"] = "DIAMETER" return self._common_service_handler(service) @serialized('update_health_monitor') @@ -1617,6 +1621,10 @@ def update_health_monitor(self, old_health_monitor, health_monitor, service): """Update pool health monitor.""" LOG.debug("Updating health monitor") + monitors = service.get("healthmonitors", list()) + for mn in monitors: + if mn.get("name") == "diameter": + mn["type"] = "DIAMETER" return self._common_service_handler(service) @serialized('delete_health_monitor') @@ -1624,6 +1632,10 @@ def update_health_monitor(self, old_health_monitor, def delete_health_monitor(self, health_monitor, service): """Delete pool health monitor.""" LOG.debug("Deleting health monitor") + monitors = service.get("healthmonitors", list()) + for mn in monitors: + if mn.get("name") == "diameter": + mn["type"] = "DIAMETER" return self._common_service_handler(service) @is_operational diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/pool_service.py b/f5_openstack_agent/lbaasv2/drivers/bigip/pool_service.py index ee00c6a95..74873f577 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/pool_service.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/pool_service.py @@ -45,6 +45,8 @@ def __init__(self, service_adapter): resource_helper.ResourceType.udp_monitor) self.sip_mon_helper = resource_helper.BigIPResourceHelper( resource_helper.ResourceType.sip_monitor) + self.diameter_mon_helper = resource_helper.BigIPResourceHelper( + resource_helper.ResourceType.diameter_monitor) self.ping_mon_helper = resource_helper.BigIPResourceHelper( resource_helper.ResourceType.ping_monitor) self.pool_helper = resource_helper.BigIPResourceHelper( @@ -252,6 +254,8 @@ def _get_monitor_helper(self, service): hm = self.udp_mon_helper elif monitor_type == "SIP": hm = self.sip_mon_helper + elif monitor_type == "DIAMETER": + hm = self.diameter_mon_helper else: hm = self.http_mon_helper return hm diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/resource_helper.py b/f5_openstack_agent/lbaasv2/drivers/bigip/resource_helper.py index f8304b24a..c8de04e5a 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/resource_helper.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/resource_helper.py @@ -63,6 +63,7 @@ class ResourceType(Enum): oneconnect = 37 udp_monitor = 38 sip_monitor = 39 + diameter_monitor = 40 class BigIPResourceHelper(object): @@ -211,6 +212,8 @@ def _resource(self, bigip): lambda bigip: bigip.tm.ltm.monitor.udps.udp, ResourceType.sip_monitor: lambda bigip: bigip.tm.ltm.monitor.sips.sip, + ResourceType.diameter_monitor: + lambda bigip: bigip.tm.ltm.monitor.diameters.diameter, ResourceType.ping_monitor: lambda bigip: bigip.tm.ltm.monitor.gateway_icmps.gateway_icmp, ResourceType.node: lambda bigip: bigip.tm.ltm.nodes.node, diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py index 222e442a5..d290bec97 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py @@ -603,13 +603,10 @@ def _add_profiles_session_persistence(self, listener, pool, vip): vip['profiles'] = ['/Common/fastL4'] elif virtual_type == 'standard' and protocol == 'TCP': vip['profiles'] = ['/Common/tcp'] - # vip['profiles'] = [] elif virtual_type == 'standard' and protocol == 'UDP': vip['profiles'] = ['/Common/udp'] - # vip['profiles'] = [] elif virtual_type == 'mr' and protocol == 'TCP': vip['profiles'] = ['/Common/tcp'] - # vip['profiles'] = ['/Common/tcp'] else: # add profiles for HTTP, TERMINATED_HTTPS protocols vip['profiles'] = ['/Common/http', '/Common/oneconnect'] @@ -653,7 +650,6 @@ def _add_profiles_session_persistence(self, listener, pool, vip): if add_diameter: diameter_session = extra_options.get('ds') diameter_router = extra_options.get('dr') - # tcp_profile = extra_options.get('dtcp') if not diameter_session: diameter_session = '/Common/diametersession' @@ -667,7 +663,6 @@ def _add_profiles_session_persistence(self, listener, pool, vip): diameter_router not in vip['profiles']: vip['profiles'] += [diameter_session, diameter_router] - # tcp_profile] def get_vlan(self, vip, bigip, network_id): if network_id in bigip.assured_networks: @@ -793,8 +788,6 @@ def _apply_fastl4_esd(self, vip, esd): if '/Common/tcp' in vip['profiles']: vip['profiles'].remove('/Common/tcp') - # if '/Common/udp' not in vip['profiles'] and \ - # '/Common/tcp' not in vip['profiles']: vip['profiles'].append({'name': ctcp_profile, 'partition': 'Common', 'context': ctcp_context}) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/test/mock_builder_base_class.py b/f5_openstack_agent/lbaasv2/drivers/bigip/test/mock_builder_base_class.py index 842ce3c4f..5f5e233fd 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/test/mock_builder_base_class.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/test/mock_builder_base_class.py @@ -425,8 +425,8 @@ def check_mocks(self, target, not_called=[]): stored.expected_call_cnt except AssertionError: raise AssertionError( - "Expected '{}.{}' method to be called {} times. " - "Impacted builder: {}".format( + "Expected '{}.{}' method to be called {} times. Was " + "actually called {} times. ".format( stored.target_mod, stored.mocked_method, stored.expected.call_cnt, stored.mock_builder))