From: Dominik Csapak <d.csapak@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH manager 4/7] status/influxdb: implement influxdb 2.x http api
Date: Wed, 2 Dec 2020 10:21:09 +0100 [thread overview]
Message-ID: <20201202092113.15911-7-d.csapak@proxmox.com> (raw)
In-Reply-To: <20201202092113.15911-1-d.csapak@proxmox.com>
needs an organization/bucket (previously db) and an optional token
the http client does not fit exactly in the connect/send/disconnect
scheme, so it simply creates a request in 'connect',
does the actual http connection in 'send' and nothing in 'disconnect'
max-body-size is set to 25.000.000 bytes by default (the influxdb default)
and the timeout to 1 second (same as default graphite tcp timeout)
the token (if given) gets saved in /etc/pve/priv/metricserver/$ID.pw
it is optional, because the 1.8.x compatibility api does not need
authentication (in contrast to influxdb 2.x)
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
PVE/Status/InfluxDB.pm | 215 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 207 insertions(+), 8 deletions(-)
diff --git a/PVE/Status/InfluxDB.pm b/PVE/Status/InfluxDB.pm
index 6663579f..f16af486 100644
--- a/PVE/Status/InfluxDB.pm
+++ b/PVE/Status/InfluxDB.pm
@@ -6,6 +6,8 @@ use warnings;
use POSIX qw(isnan isinf);
use Scalar::Util 'looks_like_number';
use IO::Socket::IP;
+use LWP::UserAgent;
+use HTTP::Request;
use PVE::SafeSyslog;
@@ -24,12 +26,51 @@ sub type {
return 'influxdb';
}
+sub properties {
+ return {
+ organization => {
+ description => "The influxdb organization. Only necessary when using the http v2 api. ".
+ "Has no meaning when using v2 compatibility api.",
+ type => 'string',
+ optional => 1,
+ },
+ bucket => {
+ description => "The influxdb bucket/db. Only necessary when using the http v2 api.",
+ type => 'string',
+ optional => 1,
+ },
+ token => {
+ description => "The influxdb access token. Only necessary when using the http v2 api. ".
+ "If the v2 compatibility api is used, use 'user:password' instead.",
+ type => 'string',
+ optional => 1,
+ },
+ influxdbproto => {
+ type => 'string',
+ enum => ['udp', 'http', 'https'],
+ default => 'udp',
+ optional => 1,
+ },
+ 'max-body-size' => {
+ description => "Influxdb max-body-size. Requests are batched up to this size.",
+ type => 'integer',
+ minimum => 1,
+ default => 25_000_000,
+ }
+ };
+}
sub options {
return {
server => {},
port => {},
mtu => { optional => 1 },
disable => { optional => 1 },
+ organization => { optional => 1},
+ bucket => { optional => 1},
+ token => { optional => 1},
+ influxdbproto => { optional => 1},
+ timeout => { optional => 1},
+ 'max-body-size' => { optional => 1 },
};
}
@@ -84,21 +125,106 @@ sub update_storage_status {
build_influxdb_payload($class, $txn, $data, $ctime, $object);
}
-sub _connect {
+sub _send_batch_size {
my ($class, $cfg) = @_;
+ my $proto = $cfg->{influxdbproto} // 'udp';
+ if ($proto ne 'udp') {
+ return $cfg->{'max-body-size'} // 25_000_000;
+ }
+
+ return $class->SUPER::_send_batch_size($cfg);
+}
+
+sub send {
+ my ($class, $connection, $data, $cfg) = @_;
+
+ my $proto = $cfg->{influxdbproto} // 'udp';
+ if ($proto eq 'udp') {
+ return $class->SUPER::send($connection, $data, $cfg);
+ } elsif ($proto =~ m/^https?$/) {
+ my $ua = LWP::UserAgent->new();
+ $ua->timeout($cfg->{timeout} // 1);
+ $connection->content($data);
+ my $response = $ua->request($connection);
+
+ if (!$response->is_success) {
+ my $err = $response->status_line;
+ die "$err\n";
+ }
+ } else {
+ die "invalid protocol\n";
+ }
+
+ return;
+}
+
+sub _disconnect {
+ my ($class, $connection, $cfg) = @_;
+ my $proto = $cfg->{influxdbproto} // 'udp';
+ if ($proto eq 'udp') {
+ return $class->SUPER::_disconnect($connection, $cfg);
+ }
+
+ return;
+}
+
+sub _connect {
+ my ($class, $cfg, $id) = @_;
my $host = $cfg->{server};
my $port = $cfg->{port};
+ my $proto = $cfg->{influxdbproto} // 'udp';
+
+ if ($proto eq 'udp') {
+ my $socket = IO::Socket::IP->new(
+ PeerAddr => $host,
+ PeerPort => $port,
+ Proto => 'udp',
+ ) || die "couldn't create influxdb socket [$host]:$port - $@\n";
+
+ $socket->blocking(0);
+
+ return $socket;
+ } elsif ($proto =~ m/^https?$/) {
+ my $token = get_credentials($id);
+ my $org = $cfg->{organization} // 'proxmox';
+ my $bucket = $cfg->{bucket} // 'proxmox';
+ my $url = "${proto}://${host}:${port}/api/v2/write?org=${org}&bucket=${bucket}";
+
+ my $req = HTTP::Request->new(POST => $url);
+ if (defined($token)) {
+ $req->header( "Authorization", "Token $token");
+ }
- my $socket = IO::Socket::IP->new(
- PeerAddr => $host,
- PeerPort => $port,
- Proto => 'udp',
- ) || die "couldn't create influxdb socket [$host]:$port - $@\n";
+ return $req;
+ }
+
+ die "cannot connect to influxdb: invalid protocol\n";
+}
- $socket->blocking(0);
+sub test_connection {
+ my ($class, $cfg, $id) = @_;
+
+ my $proto = $cfg->{influxdbproto} // 'udp';
+ if ($proto eq 'udp') {
+ return $class->SUPER::test_connection($cfg, $id);
+ } elsif ($proto =~ m/^https?$/) {
+ my $host = $cfg->{server};
+ my $port = $cfg->{port};
+ my $url = "${proto}://${host}:${port}/health";
+ my $ua = LWP::UserAgent->new();
+ $ua->timeout($cfg->{timeout} // 1);
+ my $response = $ua->get($url);
+
+ if (!$response->is_success) {
+ my $err = $response->status_line;
+ die "$err\n";
+ }
+ } else {
+ die "invalid protocol\n";
+ }
- return $socket;
+ return;
}
sub build_influxdb_payload {
@@ -177,4 +303,77 @@ sub prepare_value {
return $value;
}
+my $priv_dir = "/etc/pve/priv/metricserver";
+
+sub cred_file_name {
+ my ($id) = @_;
+ return "${priv_dir}/${id}.pw";
+}
+
+sub delete_credentials {
+ my ($id) = @_;
+
+ if (my $cred_file = cred_file_name($id)) {
+ unlink($cred_file)
+ or warn "removing influxdb credentials file '$cred_file' failed: $!\n";
+ }
+
+ return;
+}
+
+sub set_credentials {
+ my ($id, $token) = @_;
+
+ my $cred_file = cred_file_name($id);
+
+ mkdir $priv_dir;
+
+ PVE::Tools::file_set_contents($cred_file, "$token");
+}
+
+sub get_credentials {
+ my ($id) = @_;
+
+ my $cred_file = cred_file_name($id);
+
+ return PVE::Tools::file_get_contents($cred_file);
+}
+
+sub on_add_hook {
+ my ($class, $id, $opts, $sensitive_opts) = @_;
+
+ my $token = $sensitive_opts->{token};
+
+ if (defined($token)) {
+ set_credentials($id, $token);
+ } else {
+ delete_credenetials($id);
+ }
+
+ return undef;
+}
+
+sub on_update_hook {
+ my ($class, $id, $opts, $sensitive_opts) = @_;
+ return if !exists($sensitive_opts->{token});
+
+ my $token = $sensitive_opts->{token};
+ if (defined($token)) {
+ set_credentials($id, $token);
+ } else {
+ delete_credenetials($id);
+ }
+
+ return undef;
+}
+
+sub on_delete_hook {
+ my ($class, $id, $opts) = @_;
+
+ delete_credentials($id);
+
+ return undef;
+}
+
+
1;
--
2.20.1
next prev parent reply other threads:[~2020-12-02 9:21 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-12-02 9:21 [pve-devel] [PATCH common/storage/manager/docs] implement http api for influxdb status plugin Dominik Csapak
2020-12-02 9:21 ` [pve-devel] [PATCH common 1/1] tools: add extract_sensitive_params Dominik Csapak
2020-12-03 8:47 ` Thomas Lamprecht
2020-12-03 9:16 ` Wolfgang Bumiller
2020-12-03 9:35 ` Thomas Lamprecht
2020-12-03 15:52 ` [pve-devel] applied: " Thomas Lamprecht
2020-12-02 9:21 ` [pve-devel] [PATCH storage 1/1] api: storage/config: use extract_sensitive_params from tools Dominik Csapak
2021-01-28 16:31 ` [pve-devel] applied: " Thomas Lamprecht
2020-12-02 9:21 ` [pve-devel] [PATCH manager 1/7] api: cluster/metricserver: prevent simultaneosly setting and deleting of property Dominik Csapak
2020-12-03 9:05 ` Thomas Lamprecht
2020-12-04 11:30 ` Dominik Csapak
2020-12-04 11:57 ` Thomas Lamprecht
2020-12-04 12:45 ` Thomas Lamprecht
2020-12-02 9:21 ` [pve-devel] [PATCH manager 2/7] status/plugin: extend send/_connect/_disconnect/test_connection Dominik Csapak
2020-12-02 9:21 ` [pve-devel] [PATCH manager 3/7] status/plugin: extend with add/update/delete hooks Dominik Csapak
2020-12-02 9:21 ` Dominik Csapak [this message]
2020-12-02 9:21 ` [pve-devel] [PATCH manager 5/7] status/influxdb: remove unnecessary comment Dominik Csapak
2020-12-02 9:21 ` [pve-devel] [PATCH manager 6/7] ui: add necessary fields for influxdb http api Dominik Csapak
2020-12-02 9:21 ` [pve-devel] [PATCH manager 7/7] ui: dc/MetricServerView: add onlineHelp to edit windows Dominik Csapak
2020-12-02 9:21 ` [pve-devel] [PATCH docs 1/1] external metrics server: extend docs to explain http api Dominik Csapak
2021-01-28 16:36 ` [pve-devel] applied-series: [PATCH common/storage/manager/docs] implement http api for influxdb status plugin Thomas Lamprecht
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20201202092113.15911-7-d.csapak@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox