Merge pull request #1 from mateuszskoczek/dev

Dev
This commit is contained in:
2024-04-06 01:50:27 +02:00
committed by GitHub
Unverified
11 changed files with 372 additions and 0 deletions

11
.github/config/gitversion.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
next-version: 1.0.0
assembly-versioning-scheme: MajorMinorPatch
assembly-file-versioning-scheme: MajorMinorPatch
branches:
master:
regex: ^master$
mode: ContinuousDelivery
increment: Patch
tag: ''
is-release-branch: true

26
.github/workflows/pull_request_dev.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Chack code on dev pull request
on:
pull_request:
branches:
- "dev"
paths:
- "ipsec_exporter.py"
- "src/*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
- name: Analysing the code with pylint
run: pylint $(git ls-files '*.py')

View File

@@ -0,0 +1,26 @@
name: Chack code on master pull request
on:
pull_request:
branches:
- "master"
paths:
- "ipsec_exporter.py"
- "src/*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
- name: Analysing the code with pylint
run: pylint $(git ls-files '*.py')

26
.github/workflows/push_dev.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Chack code on dev push
on:
push:
branches:
- "dev"
paths:
- "ipsec_exporter.py"
- "src/*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
- name: Analysing the code with pylint
run: pylint $(git ls-files '*.py')

84
.github/workflows/push_master.yml vendored Normal file
View File

@@ -0,0 +1,84 @@
name: Chack code and publish on master push
on:
push:
branches:
- "master"
paths:
- "*.py"
jobs:
check:
name: Code check
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
- name: Analysing the code with pylint
run: pylint --exit-zero $(git ls-files '*.py')
publish:
needs: check
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup GitVersion
uses: gittools/actions/gitversion/setup@v0.9.7
with:
versionSpec: 5.x
- name: Determine Version
uses: gittools/actions/gitversion/execute@v0.9.7
id: gitversion
with:
useConfigFile: true
configFilePath: ./.github/config/gitversion.yml
- name: Create zip
uses: ihiroky/archive-action@v1
with:
root_dir: ./
file_path: ipsec_exporter_${{steps.gitversion.outputs.version}}.zip
- name: Create tar.gz
uses: ihiroky/archive-action@v1
with:
root_dir: ./
file_path: ipsec_exporter_${{steps.gitversion.outputs.version}}.tar.gz
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{steps.gitversion.outputs.version}}
release_name: ${{steps.gitversion.outputs.version}}
body_path: ./RELEASE.md
draft: false
prerelease: false
- name: Upload zip archive
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./ipsec_exporter_${{steps.gitversion.outputs.version}}.zip
asset_name: ipsec_exporter_${{steps.gitversion.outputs.version}}.zip
asset_content_type: application/zip
- name: Upload tar.gz archive
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./ipsec_exporter_${{steps.gitversion.outputs.version}}.tar.gz
asset_name: ipsec_exporter_${{steps.gitversion.outputs.version}}.tar.gz
asset_content_type: application/gzip

3
RELEASE.md Normal file
View File

@@ -0,0 +1,3 @@
# Changelog
- Initial version

5
ipsec_exporter.py Normal file
View File

@@ -0,0 +1,5 @@
from src.app import App
if __name__ == "__main__":
app = App()
app.main()

42
src/app.py Normal file
View File

@@ -0,0 +1,42 @@
from argparse import *
from src.prometheus_metrics_server import *
from src.metrics_source import *
class App:
args: Namespace
def __init__(self):
parser = ArgumentParser(description="IPsec Prometheus exporter for Libreswan")
parser.add_argument("-a", "--address", dest="address", required=False, type=str, default="0.0.0.0", help="Server IP address")
parser.add_argument("-p", "--port", dest="port", required=False, type=int, default=9446, help="Server port")
parser.add_argument("-i", "--interval", dest="interval", required=False, type=int, default=1, help="Metrics read interval (in seconds)")
self.args = parser.parse_args()
def main(self):
server = PrometheusMetricsServer(self.args.port, "IPsec exporter")
server.address = self.args.address
server.interval = self.args.interval
globalstatus_source = CommandMetricsSource("sudo ipsec globalstatus")
globalstatus_source.add_metric("ipsec_current_states", r"current\.states\.(?P<type>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_current_states_iketype", r"current\.states\.iketype\.(?P<type>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_current_states_enumerate", r"current\.states\.enumerate\.(?P<type>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_ipsec_type", r"total\.ipsec\.type\.(?P<type>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_traffic", r"total\.(?P<type>\w+)\.traffic\.(?P<direction>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_ike", r"total\.ike\.(?P<version>\w+)\.(?P<status>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_ikev2_redirect", r"total\.ike\.ikev2\.redirect\.(?P<status>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_pamauth", r"total\.pamauth\.(?P<status>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_iketcp", r"total\.iketcp\.(?P<type>\w+)\.(?P<status>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_ike_encr", r"total\.(?P<version>\w+)\.encr\.(?P<status>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_ike_integ", r"total\.(?P<version>\w+)\.integ\.(?P<status>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_ike_group", r"total\.(?P<version>\w+)\.group\.(?P<status>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_ike_notifies_error", r"total\.(?P<version>\w+)\.(?P<direction>\w+)\.notifies\.error\.(?P<status>\w+)=(?P<VALUE>\d+)")
globalstatus_source.add_metric("ipsec_total_ikev2_notifies_status", r"total\.ikev2\.(?P<direction>\w+)\.notifies\.status\.(?P<status>\w+)=(?P<VALUE>\d+)")
server.add_metrics_source(globalstatus_source)
custom_metrics_source = CustomMetricsSource()
custom_metrics_source.add_metric(IPsecTrafficCustomMetric("ipsec_traffic"))
server.add_metrics_source(custom_metrics_source)
server.run()

74
src/metric.py Normal file
View File

@@ -0,0 +1,74 @@
from abc import abstractmethod
from prometheus_client import *
from re import Pattern
import re
import os
class Metric:
gauge: Gauge
def __init__(self, name: str, labels: list[str], description: str = ""):
self.gauge = Gauge(
name,
description,
labels
)
class CommandMetric(Metric):
regex: Pattern
def __init__(self, name: str, regex: str, description: str = ""):
self.regex = re.compile(regex)
labels = list(self.regex.groupindex.keys())
labels.remove("VALUE")
super().__init__(name, labels, description)
def update(self, command_output: str):
self.gauge.clear()
results = re.finditer(self.regex, command_output)
for result in results:
groups = result.groupdict()
value = groups.pop("VALUE")
self.gauge.labels(*list(groups.values())).set(int(value))
class CustomMetric(Metric):
def __init__(self, name: str, labels: list[str], description: str = ""):
super().__init__(name, labels, description)
@abstractmethod
def update(self):
pass
class IPsecTrafficCustomMetric(CustomMetric):
def __init__(self, name: str, description: str = ""):
labels = [
"lease",
"connection",
"direction"
]
super().__init__(name, labels, description)
def update(self):
self.gauge.clear()
trafficstatus = os.popen("sudo ipsec trafficstatus").read()
trafficstatus_results = re.finditer(r""""(?P<connection>.+)"\[\d+\] \d+\.\d+\.\d+\.\d+, type=\w+, add_time=\d+, inBytes=(?P<IN_VALUE>\d+), outBytes=(?P<OUT_VALUE>\d+), maxBytes=.+, id='.+', lease=(?P<lease>\d+\.\d+\.\d+\.\d+\/\d+)""", trafficstatus)
for result in trafficstatus_results:
lease = result.groupdict()["lease"]
connection = result.groupdict()["connection"]
in_value = result.groupdict()["IN_VALUE"]
out_value = result.groupdict()["OUT_VALUE"]
self.gauge.labels(lease, connection, "in").set(int(in_value))
self.gauge.labels(lease, connection, "out").set(int(out_value))

43
src/metrics_source.py Normal file
View File

@@ -0,0 +1,43 @@
from abc import ABC, abstractmethod
from src.metric import *
import os
class MetricsSource:
_metrics : list[Metric]
def __init__(self):
self._metrics = []
@abstractmethod
def update(self):
pass
class CommandMetricsSource(MetricsSource):
command : str
def __init__(self, command: str):
super().__init__()
self.command = command
def add_metric(self, name: str, regex: str, description: str = ""):
self._metrics.append(CommandMetric(name, regex, description))
def update(self):
output = os.popen(self.command).read()
for metric in self._metrics:
metric.update(output)
class CustomMetricsSource(MetricsSource):
def __init__(self):
super().__init__()
def add_metric(self, custom_metric : CustomMetric):
self._metrics.append(custom_metric)
def update(self):
for metric in self._metrics:
metric.update()

View File

@@ -0,0 +1,32 @@
import prometheus_client
import time
from src.metrics_source import *
class PrometheusMetricsServer:
_metrics_sources: list[MetricsSource]
address: str
port: int
interval: int
server_name: str
def __init__(self, port: int, server_name: str):
self._metrics_sources = []
self.address = "0.0.0.0"
self.port = port
self.interval = 1
self.server_name = server_name
def add_metrics_source(self, source: MetricsSource):
self._metrics_sources.append(source)
def run(self):
prometheus_client.start_http_server(self.port, addr=self.address)
print(f"{self.server_name} is running on {self.address}:{self.port}")
while True:
for source in self._metrics_sources:
source.update()
time.sleep(self.interval)