Skip to content
This repository has been archived by the owner on Sep 3, 2024. It is now read-only.

Commit

Permalink
Port extension improvements
Browse files Browse the repository at this point in the history
* Allow setting a custom timeout
* Automatically obtain the API URL from the Activegate config
  • Loading branch information
dlopes7 authored and stefan-moschinski-dynatrace committed Nov 10, 2022
1 parent a83d520 commit e3778a4
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 31 deletions.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "custom.remote.python.thirdparty_port",
"version": "1.015",
"version": "1.016",
"metricGroup": "tech.Port",
"type": "python",
"entity": "CUSTOM_DEVICE",
Expand All @@ -13,10 +13,6 @@
"activation": "Remote"
},
"properties": [
{
"key": "api_url",
"type": "String"
},
{
"key": "api_token",
"type": "Password"
Expand All @@ -39,6 +35,10 @@
"dropdownValues": ["TCP", "UDP"],
"defaultValue": "TCP"
},
{
"key": "test_timeout",
"type": "Integer"
},
{
"key": "test_location",
"type": "String"
Expand Down Expand Up @@ -74,81 +74,81 @@
"configUI": {
"displayName": "Port check",
"properties": [
{
"key": "api_url",
"displayName": "Tenant URL",
"displayHint": "https://localhost:9999/e/<environment_id> or https://<my.managed.host>/e/<environment_id> or https://<environment_id>.live.dynatrace.com",
"displayOrder": 1
},
{
"key": "api_token",
"displayName": "API Token",
"displayHint": "Requires \"Create and read synthetic monitors, locations, and nodes\" permission",
"displayOrder": 2
"displayOrder": 20
},
{
"key": "test_name",
"displayName": "(Optional) Synthetic monitor name",
"displayOrder": 5
"displayOrder": 50
},
{
"key": "test_target_ip",
"displayName": "Test target host",
"displayHint": "IP address or hostname",
"displayOrder": 3
"displayOrder": 30
},
{
"key": "test_target_ports",
"displayName": "Test target Ports",
"displayHint": "Ports, separated by comma",
"displayOrder": 4
"displayOrder": 40
},
{
"key": "test_protocol",
"displayName": "Test protocol",
"displayHint": "For UDP the response time is not accurate, only availability",
"displayOrder": 5
"displayOrder": 50
},
{
"key": "frequency",
"displayName": "(Optional) Frequency",
"displayHint": "Frequency in minutes, default: 15",
"displayOrder": 6
"displayOrder": 60
},
{
"key": "test_timeout",
"displayName": "(Optional) Check Timeout",
"displayHint": "Check timeout in seconds, if empty default value is 2 seconds",
"displayOrder": 55
},
{
"key": "test_location",
"displayName": "(Optional) Location name",
"displayHint": "Default: ActiveGate",
"displayOrder": 7
"displayOrder": 70
},
{
"key": "proxy_address",
"displayName": "(Optional) Proxy Address",
"displayOrder": 8
"displayOrder": 80

},
{
"key": "proxy_username",
"displayName": "(Optional) Proxy Username",
"displayOrder": 9
"displayOrder": 90

},
{
"key": "proxy_password",
"displayName": "(Optional) Proxy Password",
"displayOrder": 10
"displayOrder": 100

},
{
"key": "log_level",
"displayName": "Log level",
"displayOrder": 11
"displayOrder": 110

},
{
"key": "failure_count",
"displayName": "Failure count",
"displayOrder": 12,
"displayOrder": 120,
"displayHint": "Number of consecutive failures before reporting error"
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@
from dynatrace import Dynatrace
from dynatrace.environment_v1.synthetic_third_party import SYNTHETIC_EVENT_TYPE_OUTAGE

from port_imports.environment import get_api_url


log = logging.getLogger(__name__)


class PortExtension(RemoteBasePlugin):
def initialize(self, **kwargs):
# The Dynatrace API client
self.dt_client = Dynatrace(self.config.get("api_url"), self.config.get("api_token"), log=log, proxies=self.build_proxy_url())
api_url = get_api_url()
self.dt_client = Dynatrace(api_url,
self.config.get("api_token"),
log=self.logger,
proxies=self.build_proxy_url())
self.executions = 0
self.failures: Dict[str, int] = defaultdict(int)

Expand Down Expand Up @@ -60,7 +65,11 @@ def query(self, **kwargs) -> None:

for i, port in enumerate(target_ports):
if port:
step_success, step_response_time = test_port(target_ip, int(port), protocol=self.config.get("test_protocol", "TCP"))
timeout = self.config.get("test_timeout", 2)
step_success, step_response_time = test_port(target_ip,
int(port),
protocol=self.config.get("test_protocol", "TCP"),
timeout=timeout)
test_response_time += step_response_time

log.info(f"{target_ip}:{port} = {step_success}, {step_response_time}")
Expand Down Expand Up @@ -115,15 +124,15 @@ def query(self, **kwargs) -> None:
self.executions += 1


def test_port(ip: str, port: int, protocol: str = "TCP") -> (bool, int):
def test_port(ip: str, port: int, protocol: str = "TCP", timeout: int = 2) -> (bool, int):
log.debug(f"Testing {ip}:{port} using protocol {protocol}")
start = datetime.now()
result = True
try:
socket_type = socket.SOCK_STREAM if protocol == "TCP" else socket.SOCK_DGRAM
sock = socket.socket(socket.AF_INET, socket_type)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(2)
sock.settimeout(timeout)
sock.connect((ip, port))

if protocol == "UDP":
Expand All @@ -135,7 +144,7 @@ def test_port(ip: str, port: int, protocol: str = "TCP") -> (bool, int):
except socket.timeout:
if protocol == "UDP":
log.warning(f"The UDP test for {ip}:{port} timed out, checking if the host can be pinged before reporting")
ping_result = ping(ip)
ping_result = ping(ip, timeout)
log.info(f"Ping result to double check UDP: {ping_result.as_dict()}")
result = ping_result.packet_loss_rate is not None and ping_result.packet_loss_rate == 0
else:
Expand All @@ -147,12 +156,12 @@ def test_port(ip: str, port: int, protocol: str = "TCP") -> (bool, int):
return result, int((datetime.now() - start).total_seconds() * 1000)


def ping(host: str) -> PingStats:
def ping(host: str, timeout: int) -> PingStats:
ping_parser = PingParsing()
transmitter = PingTransmitter()
transmitter.destination = host
transmitter.count = 1
transmitter.timeout = 2000
transmitter.timeout = timeout * 1000
return ping_parser.parse(transmitter.ping())


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
import re
from pathlib import Path
import logging

log = logging.getLogger(__name__)

tenant_regex = re.compile(r"\[(.*)\]")


def get_api_url() -> str:
endpoint = ""
environment = ""

base_path = Path(__file__)

# Only true if running from simulator
if "remotepluginmodule" not in str(base_path):
base_path = "/opt" if os.name != "nt" else "C:/Program Files"
base_path = Path(base_path) / "dynatrace" / "remotepluginmodule" / "agent"

# Go up one level until the directory name is remotepluginmodule
while True:
base_path = base_path.parent
if base_path.name == "remotepluginmodule":
extensions_conf = base_path / "agent" / "conf" / "extensions.conf"
with open(extensions_conf, "r", errors="replace") as f:
for line in f:
if line.startswith("Server "):
endpoint = line.split(" ")[1].strip()
endpoint = endpoint.replace("/communication", "")
if line.startswith("Tenant "):
environment = line.split(" ")[1].strip()
if endpoint and environment:
api_url = f"{endpoint}/e/{environment}"
log.info(f"Found API URL: '{api_url}' in '{extensions_conf}'")
return api_url
else:
raise Exception(f"Could not find API URL after reading {extensions_conf}")
# End of the line
if base_path.parent == base_path:
raise Exception("Could not find config directory")

0 comments on commit e3778a4

Please sign in to comment.