Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #1

Merged
merged 8 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/config/gitversion.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 0 deletions .github/workflows/pull_request_dev.yml
Original file line number Diff line number Diff line change
@@ -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')
26 changes: 26 additions & 0 deletions .github/workflows/pull_request_master.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 0 deletions .github/workflows/push_dev.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 84 additions & 0 deletions .github/workflows/push_master.yml
Original file line number Diff line number Diff line change
@@ -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/[email protected]
with:
versionSpec: 5.x
- name: Determine Version
uses: gittools/actions/gitversion/[email protected]
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 changes: 3 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Changelog

- Initial version
5 changes: 5 additions & 0 deletions ipsec_exporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from src.app import App

if __name__ == "__main__":
app = App()
app.main()
42 changes: 42 additions & 0 deletions src/app.py
Original file line number Diff line number Diff line change
@@ -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 changes: 74 additions & 0 deletions src/metric.py
Original file line number Diff line number Diff line change
@@ -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 changes: 43 additions & 0 deletions src/metrics_source.py
Original file line number Diff line number Diff line change
@@ -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()
32 changes: 32 additions & 0 deletions src/prometheus_metrics_server.py
Original file line number Diff line number Diff line change
@@ -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)
Loading