Repository: napalm-automation/napalm

Issue:
Unable to setup custom port for iosxr_netconf
### Problem
Unable to setup custom port for netconf connection. Exception is raised

### Did you follow the steps from https://github.com/napalm-automation/napalm#faq
- [x] Yes

### Setup
`napalm==4.0.0` or `napalm==3.4.1`

### Steps to Reproduce the Issue
`napalm --user USERNAME --password PASSWORD --vendor iosxr_netconf --debug HOSTNAME--optional_args "port=22" call get_interfaces_ip`

### Error Traceback
```
2022-05-30 15:58:03,654 - napalm - DEBUG - Starting napalm's debugging tool
2022-05-30 15:58:03,654 - napalm - DEBUG - Gathering napalm packages
2022-05-30 15:58:03,654 - napalm - DEBUG - napalm==4.0.0 # or napalm==3.4.1
2022-05-30 15:58:03,654 - napalm - DEBUG - get_network_driver - Calling with args: ('iosxr_netconf',), {}
2022-05-30 15:58:03,687 - napalm - DEBUG - get_network_driver - Successful
2022-05-30 15:58:03,687 - napalm - DEBUG - __init__ - Calling with args: (<class 'napalm.iosxr_netconf.iosxr_netconf.IOSXRNETCONFDriver'>, 'HOSTNAME', 'USERNAME'), {'password': '*******', 'timeout': 60, 'optional_args': {'port': 22}}
2022-05-30 15:58:03,687 - napalm - DEBUG - __init__ - Successful
2022-05-30 15:58:03,687 - napalm - DEBUG - pre_connection_tests - Calling with args: (<napalm.iosxr_netconf.iosxr_netconf.IOSXRNETCONFDriver object at 0x7f210fb239d0>,), {}
2022-05-30 15:58:03,687 - napalm - DEBUG - open - Calling with args: (<napalm.iosxr_netconf.iosxr_netconf.IOSXRNETCONFDriver object at 0x7f210fb239d0>,), {}
2022-05-30 15:58:03,687 - napalm.iosxr_netconf.iosxr_netconf - ERROR - connect() got multiple values for keyword argument 'port'
2022-05-30 15:58:03,687 - napalm - ERROR - open - Failed: connect() got multiple values for keyword argument 'port'

================= Traceback =================

Traceback (most recent call last):
  File "/home/jankrupa/napalm_test/napalm/napalm/iosxr_netconf/iosxr_netconf.py", line 90, in open
    self.device = manager.connect(
TypeError: connect() got multiple values for keyword argument 'port'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/jankrupa/napalm_test/venv/bin/napalm", line 11, in <module>
    load_entry_point('napalm', 'console_scripts', 'napalm')()
  File "/home/jankrupa/napalm_test/napalm/napalm/base/clitools/cl_napalm.py", line 308, in main
    run_tests(args)
  File "/home/jankrupa/napalm_test/napalm/napalm/base/clitools/cl_napalm.py", line 283, in run_tests
    call_open_device(device)
  File "/home/jankrupa/napalm_test/napalm/napalm/base/clitools/cl_napalm.py", line 27, in wrapper
    r = func(*args, **kwargs)
  File "/home/jankrupa/napalm_test/napalm/napalm/base/clitools/cl_napalm.py", line 207, in call_open_device
    return device.open()
  File "/home/jankrupa/napalm_test/napalm/napalm/iosxr_netconf/iosxr_netconf.py", line 104, in open
    raise ConnectionException(conn_err.args[0])
napalm.base.exceptions.ConnectionException: connect() got multiple values for keyword argument 'port'

```

### Problem

https://github.com/napalm-automation/napalm/blob/3.4.1/napalm/iosxr_netconf/iosxr_netconf.py#L89
```python
            self.device = manager.connect(
                host=self.hostname,
                port=self.port,
                username=self.username,
                password=self.password,
                key_filename=self.key_file,
                timeout=self.timeout,
                device_params={"name": "iosxr"},
                **self.netmiko_optional_args,
            )
```
`port=self.port` is tried to be overwritten by `**self.netmiko_optional_args` which contains `{'port': 22}`
It raises error "got multiple values for keyword argument 'port'"




Discussion:

Resolving patch:
diff --git a/napalm/iosxr_netconf/iosxr_netconf.py b/napalm/iosxr_netconf/iosxr_netconf.py
--- a/napalm/iosxr_netconf/iosxr_netconf.py
+++ b/napalm/iosxr_netconf/iosxr_netconf.py
@@ -66,14 +66,11 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
         self.pending_changes = False
         self.replace = False
         self.locked = False
-        if optional_args is None:
-            optional_args = {}
-
-        self.netmiko_optional_args = optional_args
-        self.port = optional_args.get("port", 830)
-        self.lock_on_connect = optional_args.get("config_lock", False)
-        self.key_file = optional_args.get("key_file", None)
-        self.config_encoding = optional_args.get("config_encoding", "cli")
+        self.optional_args = optional_args if optional_args else {}
+        self.port = self.optional_args.pop("port", 830)
+        self.lock_on_connect = self.optional_args.pop("config_lock", False)
+        self.key_file = self.optional_args.pop("key_file", None)
+        self.config_encoding = self.optional_args.pop("config_encoding", "cli")
         if self.config_encoding not in C.CONFIG_ENCODINGS:
             raise ValueError(f"config encoding must be one of {C.CONFIG_ENCODINGS}")
 
@@ -92,7 +89,7 @@ def open(self):
                 key_filename=self.key_file,
                 timeout=self.timeout,
                 device_params={"name": "iosxr"},
-                **self.netmiko_optional_args,
+                **self.optional_args,
             )
             if self.lock_on_connect:
                 self._lock()


Test patch: