From e6bbcfc70df7e0080fe61ddd0ffbaad99e164651 Mon Sep 17 00:00:00 2001 From: Guillaume Berche Date: Tue, 15 Oct 2024 20:07:40 +0200 Subject: [PATCH 1/3] Add github workflow to publish to ghrc --- .github/workflows/docker-publish.yml | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/docker-publish.yml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..5957e2f --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,46 @@ +name: Build latest docker image + +on: + push: + branches: ['main'] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build: + runs-on: ubuntu-latest + + #--- Set permissions to ephemeral GITHUB_TOKEN for job actions + permissions: + contents: read + packages: write + + steps: + - name: Checkout and download repository to workflow runner + uses: actions/checkout@v3 + + - name: Set up docker buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to github container registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and export to ghcr + uses: docker/build-push-action@v3 + with: + context: . + load: true + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + + - name: Build and push image to github container registry + uses: docker/build-push-action@v3 + with: + context: . + push: true + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest From 07491f60d295ab77af28561f5d6a894e7bdc819a Mon Sep 17 00:00:00 2001 From: Guillaume Berche Date: Tue, 15 Oct 2024 20:44:43 +0200 Subject: [PATCH 2/3] Add new OPTIONS env var to pass in additional iperf3 options Fix https://github.com/markormesher/iperf-prometheus-collector/issues/24 --- README.md | 9 +++++---- src/config.ts | 1 + src/server.ts | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d68fbaf..502f2b0 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,11 @@ avg by (target) (iperf_received_bytes / iperf_received_seconds * 8) Configuration is via the following environment variables: -| Variable | Required? | Description | Default | -| ------------------ | --------- | ------------------------------------------------------------------------ | ----------------------- | -| `TARGET_LIST` | yes | Comma separated list of host names or IP addresses to run tests against. | n/a | -| `TEST_INTERVAL_MS` | no | How often to run iperf tests. | 600000ms (= 10 minutes) | +| Variable | Required? | Description | Default | +|--------------------|-----------|----------------------------------------------------------------------------------------------------------------|-------------------------| +| `TARGET_LIST` | yes | Comma separated list of host names or IP addresses to run tests against. | n/a | +| `TEST_INTERVAL_MS` | no | How often to run iperf tests. | 600000ms (= 10 minutes) | +| `OPTIONS` | no | Additional [iperf options](https://github.com/esnet/iperf/blob/master/docs/invoking.rst) (e.g. `--bitrate 1k`) | none | ### `iperf` Server diff --git a/src/config.ts b/src/config.ts index 43b67f2..859ffae 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,6 +2,7 @@ import { readFileSync } from "fs"; enum ConfigKey { TargetList = "TARGET_LIST", + Options = "OPTIONS", TestIntervalMs = "TEST_INTERVAL_MS", } diff --git a/src/server.ts b/src/server.ts index 53b53f4..14f161c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,6 +8,7 @@ const exec = util.promisify(execRaw); // get config const targetList = getConfig(ConfigKey.TargetList); +const options = getConfig(ConfigKey.Options); const targets = targetList.split(",").map((t) => t.trim()); const testIntervalMs = parseInt(getConfig(ConfigKey.TestIntervalMs, "600000")); @@ -17,10 +18,11 @@ async function getMeasurements(): Promise { for (const target of targets) { const tags = { target, + options, }; try { - const iperfCmd = await exec(`iperf3 -c ${target} --json`); + const iperfCmd = await exec(`iperf3 -c ${target} --json ${options}`); const result = JSON.parse(iperfCmd.stdout); if (result["error"]) { throw result["error"]; From 8fd7d215b980c598d92186f5a8004d70882c4113 Mon Sep 17 00:00:00 2001 From: Guillaume Berche Date: Tue, 15 Oct 2024 22:04:59 +0200 Subject: [PATCH 3/3] Add support for udp protocol Fix https://github.com/markormesher/iperf-prometheus-collector/issues/26 --- README.md | 1 + src/config.ts | 1 + src/server.ts | 46 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 502f2b0..263813a 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Configuration is via the following environment variables: | `TARGET_LIST` | yes | Comma separated list of host names or IP addresses to run tests against. | n/a | | `TEST_INTERVAL_MS` | no | How often to run iperf tests. | 600000ms (= 10 minutes) | | `OPTIONS` | no | Additional [iperf options](https://github.com/esnet/iperf/blob/master/docs/invoking.rst) (e.g. `--bitrate 1k`) | none | +| `PROTOCOL` | no | One of `tcp` or `udp` | `tcp` | ### `iperf` Server diff --git a/src/config.ts b/src/config.ts index 859ffae..09ac653 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,6 +4,7 @@ enum ConfigKey { TargetList = "TARGET_LIST", Options = "OPTIONS", TestIntervalMs = "TEST_INTERVAL_MS", + Protocol = "PROTOCOL", } const loadedConfig: Partial<{ [key in ConfigKey]: string }> = {}; diff --git a/src/server.ts b/src/server.ts index 14f161c..af0dc20 100644 --- a/src/server.ts +++ b/src/server.ts @@ -9,6 +9,7 @@ const exec = util.promisify(execRaw); // get config const targetList = getConfig(ConfigKey.TargetList); const options = getConfig(ConfigKey.Options); +const protocol = getConfig(ConfigKey.Protocol, "tcp"); const targets = targetList.split(",").map((t) => t.trim()); const testIntervalMs = parseInt(getConfig(ConfigKey.TestIntervalMs, "600000")); @@ -22,22 +23,43 @@ async function getMeasurements(): Promise { }; try { - const iperfCmd = await exec(`iperf3 -c ${target} --json ${options}`); + let udpOptionStr = ""; + if (protocol == "udp") { + udpOptionStr = "--udp"; + } + + const iperfCmd = await exec(`iperf3 -c ${target} ${udpOptionStr} --json ${options}`); const result = JSON.parse(iperfCmd.stdout); if (result["error"]) { throw result["error"]; } - - measurements.push(formatMeasurement("iperf_sent_bytes", tags, parseFloat(result["end"]["sum_sent"]["bytes"]))); - measurements.push( - formatMeasurement("iperf_sent_seconds", tags, parseFloat(result["end"]["sum_sent"]["seconds"])), - ); - measurements.push( - formatMeasurement("iperf_received_bytes", tags, parseFloat(result["end"]["sum_received"]["bytes"])), - ); - measurements.push( - formatMeasurement("iperf_received_seconds", tags, parseFloat(result["end"]["sum_received"]["seconds"])), - ); + switch(protocol) { + case "tcp": + measurements.push(formatMeasurement("iperf_sent_bytes", tags, parseFloat(result["end"]["sum_sent"]["bytes"]))); + measurements.push( + formatMeasurement("iperf_sent_seconds", tags, parseFloat(result["end"]["sum_sent"]["seconds"])), + ); + measurements.push( + formatMeasurement("iperf_received_bytes", tags, parseFloat(result["end"]["sum_received"]["bytes"])), + ); + measurements.push( + formatMeasurement("iperf_received_seconds", tags, parseFloat(result["end"]["sum_received"]["seconds"])), + ); + break; + case "udp": + measurements.push( + formatMeasurement("iperf_lost_packets", tags, parseFloat(result["end"]["sum"]["lost_packets"])), + ); + measurements.push( + formatMeasurement("iperf_received_bytes", tags, parseFloat(result["end"]["sum"]["bytes"])), + ); + measurements.push( + formatMeasurement("iperf_received_seconds", tags, parseFloat(result["end"]["sum"]["seconds"])), + ); + break; + default: + throw `unsupported protocol ${protocol}`; + } } catch (e) { log(`Could not get iperf metrics for ${target}`, e); continue;