Skip to content

Commit 510aa8c

Browse files
authored
Merge pull request #314 from networktocode/develop
Release 2.0.1
2 parents 3014dfa + aa806ed commit 510aa8c

File tree

20 files changed

+689
-583
lines changed

20 files changed

+689
-583
lines changed

docs/admin/release_notes/version_2_0.md

+8
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,11 @@
1212
- [308](https://github.com/networktocode/pyntc/pull/308) Deprecated netmiko argument `delay_factor` in favor of `read_timeout` as per changes in Netmiko 4.0.
1313

1414
Refer to this blog post for more info about changes in netmiko 4.0: https://pynet.twb-tech.com/blog/netmiko-read-timeout.html
15+
16+
17+
## [2.0.1] 09-2024
18+
### Added
19+
- [311](https://github.com/networktocode/pyntc/pull/311) Extend cisco_ios set_boot_options method.
20+
21+
### Fixed
22+
- [312](https://github.com/networktocode/pyntc/pull/312) Fix Arista EOS file copy issues.

poetry.lock

+645-566
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyntc/devices/asa_device.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def _get_ipv4_addresses(self, host: str) -> Dict[str, List[IPv4Address]]:
156156
elif host == "peer":
157157
command = "failover exec mate show ip address"
158158

159-
show_ip_address = self.show(command)
159+
show_ip_address = self.show(command) # pylint: disable=possibly-used-before-assignment
160160
re_ip_addresses = RE_SHOW_IP_ADDRESS.findall(show_ip_address)
161161

162162
results = {
@@ -188,7 +188,7 @@ def _get_ipv6_addresses(self, host: str) -> Dict[str, List[IPv6Address]]:
188188
elif host == "peer":
189189
command = "failover exec mate show ipv6 interface"
190190

191-
show_ipv6_interface = self.show(command)
191+
show_ipv6_interface = self.show(command) # pylint: disable=possibly-used-before-assignment
192192
show_ipv6_interface_lines: List[str] = show_ipv6_interface.strip().splitlines()
193193
first_line = show_ipv6_interface_lines.pop(0)
194194
interface: str = first_line.split()[0]

pyntc/devices/eos_device.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ def __init__(self, host, username, password, transport="http", port=None, timeou
7373
self._connected = False
7474
log.init(host=host)
7575

76-
def _file_copy_instance(self, src, dest=None, file_system="flash:"):
76+
def _file_copy_instance(self, src, dest=None, file_system="/mnt/flash"):
77+
# "flash:" is only valid locally, "/mnt/flash" is used externally
78+
if file_system == "flash:":
79+
file_system = "/mnt/flash"
7780
if dest is None:
7881
dest = os.path.basename(src)
7982

@@ -175,7 +178,7 @@ def boot_options(self):
175178
dict: Key is ``sys`` with value being the image on the device.
176179
"""
177180
image = self.show("show boot-config")["softwareImage"]
178-
image = image.replace("flash:", "")
181+
image = image.replace("flash:/", "")
179182
log.debug("Host %s: the boot options are %s", self.host, {"sys": image})
180183
return {"sys": image}
181184

@@ -375,7 +378,7 @@ def file_copy(self, src, dest=None, file_system=None):
375378
file_copy = self._file_copy_instance(src, dest, file_system=file_system)
376379

377380
try:
378-
file_copy.enable_scp()
381+
# file_copy.enable_scp()
379382
file_copy.establish_scp_conn()
380383
file_copy.transfer_file()
381384
log.info("Host %s: File %s transferred successfully.", self.host, src)

pyntc/devices/ios_device.py

+5
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,11 @@ def set_boot_options(self, image_name, **vendor_specifics):
10291029
): # TODO: Update to CommandError when deprecating config_list
10301030
command = f"boot system switch all {file_system}{image_name}"
10311031
self.config(["no boot system", command])
1032+
1033+
# If boot system is not found in the running config, but it exists in show boot or show bootvar
1034+
elif self.boot_options["sys"]:
1035+
command = f"boot system {file_system}/{image_name}"
1036+
self.config(command)
10321037
else:
10331038
raise CommandError(
10341039
command=command,

pyntc/devices/iosxewlc_device.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Module for using a Cisco IOSXE WLC device over SSH."""
2+
23
import time
34

45
from pyntc import log

pyntc/devices/jnpr_device.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Module for using a Juniper junOS device."""
2+
23
import hashlib
34
import os
45
import re

pyntc/devices/nxos_device.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Module for using an NXOS device over NX-API."""
2+
23
import os
34
import re
45
import time

pyntc/devices/system_features/vlans/base_vlans.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Base Vlan checks."""
2+
23
from pyntc.errors import NTCError
34

45
from ..base_feature import BaseFeature

pyntc/errors.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""pyntc custom exceptions."""
2+
23
import warnings
34

45

pyntc/log.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Logging utilities for Pyntc."""
2+
23
import os
34
import logging
45

pyntc/utils/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""PyNTC Utilities."""
2+
23
from .templates import get_structured_data
34
from .converters import convert_dict_by_key, convert_list_by_key, recursive_key_lookup
45

pyntc/utils/templates/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Module to use NTC_TEMPLATES."""
2+
23
import os
34
import textfsm
45

pyproject.toml

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

tasks.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Tasks for use with Invoke."""
2+
23
import os
34
import sys
45
from distutils.util import strtobool

tests/unit/test_devices/device_mocks/eos/enable_json/show_boot-config

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"command": "show boot-config",
33
"result": {
44
"memTestIterations": 0,
5-
"softwareImage": "flash:EOS.swi",
5+
"softwareImage": "EOS.swi",
66
"abootPassword": "(not set)"
77
},
88
"encoding": "json"

tests/unit/test_devices/test_asa_device.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040

4141
class TestASADevice:
42-
def setup(self, api):
42+
def setup_method(self, api):
4343
with mock.patch("pyntc.devices.asa_device.ConnectHandler") as api:
4444
if not getattr(self, "device", None):
4545
self.device = ASADevice("host", "user", "password")
@@ -60,7 +60,7 @@ def setup(self, api):
6060
self.device.native = api
6161
self.count_setup += 1
6262

63-
def teardown(self):
63+
def teardown_method(self):
6464
# Reset the mock so we don't have transient test effects
6565
self.device.native.reset_mock()
6666
self.count_teardown += 1

tests/unit/test_devices/test_eos_device.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,10 @@ def test_file_copy(self, mock_open, mock_close, mock_ssh, mock_ft):
237237
mock_ft_instance.check_file_exists.side_effect = [False, True]
238238
self.device.file_copy("path/to/source_file")
239239

240-
mock_ft.assert_called_with(self.device.native_ssh, "path/to/source_file", "source_file", file_system="flash:")
241-
mock_ft_instance.enable_scp.assert_any_call()
240+
mock_ft.assert_called_with(
241+
self.device.native_ssh, "path/to/source_file", "source_file", file_system="/mnt/flash"
242+
)
243+
# mock_ft_instance.enable_scp.assert_any_call()
242244
mock_ft_instance.establish_scp_conn.assert_any_call()
243245
mock_ft_instance.transfer_file.assert_any_call()
244246

@@ -255,8 +257,8 @@ def test_file_copy_different_dest(self, mock_open, mock_close, mock_ssh, mock_ft
255257
mock_ft_instance.check_file_exists.side_effect = [False, True]
256258
self.device.file_copy("source_file", "dest_file")
257259

258-
mock_ft.assert_called_with(self.device.native_ssh, "source_file", "dest_file", file_system="flash:")
259-
mock_ft_instance.enable_scp.assert_any_call()
260+
mock_ft.assert_called_with(self.device.native_ssh, "source_file", "dest_file", file_system="/mnt/flash")
261+
# mock_ft_instance.enable_scp.assert_any_call()
260262
mock_ft_instance.establish_scp_conn.assert_any_call()
261263
mock_ft_instance.transfer_file.assert_any_call()
262264

@@ -289,7 +291,7 @@ def test_set_boot_options(self):
289291
[{"result": {"output": "flash:"}}],
290292
[{"result": {"output": "new_image.swi"}}],
291293
[{"result": {}}],
292-
[{"result": {"softwareImage": "flash:new_image.swi"}}],
294+
[{"result": {"softwareImage": "flash:/new_image.swi"}}],
293295
]
294296
calls = [
295297
mock.call(["dir"], encoding="text"),

tests/unit/test_devices/test_f5_device.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, fullPath, version, build):
2727

2828

2929
class TestF5Device:
30-
def setup(self):
30+
def setup_method(self):
3131
with mock.patch("pyntc.devices.f5_device.ManagementRoot") as big_ip:
3232
self.device = F5Device("host", "user", "password")
3333
self.device.api_handler = big_ip
@@ -40,7 +40,7 @@ def setup(self):
4040

4141
self.count_setup += 1
4242

43-
def teardown(self):
43+
def teardown_method(self):
4444
self.device.api_handler.reset_mock()
4545
self.count_teardown += 1
4646

tests/unit/test_devices/test_ios_device.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ def test_set_boot_options_pass_with_switch_all(mock_save, mock_boot_options, moc
898898

899899
@mock.patch.object(IOSDevice, "_get_file_system")
900900
@mock.patch.object(IOSDevice, "config")
901-
@mock.patch.object(IOSDevice, "boot_options", new_callable=mock.PropertyMock)
901+
@mock.patch.object(IOSDevice, "boot_options", new_callable=lambda: {"sys": None})
902902
@mock.patch.object(IOSDevice, "save")
903903
def test_set_boot_options_raise_commanderror(mock_save, mock_boot_options, mock_config, mock_file_system, ios_show):
904904
device = ios_show(["dir_flash:.txt", "boot flash:c3560-advipservicesk9-mz.122-44.SE.bin"])

0 commit comments

Comments
 (0)