diff --git a/networking_arista/ml2/mechanism_arista.py b/networking_arista/ml2/mechanism_arista.py index 7addaf38..018ceca8 100644 --- a/networking_arista/ml2/mechanism_arista.py +++ b/networking_arista/ml2/mechanism_arista.py @@ -14,7 +14,6 @@ # limitations under the License. import json -from multiprocessing import Queue from neutron_lib.api.definitions import portbindings from neutron_lib import constants as n_const @@ -33,6 +32,47 @@ from networking_arista.ml2 import arista_sync from networking_arista.ml2 import arista_trunk from networking_arista.ml2.rpc.arista_eapi import AristaRPCWrapperEapi +# When used as a Neutron plugin, neutron-lib imports this code. However earlier +# neutron-lib has already imported 'multiprocessing'. This means the python +# module cache (sys.modules) contains a version of 'multiprocessing' where +# select.poll() exists. +# +# Further down we import arista_sync, which spawns a greenthread. This +# greenthread then uses a green version of 'multiprocessing' where +# select.poll() has been removed. +# +# Doing here multiprocessing.Queue.put() and in the greenthread +# multiprocessing.Queue.get(timeout=...) leads to: +# AttributeError: module 'select' has no attribute 'poll' +# +# We can't do eventlet.monkey_patch() early enough (before the first +# 'mutiprocessing' import) as we would have to do it in neutron-lib and it's +# forbidden, see https://review.opendev.org/#/c/333017/ +# +# The solution is to let the python module cache here forget the already +# imported 'multiprocessing' and re-import a green one. Here again +# eventlet.monkey_patch() doesn't seem to help as it doesn't seem to touch +# 'multiprocessing'. Thus we use eventlet.import_patched() instead: +import eventlet +import sys +modules_to_forget = [] +for imported_module_name in sys.modules: + if imported_module_name.startswith('multiprocessing'): + modules_to_forget.append(imported_module_name) +for module_to_forget in modules_to_forget: + del sys.modules[module_to_forget] + +for module_to_forget in modules_to_forget: + try: + eventlet.import_patched(module_to_forget) + except ImportError: + pass + +# import a green 'multiprocessing': +multiprocessing = eventlet.import_patched('multiprocessing') +from networking_arista.ml2 import arista_sync # noqa: E402 + + LOG = logging.getLogger(__name__) cfg.CONF.import_group('ml2_arista', 'networking_arista.common.config') @@ -70,7 +110,7 @@ class AristaDriver(driver_api.MechanismDriver): self.eapi = AristaRPCWrapperEapi() self.mlag_pairs = dict() - self.provision_queue = Queue() + self.provision_queue = multiprocessing.Queue() self.trunk_driver = None def initialize(self):