Skip to content

Commit

Permalink
measurement: ip query
Browse files Browse the repository at this point in the history
Adds `ip` measurement (executable `netrics-ip`) to retrieve public IP
address and write as a result.

Measurement is added to default configuration, but easily disableable
by setting device environment variable `NETRICS_IP_QUERY=0`.

resolves #24

part of #3
  • Loading branch information
jesteria committed Mar 9, 2023
1 parent 6b4ed39 commit 6c95dff
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ netrics-dns-latency = "netrics.measurement.dns_latency:main"
netrics-hops = "netrics.measurement.hops:main"
netrics-hops-scamper = "netrics.measurement.hops:main"
netrics-hops-traceroute = "netrics.measurement.hops_traceroute:main"
netrics-ip = "netrics.measurement.ip:main"
netrics-lml = "netrics.measurement.lml:main"
netrics-lml-scamper = "netrics.measurement.lml:main"
netrics-lml-traceroute = "netrics.measurement.lml_traceroute:main"
Expand Down
4 changes: 4 additions & 0 deletions src/netrics/conf/include/measurements.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ hops-scamper:
schedule: "H/5 * * * *"
if: *scamper-on

ip:
schedule: "H/5 * * * *"
unless: env.NETRICS_IP_QUERY | default('1') | int == 0

lml:
command: lml-traceroute
schedule: "H/5 * * * *"
Expand Down
79 changes: 79 additions & 0 deletions src/netrics/measurement/ip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Retrieve public IP address from configured service."""
import ipaddress
import urllib

from schema import Optional

from netrics import task

from .common import require_lan


PARAMS = task.schema.extend('ipquery', {
Optional('service', default='https://api.ipify.org/'): task.schema.Text,
})


@task.param.require(PARAMS)
@require_lan
def main(params):
"""Retrieve public IP address from configured service.
The local network is queried first to ensure operation.
(See: `require_lan`.)
An HTTP request is then made of the configured IP address service
(`service`).
This configured service is expected to return a response with status
`200` and body of the IP address (without additional content).
The IP address is written out as a structured result according to
configuration (`result`).
"""
# initiate HTTP request
try:
response = urllib.request.urlopen(params.service)
except OSError as exc:
task.log.critical(
url=params.service,
error=str(exc),
msg='urlopen error',
)
return task.status.no_host

# check OK
if response.status != 200:
content = response.read()

if len(content) > 75:
content = content[:72] + '...'

task.log.critical(
url=params.service,
status=f'Error ({response.status})',
msg=content,
)

return task.status.no_host

# retrieve response
ip_addr = response.read().decode()

# validate response
try:
ipaddress.ip_address(ip_addr)
except ValueError as exc:
task.log.critical(
url=params.service,
response=ip_addr,
error=str(exc),
msg='service response error',
)
return task.status.software_error

# write result
task.result.write({'ipv4': ip_addr},
label=params.result.label,
annotate=params.result.annotate)

0 comments on commit 6c95dff

Please sign in to comment.