From 9897b920ad45369e2577de1f1b7deb6eb22f5c93 Mon Sep 17 00:00:00 2001 From: zhangshengping Date: Mon, 3 Jun 2019 02:20:20 -0700 Subject: [PATCH 1/3] Add ESD refresh 1. add ESD refresh periodic_task 2. fix ESD and l7policy Regex irule configuration conflicts --- .../lbaasv2/drivers/bigip/agent_manager.py | 6 ++++ .../lbaasv2/drivers/bigip/icontrol_driver.py | 21 ++++++++----- .../lbaasv2/drivers/bigip/service_adapter.py | 8 ++--- .../bigip/test/test_service_adapter.py | 30 +++++++++---------- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py index 6fb617297..13d804df1 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py @@ -497,6 +497,12 @@ def connect_driver(self, context): if self.lbdriver: self.lbdriver.connect() + @periodic_task.periodic_task(spacing=PERIODIC_TASK_INTERVAL) + def refresh_esd(self, context): + if self.lbdriver.esd: + LOG.debug("refresh esd files") + self.lbdriver.init_esd() + @periodic_task.periodic_task(spacing=PERIODIC_TASK_INTERVAL) def recover_errored_devices(self, context): """Try to reconnect to errored devices.""" diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py b/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py index 195fc3e6c..26134a287 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py @@ -369,6 +369,7 @@ def __init__(self, conf, registerOpts=True): # to store the verified esd names self.esd_names = [] + self.esd = None # service component managers self.tenant_manager = None @@ -832,21 +833,27 @@ def _post_init(self): self.network_builder.post_init() # read enhanced services definitions + self.init_esd() + self._set_agent_status(False) + + def init_esd(self): + # read all esd file from esd dir + # init esd object in lbaas_builder + # init esd object in service_adapter esd_dir = os.path.join(self.get_config_dir(), 'esd') - esd = EsdTagProcessor(esd_dir) + self.esd = EsdTagProcessor(esd_dir) try: - esd.process_esd(self.get_all_bigips()) - self.lbaas_builder.init_esd(esd) - self.service_adapter.init_esd(esd) + self.esd.process_esd(self.get_all_bigips()) + self.lbaas_builder.init_esd(self.esd) + self.service_adapter.init_esd(self.esd) LOG.debug('esd details here after process_esd(): ') - LOG.debug(esd) - self.esd_names = esd.esd_dict.keys() or [] + LOG.debug(self.esd) + self.esd_names = self.esd.esd_dict.keys() or [] LOG.debug('##### self.esd_names obtainded here:') LOG.debug(self.esd_names) except f5ex.esdJSONFileInvalidException as err: LOG.error("unable to initialize ESD. Error: %s.", err.message) - self._set_agent_status(False) def _validate_ha(self, bigip): # if there was only one address supplied and diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py index cacce33ba..f141faf56 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py @@ -479,6 +479,8 @@ def _map_virtual(self, loadbalancer, listener, self._add_vlan_and_snat(listener, vip) self._add_profiles_session_persistence(listener, pool, vip) + existed_irules = vip.get('rules', []) + irules += existed_irules vip['rules'] = irules vip['policies'] = list() if policies: @@ -685,9 +687,8 @@ def _apply_fastl4_esd(self, vip, esd): vip['fallbackPersistence'] = esd['lbaas_fallback_persist'] # iRules - vip['rules'] = list() if 'lbaas_irule' in esd: - irules = [] + irules = vip.get('rules', []) for irule in esd['lbaas_irule']: irules.append('/Common/' + irule) vip['rules'] = irules @@ -765,9 +766,8 @@ def _apply_esd(self, vip, esd): vip['fallbackPersistence'] = esd['lbaas_fallback_persist'] # iRules - vip['rules'] = list() if 'lbaas_irule' in esd: - irules = [] + irules = vip.get('rules', []) for irule in esd['lbaas_irule']: irules.append('/Common/' + irule) vip['rules'] = irules 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 788fc6677..d9981f775 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 @@ -892,8 +892,8 @@ def test_apply_esd_oneconnect_profile(adapter): dict(name="tcp", partition="Common", context="all"), - "/Common/oneconnect_profile"], - rules=[]) + "/Common/oneconnect_profile"] + ) assert vip == expected def test_apply_esd_ctcp_profile(adapter): @@ -911,8 +911,8 @@ def test_apply_esd_ctcp_profile(adapter): dict(name="tcp-mobile-optimized", partition="Common", context="all") - ], - rules=[]) + ] + ) assert vip == expected def test_apply_esd_stcp_profile(adapter): @@ -933,8 +933,8 @@ def test_apply_esd_stcp_profile(adapter): dict(name="tcp", partition="Common", context="clientside") - ], - rules=[]) + ] + ) assert vip == expected @@ -957,8 +957,8 @@ def test_apply_esd_ctcp_and_stcp_profile(adapter): dict(name="tcp-mobile-optimized", partition="Common", context="clientside") - ], - rules=[]) + ] + ) assert vip == expected @@ -980,8 +980,8 @@ def test_apply_esd_ssl_profiles(adapter): dict(name="clientssl", partition="Common", context="clientside") - ], - rules=[]) + ] + ) assert vip == expected @@ -1001,8 +1001,8 @@ def test_apply_esd_ssl_profiles(adapter): dict(name="serverssl", partition="Common", context="serverside") - ], - rules=[]) + ] + ) assert vip == expected @@ -1020,8 +1020,8 @@ def test_apply_esd_http_profiles(adapter): expected = dict(profiles=[dict(name="tcp", partition="Common", context="all"), - "/Common/http_profile"], - rules=[]) + "/Common/http_profile"] + ) assert vip == expected @@ -1136,7 +1136,6 @@ def test_apply_esd_policy(adapter): assert "persist" not in vip assert "fallbackPersistence" not in vip - assert vip['rules'] == [] assert vip['policies'] == [dict(name='demo_policy', partition="Common")] @@ -1262,6 +1261,5 @@ def test_apply_l4_esd_policy(adapter): assert "persist" not in vip assert "fallbackPersistence" not in vip - assert vip['rules'] == [] assert vip['policies'] == [dict(name='demo_policy', partition="Common")] From 81995bde57ef4ca2b4ea752caa2aaba260f0f25e Mon Sep 17 00:00:00 2001 From: zhangshengping Date: Sun, 9 Jun 2019 21:35:26 -0700 Subject: [PATCH 2/3] refresh esd, restore esd file if error happens with unit tests --- .../lbaasv2/drivers/bigip/esd_filehandler.py | 63 +++++++++---------- .../lbaasv2/drivers/bigip/icontrol_driver.py | 16 ++--- .../bigip/test/test_esd_filehandler.py | 52 +++++++-------- 3 files changed, 65 insertions(+), 66 deletions(-) diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/esd_filehandler.py b/f5_openstack_agent/lbaasv2/drivers/bigip/esd_filehandler.py index d37ce65b2..d62a835eb 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/esd_filehandler.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/esd_filehandler.py @@ -14,6 +14,7 @@ # limitations under the License. # +import copy import glob import json import os @@ -31,56 +32,50 @@ LOG = logging.getLogger(__name__) -class EsdJSONValidation(object): - """Class reads the json file(s) +class EsdTagProcessor(object): + """Class processes json dictionary - It checks and parses the content of json file(s) to a dictionary + It checks compares the tags from esdjson dictionary to list of valid tags """ - def __init__(self, esddir): - self.esdJSONFileList = glob.glob(os.path.join(esddir, '*.json')) - self.esdJSONDict = {} - - def read_json(self): - for fileList in self.esdJSONFileList: + _instance = None + esd_dict = {} + + def __new__(cls, *args, **kwargs): + if not isinstance(cls._instance, cls): + cls._instance = object.__new__(cls, *args, **kwargs) + return cls._instance + + def read_json(self, esddir): + esdJSONFileList = glob.glob(os.path.join(esddir, '*.json')) + esdJSONDict = {} + for fileList in esdJSONFileList: try: with open(fileList) as json_file: # Reading each file to a dictionary fileJSONDict = json.load(json_file) # Combine all dictionaries to one - self.esdJSONDict.update(fileJSONDict) + esdJSONDict.update(fileJSONDict) except ValueError as err: LOG.error('ESD JSON File is invalid: %s', err) raise f5_ex.esdJSONFileInvalidException() - return self.esdJSONDict - - -class EsdTagProcessor(EsdJSONValidation): - """Class processes json dictionary - - It checks compares the tags from esdjson dictionary to list of valid tags - """ - def __init__(self, esddir): - super(EsdTagProcessor, self).__init__(esddir) - - # this function will return intersection of known valid esd tags - # and the ones that user provided - def valid_tag_key_subset(self): - self.validtags = list(set(self.esdJSONDict.keys()) & - set(self.valid_esd_tags.keys())) - if not self.validtags: - LOG.error("Intersect of valid esd tags and user esd tags is empty") - - if set(self.validtags) != set(self.esdJSONDict.keys()): - LOG.error("invalid tags in the user esd tags") + return esdJSONDict - def process_esd(self, bigips): + def process_esd(self, bigips, esddir): + esd_json_backup = {} + esd_json_backup = copy.deepcopy(self.esd_dict) try: - dict = self.read_json() + dict = self.read_json(esddir) self.esd_dict = self.verify_esd_dict(bigips, dict) except f5_ex.esdJSONFileInvalidException: - self.esd_dict = {} + # if error happens, we set backup esd. + self.esd_dict = copy.deepcopy(esd_json_backup) + LOG.warning( + "ESD JSON File is invalid, " + "ESD JSON Dict is restored to %s", + esd_json_backup + ) raise def get_esd(self, name): diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py b/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py index 26134a287..b829294d7 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py @@ -841,20 +841,22 @@ def init_esd(self): # init esd object in lbaas_builder # init esd object in service_adapter esd_dir = os.path.join(self.get_config_dir(), 'esd') - self.esd = EsdTagProcessor(esd_dir) + # EsdTagProcessor is a singleton, so nothing new + self.esd = EsdTagProcessor() try: - self.esd.process_esd(self.get_all_bigips()) + self.esd.process_esd(self.get_all_bigips(), esd_dir) self.lbaas_builder.init_esd(self.esd) self.service_adapter.init_esd(self.esd) - LOG.debug('esd details here after process_esd(): ') - LOG.debug(self.esd) - self.esd_names = self.esd.esd_dict.keys() or [] - LOG.debug('##### self.esd_names obtainded here:') - LOG.debug(self.esd_names) except f5ex.esdJSONFileInvalidException as err: LOG.error("unable to initialize ESD. Error: %s.", err.message) + LOG.debug('ESD details here after process_esd(): ') + LOG.debug(self.esd) + self.esd_names = self.esd.esd_dict.keys() or [] + LOG.debug('self.esd_names obtainded here:') + LOG.debug(self.esd_names) + def _validate_ha(self, bigip): # if there was only one address supplied and # this is not a standalone device, get the diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/test/test_esd_filehandler.py b/f5_openstack_agent/lbaasv2/drivers/bigip/test/test_esd_filehandler.py index 91223abf0..ad1fcc29c 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/test/test_esd_filehandler.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/test/test_esd_filehandler.py @@ -17,7 +17,7 @@ import pytest from f5_openstack_agent.lbaasv2.drivers.bigip.esd_filehandler import \ - EsdJSONValidation + EsdTagProcessor from f5_openstack_agent.lbaasv2.drivers.bigip import exceptions as f5_ex @@ -38,57 +38,59 @@ def assertIn(obj1, dict_obj, note=''): def test_invalid_dir_name(self): # invliad directory name - reader = EsdJSONValidation('/as87awoiujasdf/') - assert not reader.esdJSONFileList + esd = EsdTagProcessor() + reader = esd.read_json('/as87awoiujasdf/') + assert not reader def test_no_files(self, get_relative_path): # verify no files in empty directory - reader = EsdJSONValidation( + esd = EsdTagProcessor() + reader = esd.read_json( '{}/{}/empty_dir'.format(get_relative_path, self.remaining_path)) - assert not reader.esdJSONFileList + assert not reader def test_no_json_files(self, get_relative_path): # verify no files are read in dir that contains non-JSON files - reader = EsdJSONValidation( + esd = EsdTagProcessor() + reader = esd.read_json( '{}/{}/no_json'.format(get_relative_path, self.remaining_path)) - assert not reader.esdJSONFileList + assert not reader def test_mix_json_files(self, get_relative_path): # verify single JSON file - reader = EsdJSONValidation( + esd = EsdTagProcessor() + reader = esd.read_json( '{}/{}/mix_json/'.format(get_relative_path, self.remaining_path)) - self.assertEqual(1, len(reader.esdJSONFileList)) + self.assertEqual(3, len(reader.keys())) def test_json_only_files(self, get_relative_path): # expect three files - reader = EsdJSONValidation( + esd = EsdTagProcessor() + reader = esd.read_json( '{}/{}/valid'.format(get_relative_path, self.remaining_path)) - self.assertEqual(3, len(reader.esdJSONFileList)) + self.assertEqual(3, len(reader.keys())) def test_invalid_json(self, get_relative_path): - handler = EsdJSONValidation( - '{}/{}/invalid'.format(get_relative_path, self.remaining_path)) - - # verify exception raised + esd = EsdTagProcessor() with self.assertRaises(f5_ex.esdJSONFileInvalidException): - handler.read_json() + esd.read_json( + '{}/{}/invalid'.format(get_relative_path, self.remaining_path)) def test_valid_json(self, get_relative_path): - handler = EsdJSONValidation( + esd = EsdTagProcessor() + result = esd.read_json( '{}/{}/valid/'.format(get_relative_path, self.remaining_path)) - dict = handler.read_json() # verify keys in the final dictionary - self.assertIn('app_type_1', dict) - self.assertIn('app_type_2', dict) - self.assertIn('app_type_3', dict) + self.assertIn('app_type_1', result) + self.assertIn('app_type_2', result) + self.assertIn('app_type_3', result) def test_empty_json(self, get_relative_path): # verify empty file is read - handler = EsdJSONValidation( + esd = EsdTagProcessor() + result = esd.read_json( '{}/{}/empty_file/'.format(get_relative_path, self.remaining_path)) - self.assertEqual(1, len(handler.esdJSONFileList)) # verify empty dict is returned - dict = handler.read_json() - assert not dict + assert not result From 9eaa7e77d6e0ad6baa87372bab32996102b26434 Mon Sep 17 00:00:00 2001 From: zhangshengping Date: Mon, 10 Jun 2019 01:18:33 -0700 Subject: [PATCH 3/3] Add control flag 'esd_auto_refresh' in configuration file Enable an esd-refresh file as flag Set 'esd_auto_refresh = False' and use command esd-refresh file as a flag to trigger ESD file refresh manually. --- .../services/f5/f5-openstack-agent.ini | 158 +++++++++--------- .../lbaasv2/drivers/bigip/agent_manager.py | 35 +++- .../lbaasv2/drivers/bigip/icontrol_driver.py | 25 +-- 3 files changed, 129 insertions(+), 89 deletions(-) diff --git a/etc/neutron/services/f5/f5-openstack-agent.ini b/etc/neutron/services/f5/f5-openstack-agent.ini index 5dfa56229..01098996b 100644 --- a/etc/neutron/services/f5/f5-openstack-agent.ini +++ b/etc/neutron/services/f5/f5-openstack-agent.ini @@ -24,7 +24,7 @@ # ####| |#########| |### # ####| |#########| |## # ###| |########/ /## -# #| |####| /## +# #| |####| /## # ############## # ########### # @@ -34,18 +34,24 @@ # [DEFAULT] # Show debugging output in log (sets DEBUG log level output). -debug = False +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. # periodic_interval = 10 # -# How often should the agent throw away its service cache and +# How often should the agent throw away its service cache and # resync assigned services with the neutron LBaaS plugin. # # service_resync_interval = 500 # +# Set esd_auto_refresh True to enable ESD files refresh periodically. +# Set esd_auto_refresh False to disable ESD files refresh periodically, +# and it needs to set 'esd-refresh' file as flag to trigger F5 agent +# reload ESD files. +# The default value is False +esd_auto_refresh = True ############################################################################### # Environment Settings ############################################################################### @@ -110,14 +116,14 @@ periodic_interval = 10 # Static Agent Configuration Setting ############################################################################### # -# Statically set the agent ID used to identify this agent with -# Neutron. +# Statically set the agent ID used to identify this agent with +# Neutron. # # The agent ID is used to identify this instance of # the agent for scheduling and messaging. If not set, the -# agent ID will be automatically populated with the host name -# of host running the agent appended with a hash of the -# environment_prefix and the first iControl endpoint. +# agent ID will be automatically populated with the host name +# of host running the agent appended with a hash of the +# environment_prefix and the first iControl endpoint. # # agent_id = None # @@ -127,7 +133,7 @@ periodic_interval = 10 # of name:value entries which will be sent in the agent's configuration # dictionary to neutron. # -# static_agent_configuration_data = location:DFW1_R122_U9, service_contract:8675309, contact:jenny +# static_agent_configuration_data = location:DFW1_R122_U9, service_contract:8675309, contact:jenny # ############################################################################### # Device Setting @@ -135,13 +141,13 @@ periodic_interval = 10 # # HA model # -# Device can be required to be: +# Device can be required to be: # # standalone - single device no HA # pair - active/standby two device HA # scalen - active device cluster -# -# If the device is external, the devices must be onboarded for the +# +# If the device is external, the devices must be onboarded for the # appropriate HA mode or else the driver will not provision devices # f5_ha_type = standalone @@ -150,24 +156,24 @@ f5_ha_type = standalone # L2 Segmentation Mode Settings ############################################################################### # -# Device VLAN to interface and tag mapping +# Device VLAN to interface and tag mapping # # For pools or VIPs created on networks with type VLAN we will map # the VLAN to a particular interface and state if the VLAN tagging -# should be enforced by the external device or not. This setting +# should be enforced by the external device or not. This setting # is a comma separated list of the following format: # # physical_network:interface_name:tagged, physical:interface_name:tagged # # where : # physical_network corresponds to provider:physical_network attributes -# interface_name is the name of an interface or LAG trunk -# tagged is a boolean (True or False) +# interface_name is the name of an interface or LAG trunk +# tagged is a boolean (True or False) # # If a network does not have a provider:physical_network attribute, # or the provider:physical_network attribute does not match in the -# configured list, the 'default' physical_network setting will be -# applied. At a minimum you must have a 'default' physical_network +# configured list, the 'default' physical_network setting will be +# applied. At a minimum you must have a 'default' physical_network # setting. # # standalone example: @@ -181,16 +187,16 @@ f5_external_physical_mappings = default:1.1:True # VLAN device and interface to port mappings # # Some systems require the need to bind and prune VLANs ids -# allowed to specific ports, often for security. +# allowed to specific ports, often for security. # # An example would be if a LBaaS iControl endpoint is using -# tagged VLANs. When a VLAN tagged network is added to a +# tagged VLANs. When a VLAN tagged network is added to a # specific BIG-IP device, the facing switch port will need # to allow traffic for that VLAN tag through to the BIG-IP's -# port for traffic to flow. +# port for traffic to flow. # # What is required is a software hook which allows the binding. -# A vlan_binding_driver class needs to reference a subclass of the +# A vlan_binding_driver class needs to reference a subclass of the # VLANBindingBase class and provides the methods to bind and prune # VLAN tags to ports. # @@ -198,18 +204,18 @@ f5_external_physical_mappings = default:1.1:True # # The interface_port_static_mappings allows for a JSON encoded dictionary # mapping BIG-IP devices and interfaces to corresponding ports. A port id can be -# any string which is meaningful to a vlan_binding_driver. It can be a +# any string which is meaningful to a vlan_binding_driver. It can be a # switch_id and port, or it might be a neutron port_id. # # In addition to any static mappings, when the iControl endpoints # are initialized, all their TMM interfaces will be collect -# for each device and neutron will be queried to see if which -# device port_ids correspond to known neutron ports. If they do, -# automatic entries for all mapped port_ids will be made referencing +# for each device and neutron will be queried to see if which +# device port_ids correspond to known neutron ports. If they do, +# automatic entries for all mapped port_ids will be made referencing # the BIG-IP device name and interface and the neutron port_ids. # -# interface_port_static_mappings = {"device_name_1":{"interface_ida":"port_ida","interface_idb":"port_idb"}, {"device_name_2":{"interface_ida":"port_ida","interface_idb":"port_idb"}} -# +# interface_port_static_mappings = {"device_name_1":{"interface_ida":"port_ida","interface_idb":"port_idb"}, {"device_name_2":{"interface_ida":"port_ida","interface_idb":"port_idb"}} +# # example: # # interface_port_static_mappings = {"bigip1":{"1.1":"switch1:g2/32","1.2":"switch1:g2/33"},"bigip2":{"1.1":"switch1:g3/32","1.2":"switch1:g3/33"}} @@ -227,10 +233,10 @@ f5_vtep_selfip_name = 'vtep' # # Tunnel types # -# This is a comma separated list of tunnel types to report +# This is a comma separated list of tunnel types to report # as available from this agent as well as to send via tunnel_sync # rpc messages to compute nodes. This should match your ml2 -# network types on your compute nodes. +# network types on your compute nodes. # # If you are using only gre tunnels it should be: # @@ -250,18 +256,18 @@ f5_vtep_selfip_name = 'vtep' # # Static ARP population for members on tunnel networks # -# This is a boolean True or False value which specifies -# that if a Pool Member IP address is associated with a gre -# or vxlan tunnel network, in addition to a tunnel fdb -# record being added, that a static arp entry will be created to +# This is a boolean True or False value which specifies +# that if a Pool Member IP address is associated with a gre +# or vxlan tunnel network, in addition to a tunnel fdb +# record being added, that a static arp entry will be created to # avoid the need to learn the member's MAC address via flooding. # # NOTE: this is currently set to false b/c the static arp population # is not functioning correctly in the BIG-IP REST api. See issues: # f5-openstack-agent issue #133 and -# f5-common-python issue #495 +# f5-common-python issue #495 f5_populate_static_arp = False - + # # Device Tunneling (VTEP) self IPs # @@ -305,24 +311,24 @@ l2_population = True # # Global Routed Mode - No L2 or L3 Segmentation on BIG-IP # -# This setting will cause the agent to assume that all VIPs -# and pool members will be reachable via global device +# This setting will cause the agent to assume that all VIPs +# and pool members will be reachable via global device # L3 routes, which must be already provisioned on the BIG-IPs. # # In f5_global_routed_mode, BIG-IP will not assume L2 -# adjacentcy to any neutron network, therefore no -# L2 segementation between tenant services in the data plane -# will be provisioned by the agent. Because the routing +# adjacentcy to any neutron network, therefore no +# L2 segementation between tenant services in the data plane +# will be provisioned by the agent. Because the routing # is global, no L3 self IPs or SNATs will be provisioned -# by the agent on behalf of tenants either. You must have -# all necessary L3 routes (including TMM default routes) -# provisioned before LBaaS resources are provisioned for tenants. +# by the agent on behalf of tenants either. You must have +# all necessary L3 routes (including TMM default routes) +# provisioned before LBaaS resources are provisioned for tenants. # # WARNING: setting this mode to True will override -# the use_namespaces, setting it to False, because only +# the use_namespaces, setting it to False, because only # one global routing space will used on the BIG-IP. This -# means overlapping IP addresses between tenants is no -# longer supported. +# means overlapping IP addresses between tenants is no +# longer supported. # # WARNING: setting this mode to True will override # the f5_snat_mode, setting it to True, because pool members @@ -333,26 +339,26 @@ l2_population = True # # WARNING: setting this mode to True will override the # f5_snat_addresses_per_subnet, setting it to 0 (zero). -# This will force all VIPs to use AutoMap SNAT for which +# This will force all VIPs to use AutoMap SNAT for which # enough Self IP will need to be pre-provisioned on the # BIG-IP to handle all pool member connections. The SNAT, # an L3 mechanism, will all be global without reference # to any specific tenant SNAT pool. # # WARNING: setting this mode will make the VIPs listen -# on all provisioned L2 segments (All VLANs). This is -# because no L2 information will be taken from +# on all provisioned L2 segments (All VLANs). This is +# because no L2 information will be taken from # neutron, thus making the assumption that all VIP -# L3 addresses will be globally routable without +# L3 addresses will be globally routable without # segmentation at L2 on the BIG-IP. # -f5_global_routed_mode = True +f5_global_routed_mode = True # # Allow overlapping IP subnets across multiple tenants. # This creates route domains on big-ip in order to # separate the tenant networks. # -# This setting is forced to False if +# This setting is forced to False if # f5_global_routed_mode = True. # use_namespaces = True @@ -385,8 +391,8 @@ use_namespaces = True # max_namespaces_per_tenant = 1 # -# Dictates the strict isolation of the routing -# tables. If you set this to True, then all +# Dictates the strict isolation of the routing +# tables. If you set this to True, then all # VIPs and Members must be in the same tenant # or less they can't communicate. # @@ -396,7 +402,7 @@ f5_route_domain_strictness = False # # SNAT Mode and SNAT Address Counts # -# This setting will force the use of SNATs. +# This setting will force the use of SNATs. # # If this is set to False, a SNAT will not # be created (routed mode) and the BIG-IP @@ -409,16 +415,16 @@ f5_route_domain_strictness = False # if the same BIG-IP device is not being used # as the Neutron Router implementation. # -# This setting will be forced to True if +# This setting will be forced to True if # f5_global_routed_mode = True. # f5_snat_mode = True # -# This setting will specify the number of snat -# addresses to put in a snat pool for each +# This setting will specify the number of snat +# addresses to put in a snat pool for each # subnet associated with a created local self IP. -# -# Setting to 0 (zero) will set VIPs to AutoMap +# +# Setting to 0 (zero) will set VIPs to AutoMap # SNAT and the device's local self IP will # be used to SNAT traffic. # @@ -426,7 +432,7 @@ f5_snat_mode = True # addresses per active traffic-group at the time # a service is provisioned. # -# This setting will be forced to 0 (zero) if +# This setting will be forced to 0 (zero) if # f5_global_routed_mode = True. # f5_snat_addresses_per_subnet = 1 @@ -437,7 +443,7 @@ f5_snat_addresses_per_subnet = 1 # assign networks to different partitions.. f5_common_networks = False # -# This setting will cause all networks with +# This setting will cause all networks with # the router:external attribute set to True # to be created in the Common partition and # placed in route domain 0. @@ -448,11 +454,11 @@ f5_common_external_networks = True # # This setting contains a name value pair comma # separated list where if the name is a neutron -# network id used for a vip or a pool member, +# network id used for a vip or a pool member, # the network should not be created or deleted # on the BIG-IP, but rather assumed that the value # is the name of the network already created in -# the Common partition with all L3 addresses +# the Common partition with all L3 addresses # assigned to route domain 0. This is useful # for shared networks which are already defined # on the BIG-IP prior to LBaaS configuration. The @@ -460,7 +466,7 @@ f5_common_external_networks = True # but can be used for VIPs or pool members # # If your Internet VLAN on your BIG-IP is named -# /Common/external, and that corresponds to +# /Common/external, and that corresponds to # Neutron uuid: 71718972-78e2-449e-bb56-ce47cc9d2680 # then the entry would look like: # @@ -473,17 +479,17 @@ f5_common_external_networks = True # # The default is no common networks defined # -# L3 Bindings +# L3 Bindings # # Some systems require the need to bind L3 addresses -# to specific ports, often for security. +# to specific ports, often for security. # # An example would be if a LBaaS iControl endpoint is using -# untagged VLANs and is a nova guest instance. By +# untagged VLANs and is a nova guest instance. By # default, neutron will attempt to apply security rule # for anti-spoofing which will not allow just any L3 # address to be used on the neutron port. The answer is to -# use allowed-address-pairs for the neutron port. +# use allowed-address-pairs for the neutron port. # # What is required is a software hook which allows the binding. # l3_binding_driver needs to reference a subclass of the L3BindingBase @@ -493,7 +499,7 @@ f5_common_external_networks = True # l3_binding_driver = f5_openstack_agent.lbaasv2.drivers.bigip.l3_binding.AllowedAddressPairs # # The l3_binding_static_mappings allows for a JSON encoded dictionary -# mapping neutron subnet ids to lists of L2 ports and devices which +# mapping neutron subnet ids to lists of L2 ports and devices which # require mapping. The entries for port and device mappings # vary between providers. They may look like a neutron port id # and a nova guest instance id. @@ -536,18 +542,18 @@ f5_bigip_lbaas_device_driver = f5_openstack_agent.lbaasv2.drivers.bigip.icontrol ############################################################################### # # icontrol_hostname is valid for external device type only. -# this setting can be either a single IP address or a -# comma separated list contain all devices in a device +# this setting can be either a single IP address or a +# comma separated list contain all devices in a device # service group. For guest devices, the first fixed_address # on the first device interfaces will be used. # -# If a single IP address is used and the HA model +# If a single IP address is used and the HA model # is not standalone, all devices in the sync failover -# device group for the hostname specified must have +# device group for the hostname specified must have # their management IP address reachable to the agent. # If order to access devices' iControl interfaces via # self IPs, you should specify them as a comma -# separated list below. +# separated list below. # icontrol_hostname = 10.190.7.232 # diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py index 13d804df1..379065293 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/agent_manager.py @@ -16,6 +16,7 @@ # import datetime +import os import sys import uuid @@ -134,6 +135,11 @@ 'password_cipher_mode', default=False, help='The flag indicating the password is plain text or not.' + ), + cfg.BoolOpt( + 'esd_auto_refresh', + default=False, + help='Enable ESD file periodic refresh' ) ] @@ -499,9 +505,32 @@ def connect_driver(self, context): @periodic_task.periodic_task(spacing=PERIODIC_TASK_INTERVAL) def refresh_esd(self, context): - if self.lbdriver.esd: - LOG.debug("refresh esd files") - self.lbdriver.init_esd() + """Refresh ESD files.""" + LOG.debug("start refreshing ESD files") + if self.lbdriver.esd_processor: + try: + if self.conf.esd_auto_refresh: + self.lbdriver.init_esd() + else: + refresh_flag_path = os.path.join( + self.lbdriver.get_config_dir(), + 'esd-refresh' + ) + refresh_flag = os.path.isfile(refresh_flag_path) + if refresh_flag: + self.lbdriver.init_esd() + os.remove(refresh_flag_path) + except f5_ex.esdJSONFileInvalidException: + LOG.warning("ESD file refresh is failed") + LOG.warning("ESD file refresh is failed") + except IOError as err: + LOG.warning("Can not operate esd-refresh file") + LOG.warning("Message: %s", err.message) + except OSError as err: + LOG.warning("esd-refresh file may be removed in advance") + LOG.warning("Message: %s", err.message) + except Exception: + LOG.exception("ESD refresh failed") @periodic_task.periodic_task(spacing=PERIODIC_TASK_INTERVAL) def recover_errored_devices(self, context): diff --git a/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py b/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py index b829294d7..f6af060f0 100644 --- a/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py +++ b/f5_openstack_agent/lbaasv2/drivers/bigip/icontrol_driver.py @@ -369,7 +369,7 @@ def __init__(self, conf, registerOpts=True): # to store the verified esd names self.esd_names = [] - self.esd = None + self.esd_processor = None # service component managers self.tenant_manager = None @@ -833,7 +833,12 @@ def _post_init(self): self.network_builder.post_init() # read enhanced services definitions - self.init_esd() + try: + self.init_esd() + except f5ex.esdJSONFileInvalidException as err: + LOG.error("unable to initialize ESD at startup. Error: %s.", + err.message) + self._set_agent_status(False) def init_esd(self): @@ -842,18 +847,18 @@ def init_esd(self): # init esd object in service_adapter esd_dir = os.path.join(self.get_config_dir(), 'esd') # EsdTagProcessor is a singleton, so nothing new - self.esd = EsdTagProcessor() + self.esd_processor = EsdTagProcessor() try: - self.esd.process_esd(self.get_all_bigips(), esd_dir) - self.lbaas_builder.init_esd(self.esd) - self.service_adapter.init_esd(self.esd) + self.esd_processor.process_esd(self.get_all_bigips(), esd_dir) + self.lbaas_builder.init_esd(self.esd_processor) + self.service_adapter.init_esd(self.esd_processor) - except f5ex.esdJSONFileInvalidException as err: - LOG.error("unable to initialize ESD. Error: %s.", err.message) + except f5ex.esdJSONFileInvalidException: + raise LOG.debug('ESD details here after process_esd(): ') - LOG.debug(self.esd) - self.esd_names = self.esd.esd_dict.keys() or [] + LOG.debug(self.esd_processor) + self.esd_names = self.esd_processor.esd_dict.keys() or [] LOG.debug('self.esd_names obtainded here:') LOG.debug(self.esd_names)