Gets various network diagnostics for a pc
Project description
py-net-diags
A Python package for network diagnostics that collects various metrics about your machine's network connection.
Installation
Basic Installation
pip install py-net-diags
With Optional speedcheck Support
pip install py-net-diags[speedcheck]
Configuration
Environment Variables
Create a .env file based on the provided .env-sample:
# ConnectWise API Configuration
RUNNING_IN_ASIO=true/false # Set to true if running in ASIO environment
CW_BASE_URL=your_cw_url # ConnectWise base URL
AUTHORIZATION=your_auth_token # ConnectWise authorization token
CLIENTID=your_client_id # ConnectWise client ID
# Retry Configuration
RETRY_ATTEMPTS=3 # Number of retry attempts for operations
RETRY_DELAY=2 # Delay in seconds between retry attempts
Usage Examples
Run locally (without ConnectWise upload)
import logging
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
log.addHandler(logging.StreamHandler())
try:
import os
import sys
import traceback
import argparse
from py_net_diags import NetworkDiagnostics
except Exception as e:
log.exception(e, stack_info=True)
sys.exit(1)
os.environ["RUNNING_IN_ASIO"] = "False"
log.debug("Starting Bot to run network diagnostics...")
def parse_args():
parser = argparse.ArgumentParser(description="Run network diagnostics and upload results to ConnectWise.")
parser.add_argument("--targets", help="List of targets to test, separated by commas")
parser.add_argument("--log-level", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], default="INFO", help="Set the logging level")
return parser.parse_args()
def main():
try:
args = parse_args()
user_input = args.targets
log_level = args.log_level
if log_level:
log.setLevel(getattr(logging, log_level.upper()))
log.debug(f"User input received: {user_input}")
network_diag = NetworkDiagnostics(log=log)
try:
targets = network_diag.parse_targets(user_input)
log.debug(f"Parsed targets: {targets}")
except Exception:
log.exception(f"Error processing user input: {traceback.format_exc()} {type(user_input)}", stack_info=True)
targets = []
log.debug("Running network diagnostics...")
diagnostics = network_diag.build_diagnostics_dict(additional_targets=targets)
network_diag.print_diagnostics(diagnostics)
success, file_path = network_diag.write_diagnostics_to_files(diagnostics)
if success:
log.info(f"Network diagnostics successfully generated and uploaded to {file_path}")
msg = network_diag.format_diagnostic_message(diagnostics)
md_msg = network_diag.format_diagnostic_message_markdown(diagnostics)
log.debug(f"Formatted message: {msg}")
log.debug(f"Formatted markdown message: {md_msg}")
diagnostics_log_dict = {"diagnostics": diagnostics}
log.debug(f"Diagnostics log dictionary: {diagnostics_log_dict} -> {msg}")
log.info(f"Network diagnostics completed successfully. -> {file_path}")
except Exception:
log.exception(f"An exception occurred while performing task: {traceback.format_exc()}", stack_info=True)
finally:
log.debug("Bot execution has ended.")
if __name__ == "__main__":
main()
Run locally (with ConnectWise upload)
import logging
log = logging.getLogger(__name__).setLevel(logging.DEBUG)
log.setLevel(logging.DEBUG)
log.addHandler(logging.StreamHandler())
try:
import os
import sys
import traceback
import argparse
from py_net_diags import NetworkDiagnostics, ConnectWiseServiceAPI
except Exception as e:
log.exception(e, stack_info=True)
sys.exit(1)
os.environ["RUNNING_IN_ASIO"] = "False"
log.debug("Starting Bot to run network diagnostics...")
def parse_args():
parser = argparse.ArgumentParser(description="Run network diagnostics and upload results to ConnectWise.")
parser.add_argument("--targets", help="List of targets to test, separated by commas")
parser.add_argument("--ticket-id", help="ConnectWise ticket ID for uploading results")
parser.add_argument("--log-level", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], help="Set the logging level")
return parser.parse_args()
def main():
try:
args = parse_args()
user_input = args.targets
ticket_id = args.ticket_id
log_level = args.log_level
if log_level:
log.setLevel(getattr(logging, log_level.upper()))
log.debug(f"User input received: {user_input}")
network_diag = NetworkDiagnostics(log=log)
try:
targets = network_diag.parse_targets(user_input)
log.debug(f"Parsed targets: {targets}")
except Exception:
log.exception(f"Error processing user input: {traceback.format_exc()} {type(user_input)}", stack_info=True)
targets = []
log.debug("Running network diagnostics...")
diagnostics = network_diag.build_diagnostics_dict(additional_targets=targets)
network_diag.print_diagnostics(diagnostics)
success, file_path = network_diag.write_diagnostics_to_files(diagnostics)
if ticket_id is not None and success:
service_api = ConnectWiseServiceAPI(log=log)
ticket_id = network_diag.parse_ticket_id(ticket_id)
log.debug(f"Parsed ticket ID: {ticket_id}")
attachment_response = service_api.upload_attachment(ticket_id=ticket_id, file_path=file_path)
log.debug(f"Attachment uploaded successfully: {attachment_response}")
msg = network_diag.format_diagnostic_message(diagnostics)
md_msg = network_diag.format_diagnostic_message_markdown(diagnostics)
log.debug(f"Formatted message: {msg}")
log.debug(f"Formatted markdown message: {md_msg}")
if ticket_id is not None:
try:
note_response = service_api.add_ticket_note(ticket_id=ticket_id, note_text=md_msg, internal=True)
log.debug(f"Note created successfully: {note_response}")
except Exception as e:
log.exception(e, stack_info=True)
else:
log.debug("No ticket ID provided.")
diagnostics_log_dict = {"diagnostics": diagnostics}
log.debug(f"Diagnostics log dictionary: {diagnostics_log_dict} -> {msg}")
except Exception:
log.exception(f"An exception occurred while performing task: {traceback.format_exc()}", stack_info=True)
finally:
log.debug("Bot execution has ended.")
if __name__ == "__main__":
main()
Run in ASIO
from cw_rpa import Logger, Input
import logging
OVERWRITE_LOG = True
if OVERWRITE_LOG:
log = Logger(level=logging.WARNING)
log.warning("Overwriting log file at the end with text content.")
else:
log = Logger(level=logging.DEBUG)
log.debug("Using normal logging format and file.")
log.debug("Importing required modules...")
try:
import os
import sys
import traceback
from py_net_diags import NetworkDiagnostics, ConnectWiseServiceAPI
except Exception as e:
log.exception(e, stack_info=True)
sys.exit(1)
os.environ["RUNNING_IN_ASIO"] = "True"
log.debug("Starting Bot to run network diagnostics...")
def find_result_file(filename: str, starting_directory: str = ".", max_depth: int = 3) -> str:
"""
find_result_file: Find the specified file starting in the given directory and going down through the subdirectories.
Args:
filename (str): The name of the file to find
Kwargs:
starting_directory (str, optional): The directory to start searching from. (default: ".").
max_depth (int, optional): The maximum depth to search through subdirectories. (default: 3).
Returns:
str: The full path to the found file, or an empty string if not found.
"""
# Normalize the starting directory path
starting_directory = os.path.abspath(starting_directory)
# Check if the starting directory exists
if not os.path.exists(starting_directory):
return ""
# Perform a breadth-first search to avoid going too deep unnecessarily
current_depth = 0
directories_to_search = [(starting_directory, current_depth)]
while directories_to_search:
current_dir, depth = directories_to_search.pop(0)
# Skip if we've exceeded the max depth
if depth > max_depth:
continue
try:
# List all entries in the current directory
entries = os.listdir(current_dir)
# First check for the target file in the current directory
for entry in entries:
full_path = os.path.join(current_dir, entry)
# If it's a file and matches the filename, return the full path
if os.path.isfile(full_path) and entry == filename:
return full_path
# Then add subdirectories to the search queue if we're not at max depth
if depth < max_depth:
for entry in entries:
full_path = os.path.join(current_dir, entry)
if os.path.isdir(full_path):
directories_to_search.append((full_path, depth + 1))
except (PermissionError, OSError):
# Skip directories we don't have permission to access
continue
# If we've searched all directories up to max_depth and haven't found the file
return ""
def overwrite_log_file(file_path: str, message: str, overwrite: bool = False) -> bool:
try:
if overwrite and os.path.exists(file_path):
with open(file_path, "w", encoding='utf-8') as result_file:
result_file.write(message)
return True
else:
log.info("Not overwriting existing log file.")
except Exception as e:
log.exception(e, stack_info=True)
return False
def main():
try:
input = Input()
log.debug("Bot execution has started.")
# Replace these with the keys in your input schema
user_input = input.get_value("Endpoints_1744302956989")
ticket_id = input.get_value("TicketID_1744387558332")
log.debug(f"User input received: {user_input}")
# Create an instance of the NetworkDiagnostics class
network_diag = NetworkDiagnostics(log=log)
try:
targets = network_diag.parse_targets(user_input)
log.debug(f"Parsed targets: {targets}")
except Exception:
log.exception(f"Error processing user input: {traceback.format_exc()} {type(user_input)}", stack_info=True)
targets = []
if ticket_id is not None:
try:
service_api = ConnectWiseServiceAPI(log=log)
ticket_id = network_diag.parse_ticket_id(ticket_id)
log.debug(f"Parsed ticket ID: {ticket_id}")
except Exception:
log.exception(f"Error parsing ticket ID: {traceback.format_exc()}", stack_info=True)
ticket_id = None
log.debug("Running network diagnostics...")
diagnostics = network_diag.build_diagnostics_dict(additional_targets=targets)
network_diag.print_diagnostics(diagnostics)
success, file_path = network_diag.write_diagnostics_to_files(diagnostics)
if ticket_id is not None and success:
attachment_response = service_api.upload_attachment(ticket_id=ticket_id, file_path=file_path)
log.debug(f"Attachment uploaded successfully: {attachment_response}")
msg = network_diag.format_diagnostic_message(diagnostics)
md_msg = network_diag.format_diagnostic_message_markdown(diagnostics)
log.debug(f"Formatted message: {msg}")
log.debug(f"Formatted markdown message: {md_msg}")
if ticket_id is not None:
try:
note_response = service_api.add_ticket_note(ticket_id=ticket_id, note_text=md_msg, internal=True)
log.debug(f"Note created successfully: {note_response}")
except Exception as e:
log.exception(e, stack_info=True)
else:
log.debug("No ticket ID provided.")
diagnostics_log_dict = {"diagnostics": diagnostics}
log.debug(f"Diagnostics log dictionary: {diagnostics_log_dict} -> {msg}")
log.result_success_message(msg)
log.result_data(diagnostics_log_dict)
result_txt_file = find_result_file("Result.txt")
log.info(f"Result.txt file found: {result_txt_file}")
overwrite_log_file(result_txt_file, md_msg, overwrite=OVERWRITE_LOG)
except Exception:
log.exception(f"An exception occurred while performing task: {traceback.format_exc()}", stack_info=True)
log.result_failed_message(f"An exception occurred while performing task: {traceback.format_exc()}")
finally:
log.debug("Bot execution has ended.")
if __name__ == "__main__":
main()
Conda Environment Setup
Basic Environment
name: net-diags
channels:
- defaults
- conda-forge
dependencies:
- python=3.11
- pip
- pip:
- py-net-diags
With speedcheck Support
name: net-diags-full
channels:
- defaults
- conda-forge
dependencies:
- python=3.11
- pip
- pip:
- "py-net-diags[speedcheck]"
Save either of these as environment.yml and create the environment with:
conda env create -f environment.yml
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
py_net_diags-0.1.6.tar.gz
(21.6 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py_net_diags-0.1.6.tar.gz.
File metadata
- Download URL: py_net_diags-0.1.6.tar.gz
- Upload date:
- Size: 21.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.2 CPython/3.12.7 Darwin/24.0.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd822e94f3e2a9d37bf8c3698b309125fa66db4194d410c36e01a70f860ed907
|
|
| MD5 |
031fbc80b38ffc0ac343e90ced2a7669
|
|
| BLAKE2b-256 |
27f7f7e1f37c80ad94d4bc5ec2393b08803807b089c285a24d811807fb84368a
|
File details
Details for the file py_net_diags-0.1.6-py3-none-any.whl.
File metadata
- Download URL: py_net_diags-0.1.6-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.2 CPython/3.12.7 Darwin/24.0.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5dedcd8d04fcba1bd5bce62ad3564e90f0a722338f236305a6962e9f9c85a2b
|
|
| MD5 |
c96ab6ab05bfee2cb78b71b5df5d7461
|
|
| BLAKE2b-256 |
da540a312a9816cf70f49c6ea87c49da20ba9589bc3a8d43a69b8f6d1e06582a
|