From d1ffd34ccf708b812f609f46a041423cf56f2881 Mon Sep 17 00:00:00 2001 From: Niklaus Xie Date: Mon, 23 Aug 2021 13:43:07 +0800 Subject: [PATCH] redirect feature using irules --- .../drivers/bigip/agent_manager_lite.py | 8 + .../lbaasv2/drivers/bigip/resource_manager.py | 171 ++++++++++++++++++ .../lbaasv2/drivers/bigip/service_adapter.py | 9 + 3 files changed, 188 insertions(+) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager_lite.py b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager_lite.py index d128055fa..851e53bb0 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager_lite.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager_lite.py @@ -1454,6 +1454,14 @@ def create_listener(self, context, listener, service): id = listener['id'] try: mgr = resource_manager.ListenerManager(self.lbdriver) + # todo delete these 3 lines + # listener['redirect_up'] = True + # listener['redirect_protocol'] = 'HTTPS' + # listener['redirect_port'] = 444 + # service["listeners"][0]["redirect_up"] = True + # service["listeners"][0]["redirect_port"] = 444 + # service["listeners"][0]["redirect_protocol"] = 'HTTPS' + mgr.create(listener, service) provision_status = constants_v2.F5_ACTIVE operating_status = constants_v2.F5_ONLINE diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py b/f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py index 74110a920..783e052c6 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py @@ -756,10 +756,42 @@ def _check_http2_changed(self, old_listener, listener): return True return False + def _check_redirect_changed(self, old_listener, listener): + LOG.debug(old_listener) + LOG.debug(listener) + + if 'redirect_up' not in old_listener or 'redirect_up' not in listener: + return 'none' + + old_redirect_up = old_listener.get('redirect_up') + new_redirect_up = listener.get('redirect_up') + + if new_redirect_up and not old_redirect_up: + LOG.debug('enabling') + return 'enable' + elif old_redirect_up and not new_redirect_up: + LOG.debug('disabling') + return 'disable' + elif old_redirect_up and new_redirect_up: + old_num = old_listener.get('redirect_port') + new_num = listener.get('redirect_port') + LOG.debug('old_num:') + LOG.debug(old_num) + LOG.debug('new_num:') + LOG.debug(new_num) + if old_num != new_num: + LOG.debug('modifying') + return 'modify' + + LOG.debug('nothing') + return 'none' + def _update_needed(self, payload, old_listener, listener): if self._check_tls_changed(old_listener, listener) or \ self._check_customized_changed(old_listener, listener) or \ + self._check_redirect_changed(old_listener, listener) != 'none' or \ self._check_http2_changed(old_listener, listener): + LOG.info('true here') return True return super(ListenerManager, self)._update_needed( payload, old_listener, listener) @@ -1098,6 +1130,22 @@ def _detach_profile(self, bigip, vs, profile): LOG.debug("Profile %s is not attached to vs %s", profile['name'], vs['name']) + def _create_redirect_irule_payload(self, vs, loadbalancer, listener): + # create the redirect irule + irule = {} + irule['partition'] = self.driver.service_adapter.\ + get_folder_name(loadbalancer['tenant_id']) + irule['name'] = 'redirect_irule_' + listener['id'] + + redi_port = listener.get('redirect_port') + if redi_port is None: + LOG.error('redirect port is none, seems safe to neglect now') + + irule['apiAnonymous'] = 'when HTTP_REQUEST {\n\ + HTTP::redirect https://[getfield [HTTP::host] ":" 1]:' + \ + str(redi_port) + '[HTTP::uri]\n}' + return irule + def _create_websocket_irule(self, bigip, vs): # Websocket iRule is shared across all partitions. Needn't delete it. irule = { @@ -1120,6 +1168,36 @@ def _create_websocket_irule(self, bigip, vs): bigip, irule, None, None, type="irule", helper=self.irule_helper) vs['rules'].append("/Common/websocket_irule") + def try_create_redirect_irule(self, bigip, vs, loadbalancer, listener): + LOG.debug('vs here is:') + LOG.debug(vs) + the_payload = self._create_redirect_irule_payload( + vs, loadbalancer, listener + ) + super(ListenerManager, self)._create( + bigip, the_payload, None, None, type="irule", + helper=self.irule_helper, overwrite=True + ) + + def try_del_redirect_irule(self, bigip, vs, loadbalancer, listener): + LOG.debug('inside try_del_redirect_irule') + LOG.debug('vs here:') + LOG.debug(vs) + + LOG.debug('loadbalancer here:') + LOG.debug(loadbalancer) + + LOG.debug('listener here:') + LOG.debug(listener) + + payload = self._create_redirect_irule_payload( + vs, loadbalancer, listener + ) + super(ListenerManager, self).\ + _delete(bigip, payload, None, None, + type="irule", + helper=self.irule_helper) + def _create(self, bigip, vs, listener, service): tls = self.driver.service_adapter.get_tls(service) if tls: @@ -1154,6 +1232,23 @@ def _create(self, bigip, vs, listener, service): bwc_policy = LoadBalancerManager.get_bwc_policy_name( self.driver.service_adapter, loadbalancer) vs['bwcPolicy'] = bwc_policy + + # redirect_up, redirect_protocol, redirect_port + if vs.get('redirect_up'): + LOG.debug('at redirect_up') + self.try_create_redirect_irule( + bigip, vs, loadbalancer, listener + ) + # use full name + # redirect_irule_name = 'redirect_irule_' + listener['id'] + redi_irule_full_name = self.get_redirect_irule_full_name( + self.driver.service_adapter, + loadbalancer, listener['id'] + ) + vs['rules'].append(redi_irule_full_name) + + LOG.debug('vs details:') + LOG.debug(vs) super(ListenerManager, self)._create(bigip, vs, listener, service) def __get_profiles_from_bigip(self, bigip, vs): @@ -1166,6 +1261,13 @@ def __get_profiles_from_bigip(self, bigip, vs): profiles = v.profilesReference return profiles + @staticmethod + def get_redirect_irule_full_name(adapter, loadbalancer, listener_id): + irule_name = '/' + adapter.\ + get_folder_name(loadbalancer['tenant_id']) + '/redirect_irule_'\ + + listener_id + return irule_name + def _update(self, bigip, vs, old_listener, listener, service): # Add conditions here for more update requests via vs['profiles'] extended_profile_updated = False @@ -1222,7 +1324,74 @@ def _update(self, bigip, vs, old_listener, listener, service): old_service = {"listener": old_listener} self._delete_ssl_profiles(bigip, vs, old_service) + LOG.debug('checking redirect here') + # enable, disable, modify, none + redirect_ret = self._check_redirect_changed(old_listener, listener) + LOG.debug('redirect_ret here is:') + LOG.debug(redirect_ret) + loadbalancer = service.get('loadbalancer', dict()) + + # redirect_irule_name = 'redirect_irule_' + listener['id'] + redi_irule_full_name = self.get_redirect_irule_full_name( + self.driver.service_adapter, + loadbalancer, listener['id'] + ) + + the_vs_payload = self.driver.service_adapter.\ + _init_virtual_name(loadbalancer, listener) + the_vs = self.resource_helper.load( + bigip, name=the_vs_payload['name'], + partition=the_vs_payload['partition'] + ) + + the_vs_payload['rules'] = the_vs.rules + + if redirect_ret == 'enable': + LOG.debug('to redirect') + self.try_create_redirect_irule( + bigip, the_vs_payload, + loadbalancer, listener + ) + the_vs_payload['rules'].append(redi_irule_full_name) + + elif redirect_ret == 'disable': + LOG.debug('to reverse redirect') + # not able to delete here because it is still in use by listener + # self.try_del_redirect_irule(bigip,vs,loadbalancer,old_listener) + + if redi_irule_full_name in the_vs_payload['rules']: + LOG.debug('remove from rules') + LOG.debug(redi_irule_full_name) + the_vs_payload['rules'].remove(redi_irule_full_name) + + elif redirect_ret == 'modify': + LOG.debug('to redirect elsewhere') + # self.try_del_redirect_irule(bigip, the_vs_payload, + # loadbalancer,old_listener) + LOG.debug('creating new:') + self.try_create_redirect_irule( + bigip, the_vs_payload, loadbalancer, listener + ) + + if redi_irule_full_name not in the_vs_payload['rules']: + LOG.info('add to rules') + the_vs_payload['rules'].append(redi_irule_full_name) + else: + LOG.debug('no redirect change here') + + if redirect_ret in ('enable', 'disable', 'modify'): + LOG.debug('calling _update') + self.resource_helper.update(bigip, the_vs_payload) + + if redirect_ret == 'disable': + LOG.debug('delete the irule here.') + self.try_del_redirect_irule( + bigip, the_vs_payload, loadbalancer, old_listener + ) + def _delete(self, bigip, vs, listener, service): + loadbalancer = service.get("loadbalancer", None) + super(ListenerManager, self)._delete(bigip, vs, listener, service) self._delete_persist_profile(bigip, vs) self._delete_ssl_profiles(bigip, vs, service) @@ -1231,6 +1400,8 @@ def _delete(self, bigip, vs, listener, service): if ftp_enable: self.ftp_helper.remove_profile(service, vs, bigip) + self.try_del_redirect_irule(bigip, vs, loadbalancer, listener) + @serialized('ListenerManager.create') @log_helpers.log_method_call def create(self, listener, service, **kwargs): diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py index e97aa951f..5470be262 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py @@ -111,6 +111,7 @@ def get_resource_description(self, resource): return full_description def get_virtual(self, service): + # whether need to pass redirect from driver side. listener = service["listener"] loadbalancer = service["loadbalancer"] @@ -506,6 +507,14 @@ def _map_virtual(self, loadbalancer, listener, self._add_vlan_and_snat(listener, vip) self._add_profiles_session_persistence(listener, pool, vip) + if "redirect_up" in listener: + if listener["redirect_up"]: + vip["redirect_up"] = True + vip["redirect_port"] = listener["redirect_port"] + vip["redirect_protocol"] = listener["redirect_protocol"] + else: + vip["redirect_up"] = False + # Set bandwidth controller if 'bwcPolicy' in listener: vip['bwcPolicy'] = listener.get("bwcPolicy")