Repository: juniper/py-junos-eznc
Issue: Run-time error when using "outbound-ssh" with PyEZ

Here is my environment...
* Python Server
  * Python 3.7
  * junos-eznc v2.2.0
  * Macbook Pro
* Juniper device
  * Model = EX-4600
  * Junos 18.4R1.8

I am seeing this run-time error when using PyEZ as an "outbound-ssh" server running on my local development machine.  The "outbound-ssh" connection is being received fine, but PyEZ has a run-time error while performing an SSH config lookup...

```
Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
    self.run()
  File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/mark/go/src/github.com/mistsys/mist-ai4it-collectors/python/outbound_ssh_pyez.py", line 10, in PyezClient
    dev = Device(sock_fd=client, user="mist", password="mist@1601")
  File "/Users/mark/go/src/github.com/mistsys/mist-ai4it-collectors/python/venv3/lib/python3.7/site-packages/jnpr/junos/device.py", line 1156, in __init__
    self._sshconf_lkup()
  File "/Users/mark/go/src/github.com/mistsys/mist-ai4it-collectors/python/venv3/lib/python3.7/site-packages/jnpr/junos/device.py", line 407, in _sshconf_lkup
    found = sshconf.lookup(self._hostname)
  File "/Users/mark/go/src/github.com/mistsys/mist-ai4it-collectors/python/venv3/lib/python3.7/site-packages/paramiko/config.py", line 118, in lookup
    for config in self._config
  File "/Users/mark/go/src/github.com/mistsys/mist-ai4it-collectors/python/venv3/lib/python3.7/site-packages/paramiko/config.py", line 119, in <listcomp>
    if self._allowed(config["host"], hostname)
  File "/Users/mark/go/src/github.com/mistsys/mist-ai4it-collectors/python/venv3/lib/python3.7/site-packages/paramiko/config.py", line 154, in _allowed
    elif fnmatch.fnmatch(hostname, host):
  File "/Users/mark/go/src/github.com/mistsys/mist-ai4it-collectors/python/venv3/lib/python3.7/fnmatch.py", line 34, in fnmatch
    name = os.path.normcase(name)
  File "/Users/mark/go/src/github.com/mistsys/mist-ai4it-collectors/python/venv3/bin/../lib/python3.7/posixpath.py", line 54, in normcase
    s = os.fspath(s)
TypeError: expected str, bytes or os.PathLike object, not NoneType
```

I have tried two different `~/.ssh/config` files, and I see the same run-time error with either SSH config file...

*SSH config file #1*
```
Host 10.2.15.243
 AddKeysToAgent yes
 UseKeychain yes
 IdentityFile ~/.ssh/id_rsa
Host 10.2.11.137
 AddKeysToAgent yes
 UseKeychain yes
 IdentityFile ~/.ssh/id_rsa
```

*SSH config file #2*
```
Host *
 AddKeysToAgent yes
 UseKeychain yes
 IdentityFile ~/.ssh/id_rsa
```

My python script (`outbound_ssh_pyez.py`) and `requirements.txt` files are attached.

* [requirements.txt](https://github.com/Juniper/py-junos-eznc/files/2981017/requirements.txt)
* [outbound_ssh_pyez.py.zip](https://github.com/Juniper/py-junos-eznc/files/2981018/outbound_ssh_pyez.py.zip)

It looks like the PyEZ "device" class is referencing `self._hostname` when it should not be (whenever `sock_fd` is passed to `Device()` for "outbound-ssh" scenarios).

Also, here is my Junos "outbound-ssh" config, which appears to be working fine (correctly reaches my python server)...
```
    services {
        ssh {
            root-login allow;
            protocol-version v2;
        }
        netconf {
            ssh;
        }
        outbound-ssh {
            traceoptions {
                file outbound-ssh-trace.txt size 1m files 20;
                flag all;
            }
            client mist-cloud-production {
                device-id jnpr-test-device;
                secret "$9$FIWYnCuSyK8xdevMLx7Vb5QzF9Ap0B"; ## SECRET-DATA
                keep-alive {
                    retry 3;
                    timeout 5;
                }
                services netconf;
                10.2.15.211 {
                    port 2222;
                    retry 99999;
                    timeout 15;
                }
            }
        }
    }
```


Changed files:
lib/jnpr/junos/device.py, tests/unit/test_device.py

diff --git a/lib/jnpr/junos/device.py b/lib/jnpr/junos/device.py
index 12fcfadb..a4316cee 100644
--- a/lib/jnpr/junos/device.py
+++ b/lib/jnpr/junos/device.py
@@ -391,6 +391,9 @@ class _Connection(object):
         raise RuntimeError("re_name is read-only!")
 
     def _sshconf_lkup(self):
+        # When sockfd passed to device for 'outbound ssh'
+        if self.__class__.__name__ == 'Device' and self._sock_fd is not None:
+            return None
         if self._ssh_config:
             sshconf_path = os.path.expanduser(self._ssh_config)
         else:
diff --git a/tests/unit/test_device.py b/tests/unit/test_device.py
index 34263bc7..d3e7bca3 100644
--- a/tests/unit/test_device.py
+++ b/tests/unit/test_device.py
@@ -345,6 +345,12 @@ class TestDevice(unittest.TestCase):
         self.dev._sshconf_lkup()
         mock_paramiko.assert_called_once_with('1.1.1.1')
 
+    @patch('paramiko.config.SSHConfig.lookup')
+    def test_device__sshconf_lkup_sock_fd(self, mock_paramiko):
+        self.dev2 = Device(sock_fd=6)
+        self.dev2._sshconf_lkup()
+        self.assertEqual(self.dev2._sshconf_lkup(), None)
+
     @patch('os.getenv')
     def test_device__sshconf_lkup_path_not_exists(self, mock_env):
         mock_env.return_value = '/home/test'