diff --git a/.github/workflows/bloat-test.yml b/.github/bloat-test.yml similarity index 100% rename from .github/workflows/bloat-test.yml rename to .github/bloat-test.yml diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 8f14260f..0126a17e 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -65,7 +65,7 @@ jobs: fmt_and_clippy_windows: name: Cargo Fmt and Clippy - Windows runs-on: windows-latest - needs: fmt_and_clippy_linux + needs: check_pr_is_on_the_right_branch steps: - name: Checkout uses: actions/checkout@v2 @@ -90,7 +90,6 @@ jobs: test_linux_x86_64: name: Test on GNU/Linux x86_64 (Bare metal worker) runs-on: ubuntu-latest - needs: fmt_and_clippy_linux steps: - name: Install dependencies (awxkit) uses: actions/setup-python@v2 @@ -120,7 +119,9 @@ jobs: build_linux_x86_64: name: Build on GNU/Linux x86_64 (Bare metal worker) runs-on: ubuntu-latest - needs: test_linux_x86_64 + needs: + - fmt_and_clippy_linux + - test_linux_x86_64 steps: - name: Install dependencies (awxkit) uses: actions/setup-python@v2 @@ -146,7 +147,6 @@ jobs: test_windows_x86_64: name: Test on Windows x86_64 (Virtual machine worker) runs-on: "windows-2019" - needs: fmt_and_clippy_windows steps: - name: Checkout uses: actions/checkout@v2 @@ -157,6 +157,9 @@ jobs: - name: Install Rust toolchain run: | rustup toolchain install stable-x86_64-pc-windows-msvc + - name: Build (debug mode) + run: | + cargo test --no-default-features --features "prometheus json riemann" - name: Build (debug mode) run: | cargo build --no-default-features --features "prometheus json riemann" diff --git a/.github/workflows/codesee-arch-diagram.yml b/.github/workflows/codesee-arch-diagram.yml index 1ec93fd3..5b345f05 100644 --- a/.github/workflows/codesee-arch-diagram.yml +++ b/.github/workflows/codesee-arch-diagram.yml @@ -26,33 +26,33 @@ jobs: id: detect-languages uses: Codesee-io/codesee-detect-languages-action@latest - - name: Configure JDK 16 - uses: actions/setup-java@v2 - if: ${{ fromJSON(steps.detect-languages.outputs.languages).java }} - with: - java-version: '16' - distribution: 'zulu' + #- name: Configure JDK 16 + # uses: actions/setup-java@v2 + # if: ${{ fromJSON(steps.detect-languages.outputs.languages).java }} + # with: + # java-version: '16' + # distribution: 'zulu' - # CodeSee Maps Go support uses a static binary so there's no setup step required. + ## CodeSee Maps Go support uses a static binary so there's no setup step required. - - name: Configure Node.js 14 - uses: actions/setup-node@v2 - if: ${{ fromJSON(steps.detect-languages.outputs.languages).javascript }} - with: - node-version: '14' + #- name: Configure Node.js 14 + # uses: actions/setup-node@v2 + # if: ${{ fromJSON(steps.detect-languages.outputs.languages).javascript }} + # with: + # node-version: '14' - - name: Configure Python 3.x - uses: actions/setup-python@v2 - if: ${{ fromJSON(steps.detect-languages.outputs.languages).python }} - with: - python-version: '3.10' - architecture: 'x64' + #- name: Configure Python 3.x + # uses: actions/setup-python@v2 + # if: ${{ fromJSON(steps.detect-languages.outputs.languages).python }} + # with: + # python-version: '3.10' + # architecture: 'x64' - - name: Configure Ruby '3.x' - uses: ruby/setup-ruby@v1 - if: ${{ fromJSON(steps.detect-languages.outputs.languages).ruby }} - with: - ruby-version: '3.0' + #- name: Configure Ruby '3.x' + # uses: ruby/setup-ruby@v1 + # if: ${{ fromJSON(steps.detect-languages.outputs.languages).ruby }} + # with: + # ruby-version: '3.0' # We need the rust toolchain because it uses rustc and cargo to inspect the package - name: Configure Rust 1.x stable diff --git a/.github/workflows/python_build.yml b/.github/workflows/python_build.yml new file mode 100644 index 00000000..1a93f310 --- /dev/null +++ b/.github/workflows/python_build.yml @@ -0,0 +1,69 @@ +name: python_build + +on: + push: + branches: [main] + pull_request: + branches: [main] + +defaults: + run: + working-directory: ./python + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - name: Check Python + run: | + pip install black isort mypy + make check-python + - name: Install minimal stable with clippy and rustfmt + uses: actions-rs/toolchain@v1 + with: + profile: default + toolchain: stable + override: true + - name: Check Rust + run: make check-rust + + test: + name: Python Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + + - uses: Swatinem/rust-cache@v1 + + - uses: actions/setup-python@v3 + with: + python-version: "3.7" + + - name: Build and install scaphandre + run: | + pip install virtualenv + virtualenv venv + source venv/bin/activate + make develop + + - name: Run tests + run: | + source venv/bin/activate + make unit-test + + - name: Build Sphinx documentation + run: | + source venv/bin/activate + make build-documentation \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 8a359b91..549b73a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,9 +59,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base-x" @@ -700,9 +700,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.112" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libnghttp2-sys" @@ -734,10 +734,11 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lock_api" -version = "0.4.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ + "autocfg", "scopeguard", ] @@ -806,31 +807,21 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.13" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", ] [[package]] name = "ntapi" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" dependencies = [ "winapi", ] @@ -920,27 +911,25 @@ checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" [[package]] name = "parking_lot" -version = "0.11.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.8.3" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", - "instant", "libc", "redox_syscall", "smallvec", - "winapi", + "windows-sys 0.45.0", ] [[package]] @@ -1159,9 +1148,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.5" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -1432,9 +1421,9 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "socket2" -version = "0.4.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1529,9 +1518,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.22.4" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb37aa4af23791c584202d286ed9420e023e9d27e49d5a76215623f4bcc2502" +checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56" dependencies = [ "cfg-if", "core-foundation-sys", @@ -1631,9 +1620,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.11.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg", "bytes", @@ -1641,19 +1630,19 @@ dependencies = [ "memchr", "mio", "num_cpus", - "once_cell", "parking_lot", "pin-project-lite", "signal-hook-registry", + "socket2", "tokio-macros", - "winapi", + "windows-sys 0.45.0", ] [[package]] name = "tokio-macros" -version = "1.3.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -1835,6 +1824,12 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.72" @@ -1955,7 +1950,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebbc80318ebf919219a113c41deae34aa90198e4a15e93c810a9ea1aaa4c1a78" dependencies = [ - "windows-sys", + "windows-sys 0.27.0", ] [[package]] @@ -1964,43 +1959,109 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cae116ee11e4bce7c0a0425f2b0c866a91d86d209624b7707a7deea52da786" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.27.0", + "windows_i686_gnu 0.27.0", + "windows_i686_msvc 0.27.0", + "windows_x86_64_gnu 0.27.0", + "windows_x86_64_msvc 0.27.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_msvc" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7d1649bbab232cde71148c6ef7bbe647f214d2154dd66347fada60de40cda7" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_i686_gnu" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4eb20b59b93fc302839f3b0df3e61de7e9606b44cb54cbeb68d71cf137309fa" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_msvc" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40331d8ef3e4dcdc8982eb7de16e1f09b86f5384626a56b3a99c2a51b88ff98e" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_x86_64_gnu" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5937d290e39c3308147d9b877c5fa741c50f4121ea78d2d20c4a138ad365464a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_msvc" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee1b76aec4e2bead4758a181b663c37af0de7ec56fe6837c10215b8d6a1635f" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index a06ace0a..1cf6fc80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,15 +30,14 @@ chrono = "0.4.19" docker-sync = { version = "0.1.2", optional = true } k8s-sync = { version = "0.2.3", optional = true } hyper = { version = "0.14", features = ["full"], optional = true } -tokio = { version = "1", features = ["full"], optional = true} +tokio = { version = "1.26.0", features = ["full"], optional = true} +sysinfo = { version = "0.28.2"} [target.'cfg(target_os="linux")'.dependencies] procfs = { version = "0.12.0" } [target.'cfg(target_os="windows")'.dependencies] windows = { version = "0.27.0", features = ["alloc","Win32_Storage_FileSystem","Win32_Foundation","Win32_Security","Win32_System_IO","Win32_System_Ioctl"]} -sysinfo = { version = "0.22.4"} - [features] default = ["prometheus", "riemann", "warpten", "json", "containers"] @@ -46,4 +45,4 @@ prometheus = ["hyper", "tokio"] riemann = ["riemann_client"] json = ["serde", "serde_json"] containers = ["docker-sync", "k8s-sync"] -warpten = ["warp10"] +warpten = ["warp10"] \ No newline at end of file diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 00000000..5c743caf --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1 @@ +github: hubblo-org diff --git a/README.md b/README.md index 784d3e9e..c27f6bff 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Scaphandre *[skafɑ̃dʁ]* is a metrology agent dedicated to electrical [power]( **Scaphandre** means *heavy* **diving suit** in [:fr:](https://fr.wikipedia.org/wiki/Scaphandre_%C3%A0_casque). It comes from the idea that tech related services often don't track their power consumption and thus don't expose it to their clients. Most of the time the reason is a presumed bad [ROI](https://en.wikipedia.org/wiki/Return_on_investment). Scaphandre makes, for tech providers and tech users, easier and cheaper to go under the surface to bring back the desired power consumption metrics, take better sustainability focused decisions, and then show the metrics to their clients to allow them to do the same. -This project was born from a deep sense of duty from tech workers. Please refer to the [why](https://hubblo-org.github.io/scaphandre-documentation/why.html) section for know more about its goals. +This project was born from a deep sense of duty from tech workers. Please refer to the [why](https://hubblo-org.github.io/scaphandre-documentation/why.html) section to know more about its goals. **Warning**: this is still a very early stage project. Any feedback or contribution will be highly appreciated. Please refer to the [contribution](https://hubblo-org.github.io/scaphandre-documentation/contributing.html) section. diff --git a/docker-compose/dashboards/sample-dashboard.json b/docker-compose/dashboards/sample-dashboard.json index 5b62fba5..4ed31395 100644 --- a/docker-compose/dashboards/sample-dashboard.json +++ b/docker-compose/dashboards/sample-dashboard.json @@ -49,7 +49,7 @@ "repeat": null, "seriesOverrides": [ ], "spaceLength": 10, - "span": 6, + "span": 4, "stack": false, "steppedLine": false, "targets": [ @@ -59,6 +59,20 @@ "intervalFactor": 2, "legendFormat": "{{instance}}", "refId": "A" + }, + { + "expr": "sum(scaph_process_power_consumption_microwatts) / 1000000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "sum of processes power", + "refId": "B" + }, + { + "expr": "sum(scaph_domain_power_microwatts) / 1000000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "sum of rapl domains power", + "refId": "C" } ], "thresholds": [ ], @@ -180,6 +194,101 @@ "show": true } ] + }, + { + "aliasColors": { }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${PROMETHEUS_DS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { }, + "id": 4, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scaph_host_load_avg_one", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "load_avg_1", + "refId": "A" + }, + { + "expr": "scaph_host_load_avg_five", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "load_avg_5", + "refId": "B" + }, + { + "expr": "scaph_host_load_avg_fifteen", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "load_avg_15", + "refId": "C" + } + ], + "thresholds": [ ], + "timeFrom": null, + "timeShift": null, + "title": "Host load average", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ ] + }, + "yaxes": [ + { + "format": "", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] } ], "repeat": null, @@ -203,7 +312,7 @@ "fill": 1, "fillGradient": 0, "gridPos": { }, - "id": 4, + "id": 5, "legend": { "alignAsTable": false, "avg": false, @@ -227,7 +336,7 @@ "repeat": null, "seriesOverrides": [ ], "spaceLength": 10, - "span": 6, + "span": 3, "stack": false, "steppedLine": false, "targets": [ @@ -274,62 +383,87 @@ "show": true } ] - } - ], - "repeat": null, - "repeatIteration": null, - "repeatRowId": null, - "showTitle": true, - "title": "Per CPU Sockets", - "titleSize": "h6", - "type": "row" - }, - { - "collapse": false, - "collapsed": false, - "panels": [ + }, { + "aliasColors": { }, + "bars": false, + "dashLength": 10, + "dashes": false, "datasource": "${PROMETHEUS_DS}", - "fieldConfig": { - "defaults": { - "links": [ ], - "mappings": [ ], - "thresholds": { - "mode": "absolute", - "steps": [ ] - }, - "unit": "none" - } - }, + "fill": 1, + "fillGradient": 0, "gridPos": { }, - "id": 5, - "links": [ ], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - } + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false }, - "pluginVersion": "7", + "lines": true, + "linewidth": 1, + "links": [ ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ ], + "spaceLength": 10, + "span": 3, + "stack": false, + "steppedLine": false, "targets": [ { - "expr": "sort_desc(topk(3, sum by (exe) (scaph_process_power_consumption_microwatts/1000000)))", + "expr": "scaph_domain_power_microwatts / 1000000", "format": "time_series", "intervalFactor": 2, - "legendFormat": "{{exe}}", + "legendFormat": "{{domain_name}}", "refId": "A" } ], - "title": "Top process consumers", - "transparent": false, - "type": "stat" + "thresholds": [ ], + "timeFrom": null, + "timeShift": null, + "title": "scaph_domain_power", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ ] + }, + "yaxes": [ + { + "format": "W", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "W", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] }, { "aliasColors": { }, @@ -340,16 +474,16 @@ "fill": 1, "fillGradient": 0, "gridPos": { }, - "id": 6, + "id": 7, "legend": { - "alignAsTable": true, + "alignAsTable": false, "avg": false, "current": false, "max": false, "min": false, "rightSide": false, "show": true, - "sideWidth": "30%", + "sideWidth": null, "total": false, "values": false }, @@ -364,22 +498,22 @@ "repeat": null, "seriesOverrides": [ ], "spaceLength": 10, - "span": 8, - "stack": true, + "span": 3, + "stack": false, "steppedLine": false, "targets": [ { - "expr": "scaph_process_power_consumption_microwatts{exe=~\".*${process_filter}.*\"}/1000000", + "expr": "scaph_self_cpu_usage_percent", "format": "time_series", "intervalFactor": 2, - "legendFormat": "{{ cmdline }}", + "legendFormat": "{{__name__}}", "refId": "A" } ], "thresholds": [ ], "timeFrom": null, "timeShift": null, - "title": "Filtered process (process_filter) power, by exe", + "title": "scaph_self_cpu", "tooltip": { "shared": true, "sort": 0, @@ -395,7 +529,7 @@ }, "yaxes": [ { - "format": "W", + "format": "%", "label": null, "logBase": 1, "max": null, @@ -403,7 +537,432 @@ "show": true }, { - "format": "W", + "format": "%", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${PROMETHEUS_DS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { }, + "id": 8, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ ], + "spaceLength": 10, + "span": 3, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scaph_self_memory_bytes", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{__name__}}", + "refId": "A" + }, + { + "expr": "scaph_self_memory_virtual_bytes", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{__name__}}", + "refId": "B" + } + ], + "thresholds": [ ], + "timeFrom": null, + "timeShift": null, + "title": "scaph_self_mem", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ ] + }, + "yaxes": [ + { + "format": "Bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "Bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Per CPU Sockets", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": { }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${PROMETHEUS_DS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { }, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": "30%", + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ ], + "spaceLength": 10, + "span": 3, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "scaph_process_power_consumption_microwatts{cmdline=~\".*${process_filter}.*\"}/1000000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ cmdline }}", + "refId": "A" + } + ], + "thresholds": [ ], + "timeFrom": null, + "timeShift": null, + "title": "Filtered process (process_filter) power, by cmdline", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ ] + }, + "yaxes": [ + { + "format": "W", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "W", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${PROMETHEUS_DS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { }, + "id": 10, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": "30%", + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ ], + "spaceLength": 10, + "span": 3, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "scaph_process_cpu_usage_percentage{cmdline=~\".*${process_filter}.*\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ cmdline }}", + "refId": "A" + } + ], + "thresholds": [ ], + "timeFrom": null, + "timeShift": null, + "title": "scaph_process_cpu", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ ] + }, + "yaxes": [ + { + "format": "%", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "%", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${PROMETHEUS_DS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { }, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": "30%", + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ ], + "spaceLength": 10, + "span": 3, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "scaph_process_memory_bytes{cmdline=~\".*${process_filter}.*\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ cmdline }}", + "refId": "A" + } + ], + "thresholds": [ ], + "timeFrom": null, + "timeShift": null, + "title": "scaph_process_mem", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ ] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${PROMETHEUS_DS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { }, + "id": 12, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": "30%", + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ ], + "spaceLength": 10, + "span": 3, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "scaph_process_memory_virtual_bytes{cmdline=~\".*${process_filter}.*\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ cmdline }}", + "refId": "A" + } + ], + "thresholds": [ ], + "timeFrom": null, + "timeShift": null, + "title": "scaph_process_mem_virtual", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ ] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "bytes", "label": null, "logBase": 1, "max": null, diff --git a/docker-compose/docker-compose-dev.yaml b/docker-compose/docker-compose-dev.yaml index 3e70a730..8c77befe 100644 --- a/docker-compose/docker-compose-dev.yaml +++ b/docker-compose/docker-compose-dev.yaml @@ -16,7 +16,9 @@ services: - type: bind source: /var/run/docker.sock target: /var/run/docker.sock - command: ["-v", "prometheus", "--containers"] + command: ["-vvvv", "prometheus", "--containers"] + environment: + RUST_BACKTRACE: "full" networks: - scaphandre-network diff --git a/docker-compose/sample.jsonnet b/docker-compose/sample.jsonnet index 975a235b..cd50f50f 100644 --- a/docker-compose/sample.jsonnet +++ b/docker-compose/sample.jsonnet @@ -32,7 +32,7 @@ dashboard.new( title='Hosts power consumption', datasource='${PROMETHEUS_DS}', format='W', - span=6, + span=4, min=0 ) .addTarget( @@ -41,6 +41,18 @@ dashboard.new( legendFormat='{{instance}}', ) ) + .addTarget( + grafana.prometheus.target( + 'sum(scaph_process_power_consumption_microwatts) / 1000000', + legendFormat='sum of processes power', + ) + ) + .addTarget( + grafana.prometheus.target( + 'sum(scaph_domain_power_microwatts) / 1000000', + legendFormat='sum of rapl domains power', + ) + ) ) .addPanel( grafana.graphPanel.new( @@ -60,6 +72,33 @@ dashboard.new( ) ) ) + .addPanel( + grafana.graphPanel.new( + title='Host load average', + datasource='${PROMETHEUS_DS}', + span=4, + format='', + min=0 + ) + .addTarget( + grafana.prometheus.target( + 'scaph_host_load_avg_one', + legendFormat='load_avg_1', + ) + ) + .addTarget( + grafana.prometheus.target( + 'scaph_host_load_avg_five', + legendFormat='load_avg_5', + ) + ) + .addTarget( + grafana.prometheus.target( + 'scaph_host_load_avg_fifteen', + legendFormat='load_avg_15', + ) + ) + ) ) .addRow( row.new( @@ -70,7 +109,7 @@ dashboard.new( title='Socket power consumption', datasource='${PROMETHEUS_DS}', format='W', - span=6, + span=3, min=0 ) .addTarget( @@ -80,29 +119,125 @@ dashboard.new( ) ) ) + .addPanel( + grafana.graphPanel.new( + title='scaph_domain_power', + datasource='${PROMETHEUS_DS}', + format='W', + span=3, + min=0 + ) + .addTarget( + grafana.prometheus.target( + 'scaph_domain_power_microwatts / 1000000', + legendFormat='{{domain_name}}', + ) + ) + ) + .addPanel( + grafana.graphPanel.new( + title='scaph_self_cpu', + datasource='${PROMETHEUS_DS}', + format='%', + span=3, + min=0 + ) + .addTarget( + grafana.prometheus.target( + 'scaph_self_cpu_usage_percent', + legendFormat='{{__name__}}', + ) + ) + ) + .addPanel( + grafana.graphPanel.new( + title='scaph_self_mem', + datasource='${PROMETHEUS_DS}', + format='Bytes', + span=3, + min=0 + ) + .addTarget( + grafana.prometheus.target( + 'scaph_self_memory_bytes', + legendFormat='{{__name__}}', + ) + ) + .addTarget( + grafana.prometheus.target( + 'scaph_self_memory_virtual_bytes', + legendFormat='{{__name__}}', + ) + ) + ) ) .addRow( row.new( title='Per process', ) .addPanel( - grafana.statPanel.new( - title='Top process consumers', + grafana.graphPanel.new( + title='Filtered process (process_filter) power, by cmdline', datasource='${PROMETHEUS_DS}', + span=3, + format='W', + legend_rightSide=false, + legend_alignAsTable=true, + legend_sideWidth='30%', + stack=true, + min=0 ) .addTarget( grafana.prometheus.target( - 'sort_desc(topk(3, sum by (exe) (scaph_process_power_consumption_microwatts/1000000)))', - legendFormat='{{exe}}', + 'scaph_process_power_consumption_microwatts{cmdline=~".*${process_filter}.*"}/1000000', + legendFormat='{{ cmdline }}', ) ) ) .addPanel( grafana.graphPanel.new( - title='Filtered process (process_filter) power, by exe', + title='scaph_process_cpu', datasource='${PROMETHEUS_DS}', - span=8, - format='W', + span=3, + format='%', + legend_rightSide=false, + legend_alignAsTable=true, + legend_sideWidth='30%', + stack=true, + min=0 + ) + .addTarget( + grafana.prometheus.target( + 'scaph_process_cpu_usage_percentage{cmdline=~".*${process_filter}.*"}', + legendFormat='{{ cmdline }}', + ) + ) + ) + .addPanel( + grafana.graphPanel.new( + title='scaph_process_mem', + datasource='${PROMETHEUS_DS}', + span=3, + format='bytes', + legend_rightSide=false, + legend_alignAsTable=true, + legend_sideWidth='30%', + stack=true, + min=0 + ) + .addTarget( + grafana.prometheus.target( + 'scaph_process_memory_bytes{cmdline=~".*${process_filter}.*"}', + legendFormat='{{ cmdline }}', + ) + ) + ) + .addPanel( + grafana.graphPanel.new( + title='scaph_process_mem_virtual', + datasource='${PROMETHEUS_DS}', + span=3, + format='bytes', legend_rightSide=false, legend_alignAsTable=true, legend_sideWidth='30%', @@ -111,7 +246,7 @@ dashboard.new( ) .addTarget( grafana.prometheus.target( - 'scaph_process_power_consumption_microwatts{exe=~".*${process_filter}.*"}/1000000', + 'scaph_process_memory_virtual_bytes{cmdline=~".*${process_filter}.*"}', legendFormat='{{ cmdline }}', ) ) diff --git a/docs_src/references/exporter-prometheus.md b/docs_src/references/exporter-prometheus.md index 5ca8e77a..ceca1d59 100644 --- a/docs_src/references/exporter-prometheus.md +++ b/docs_src/references/exporter-prometheus.md @@ -52,11 +52,9 @@ And some more deep metrics that you may want if you need to make more complex ca If you hack scaph or just want to investigate its behavior, you may be interested in some internal metrics: -- `scaph_self_mem_total_program_size`: Total program size, measured in pages +- `scaph_self_memory_bytes`: Scaphandre memory usage, in bytes -- `scaph_self_mem_resident_set_size`: Resident set size, measured in pages - -- `scaph_self_mem_shared_resident_size`: Number of resident shared pages (i.e., backed by a file) +- `scaph_self_memory_virtual_bytes`: Scaphandre virtual memory usage, in bytes - `scaph_self_topo_stats_nb`: Number of CPUStat traces stored for the host diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 00000000..f2914074 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,18 @@ +# venv +venv + +# Byte-compiled / optimized / DLL files +__pycache__/ +/target + +# Unit test / coverage reports +.coverage +.pytest_cache/ + +# mypy +.mypy_cache/ + +# sphinx build directory +docs/build + +*.so diff --git a/python/Cargo.lock b/python/Cargo.lock new file mode 100644 index 00000000..8a5247a0 --- /dev/null +++ b/python/Cargo.lock @@ -0,0 +1,1938 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[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.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base-x" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + +[[package]] +name = "castaway" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[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", + "serde", + "time 0.1.44", + "winapi", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "curl" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi", +] + +[[package]] +name = "curl-sys" +version = "0.4.55+curl-7.83.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi", +] + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + +[[package]] +name = "docker-sync" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c989c4ad66535edd02443e7d7699d3ab530df4523f12f6aeb7888bdc5ab7c32" +dependencies = [ + "http", + "isahc", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "docopt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f" +dependencies = [ + "lazy_static", + "regex", + "serde", + "strsim 0.10.0", +] + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "event-listener" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "indoc" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "isahc" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" +dependencies = [ + "async-channel", + "castaway", + "crossbeam-utils", + "curl", + "curl-sys", + "encoding_rs", + "event-listener", + "futures-lite", + "http", + "log", + "mime", + "once_cell", + "polling", + "serde", + "serde_json", + "slab", + "sluice", + "tracing", + "tracing-futures", + "url", + "waker-fn", +] + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "js-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k8s-openapi" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8de9873b904e74b3533f77493731ee26742418077503683db44e1b3c54aa5c" +dependencies = [ + "base64", + "bytes", + "chrono", + "http", + "percent-encoding", + "serde", + "serde-value", + "serde_json", + "url", +] + +[[package]] +name = "k8s-sync" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3c2f8f5cb2611742f8ceb73f23451690ff0d930149eac45fcb63ca86fbd443" +dependencies = [ + "base64", + "chrono", + "dirs", + "http", + "isahc", + "k8s-openapi", + "openssl", + "serde", + "serde_yaml", + "tempfile", + "url", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libnghttp2-sys" +version = "0.1.7+1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libz-sys" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loggerv" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d8de15ae71e760bce7f05447f85f73624fe0d3b1e4c5a63ba5d4cb0748d374" +dependencies = [ + "ansi_term", + "atty", + "log", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b10983b38c53aebdf33f542c6275b0f58a238129d00c4ae0e6fb59738d783ca" + +[[package]] +name = "openssl" +version = "0.10.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "polling" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +dependencies = [ + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "winapi", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "procfs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0941606b9934e2d98a3677759a971756eb821f75764d0e0d26946d08e74d9104" +dependencies = [ + "bitflags", + "byteorder", + "chrono", + "flate2", + "hex", + "lazy_static", + "libc", +] + +[[package]] +name = "protobuf" +version = "2.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96" + +[[package]] +name = "pyo3" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6302e85060011447471887705bb7838f14aba43fcb06957d823739a496b3dc" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "parking_lot", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b65b546c35d8a3b1b2f0ddbac7c6a569d759f357f2b9df884f5d6b719152c8" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c275a07127c1aca33031a563e384ffdd485aee34ef131116fcd58e3430d1742b" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284fc4485bfbcc9850a6d661d627783f18d19c2ab55880b021671c4ba83e90f7" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53bda0f58f73f5c5429693c96ed57f7abdb38fdfc28ae06da4101a257adb7faf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "riemann_client" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1005d55a9a8cb53f6ab2380792031394fff549b020cde16cecbc0978df9e7242" +dependencies = [ + "docopt", + "libc", + "protobuf", + "rustls", + "rustls-pemfile", + "serde", + "webpki", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "scaphandre" +version = "0.4.1" +dependencies = [ + "chrono", + "clap", + "colored", + "docker-sync", + "hostname", + "hyper", + "k8s-sync", + "log", + "loggerv", + "procfs", + "protobuf", + "regex", + "riemann_client", + "serde", + "serde_json", + "time 0.2.27", + "tokio", + "warp10", +] + +[[package]] +name = "scaphandre-python" +version = "0.1.0" +dependencies = [ + "env_logger", + "pyo3", + "scaphandre", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" + +[[package]] +name = "sluice" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" +dependencies = [ + "async-channel", + "futures-core", + "futures-io", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "standback" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +dependencies = [ + "version_check", +] + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check", + "winapi", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unindent" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "warp10" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140e989c5e92da4e09581133f4de7df32d1ce7de6b7a077652bdfa3f9aef97bf" +dependencies = [ + "isahc", + "percent-encoding", + "serde", + "serde_json", + "time 0.2.27", + "url", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" + +[[package]] +name = "web-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + +[[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-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/python/Cargo.toml b/python/Cargo.toml new file mode 100644 index 00000000..2fb3fd55 --- /dev/null +++ b/python/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "scaphandre-python" +version = "0.1.0" +authors = ["fvaleye@github.com"] +homepage = "https://hubblo-org.github.io/scaphandre-documentation" +license = "Apache-2.0" +description = "Electrical power consumption measurement agent." +readme = "README.md" +edition = "2021" +keywords = ["energy", "sustainability", "measure", "virtual-machine", "energy-monitor", "electricity", "virtual-machines", "energy-consumption", "electricity-consumption", "energy-efficiency", "carbon-footprint"] + +[lib] +name = "scaphandre" +crate-type = ["cdylib"] + +[dependencies] +env_logger = "0" + +[dependencies.pyo3] +version = "0.16" +features = ["extension-module", "abi3", "abi3-py37"] + +[dependencies.scaphandre] +path = "../" +version = "0" \ No newline at end of file diff --git a/python/LICENSE.txt b/python/LICENSE.txt new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/python/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/python/Makefile b/python/Makefile new file mode 100644 index 00000000..3b805cb3 --- /dev/null +++ b/python/Makefile @@ -0,0 +1,78 @@ +.DEFAULT_GOAL := help + +VENV := venv +MATURIN_VERSION := $(shell awk -F '[ ="]+' '$$1 == "requires" { print $$4 }' pyproject.toml) +PACKAGE_VERSION := $(shell cargo pkgid | cut -d\# -f2 | cut -d: -f2) + +.PHONY: setup-venv +setup-venv: ## Setup the virtualenv + $(info --- Setup virtualenv ---) + python -m venv $(VENV) + +.PHONY: setup +setup: ## Setup the requirements + $(info --- Setup dependencies ---) + pip install maturin==$(MATURIN_VERSION) + +.PHONY: build +build: setup ## Build Python binding of scaphandre + $(info --- Build Python binding ---) + maturin build $(MATURIN_EXTRA_ARGS) + +.PHONY: develop +develop: setup ## Install Python binding of scaphandre + $(info --- Develop with Python binding ---) + maturin develop --extras=devel $(MATURIN_EXTRA_ARGS) + +.PHONY: install +install: build ## Install Python binding of scaphandre + $(info --- Uninstall Python binding ---) + pip uninstall -y scaphandre + $(info --- Install Python binding ---) + $(eval TARGET_WHEEL := $(shell ls ../target/wheels/scaphandre-${PACKAGE_VERSION}-*.whl)) + pip install $(TARGET_WHEEL)[devel] + +.PHONY: format +format: ## Format the code + $(info --- Rust format ---) + cargo fmt + $(info --- Python format ---) + black . + isort . + +.PHONY: check-rust +check-rust: ## Run check on Rust + $(info --- Check Rust clippy ---) + cargo clippy + $(info --- Check Rust format ---) + cargo fmt -- --check + +.PHONY: check-python +check-python: ## Run check on Python + $(info Check Python isort) + isort --diff --check-only . + $(info Check Python black) + black --check . + $(info Check Python mypy) + mypy + +.PHONY: unit-test +unit-test: ## Run unit test + $(info --- Run Python unit-test ---) + python -m pytest + +.PHONY: build-documentation +build-documentation: ## Build documentation with Sphinx + $(info --- Run build of the Sphinx documentation ---) + sphinx-build -Wn -b html -d ./docs/build/doctrees ./docs/source ./docs/build/html + +.PHONY: clean +clean: ## Run clean + $(warning --- Clean virtualenv and target directory ---) + cargo clean + rm -rf $(VENV) + find . -type f -name '*.pyc' -delete + +.PHONY: help +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/python/README.md b/python/README.md new file mode 100644 index 00000000..e69de29b diff --git a/python/docs/Makefile b/python/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/python/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/python/docs/make.bat b/python/docs/make.bat new file mode 100644 index 00000000..747ffb7b --- /dev/null +++ b/python/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/python/docs/source/_static/.gitignore b/python/docs/source/_static/.gitignore new file mode 100644 index 00000000..86d0cb27 --- /dev/null +++ b/python/docs/source/_static/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/python/docs/source/api_reference.rst b/python/docs/source/api_reference.rst new file mode 100644 index 00000000..0b9b37a0 --- /dev/null +++ b/python/docs/source/api_reference.rst @@ -0,0 +1,11 @@ +API Reference +==================================== + +Scaphandre +---------- + +.. automodule:: scaphandre.Scaphandre + :members: + +.. automodule:: scaphandre.EnergyRecord + :members: \ No newline at end of file diff --git a/python/docs/source/conf.py b/python/docs/source/conf.py new file mode 100644 index 00000000..c3d528a3 --- /dev/null +++ b/python/docs/source/conf.py @@ -0,0 +1,80 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +import sys + +import toml + +sys.path.insert(0, os.path.abspath("../scaphandre/")) +sys.path.insert(0, os.path.abspath("./_ext")) + + +def get_release_version() -> str: + """ + Get the release version from the Cargo.toml file + + :return: + """ + cargo_content = toml.load("../../Cargo.toml") + return cargo_content["package"]["version"] + + +# -- Project information ----------------------------------------------------- + +project = "scaphandre" +copyright = "2022, hubblo" +author = "hubblo" + +# The full version, including alpha/beta/rc tags +release = get_release_version() + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx_rtd_theme", + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", +] +autodoc_typehints = "description" +nitpicky = True + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +page_source_prefix = "python/docs/source" diff --git a/python/docs/source/index.rst b/python/docs/source/index.rst new file mode 100644 index 00000000..620f3fd9 --- /dev/null +++ b/python/docs/source/index.rst @@ -0,0 +1,19 @@ +.. scaphandre documentation master file, created by + sphinx-quickstart on Sun Jul 3 20:27:34 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to scaphandre's documentation! +====================================== + +.. toctree:: + :maxdepth: 2 + + api_reference + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 00000000..6274c98c --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,60 @@ +[build-system] +requires = ["maturin==0.12.20"] +build-backend = "maturin" + +[project] +name = "scaphandre" +description = "Electrical power consumption measurement agent." +readme = "README.md" +license = {file = "LICENSE.txt"} +requires-python = ">=3.7" +keywords = ["energy", "sustainability", "measure", "virtual-machine", "energy-monitor", "electricity", "virtual-machines", "energy-consumption", "electricity-consumption", "energy-efficiency", "carbon-footprint"] +classifiers = [ + "Development Status :: 3 - Alpha", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3 :: Only" +] +dependencies = [] + +[project.optional-dependencies] +devel = [ + "mypy", + "black", + "isort", + "pytest", + "pytest-mock", + "pytest-cov", + "sphinx", + "sphinx-rtd-theme", + "toml", +] + +[project.urls] +documentation = "https://hubblo-org.github.io/scaphandre-documentation" +repository = "https://github.com/hubblo-org/scaphandre" + +[tool.mypy] +files = "scaphandre/*.py" +exclude = "^tests" +mypy_path = "./stubs" +disallow_any_generics = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +check_untyped_defs = true +disallow_untyped_decorators = true +no_implicit_optional = true +warn_redundant_casts = true +warn_unused_ignores = true +warn_return_any = false +implicit_reexport = false +strict_equality = true + +[tool.isort] +profile = "black" +src_paths = ["scaphandre", "tests"] + +[tool.black] +include = '\.pyi?$' +exclude = "venv" \ No newline at end of file diff --git a/python/scaphandre/__init__.py b/python/scaphandre/__init__.py new file mode 100644 index 00000000..5e36334c --- /dev/null +++ b/python/scaphandre/__init__.py @@ -0,0 +1 @@ +from .sensors import * diff --git a/python/scaphandre/sensors.py b/python/scaphandre/sensors.py new file mode 100644 index 00000000..7bfa4400 --- /dev/null +++ b/python/scaphandre/sensors.py @@ -0,0 +1,59 @@ +from dataclasses import dataclass + +from .scaphandre import RawScaphandre + + +@dataclass +class EnergyRecord: + """ + Energy record measured by Scaphandre + """ + + timestamp: str + value: str + unit: str + + +@dataclass(init=False) +class Scaphandre: + """ + Scaphandre, a metrology agent dedicated to electrical power consumption metrics. + """ + + sensor_name: str + + def __init__( + self, + is_virtual_machine: bool = False, + buffer_per_socket_max_kbytes: int = 8, + buffer_per_domain_max_kbytes: int = 8, + ): + """ + Init Scaphandre + + :param is_virtual_machine: running on a virtual machine for powercap_rapl sensor + :param buffer_per_socket_max_kbytes: max buffer per socket in kbytes for powercap_rapl sensor + :param buffer_per_domain_max_kbytes: max buffer per domain in kbytes for powercap_rapl sensor + """ + self._scaphandre = RawScaphandre( + buffer_per_socket_max_kbytes, + buffer_per_domain_max_kbytes, + is_virtual_machine, + ) + self.name = self._scaphandre.sensor_name + + def is_compatible(self) -> bool: + """ + Check if Scaphandre has a sensor available and valid depending on the hardware context. + + :return: a sensor is available and valid + """ + return self._scaphandre.is_compatible() + + def get_energy_consumption_measures(self) -> EnergyRecord: + """ + Get the energy records from Scaphandre. + + :return: the energy record measured + """ + return self._scaphandre.get_energy_consumption_measures() diff --git a/python/src/lib.rs b/python/src/lib.rs new file mode 100644 index 00000000..795f3499 --- /dev/null +++ b/python/src/lib.rs @@ -0,0 +1,91 @@ +#![deny(warnings)] + +extern crate pyo3; + +use pyo3::create_exception; +use pyo3::exceptions::PyException; +use pyo3::prelude::*; +use scaphandre::sensors; +use scaphandre::sensors::powercap_rapl; +use scaphandre::sensors::units; +use sensors::{powercap_rapl::PowercapRAPLSensor, Sensor}; +use std::error::Error; +use std::time::Duration; + +create_exception!(scaphandre, PyScaphandreError, PyException); + +impl PyScaphandreError { + fn from_error(err: Box) -> pyo3::PyErr { + PyScaphandreError::new_err(err.to_string()) + } +} + +#[pyclass] +struct RawScaphandre { + _scaphandre: powercap_rapl::PowercapRAPLSensor, + #[pyo3(get)] + sensor_name: String, +} + +#[pymethods] +impl RawScaphandre { + #[new] + fn new( + buffer_per_socket_max_kbytes: u16, + buffer_per_domain_max_kbytes: u16, + is_virtual_machine: bool, + ) -> PyResult { + let sensor = PowercapRAPLSensor::new( + buffer_per_socket_max_kbytes, + buffer_per_domain_max_kbytes, + is_virtual_machine, + ); + Ok(RawScaphandre { + _scaphandre: sensor, + sensor_name: "PowercapRAPL".to_string(), + }) + } + + fn is_compatible(&self) -> bool { + matches!(PowercapRAPLSensor::check_module(), Ok(_)) + } + + fn get_energy_consumption_measures(&self) -> PyResult> { + Ok(self + ._scaphandre + .generate_topology() + .map_err(PyScaphandreError::from_error)? + .record_buffer + .iter() + .map(|record| RawEnergyRecord { + _timestamp: record.timestamp, + _value: record.value.clone(), + _unit: record.unit, + }) + .collect()) + } +} + +#[pyclass] +struct RawEnergyRecord { + _timestamp: Duration, + _value: String, + _unit: units::Unit, +} + +#[pyfunction] +fn rust_core_version() -> &'static str { + scaphandre::crate_version() +} + +#[pymodule] +// module name need to match project name +fn scaphandre(py: Python, m: &PyModule) -> PyResult<()> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); + + m.add_function(pyo3::wrap_pyfunction!(rust_core_version, m)?)?; + m.add_class::()?; + m.add_class::()?; + m.add("PyScaphandreError", py.get_type::())?; + Ok(()) +} diff --git a/python/stubs/scaphandre/__init__.pyi b/python/stubs/scaphandre/__init__.pyi new file mode 100644 index 00000000..c35db584 --- /dev/null +++ b/python/stubs/scaphandre/__init__.pyi @@ -0,0 +1,3 @@ +from typing import Any + +RawScaphandre: Any diff --git a/python/stubs/scaphandre/scaphandre.pyi b/python/stubs/scaphandre/scaphandre.pyi new file mode 100644 index 00000000..c35db584 --- /dev/null +++ b/python/stubs/scaphandre/scaphandre.pyi @@ -0,0 +1,3 @@ +from typing import Any + +RawScaphandre: Any diff --git a/python/tests/test_scaphandre.py b/python/tests/test_scaphandre.py new file mode 100644 index 00000000..16fcfc96 --- /dev/null +++ b/python/tests/test_scaphandre.py @@ -0,0 +1,5 @@ +from scaphandre import RawScaphandre, Scaphandre + + +def test_scaphandre_should_init_with_the_good_name(): + assert Scaphandre().name == "PowercapRAPL" diff --git a/src/exporters/json.rs b/src/exporters/json.rs index 3fbeef1f..6da0d452 100644 --- a/src/exporters/json.rs +++ b/src/exporters/json.rs @@ -220,12 +220,11 @@ impl JSONExporter { .iter() .find(|x| { x.name == "scaph_process_power_consumption_microwatts" - && process.pid - == x.attributes.get("pid").unwrap().parse::().unwrap() + && &process.pid.to_string() == x.attributes.get("pid").unwrap() }) .map(|metric| Consumer { exe: PathBuf::from(metric.attributes.get("exe").unwrap()), - pid: process.pid, + pid: process.pid.to_string().parse::().unwrap(), consumption: format!("{}", metric.metric_value).parse::().unwrap(), timestamp: metric.timestamp.as_secs_f64(), container: match parameters.is_present("containers") { diff --git a/src/exporters/mod.rs b/src/exporters/mod.rs index 8241a645..cf6bbe54 100644 --- a/src/exporters/mod.rs +++ b/src/exporters/mod.rs @@ -14,7 +14,7 @@ pub mod utils; #[cfg(feature = "warpten")] pub mod warpten; use crate::sensors::{ - utils::{current_system_time_since_epoch, page_size, IProcess}, + utils::{current_system_time_since_epoch, IProcess}, RecordGenerator, Topology, }; use chrono::Utc; @@ -59,12 +59,11 @@ struct Metric { timestamp: Duration, } -#[derive(Clone)] enum MetricValueType { // IntSigned(i64), // Float(f32), Text(String), - FloatDouble(f64), + //FloatDouble(f64), IntUnsigned(u64), } @@ -74,7 +73,7 @@ impl fmt::Display for MetricValueType { // MetricValueType::IntSigned(value) => write!(f, "{}", value), // MetricValueType::Float(value) => write!(f, "{}", value), MetricValueType::Text(text) => write!(f, "{text}"), - MetricValueType::FloatDouble(value) => write!(f, "{value}"), + //MetricValueType::FloatDouble(value) => write!(f, "{value}"), MetricValueType::IntUnsigned(value) => write!(f, "{value}"), } } @@ -86,7 +85,7 @@ impl fmt::Debug for MetricValueType { // MetricValueType::IntSigned(value) => write!(f, "{}", value), // MetricValueType::Float(value) => write!(f, "{}", value), MetricValueType::Text(text) => write!(f, "{text}"), - MetricValueType::FloatDouble(value) => write!(f, "{value}"), + //MetricValueType::FloatDouble(value) => write!(f, "{value}"), MetricValueType::IntUnsigned(value) => write!(f, "{value}"), } } @@ -219,15 +218,12 @@ impl MetricGenerator { topology, hostname, #[cfg(target_os = "linux")] - qemu, + qemu: _qemu, } } /// Generate all scaphandre internal metrics. fn gen_self_metrics(&mut self) { - #[cfg(target_os = "linux")] - let myself = IProcess::myself().unwrap(); - #[cfg(target_os = "windows")] let myself = IProcess::myself(self.topology.get_proc_tracker()).unwrap(); let default_timestamp = current_system_time_since_epoch(); @@ -244,10 +240,7 @@ impl MetricGenerator { metric_value: MetricValueType::Text(get_scaphandre_version()), }); - if let Some(metric_value) = self - .topology - .get_process_cpu_consumption_percentage(myself.pid) - { + if let Some(metric_value) = self.topology.get_process_cpu_usage_percentage(myself.pid) { self.data.push(Metric { name: String::from("scaph_self_cpu_usage_percent"), metric_type: String::from("gauge"), @@ -257,17 +250,14 @@ impl MetricGenerator { state: String::from("ok"), tags: vec!["scaphandre".to_string()], attributes: HashMap::new(), - description: String::from("CPU % consumed by scaphandre."), - metric_value: MetricValueType::FloatDouble( - metric_value.value.parse::().unwrap(), - ), + description: format!("CPU time consumed by scaphandre, as {}", metric_value.unit), + metric_value: MetricValueType::Text(metric_value.value), }); } - if let Ok(metric_value) = myself.statm() { - let value = metric_value.size * page_size().unwrap() as u64; + if let Some(metric_value) = self.topology.get_process_memory_virtual_bytes(myself.pid) { self.data.push(Metric { - name: String::from("scaph_self_mem_total_program_size"), + name: String::from("scaph_self_memory_virtual_bytes"), metric_type: String::from("gauge"), ttl: 60.0, timestamp: default_timestamp, @@ -275,13 +265,16 @@ impl MetricGenerator { state: String::from("ok"), tags: vec!["scaphandre".to_string()], attributes: HashMap::new(), - description: String::from("Total program size, measured in bytes."), - metric_value: MetricValueType::IntUnsigned(value), + description: format!("Total program size, measured in {}.", metric_value.unit), + metric_value: MetricValueType::IntUnsigned( + metric_value.value.parse::().unwrap(), + ), }); + } - let value = metric_value.resident * page_size().unwrap() as u64; + if let Some(metric_value) = self.topology.get_process_memory_bytes(myself.pid) { self.data.push(Metric { - name: String::from("scaph_self_mem_resident_set_size"), + name: String::from("scaph_self_memory_bytes"), metric_type: String::from("gauge"), ttl: 60.0, hostname: self.hostname.clone(), @@ -290,23 +283,9 @@ impl MetricGenerator { tags: vec!["scaphandre".to_string()], attributes: HashMap::new(), description: String::from("Resident set size, measured in bytes."), - metric_value: MetricValueType::IntUnsigned(value), - }); - - let value = metric_value.shared * page_size().unwrap() as u64; - self.data.push(Metric { - name: String::from("scaph_self_mem_shared_resident_size"), - metric_type: String::from("gauge"), - ttl: 60.0, - timestamp: default_timestamp, - hostname: self.hostname.clone(), - state: String::from("ok"), - tags: vec!["scaphandre".to_string()], - attributes: HashMap::new(), - description: String::from( - "Number of resident shared bytes (i.e., backed by a file).", + metric_value: MetricValueType::IntUnsigned( + metric_value.value.parse::().unwrap(), ), - metric_value: MetricValueType::IntUnsigned(value), }); } @@ -445,6 +424,60 @@ impl MetricGenerator { }); } } + if let Some(metric_value) = self.topology.get_load_avg() { + self.data.push(Metric { + name: String::from("scaph_host_load_avg_one"), + metric_type: String::from("gauge"), + ttl: 60.0, + timestamp: metric_value[0].timestamp, + hostname: self.hostname.clone(), + state: String::from("ok"), + tags: vec!["scaphandre".to_string()], + attributes: HashMap::new(), + description: String::from("Load average on 1 minute."), + metric_value: MetricValueType::Text(metric_value[0].value.clone()), + }); + self.data.push(Metric { + name: String::from("scaph_host_load_avg_five"), + metric_type: String::from("gauge"), + ttl: 60.0, + timestamp: metric_value[1].timestamp, + hostname: self.hostname.clone(), + state: String::from("ok"), + tags: vec!["scaphandre".to_string()], + attributes: HashMap::new(), + description: String::from("Load average on 5 minutes."), + metric_value: MetricValueType::Text(metric_value[1].value.clone()), + }); + self.data.push(Metric { + name: String::from("scaph_host_load_avg_fifteen"), + metric_type: String::from("gauge"), + ttl: 60.0, + timestamp: metric_value[2].timestamp, + hostname: self.hostname.clone(), + state: String::from("ok"), + tags: vec!["scaphandre".to_string()], + attributes: HashMap::new(), + description: String::from("Load average on 15 minutes."), + metric_value: MetricValueType::Text(metric_value[2].value.clone()), + }); + } + let freq = self.topology.get_cpu_frequency(); + self.data.push(Metric { + name: String::from("scaph_host_cpu_frequency"), + metric_type: String::from("gauge"), + ttl: 60.0, + timestamp: freq.timestamp, + hostname: self.hostname.clone(), + state: String::from("ok"), + tags: vec!["scaphandre".to_string()], + attributes: HashMap::new(), + description: format!("Global frequency of all the cpus. In {}", freq.unit), + metric_value: MetricValueType::Text(freq.value), + }); + //for c in self.topology.proc_tracker.components() { + // println!("component: {}", c) + //} } /// Generate socket metrics. @@ -745,20 +778,21 @@ impl MetricGenerator { } } - let metric_name = String::from("scaph_process_power_consumption_microwatts"); - if let Some(power) = self.topology.get_process_power_consumption_microwatts(pid) { - self.data.push(Metric { - name: metric_name, - metric_type: String::from("gauge"), - ttl: 60.0, - timestamp: power.timestamp, - hostname: self.hostname.clone(), - state: String::from("ok"), - tags: vec!["scaphandre".to_string()], - attributes, - description: String::from("Power consumption due to the process, measured on at the topology level, in microwatts"), - metric_value: MetricValueType::Text(power.value), - }); + if let Some(metrics) = self.topology.get_all_per_process(pid) { + for (k, v) in metrics { + self.data.push(Metric { + name: k, + metric_type: String::from("gauge"), + ttl: 60.0, + timestamp: v.1.timestamp, + hostname: self.hostname.clone(), + state: String::from("ok"), + tags: vec!["scaphandre".to_string()], + attributes: attributes.clone(), + description: v.0, + metric_value: MetricValueType::Text(v.1.value), + }) + } } } } diff --git a/src/exporters/prometheus.rs b/src/exporters/prometheus.rs index 1cd2db12..b1839641 100644 --- a/src/exporters/prometheus.rs +++ b/src/exporters/prometheus.rs @@ -230,62 +230,75 @@ async fn show_metrics( trace!("{}", req.uri()); let mut body = String::new(); if req.uri().path() == format!("/{}", &suffix) { - trace!("in metrics !"); let now = current_system_time_since_epoch(); - let mut last_request = context.last_request.lock().unwrap(); - let mut metric_generator = context.metric_generator.lock().unwrap(); - if now - (*last_request) > Duration::from_secs(2) { - { - info!( - "{}: Refresh topology", - Utc::now().format("%Y-%m-%dT%H:%M:%S") - ); - metric_generator - .topology - .proc_tracker - .clean_terminated_process_records_vectors(); - metric_generator.topology.refresh(); - } - } - *last_request = now; + match context.last_request.lock() { + Ok(mut last_request) => { + match context.metric_generator.lock() { + Ok(mut metric_generator) => { + if now - (*last_request) > Duration::from_secs(2) { + { + info!( + "{}: Refresh topology", + Utc::now().format("%Y-%m-%dT%H:%M:%S") + ); + metric_generator + .topology + .proc_tracker + .clean_terminated_process_records_vectors(); + metric_generator.topology.refresh(); + } + } + *last_request = now; - info!("{}: Refresh data", Utc::now().format("%Y-%m-%dT%H:%M:%S")); + info!("{}: Refresh data", Utc::now().format("%Y-%m-%dT%H:%M:%S")); - metric_generator.gen_all_metrics(); + metric_generator.gen_all_metrics(); - let mut metrics_pushed: Vec = vec![]; + let mut metrics_pushed: Vec = vec![]; - // Send all data - for msg in metric_generator.pop_metrics() { - let mut attributes: Option<&HashMap> = None; - if !msg.attributes.is_empty() { - attributes = Some(&msg.attributes); - } + // Send all data + for msg in metric_generator.pop_metrics() { + let mut attributes: Option<&HashMap> = None; + if !msg.attributes.is_empty() { + attributes = Some(&msg.attributes); + } - let value = match msg.metric_value { - // MetricValueType::IntSigned(value) => event.set_metric_sint64(value), - // MetricValueType::Float(value) => event.set_metric_f(value), - MetricValueType::FloatDouble(value) => value.to_string(), - MetricValueType::IntUnsigned(value) => value.to_string(), - MetricValueType::Text(ref value) => value.to_string(), - }; + let value = match msg.metric_value { + // MetricValueType::IntSigned(value) => event.set_metric_sint64(value), + // MetricValueType::Float(value) => event.set_metric_f(value), + //MetricValueType::FloatDouble(value) => value.to_string(), + MetricValueType::IntUnsigned(value) => value.to_string(), + MetricValueType::Text(ref value) => value.to_string(), + }; - let mut should_i_add_help = true; + let mut should_i_add_help = true; - if metrics_pushed.contains(&msg.name) { - should_i_add_help = false; - } else { - metrics_pushed.insert(0, msg.name.clone()); - } + if metrics_pushed.contains(&msg.name) { + should_i_add_help = false; + } else { + metrics_pushed.insert(0, msg.name.clone()); + } - body = push_metric( - body, - msg.description.clone(), - msg.metric_type.clone(), - msg.name.clone(), - format_metric(&msg.name, &value, attributes), - should_i_add_help, - ); + body = push_metric( + body, + msg.description.clone(), + msg.metric_type.clone(), + msg.name.clone(), + format_metric(&msg.name, &value, attributes), + should_i_add_help, + ); + } + } + Err(e) => { + error!("Error while locking metric_generator: {e:?}"); + error!("Error while locking metric_generator: {}", e.to_string()); + } + } + } + Err(e) => { + error!("Error in show_metrics : {e:?}"); + error!("Error details : {}", e.to_string()); + } } } else { let _ = write!(body, "Scaphandre's prometheus exporter here. Metrics available on /{suffix}"); diff --git a/src/exporters/qemu.rs b/src/exporters/qemu.rs index f8e5e8a0..e442c685 100644 --- a/src/exporters/qemu.rs +++ b/src/exporters/qemu.rs @@ -69,9 +69,10 @@ impl QemuExporter { let last = qp.first().unwrap(); let previous = qp.get(1).unwrap(); let vm_name = QemuExporter::get_vm_name_from_cmdline( - &last.process.original.cmdline().unwrap(), + &last.process.cmdline(proc_tracker).unwrap(), ); - let time_pdiff = last.total_time_jiffies() - previous.total_time_jiffies(); + let time_pdiff = last.process.total_time_jiffies(proc_tracker) + - previous.process.total_time_jiffies(proc_tracker); if let Some(time_tdiff) = &topo_stat_diff { let first_domain_path = format!("{path}/{vm_name}/intel-rapl:0:0"); if fs::read_dir(&first_domain_path).is_err() { @@ -137,15 +138,18 @@ impl QemuExporter { for vecp in processes.iter() { if !vecp.is_empty() { if let Some(pr) = vecp.get(0) { - if let Ok(cmdline) = pr.process.original.cmdline() { - if let Some(res) = cmdline.iter().find(|x| x.contains("qemu-system")) { - debug!("Found a process with {}", res); - let mut tmp: Vec = vec![]; - for p in vecp.iter() { - tmp.push(p.clone()); - } - qemu_processes.push(tmp); + if let Some(res) = pr + .process + .cmdline + .iter() + .find(|x| x.contains("qemu-system")) + { + debug!("Found a process with {}", res); + let mut tmp: Vec = vec![]; + for p in vecp.iter() { + tmp.push(p.clone()); } + qemu_processes.push(tmp); } } } diff --git a/src/exporters/riemann.rs b/src/exporters/riemann.rs index 5533ef14..c624f074 100644 --- a/src/exporters/riemann.rs +++ b/src/exporters/riemann.rs @@ -82,7 +82,7 @@ impl RiemannClient { match metric.metric_value { // MetricValueType::IntSigned(value) => event.set_metric_sint64(value), // MetricValueType::Float(value) => event.set_metric_f(value), - MetricValueType::FloatDouble(value) => event.set_metric_d(value), + //MetricValueType::FloatDouble(value) => event.set_metric_d(value), MetricValueType::IntUnsigned(value) => event.set_metric_sint64( i64::try_from(value).expect("Metric cannot be converted to signed integer."), ), diff --git a/src/exporters/stdout.rs b/src/exporters/stdout.rs index c466d6a3..f60fcd97 100644 --- a/src/exporters/stdout.rs +++ b/src/exporters/stdout.rs @@ -167,9 +167,10 @@ impl StdoutExporter { let metrics = metric_generator.pop_metrics(); let mut metrics_iter = metrics.iter(); + let none_value = MetricValueType::Text("0".to_string()); let host_power = match metrics_iter.find(|x| x.name == "scaph_host_power_microwatts") { - Some(m) => m.metric_value.clone(), - None => MetricValueType::Text("0".to_string()), + Some(m) => &m.metric_value, + None => &none_value, }; let domain_names = metric_generator.topology.domains_names.as_ref(); @@ -260,7 +261,7 @@ impl StdoutExporter { if let Some(process) = metrics.iter().find(|x| { if x.name == "scaph_process_power_consumption_microwatts" { let pid = x.attributes.get("pid").unwrap(); - pid.parse::().unwrap() == c.0.pid + pid == &c.0.pid.to_string() } else { false } diff --git a/src/exporters/warpten.rs b/src/exporters/warpten.rs index 4ebf4ce2..717c652b 100644 --- a/src/exporters/warpten.rs +++ b/src/exporters/warpten.rs @@ -1,5 +1,5 @@ use crate::exporters::*; -use crate::sensors::{RecordGenerator, Sensor, Topology}; +use crate::sensors::{utils::IProcess, RecordGenerator, Sensor, Topology}; use clap::Arg; use std::time::Duration; use std::{env, thread}; @@ -153,10 +153,9 @@ impl Warp10Exporter { warp10::Value::Double(scaphandre_version.parse::().unwrap()), )]; - if let Some(metric_value) = self - .topology - .get_process_cpu_consumption_percentage(procfs::process::Process::myself().unwrap().pid) - { + if let Some(metric_value) = self.topology.get_process_cpu_usage_percentage( + IProcess::myself(&self.topology.proc_tracker).unwrap().pid, + ) { data.push(warp10::Data::new( time::OffsetDateTime::now_utc(), None, @@ -166,10 +165,9 @@ impl Warp10Exporter { )); } - if let Some(metric_value) = self - .topology - .get_process_cpu_consumption_percentage(procfs::process::Process::myself().unwrap().pid) - { + if let Some(metric_value) = self.topology.get_process_cpu_usage_percentage( + IProcess::myself(&self.topology.proc_tracker).unwrap().pid, + ) { data.push(warp10::Data::new( time::OffsetDateTime::now_utc(), None, diff --git a/src/lib.rs b/src/lib.rs index 7eaea039..b9631b19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,6 +187,11 @@ pub fn scaphandre_header(exporter_name: &str) { println!("Sending ⚡ metrics"); } +/// Returns rust crate version, can be use used in language bindings to expose Rust core version +pub fn crate_version() -> &'static str { + env!("CARGO_PKG_VERSION") +} + // Copyright 2020 The scaphandre authors. // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/sensors/mod.rs b/src/sensors/mod.rs index 00e370e7..7f578d7b 100644 --- a/src/sensors/mod.rs +++ b/src/sensors/mod.rs @@ -10,14 +10,14 @@ pub mod powercap_rapl; pub mod units; pub mod utils; #[cfg(target_os = "linux")] -use procfs::{process, CpuInfo, CpuTime, KernelStats}; +use procfs::{CpuInfo, CpuTime, KernelStats}; use std::collections::HashMap; use std::error::Error; use std::fmt; use std::mem::size_of_val; use std::time::Duration; -#[cfg(not(target_os = "linux"))] -use sysinfo::{ProcessorExt, System, SystemExt}; +#[allow(unused_imports)] +use sysinfo::{CpuExt, Pid, System, SystemExt}; use utils::{current_system_time_since_epoch, IProcess, ProcessTracker}; // !!!!!!!!!!!!!!!!! Sensor !!!!!!!!!!!!!!!!!!!!!!! @@ -58,10 +58,8 @@ pub struct Topology { pub buffer_max_kbytes: u16, /// Sorted list of all domains names pub domains_names: Option>, - /// - #[cfg(target_os = "windows")] - #[allow(dead_code)] - sensor_data: HashMap, + /// Sensor-specific data needed in the topology + _sensor_data: HashMap, } impl RecordGenerator for Topology { @@ -141,19 +139,14 @@ impl RecordGenerator for Topology { impl Default for Topology { fn default() -> Self { - #[cfg(target_os = "windows")] { Self::new(HashMap::new()) } - - #[cfg(target_os = "linux")] - Self::new() } } impl Topology { /// Instanciates Topology and returns the instance - #[cfg(target_os = "windows")] pub fn new(sensor_data: HashMap) -> Topology { Topology { sockets: vec![], @@ -162,19 +155,7 @@ impl Topology { record_buffer: vec![], buffer_max_kbytes: 1, domains_names: None, - sensor_data, - } - } - /// Instanciates Topology and returns the instance - #[cfg(target_os = "linux")] - pub fn new() -> Topology { - Topology { - sockets: vec![], - proc_tracker: ProcessTracker::new(5), - stat_buffer: vec![], - record_buffer: vec![], - buffer_max_kbytes: 1, - domains_names: None, + _sensor_data: sensor_data, } } @@ -188,37 +169,30 @@ impl Topology { /// if let Some(cores) = Topology::generate_cpu_cores() { /// println!("There are {} cores on this host.", cores.len()); /// for c in &cores { - /// println!("Here is CPU Core number {}", c.attributes.get("processor").unwrap()); + /// println!("CPU info {:?}", c.attributes); /// } /// } /// ``` pub fn generate_cpu_cores() -> Option> { let mut cores = vec![]; + let sysinfo_system = System::new_all(); + let sysinfo_cores = sysinfo_system.cpus(); #[cfg(target_os = "linux")] - { - let cpuinfo = CpuInfo::new().unwrap(); - for id in 0..(cpuinfo.num_cores() - 1) { - let mut info = HashMap::new(); - for (k, v) in cpuinfo.get_info(id).unwrap().iter() { + let cpuinfo = CpuInfo::new().unwrap(); + for (id, c) in (0_u16..).zip(sysinfo_cores.iter()) { + let mut info = HashMap::::new(); + #[cfg(target_os = "linux")] + { + for (k, v) in cpuinfo.get_info(id as usize).unwrap().iter() { info.insert(String::from(*k), String::from(*v)); } - cores.push(CPUCore::new(id as u16, info)); - } - } - #[cfg(target_os = "windows")] - { - warn!("generate_cpu_info is not implemented yet on this OS."); - let sysinfo_system = System::new_all(); - let sysinfo_cores = sysinfo_system.processors(); - for (id, c) in (0_u16..).zip(sysinfo_cores.iter()) { - let mut info = HashMap::new(); - info.insert(String::from("frequency"), c.frequency().to_string()); - info.insert(String::from("name"), c.name().to_string()); - info.insert(String::from("vendor_id"), c.vendor_id().to_string()); - info.insert(String::from("brand"), c.brand().to_string()); - cores.push(CPUCore::new(id, info)); } + info.insert(String::from("frequency"), c.frequency().to_string()); + info.insert(String::from("name"), c.name().to_string()); + info.insert(String::from("vendor_id"), c.vendor_id().to_string()); + info.insert(String::from("brand"), c.brand().to_string()); + cores.push(CPUCore::new(id, info)); } Some(cores) } @@ -345,6 +319,7 @@ impl Topology { // //} } + self.proc_tracker.refresh(); self.refresh_procs(); self.refresh_record(); self.refresh_stats(); @@ -353,36 +328,14 @@ impl Topology { /// Gets currently running processes (as procfs::Process instances) and stores /// them in self.proc_tracker fn refresh_procs(&mut self) { - #[cfg(target_os = "linux")] - { - //current_procs is the up to date list of processus running on the host - if let Ok(procs) = process::all_processes() { - info!("Before refresh procs init."); - procs - .iter() - .map(IProcess::from_linux_process) - .for_each(|p| { - let pid = p.pid; - let res = self.proc_tracker.add_process_record(p); - match res { - Ok(_) => {} - Err(msg) => { - panic!("Failed to track process with pid {} !\nGot: {}", pid, msg) - } - } - }); - } - } - #[cfg(target_os = "windows")] { let pt = &mut self.proc_tracker; pt.sysinfo.refresh_processes(); - pt.sysinfo.refresh_cpu(); let current_procs = pt .sysinfo .processes() .values() - .map(IProcess::from_windows_process) + .map(IProcess::new) .collect::>(); for p in current_procs { match pt.add_process_record(p) { @@ -593,52 +546,38 @@ impl Topology { None } + pub fn get_cpu_frequency(&self) -> Record { + Record::new( + current_system_time_since_epoch(), + self.proc_tracker.get_cpu_frequency().to_string(), + units::Unit::MegaHertz, + ) + } + + pub fn get_load_avg(&self) -> Option> { + let load = self.get_proc_tracker().sysinfo.load_average(); + let timestamp = current_system_time_since_epoch(); + Some(vec![ + Record::new(timestamp, load.one.to_string(), units::Unit::Numeric), + Record::new(timestamp, load.five.to_string(), units::Unit::Numeric), + Record::new(timestamp, load.five.to_string(), units::Unit::Numeric), + ]) + } + /// Returns the power consumed between last and previous measurement for a given process ID, in microwatts - pub fn get_process_power_consumption_microwatts(&self, pid: i32) -> Option { - let tracker = self.get_proc_tracker(); - if let Some(recs) = tracker.find_records(pid) { - if recs.len() > 1 { - #[cfg(target_os = "linux")] - { - let last = recs.first().unwrap(); - let previous = recs.get(1).unwrap(); - if let Some(topo_stats_diff) = self.get_stats_diff() { - //trace!("Topology stats measured diff: {:?}", topo_stats_diff); - let process_total_time = - last.total_time_jiffies() - previous.total_time_jiffies(); - let topo_total_time = topo_stats_diff.total_time_jiffies(); - let usage_percent = process_total_time as f64 / topo_total_time as f64; - let topo_conso = self.get_records_diff_power_microwatts(); - if let Some(val) = &topo_conso { - //trace!("topo conso: {}", val); - let val_f64 = val.value.parse::().unwrap(); - //trace!("val f64: {}", val_f64); - let result = (val_f64 * usage_percent) as u64; - //trace!("result: {}", result); - return Some(Record::new( - last.timestamp, - result.to_string(), - units::Unit::MicroWatt, - )); - } - } - } - #[cfg(target_os = "windows")] - { - let last = recs.first().unwrap(); - let process_cpu_percentage = - tracker.get_cpu_usage_percentage(pid as usize, tracker.nb_cores); - let topo_conso = self.get_records_diff_power_microwatts(); - if let Some(conso) = &topo_conso { - let conso_f64 = conso.value.parse::().unwrap(); - let result = (conso_f64 * process_cpu_percentage as f64) / 100.0_f64; - return Some(Record::new( - last.timestamp, - result.to_string(), - units::Unit::MicroWatt, - )); - } - } + pub fn get_process_power_consumption_microwatts(&self, pid: Pid) -> Option { + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + let process_cpu_percentage = self.get_process_cpu_usage_percentage(pid).unwrap(); + let topo_conso = self.get_records_diff_power_microwatts(); + if let Some(conso) = &topo_conso { + let conso_f64 = conso.value.parse::().unwrap(); + let result = + (conso_f64 * process_cpu_percentage.value.parse::().unwrap()) / 100.0_f64; + return Some(Record::new( + record.timestamp, + result.to_string(), + units::Unit::MicroWatt, + )); } } else { trace!("Couldn't find records for PID: {}", pid); @@ -646,27 +585,178 @@ impl Topology { None } - pub fn get_process_cpu_consumption_percentage(&self, pid: i32) -> Option { - let tracker = self.get_proc_tracker(); - if let Some(recs) = tracker.find_records(pid) { - if recs.len() > 1 { - let last = recs.first().unwrap(); - let previous = recs.get(1).unwrap(); - if let Some(topo_stats_diff) = self.get_stats_diff() { - let process_total_time = - last.total_time_jiffies() - previous.total_time_jiffies(); + pub fn get_all_per_process(&self, pid: Pid) -> Option> { + let mut res = HashMap::new(); + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + let process_cpu_percentage = + record.process.cpu_usage_percentage / self.proc_tracker.nb_cores as f32; + res.insert( + String::from("scaph_process_cpu_usage_percentage"), + (String::from("CPU time consumed by the process, as a percentage of the capacity of all the CPU Cores"), + Record::new( + record.timestamp, + process_cpu_percentage.to_string(), + units::Unit::Percentage, + ) + ) + ); + res.insert( + String::from("scaph_process_memory_virtual_bytes"), + ( + String::from("Virtual RAM usage by the process, in bytes"), + Record::new( + record.timestamp, + record.process.virtual_memory.to_string(), + units::Unit::Percentage, + ), + ), + ); + res.insert( + String::from("scaph_process_memory_bytes"), + ( + String::from("Physical RAM usage by the process, in bytes"), + Record::new( + record.timestamp, + record.process.memory.to_string(), + units::Unit::Bytes, + ), + ), + ); + res.insert( + String::from("scaph_process_disk_write_bytes"), + ( + String::from("Data written on disk by the process, in bytes"), + Record::new( + record.timestamp, + record.process.disk_written.to_string(), + units::Unit::Bytes, + ), + ), + ); + res.insert( + String::from("scaph_process_disk_read_bytes"), + ( + String::from("Data read on disk by the process, in bytes"), + Record::new( + record.timestamp, + record.process.disk_read.to_string(), + units::Unit::Bytes, + ), + ), + ); + res.insert( + String::from("scaph_process_disk_total_write_bytes"), + ( + String::from("Total data written on disk by the process, in bytes"), + Record::new( + record.timestamp, + record.process.total_disk_written.to_string(), + units::Unit::Bytes, + ), + ), + ); + res.insert( + String::from("scaph_process_disk_total_read_bytes"), + ( + String::from("Total data read on disk by the process, in bytes"), + Record::new( + record.timestamp, + record.process.total_disk_read.to_string(), + units::Unit::Bytes, + ), + ), + ); + let topo_conso = self.get_records_diff_power_microwatts(); + if let Some(conso) = &topo_conso { + let conso_f64 = conso.value.parse::().unwrap(); + let result = (conso_f64 * process_cpu_percentage as f64) / 100.0_f64; + res.insert( + String::from("scaph_process_power_consumption_microwatts"), + ( + String::from("Total data read on disk by the process, in bytes"), + Record::new(record.timestamp, result.to_string(), units::Unit::MicroWatt), + ), + ); + } + } + Some(res) + } + + // Per process metrics, from ProcessRecord during last refresh, returned in Record structs + + pub fn get_process_cpu_usage_percentage(&self, pid: Pid) -> Option { + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + return Some(Record::new( + record.timestamp, + (record.process.cpu_usage_percentage / self.proc_tracker.nb_cores as f32) + .to_string(), + units::Unit::Percentage, + )); + } + None + } + + pub fn get_process_memory_virtual_bytes(&self, pid: Pid) -> Option { + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + return Some(Record::new( + record.timestamp, + record.process.virtual_memory.to_string(), + units::Unit::Bytes, + )); + } + None + } - let topo_total_time = topo_stats_diff.total_time_jiffies(); + pub fn get_process_memory_bytes(&self, pid: Pid) -> Option { + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + return Some(Record::new( + record.timestamp, + record.process.memory.to_string(), + units::Unit::Bytes, + )); + } + None + } - let usage = process_total_time as f64 / topo_total_time as f64; + pub fn get_process_disk_written_bytes(&self, pid: Pid) -> Option { + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + return Some(Record::new( + record.timestamp, + record.process.disk_written.to_string(), + units::Unit::Bytes, + )); + } + None + } - return Some(Record::new( - current_system_time_since_epoch(), - usage.to_string(), - units::Unit::Percentage, - )); - } - } + pub fn get_process_disk_read_bytes(&self, pid: Pid) -> Option { + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + return Some(Record::new( + record.timestamp, + record.process.disk_read.to_string(), + units::Unit::Bytes, + )); + } + None + } + pub fn get_process_disk_total_read_bytes(&self, pid: Pid) -> Option { + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + return Some(Record::new( + record.timestamp, + record.process.total_disk_read.to_string(), + units::Unit::Bytes, + )); + } + None + } + + pub fn get_process_disk_total_write_bytes(&self, pid: Pid) -> Option { + if let Some(record) = self.get_proc_tracker().get_process_last_record(pid) { + return Some(Record::new( + record.timestamp, + record.process.total_disk_written.to_string(), + units::Unit::Bytes, + )); } None } @@ -1255,11 +1345,11 @@ mod tests { cores[0].attributes.len() ); for c in &cores { - println!("{:?}", c.attributes.get("processor")); + println!("{:?}", c.attributes); } assert_eq!(!cores.is_empty(), true); for c in &cores { - assert_eq!(c.attributes.len() > 5, true); + assert_eq!(c.attributes.len() > 3, true); } } diff --git a/src/sensors/powercap_rapl.rs b/src/sensors/powercap_rapl.rs index 4cab2836..d1a1ed44 100644 --- a/src/sensors/powercap_rapl.rs +++ b/src/sensors/powercap_rapl.rs @@ -108,7 +108,7 @@ impl Sensor for PowercapRAPLSensor { if modules_state.is_err() && !self.virtual_machine { warn!("Couldn't find intel_rapl modules."); } - let mut topo = Topology::new(); + let mut topo = Topology::new(HashMap::new()); let re_socket = Regex::new(r"^.*/intel-rapl:\d+$").unwrap(); let re_domain = Regex::new(r"^.*/intel-rapl:\d+:\d+$").unwrap(); let mut re_domain_matched = false; diff --git a/src/sensors/units.rs b/src/sensors/units.rs index e0274f35..a2eff801 100644 --- a/src/sensors/units.rs +++ b/src/sensors/units.rs @@ -3,6 +3,7 @@ use std::{cmp::Ordering, fmt}; // !!!!!!!!!!!!!!!!! Unit !!!!!!!!!!!!!!!!!!!!!!! #[derive(Debug)] pub enum Unit { + Numeric, Joule, MilliJoule, MicroJoule, @@ -12,6 +13,11 @@ pub enum Unit { MilliWatt, MicroWatt, Percentage, + Bytes, + KiloBytes, + MegaBytes, + GigaBytes, + MegaHertz, } impl Unit { @@ -35,7 +41,7 @@ impl Unit { } else if let (Some(pos_source), Some(pos_dest)) = (pos_source_power, pos_dest_power) { Ok(measure * Unit::get_mult(pos_source, pos_dest)) } else { - panic!("Impossible conversion asked from energy value to power value (without time dimension)."); + panic!("Unimplemented or impossible conversion (if asked from energy value to power value without time dimension)."); } } @@ -63,6 +69,12 @@ impl fmt::Display for Unit { Unit::KiloWatt => write!(f, "KiloWatts"), Unit::MegaWatt => write!(f, "MegaWatts"), Unit::Percentage => write!(f, "Percentage"), + Unit::Bytes => write!(f, "Bytes"), + Unit::KiloBytes => write!(f, "KiloBytes"), + Unit::MegaBytes => write!(f, "MegaBytes"), + Unit::GigaBytes => write!(f, "GigaBytes"), + Unit::MegaHertz => write!(f, "MegaHertz"), + Unit::Numeric => write!(f, ""), } } } diff --git a/src/sensors/utils.rs b/src/sensors/utils.rs index 6748c3ba..9431427b 100644 --- a/src/sensors/utils.rs +++ b/src/sensors/utils.rs @@ -1,14 +1,16 @@ +use ordered_float::*; #[cfg(target_os = "linux")] -use procfs::{self, process::Process}; +use procfs; use regex::Regex; -#[cfg(feature = "containers")] +#[allow(unused_imports)] use std::collections::HashMap; -#[cfg(target_os = "windows")] -use sysinfo::{get_current_pid, Process, ProcessExt, ProcessorExt, System, SystemExt}; -//use std::error::Error; -use ordered_float::*; +use std::io::{Error, ErrorKind}; use std::path::PathBuf; use std::time::{Duration, SystemTime}; +use sysinfo::{ + get_current_pid, CpuExt, CpuRefreshKind, Pid, Process, ProcessExt, ProcessStatus, System, + SystemExt, +}; #[cfg(all(target_os = "linux", feature = "containers"))] use {docker_sync::container::Container, k8s_sync::Pod}; @@ -33,325 +35,151 @@ pub struct IStat { pub tty_nr: i32, pub tpgid: i32, pub flags: u32, - //pub minflt: u64, - //pub cminflt: u64, - //pub majflt: u64, - //pub cmajflt: u64, pub utime: u64, pub stime: u64, pub cutime: i64, pub cstime: i64, - //pub priority: i64, pub nice: i64, pub num_threads: i64, pub itrealvalue: i64, pub starttime: u64, pub vsize: u64, - //pub rss: i64, - //pub rsslim: u64, - //pub startcode: u64, - //pub endcode: u64, - //pub startstack: u64, - //pub kstkesp: u64, - //pub kstkeip: u64, pub signal: u64, pub blocked: u64, - //pub sigignore: u64, - //pub sigcatch: u64, - //pub wchan: u64, - //pub nswap: u64, - //pub cnswap: u64, pub exit_signal: Option, pub processor: Option, - //pub rt_priority: Option, - //pub policy: Option, pub delayacct_blkio_ticks: Option, pub guest_time: Option, pub cguest_time: Option, pub start_data: Option, pub end_data: Option, - //pub start_brk: Option, - //pub arg_start: Option, - //pub arg_end: Option, - //pub env_start: Option, - //pub env_end: Option, pub exit_code: Option, } -impl IStat { - #[cfg(target_os = "linux")] - fn from_procfs_stat(stat: &procfs::process::Stat) -> IStat { - IStat { - blocked: stat.blocked, - cguest_time: stat.cguest_time, - comm: stat.comm.clone(), - cstime: stat.cstime, - cutime: stat.cutime, - delayacct_blkio_ticks: stat.delayacct_blkio_ticks, - end_data: stat.end_data, - exit_code: stat.exit_code, - exit_signal: stat.exit_signal, - flags: stat.flags, - guest_time: stat.guest_time, - itrealvalue: stat.itrealvalue, - nice: stat.nice, - num_threads: stat.num_threads, - pgrp: stat.pgrp, - pid: stat.pid, - ppid: stat.ppid, - processor: stat.processor, - session: stat.session, - signal: stat.signal, - start_data: stat.start_data, - starttime: stat.starttime, - state: stat.state, - stime: stat.stime, - tpgid: stat.tpgid, - tty_nr: stat.tty_nr, - utime: stat.utime, - vsize: stat.vsize, - } - } - - #[cfg(target_os = "windows")] - fn from_windows_process_stat(_process: &Process) -> IStat { - IStat { - blocked: 0, - cguest_time: Some(0), - comm: String::from("Not implemented yet !"), - cstime: 0, - cutime: 0, - delayacct_blkio_ticks: Some(0), - end_data: Some(0), - exit_code: Some(0), - exit_signal: Some(0), - flags: 0, - guest_time: Some(0), - itrealvalue: 0, - nice: 0, - num_threads: 0, - pgrp: 0, - pid: 0, - ppid: 0, - processor: Some(0), - session: 0, - signal: 0, - start_data: Some(0), - starttime: 0, - state: 'X', - stime: 0, - tpgid: 0, - tty_nr: 0, - utime: 0, - vsize: 0, - } - } -} - #[derive(Clone)] pub struct IStatus { pub name: String, pub umask: Option, pub state: String, - //pub tgid: i32, - //pub ngid: Option, pub pid: i32, pub ppid: i32, - //pub tracerpid: i32, - //pub ruid: u32, - //pub euid: u32, - //pub suid: u32, - //pub fuid: u32, - //pub rgid: u32, - //pub egid: u32, - //pub sgid: u32, - //pub fgid: u32, - //pub fdsize: u32, - //pub groups: Vec, - //pub nstgid: Option>, - //pub nspid: Option>, - //pub nspgid: Option>, - //pub nssid: Option>, - //pub vmpeak: Option, - //pub vmsize: Option, - //pub vmlck: Option, - //pub vmpin: Option, - //pub vmhwm: Option, - //pub vmrss: Option, - //pub rssanon: Option, - //pub rssfile: Option, - //pub rssshmem: Option, - //pub vmdata: Option, - //pub vmstk: Option, - //pub vmexe: Option, - //pub vmlib: Option, - //pub vmpte: Option, - //pub vmswap: Option, - //pub hugetlbpages: Option, - //pub threads: u64, - //pub sigq: (u64, u64), - //pub sigpnd: u64, - //pub shdpnd: u64, - //pub sigblk: u64, - //pub sigign: u64, - //pub sigcgt: u64, - //pub capinh: u64, - //pub capprm: u64, - //pub capeff: u64, - //pub capbnd: Option, - //pub capamb: Option, - //pub nonewprivs: Option, - //pub seccomp: Option, - //pub speculation_store_bypass: Option, - //pub cpus_allowed: Option>, - //pub cpus_allowed_list: Option>, - //pub mems_allowed: Option>, - //pub mems_allowed_list: Option>, - //pub voluntary_ctxt_switches: Option, - //pub nonvoluntary_ctxt_switches: Option, - //pub core_dumping: Option, - //pub thp_enabled: Option, } #[derive(Debug, Clone)] pub struct IProcess { - pub pid: i32, + pub pid: Pid, pub owner: u32, pub comm: String, pub cmdline: Vec, - pub stat: Option, - //pub root: Option, + //CPU (all of them) time usage, as a percentage + pub cpu_usage_percentage: f32, + // Virtual memory used by the process (at the time the struct is created), in bytes + pub virtual_memory: u64, + // Memory consumed by the process (at the time the struct is created), in bytes + pub memory: u64, + // Disk bytes read by the process + pub disk_read: u64, + // Disk bytes written by the process + pub disk_written: u64, + // Total disk bytes read by the process + pub total_disk_read: u64, + // Total disk bytes written by the process + pub total_disk_written: u64, + #[cfg(target_os = "linux")] + pub stime: u64, #[cfg(target_os = "linux")] - pub original: Process, + pub utime: u64, } impl IProcess { - #[cfg(target_os = "linux")] - pub fn from_linux_process(process: &Process) -> IProcess { - //let root = process.root(); - let mut cmdline = vec![String::from("")]; - if let Ok(raw_cmdline) = process.cmdline() { - cmdline = raw_cmdline; - } - IProcess { - pid: process.pid, - owner: process.owner, - original: process.clone(), - comm: process.stat.comm.clone(), - cmdline, - stat: Some(IStat::from_procfs_stat(&process.stat)), + pub fn new(process: &Process) -> IProcess { + let disk_usage = process.disk_usage(); + #[cfg(target_os = "linux")] + { + let mut stime = 0; + let mut utime = 0; + if let Ok(procfs_process) = + procfs::process::Process::new(process.pid().to_string().parse::().unwrap()) + { + if let Ok(stat) = procfs_process.stat() { + stime += stat.stime; + utime += stat.utime; + } + } + IProcess { + pid: process.pid(), + owner: 0, + comm: String::from(process.exe().to_str().unwrap()), + cmdline: process.cmd().to_vec(), + cpu_usage_percentage: process.cpu_usage(), + memory: process.memory(), + virtual_memory: process.virtual_memory(), + disk_read: disk_usage.read_bytes, + disk_written: disk_usage.written_bytes, + total_disk_read: disk_usage.total_read_bytes, + total_disk_written: disk_usage.total_written_bytes, + stime, + utime, + } } - } - - #[cfg(target_os = "windows")] - pub fn from_windows_process(process: &Process) -> IProcess { - IProcess { - pid: process.pid() as i32, - owner: 0, - comm: String::from(process.exe().to_str().unwrap()), - cmdline: process.cmd().to_vec(), - stat: Some(IStat::from_windows_process_stat(process)), + #[cfg(not(target_os = "linux"))] + { + IProcess { + pid: process.pid(), + owner: 0, + comm: String::from(process.exe().to_str().unwrap()), + cmdline: process.cmd().to_vec(), + cpu_usage_percentage: process.cpu_usage(), + memory: process.memory(), + virtual_memory: process.virtual_memory(), + disk_read: disk_usage.read_bytes, + disk_written: disk_usage.written_bytes, + total_disk_read: disk_usage.total_read_bytes, + total_disk_written: disk_usage.total_written_bytes, + } } } - #[cfg(target_os = "linux")] - pub fn cmdline(&self) -> Result, String> { - if let Ok(cmdline) = self.original.cmdline() { - Ok(cmdline) - } else { - Err(String::from("cmdline() was none")) - } - } - #[cfg(target_os = "windows")] - pub fn cmdline(&self, proc_tracker: &ProcessTracker) -> Result, String> { - if let Some(p) = proc_tracker.sysinfo.process(self.pid as usize) { + /// Returns the command line of related to the process, as found by sysinfo. + pub fn cmdline(&self, proc_tracker: &ProcessTracker) -> Result, Error> { + if let Some(p) = proc_tracker.sysinfo.process(self.pid) { Ok(p.cmd().to_vec()) } else { - Err(String::from("Failed to get original process.")) - } - } - - pub fn statm(&self) -> Result { - #[cfg(target_os = "linux")] - { - let mystatm = self.original.statm().unwrap(); - Ok(IStatM { - size: mystatm.size, - data: mystatm.data, - dt: mystatm.dt, - lib: mystatm.lib, - resident: mystatm.resident, - shared: mystatm.shared, - text: mystatm.text, - }) + Err(Error::new( + ErrorKind::Other, + "Failed to get original process.", + )) } - #[cfg(target_os = "windows")] - Ok(IStatM { - size: 42, - data: 42, - dt: 42, - lib: 42, - resident: 42, - shared: 42, - text: 42, - }) } - #[cfg(target_os = "linux")] - pub fn exe(&self) -> Result { - let original_exe = self.original.exe().unwrap(); - Ok(original_exe) - } - #[cfg(target_os = "windows")] + /// Returns the executable string related to the process pub fn exe(&self, proc_tracker: &ProcessTracker) -> Result { - if let Some(p) = proc_tracker.sysinfo.process(self.pid as usize) { + if let Some(p) = proc_tracker.sysinfo.process(self.pid) { Ok(PathBuf::from(p.exe().to_str().unwrap())) } else { Err(String::from("Couldn't get process.")) } } - pub fn status(&self) -> Result { - #[cfg(target_os = "linux")] - { - if let Ok(original_status) = self.original.status() { - let status = IStatus { - name: original_status.name, - pid: original_status.pid, - ppid: original_status.ppid, - state: original_status.state, - umask: original_status.umask, - }; - Ok(status) - } else { - Err(format!("Couldn't get status for {}", self.pid)) - } - } - #[cfg(target_os = "windows")] - { - Ok(IStatus { - name: String::from("Not implemented yet !"), - pid: 42, - ppid: 42, - state: String::from("X"), - umask: None, - }) + #[cfg(target_os = "linux")] + pub fn total_time_jiffies(&self, proc_tracker: &ProcessTracker) -> u64 { + if let Some(rec) = proc_tracker.get_process_last_record(self.pid) { + return rec.process.stime + rec.process.utime; } + 0 } - #[cfg(target_os = "linux")] - pub fn myself() -> Result { - Ok(IProcess::from_linux_process(&Process::myself().unwrap())) - } - #[cfg(target_os = "windows")] pub fn myself(proc_tracker: &ProcessTracker) -> Result { - Ok(IProcess::from_windows_process( + Ok(IProcess::new( proc_tracker .sysinfo - .process(get_current_pid().unwrap() as usize) + .process(get_current_pid().unwrap()) .unwrap(), )) } + + #[cfg(target_os = "linux")] + pub fn cgroups() {} } pub fn page_size() -> Result { @@ -377,7 +205,7 @@ pub struct ProcessTracker { /// Maximum number of ProcessRecord instances that scaphandre is allowed to /// store, per PID (thus, for each subvector). pub max_records_per_process: u16, - #[cfg(target_os = "windows")] + /// Sysinfo system for resources monitoring pub sysinfo: System, #[cfg(feature = "containers")] pub regex_cgroup_docker: Regex, @@ -392,7 +220,6 @@ impl Clone for ProcessTracker { ProcessTracker { procs: self.procs.clone(), max_records_per_process: self.max_records_per_process, - #[cfg(target_os = "windows")] sysinfo: System::new_all(), #[cfg(feature = "containers")] regex_cgroup_docker: self.regex_cgroup_docker.clone(), @@ -423,37 +250,66 @@ impl ProcessTracker { #[cfg(feature = "containers")] let regex_cgroup_containerd = Regex::new("/system.slice/containerd.service/.*$").unwrap(); + let mut system = System::new_all(); + system.refresh_cpu_specifics(CpuRefreshKind::everything()); + let nb_cores = system.cpus().len(); + ProcessTracker { procs: vec![], max_records_per_process, - #[cfg(target_os = "windows")] - sysinfo: System::new_all(), + sysinfo: system, #[cfg(feature = "containers")] regex_cgroup_docker, #[cfg(feature = "containers")] regex_cgroup_kubernetes, #[cfg(feature = "containers")] regex_cgroup_containerd, - #[cfg(target_os = "windows")] - nb_cores: System::new_all().processors().len(), - #[cfg(target_os = "linux")] - nb_cores: 0, // TODO implement + nb_cores, } } + pub fn refresh(&mut self) { + self.sysinfo.refresh_components(); + self.sysinfo.refresh_memory(); + self.sysinfo.refresh_disks(); + self.sysinfo.refresh_disks_list(); + self.sysinfo + .refresh_cpu_specifics(CpuRefreshKind::everything()); + } + + pub fn components(&mut self) -> Vec { + let mut res = vec![]; + for c in self.sysinfo.components() { + res.push(format!("{c:?}")); + } + res + } + /// Properly creates and adds a ProcessRecord to 'procs', the vector of vectors or ProcessRecords /// owned by the ProcessTracker instance. This method should be used to keep track of processes /// states during all the lifecycle of the exporter. /// # Linux Example: /// ``` - /// use procfs::process::Process; /// use scaphandre::sensors::utils::{ProcessTracker, IProcess}; - /// let mut tracker = ProcessTracker::new(5); - /// let pid = 1; - /// if let Ok(result) = tracker.add_process_record( - /// IProcess::from_linux_process(&Process::new(pid).unwrap()) - /// ){ - /// println!("ProcessRecord stored successfully: {}", result); + /// use scaphandre::sensors::Topology; + /// use std::collections::HashMap; + /// use sysinfo::SystemExt; + /// let mut pt = ProcessTracker::new(5); + /// pt.sysinfo.refresh_processes(); + /// pt.sysinfo.refresh_cpu(); + /// let current_procs = pt + /// .sysinfo + /// .processes() + /// .values() + /// .map(IProcess::new) + /// .collect::>(); + /// for p in current_procs { + /// match pt.add_process_record(p) { + /// Ok(result) => { println!("ProcessRecord stored successfully: {}", result); } + /// Err(msg) => { + /// panic!("Failed to track process !\nGot: {}", msg) + /// } + /// } /// } /// ``` pub fn add_process_record(&mut self, process: IProcess) -> Result { @@ -483,6 +339,15 @@ impl ProcessTracker { Ok(String::from("Successfully added record to process.")) } + pub fn get_process_last_record(&self, pid: Pid) -> Option<&ProcessRecord> { + if let Some(records) = self.find_records(pid) { + if let Some(last) = records.first() { + return Some(last); + } + } + None + } + /// Removes as many ProcessRecords as needed from the vector (passed as a mutable ref in parameters) /// in order for the vector length to match self.max_records_per_process. fn clean_old_process_records(records: &mut Vec, max_records_per_process: u16) { @@ -502,7 +367,7 @@ impl ProcessTracker { /// Returns a Some(ref to vector of ProcessRecords) if the pid is found /// in self.procs. Returns None otherwise. - pub fn find_records(&self, pid: i32) -> Option<&Vec> { + pub fn find_records(&self, pid: Pid) -> Option<&Vec> { let mut refer = None; for v in &self.procs { if !v.is_empty() && v[0].process.pid == pid { @@ -515,31 +380,8 @@ impl ProcessTracker { refer } - /// Returns the result of the substraction of utime between last and - /// previous ProcessRecord for a given pid. - pub fn get_diff_utime(&self, pid: i32) -> Option { - let records = self.find_records(pid).unwrap(); - if records.len() > 1 { - if let Some(previous) = &records[0].process.stat { - if let Some(current) = &records[1].process.stat { - return Some(previous.utime - current.utime); - } - } - } - None - } - /// Returns the result of the substraction of stime between last and - /// previous ProcessRecord for a given pid. - pub fn get_diff_stime(&self, pid: i32) -> Option { - let records = self.find_records(pid).unwrap(); - if records.len() > 1 { - if let Some(previous) = &records[0].process.stat { - if let Some(current) = &records[1].process.stat { - return Some(previous.stime - current.stime); - } - } - } - None + pub fn get_cpu_frequency(&self) -> u64 { + self.sysinfo.global_cpu_info().frequency() } /// Returns all vectors of process records linked to a running, sleeping, waiting or zombie process. @@ -548,21 +390,20 @@ impl ProcessTracker { debug!("In get alive processes."); let mut res = vec![]; for p in self.procs.iter() { - #[cfg(target_os = "linux")] - if !p.is_empty() { - let status = p[0].process.status(); - if let Ok(status_val) = status { - if !&status_val.state.contains('T') { - // !&status_val.state.contains("Z") && - res.push(p); - } - } - } - #[cfg(target_os = "windows")] + //#[cfg(target_os = "linux")] + //if !p.is_empty() { + // let status = p[0].process.status(); + // if let Ok(status_val) = status { + // if !&status_val.state.contains('T') { + // // !&status_val.state.contains("Z") && + // res.push(p); + // } + // } + //} if !p.is_empty() { //TODO implement // clippy will ask you to remove mut from res, but you just need to implement to fix that - if let Some(_sysinfo_p) = self.sysinfo.process(p[0].process.pid as usize) { + if let Some(_sysinfo_p) = self.sysinfo.process(p[0].process.pid) { //let status = sysinfo_p.status(); //if status != ProcessStatus::Dead {//&& status != ProcessStatus::Stop { res.push(p); @@ -599,7 +440,7 @@ impl ProcessTracker { #[cfg(feature = "containers")] pub fn get_process_container_description( &self, - pid: i32, // the PID of the process to look for + pid: Pid, // the PID of the process to look for containers: &[Container], docker_version: String, pods: &[Pod], @@ -612,142 +453,158 @@ impl ProcessTracker { let process = result.next().unwrap(); let mut description = HashMap::new(); let regex_clean_container_id = Regex::new("[[:alnum:]]{12,}").unwrap(); - if let Some(p) = process.get(0) { + if let Some(_p) = process.get(0) { // if we have the cgroups data from the original process struct - if let Ok(cgroups) = p.process.original.cgroups() { - let mut found = false; - for cg in &cgroups { - if found { - break; - } - // docker - if self.regex_cgroup_docker.is_match(&cg.pathname) { - debug!("regex docker matched : {}", &cg.pathname); //coucou - description - .insert(String::from("container_scheduler"), String::from("docker")); - // extract container_id - //let container_id = cg.pathname.split('/').last().unwrap(); - if let Some(container_id_capture) = - regex_clean_container_id.captures(&cg.pathname) - { - let container_id = &container_id_capture[0]; - debug!("container_id = {}", container_id); - description - .insert(String::from("container_id"), String::from(container_id)); - if let Some(container) = - containers.iter().find(|x| x.Id == container_id) + if let Ok(procfs_process) = + procfs::process::Process::new(pid.to_string().parse::().unwrap()) + { + if let Ok(cgroups) = procfs_process.cgroups() { + let mut found = false; + for cg in &cgroups { + if found { + break; + } + // docker + if self.regex_cgroup_docker.is_match(&cg.pathname) { + debug!("regex docker matched : {}", &cg.pathname); //coucou + description.insert( + String::from("container_scheduler"), + String::from("docker"), + ); + // extract container_id + //let container_id = cg.pathname.split('/').last().unwrap(); + if let Some(container_id_capture) = + regex_clean_container_id.captures(&cg.pathname) { - debug!("found container with id: {}", &container_id); - let mut names = String::from(""); - for n in &container.Names { - debug!("adding container name: {}", &n.trim().replace('/', "")); - names.push_str(&n.trim().replace('/', "")); - } - description.insert(String::from("container_names"), names); + let container_id = &container_id_capture[0]; + debug!("container_id = {}", container_id); description.insert( - String::from("container_docker_version"), - docker_version.clone(), + String::from("container_id"), + String::from(container_id), ); - if let Some(labels) = &container.Labels { - for (k, v) in labels { - let escape_list = ["-", ".", ":", " "]; - let mut key = k.clone(); - for e in escape_list.iter() { - key = key.replace(e, "_"); - } - description.insert( - format!("container_label_{key}"), - v.to_string(), + if let Some(container) = + containers.iter().find(|x| x.Id == container_id) + { + debug!("found container with id: {}", &container_id); + let mut names = String::from(""); + for n in &container.Names { + debug!( + "adding container name: {}", + &n.trim().replace('/', "") ); + names.push_str(&n.trim().replace('/', "")); + } + description.insert(String::from("container_names"), names); + description.insert( + String::from("container_docker_version"), + docker_version.clone(), + ); + if let Some(labels) = &container.Labels { + for (k, v) in labels { + let escape_list = ["-", ".", ":", " "]; + let mut key = k.clone(); + for e in escape_list.iter() { + key = key.replace(e, "_"); + } + description.insert( + format!("container_label_{key}"), + v.to_string(), + ); + } } } + found = true; } - found = true; - } - } else { - // containerd - if self.regex_cgroup_containerd.is_match(&cg.pathname) { - debug!("regex containerd matched : {}", &cg.pathname); - description.insert( - String::from("container_runtime"), - String::from("containerd"), - ); - } else if self.regex_cgroup_kubernetes.is_match(&cg.pathname) { - debug!("regex kubernetes matched : {}", &cg.pathname); - // kubernetes not using containerd but we can get the container id } else { - // cgroup not related to a container technology - continue; - } + // containerd + if self.regex_cgroup_containerd.is_match(&cg.pathname) { + debug!("regex containerd matched : {}", &cg.pathname); + description.insert( + String::from("container_runtime"), + String::from("containerd"), + ); + } else if self.regex_cgroup_kubernetes.is_match(&cg.pathname) { + debug!("regex kubernetes matched : {}", &cg.pathname); + // kubernetes not using containerd but we can get the container id + } else { + // cgroup not related to a container technology + continue; + } - let container_id = - match self.extract_pod_id_from_cgroup_path(cg.pathname.clone()) { - Ok(id) => id, - Err(err) => { - info!("Couldn't get container id : {}", err); - "ERROR Couldn't get container id".to_string() - } - }; - description.insert(String::from("container_id"), container_id.clone()); - // find pod in pods that has pod_status > container_status.container - if let Some(pod) = pods.iter().find(|x| match &x.status { - Some(status) => { - if let Some(container_statuses) = &status.container_statuses { - container_statuses.iter().any(|y| match &y.container_id { - Some(id) => { - if let Some(final_id) = id.strip_prefix("docker://") { - final_id == container_id - } else if let Some(final_id) = - id.strip_prefix("containerd://") - { - final_id == container_id - } else { - false + let container_id = + match self.extract_pod_id_from_cgroup_path(cg.pathname.clone()) { + Ok(id) => id, + Err(err) => { + info!("Couldn't get container id : {}", err); + "ERROR Couldn't get container id".to_string() + } + }; + description.insert(String::from("container_id"), container_id.clone()); + // find pod in pods that has pod_status > container_status.container + if let Some(pod) = pods.iter().find(|x| match &x.status { + Some(status) => { + if let Some(container_statuses) = &status.container_statuses { + container_statuses.iter().any(|y| match &y.container_id { + Some(id) => { + if let Some(final_id) = id.strip_prefix("docker://") + { + final_id == container_id + } else if let Some(final_id) = + id.strip_prefix("containerd://") + { + final_id == container_id + } else { + false + } } - } - None => false, - }) - } else { - false + None => false, + }) + } else { + false + } } - } - None => false, - }) { - description.insert( - String::from("container_scheduler"), - String::from("kubernetes"), - ); - if let Some(pod_name) = &pod.metadata.name { - description - .insert(String::from("kubernetes_pod_name"), pod_name.clone()); - } - if let Some(pod_namespace) = &pod.metadata.namespace { + None => false, + }) { description.insert( - String::from("kubernetes_pod_namespace"), - pod_namespace.clone(), + String::from("container_scheduler"), + String::from("kubernetes"), ); - } - if let Some(pod_spec) = &pod.spec { - if let Some(node_name) = &pod_spec.node_name { + if let Some(pod_name) = &pod.metadata.name { + description.insert( + String::from("kubernetes_pod_name"), + pod_name.clone(), + ); + } + if let Some(pod_namespace) = &pod.metadata.namespace { description.insert( - String::from("kubernetes_node_name"), - node_name.clone(), + String::from("kubernetes_pod_namespace"), + pod_namespace.clone(), ); } + if let Some(pod_spec) = &pod.spec { + if let Some(node_name) = &pod_spec.node_name { + description.insert( + String::from("kubernetes_node_name"), + node_name.clone(), + ); + } + } } - } - found = true; - } //else { - // debug!("Cgroup not identified as related to a container technology : {}", &cg.pathname); - //} + found = true; + } //else { + // debug!("Cgroup not identified as related to a container technology : {}", &cg.pathname); + //} + } } + } else { + debug!("Could'nt find {} in procfs.", pid.to_string()); } } description } /// Returns a vector containing pids of all running, sleeping or waiting current processes. - pub fn get_alive_pids(&self) -> Vec { + pub fn get_alive_pids(&self) -> Vec { self.get_alive_processes() .iter() .filter(|x| !x.is_empty()) @@ -756,7 +613,7 @@ impl ProcessTracker { } /// Returns a vector containing pids of all processes being tracked. - pub fn get_all_pids(&self) -> Vec { + pub fn get_all_pids(&self) -> Vec { self.procs .iter() .filter(|x| !x.is_empty()) @@ -765,7 +622,7 @@ impl ProcessTracker { } /// Returns the process name associated to a PID - pub fn get_process_name(&self, pid: i32) -> String { + pub fn get_process_name(&self, pid: Pid) -> String { let mut result = self .procs .iter() @@ -779,17 +636,14 @@ impl ProcessTracker { } /// Returns the cmdline string associated to a PID - pub fn get_process_cmdline(&self, pid: i32) -> Option { + pub fn get_process_cmdline(&self, pid: Pid) -> Option { let mut result = self .procs .iter() .filter(|x| !x.is_empty() && x.get(0).unwrap().process.pid == pid); let process = result.next().unwrap(); if let Some(p) = process.get(0) { - #[cfg(target_os = "windows")] let cmdline_request = p.process.cmdline(self); - #[cfg(target_os = "linux")] - let cmdline_request = p.process.cmdline(); if let Ok(mut cmdline_vec) = cmdline_request { let mut cmdline = String::from(""); while !cmdline_vec.is_empty() { @@ -805,27 +659,11 @@ impl ProcessTracker { None } - #[cfg(target_os = "linux")] - /// Returns the CPU time consumed between two measure iteration - fn get_cpu_time_consumed(&self, p: &[ProcessRecord]) -> u64 { - let last_time = p.first().unwrap().total_time_jiffies(); - let previous_time = p.get(1).unwrap().total_time_jiffies(); - let mut diff = 0; - if previous_time <= last_time { - diff = last_time - previous_time; - } - diff - } - - #[cfg(target_os = "windows")] - pub fn get_cpu_usage_percentage(&self, pid: usize, nb_cores: usize) -> f32 { - let mut cpu_current_usage = 0.0; - for c in self.sysinfo.processors() { - cpu_current_usage += c.cpu_usage(); - } + pub fn get_cpu_usage_percentage(&self, pid: Pid, nb_cores: usize) -> f32 { + info!("CALL FOR CPU USAGE"); + let cpu_current_usage = self.sysinfo.global_cpu_info().cpu_usage(); if let Some(p) = self.sysinfo.process(pid) { - (p.cpu_usage() + (100.0 - cpu_current_usage / nb_cores as f32) * p.cpu_usage() / 100.0) - / nb_cores as f32 + (cpu_current_usage * p.cpu_usage() / 100.0) / nb_cores as f32 } else { 0.0 } @@ -836,51 +674,29 @@ impl ProcessTracker { let mut consumers: Vec<(IProcess, OrderedFloat)> = vec![]; for p in &self.procs { if p.len() > 1 { - #[cfg(target_os = "linux")] + let diff = self + .get_cpu_usage_percentage(p.first().unwrap().process.pid as _, self.nb_cores); + if consumers + .iter() + .filter(|x| { + if let Some(p) = self.sysinfo.process(x.0.pid as _) { + return p.cpu_usage() > diff; + } + false + }) + .count() + < top as usize { - let diff = self.get_cpu_time_consumed(p); - if consumers - .iter() - .filter(|x| ProcessRecord::new(x.0.to_owned()).total_time_jiffies() > diff) - .count() - < top as usize - { - consumers - .push((p.last().unwrap().process.clone(), OrderedFloat(diff as f64))); + let pid = p.first().unwrap().process.pid; + if let Some(sysinfo_process) = self.sysinfo.process(pid as _) { + let new_consumer = IProcess::new(sysinfo_process); + consumers.push((new_consumer, OrderedFloat(diff as f64))); consumers.sort_by(|x, y| y.1.cmp(&x.1)); if consumers.len() > top as usize { consumers.pop(); } - } - } - #[cfg(target_os = "windows")] - { - let diff = self.get_cpu_usage_percentage( - p.first().unwrap().process.pid as _, - self.nb_cores, - ); - if consumers - .iter() - .filter(|x| { - if let Some(p) = self.sysinfo.process(x.0.pid as _) { - return p.cpu_usage() > diff; - } - false - }) - .count() - < top as usize - { - let pid = p.first().unwrap().process.pid; - if let Some(sysinfo_process) = self.sysinfo.process(pid as _) { - let new_consumer = IProcess::from_windows_process(sysinfo_process); - consumers.push((new_consumer, OrderedFloat(diff as f64))); - consumers.sort_by(|x, y| y.1.cmp(&x.1)); - if consumers.len() > top as usize { - consumers.pop(); - } - } else { - warn!("Couldn't get process info for {}", pid); - } + } else { + warn!("Couldn't get process info for {}", pid); } } } @@ -897,28 +713,12 @@ impl ProcessTracker { let mut consumers: Vec<(IProcess, OrderedFloat)> = vec![]; for p in &self.procs { if p.len() > 1 { - #[cfg(target_os = "linux")] - { - let diff = self.get_cpu_time_consumed(p); - let process_exe = p.last().unwrap().process.exe().unwrap_or_default(); - if regex_filter.is_match(process_exe.to_str().unwrap_or_default()) { - consumers - .push((p.last().unwrap().process.clone(), OrderedFloat(diff as f64))); - consumers.sort_by(|x, y| y.1.cmp(&x.1)); - } - } - #[cfg(target_os = "windows")] - { - let diff = self.get_cpu_usage_percentage( - p.first().unwrap().process.pid as _, - self.nb_cores, - ); - let process_exe = p.last().unwrap().process.exe(self).unwrap_or_default(); - if regex_filter.is_match(process_exe.to_str().unwrap_or_default()) { - consumers - .push((p.last().unwrap().process.clone(), OrderedFloat(diff as f64))); - consumers.sort_by(|x, y| y.1.cmp(&x.1)); - } + let diff = self + .get_cpu_usage_percentage(p.first().unwrap().process.pid as _, self.nb_cores); + let process_exe = p.last().unwrap().process.exe(self).unwrap_or_default(); + if regex_filter.is_match(process_exe.to_str().unwrap_or_default()) { + consumers.push((p.last().unwrap().process.clone(), OrderedFloat(diff as f64))); + consumers.sort_by(|x, y| y.1.cmp(&x.1)); } } } @@ -934,44 +734,28 @@ impl ProcessTracker { /// (if the process is not running anymore) pub fn clean_terminated_process_records_vectors(&mut self) { //TODO get stats from processes to know what is hapening ! - let mut d_unint_sleep = 0; - let mut r_running = 0; - let mut s_int_sleep = 0; - let mut t_stopped = 0; - let mut z_defunct_zombie = 0; - let mut w_no_resident_high_prio = 0; - let mut n_low_prio = 0; - let mut l_pages_locked = 0; - let mut i_idle = 0; - let mut unknown = 0; for v in &mut self.procs { if !v.is_empty() { if let Some(first) = v.first() { - if let Ok(status) = first.process.status() { - if status.state.contains('T') { - while !v.is_empty() { - v.pop(); + if let Some(p) = self.sysinfo.process(first.process.pid) { + match p.status() { + ProcessStatus::Idle => {} + ProcessStatus::Dead => {} + ProcessStatus::Stop => { + while !v.is_empty() { + v.pop(); + } } - t_stopped += 1; - } else if status.state.contains('D') { - d_unint_sleep += 1; - } else if status.state.contains('R') { - r_running += 1; - } else if status.state.contains('S') { - s_int_sleep += 1; - } else if status.state.contains('Z') { - z_defunct_zombie += 1; - } else if status.state.contains('W') { - w_no_resident_high_prio += 1; - } else if status.state.contains('N') { - n_low_prio += 1; - } else if status.state.contains('L') { - l_pages_locked += 1; - } else if status.state.contains('I') { - i_idle += 1; - } else { - unknown += 1; - debug!("unkown state: {} name: {}", status.state, status.name); + ProcessStatus::Run => {} + ProcessStatus::LockBlocked => {} + ProcessStatus::Waking => {} + ProcessStatus::Wakekill => {} + ProcessStatus::Tracing => {} + ProcessStatus::Zombie => {} + ProcessStatus::Sleep => {} + ProcessStatus::Parked => {} + ProcessStatus::UninterruptibleDiskSleep => {} + ProcessStatus::Unknown(_code) => {} } } else { while !v.is_empty() { @@ -981,19 +765,6 @@ impl ProcessTracker { } } } - debug!( - "d:{} r:{} s:{} t:{} z:{} w:{} n:{} l:{} i:{} u:{}", - d_unint_sleep, - r_running, - s_int_sleep, - t_stopped, - z_defunct_zombie, - w_no_resident_high_prio, - n_low_prio, - l_pages_locked, - i_idle, - unknown - ); self.drop_empty_process_records_vectors(); } @@ -1029,35 +800,6 @@ impl ProcessRecord { timestamp: current_system_time_since_epoch(), } } - - // Returns the total CPU time consumed by this process since its creation - pub fn total_time_jiffies(&self) -> u64 { - #[cfg(target_os = "linux")] - if let Some(stat) = &self.process.stat { - trace!( - "ProcessRecord: stime {} utime {}", //cutime {} cstime {} guest_time {} cguest_time {} delayacct_blkio_ticks {} itrealvalue {}", - stat.stime, - stat.utime //, cutime, cstime, guest_time, cguest_time, delayacct_blkio_ticks, itrealvalue - ); - return stat.stime + stat.utime; - } else { - warn!("No IStat !"); - } - - //#[cfg(target_os="windows")] - //let usage = &self.sysinfo. - //let cutime = self.process.stat.cutime as u64; - //let cstime = self.process.stat.cstime as u64; - //let guest_time = self.process.stat.guest_time.unwrap_or_default(); - //let cguest_time = self.process.stat.cguest_time.unwrap_or_default() as u64; - //let delayacct_blkio_ticks = self.process.stat.delayacct_blkio_ticks.unwrap_or_default(); - //let itrealvalue = self.process.stat.itrealvalue as u64; - - // not including cstime and cutime in total as they are reported only when child dies - // child metrics as already reported as the child processes are in the global process - // list, found as /proc/PID/stat - 0 //+ guest_time + cguest_time + delayacct_blkio_ticks + itrealvalue - } } /// Returns a Duration instance with the current timestamp @@ -1067,46 +809,61 @@ pub fn current_system_time_since_epoch() -> Duration { .unwrap() } -#[cfg(all(test, target_os = "linux"))] mod tests { - use super::*; + + #[test] + fn process_cmdline() { + use super::*; + use crate::sensors::Topology; + // find the cmdline of current proc thanks to sysinfo + // do the same with processtracker + // assert + let mut system = System::new(); + system.refresh_all(); + let self_pid_by_sysinfo = get_current_pid(); + let self_process_by_sysinfo = system.process(self_pid_by_sysinfo.unwrap()).unwrap(); + + let mut topo = Topology::new(HashMap::new()); + topo.refresh(); + let self_process_by_scaph = IProcess::myself(&topo.proc_tracker).unwrap(); + + assert_eq!( + self_process_by_sysinfo.cmd().concat(), + topo.proc_tracker + .get_process_cmdline(self_process_by_scaph.pid) + .unwrap() + ); + } + + #[cfg(all(test, target_os = "linux"))] #[test] fn process_records_added() { - let proc = Process::myself().unwrap(); + use super::*; + use crate::sensors::Topology; + let mut topo = Topology::new(HashMap::new()); + topo.refresh(); + let proc = IProcess::myself(&topo.proc_tracker).unwrap(); let mut tracker = ProcessTracker::new(3); for _ in 0..3 { - assert_eq!( - tracker - .add_process_record(IProcess::from_linux_process(&proc)) - .is_ok(), - true - ); + assert_eq!(tracker.add_process_record(proc.clone()).is_ok(), true); } assert_eq!(tracker.procs.len(), 1); assert_eq!(tracker.procs[0].len(), 3); } + #[cfg(all(test, target_os = "linux"))] #[test] fn process_records_cleaned() { - let proc = Process::myself().unwrap(); + use super::*; let mut tracker = ProcessTracker::new(3); + let proc = IProcess::myself(&tracker).unwrap(); for _ in 0..5 { - assert_eq!( - tracker - .add_process_record(IProcess::from_linux_process(&proc)) - .is_ok(), - true - ); + assert_eq!(tracker.add_process_record(proc.clone()).is_ok(), true); } assert_eq!(tracker.procs.len(), 1); assert_eq!(tracker.procs[0].len(), 3); for _ in 0..15 { - assert_eq!( - tracker - .add_process_record(IProcess::from_linux_process(&proc)) - .is_ok(), - true - ); + assert_eq!(tracker.add_process_record(proc.clone()).is_ok(), true); } assert_eq!(tracker.procs.len(), 1); assert_eq!(tracker.procs[0].len(), 3); diff --git a/tests/integration.rs b/tests/integration.rs index a3dd4c2e..3f23f51a 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,8 +1,11 @@ +#[cfg(target_os = "linux")] use scaphandre::exporters::qemu::QemuExporter; +#[cfg(target_os = "linux")] use scaphandre::sensors::powercap_rapl::PowercapRAPLSensor; use std::env::current_dir; use std::fs::{create_dir, read_dir}; +#[cfg(target_os = "linux")] #[test] fn exporter_qemu() { let sensor = PowercapRAPLSensor::new(1, 1, false);