Thread safety in jnpr.junos.Device

109 views
Skip to first unread message

Chris Lorier

unread,
Mar 21, 2017, 9:39:43 PM3/21/17
to Junos Python EZ
Hi,

I am trying to log into multiple devices, but I am hitting thread safety issues with the Device object.

I am running the following code:
import sys
import getpass
import logging
from concurrent.futures import ThreadPoolExecutor
from jnpr.junos import Device

logger = logging.getLogger(__name__)

class OpenAndPrintHello(object):
    def __init__(self, target):
        self.device = Device(
            target,
            user=getpass.getuser(),
            port=22,
            autoprobe=False
            )

    def __call__(self):
        self.device.open()
        logging.warning("hello")
        self.device.close()

with ThreadPoolExecutor() as executor:
    for target in sys.argv[1:]:
        executor.submit(OpenAndPrintHello(target))

 If I give it more than 11 devices to log into I start to get errors:
Unknown exception: module 'pkg_resources' has no attribute 'iter_entry_points'
Traceback (most recent call last):
  File "/home/chrislorier/envs/netconf/lib/python3.5/site-packages/paramiko/transport.py", line 1783, in run
    self.kex_engine.parse_next(ptype, m)
  File "/home/chrislorier/envs/netconf/lib/python3.5/site-packages/paramiko/kex_group1.py", line 75, in parse_next
    return self._parse_kexdh_reply(m)
  File "/home/chrislorier/envs/netconf/lib/python3.5/site-packages/paramiko/kex_group1.py", line 111, in _parse_kexdh_reply
    self.transport._verify_key(host_key, sig)
  File "/home/chrislorier/envs/netconf/lib/python3.5/site-packages/paramiko/transport.py", line 1628, in _verify_key
    key = self._key_info[self.host_key_type](Message(host_key))
  File "/home/chrislorier/envs/netconf/lib/python3.5/site-packages/paramiko/rsakey.py", line 59, in __init__
    ).public_key(default_backend())
  File "/home/chrislorier/envs/netconf/lib/python3.5/site-packages/cryptography/hazmat/backends/__init__.py", line 74, in default_backend
    _default_backend = MultiBackend(_available_backends())
  File "/home/chrislorier/envs/netconf/lib/python3.5/site-packages/cryptography/hazmat/backends/__init__.py", line 30, in _available_backends
    for ep in pkg_resources.iter_entry_points(
AttributeError: module 'pkg_resources' has no attribute 'iter_entry_points'

I think the issue is the cryptography library is trying to set a global variable in a non thread safe way. I have found that I can get around this by calling Device.open() once before trying to parallelise, but I am not particularly confident in that approach. I also could use processes rather than threads for each device, but I would prefer not to.

How do you recommend I get around this issue?

For completeness here is the list of versions of libraries I am running:
asn1crypto                0.22.0                    <pip>
cffi                      1.9.1                     <pip>
cryptography              1.8.1                     <pip>
idna                      2.5                       <pip>
Jinja2                    2.9.5                     <pip>
junos-eznc                2.0.1                     <pip>
lxml                      3.7.3                     <pip>
MarkupSafe                1.0                       <pip>
ncclient                  0.5.3                     <pip>
netaddr                   0.7.19                    <pip>
openssl                   1.0.2k                        1
packaging                 16.8                      <pip>
paramiko                  2.1.2                     <pip>
pip                       9.0.1                    py35_1
pyasn1                    0.2.3                     <pip>
pycparser                 2.17                      <pip>
pyparsing                 2.2.0                     <pip>
pyserial                  3.3                       <pip>
python                    3.5.3                         1
PyYAML                    3.12                      <pip>
readline                  6.2                           2
scp                       0.10.2                    <pip>
setuptools                27.2.0                   py35_0
six                       1.10.0                    <pip>
sqlite                    3.13.0                        0
tk                        8.5.18                        0
wheel                     0.29.0                   py35_0
xz                        5.2.2                         1
zlib                      1.2.8                         3 

Thanks 

Ansel Gaddy

unread,
Mar 23, 2017, 12:43:25 PM3/23/17
to Junos Python EZ
I have had great results using Deco module to make multiple simultaneous connections.

from deco import *
from jnpr.junos import Device
from jnpr.junos.exception import ConnectError,RpcError

@concurrent # <- this is the Deco part that makes it multithreaded
def get_disabled_lag_int_junos(**kvargs):
    dev = Device(host=kvargs['hostname'], user=kvargs['user'], password=kvargs['password'])
    xml_gotten = False
    result_attributes = False
    try:
        dev.open(gather_facts=False)
        print('{0} - connected to device {1}'.format(_get_time(),kvargs['hostname']))
        dev.timeout = 30
        xml = dev.rpc.get_configuration()
        xml_gotten = True
        dev.close()
        if 'verbose' in kvargs.keys():
            if kvargs['verbose']:
                print('{0} - got config from router {1} {2}'.format(_get_time(), kvargs['hostname'], kvargs['user']))
    except ConnectError as err:
        print('{0} - Unable to connect to router! {1} {2} {3}'.format(_get_time(), kvargs['hostname'], err, kvargs['user']))
    except RpcError as err: # attempt to retry
        print('{0} - Unable to get RPC! {1} {2} {3}'.format(_get_time(), kvargs['hostname'], err, kvargs['user']))
    if xml_gotten:
        # config section
        result_attributes_ether = identify_ether_options_inactive(xml)
        result_attributes_gigether = identify_gigether_options_inactive(xml)
        if result_attributes_ether:
            result_attributes = result_attributes_ether
        elif result_attributes_gigether:
            result_attributes = result_attributes_gigether
        # lacp section


    return {'result':result_attributes,'date':_get_time(),'hostname':kvargs['hostname']}


@synchronized
def map_get_disabled_lag_ints(**kwargs):
    results = [get_disabled_lag_int_junos(hostname=x,user=kwargs['user'],password=kwargs['password']) for x in kwargs['hosts']]
    result_gets = [x.get() for x in results]
    return(result_gets)

Chris Lorier

unread,
Apr 12, 2017, 6:56:52 PM4/12/17
to Junos Python EZ
Ok, it seems like the issue here was the way pip and conda built cryptography. When I was using pip to install cryptography in my conda environment I would get:
 c/_cffi_backend.c:15:17: fatal error: ffi.h: No such file or directory
   #include <ffi.h>

Which would fail to build the wheel, so it would use setup.py instead.

To fix this I got conda to build cryptography when I created the environment rather than using pip. This seems to have fixed the issue, but conda also seems to be unable to locate cryptography 1.8.1, so I am stuck with 1.7.1 instead.

I'm not sure if the issue is with cryptography 1.8.1 or with installing cryptography with setuptools, but it seems more likely to be the latter.

Nitin Kumar

unread,
Apr 13, 2017, 6:47:26 AM4/13/17
to Chris Lorier, Junos Python EZ

Hi Chris,

 

Did you install libffi-dev

https://cryptography.io/en/latest/installation/#building-cryptography-on-linux

 

Thanks

Nitin Kr

--
You received this message because you are subscribed to the Google Groups "Junos Python EZ" group.
To unsubscribe from this group and stop receiving emails from it, send an email to junos-python-...@googlegroups.com.
Visit this group at https://groups.google.com/group/junos-python-ez.
To view this discussion on the web visit https://groups.google.com/d/msgid/junos-python-ez/de65e92d-3949-4ae9-924e-1d0bcb9e0112%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages