diff --git a/etc/neutron/services/f5/f5-extended-profile.json b/etc/neutron/services/f5/f5-extended-profile.json new file mode 100644 index 000000000..af65bdca5 --- /dev/null +++ b/etc/neutron/services/f5/f5-extended-profile.json @@ -0,0 +1,5 @@ +{ + "http_profile":{ + "insertXforwardedFor": "enabled" + } +} diff --git a/etc/neutron/services/f5/f5-openstack-agent.ini b/etc/neutron/services/f5/f5-openstack-agent.ini index 824dc32f1..ccc167305 100644 --- a/etc/neutron/services/f5/f5-openstack-agent.ini +++ b/etc/neutron/services/f5/f5-openstack-agent.ini @@ -631,3 +631,5 @@ os_project_domain_name = default # and if it does not exist on your BIG-IP system the agent will use the default # profile, clientssl. f5_parent_ssl_profile = clientssl + +f5_extended_profile = /etc/neutron/services/f5/f5-extended-profile.json diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py index 7c291c253..365f57d76 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py @@ -149,7 +149,12 @@ 'esd_auto_refresh', default=True, help='Enable ESD file periodic refresh' - ) + ), + cfg.StrOpt( + 'f5_extended_profile', + default='', + help=('The file name of extended profiles definition of a listener') + ), ] PERIODIC_TASK_INTERVAL = 10 diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/resource_helper.py b/f5_openstack_agent/lbaasv2/drivers/bigip/resource_helper.py index 592c2ac6c..b4d8662f7 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/resource_helper.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/resource_helper.py @@ -263,6 +263,8 @@ def _resource(self, bigip): lambda bigip: bigip.tm.ltm.persistence.ssl, ResourceType.universal_persistence: lambda bigip: bigip.tm.ltm.persistence.universal, + ResourceType.http_profile: + lambda bigip: bigip.tm.ltm.profile.https.http, ResourceType.ssl_cert_file: lambda bigip: bigip.tm.sys.file.ssl_certs.ssl_cert }[self.resource_type](bigip) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py b/f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py index d94e7f6c6..95bcd9e71 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json import urllib from f5_openstack_agent.lbaasv2.drivers.bigip import exceptions as f5_ex @@ -24,10 +25,11 @@ from oslo_log import helpers as log_helpers from oslo_log import log as logging -from requests import HTTPError - +from pathlib import Path from time import time +from requests import HTTPError + LOG = logging.getLogger(__name__) @@ -288,6 +290,8 @@ def __init__(self, driver): resource_helper.ResourceType.cookie_persistence) self.source_addr_persist_helper = resource_helper.BigIPResourceHelper( resource_helper.ResourceType.source_addr_persistence) + self.http_profile_helper = resource_helper.BigIPResourceHelper( + resource_helper.ResourceType.http_profile) self.mutable_props = { "name": "description", "default_pool_id": "pool", @@ -384,6 +388,71 @@ def _delete_persist_profile(self, bigip, vs): self._delete_http_cookie_persist_profile(bigip, vs) self._delete_source_addr_persist_profile(bigip, vs) + def _delete_http_profile(self, bigip, vs): + payload = { + "name": "http_profile_" + vs['name'], + "partition": vs['partition'], + } + super(ListenerManager, self)._delete( + bigip, payload, None, None, + helper=self.http_profile_helper) + + def _create_http_profile(self, bigip, vs): + # the logic is, if the http_profile_file is configured + # in the ini file, then + # 1) check if the configured file exists or not. + # 2) parse the content in the file + # 3) call the restful api to create the http_profile for the listener + # 4) set the http_profile in the profiles. + if self.driver.conf.f5_extended_profile: + # check if the file exists or not. + # check the content of the file content + file_name = self.driver.conf.f5_extended_profile + LOG.debug("extended profile file configured is %s", + file_name) + profile_file = Path(file_name) + if not profile_file.exists(): + LOG.warning("extended profile %s doesn't exist", + file_name) + return + try: + with open(file_name) as fp: + payload = json.load(fp) + if 'http_profile' not in payload: + LOG.debug("http profile is not defined in %s", + file_name) + return + http_profile = payload['http_profile'] + except ValueError: + LOG.warning("extended profile %s is not a valid json file", + file_name) + return + + LOG.debug("http profile content is %s", http_profile) + + # The name and parition items in the file will be overwriten + + if 'name' in http_profile: + del http_profile['name'] + http_profile['name'] = "http_profile_" + vs['name'] + + if 'partition' in http_profile: + del http_profile['partition'] + http_profile['partition'] = vs['partition'] + + profile_name = '/' + http_profile['partition'] + '/' \ + + http_profile['name'] + profiles = vs.get('profiles', []) + # in agent_lite, we will not apply esd so that only + # /common/http is possibly set in the profiles. + if '/Common/http' in vs['profiles']: + profiles.remove('/Common/http') + profiles.append(profile_name) + + super(ListenerManager, self)._create( + bigip, http_profile, None, None, type="http-profile", + helper=self.http_profile_helper, overwrite=False) + def _create(self, bigip, vs, listener, service): persist = service[self._key].get('session_persistence') if persist: @@ -393,6 +462,8 @@ def _create(self, bigip, vs, listener, service): loadbalancer = service.get('loadbalancer', dict()) network_id = loadbalancer.get('network_id', "") self.driver.service_adapter.get_vlan(vs, bigip, network_id) + if listener['protocol'] == "HTTP": + self._create_http_profile(bigip, vs) super(ListenerManager, self)._create(bigip, vs, listener, service) def _update(self, bigip, vs, old_listener, listener, service): @@ -411,6 +482,7 @@ def _update(self, bigip, vs, old_listener, listener, service): def _delete(self, bigip, vs, listener, service): super(ListenerManager, self)._delete(bigip, vs, listener, service) self._delete_persist_profile(bigip, vs) + self._delete_http_profile(bigip, vs) @serialized('ListenerManager.create') @log_helpers.log_method_call