Return value lost after threads finished
I have made two functions:
from nornir.plugins.tasks.networking import netmiko_send_config, netmiko_save_config def asa_add_config(task, template=None, **kwargs): cmd = task.run(task=template_file, name="Generating template", template=template, path="../network/templates/", conf_attr=kwargs) task.host["config"] = cmd.result task.run(task=netmiko_send_config, name="Sending configuration", config_commands=task.host["config"]) task.run(task=netmiko_save_config, name="Saving configuration", cmd="write memory") def send_net_objs_to_asa_host(hostname=None, payload=None): asa_host = connect_asa_host(hostname) response = asa_host.run(task=asa_add_config, template="asa_obj_net_confg.j2", **payload) return { "status": "success" if not response[hostname][0].failed else "failed", "result": response[hostname][1].result.rsplit("\n") }
The jinja template for preparing the object network {name}
command:
object network {{ conf_attr.object_id }} {% if conf_attr.network_type == "host" %} host {{ conf_attr.network_object1 }} {% elif conf_attr.network_type == "range" %} range {{ conf_attr.network_object1 }} {{ conf_attr.network_object2 }} {% elif conf_attr.network_type == "subnet" %} subnet {{ conf_attr.network_object1 }} {{ conf_attr.network_object2 }} {% endif %} {% if conf_attr.description is defined %} description {{ conf_attr.description }} {% endif %}
Then I create a test code to push multiple object network {name}
commands to a single cisco asa – fw02.
To achieve concurrency I use the threading.Thread
module, however my return value of send_net_objs_to_asa_host
is lost, I cannot get the return value from thread.join
as the documentation has already stated that join method always returns None
.
This is the code for testing:
from network.ciscoasa import send_net_objs_to_asa_host from threading import Thread from time import sleep payloads = [ { "object_id": "test_object5", "network_type": "range", "network_object1": "172.16.10.0", "network_object2": "172.16.10.10", "description": "set 1" }, { "object_id": "test_host1", "network_type": "host", "network_object1": "192.168.100.100", "description": "Set 2" }, { "object_id": "test_range1", "network_type": "range", "network_object1": "192.168.10.1", "network_object2": "192.168.10.100" }, { "object_id": "test_net1", "network_type": "subnet", "network_object1": "10.0.0.0", "network_object2": "255.0.0.0", "description": "classful A" } ] threads = [] for payload in payloads: t = Thread(target=send_net_objs_to_asa_host, args=("fw02", payload,)) threads.append(t) t.start() sleep(1) for thread in threads: thread.join()
commands were pushed but I got no way to get the return value.
Creating a subclass of threading.Thread and modify the behaviour of run and join
I found the answer in here. The child class added return value on run and join methods of the parent class, this is known as overriding.
Here’s the code of my sub class:
from threading import Thread """ See reference: https://stackoverflow.com/questions/6893968/how-to-get-the-return-value-from-a-thread-in-python/40344234#40344234 Check the answer by GuySoft. """ class AsaThread(Thread): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._return = None # child class's variable, not available in parent. def run(self): """ The original run method does not return value after self._target is run. This child class added a return value. :return: """ if self._target is not None: self._return = self._target(*self._args, **self._kwargs) def join(self, *args, **kwargs): """ Join normally like the parent class, but added a return value which the parent class join method does not have. """ super().join(*args, **kwargs) return self._return
Then on my main test code:
from network.ciscoasa import send_net_objs_to_asa_host from time import sleep from network.threads import AsaThread from pprint import pprint payloads = [ { "object_id": "test_object5", "network_type": "range", "network_object1": "172.16.10.0", "network_object2": "172.16.10.10", "description": "set 1" }, { "object_id": "test_host1", "network_type": "host", "network_object1": "192.168.100.100", "description": "Set 2" }, { "object_id": "test_range1", "network_type": "range", "network_object1": "192.168.10.1", "network_object2": "192.168.10.100" }, { "object_id": "test_net1", "network_type": "subnet", "network_object1": "10.0.0.0", "network_object2": "255.0.0.0", "description": "classful A" } ] threads = [] response_list = [] for payload in payloads: t = AsaThread(target=send_net_objs_to_asa_host, args=("fw02", payload,)) threads.append(t) t.start() sleep(1) for thread in threads: response_list.append(thread.join()) pprint(response_list)
The output looks like this:
The original parent Thread
does not return value on run
and join
, the overriding of the customed class AsaThread
added the return value functionality.
See the original Thread class run method:
A method if not specified defaults to return None, this is the same with function.
See the original Thread class join method: