Skip to content

Commit 5f263ad

Browse files
authored
Merge pull request #301 from networktocode/release-1.0.1
Release 1.0.1
2 parents 74c99a4 + 6ad3186 commit 5f263ad

File tree

14 files changed

+1048
-1107
lines changed

14 files changed

+1048
-1107
lines changed

.github/workflows/ci.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
strategy:
8484
fail-fast: true
8585
matrix:
86-
python-version: ["3.7", "3.8", "3.9"] # "3.10" add back after pyeapi new release.
86+
python-version: ["3.8", "3.9", "3.10", "3.11"] # "3.10" add back after pyeapi new release.
8787
runs-on: "ubuntu-20.04"
8888
env:
8989
PYTHON_VER: "${{ matrix.python-version }}"
@@ -156,7 +156,7 @@ jobs:
156156
strategy:
157157
fail-fast: true
158158
matrix:
159-
python-version: ["3.7", "3.8", "3.9"] # "3.10" add back after pyeapi new release.
159+
python-version: ["3.8", "3.9", "3.10", "3.11"]
160160
runs-on: "ubuntu-20.04"
161161
env:
162162
PYTHON_VER: "${{ matrix.python-version }}"

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ RUN apt-get update && \
1515
RUN pip install --upgrade pip
1616

1717
RUN curl -sSL https://install.python-poetry.org -o /tmp/install-poetry.py && \
18-
python /tmp/install-poetry.py --version 1.2.0 && \
18+
python /tmp/install-poetry.py --version 1.6.0 && \
1919
rm -f /tmp/install-poetry.py
2020

2121
# Add poetry install location to the $PATH

docs/admin/release_notes/version_1_0.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# v1.0 Release Notes
22

3+
## [1.0.1] 11-2023
4+
5+
### Fixed
6+
- [300](https://github.com/networktocode/pyntc/pull/300) Fixed default port value handling for `aireos, asa, ios` drivers, dropped python 3.7 support, updated pyeapi dependency, refreshed poetry dependencies.
7+
38
## [1.0.0] 04-2023
49

510
### Added

poetry.lock

+990-1,075
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyntc/__init__.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44
import warnings
5+
from importlib import metadata
56

67
from .devices import supported_devices
78
from .errors import ConfFileNotFoundError, DeviceNameNotFoundError, UnsupportedDeviceError
@@ -11,11 +12,6 @@
1112
except ImportError:
1213
from ConfigParser import SafeConfigParser
1314

14-
try:
15-
from importlib import metadata
16-
except ImportError:
17-
# Python version < 3.8
18-
import importlib_metadata as metadata
1915

2016
__version__ = metadata.version(__name__)
2117

pyntc/devices/aireos_device.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class AIREOSDevice(BaseDevice):
7070
active_redundancy_states = {None, "active"}
7171

7272
def __init__( # nosec # pylint: disable=too-many-arguments
73-
self, host, username, password, secret="", port=22, confirm_active=True, **kwargs
73+
self, host, username, password, secret="", port=None, confirm_active=True, **kwargs
7474
): # noqa: D403
7575
"""
7676
PyNTC Device implementation for Cisco WLC.
@@ -80,13 +80,13 @@ def __init__( # nosec # pylint: disable=too-many-arguments
8080
username (str): The username to authenticate with the device.
8181
password (str): The password to authenticate with the device.
8282
secret (str): The password to escalate privilege on the device.
83-
port (int): The port to use to establish the connection.
83+
port (int): The port to use to establish the connection. Defaults to 22.
8484
confirm_active (bool): Determines if device's high availability state should be validated before leaving connection open.
8585
"""
8686
super().__init__(host, username, password, device_type="cisco_aireos_ssh")
8787
self.native = None
8888
self.secret = secret
89-
self.port = int(port)
89+
self.port = int(port) if port else 22
9090
self.global_delay_factor = kwargs.get("global_delay_factor", 1)
9191
self.delay_factor = kwargs.get("delay_factor", 1)
9292
self._connected = False

pyntc/devices/asa_device.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,22 @@ class ASADevice(BaseDevice):
3838
vendor = "cisco"
3939
active_redundancy_states = {None, "active"}
4040

41-
def __init__(self, host: str, username: str, password: str, secret="", port=22, **kwargs): # nosec
41+
def __init__(self, host: str, username: str, password: str, secret="", port=None, **kwargs): # nosec
4242
"""
4343
Pyntc Device constructor for Cisco ASA.
4444
4545
Args:
4646
host (str): The address of the network device.
4747
username (str): The username to authenticate to the device.
4848
password (str): The password to authenticate to the device.
49-
secret (str, optional): The password to escalate privilege on the device. Defaults to "".
49+
secret (str, optional): The password to escalate privilege on the device. Defaults to 22.
5050
port (int, optional): Port used to establish connection. Defaults to 22.
5151
"""
5252
super().__init__(host, username, password, device_type="cisco_asa_ssh")
5353

5454
self.native: Optional[CiscoAsaSSH] = None
5555
self.secret = secret
56-
self.port = int(port)
56+
self.port = int(port) if port else 22
5757
self.kwargs = kwargs
5858
self.global_delay_factor: int = kwargs.get("global_delay_factor", 1)
5959
self.delay_factor: int = kwargs.get("delay_factor", 1)

pyntc/devices/ios_device.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class IOSDevice(BaseDevice):
4444
active_redundancy_states = {None, "active"}
4545

4646
def __init__( # nosec
47-
self, host, username, password, secret="", port=22, confirm_active=True, fast_cli=True, **kwargs
47+
self, host, username, password, secret="", port=None, confirm_active=True, fast_cli=True, **kwargs
4848
): # noqa: D403
4949
"""
5050
PyNTC Device implementation for Cisco IOS.
@@ -54,15 +54,15 @@ def __init__( # nosec
5454
username (str): The username to authenticate with the device.
5555
password (str): The password to authenticate with the device.
5656
secret (str): The password to escalate privilege on the device.
57-
port (int): The port to use to establish the connection.
57+
port (int): The port to use to establish the connection. Defaults to 22.
5858
confirm_active (bool): Determines if device's high availability state should be validated before leaving connection open.
5959
fast_cli (bool): Fast CLI mode for Netmiko, it is recommended to use False when opening the client on code upgrades
6060
"""
6161
super().__init__(host, username, password, device_type="cisco_ios_ssh")
6262

6363
self.native = None
6464
self.secret = secret
65-
self.port = int(port)
65+
self.port = int(port) if port else 22
6666
self.global_delay_factor = kwargs.get("global_delay_factor", 1)
6767
self.delay_factor = kwargs.get("delay_factor", 1)
6868
self._fast_cli = fast_cli

pyproject.toml

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "pyntc"
3-
version = "1.0.0"
3+
version = "1.0.1"
44
description = "SDK to simplify common workflows for Network Devices."
55
authors = ["Network to Code, LLC <[email protected]>"]
66
readme = "README.md"
@@ -24,14 +24,11 @@ include = [
2424
]
2525

2626
[tool.poetry.dependencies]
27-
python = "^3.7"
28-
# Required for Python 3.7 for now. See: https://stackoverflow.com/a/73932581/194311
29-
importlib-metadata = "4.13.0"
27+
python = "^3.8"
3028
f5-sdk = "^3.0.21"
3129
junos-eznc = "^2.6"
3230
netmiko = "^4.0"
33-
# pyeapi doesn't support py3.10 yet in a release, and pypi doesn't allow direct dependencies. py3.10 to work. https://github.com/arista-eosplus/pyeapi/blob/236503162d1aa3ecc953678ec05380f1f605be02/pyeapi/api/abstract.py#L44
34-
pyeapi = "^0.8.4"
31+
pyeapi = "^1.0.2"
3532
pynxos = "^0.0.5"
3633
requests = "^2.28"
3734
scp = "^0.14"

tests/unit/test_devices/test_aireos_device.py

+10
Original file line numberDiff line numberDiff line change
@@ -1671,3 +1671,13 @@ def test_uptime_string(mock_uptime_components, aireos_device):
16711671
def test_wlans(aireos_show, aireos_expected_wlans):
16721672
device = aireos_show(["show_wlan_summary.txt"])
16731673
assert device.wlans == aireos_expected_wlans
1674+
1675+
1676+
def test_port(aireos_device):
1677+
assert aireos_device.port == 22
1678+
1679+
1680+
@mock.patch.object(AIREOSDevice, "open")
1681+
def test_port_none(patch):
1682+
device = AIREOSDevice("host", "user", "pass", port=None)
1683+
assert device.port == 22

tests/unit/test_devices/test_asa_device.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
class TestASADevice:
4242
def setup(self, api):
4343
with mock.patch("pyntc.devices.asa_device.ConnectHandler") as api:
44-
4544
if not getattr(self, "device", None):
4645
self.device = ASADevice("host", "user", "password")
4746

@@ -66,6 +65,9 @@ def teardown(self):
6665
self.device.native.reset_mock()
6766
self.count_teardown += 1
6867

68+
def test_port(self):
69+
assert self.device.port == 22
70+
6971
@mock.patch.object(ASADevice, "_get_file_system", return_value="disk0:")
7072
def test_boot_options_dir(self, mock_boot):
7173
self.device.native.send_command_timing.side_effect = None
@@ -891,3 +893,9 @@ def test_vlan(mock_get_vlans, asa_device):
891893
mock_get_vlans.return_value = expected
892894
vlans = asa_device.vlans
893895
assert vlans == [10, 20]
896+
897+
898+
@mock.patch.object(ASADevice, "open")
899+
def test_port_none(patch):
900+
device = ASADevice("host", "user", "pass", port=None)
901+
assert device.port == 22

tests/unit/test_devices/test_eos_device.py

-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ def test_file_copy_remote_exists_bad_md5(self, mock_open, mock_close, mock_ssh,
212212
@mock.patch.object(EOSDevice, "close")
213213
@mock.patch("netmiko.arista.arista.AristaSSH", autospec=True)
214214
def test_file_copy_remote_not_exist(self, mock_open, mock_close, mock_ssh, mock_ft):
215-
216215
self.device.native_ssh = mock_open
217216
self.device.native_ssh.send_command_timing.side_effect = None
218217
self.device.native_ssh.send_command_timing.return_value = "flash: /dev/null"

tests/unit/test_devices/test_f5_device.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def test_reboot(self):
132132

133133
volume = VOLUME
134134
# skip the wait_for_device_reboot
135-
with (mock.patch.object(self.device, "_wait_for_device_reboot", return_value=True)):
135+
with mock.patch.object(self.device, "_wait_for_device_reboot", return_value=True):
136136
self.device.reboot(volume=volume)
137137

138138
# # Check if _get_active_volume worked
@@ -146,7 +146,7 @@ def test_reboot_with_timer(self):
146146
api.tm.sys.software.volumes.volume.load.return_value.active = True
147147

148148
# skipping timeout! It's too long!!
149-
with (mock.patch.object(self.device, "_wait_for_device_reboot", timeout=0)):
149+
with mock.patch.object(self.device, "_wait_for_device_reboot", timeout=0):
150150
self.device.reboot(volume=volume)
151151

152152
# # Check if _get_active_volume worked
@@ -157,7 +157,7 @@ def test_reboot_with_timer(self):
157157
def test_reboot_no_volume(self):
158158
api = self.device.api_handler
159159

160-
with (mock.patch.object(self.device, "_wait_for_device_reboot", return_value=True)):
160+
with mock.patch.object(self.device, "_wait_for_device_reboot", return_value=True):
161161
self.device.reboot()
162162

163163
# Check if _reboot_to_volume worked
@@ -175,7 +175,7 @@ def test_set_boot_options(self):
175175
# Patching out _volume_exists for _image_install
176176
api.tm.sys.software.volumes.volume.exists.return_value = True
177177

178-
with (mock.patch.object(self.device, "_wait_for_image_installed", timeout=0, return_value=None)):
178+
with mock.patch.object(self.device, "_wait_for_image_installed", timeout=0, return_value=None):
179179
self.device.set_boot_options(image_name=image_name, volume=volume)
180180

181181
api.tm.util.bash.exec_cmd.assert_called()
@@ -194,7 +194,7 @@ def test_set_boot_options_no_image(self):
194194
# Patching out _volume_exists for _image_install
195195
api.tm.sys.software.volumes.volume.exists.return_value = False
196196

197-
with (mock.patch.object(self.device, "_wait_for_image_installed", timeout=0, return_value=None)):
197+
with mock.patch.object(self.device, "_wait_for_image_installed", timeout=0, return_value=None):
198198
self.device.set_boot_options(image_name=image_name, volume=volume)
199199

200200
api.tm.util.bash.exec_cmd.assert_called()
@@ -215,7 +215,7 @@ def test_set_boot_options_bad_boot(self):
215215
# Patching out _volume_exists for _image_install
216216
api.tm.sys.software.volumes.volume.exists.return_value = False
217217

218-
with (mock.patch.object(self.device, "_wait_for_image_installed", timeout=0, return_value=None)):
218+
with mock.patch.object(self.device, "_wait_for_image_installed", timeout=0, return_value=None):
219219
with pytest.raises(NTCFileNotFoundError):
220220
self.device.set_boot_options(image_name="bad_image", volume=volume)
221221

@@ -248,7 +248,7 @@ def test_install_os(self):
248248
# Patching out _image_install
249249
api.tm.sys.software.volumes.volume.exists.return_value = True
250250

251-
with (mock.patch.object(self.device, "_wait_for_image_installed", timeout=0, return_value=None)):
251+
with mock.patch.object(self.device, "_wait_for_image_installed", timeout=0, return_value=None):
252252
self.device.install_os(image_name=image_name, volume=volume)
253253

254254
api.tm.util.bash.exec_cmd.assert_called()

tests/unit/test_devices/test_ios_device.py

+11
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ def tearDown(self):
7878
# Reset the mock so we don't have transient test effects
7979
self.device.native.reset_mock()
8080

81+
def test_port(self):
82+
self.assertEqual(self.device.port, 22)
83+
8184
def test_bad_show(self):
8285
command = "show microsoft"
8386
self.device.native.send_command.return_value = "Error: Microsoft"
@@ -393,6 +396,12 @@ def test_install_os_error(self, mock_wait, mock_reboot, mock_set_boot, mock_imag
393396
unittest.main()
394397

395398

399+
@mock.patch.object(IOSDevice, "open")
400+
def test_port_none(patch):
401+
device = IOSDevice("host", "user", "pass", port=None)
402+
assert device.port == 22
403+
404+
396405
def test_check_command_output_for_errors(ios_device):
397406
command_passes = ios_device._check_command_output_for_errors("valid command", "valid output")
398407
assert command_passes is None
@@ -978,6 +987,7 @@ def test_set_boot_options_image_packages_conf_file(
978987
# TESTS FOR IOS INSTALL MODE METHOD
979988
#
980989

990+
981991
# Test install mode upgrade for install mode with latest method
982992
@mock.patch.object(IOSDevice, "os_version", new_callable=mock.PropertyMock)
983993
@mock.patch.object(IOSDevice, "_image_booted")
@@ -1106,6 +1116,7 @@ def test_install_os_install_mode_no_upgrade(
11061116
# FROM CISCO IOS EVEREST VERSION TESTS
11071117
#
11081118

1119+
11091120
# Test install mode upgrade for install mode with interim method on OS Version
11101121
@mock.patch.object(IOSDevice, "os_version", new_callable=mock.PropertyMock)
11111122
@mock.patch.object(IOSDevice, "_image_booted")

0 commit comments

Comments
 (0)