From afa1e9588002bb2cade23451326bdbea06e7496e Mon Sep 17 00:00:00 2001 From: Alessandro Maggio Date: Tue, 25 Oct 2022 11:31:35 +0100 Subject: [PATCH] feat: Release 1.1.4 (#96) * fix(icmp.py): ICMP header format string All values in the struct should be unsigned. * pylint issues resolved Fixed a bunch of pylint issues * First Readme push headers updated. * Update README.md * Update README.md * UMLs created. * UMLs created. * Folder Structure Created. * backticks added. * Supporting words added. * Grammer fix * Code Styles section finished. * Fix made * Fix made * Readme fixed. * Readme fixed. * Ready for pull request. * Finally ready for pull request. * Testcase added, * feat: integrate #89, #92, and #93 Allow count greater than 16-bit, fix pylint issues, update and expand readme Co-authored-by: David A. Bell <8835326+dabell-cc@users.noreply.github.com> Co-authored-by: Mark Mayo Co-authored-by: Aleksa Zatezalo Co-authored-by: Aleksa Zatezalo <59803757+AleksaZatezalo@users.noreply.github.com> --- .coverage | Bin 0 -> 69632 bytes .vscode/settings.json | 7 +++ CONTRIBUTING.md | 9 ++-- README.md | 88 ++++++++++++++++++++++++++++++++++ docs/UML-Diagram.png | Bin 0 -> 30585 bytes pythonping/__init__.py | 2 +- pythonping/executor.py | 86 +++++++++++++++++---------------- pythonping/icmp.py | 37 +++++++------- requirements.txt | 1 + setup.py | 4 +- test/test_executor.py | 12 ++--- test/test_network.py | 5 +- test/test_payload_provider.py | 7 +++ 13 files changed, 180 insertions(+), 78 deletions(-) create mode 100644 .coverage create mode 100644 .vscode/settings.json create mode 100644 docs/UML-Diagram.png create mode 100644 requirements.txt diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..2770929603245d4235c6e9a56eb416a01a15ba14 GIT binary patch literal 69632 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 E1#GrB5&!@I literal 0 HcmV?d00001 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b2b8866 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "python.testing.pytestArgs": [ + "test" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb2c0fd..dd4b587 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,12 +6,11 @@ here on GitHub. Please note we have a code of conduct, please follow it in all your interactions with the project. ## Pull Request Process +** All PRs must merge into the dev branch, and not into master**. The dev branch is the only one that can merge into master directly. 1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. -2. Update the README.md with details of changes to the interface, this includes new environment +2. Make sure all corresponding test cases pass. +3. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this - Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you - do not have permission to do that, you may request the second reviewer to merge it for you. +4. Version number will be increased only when merging dev into master, so that a version update may carry multiple PRs from various developers. The versioning scheme we use is [SemVer](http://semver.org/). diff --git a/README.md b/README.md index 8692a31..0101415 100644 --- a/README.md +++ b/README.md @@ -71,3 +71,91 @@ which payloads to send to the remote device. For that, we have several classes i `payload_provider` module. You may want to create your own provider by extending `payload_provider.PayloadProvider`. If you are interested in that, you should check the documentation of both `executor` and `payload_provider` module. + +## Code Structure + +### Top Level Directory Layout +Our project directory structure contains all src files in the pythonping folder, test cases in another folder, and helping documentation in on the top level directory. + +``` +. +├── pythonping # Source files +├── test # Automated Testcases for the package +├── CODE_OF_CONDUCT # An md file containing code of conduct +├── CONTRIBUTING # Contributing Guidlins +├── LICENSE # MIT License +├── README.md # An md file +└── setup.py # Instalation +``` + +A UML Diagram of the code structure is below: + +![ER1](https://raw.githubusercontent.com/alessandromaggio/pythonping/master/docs/UML-Diagram.png) + +As per the uml diagram above five distinct classes outside of init exist in this package: Executor, Icmp, Payload Provider, and Utils. Each of them rely on attributes which have been listed as sub-classes for brevities sake. An overview of each class is as follows. + +### Utils +Simply generates random text. See function random_text. + +### Network +Opens a socket to send and recive data. See functions send, recv, and del. + +### Payload Provider +Generates ICMP Payloads with no Headers. It's functionaly a interface. It has three +functions init, iter, and next, which are all implmented by subclasses List, Repeat, and Sweep which store payloads in diffrent lists. + +### ICMP +Generates the ICMP heaser through subclass ICMPType, and various helper functions. + +### Executor +Has various subclasses including Message, Response, Success, and Communicator used for sending icmp packets and collecting data. + +### Init +Uses network, executor, payload_provider and utils.random_text to construct and send ICMP packets to ping a network. + +## Tests +A test package exists under the folder test, and contains a serise of unit tests. Before commiting changes make sure to run the test bench and make sure all corrisponding cases pass. For new functionality new test cases must be added and documented. + +To run testcases we can simply use the ```unitest discover``` utility by running the following command: + +``` +python -m unittest discover +``` + +To run the test cases in a specific file FILE we must run the following command: + +``` +python -m unittest discover -s -p FILE +``` + +Another option is to run the following from the top level directory: + +``` +pytest test +``` + +To test for coverage simply run: + +``` +coverage run -m pytest test +``` + +## Contributing +Before contributing read through the contribution guidlines found the CONTRIBUTING file. + +### Code Style +A few key points when contributing to this repo are as follows: +1. Use tabs over spaces. +2. Format doc strings as such: + ``` + DESCRIPTION + + :param X: DESCRIPTION + :type X: Type + :param Y: DESCRIPTION + :type Y: Type + ``` + Please add doc strings to all functions added. +3. Do not add spaces between docstring and first function line. +4. Do not go over 200 characters per line. +5. When closing multiline items under brackets('()', '[]', ... etc) put the closing bracket on it's own line. \ No newline at end of file diff --git a/docs/UML-Diagram.png b/docs/UML-Diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..6766f5f8f31e0770ce87c65a5fda06535d74b0f2 GIT binary patch literal 30585 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 literal 0 HcmV?d00001 diff --git a/pythonping/__init__.py b/pythonping/__init__.py index 78cfecb..2b3c29e 100644 --- a/pythonping/__init__.py +++ b/pythonping/__init__.py @@ -1,7 +1,7 @@ import sys +from random import randint from . import network, executor, payload_provider from .utils import random_text -from random import randint # this needs to be available across all thread usages and will hold ints diff --git a/pythonping/executor.py b/pythonping/executor.py index 58bdcf3..085c6b1 100644 --- a/pythonping/executor.py +++ b/pythonping/executor.py @@ -92,35 +92,34 @@ def success(self): def error_message(self): if self.message is None: return 'No response' - else: - if self.message.packet.message_type == 0 and self.message.packet.message_code == 0: - # Echo Reply, response OK - no error - return None - elif self.message.packet.message_type == 3: - # Destination unreachable, returning more details based on message code - unreachable_messages = [ - 'Network Unreachable', - 'Host Unreachable', - 'Protocol Unreachable', - 'Port Unreachable', - 'Fragmentation Required', - 'Source Route Failed', - 'Network Unknown', - 'Host Unknown', - 'Source Host Isolated', - 'Communication with Destination Network is Administratively Prohibited', - 'Communication with Destination Host is Administratively Prohibited', - 'Network Unreachable for ToS', - 'Host Unreachable for ToS', - 'Communication Administratively Prohibited', - 'Host Precedence Violation', - 'Precedence Cutoff in Effect' - ] - try: - return unreachable_messages[self.message.packet.message_code] - except IndexError: - # Should never generate IndexError, this serves as additional protection - return 'Unreachable' + if self.message.packet.message_type == 0 and self.message.packet.message_code == 0: + # Echo Reply, response OK - no error + return None + if self.message.packet.message_type == 3: + # Destination unreachable, returning more details based on message code + unreachable_messages = [ + 'Network Unreachable', + 'Host Unreachable', + 'Protocol Unreachable', + 'Port Unreachable', + 'Fragmentation Required', + 'Source Route Failed', + 'Network Unknown', + 'Host Unknown', + 'Source Host Isolated', + 'Communication with Destination Network is Administratively Prohibited', + 'Communication with Destination Host is Administratively Prohibited', + 'Network Unreachable for ToS', + 'Host Unreachable for ToS', + 'Communication Administratively Prohibited', + 'Host Precedence Violation', + 'Precedence Cutoff in Effect' + ] + try: + return unreachable_messages[self.message.packet.message_code] + except IndexError: + # Should never generate IndexError, this serves as additional protection + return 'Unreachable' # Error was not identified return 'Network Error' @@ -131,28 +130,26 @@ def time_elapsed_ms(self): def legacy_repr(self): if self.message is None: return 'Request timed out' - elif self.success: + if self.success: return 'Reply from {0}, {1} bytes in {2}ms'.format(self.message.source, len(self.message.packet.raw), self.time_elapsed_ms) - else: - # Not successful, but with some code (e.g. destination unreachable) - return '{0} from {1} in {2}ms'.format(self.error_message, self.message.source, self.time_elapsed_ms) + # Not successful, but with some code (e.g. destination unreachable) + return '{0} from {1} in {2}ms'.format(self.error_message, self.message.source, self.time_elapsed_ms) def __repr__(self): if self.repr_format == 'legacy': return self.legacy_repr() if self.message is None: return 'Timed out' - elif self.success: + if self.success: return 'status=OK\tfrom={0}\tms={1}\t\tbytes\tsnt={2}\trcv={3}'.format( self.message.source, self.time_elapsed_ms, len(self.source_request.raw)+20, len(self.message.packet.raw) ) - else: - return 'status=ERR\tfrom={1}\terror="{0}"'.format(self.message.source, self.error_message) + return 'status=ERR\tfrom={1}\terror="{0}"'.format(self.message.source, self.error_message) class ResponseList: """Represents a series of ICMP responses""" @@ -231,23 +228,28 @@ def append(self, value): self.rtt_max = value.time_elapsed if value.time_elapsed < self.rtt_min: self.rtt_min = value.time_elapsed - if value.success: self.stats_packets_returned += 1 + if value.success: + self.stats_packets_returned += 1 if self.verbose: print(value, file=self.output) @property - def stats_packets_lost(self): return self.stats_packets_sent - self.stats_packets_returned + def stats_packets_lost(self): + return self.stats_packets_sent - self.stats_packets_returned @property - def stats_success_ratio(self): return self.stats_packets_returned / self.stats_packets_sent + def stats_success_ratio(self): + return self.stats_packets_returned / self.stats_packets_sent @property - def stats_lost_ratio(self): return 1 - self.stats_success_ratio + def stats_lost_ratio(self): + return 1 - self.stats_success_ratio @property - def packets_lost(self): return self.stats_lost_ratio - + def packets_lost(self): + return self.stats_lost_ratio + def __len__(self): return len(self._responses) diff --git a/pythonping/icmp.py b/pythonping/icmp.py index e060735..2814a99 100644 --- a/pythonping/icmp.py +++ b/pythonping/icmp.py @@ -1,9 +1,5 @@ import os -import socket import struct -import select -import time - def checksum(data): """Creates the ICMP checksum as in RFC 1071 @@ -61,64 +57,64 @@ class DestinationUnreachable(ICMPType): class SourceQuench(ICMPType): type_id = 4 SOURCE_QUENCH = (type_id, 0,) - + class Redirect(ICMPType): type_id = 5 FOR_NETWORK = (type_id, 0,) FOR_HOST = (type_id, 1,) FOR_TOS_AND_NETWORK = (type_id, 2,) FOR_TOS_AND_HOST = (type_id, 3,) - + class EchoRequest(ICMPType): type_id = 8 ECHO_REQUEST = (type_id, 0,) - + class RouterAdvertisement(ICMPType): type_id = 9 ROUTER_ADVERTISEMENT = (type_id, 0,) - + class RouterSolicitation(ICMPType): type_id = 10 ROUTER_SOLICITATION = (type_id, 0) # Aliases ROUTER_DISCOVERY = ROUTER_SOLICITATION ROUTER_SELECTION = ROUTER_SOLICITATION - + class TimeExceeded(ICMPType): type_id = 11 TTL_EXPIRED_IN_TRANSIT = (type_id, 0) FRAGMENT_REASSEMBLY_TIME_EXCEEDED = (type_id, 1) - + class BadIPHeader(ICMPType): type_id = 12 POINTER_INDICATES_ERROR = (type_id, 0) MISSING_REQUIRED_OPTION = (type_id, 1) BAD_LENGTH = (type_id, 2) - + class Timestamp(ICMPType): type_id = 13 TIMESTAMP = (type_id, 0) - + class TimestampReply(ICMPType): type_id = 14 TIMESTAMP_REPLY = (type_id, 0) - + class InformationRequest(ICMPType): type_id = 15 INFORMATION_REQUEST = (type_id, 0) - + class InformationReply(ICMPType): type_id = 16 INFORMATION_REPLY = (type_id, 0) - + class AddressMaskRequest(ICMPType): type_id = 17 ADDRESS_MASK_REQUEST = (type_id, 0) - + class AddressMaskReply(ICMPType): type_id = 18 ADDRESS_MASK_REPLY = (type_id, 0) - + class Traceroute(ICMPType): type_id = 30 INFORMATION_REQUEST = (type_id, 30) @@ -160,7 +156,8 @@ def __init__(self, message_type=Types.EchoReply, payload=None, identifier=None, def packet(self): """The raw packet with header, ready to be sent from a socket""" p = self._header(check=self.expected_checksum) + self.payload - if (self.raw is None): self.raw = p + if self.raw is None: + self.raw = p return p def _header(self, check=0): @@ -171,7 +168,7 @@ def _header(self, check=0): :return: The packed header :rtype: bytes""" # TODO implement sequence number - return struct.pack("bbHHh", + return struct.pack("BBHHH", self.message_type, self.message_code, check, @@ -220,5 +217,5 @@ def unpack(self, raw): self.message_code, \ self.received_checksum, \ self.id, \ - self.sequence_number = struct.unpack("bbHHh", raw[20:28]) + self.sequence_number = struct.unpack("BBHHH", raw[20:28]) self.payload = raw[28:] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e7af550 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pexpect==4.0.8 \ No newline at end of file diff --git a/setup.py b/setup.py index e460001..379ca3f 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,10 @@ from setuptools import setup -with open('README.md', 'r') as file: +with open('README.md', 'r', encoding='utf-8') as file: long_description = file.read() setup(name='pythonping', - version='1.1.3', + version='1.1.4', description='A simple way to ping in Python', url='https://github.com/alessandromaggio/pythonping', author='Alessandro Maggio', diff --git a/test/test_executor.py b/test/test_executor.py index cfa16d6..0b62a5d 100644 --- a/test/test_executor.py +++ b/test/test_executor.py @@ -257,8 +257,8 @@ def test_no_packets_lost(self): self.assertEqual(rs.stats_packets_sent, rs.stats_packets_returned, 'unable to correctly count sent and returned packets when all responses successful') self.assertEqual( - rs.stats_packets_lost, - 0, + rs.stats_packets_lost, + 0, "Unable to calculate packet loss correctly when all responses successful" ) @@ -271,8 +271,8 @@ def test_all_packets_lost(self): ]) self.assertEqual(rs.stats_packets_returned, 0, 'unable to correctly count sent and returned packets when all responses failed') self.assertEqual( - rs.stats_lost_ratio, - 1.0, + rs.stats_lost_ratio, + 1.0, "Unable to calculate packet loss correctly when all responses failed" ) @@ -286,8 +286,8 @@ def test_some_packets_lost(self): self.assertEqual(rs.stats_packets_sent, 4, 'unable to correctly count sent packets when some of the responses failed') self.assertEqual(rs.stats_packets_returned, 2, 'unable to correctly count returned packets when some of the responses failed') self.assertEqual( - rs.stats_lost_ratio, - 0.5, + rs.stats_lost_ratio, + 0.5, "Unable to calculate packet loss correctly when some of the responses failed" ) diff --git a/test/test_network.py b/test/test_network.py index b7cba82..144a58a 100644 --- a/test/test_network.py +++ b/test/test_network.py @@ -1,11 +1,12 @@ import unittest from pythonping.network import Socket - class UtilsTestCase(unittest.TestCase): """Tests for Socket class""" + + def test_raise_explicative_error_on_name_resolution_failure(self): """Test a runtime error is generated if the name cannot be resolved""" with self.assertRaises(RuntimeError): - Socket('invalid', 'raw') + Socket('invalid', 'raw') \ No newline at end of file diff --git a/test/test_payload_provider.py b/test/test_payload_provider.py index 48a20ef..9cbc474 100644 --- a/test/test_payload_provider.py +++ b/test/test_payload_provider.py @@ -1,10 +1,17 @@ import unittest + +from pexpect import ExceptionPexpect from pythonping import payload_provider class PayloadProviderTestCase(unittest.TestCase): """Tests for PayloadProvider class""" + def test_basic(self): + """Verifies that a provider raises error when calling own functions""" + self.assertRaises(NotImplementedError, payload_provider.PayloadProvider) + + def test_list(self): """Verifies that a list provider generates the correct payloads""" payloads = [