first
This commit is contained in:
commit
30bb4a2fd9
11 changed files with 1071 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/result*
|
||||||
|
/zig-cache
|
||||||
|
/zig-out
|
||||||
|
/config.json
|
0
README.md
Normal file
0
README.md
Normal file
74
build.zig
Normal file
74
build.zig
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
// Although this function looks imperative, note that its job is to
|
||||||
|
// declaratively construct a build graph that will be executed by an external
|
||||||
|
// runner.
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "iperf-watcher",
|
||||||
|
// In this case the main source file is merely a path, however, in more
|
||||||
|
// complicated build scripts, this could be a generated file.
|
||||||
|
.root_source_file = .{ .path = "src/main.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const datetime = b.dependency("datetime", .{});
|
||||||
|
exe.addModule("datetime", datetime.module("datetime"));
|
||||||
|
// exe.linkLibrary(datetime.artifact("datetime"));
|
||||||
|
|
||||||
|
// This declares intent for the executable to be installed into the
|
||||||
|
// standard location when the user invokes the "install" step (the default
|
||||||
|
// step when running `zig build`).
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
// This *creates* a Run step in the build graph, to be executed when another
|
||||||
|
// step is evaluated that depends on it. The next line below will establish
|
||||||
|
// such a dependency.
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
|
||||||
|
// By making the run step depend on the install step, it will be run from the
|
||||||
|
// installation directory rather than directly from within the cache directory.
|
||||||
|
// This is not necessary, however, if the application depends on other installed
|
||||||
|
// files, this ensures they will be present and in the expected location.
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
// This allows the user to pass arguments to the application in the build
|
||||||
|
// command itself, like this: `zig build run -- arg1 arg2 etc`
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This creates a build step. It will be visible in the `zig build --help` menu,
|
||||||
|
// and can be selected like this: `zig build run`
|
||||||
|
// This will evaluate the `run` step rather than the default, which is "install".
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
// Creates a step for unit testing. This only builds the test executable
|
||||||
|
// but does not run it.
|
||||||
|
const unit_tests = b.addTest(.{
|
||||||
|
.root_source_file = .{ .path = "src/main.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_unit_tests = b.addRunArtifact(unit_tests);
|
||||||
|
|
||||||
|
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||||
|
// the `zig build --help` menu, providing a way for the user to request
|
||||||
|
// running the unit tests.
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&run_unit_tests.step);
|
||||||
|
}
|
11
build.zig.zon
Normal file
11
build.zig.zon
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.{
|
||||||
|
.name = "iperf-watcher",
|
||||||
|
.version = "0.0.1",
|
||||||
|
.paths = .{""},
|
||||||
|
.dependencies = .{
|
||||||
|
.datetime = .{
|
||||||
|
.url = "https://git.ocjtech.us/jeff/zig-datetime/archive/15fb4297884e4875833a1a0cdb8394c5269d11f9.tar.gz",
|
||||||
|
.hash = "12202e0deaf429245daffbc7dcaf810e31123e18f7d511f6ced4e54fad994242f670",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
13
deps.nix
Normal file
13
deps.nix
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# generated by zon2nix (https://github.com/nix-community/zon2nix)
|
||||||
|
|
||||||
|
{ linkFarm, fetchzip }:
|
||||||
|
|
||||||
|
linkFarm "zig-packages" [
|
||||||
|
{
|
||||||
|
name = "12202e0deaf429245daffbc7dcaf810e31123e18f7d511f6ced4e54fad994242f670";
|
||||||
|
path = fetchzip {
|
||||||
|
url = "https://git.ocjtech.us/jeff/zig-datetime/archive/15fb4297884e4875833a1a0cdb8394c5269d11f9.tar.gz";
|
||||||
|
hash = "sha256-IWQLSYM2HLO3DMaiCwbbBpnWFojDJhnCRyuHCkngDpc=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
182
flake.lock
Normal file
182
flake.lock
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"bash": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1697126158,
|
||||||
|
"narHash": "sha256-XoRmgs8U78oVMVzk4riJpkmXaX1Pk2Ya/wYMmTYt2mA=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "443dc212854202ddf2bb3bf29ad6d6c1f8829ff6",
|
||||||
|
"revCount": 11,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.ocjtech.us/jeff/nixos-bash-prompt-builder.git"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.ocjtech.us/jeff/nixos-bash-prompt-builder.git"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694529238,
|
||||||
|
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1689068808,
|
||||||
|
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"make-shell": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1634940815,
|
||||||
|
"narHash": "sha256-P69OmveboXzS+es1vQGS4bt+ckwbeIExqxfGLjGuJqA=",
|
||||||
|
"owner": "ursi",
|
||||||
|
"repo": "nix-make-shell",
|
||||||
|
"rev": "8add91681170924e4d0591b22f294aee3f5516f9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ursi",
|
||||||
|
"repo": "nix-make-shell",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-github-actions": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"poetry2nix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1688870561,
|
||||||
|
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1697059129,
|
||||||
|
"narHash": "sha256-9NJcFF9CEYPvHJ5ckE8kvINvI84SZZ87PvqMbH6pro0=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "5e4c2ada4fcd54b99d56d7bd62f384511a7e2593",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1697059129,
|
||||||
|
"narHash": "sha256-9NJcFF9CEYPvHJ5ckE8kvINvI84SZZ87PvqMbH6pro0=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "5e4c2ada4fcd54b99d56d7bd62f384511a7e2593",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"poetry2nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nix-github-actions": "nix-github-actions",
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1696512612,
|
||||||
|
"narHash": "sha256-p6niqag7b4XEHvzWgG0X/xjoW/ZXbAxW8ggd8yReT3Y=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "poetry2nix",
|
||||||
|
"rev": "e23218d1599e3369dfc878757e58974017e0ecc8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "poetry2nix",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"bash": "bash",
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"make-shell": "make-shell",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"poetry2nix": "poetry2nix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
76
flake.nix
Normal file
76
flake.nix
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
description = "iperf watcher";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs = {
|
||||||
|
url = "nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
flake-utils = {
|
||||||
|
url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
bash = {
|
||||||
|
url = "git+https://git.ocjtech.us/jeff/nixos-bash-prompt-builder.git";
|
||||||
|
};
|
||||||
|
make-shell = {
|
||||||
|
url = "github:ursi/nix-make-shell";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, poetry2nix, flake-utils, bash, ... }@inputs:
|
||||||
|
flake-utils.lib.eachDefaultSystem
|
||||||
|
(system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default =
|
||||||
|
let
|
||||||
|
project = "iperf-watcher";
|
||||||
|
prompt = (
|
||||||
|
bash.build_ps1_prompt
|
||||||
|
bash.ansi_normal_blue
|
||||||
|
"${project} - ${bash.username}@${bash.hostname_short}: ${bash.current_working_directory}"
|
||||||
|
"${project}:${bash.current_working_directory}"
|
||||||
|
);
|
||||||
|
make-shell = import inputs.make-shell {
|
||||||
|
inherit system;
|
||||||
|
pkgs = pkgs;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
make-shell {
|
||||||
|
packages = [
|
||||||
|
# python
|
||||||
|
# pkgs.poetry
|
||||||
|
pkgs.iperf3
|
||||||
|
pkgs.zon2nix
|
||||||
|
pkgs.zig_0_11
|
||||||
|
pkgs.zls
|
||||||
|
];
|
||||||
|
env = {
|
||||||
|
POETRY_VIRTUALENVS_IN_PROJECT = "true";
|
||||||
|
POETRY_VIRTUALENVS_OPTIONS_SYSTEM_SITE_PACKAGES = "true";
|
||||||
|
PS1 = prompt;
|
||||||
|
PYTHON_KEYRING_BACKEND = "keyring.backends.null.Keyring";
|
||||||
|
IPERF3 = "${pkgs.iperf3}/bin/iperf3";
|
||||||
|
};
|
||||||
|
setup = ''
|
||||||
|
export PATH=$(pwd)/.venv/bin:$PATH
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
packages = {
|
||||||
|
iperf-watcher = pkgs.stdenv.mkDerivation {
|
||||||
|
name = "iperf-watcher";
|
||||||
|
src = ./.;
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.zig_0_11.hook
|
||||||
|
];
|
||||||
|
postPatch = ''
|
||||||
|
ln -s ${pkgs.callPackage ./deps.nix { }} $ZIG_GLOBAL_CACHE_DIR/p
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
124
src/iperf3.zig
Normal file
124
src/iperf3.zig
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const IPerfReturn = struct {
|
||||||
|
start: struct {
|
||||||
|
connected: []struct {
|
||||||
|
socket: u64,
|
||||||
|
local_host: []const u8,
|
||||||
|
local_port: u16,
|
||||||
|
remote_host: []const u8,
|
||||||
|
remote_port: u16,
|
||||||
|
},
|
||||||
|
version: []const u8,
|
||||||
|
system_info: []const u8,
|
||||||
|
sock_bufsize: ?u64 = null,
|
||||||
|
sndbuf_actual: ?u64 = null,
|
||||||
|
rcvbuf_actual: ?u64 = null,
|
||||||
|
timestamp: ?struct {
|
||||||
|
time: []const u8,
|
||||||
|
timesecs: u64,
|
||||||
|
} = null,
|
||||||
|
accepted_connection: ?struct {
|
||||||
|
host: []const u8,
|
||||||
|
port: u16,
|
||||||
|
} = null,
|
||||||
|
cookie: ?[]const u8 = null,
|
||||||
|
tcp_mss_default: ?u64 = null,
|
||||||
|
target_bitrate: ?u64 = null,
|
||||||
|
fq_rate: ?u64 = null,
|
||||||
|
test_start: ?struct {
|
||||||
|
protocol: []const u8,
|
||||||
|
num_streams: u64,
|
||||||
|
blksize: u64,
|
||||||
|
omit: u64,
|
||||||
|
duration: u64,
|
||||||
|
bytes: u64,
|
||||||
|
blocks: u64,
|
||||||
|
reverse: u64,
|
||||||
|
tos: u64,
|
||||||
|
target_bitrate: u64,
|
||||||
|
bidir: u64,
|
||||||
|
fqrate: u64,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
intervals: []struct {
|
||||||
|
streams: []struct {
|
||||||
|
socket: u64,
|
||||||
|
start: f128,
|
||||||
|
end: f128,
|
||||||
|
seconds: f128,
|
||||||
|
bytes: u64,
|
||||||
|
bits_per_second: f128,
|
||||||
|
omitted: bool,
|
||||||
|
sender: bool,
|
||||||
|
},
|
||||||
|
sum: struct {
|
||||||
|
start: f128,
|
||||||
|
end: f128,
|
||||||
|
seconds: f128,
|
||||||
|
bytes: u64,
|
||||||
|
bits_per_second: f128,
|
||||||
|
omitted: bool,
|
||||||
|
sender: bool,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
end: struct {
|
||||||
|
streams: ?[]struct {
|
||||||
|
sender: struct {
|
||||||
|
socket: u64,
|
||||||
|
start: f128,
|
||||||
|
end: f128,
|
||||||
|
seconds: f128,
|
||||||
|
bytes: u64,
|
||||||
|
bits_per_second: f128,
|
||||||
|
sender: bool,
|
||||||
|
},
|
||||||
|
receiver: struct {
|
||||||
|
socket: u64,
|
||||||
|
start: f128,
|
||||||
|
end: f128,
|
||||||
|
seconds: f128,
|
||||||
|
bytes: u64,
|
||||||
|
bits_per_second: f128,
|
||||||
|
sender: bool,
|
||||||
|
},
|
||||||
|
} = null,
|
||||||
|
sum_sent: ?struct {
|
||||||
|
start: f128,
|
||||||
|
end: f128,
|
||||||
|
seconds: f128,
|
||||||
|
bytes: u64,
|
||||||
|
bits_per_second: f128,
|
||||||
|
sender: bool,
|
||||||
|
} = null,
|
||||||
|
sum_received: ?struct {
|
||||||
|
start: f128,
|
||||||
|
end: f128,
|
||||||
|
seconds: f128,
|
||||||
|
bytes: u64,
|
||||||
|
bits_per_second: f128,
|
||||||
|
sender: bool,
|
||||||
|
} = null,
|
||||||
|
cpu_utilization_percent: ?struct {
|
||||||
|
host_total: f128,
|
||||||
|
host_user: f128,
|
||||||
|
host_system: f128,
|
||||||
|
remote_total: f128,
|
||||||
|
remote_user: f128,
|
||||||
|
remote_system: f128,
|
||||||
|
} = null,
|
||||||
|
receiver_tcp_congestion: ?[]const u8 = null,
|
||||||
|
},
|
||||||
|
@"error": ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
test "test-normal" {
|
||||||
|
const input = @embedFile("test/test-normal.json");
|
||||||
|
const result = try std.json.parseFromSlice(IPerfReturn, std.testing.allocator, input, .{});
|
||||||
|
defer result.deinit();
|
||||||
|
// std.debug.print("{}\n", .{result.value});
|
||||||
|
// const test_output = try std.fs.cwd().createFile("test.json", .{});
|
||||||
|
// try std.json.stringify(result.value, .{}, test_output.writer());
|
||||||
|
// test_output.close();
|
||||||
|
try std.testing.expect(result.value.@"error" == null);
|
||||||
|
}
|
48
src/loki.zig
Normal file
48
src/loki.zig
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const LogLevel = enum {
|
||||||
|
critical,
|
||||||
|
@"error",
|
||||||
|
warning,
|
||||||
|
info,
|
||||||
|
debug,
|
||||||
|
trace,
|
||||||
|
unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const LokiLabels = struct {
|
||||||
|
job: []const u8,
|
||||||
|
server: []const u8,
|
||||||
|
level: LogLevel = .info,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const LokiValue = struct {
|
||||||
|
ts: i128,
|
||||||
|
line: []const u8,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn jsonStringify(self: Self, jws: anytype) !void {
|
||||||
|
var tsb: [32]u8 = undefined;
|
||||||
|
const tsb_size = std.fmt.formatIntBuf(
|
||||||
|
&tsb,
|
||||||
|
self.ts,
|
||||||
|
10,
|
||||||
|
.lower,
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
try jws.beginArray();
|
||||||
|
try jws.write(tsb[0..tsb_size]);
|
||||||
|
try jws.write(self.line);
|
||||||
|
try jws.endArray();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const LokiStream = struct {
|
||||||
|
stream: LokiLabels,
|
||||||
|
values: []const LokiValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const LokiStreams = struct {
|
||||||
|
streams: []const LokiStream,
|
||||||
|
};
|
229
src/main.zig
Normal file
229
src/main.zig
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const datetime = @import("datetime");
|
||||||
|
|
||||||
|
const iperf3 = @import("iperf3.zig");
|
||||||
|
const loki = @import("loki.zig");
|
||||||
|
|
||||||
|
pub const std_options = struct {
|
||||||
|
pub const log_level = .debug;
|
||||||
|
pub const logFn = myLogFn;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn myLogFn(
|
||||||
|
comptime level: std.log.Level,
|
||||||
|
comptime scope: @TypeOf(.EnumLiteral),
|
||||||
|
comptime format: []const u8,
|
||||||
|
args: anytype,
|
||||||
|
) void {
|
||||||
|
// const scope_prefix = "(" ++ switch (scope) {
|
||||||
|
// .iperf3, std.log.default_log_scope => @tagName(scope),
|
||||||
|
// else => if (@intFromEnum(level) <= @intFromEnum(std.log.Level.debug))
|
||||||
|
// @tagName(scope)
|
||||||
|
// else
|
||||||
|
// return,
|
||||||
|
// } ++ "): ";
|
||||||
|
const scope_prefix = "(" ++ @tagName(scope) ++ "): ";
|
||||||
|
const prefix = " [" ++ comptime level.asText() ++ "] " ++ scope_prefix;
|
||||||
|
|
||||||
|
// Print the message to stderr, silently ignoring any errors
|
||||||
|
std.debug.getStderrMutex().lock();
|
||||||
|
defer std.debug.getStderrMutex().unlock();
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const now = datetime.Instant.now().asDateTime();
|
||||||
|
nosuspend now.format("YYYY-MM-DDTHH:mm:ss.SSSSSSSSS", .{}, stderr) catch return;
|
||||||
|
nosuspend stderr.writeAll(prefix) catch return;
|
||||||
|
nosuspend stderr.print(format, args) catch return;
|
||||||
|
if (format[format.len - 1] != '\n') nosuspend stderr.writeAll("\n") catch return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Config = struct {
|
||||||
|
loki: struct {
|
||||||
|
url: []const u8,
|
||||||
|
username: []const u8,
|
||||||
|
password: ?[]const u8 = null,
|
||||||
|
password_file: ?[]const u8 = null,
|
||||||
|
},
|
||||||
|
iperf3: struct {
|
||||||
|
path: ?[]const u8 = null,
|
||||||
|
port: ?u16 = null,
|
||||||
|
},
|
||||||
|
|
||||||
|
pub fn deinit(self: @This(), allocator: std.mem.Allocator) void {
|
||||||
|
allocator.free(self.loki.url);
|
||||||
|
allocator.free(self.loki.username);
|
||||||
|
if (self.loki.password) |password| allocator.free(password);
|
||||||
|
if (self.loki.password_file) |password_file| allocator.free(password_file);
|
||||||
|
if (self.iperf3.path) |path| allocator.free(path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn ConfigWrapper(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
arena: *std.heap.ArenaAllocator,
|
||||||
|
value: T,
|
||||||
|
|
||||||
|
pub fn deinit(self: @This()) void {
|
||||||
|
const allocator = self.arena.child_allocator;
|
||||||
|
self.arena.deinit();
|
||||||
|
allocator.destroy(self.arena);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readConfig(allocator: std.mem.Allocator, path: []const u8) !ConfigWrapper(Config) {
|
||||||
|
var config = ConfigWrapper(Config){
|
||||||
|
.arena = try allocator.create(std.heap.ArenaAllocator),
|
||||||
|
.value = undefined,
|
||||||
|
};
|
||||||
|
config.arena.child_allocator = allocator;
|
||||||
|
const data = try std.fs.cwd().readFileAlloc(config.arena.child_allocator, path, 1024);
|
||||||
|
config.value = try std.json.parseFromSliceLeaky(Config, config.arena.child_allocator, data, .{});
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const iperf3_log = std.log.scoped(.iperf3);
|
||||||
|
|
||||||
|
const hostname = blk: {
|
||||||
|
var buffer: [std.os.HOST_NAME_MAX]u8 = undefined;
|
||||||
|
const name = try std.os.gethostname(&buffer);
|
||||||
|
iperf3_log.debug("{s}", .{name});
|
||||||
|
break :blk name;
|
||||||
|
};
|
||||||
|
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
const config = try readConfig(allocator, "config.json");
|
||||||
|
|
||||||
|
const b64 = std.base64.standard.Encoder;
|
||||||
|
|
||||||
|
var auth_buf: [256]u8 = undefined;
|
||||||
|
const auth = try std.fmt.bufPrint(
|
||||||
|
&auth_buf,
|
||||||
|
"{s}:{s}",
|
||||||
|
.{
|
||||||
|
config.value.loki.username,
|
||||||
|
config.value.loki.password.?,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
var auth_encoded_buf: [b64.calcSize(auth_buf.len)]u8 = undefined;
|
||||||
|
var auth_encoded = b64.encode(&auth_encoded_buf, auth);
|
||||||
|
var auth_header_buf: [256]u8 = undefined;
|
||||||
|
var auth_header = try std.fmt.bufPrint(&auth_header_buf, "Basic {s}", .{auth_encoded});
|
||||||
|
|
||||||
|
const uri = try std.Uri.parse(config.value.loki.url);
|
||||||
|
|
||||||
|
// var headers = std.http.Headers{ .allocator = allocator };
|
||||||
|
// try headers.append("Authorization", auth_header);
|
||||||
|
// try headers.append("Content-Type", "application/json");
|
||||||
|
|
||||||
|
var client = std.http.Client{ .allocator = allocator };
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
// const stderr_file = std.io.getStdErr().writer();
|
||||||
|
// var stderr_bw = std.io.bufferedWriter(stderr_file);
|
||||||
|
// const stderr = stderr_bw.writer();
|
||||||
|
// _ = stderr;
|
||||||
|
|
||||||
|
var port_buf: [16]u8 = undefined;
|
||||||
|
var port: []u8 = undefined;
|
||||||
|
if (config.value.iperf3.port) |p| {
|
||||||
|
port = try std.fmt.bufPrint(&port_buf, "{d}", .{p});
|
||||||
|
} else {
|
||||||
|
port = try std.fmt.bufPrint(&port_buf, "{d}", .{5201});
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
iperf3_log.info("waiting for connection", .{});
|
||||||
|
// try stderr_bw.flush();
|
||||||
|
var c = std.process.Child.init(
|
||||||
|
&[_][]const u8{
|
||||||
|
if (config.value.iperf3.path) |path| path else "iperf3",
|
||||||
|
"--server",
|
||||||
|
"--port",
|
||||||
|
port,
|
||||||
|
"--json",
|
||||||
|
"--one-off",
|
||||||
|
},
|
||||||
|
allocator,
|
||||||
|
);
|
||||||
|
c.stdin_behavior = .Ignore;
|
||||||
|
c.stdout_behavior = .Pipe;
|
||||||
|
c.stderr_behavior = .Ignore;
|
||||||
|
try c.spawn();
|
||||||
|
|
||||||
|
var reader = c.stdout.?.reader();
|
||||||
|
var token_reader = std.json.reader(allocator, reader);
|
||||||
|
|
||||||
|
var obj = try std.json.parseFromTokenSource(
|
||||||
|
iperf3.IPerfReturn,
|
||||||
|
allocator,
|
||||||
|
&token_reader,
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
defer obj.deinit();
|
||||||
|
|
||||||
|
var line = std.ArrayList(u8).init(allocator);
|
||||||
|
try std.json.stringify(
|
||||||
|
obj.value,
|
||||||
|
.{
|
||||||
|
.emit_null_optional_fields = false,
|
||||||
|
},
|
||||||
|
line.writer(),
|
||||||
|
);
|
||||||
|
defer line.deinit();
|
||||||
|
|
||||||
|
const streams = loki.LokiStreams{
|
||||||
|
.streams = &[_]loki.LokiStream{
|
||||||
|
.{
|
||||||
|
.stream = .{
|
||||||
|
.job = "iperf3",
|
||||||
|
.server = hostname,
|
||||||
|
.level = if (obj.value.@"error" == null) .info else .@"error",
|
||||||
|
},
|
||||||
|
.values = &[_]loki.LokiValue{
|
||||||
|
.{
|
||||||
|
.ts = std.time.nanoTimestamp(),
|
||||||
|
.line = line.items,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var data = std.ArrayList(u8).init(allocator);
|
||||||
|
try std.json.stringify(
|
||||||
|
streams,
|
||||||
|
.{
|
||||||
|
.emit_null_optional_fields = false,
|
||||||
|
},
|
||||||
|
data.writer(),
|
||||||
|
);
|
||||||
|
defer data.deinit();
|
||||||
|
|
||||||
|
// try stderr.writeAll(data.items);
|
||||||
|
// try stderr.writeAll("\n");
|
||||||
|
// try stderr_bw.flush();
|
||||||
|
|
||||||
|
var content_length_buf: [16]u8 = undefined;
|
||||||
|
var content_length = try std.fmt.bufPrint(
|
||||||
|
&content_length_buf,
|
||||||
|
"{d}",
|
||||||
|
.{data.items.len},
|
||||||
|
);
|
||||||
|
|
||||||
|
var headers = std.http.Headers{ .allocator = allocator };
|
||||||
|
try headers.append("Authorization", auth_header);
|
||||||
|
try headers.append("Accept", "application/json");
|
||||||
|
try headers.append("Content-Type", "application/json");
|
||||||
|
try headers.append("Content-Length", content_length);
|
||||||
|
|
||||||
|
var req = try client.request(.POST, uri, headers, .{});
|
||||||
|
try req.start();
|
||||||
|
try req.writeAll(data.items);
|
||||||
|
try req.finish();
|
||||||
|
try req.wait();
|
||||||
|
|
||||||
|
iperf3_log.info("{}\n", .{req.response.status});
|
||||||
|
}
|
||||||
|
}
|
310
src/test/test-normal.json
Normal file
310
src/test/test-normal.json
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
{
|
||||||
|
"start": {
|
||||||
|
"connected": [{
|
||||||
|
"socket": 5,
|
||||||
|
"local_host": "127.0.0.1",
|
||||||
|
"local_port": 5201,
|
||||||
|
"remote_host": "127.0.0.1",
|
||||||
|
"remote_port": 58332
|
||||||
|
}],
|
||||||
|
"version": "iperf 3.14",
|
||||||
|
"system_info": "Linux localhost 6.5.5 #1-NixOS SMP PREEMPT_DYNAMIC Sat Sep 23 09:14:39 UTC 2023 x86_64",
|
||||||
|
"sock_bufsize": 0,
|
||||||
|
"sndbuf_actual": 16384,
|
||||||
|
"rcvbuf_actual": 131072,
|
||||||
|
"timestamp": {
|
||||||
|
"time": "Sat, 14 Oct 2023 04:54:02 GMT",
|
||||||
|
"timesecs": 1697259242
|
||||||
|
},
|
||||||
|
"accepted_connection": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 58328
|
||||||
|
},
|
||||||
|
"cookie": "jy5fh44dohyv7imf4ouzjm4sx5ssdffs5teh",
|
||||||
|
"tcp_mss_default": 0,
|
||||||
|
"target_bitrate": 0,
|
||||||
|
"fq_rate": 0,
|
||||||
|
"test_start": {
|
||||||
|
"protocol": "TCP",
|
||||||
|
"num_streams": 1,
|
||||||
|
"blksize": 131072,
|
||||||
|
"omit": 0,
|
||||||
|
"duration": 10,
|
||||||
|
"bytes": 0,
|
||||||
|
"blocks": 0,
|
||||||
|
"reverse": 0,
|
||||||
|
"tos": 0,
|
||||||
|
"target_bitrate": 0,
|
||||||
|
"bidir": 0,
|
||||||
|
"fqrate": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intervals": [{
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 0,
|
||||||
|
"end": 1.000003,
|
||||||
|
"seconds": 1.0000029802322388,
|
||||||
|
"bytes": 6765608907,
|
||||||
|
"bits_per_second": 54124709951.794487,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 1.000003,
|
||||||
|
"seconds": 1.0000029802322388,
|
||||||
|
"bytes": 6765608907,
|
||||||
|
"bits_per_second": 54124709951.794487,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 1.000003,
|
||||||
|
"end": 2.000009,
|
||||||
|
"seconds": 1.0000059604644775,
|
||||||
|
"bytes": 7257980928,
|
||||||
|
"bits_per_second": 58063501338.562828,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 1.000003,
|
||||||
|
"end": 2.000009,
|
||||||
|
"seconds": 1.0000059604644775,
|
||||||
|
"bytes": 7257980928,
|
||||||
|
"bits_per_second": 58063501338.562828,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 2.000009,
|
||||||
|
"end": 3.000008,
|
||||||
|
"seconds": 0.99999898672103882,
|
||||||
|
"bytes": 7245135872,
|
||||||
|
"bits_per_second": 57961145706.8095,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 2.000009,
|
||||||
|
"end": 3.000008,
|
||||||
|
"seconds": 0.99999898672103882,
|
||||||
|
"bytes": 7245135872,
|
||||||
|
"bits_per_second": 57961145706.8095,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 3.000008,
|
||||||
|
"end": 4.000009,
|
||||||
|
"seconds": 1.0000009536743164,
|
||||||
|
"bytes": 7196770304,
|
||||||
|
"bits_per_second": 57574107525.052361,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 3.000008,
|
||||||
|
"end": 4.000009,
|
||||||
|
"seconds": 1.0000009536743164,
|
||||||
|
"bytes": 7196770304,
|
||||||
|
"bits_per_second": 57574107525.052361,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 4.000009,
|
||||||
|
"end": 5.000013,
|
||||||
|
"seconds": 1.0000040531158447,
|
||||||
|
"bytes": 6916538368,
|
||||||
|
"bits_per_second": 55332082676.658981,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 4.000009,
|
||||||
|
"end": 5.000013,
|
||||||
|
"seconds": 1.0000040531158447,
|
||||||
|
"bytes": 6916538368,
|
||||||
|
"bits_per_second": 55332082676.658981,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 5.000013,
|
||||||
|
"end": 6.000005,
|
||||||
|
"seconds": 0.9999920129776,
|
||||||
|
"bytes": 7191789568,
|
||||||
|
"bits_per_second": 57534776075.545288,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 5.000013,
|
||||||
|
"end": 6.000005,
|
||||||
|
"seconds": 0.9999920129776,
|
||||||
|
"bytes": 7191789568,
|
||||||
|
"bits_per_second": 57534776075.545288,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 6.000005,
|
||||||
|
"end": 7.000006,
|
||||||
|
"seconds": 1.0000009536743164,
|
||||||
|
"bytes": 7165444096,
|
||||||
|
"bits_per_second": 57323498100.052132,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 6.000005,
|
||||||
|
"end": 7.000006,
|
||||||
|
"seconds": 1.0000009536743164,
|
||||||
|
"bytes": 7165444096,
|
||||||
|
"bits_per_second": 57323498100.052132,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 7.000006,
|
||||||
|
"end": 8.000009,
|
||||||
|
"seconds": 1.0000029802322388,
|
||||||
|
"bytes": 7299137536,
|
||||||
|
"bits_per_second": 58392926263.518631,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 7.000006,
|
||||||
|
"end": 8.000009,
|
||||||
|
"seconds": 1.0000029802322388,
|
||||||
|
"bytes": 7299137536,
|
||||||
|
"bits_per_second": 58392926263.518631,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 8.000009,
|
||||||
|
"end": 9.00002,
|
||||||
|
"seconds": 1.0000109672546387,
|
||||||
|
"bytes": 7292846080,
|
||||||
|
"bits_per_second": 58342128787.017433,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 8.000009,
|
||||||
|
"end": 9.00002,
|
||||||
|
"seconds": 1.0000109672546387,
|
||||||
|
"bytes": 7292846080,
|
||||||
|
"bits_per_second": 58342128787.017433,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 9.00002,
|
||||||
|
"end": 10.000013,
|
||||||
|
"seconds": 0.99999302625656128,
|
||||||
|
"bytes": 7296516096,
|
||||||
|
"bits_per_second": 58372535843.088837,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 9.00002,
|
||||||
|
"end": 10.000013,
|
||||||
|
"seconds": 0.99999302625656128,
|
||||||
|
"bytes": 7296516096,
|
||||||
|
"bits_per_second": 58372535843.088837,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"streams": [{
|
||||||
|
"socket": 5,
|
||||||
|
"start": 10.000013,
|
||||||
|
"end": 10.00009,
|
||||||
|
"seconds": 7.6999996963422745e-05,
|
||||||
|
"bytes": 458805,
|
||||||
|
"bits_per_second": 47668053827.89257,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}],
|
||||||
|
"sum": {
|
||||||
|
"start": 10.000013,
|
||||||
|
"end": 10.00009,
|
||||||
|
"seconds": 7.6999996963422745e-05,
|
||||||
|
"bytes": 458805,
|
||||||
|
"bits_per_second": 47668053827.89257,
|
||||||
|
"omitted": false,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"end": {
|
||||||
|
"streams": [{
|
||||||
|
"sender": {
|
||||||
|
"socket": 5,
|
||||||
|
"start": 0,
|
||||||
|
"end": 10.00009,
|
||||||
|
"seconds": 10.00009,
|
||||||
|
"bytes": 0,
|
||||||
|
"bits_per_second": 0,
|
||||||
|
"sender": false
|
||||||
|
},
|
||||||
|
"receiver": {
|
||||||
|
"socket": 5,
|
||||||
|
"start": 0,
|
||||||
|
"end": 10.00009,
|
||||||
|
"seconds": 10.00009,
|
||||||
|
"bytes": 71628226560,
|
||||||
|
"bits_per_second": 57302065529.410233,
|
||||||
|
"sender": false
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"sum_sent": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 10.00009,
|
||||||
|
"seconds": 10.00009,
|
||||||
|
"bytes": 0,
|
||||||
|
"bits_per_second": 0,
|
||||||
|
"sender": false
|
||||||
|
},
|
||||||
|
"sum_received": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 10.00009,
|
||||||
|
"seconds": 10.00009,
|
||||||
|
"bytes": 71628226560,
|
||||||
|
"bits_per_second": 57302065529.410233,
|
||||||
|
"sender": false
|
||||||
|
},
|
||||||
|
"cpu_utilization_percent": {
|
||||||
|
"host_total": 88.797154269026464,
|
||||||
|
"host_user": 3.33468598620294,
|
||||||
|
"host_system": 85.4624582829255,
|
||||||
|
"remote_total": 0,
|
||||||
|
"remote_user": 0,
|
||||||
|
"remote_system": 0
|
||||||
|
},
|
||||||
|
"receiver_tcp_congestion": "cubic"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue