first commit
This commit is contained in:
commit
df462caa1b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
374
Cargo.lock
generated
Normal file
374
Cargo.lock
generated
Normal file
@ -0,0 +1,374 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bgutil-rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cassandra-cpp",
|
||||
"chrono",
|
||||
"clap",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cassandra-cpp"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0767df74532058d1be2b1d7cad0b4808bb1abebbb428123b4130378227386d44"
|
||||
dependencies = [
|
||||
"cassandra-cpp-sys",
|
||||
"decimal",
|
||||
"error-chain",
|
||||
"slog",
|
||||
"time",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cassandra-cpp-sys"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2481c6da4dd98a4ad6156ddb66784622773be605ce2427214c2c0a0a2cda512a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags 1.2.1",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decimal"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1556196ca80d503ddabe15a2e97004cb7e48a57be565a2e49984d84098cad669"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0",
|
||||
"gcc",
|
||||
"libc",
|
||||
"ord_subset",
|
||||
"rustc-serialize",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
|
||||
|
||||
[[package]]
|
||||
name = "ord_subset"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "010031fbb4788c3d4b0cdbabfed03b981761727fc064e3d75c90e33a826cab8f"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.123"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
|
||||
|
||||
[[package]]
|
||||
name = "slog"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "bgutil-rs"
|
||||
version = "0.1.0"
|
||||
authors = ["Patrick MARIE <pm@mkz.me>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cassandra-cpp = "0.15.1"
|
||||
chrono = "0.4"
|
||||
clap = "2.33.3"
|
||||
regex = "1.4.3"
|
53
README.md
Normal file
53
README.md
Normal file
@ -0,0 +1,53 @@
|
||||
# bgutil-rs
|
||||
|
||||
## Build
|
||||
|
||||
Don't forget to download & install [cassandra-cpp](https://downloads.datastax.com/cpp-driver/centos/8/cassandra/v2.15.3/) & [libuv](https://downloads.datastax.com/cpp-driver/centos/8/dependencies/libuv/v1.35.0/).
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
$ cargo build
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
|
||||
|
||||
$ cargo run -- --help
|
||||
bgutil-rs
|
||||
|
||||
USAGE:
|
||||
bgutil-rs <SUBCOMMAND>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
SUBCOMMANDS:
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
info Information about a metric
|
||||
```
|
||||
|
||||
### Info
|
||||
|
||||
```sh
|
||||
$ cargo run -- info --help
|
||||
bgutil-rs-info
|
||||
Information about a metric
|
||||
|
||||
USAGE:
|
||||
bgutil-rs info <metric>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
ARGS:
|
||||
<metric> the metric
|
||||
```
|
||||
|
||||
|
||||
## Todo
|
||||
|
||||
* command: read
|
||||
- async
|
||||
- human timestamps
|
||||
* command: list
|
||||
* command: clean
|
196
src/main.rs
Normal file
196
src/main.rs
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* bgutil-rs
|
||||
*
|
||||
* Author: Patrick MARIE <pm@mkz.me>
|
||||
*/
|
||||
use std::str::FromStr;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use cassandra_cpp::*;
|
||||
use chrono::Utc;
|
||||
use clap::{App,AppSettings,Arg,SubCommand};
|
||||
|
||||
mod metric;
|
||||
mod stage;
|
||||
use crate::metric::*;
|
||||
use crate::stage::*;
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn describe_result(result: &CassResult) {
|
||||
println!("Result has {} record(s).", result.row_count());
|
||||
println!("Schema is:");
|
||||
|
||||
for column_id in 0..result.column_count() {
|
||||
println!("{:?}: {:?}",
|
||||
result.column_type(column_id as usize),
|
||||
result.column_name(column_id as usize)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(contact_points: &str) -> Result<Session> {
|
||||
set_level(LogLevel::DISABLED);
|
||||
|
||||
let mut cluster = Cluster::default();
|
||||
cluster.set_contact_points(contact_points).unwrap();
|
||||
cluster.set_load_balance_round_robin();
|
||||
|
||||
cluster.set_protocol_version(4)?;
|
||||
|
||||
cluster.connect()
|
||||
}
|
||||
|
||||
fn fetch_metric(session: &Session, metric_name: &str) -> Result<Metric> {
|
||||
let mut query = stmt!("SELECT * FROM biggraphite_metadata.metrics_metadata WHERE name = ?");
|
||||
query.bind(0, metric_name)?;
|
||||
|
||||
let result = session.execute(&query).wait()?;
|
||||
Ok(result.first_row().unwrap().into())
|
||||
}
|
||||
|
||||
fn metric_info(session: &Session, metric_name: &str) -> Result<()> {
|
||||
let metric = fetch_metric(session, metric_name)?;
|
||||
|
||||
println!("{}", metric);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fetch_points(session_points: &Session, m: &Metric, s: &Stage, time_start: i64, time_end: i64) -> Result<()> {
|
||||
let table_name = s.table_name();
|
||||
|
||||
let q = format!(
|
||||
"SELECT time_start_ms, offset, value FROM biggraphite.{} WHERE metric = ? AND time_start_ms = ? AND offset >= ? AND offset < ? ORDER BY offset",
|
||||
table_name
|
||||
);
|
||||
|
||||
let ranges = TimeRange::new(&s, time_start, time_end).ranges();
|
||||
// XXX concurrent
|
||||
for range in ranges.iter() {
|
||||
let mut query = stmt!(q.as_str());
|
||||
query.bind(0, Uuid::from_str(m.id().as_str())?)?;
|
||||
query.bind(1, range.0)?;
|
||||
query.bind(2, range.1 as i16)?;
|
||||
query.bind(3, range.2 as i16)?;
|
||||
|
||||
let result = session_points.execute(&query).wait()?;
|
||||
|
||||
for row in result.iter() {
|
||||
let ts : i64 = row.get_column_by_name("time_start_ms".to_string())?.get_i64()?;
|
||||
let offset : i16 = row.get_column_by_name("offset".to_string())?.get_i16()?;
|
||||
let value : f64 = row.get_column_by_name("value".to_string())?.get_f64()?;
|
||||
|
||||
let ts = ts / 1000;
|
||||
let offset : i64 = offset as i64 * s.precision_as_seconds();
|
||||
|
||||
println!("{:?};{:?}", ts + offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let matches = App::new("bgutil-rs")
|
||||
.setting(AppSettings::SubcommandRequired)
|
||||
.subcommand(SubCommand::with_name("info")
|
||||
.about("Information about a metric")
|
||||
.arg(Arg::with_name("metric")
|
||||
.help("metric to retrieve info about")
|
||||
.index(1)
|
||||
.required(true)
|
||||
))
|
||||
.subcommand(SubCommand::with_name("read")
|
||||
.about("Read a metric contents")
|
||||
.arg(Arg::with_name("stage")
|
||||
.long("stage")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("time-start")
|
||||
.long("time-start")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("time-end")
|
||||
.long("time-end")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("metric")
|
||||
.help("metric to get values")
|
||||
.index(1)
|
||||
.required(true)
|
||||
))
|
||||
.get_matches();
|
||||
|
||||
let contact_points_metadata = "tag--cstars07--cassandra-cstars07.query.consul.preprod.crto.in";
|
||||
let contact_points_data = "tag--cstars04--cassandra-cstars04.query.consul.preprod.crto.in";
|
||||
|
||||
let session_metadata = match connect(contact_points_metadata) {
|
||||
Ok(session) => session,
|
||||
Err(err) => {
|
||||
eprintln!("{:?}", err);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let session_points = match connect(contact_points_data) {
|
||||
Ok(session) => session,
|
||||
Err(err) => {
|
||||
eprintln!("{:?}", err);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
match matches.subcommand_name() {
|
||||
Some("info") => {
|
||||
let matches = matches.subcommand_matches("info").unwrap();
|
||||
metric_info(&session_metadata, matches.value_of("metric").unwrap())?;
|
||||
},
|
||||
Some("read") => {
|
||||
let matches = matches.subcommand_matches("read").unwrap();
|
||||
let stage = matches.value_of("stage").unwrap_or("11520*60s");
|
||||
// XXX: Change default value relative to stage's precision to have more or less data
|
||||
let time_start = matches.value_of("time-start"); // default now - 1h
|
||||
let time_end= matches.value_of("time-end"); // default: now
|
||||
|
||||
let time_start = match time_start {
|
||||
None => Utc::now().timestamp() - 3600,
|
||||
Some(s) => match s.parse::<i64>() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
eprintln!("Could not parse {}", s);
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let time_end = match time_end {
|
||||
None => time_start + 3600,
|
||||
Some(s) => match s.parse::<i64>() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
eprintln!("Could not parse {}", s);
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let metric_name = matches.value_of("metric").unwrap();
|
||||
let metric = fetch_metric(&session_metadata, metric_name)?;
|
||||
|
||||
let available_stages = metric.stages()?;
|
||||
let stage = Stage::try_from(stage)?;
|
||||
|
||||
if !available_stages.iter().any(|x| *x == stage) {
|
||||
eprintln!("Could not find any stage matching {}", stage);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fetch_points(&session_points, &metric, &stage, time_start, time_end)?;
|
||||
}
|
||||
None => {
|
||||
eprintln!("No command was used.");
|
||||
return Ok(());
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
103
src/metric.rs
Normal file
103
src/metric.rs
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* bgutil-rs
|
||||
*
|
||||
* Author: Patrick MARIE <pm@mkz.me>
|
||||
*/
|
||||
use crate::stage::Stage;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use cassandra_cpp::Row;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Metric {
|
||||
id: String,
|
||||
name: String,
|
||||
config: HashMap<String, String>,
|
||||
created_on: u64,
|
||||
updated_on: u64
|
||||
}
|
||||
|
||||
impl Metric {
|
||||
pub fn id(self: &Self) -> &String {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn config(self: &Self, name: String) -> Result<String, String> {
|
||||
let res = self.config.get(&name);
|
||||
if let Some(v) = res {
|
||||
Ok(v.to_string())
|
||||
} else {
|
||||
Err("Invalid key".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stages(self: &Self) -> Result<Vec<Stage>, String> {
|
||||
let mut out = vec![];
|
||||
let stages = self.config("retention".to_string());
|
||||
|
||||
if let Err(err) = stages {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
for stage in stages.unwrap().split(":") {
|
||||
match Stage::try_from(stage) {
|
||||
Ok(stage) => out.push(stage),
|
||||
Err(err) => {
|
||||
return Err(err.to_string())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Metric {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{} created_on:{} updated_on:{}\n{:?}",
|
||||
self.name,
|
||||
self.created_on,
|
||||
self.updated_on,
|
||||
self.config,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Metric {
|
||||
fn from(name: String) -> Self {
|
||||
Metric {
|
||||
id: String::from(""),
|
||||
name: name,
|
||||
config: HashMap::new(),
|
||||
created_on: 0,
|
||||
updated_on: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Row> for Metric {
|
||||
fn from(row: Row) -> Self {
|
||||
let config_collection = row.get_column_by_name("config".to_string()).unwrap().get_map().unwrap();
|
||||
let mut config : HashMap<String, String> = HashMap::new();
|
||||
config_collection
|
||||
.map(|(k, v)| config.insert(k.to_string(), v.to_string()))
|
||||
.count();
|
||||
|
||||
let created_on = row.get_column_by_name("created_on".to_string()).unwrap();
|
||||
let created_on_timestamp = created_on.get_uuid().unwrap().timestamp();
|
||||
|
||||
let updated_on = row.get_column_by_name("updated_on".to_string()).unwrap();
|
||||
let updated_on_timestamp = updated_on.get_uuid().unwrap().timestamp();
|
||||
|
||||
Self {
|
||||
id: row.get_column_by_name("id".to_string()).unwrap().get_uuid().unwrap().to_string(),
|
||||
name: row.get_column_by_name("name".to_string()).unwrap().to_string(),
|
||||
config: config,
|
||||
created_on: created_on_timestamp,
|
||||
updated_on: updated_on_timestamp
|
||||
}
|
||||
}
|
||||
}
|
181
src/stage.rs
Normal file
181
src/stage.rs
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* bgutil-rs
|
||||
*
|
||||
* Author: Patrick MARIE <pm@mkz.me>
|
||||
*/
|
||||
use std::fmt;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Stage {
|
||||
stage: String,
|
||||
points: u32,
|
||||
precision: u32,
|
||||
factor: char,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Stage {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(stage: &str) -> Result<Self, Self::Error> {
|
||||
let re = Regex::new(r"^(\d+)\*(\d+)(.)");
|
||||
|
||||
if let Err(_) = re {
|
||||
return Err("regex initialisation failed");
|
||||
}
|
||||
|
||||
let captures = match re.unwrap().captures(&stage) {
|
||||
None => return Err("invalid regex capture"),
|
||||
Some(c) => c,
|
||||
};
|
||||
|
||||
let points = captures.get(1).unwrap().as_str().parse::<u32>().unwrap();
|
||||
|
||||
let factor = captures.get(3).unwrap().as_str();
|
||||
if factor.len() != 1 {
|
||||
return Err("invalid factor length")
|
||||
}
|
||||
|
||||
let factor = factor.chars().nth(0).unwrap();
|
||||
|
||||
match factor {
|
||||
's' | 'm' | 'h' | 'd' | 'w' | 'y' => {},
|
||||
_ => {
|
||||
return Err("invalid precision unit")
|
||||
}
|
||||
};
|
||||
|
||||
let precision = captures.get(2).unwrap().as_str().parse::<u32>().unwrap();
|
||||
|
||||
Ok(Stage {
|
||||
stage: String::from(stage),
|
||||
points: points,
|
||||
precision: precision,
|
||||
factor: factor,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Stage {
|
||||
pub fn precision_as_seconds(self: &Self) -> i64 {
|
||||
let factor = match self.factor {
|
||||
's' => 1,
|
||||
'm' => 60,
|
||||
'h' => 60 * 60,
|
||||
'd' => 60 * 60 * 24,
|
||||
'w' => 60 * 60 * 24 * 7,
|
||||
'y' => 60 * 60 * 24 * 365,
|
||||
_ => unreachable!()
|
||||
};
|
||||
|
||||
factor * self.precision as i64
|
||||
}
|
||||
|
||||
pub fn time_offset_ms(self: &Self, ts: i64) -> (i64, i64) {
|
||||
let table_row_size_ms = self.table_row_size_ms();
|
||||
let time_offset_ms = ts * 1000 % table_row_size_ms;
|
||||
let time_start_ms = ts * 1000 - time_offset_ms;
|
||||
|
||||
(
|
||||
time_start_ms,
|
||||
time_offset_ms / (self.precision_as_seconds() * 1000)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn table_name(self: &Self) -> String {
|
||||
// XXX aggregations?
|
||||
format!("datapoints_{}p_{}{}_0", self.points, self.precision, self.factor)
|
||||
}
|
||||
|
||||
pub fn table_row_size_ms(self: &Self) -> i64 {
|
||||
let hour = 3600;
|
||||
let _max_partition_size = 25000;
|
||||
let _expected_points_per_read = 2000;
|
||||
let _min_partition_size_ms = 6 * hour;
|
||||
|
||||
std::cmp::min(
|
||||
self.precision_as_seconds() * 1000 * _max_partition_size,
|
||||
std::cmp::max(
|
||||
self.precision_as_seconds() * 1000 * _expected_points_per_read,
|
||||
_min_partition_size_ms,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Stage {
|
||||
fn clone(&self) -> Stage {
|
||||
Stage {
|
||||
stage: self.stage.to_string(),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Stage {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.points == other.points
|
||||
&& self.precision == other.precision
|
||||
&& self.factor == other.factor
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Stage {}
|
||||
|
||||
impl fmt::Display for Stage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.table_name())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TimeRange {
|
||||
stage: Stage,
|
||||
time_start: i64,
|
||||
time_end: i64
|
||||
}
|
||||
|
||||
impl TimeRange {
|
||||
pub fn new(stage: &Stage, time_start: i64, time_end: i64) -> Self {
|
||||
TimeRange {
|
||||
stage: stage.clone(),
|
||||
time_start: time_start,
|
||||
time_end: time_end,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ranges(&self) -> Vec<(i64, i64, i64)> {
|
||||
let first_offset = self.stage.time_offset_ms(self.time_start);
|
||||
let last_offset = self.stage.time_offset_ms(self.time_end);
|
||||
|
||||
let mut offset = first_offset.0;
|
||||
let mut offset_start = first_offset.1;
|
||||
|
||||
let mut out = vec![];
|
||||
|
||||
while offset != last_offset.0 {
|
||||
out.push((offset, offset_start, self.stage.table_row_size_ms()));
|
||||
|
||||
offset_start = 0;
|
||||
offset += self.stage.table_row_size_ms();
|
||||
}
|
||||
|
||||
out.push((offset, offset_start, last_offset.1));
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TimeRange {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{} ({} -> {})", self.stage, self.time_start, self.time_end)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timerange() {
|
||||
let stage = Stage::try_from("11520*60s").unwrap();
|
||||
assert_eq!(vec![(0, 2, 4)], TimeRange::new(&stage, 120, 240).ranges());
|
||||
assert_eq!(vec![(0, 2, 11520),(691200000, 0, 4)], TimeRange::new(&stage, 120, 691200 + 240).ranges());
|
||||
}
|
Loading…
Reference in New Issue
Block a user