Skip to content

Commit 782e60e

Browse files
authored
Added docstrings to asa_device.py functions (#203)
1 parent 14fec04 commit 782e60e

File tree

3 files changed

+164
-17
lines changed

3 files changed

+164
-17
lines changed

.travis.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ python:
66
- "3.7"
77
- "3.8"
88

9-
109
install:
1110
# XXX: Migrate this to Poetry fully
12-
# Install Tox, which is currently our testrunner and helper
13-
- pip install tox
11+
- pip install virtualenv
1412

1513

1614
script:
17-
# Run all our tests
15+
# Activate virtualenv, install tox and run all tests.
16+
- virtualenv ./venv
17+
- source ./venv/bin/activate
18+
- python -m pip install -U pip
19+
- pip install tox
1820
- tox
1921
- tox -e coveralls

pyntc/devices/asa_device.py

Lines changed: 157 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
"""Module for using a Cisco ASA device over SSH.
2-
"""
1+
"""Module for using a Cisco ASA device over SSH."""
32

43
import os
54
import re
@@ -41,6 +40,16 @@ class ASADevice(BaseDevice):
4140
active_redundancy_states = {None, "active"}
4241

4342
def __init__(self, host: str, username: str, password: str, secret="", port=22, **kwargs):
43+
"""
44+
Pyntc Device constructor for Cisco ASA.
45+
46+
Args:
47+
host (str): The address of the network device.
48+
username (str): The username to authenticate to the device.
49+
password (str): The password to authenticate to the device.
50+
secret (str, optional): The password to escalate privlege on the device. Defaults to "".
51+
port (int, optional): Port used to establish connection. Defaults to 22.
52+
"""
4453
super().__init__(host, username, password, device_type="cisco_asa_ssh")
4554

4655
self.native: Optional[CiscoAsaSSH] = None
@@ -93,7 +102,7 @@ def _file_copy_instance(
93102
return fc
94103

95104
def _get_file_system(self):
96-
"""Determines the default file system or directory for device.
105+
"""Determine the default file system or directory for device.
97106
98107
Returns:
99108
str: The name of the default file system or directory for the device.
@@ -301,11 +310,23 @@ def _wait_for_peer_reboot(self, acceptable_states: Iterable[str], timeout: int =
301310
raise RebootTimeoutError(hostname=f"{self.host}-peer", wait_time=timeout)
302311

303312
def backup_running_config(self, filename):
313+
"""
314+
Backups running config.
315+
316+
Args:
317+
filename (str): Name of backup file.
318+
"""
304319
with open(filename, "w") as f:
305320
f.write(self.running_config)
306321

307322
@property
308323
def boot_options(self):
324+
"""
325+
Determine boot image.
326+
327+
Returns:
328+
dict: Key: 'sys' Value: Current boot image.
329+
"""
309330
show_boot_out = self.show("show boot | i BOOT variable")
310331
# Improve regex to get only the first boot $var in the sequence!
311332
boot_path_regex = r"Current BOOT variable = (\S+):\/(\S+)"
@@ -319,19 +340,41 @@ def boot_options(self):
319340
return dict(sys=boot_image)
320341

321342
def checkpoint(self, checkpoint_file):
343+
"""
344+
Create a checkpoint file of the current config.
345+
346+
Args:
347+
checkpoint_file (str): Saves a checkpoint file with the name provided to the function.
348+
"""
322349
self.save(filename=checkpoint_file)
323350

324351
def close(self):
352+
"""Disconnect from device."""
325353
if self._connected:
326354
self.native.disconnect()
327355
self._connected = False
328356

329357
def config(self, command):
358+
"""
359+
Send single command to device.
360+
361+
Args:
362+
command (str): command to be sent to device.
363+
"""
330364
self._enter_config()
331365
self._send_command(command)
332366
self.native.exit_config_mode()
333367

334368
def config_list(self, commands):
369+
"""
370+
Send list of commands to device.
371+
372+
Args:
373+
commands (list): list of commands to be set to device.
374+
375+
Raises:
376+
CommandListError: Message stating which command failed and the response from the device.
377+
"""
335378
self._enter_config()
336379
entered_commands = []
337380
for command in commands:
@@ -345,7 +388,7 @@ def config_list(self, commands):
345388
@property
346389
def connected_interface(self) -> str:
347390
"""
348-
The interface that is assigned an IP Address of ``self.ip_address``.
391+
Interface that is assigned an IP Address of ``self.ip_address``.
349392
350393
Returns:
351394
str: The name of the interfaces associated to ``self.ip_address``.
@@ -415,7 +458,7 @@ def enable_scp(self) -> None:
415458

416459
@property
417460
def facts(self):
418-
"""Implement this once facts' re-factor is done. """
461+
"""Implement this once facts re-factor is done."""
419462
return {}
420463

421464
def file_copy(
@@ -462,6 +505,25 @@ def file_copy(
462505

463506
# TODO: Make this an internal method since exposing file_copy should be sufficient
464507
def file_copy_remote_exists(self, src, dest=None, file_system=None):
508+
"""
509+
Copy ``src`` file to device.
510+
511+
Args:
512+
src (str): The path to the file to be copied to the device.
513+
dest (str, optional): The name to use for storing the file on the device.
514+
Defaults to use the name of the ``src`` file..
515+
file_system (str, optional): The directory name to store files on the device.
516+
Defaults to discover the default directory of the device.
517+
518+
Returns:
519+
bool: True if the file exists on the device and the md5 hashes match. Otherwise, false.
520+
521+
Example:
522+
>>> status = file_copy_remote_exists("path/to/asa-image.bin")
523+
>>> print(status)
524+
True
525+
>>>
526+
"""
465527
self.enable()
466528
if file_system is None:
467529
file_system = self._get_file_system()
@@ -472,6 +534,18 @@ def file_copy_remote_exists(self, src, dest=None, file_system=None):
472534
return False
473535

474536
def install_os(self, image_name, **vendor_specifics):
537+
"""
538+
Install OS on device.
539+
540+
Args:
541+
image_name (str): Name of the image to be installed.
542+
543+
Raises:
544+
OSInstallError: Message stating the end device could not boot into the new image.
545+
546+
Returns:
547+
bool: True if new image is installed correctly. False if device is already running image_name.
548+
"""
475549
timeout = vendor_specifics.get("timeout", 3600)
476550
if not self._image_booted(image_name):
477551
self.set_boot_options(image_name, **vendor_specifics)
@@ -487,7 +561,7 @@ def install_os(self, image_name, **vendor_specifics):
487561
@property
488562
def ip_address(self) -> Union[IPv4Address, IPv6Address]:
489563
"""
490-
The IP Address used to establish the connection to the device.
564+
IP Address used to establish the connection to the device.
491565
492566
Returns:
493567
IPv4Address/IPv6Address: The IP address used by the paramiko connection.
@@ -515,7 +589,7 @@ def ip_address(self) -> Union[IPv4Address, IPv6Address]:
515589
@property
516590
def ipv4_addresses(self) -> Dict[str, List[IPv4Address]]:
517591
"""
518-
The IPv4 addresses of the device's interfaces.
592+
IPv4 addresses of the device's interfaces.
519593
520594
Returns:
521595
dict: The ipv4 addresses mapped to their interfaces.
@@ -531,7 +605,7 @@ def ipv4_addresses(self) -> Dict[str, List[IPv4Address]]:
531605
@property
532606
def ipv6_addresses(self) -> Dict[str, List[IPv6Address]]:
533607
"""
534-
The IPv6 addresses of the device's interfaces.
608+
IPv6 addresses of the device's interfaces.
535609
536610
Returns:
537611
dict: The ipv6 addresses mapped to their interfaces.
@@ -547,7 +621,7 @@ def ipv6_addresses(self) -> Dict[str, List[IPv6Address]]:
547621
@property
548622
def ip_protocol(self) -> str:
549623
"""
550-
The IP Protocol of the IP Addressed used by the underlying paramiko connection.
624+
IP Protocol of the IP Addressed used by the underlying paramiko connection.
551625
552626
Returns:
553627
str: "ipv4" for IPv4 Addresses and "ipv6" for IPv6 Addresses.
@@ -584,6 +658,7 @@ def is_active(self):
584658
return self.redundancy_state in self.active_redundancy_states
585659

586660
def open(self):
661+
"""Attempt to find device prompt. If not found, create Connecthandler object to device."""
587662
if self._connected:
588663
try:
589664
self.native.find_prompt()
@@ -605,6 +680,12 @@ def open(self):
605680

606681
@property
607682
def peer_device(self) -> "ASADevice":
683+
"""
684+
Create instance of ASADevice for peer device.
685+
686+
Returns:
687+
:class`~devices.ASADevice`: Cisco ASA device instance.
688+
"""
608689
if self._peer_device is None:
609690
self._peer_device = self.__class__(
610691
str(self.peer_ip_address), self.username, self.password, self.secret, self.port, **self.kwargs
@@ -617,7 +698,7 @@ def peer_device(self) -> "ASADevice":
617698
@property
618699
def peer_ip_address(self) -> Union[IPv4Address, IPv6Address]:
619700
"""
620-
The IP Address associated with ``self.ip_address`` on the peer device.
701+
IP Address associated with ``self.ip_address`` on the peer device.
621702
622703
Returns:
623704
IPv4Address/IPv6Address: The IP address used by the paramiko connection.
@@ -644,7 +725,7 @@ def peer_ip_address(self) -> Union[IPv4Address, IPv6Address]:
644725
@property
645726
def peer_ipv4_addresses(self) -> Dict[str, List[IPv4Address]]:
646727
"""
647-
The IPv4 addresses of the peer device's interfaces.
728+
IPv4 addresses of the peer device's interfaces.
648729
649730
Returns:
650731
dict: The ipv4 addresses mapped to their interfaces.
@@ -660,7 +741,7 @@ def peer_ipv4_addresses(self) -> Dict[str, List[IPv4Address]]:
660741
@property
661742
def peer_ipv6_addresses(self) -> Dict[str, List[IPv6Address]]:
662743
"""
663-
The IPv6 addresses of the peer device's interfaces.
744+
IPv6 addresses of the peer device's interfaces.
664745
665746
Returns:
666747
dict: The ipv6 addresses mapped to their interfaces.
@@ -789,7 +870,7 @@ def reboot_standby(self, acceptable_states: Optional[Iterable[str]] = None, time
789870
@property
790871
def redundancy_mode(self):
791872
"""
792-
The operating redundancy mode of the device.
873+
Operating redundancy mode of the device.
793874
794875
Returns:
795876
str: The redundancy mode the device is operating in.
@@ -852,13 +933,37 @@ def redundancy_state(self):
852933
return redundancy_state.lower()
853934

854935
def rollback(self, rollback_to):
936+
"""
937+
Rollback the device configuration.
938+
939+
Args:
940+
rollback_to (str): Name of checkpoint file to rollback to
941+
942+
Raises:
943+
NotImplementedError: Function not implemented yet.
944+
"""
855945
raise NotImplementedError
856946

857947
@property
858948
def running_config(self):
949+
"""
950+
Get current running config on device.
951+
952+
Returns:
953+
str: Running configuration on device.
954+
"""
859955
return self.show("show running-config")
860956

861957
def save(self, filename="startup-config"):
958+
"""
959+
Save changes to startup config.
960+
961+
Args:
962+
filename (str, optional): Name of startup configuration file. Defaults to "startup-config".
963+
964+
Returns:
965+
bool: True if configuration saved succesfully.
966+
"""
862967
command = "copy running-config %s" % filename
863968
# Changed to send_command_timing to not require a direct prompt return.
864969
self.native.send_command_timing(command)
@@ -871,6 +976,16 @@ def save(self, filename="startup-config"):
871976
return True
872977

873978
def set_boot_options(self, image_name, **vendor_specifics):
979+
"""
980+
Set new image as boot option on device.
981+
982+
Args:
983+
image_name (str): AName of image.
984+
985+
Raises:
986+
NTCFileNotFoundError: File not found on device.
987+
CommandError: Unable to issue command on device.
988+
"""
874989
current_boot = self.show("show running-config | inc ^boot system ")
875990
file_system = vendor_specifics.get("file_system")
876991
if file_system is None:
@@ -898,10 +1013,32 @@ def set_boot_options(self, image_name, **vendor_specifics):
8981013
)
8991014

9001015
def show(self, command, expect_string=None):
1016+
"""
1017+
Send command to device.
1018+
1019+
Args:
1020+
command (str): Command to be ran on device.
1021+
expect_string (str, optional): Expected response from running command on device. Defaults to None.
1022+
1023+
Returns:
1024+
str: Output from running command on device.
1025+
"""
9011026
self.enable()
9021027
return self._send_command(command, expect_string=expect_string)
9031028

9041029
def show_list(self, commands):
1030+
"""
1031+
Send list of commands to device.
1032+
1033+
Args:
1034+
commands (list): Commands to be sent to device.
1035+
1036+
Raises:
1037+
CommandListError: Failure running command on device.
1038+
1039+
Returns:
1040+
list: Output from each command sent.
1041+
"""
9051042
self.enable()
9061043

9071044
responses = []
@@ -917,8 +1054,15 @@ def show_list(self, commands):
9171054

9181055
@property
9191056
def startup_config(self):
1057+
"""
1058+
Show startup config.
1059+
1060+
:return: Output of command 'show startup-config'.
1061+
"""
9201062
return self.show("show startup-config")
9211063

9221064

9231065
class RebootSignal(NTCError):
1066+
"""Not implemented."""
1067+
9241068
pass

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
envlist = py{36,37,38},black,flake8,bandit
99
skip_missing_interpreters=true
1010
isolated_build = True
11+
download=true
1112

1213
[testenv]
1314
passenv = TRAVIS TRAVIS_*

0 commit comments

Comments
 (0)