summaryrefslogtreecommitdiff
path: root/opendc-experiments
diff options
context:
space:
mode:
authorDante Niewenhuis <d.niewenhuis@hotmail.com>2024-03-19 20:26:04 +0100
committerGitHub <noreply@github.com>2024-03-19 20:26:04 +0100
commitdff30fa60809c018101052f395b09cf17cb83ccb (patch)
tree2c5f67b9424547061aaa0c6b6b85af9a125ec263 /opendc-experiments
parent960b3d8a13c67ac4b7f479d5764b0b618fc9ea09 (diff)
Scenario and Portfolio update (#209)
* Initial commit * Implemented a new systems of defining and running scenarios / portfolios. Scenarios and Portfolios can now be defined using JSON files similar to topologies. This allows user to define experiments without changing any KotLin code. * Ran spotlessApply
Diffstat (limited to 'opendc-experiments')
-rw-r--r--opendc-experiments/opendc-experiments-base/build.gradle.kts12
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/Portfolio.kt (renamed from opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/Portfolio.kt)13
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioFactories.kt (renamed from opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/OperationalPhenomena.kt)32
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioReader.kt48
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioSpec.kt (renamed from opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Topology.kt)15
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/Scenario.kt (renamed from opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Workload.kt)25
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioFactories.kt (renamed from opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/TestPortfolio.kt)55
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioReader.kt (renamed from opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Scenario.kt)44
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioSpecs.kt165
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioHelpers.kt (renamed from opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/TraceHelpers.kt)7
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioRunner.kt142
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/output/host/seed=0/data.parquetbin553104 -> 0 bytes
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/output/server/seed=0/data.parquetbin719109 -> 0 bytes
-rw-r--r--opendc-experiments/opendc-experiments-base/src/main/output/service/seed=0/data.parquetbin2614 -> 0 bytes
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/ScenarioIntegrationTest.kt (renamed from opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt)29
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/ScenarioRunnerTest.kt79
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/resources/env/multi.json (renamed from opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.json)0
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/resources/env/single.json (renamed from opendc-experiments/opendc-experiments-capelin/src/test/resources/env/single.json)0
-rw-r--r--opendc-experiments/opendc-experiments-capelin/.gitignore2
-rw-r--r--opendc-experiments/opendc-experiments-capelin/LICENSE.txt21
-rw-r--r--opendc-experiments/opendc-experiments-capelin/README.md9
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt167
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt105
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/CompositeWorkloadPortfolio.kt82
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/HorVerPortfolio.kt72
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreHpcPortfolio.kt71
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreVelocityPortfolio.kt69
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/OperationalPhenomenaPortfolio.kt79
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinRunnerTest.kt87
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt158
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierPortfolio.kt62
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt105
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/benchmark/trace/meta.parquetbin4514 -> 0 bytes
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/benchmark/trace/trace.parquetbin3749 -> 0 bytes
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.txt5
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.txt2
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierIntegrationTest.kt295
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierRunnerTest.kt87
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/build.gradle.kts (renamed from opendc-experiments/opendc-experiments-capelin/build.gradle.kts)33
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt (renamed from opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt)2
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/jmh/resources/log4j2.xml (renamed from opendc-experiments/opendc-experiments-capelin/src/jmh/resources/log4j2.xml)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/jmh/resources/topology.txt (renamed from opendc-experiments/opendc-experiments-base/src/test/resources/env/topology.txt)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb (renamed from opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/Python_scripts/OpenDCdemo.ipynb (renamed from opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/OpenDCdemo.ipynb)515
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/kotlin/org/opendc/experiments/scenario/ExamplePortfolio.kt69
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/kotlin/org/opendc/experiments/scenario/PortfolioCli.kt64
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/interference-model.json (renamed from opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/interference-model.json)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/trace/meta.parquet (renamed from opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/trace/meta.parquet)bin2723 -> 2723 bytes
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/trace/trace.parquet (renamed from opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/trace/trace.parquet)bin2163354 -> 2163354 bytes
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/resources/env/multi.json (renamed from opendc-experiments/opendc-experiments-capelin/src/test/resources/env/topology.json)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/resources/env/single.json (renamed from opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.json)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/resources/log4j2.xml (renamed from opendc-experiments/opendc-experiments-capelin/src/main/resources/log4j2.xml)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/main/resources/portfolio.json31
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/test/resources/env/single.txt (renamed from opendc-experiments/opendc-experiments-base/src/test/resources/env/single.txt)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/test/resources/env/topology.txt (renamed from opendc-experiments/opendc-experiments-capelin/src/jmh/resources/topology.txt)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/interference-model.json (renamed from opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/interference-model.json)0
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/meta.parquet (renamed from opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/meta.parquet)bin2723 -> 2723 bytes
-rw-r--r--opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/trace.parquet (renamed from opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/trace.parquet)bin2163354 -> 2163354 bytes
-rw-r--r--opendc-experiments/opendc-experiments-scenario/build.gradle.kts (renamed from opendc-experiments/opendc-experiments-greenifier/build.gradle.kts)21
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt (renamed from opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt)20
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/jmh/resources/log4j2.xml (renamed from opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/log4j2.xml)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/jmh/resources/topology.txt (renamed from opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/topology.txt)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb1379
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/Python_scripts/OpenDCdemo.ipynb1121
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/kotlin/org/opendc/experiments/scenario/ScenarioCli.kt64
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/interference-model.json (renamed from opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/interference-model.json)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/trace/meta.parquet (renamed from opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/meta.parquet)bin2723 -> 2723 bytes
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/trace/trace.parquet (renamed from opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/trace.parquet)bin2163354 -> 2163354 bytes
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/resources/env/multi.json (renamed from opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/topology.json)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/resources/env/single.json (renamed from opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/single.json)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/resources/log4j2.xml (renamed from opendc-experiments/opendc-experiments-greenifier/src/main/resources/log4j2.xml)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/main/resources/scenario.json13
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/test/resources/env/single.txt (renamed from opendc-experiments/opendc-experiments-capelin/src/main/resources/env/single.txt)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/test/resources/env/topology.txt (renamed from opendc-experiments/opendc-experiments-capelin/src/main/resources/env/multi.txt)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/interference-model.json (renamed from opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/interference-model.json)0
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/meta.parquet (renamed from opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/meta.parquet)bin2723 -> 2723 bytes
-rw-r--r--opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/trace.parquet (renamed from opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/trace.parquet)bin2163354 -> 2163354 bytes
77 files changed, 3428 insertions, 2048 deletions
diff --git a/opendc-experiments/opendc-experiments-base/build.gradle.kts b/opendc-experiments/opendc-experiments-base/build.gradle.kts
index 8aa82b67..d7d8f235 100644
--- a/opendc-experiments/opendc-experiments-base/build.gradle.kts
+++ b/opendc-experiments/opendc-experiments-base/build.gradle.kts
@@ -27,10 +27,22 @@ plugins {
`kotlin-library-conventions`
`testing-conventions`
`jacoco-conventions`
+ kotlin("plugin.serialization") version "1.9.22"
}
dependencies {
+
api(projects.opendcCompute.opendcComputeService)
api(projects.opendcCompute.opendcComputeSimulator)
+
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
+ implementation(libs.progressbar)
implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-workload")))
+ implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-telemetry")))
+ implementation(project(mapOf("path" to ":opendc-simulator:opendc-simulator-core")))
+ implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-topology")))
+
+ runtimeOnly(projects.opendcTrace.opendcTraceOpendc)
+ runtimeOnly(libs.log4j.core)
+ runtimeOnly(libs.log4j.slf4j)
}
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/Portfolio.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/Portfolio.kt
index 961ae106..7b0299c5 100644
--- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/Portfolio.kt
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/Portfolio.kt
@@ -20,16 +20,13 @@
* SOFTWARE.
*/
-package org.opendc.experiments.base.portfolio
+package org.opendc.experiments.base.models.portfolio
-import org.opendc.experiments.base.portfolio.model.Scenario
+import org.opendc.experiments.base.models.scenario.Scenario
/**
* A portfolio represents a collection of scenarios are tested for the work.
*/
-public interface Portfolio {
- /**
- * The scenarios that belong to this portfolio.
- */
- public val scenarios: Iterable<Scenario>
-}
+public class Portfolio(
+ public val scenarios: Iterable<Scenario>,
+)
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/OperationalPhenomena.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioFactories.kt
index ea78e556..aee87814 100644
--- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/OperationalPhenomena.kt
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioFactories.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 AtLarge Research
+ * Copyright (c) 2024 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,12 +20,26 @@
* SOFTWARE.
*/
-package org.opendc.experiments.base.portfolio.model
+package org.opendc.experiments.base.models.portfolio
-/**
- * Operation phenomena during experiments.
- *
- * @param failureFrequency The average time between failures in hours.
- * @param hasInterference A flag to enable performance interference between VMs.
- */
-public data class OperationalPhenomena(val failureFrequency: Double, val hasInterference: Boolean)
+import org.opendc.experiments.base.models.scenario.getScenario
+import java.io.File
+
+private val porfolioReader = PortfolioReader()
+
+public fun getPortfolio(filePath: String): Portfolio {
+ return getPortfolio(File(filePath))
+}
+
+public fun getPortfolio(file: File): Portfolio {
+ return getPortfolio(porfolioReader.read(file))
+}
+
+public fun getPortfolio(portfolioSpec: PortfolioSpec): Portfolio {
+ return Portfolio(
+ portfolioSpec.scenarios.map {
+ scenario ->
+ getScenario(scenario)
+ },
+ )
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioReader.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioReader.kt
new file mode 100644
index 00000000..767b61bb
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioReader.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2024 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.experiments.base.models.portfolio
+
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.decodeFromStream
+import java.io.File
+import java.io.InputStream
+
+public class PortfolioReader {
+ @OptIn(ExperimentalSerializationApi::class)
+ public fun read(file: File): PortfolioSpec {
+ val input = file.inputStream()
+ val obj = Json.decodeFromStream<PortfolioSpec>(input)
+
+ return obj
+ }
+
+ /**
+ * Read the specified [input].
+ */
+ @OptIn(ExperimentalSerializationApi::class)
+ public fun read(input: InputStream): PortfolioSpec {
+ val obj = Json.decodeFromStream<PortfolioSpec>(input)
+ return obj
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Topology.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioSpec.kt
index 0053b541..554442b2 100644
--- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Topology.kt
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/portfolio/PortfolioSpec.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 AtLarge Research
+ * Copyright (c) 2024 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,9 +20,12 @@
* SOFTWARE.
*/
-package org.opendc.experiments.base.portfolio.model
+package org.opendc.experiments.base.models.portfolio
-/**
- * The topology on which we simulate the workload.
- */
-public data class Topology(val name: String)
+import kotlinx.serialization.Serializable
+import org.opendc.experiments.base.models.scenario.ScenarioSpec
+
+@Serializable
+public data class PortfolioSpec(
+ val scenarios: List<ScenarioSpec>,
+)
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Workload.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/Scenario.kt
index 0dd9df09..192bba1e 100644
--- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Workload.kt
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/Scenario.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 AtLarge Research
+ * Copyright (c) 2024 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,14 +20,19 @@
* SOFTWARE.
*/
-package org.opendc.experiments.base.portfolio.model
+package org.opendc.experiments.base.models.scenario
-import org.opendc.compute.workload.ComputeWorkload
+import org.opendc.compute.simulator.failure.FailureModel
+import org.opendc.compute.topology.specs.HostSpec
-/**
- * A single workload originating from a trace.
- *
- * @param name the name of the workload.
- * @param source The source of the workload data.
- */
-public data class Workload(val name: String, val source: ComputeWorkload)
+public data class Scenario(
+ val topology: List<HostSpec>,
+ val workload: WorkloadSpec,
+ val allocationPolicy: AllocationPolicySpec,
+ val failureModel: FailureModel?,
+ val exportModel: ExportSpec = ExportSpec(),
+ val outputFolder: String = "output",
+ val name: String = "",
+ val runs: Int = 1,
+ val initialSeed: Int = 0,
+)
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/TestPortfolio.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioFactories.kt
index 729fb017..d806e95e 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/TestPortfolio.kt
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioFactories.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 AtLarge Research
+ * Copyright (c) 2024 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,27 +20,38 @@
* SOFTWARE.
*/
-package org.opendc.experiments.capelin.portfolios
+package org.opendc.experiments.base.models.scenario
-import org.opendc.compute.workload.sampleByLoad
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.Portfolio
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
+import org.opendc.compute.simulator.failure.getFailureModel
+import org.opendc.compute.topology.clusterTopology
+import java.io.File
-/**
- * A [Portfolio] to perform a simple test run.
- */
-public class TestPortfolio : Portfolio {
- override val scenarios: Iterable<Scenario> =
- listOf(
- Scenario(
- Topology("single"),
- Workload("bitbrains-small", trace("trace").sampleByLoad(1.0)),
- OperationalPhenomena(failureFrequency = 0.0, hasInterference = true),
- "active-servers",
- ),
- )
+private val scenarioReader = ScenarioReader()
+
+public fun getScenario(filePath: String): Scenario {
+ return getScenario(File(filePath))
+}
+
+public fun getScenario(file: File): Scenario {
+ return getScenario(scenarioReader.read(file))
+}
+
+public fun getScenario(scenarioSpec: ScenarioSpec): Scenario {
+ val topology = clusterTopology(File(scenarioSpec.topology.pathToFile))
+ val workload = scenarioSpec.workload
+ val allocationPolicy = scenarioSpec.allocationPolicy
+ val failureModel = getFailureModel(scenarioSpec.failureModel.failureInterval)
+ val exportModel = scenarioSpec.exportModel
+
+ return Scenario(
+ topology,
+ workload,
+ allocationPolicy,
+ failureModel,
+ exportModel,
+ scenarioSpec.outputFolder,
+ scenarioSpec.name,
+ scenarioSpec.runs,
+ scenarioSpec.initialSeed,
+ )
}
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Scenario.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioReader.kt
index cf0f5320..e7c7b4ae 100644
--- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/portfolio/model/Scenario.kt
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioReader.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 AtLarge Research
+ * Copyright (c) 2024 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,21 +20,29 @@
* SOFTWARE.
*/
-package org.opendc.experiments.base.portfolio.model
+package org.opendc.experiments.base.models.scenario
-/**
- * A single scenario of a portfolio.
- *
- * @property topology The topology to test.
- * @property workload The workload to test.
- * @property operationalPhenomena The [OperationalPhenomena] to model.
- * @property allocationPolicy The allocation policy of the scheduler.
- * @property partitions The partition of the scenario.
- */
-public data class Scenario(
- val topology: Topology,
- val workload: Workload,
- val operationalPhenomena: OperationalPhenomena,
- val allocationPolicy: String,
- val partitions: Map<String, String> = emptyMap(),
-)
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.decodeFromStream
+import java.io.File
+import java.io.InputStream
+
+public class ScenarioReader {
+ @OptIn(ExperimentalSerializationApi::class)
+ public fun read(file: File): ScenarioSpec {
+ val input = file.inputStream()
+ val obj = Json.decodeFromStream<ScenarioSpec>(input)
+
+ return obj
+ }
+
+ /**
+ * Read the specified [input].
+ */
+ @OptIn(ExperimentalSerializationApi::class)
+ public fun read(input: InputStream): ScenarioSpec {
+ val obj = Json.decodeFromStream<ScenarioSpec>(input)
+ return obj
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioSpecs.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioSpecs.kt
new file mode 100644
index 00000000..20c8a6e0
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioSpecs.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2024 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.experiments.base.models.scenario
+
+import kotlinx.serialization.Serializable
+import org.opendc.compute.service.scheduler.ComputeSchedulerEnum
+import org.opendc.compute.workload.ComputeWorkload
+import org.opendc.compute.workload.sampleByLoad
+import org.opendc.compute.workload.trace
+import java.io.File
+
+/**
+ * specification describing a scenario
+ *
+ * @property topology
+ * @property workload
+ * @property allocationPolicy
+ * @property failureModel
+ * @property exportModel
+ * @property outputFolder
+ * @property initialSeed
+ * @property runs
+ */
+@Serializable
+public data class ScenarioSpec(
+ val topology: TopologySpec,
+ val workload: WorkloadSpec,
+ val allocationPolicy: AllocationPolicySpec,
+ val failureModel: FailureModelSpec = FailureModelSpec(),
+ val exportModel: ExportSpec = ExportSpec(),
+ val outputFolder: String = "output",
+ val initialSeed: Int = 0,
+ val runs: Int = 1,
+ var name: String = "",
+) {
+ init {
+ require(runs > 0) { "The number of runs should always be positive" }
+
+ // generate name if not provided
+ if (name == "") {
+ name = "workload=${workload.name}_topology=${topology.name}_allocationPolicy=${allocationPolicy.name}"
+ }
+ }
+}
+
+/**
+ * specification describing a topology
+ *
+ * @property pathToFile
+ */
+@Serializable
+public data class TopologySpec(
+ val pathToFile: String,
+) {
+ public val name: String = File(pathToFile).nameWithoutExtension
+
+ init {
+ require(File(pathToFile).exists()) { "The provided path to the topology: $pathToFile does not exist " }
+ }
+}
+
+/**
+ * specification describing a workload
+ *
+ * @property pathToFile
+ * @property type
+ */
+@Serializable
+public data class WorkloadSpec(
+ val pathToFile: String,
+ val type: WorkloadTypes,
+) {
+ public val name: String = File(pathToFile).nameWithoutExtension
+
+ init {
+ require(File(pathToFile).exists()) { "The provided path to the workload: $pathToFile does not exist " }
+ }
+}
+
+/**
+ * specification describing a workload type
+ *
+ * @constructor Create empty Workload types
+ */
+public enum class WorkloadTypes {
+ /**
+ * Compute workload
+ *
+ * @constructor Create empty Compute workload
+ */
+ ComputeWorkload,
+}
+
+/**
+ *
+ *TODO: move to separate file
+ * @param type
+ */
+public fun getWorkloadType(type: WorkloadTypes): ComputeWorkload {
+ return when (type) {
+ WorkloadTypes.ComputeWorkload -> trace("trace").sampleByLoad(1.0)
+ }
+}
+
+/**
+ * specification describing how tasks are allocated
+ *
+ * @property policyType
+ *
+ * TODO: expand with more variables such as allowed over-subscription
+ */
+@Serializable
+public data class AllocationPolicySpec(
+ val policyType: ComputeSchedulerEnum,
+) {
+ public val name: String = policyType.toString()
+}
+
+/**
+ * specification describing the failure model
+ *
+ * @property failureInterval The interval between failures in s. Should be 0.0 or higher
+ */
+@Serializable
+public data class FailureModelSpec(
+ val failureInterval: Double = 0.0,
+) {
+ init {
+ require(failureInterval >= 0.0) { "failure frequency cannot be lower than 0" }
+ }
+}
+
+/**
+ * specification describing how the results should be exported
+ *
+ * @property exportInterval The interval of exporting results in s. Should be higher than 0.0
+ */
+@Serializable
+public data class ExportSpec(
+ val exportInterval: Long = 5 * 60,
+) {
+ init {
+ require(exportInterval > 0) { "The Export interval has to be higher than 0" }
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/TraceHelpers.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioHelpers.kt
index ddfa35cc..a6a05d78 100644
--- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/TraceHelpers.kt
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioHelpers.kt
@@ -77,7 +77,6 @@ public class RunningServerWatcher : ServerWatcher {
* @param seed The seed to use for randomness.
* @param submitImmediately A flag to indicate that the servers are scheduled immediately (so not at their start time).
* @param failureModel A failure model to use for injecting failures.
- * @param interference A flag to indicate that VM interference needs to be enabled.
*/
public suspend fun ComputeService.replay(
clock: InstantSource,
@@ -85,7 +84,6 @@ public suspend fun ComputeService.replay(
seed: Long,
submitImmediately: Boolean = false,
failureModel: FailureModel? = null,
- interference: Boolean = false,
) {
val injector = failureModel?.createInjector(coroutineContext, clock, this, Random(seed))
val client = newClient()
@@ -120,11 +118,6 @@ public suspend fun ComputeService.replay(
val workload = entry.trace.createWorkload(start)
val meta = mutableMapOf<String, Any>("workload" to workload)
- val interferenceProfile = entry.interferenceProfile
- if (interference && interferenceProfile != null) {
- meta["interference-profile"] = interferenceProfile
- }
-
launch {
val server =
client.newServer(
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioRunner.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioRunner.kt
new file mode 100644
index 00000000..3dce2bf1
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioRunner.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.experiments.base.runner
+
+import me.tongfei.progressbar.ProgressBarBuilder
+import me.tongfei.progressbar.ProgressBarStyle
+import org.opendc.compute.service.ComputeService
+import org.opendc.compute.service.scheduler.ComputeSchedulerEnum
+import org.opendc.compute.service.scheduler.createComputeScheduler
+import org.opendc.compute.simulator.provisioner.Provisioner
+import org.opendc.compute.simulator.provisioner.registerComputeMonitor
+import org.opendc.compute.simulator.provisioner.setupComputeService
+import org.opendc.compute.simulator.provisioner.setupHosts
+import org.opendc.compute.telemetry.export.parquet.ParquetComputeMonitor
+import org.opendc.compute.workload.ComputeWorkloadLoader
+import org.opendc.experiments.base.models.portfolio.Portfolio
+import org.opendc.experiments.base.models.scenario.Scenario
+import org.opendc.experiments.base.models.scenario.getWorkloadType
+import org.opendc.simulator.kotlin.runSimulation
+import java.io.File
+import java.time.Duration
+import java.util.Random
+import java.util.concurrent.ForkJoinPool
+import java.util.stream.LongStream
+
+public fun runPortfolio(
+ portfolio: Portfolio,
+ parallelism: Int,
+) {
+ val pool = ForkJoinPool(parallelism)
+
+ for (scenario in portfolio.scenarios) {
+ runScenario(scenario, pool)
+ }
+}
+
+/**
+ * Run scenario when no pool is available for parallel execution
+ *
+ * @param scenario The scenario to run
+ * @param parallelism The number of scenarios that can be run in parallel
+ */
+public fun runScenario(
+ scenario: Scenario,
+ parallelism: Int,
+) {
+ val pool = ForkJoinPool(parallelism)
+ runScenario(scenario, pool)
+}
+
+/**
+ * Run scenario when a pool is available for parallel execution
+ * The scenario is run multiple times based on the user input
+ *
+ * @param scenario The scenario to run
+ * @param pool The pool on which to run the scenarios
+ */
+public fun runScenario(
+ scenario: Scenario,
+ pool: ForkJoinPool,
+) {
+ val pb =
+ ProgressBarBuilder()
+ .setInitialMax(scenario.runs.toLong())
+ .setStyle(ProgressBarStyle.ASCII)
+ .setTaskName("Simulating...")
+ .build()
+
+ pool.submit {
+ LongStream.range(0, scenario.runs.toLong())
+ .parallel()
+ .forEach {
+ runScenario(scenario, scenario.initialSeed + it)
+ pb.step()
+ }
+
+ pb.close()
+ }.join()
+}
+
+/**
+ * Run a single scenario with a specific seed
+ *
+ * @param scenario The scenario to run
+ * @param seed The starting seed of the random generator.
+ */
+public fun runScenario(
+ scenario: Scenario,
+ seed: Long,
+): Unit =
+ runSimulation {
+ val serviceDomain = "compute.opendc.org"
+
+ Provisioner(dispatcher, seed).use { provisioner ->
+
+ provisioner.runSteps(
+ setupComputeService(serviceDomain, { createComputeScheduler(ComputeSchedulerEnum.Mem, Random(it.seeder.nextLong())) }),
+ setupHosts(serviceDomain, scenario.topology, optimize = true),
+ )
+
+ val partition = scenario.name + "/seed=$seed"
+
+ provisioner.runStep(
+ registerComputeMonitor(
+ serviceDomain,
+ ParquetComputeMonitor(
+ File(scenario.outputFolder),
+ partition,
+ bufferSize = 4096,
+ ),
+ Duration.ofSeconds(scenario.exportModel.exportInterval),
+ ),
+ )
+
+ val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!!
+
+ val workloadLoader = ComputeWorkloadLoader(File(scenario.workload.pathToFile))
+ val vms = getWorkloadType(scenario.workload.type).resolve(workloadLoader, Random(seed))
+
+ service.replay(timeSource, vms, seed, failureModel = scenario.failureModel)
+ }
+ }
diff --git a/opendc-experiments/opendc-experiments-base/src/main/output/host/seed=0/data.parquet b/opendc-experiments/opendc-experiments-base/src/main/output/host/seed=0/data.parquet
deleted file mode 100644
index d3c19ab4..00000000
--- a/opendc-experiments/opendc-experiments-base/src/main/output/host/seed=0/data.parquet
+++ /dev/null
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-base/src/main/output/server/seed=0/data.parquet b/opendc-experiments/opendc-experiments-base/src/main/output/server/seed=0/data.parquet
deleted file mode 100644
index 6049e8cb..00000000
--- a/opendc-experiments/opendc-experiments-base/src/main/output/server/seed=0/data.parquet
+++ /dev/null
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-base/src/main/output/service/seed=0/data.parquet b/opendc-experiments/opendc-experiments-base/src/main/output/service/seed=0/data.parquet
deleted file mode 100644
index 969954bb..00000000
--- a/opendc-experiments/opendc-experiments-base/src/main/output/service/seed=0/data.parquet
+++ /dev/null
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt b/opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/ScenarioIntegrationTest.kt
index 9a00c80e..d67ed727 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt
+++ b/opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/ScenarioIntegrationTest.kt
@@ -20,7 +20,7 @@
* SOFTWARE.
*/
-package org.opendc.experiments.capelin
+package org.opendc.experiments.base
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
@@ -32,7 +32,7 @@ import org.opendc.compute.service.scheduler.filters.ComputeFilter
import org.opendc.compute.service.scheduler.filters.RamFilter
import org.opendc.compute.service.scheduler.filters.VCpuFilter
import org.opendc.compute.service.scheduler.weights.CoreRamWeigher
-import org.opendc.compute.simulator.failure.grid5000
+import org.opendc.compute.simulator.failure.getFailureModel
import org.opendc.compute.simulator.provisioner.Provisioner
import org.opendc.compute.simulator.provisioner.registerComputeMonitor
import org.opendc.compute.simulator.provisioner.setupComputeService
@@ -49,13 +49,12 @@ import org.opendc.compute.workload.trace
import org.opendc.experiments.base.runner.replay
import org.opendc.simulator.kotlin.runSimulation
import java.io.File
-import java.time.Duration
import java.util.Random
/**
- * An integration test suite for the Capelin experiments.
+ * An integration test suite for the Scenario experiments.
*/
-class CapelinIntegrationTest {
+class ScenarioIntegrationTest {
/**
* The monitor used to keep track of the metrics.
*/
@@ -93,7 +92,7 @@ class CapelinIntegrationTest {
runSimulation {
val seed = 0L
val workload = createTestWorkload(1.0, seed)
- val topology = createTopology()
+ val topology = createTopology("multi.json")
val monitor = monitor
Provisioner(dispatcher, seed).use { provisioner ->
@@ -126,7 +125,8 @@ class CapelinIntegrationTest {
{ assertEquals(66977091124, monitor.activeTime) { "Incorrect active time" } },
{ assertEquals(3160267873, monitor.stealTime) { "Incorrect steal time" } },
{ assertEquals(0, monitor.lostTime) { "Incorrect lost time" } },
- { assertEquals(7.767237E9, monitor.energyUsage, 1E4) { "Incorrect power draw" } },
+ { assertEquals(2.5892214E7, monitor.powerDraw, 1E4) { "Incorrect power draw" } },
+ { assertEquals(7.7672373E9, monitor.energyUsage, 1E4) { "Incorrect energy usage" } },
)
}
@@ -167,14 +167,15 @@ class CapelinIntegrationTest {
{ assertEquals(9741285381, monitor.activeTime) { "Active time incorrect" } },
{ assertEquals(152, monitor.stealTime) { "Steal time incorrect" } },
{ assertEquals(0, monitor.lostTime) { "Lost time incorrect" } },
- { assertEquals(7.933686E8, monitor.energyUsage, 1E4) { "Incorrect power draw" } },
+ { assertEquals(2644612.0, monitor.powerDraw, 1E4) { "Incorrect power draw" } },
+ { assertEquals(7.9336867E8, monitor.energyUsage, 1E4) { "Incorrect energy usage" } },
)
}
/**
* Test a small simulation setup with interference.
+ * TODO: Interference is currently removed from OpenDC. Reactivate when interference is back in.
*/
- @Test
fun testInterference() =
runSimulation {
val seed = 0L
@@ -189,7 +190,7 @@ class CapelinIntegrationTest {
)
val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
- service.replay(timeSource, workload, seed, interference = true)
+ service.replay(timeSource, workload, seed)
}
println(
@@ -206,7 +207,7 @@ class CapelinIntegrationTest {
{ assertEquals(42814948316, monitor.idleTime) { "Idle time incorrect" } },
{ assertEquals(40138266225, monitor.activeTime) { "Active time incorrect" } },
{ assertEquals(23489356981, monitor.stealTime) { "Steal time incorrect" } },
- { assertEquals(424267131, monitor.lostTime) { "Lost time incorrect" } },
+ { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } },
)
}
@@ -229,7 +230,7 @@ class CapelinIntegrationTest {
)
val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
- service.replay(timeSource, workload, seed, failureModel = grid5000(Duration.ofDays(7)))
+ service.replay(timeSource, workload, seed, failureModel = getFailureModel(7.0 * 24 * 60 * 60))
}
// Note that these values have been verified beforehand
@@ -256,7 +257,7 @@ class CapelinIntegrationTest {
/**
* Obtain the topology factory for the test.
*/
- private fun createTopology(name: String = "topology.json"): List<HostSpec> {
+ private fun createTopology(name: String): List<HostSpec> {
val stream = checkNotNull(object {}.javaClass.getResourceAsStream("/env/$name"))
return stream.use { clusterTopology(stream) }
}
@@ -280,6 +281,7 @@ class CapelinIntegrationTest {
var activeTime = 0L
var stealTime = 0L
var lostTime = 0L
+ var powerDraw = 0.0
var energyUsage = 0.0
var uptime = 0L
@@ -288,6 +290,7 @@ class CapelinIntegrationTest {
activeTime += reader.cpuActiveTime
stealTime += reader.cpuStealTime
lostTime += reader.cpuLostTime
+ powerDraw += reader.powerDraw
energyUsage += reader.energyUsage
uptime += reader.uptime
}
diff --git a/opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/ScenarioRunnerTest.kt b/opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/ScenarioRunnerTest.kt
new file mode 100644
index 00000000..f10ab310
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/ScenarioRunnerTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.experiments.base
+
+import java.io.File
+
+/**
+ * Test suite for [ScenarioRunner].
+ */
+class ScenarioRunnerTest {
+ /**
+ * The path to the environments.
+ */
+ private val envPath = File("src/test/resources/env")
+
+ /**
+ * The path to the traces.
+ */
+ private val tracePath = File("src/test/resources/trace")
+
+ /**
+ * Smoke test with output.
+ * fixme: Fix failures and enable
+ *
+ fun testSmoke() {
+ val outputPath = Files.createTempDirectory("output").toFile()
+
+ try {
+ val runner = ScenarioRunner(envPath, tracePath, outputPath)
+ val scenario = Scenario(
+ Topology("topology"),
+ Workload("bitbrains-small", trace("bitbrains-small")),
+ OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
+ "active-servers"
+ )
+
+ assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) }
+ } finally {
+ outputPath.delete()
+ }
+ }
+
+ /**
+ * Smoke test without output.
+ * fixme: Fix failures and enable
+ */
+ fun testSmokeNoOutput() {
+ val runner = ScenarioRunner(envPath, tracePath, null)
+ val scenario = Scenario(
+ Topology("topology"),
+ Workload("bitbrains-small", trace("bitbrains-small")),
+ OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
+ "active-servers"
+ )
+
+ assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) }
+ }
+ */
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.json b/opendc-experiments/opendc-experiments-base/src/test/resources/env/multi.json
index 721005b0..721005b0 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.json
+++ b/opendc-experiments/opendc-experiments-base/src/test/resources/env/multi.json
diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/resources/env/single.json b/opendc-experiments/opendc-experiments-base/src/test/resources/env/single.json
index a1c8d95a..a1c8d95a 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/test/resources/env/single.json
+++ b/opendc-experiments/opendc-experiments-base/src/test/resources/env/single.json
diff --git a/opendc-experiments/opendc-experiments-capelin/.gitignore b/opendc-experiments/opendc-experiments-capelin/.gitignore
deleted file mode 100644
index ba64707c..00000000
--- a/opendc-experiments/opendc-experiments-capelin/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-input/
-output/
diff --git a/opendc-experiments/opendc-experiments-capelin/LICENSE.txt b/opendc-experiments/opendc-experiments-capelin/LICENSE.txt
deleted file mode 100644
index 3931600a..00000000
--- a/opendc-experiments/opendc-experiments-capelin/LICENSE.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2020 Georgios Andreadis, Fabian Mastenbroek
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/opendc-experiments/opendc-experiments-capelin/README.md b/opendc-experiments/opendc-experiments-capelin/README.md
deleted file mode 100644
index d86fe81d..00000000
--- a/opendc-experiments/opendc-experiments-capelin/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Capelin
-Data-Driven Compute Capacity Procurement for Cloud Datacenters Using Portfolios of Scenarios.
-
-See the [publication](https://arxiv.org/abs/2103.02060) and
-[thesis](https://repository.tudelft.nl/islandora/object/uuid:d6d50861-86a3-4dd3-a13f-42d84db7af66?collection=education) for information.
-
-## License
-
-OpenDC and Capelin are distributed under the MIT license. See [LICENSE-OpenDC.txt](/LICENSE.txt) and [LICENSE.txt](LICENSE.txt).
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt
deleted file mode 100644
index 5bec8c6d..00000000
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-@file:JvmName("CapelinCli")
-
-package org.opendc.experiments.capelin
-
-import com.github.ajalt.clikt.core.CliktCommand
-import com.github.ajalt.clikt.parameters.arguments.argument
-import com.github.ajalt.clikt.parameters.options.associate
-import com.github.ajalt.clikt.parameters.options.default
-import com.github.ajalt.clikt.parameters.options.defaultLazy
-import com.github.ajalt.clikt.parameters.options.flag
-import com.github.ajalt.clikt.parameters.options.option
-import com.github.ajalt.clikt.parameters.types.choice
-import com.github.ajalt.clikt.parameters.types.file
-import com.github.ajalt.clikt.parameters.types.int
-import com.github.ajalt.clikt.parameters.types.long
-import me.tongfei.progressbar.ProgressBarBuilder
-import me.tongfei.progressbar.ProgressBarStyle
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.capelin.portfolios.CompositeWorkloadPortfolio
-import org.opendc.experiments.capelin.portfolios.HorVerPortfolio
-import org.opendc.experiments.capelin.portfolios.MoreHpcPortfolio
-import org.opendc.experiments.capelin.portfolios.MoreVelocityPortfolio
-import org.opendc.experiments.capelin.portfolios.OperationalPhenomenaPortfolio
-import org.opendc.experiments.capelin.portfolios.TestPortfolio
-import java.io.File
-import java.util.concurrent.ForkJoinPool
-import java.util.stream.LongStream
-
-/**
- * Main entrypoint of the application.
- */
-fun main(args: Array<String>): Unit = CapelinCommand().main(args)
-
-/**
- * Represents the command for the Capelin experiments.
- */
-internal class CapelinCommand : CliktCommand(name = "capelin") {
- /**
- * The path to the environment directory.
- */
- private val envPath by option("--env-path", help = "path to environment directory")
- .file(canBeDir = true, canBeFile = false)
- .defaultLazy { File("input/environments") }
-
- /**
- * The path to the trace directory.
- */
- private val tracePath by option("--trace-path", help = "path to trace directory")
- .file(canBeDir = true, canBeFile = false)
- .defaultLazy { File("input/traces") }
-
- /**
- * The path to the experiment output.
- */
- private val outputPath by option("-O", "--output", help = "path to experiment output")
- .file(canBeDir = true, canBeFile = false)
- .defaultLazy { File("output") }
-
- /**
- * Disable writing output.
- */
- private val disableOutput by option("--disable-output", help = "disable output").flag()
-
- /**
- * The number of threads to use for parallelism.
- */
- private val parallelism by option("-p", "--parallelism", help = "number of worker threads")
- .int()
- .default(Runtime.getRuntime().availableProcessors() - 1)
-
- /**
- * The number of repeats.
- */
- private val repeats by option("-r", "--repeats", help = "number of repeats")
- .int()
- .default(128)
-
- /**
- * The seed for seeding the random instances.
- */
- private val seed by option("-s", "--seed", help = "initial seed for randomness")
- .long()
- .default(0)
-
- /**
- * The portfolio to replay.
- */
- private val portfolio by argument(help = "portfolio to replay")
- .choice(
- "test" to { TestPortfolio() },
- "composite-workload" to { CompositeWorkloadPortfolio() },
- "hor-ver" to { HorVerPortfolio() },
- "more-hpc" to { MoreHpcPortfolio() },
- "more-velocity" to { MoreVelocityPortfolio() },
- "op-phen" to { OperationalPhenomenaPortfolio() },
- )
-
- /**
- * The base partitions to use for the invocation
- */
- private val basePartitions: Map<String, String> by option("-P", "--base-partitions").associate()
-
- override fun run() {
- val runner = CapelinRunner(envPath, tracePath, outputPath.takeUnless { disableOutput })
- val scenarios = portfolio().scenarios.toList()
-
- val pool = ForkJoinPool(parallelism)
-
- echo("Detected ${scenarios.size} scenarios [$repeats replications]")
-
- for (scenario in scenarios) {
- runScenario(runner, pool, scenario)
- }
-
- pool.shutdown()
- }
-
- /**
- * Run a single scenario.
- */
- private fun runScenario(
- runner: CapelinRunner,
- pool: ForkJoinPool,
- scenario: Scenario,
- ) {
- val pb =
- ProgressBarBuilder()
- .setInitialMax(repeats.toLong())
- .setStyle(ProgressBarStyle.ASCII)
- .setTaskName("Simulating...")
- .build()
-
- pool.submit {
- LongStream.range(0, repeats.toLong())
- .parallel()
- .forEach { repeat ->
- val augmentedScenario = scenario.copy(partitions = basePartitions + scenario.partitions)
- runner.runScenario(augmentedScenario, seed + repeat)
- pb.step()
- }
-
- pb.close()
- }.join()
- }
-}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt
deleted file mode 100644
index 0de72afa..00000000
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.capelin
-
-import org.opendc.compute.service.ComputeService
-import org.opendc.compute.service.scheduler.createComputeScheduler
-import org.opendc.compute.simulator.failure.grid5000
-import org.opendc.compute.simulator.provisioner.Provisioner
-import org.opendc.compute.simulator.provisioner.registerComputeMonitor
-import org.opendc.compute.simulator.provisioner.setupComputeService
-import org.opendc.compute.simulator.provisioner.setupHosts
-import org.opendc.compute.telemetry.export.parquet.ParquetComputeMonitor
-import org.opendc.compute.topology.clusterTopology
-import org.opendc.compute.workload.ComputeWorkloadLoader
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.runner.replay
-import org.opendc.simulator.kotlin.runSimulation
-import java.io.File
-import java.time.Duration
-import java.util.Random
-import kotlin.math.roundToLong
-
-/**
- * Helper class for running the Capelin experiments.
- *
- * @param envPath The path to the directory containing the environments.
- * @param tracePath The path to the directory containing the traces.
- * @param outputPath The path to the directory where the output should be written (or `null` if no output should be generated).
- */
-public class CapelinRunner(
- private val envPath: File,
- tracePath: File,
- private val outputPath: File?,
-) {
- /**
- * The [ComputeWorkloadLoader] to use for loading the traces.
- */
- private val workloadLoader = ComputeWorkloadLoader(tracePath)
-
- /**
- * Run a single [scenario] with the specified seed.
- */
- fun runScenario(
- scenario: Scenario,
- seed: Long,
- ) = runSimulation {
- val serviceDomain = "compute.opendc.org"
- val topology = clusterTopology(File(envPath, "${scenario.topology.name}.txt"))
-
- Provisioner(dispatcher, seed).use { provisioner ->
- provisioner.runSteps(
- setupComputeService(serviceDomain, { createComputeScheduler(scenario.allocationPolicy, Random(it.seeder.nextLong())) }),
- setupHosts(serviceDomain, topology, optimize = true),
- )
-
- if (outputPath != null) {
- val partitions = scenario.partitions + ("seed" to seed.toString())
- val partition = partitions.map { (k, v) -> "$k=$v" }.joinToString("/")
-
- provisioner.runStep(
- registerComputeMonitor(
- serviceDomain,
- ParquetComputeMonitor(
- outputPath,
- partition,
- bufferSize = 4096,
- ),
- ),
- )
- }
-
- val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!!
- val vms = scenario.workload.source.resolve(workloadLoader, Random(seed))
- val operationalPhenomena = scenario.operationalPhenomena
- val failureModel =
- if (operationalPhenomena.failureFrequency > 0) {
- grid5000(Duration.ofSeconds((operationalPhenomena.failureFrequency * 60).roundToLong()))
- } else {
- null
- }
-
- service.replay(timeSource, vms, seed, failureModel = failureModel, interference = operationalPhenomena.hasInterference)
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/CompositeWorkloadPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/CompositeWorkloadPortfolio.kt
deleted file mode 100644
index 140f0480..00000000
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/CompositeWorkloadPortfolio.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.capelin.portfolios
-
-import org.opendc.compute.workload.composite
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.Portfolio
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
-
-/**
- * A [Portfolio] that explores the effect of a composite workload.
- */
-public class CompositeWorkloadPortfolio : Portfolio {
- private val topologies =
- listOf(
- Topology("base"),
- Topology("exp-vol-hor-hom"),
- Topology("exp-vol-ver-hom"),
- Topology("exp-vel-ver-hom"),
- )
- private val workloads =
- listOf(
- Workload(
- "all-azure",
- composite(trace("solvinity-short") to 0.0, trace("azure") to 1.0),
- ),
- Workload(
- "solvinity-25-azure-75",
- composite(trace("solvinity-short") to 0.25, trace("azure") to 0.75),
- ),
- Workload(
- "solvinity-50-azure-50",
- composite(trace("solvinity-short") to 0.5, trace("azure") to 0.5),
- ),
- Workload(
- "solvinity-75-azure-25",
- composite(trace("solvinity-short") to 0.75, trace("azure") to 0.25),
- ),
- Workload(
- "all-solvinity",
- composite(trace("solvinity-short") to 1.0, trace("azure") to 0.0),
- ),
- )
- private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = false)
- private val allocationPolicy = "active-servers"
-
- override val scenarios: Iterable<Scenario> =
- topologies.flatMap { topology ->
- workloads.map { workload ->
- Scenario(
- topology,
- workload,
- operationalPhenomena,
- allocationPolicy,
- mapOf("topology" to topology.name, "workload" to workload.name),
- )
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/HorVerPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/HorVerPortfolio.kt
deleted file mode 100644
index da884f35..00000000
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/HorVerPortfolio.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.capelin.portfolios
-
-import org.opendc.compute.workload.sampleByLoad
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.Portfolio
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
-
-/**
- * A [Portfolio] that explores the difference between horizontal and vertical scaling.
- */
-public class HorVerPortfolio : Portfolio {
- private val topologies =
- listOf(
- Topology("base"),
- Topology("rep-vol-hor-hom"),
- Topology("rep-vol-hor-het"),
- Topology("rep-vol-ver-hom"),
- Topology("rep-vol-ver-het"),
- Topology("exp-vol-hor-hom"),
- Topology("exp-vol-hor-het"),
- Topology("exp-vol-ver-hom"),
- Topology("exp-vol-ver-het"),
- )
-
- private val workloads =
- listOf(
- Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)),
- Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)),
- Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)),
- Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)),
- )
- private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true)
- private val allocationPolicy = "active-servers"
-
- override val scenarios: Iterable<Scenario> =
- topologies.flatMap { topology ->
- workloads.map { workload ->
- Scenario(
- topology,
- workload,
- operationalPhenomena,
- allocationPolicy,
- mapOf("topology" to topology.name, "workload" to workload.name),
- )
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreHpcPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreHpcPortfolio.kt
deleted file mode 100644
index e060ff14..00000000
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreHpcPortfolio.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.capelin.portfolios
-
-import org.opendc.compute.workload.sampleByHpc
-import org.opendc.compute.workload.sampleByHpcLoad
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.Portfolio
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
-
-/**
- * A [Portfolio] to explore the effect of HPC workloads.
- */
-public class MoreHpcPortfolio : Portfolio {
- private val topologies =
- listOf(
- Topology("base"),
- Topology("exp-vol-hor-hom"),
- Topology("exp-vol-ver-hom"),
- Topology("exp-vel-ver-hom"),
- )
- private val workloads =
- listOf(
- Workload("hpc-0%", trace("solvinity").sampleByHpc(0.0)),
- Workload("hpc-25%", trace("solvinity").sampleByHpc(0.25)),
- Workload("hpc-50%", trace("solvinity").sampleByHpc(0.5)),
- Workload("hpc-100%", trace("solvinity").sampleByHpc(1.0)),
- Workload("hpc-load-25%", trace("solvinity").sampleByHpcLoad(0.25)),
- Workload("hpc-load-50%", trace("solvinity").sampleByHpcLoad(0.5)),
- Workload("hpc-load-100%", trace("solvinity").sampleByHpcLoad(1.0)),
- )
-
- private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true)
- private val allocationPolicy: String = "active-servers"
-
- override val scenarios: Iterable<Scenario> =
- topologies.flatMap { topology ->
- workloads.map { workload ->
- Scenario(
- topology,
- workload,
- operationalPhenomena,
- allocationPolicy,
- mapOf("topology" to topology.name, "workload" to workload.name),
- )
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreVelocityPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreVelocityPortfolio.kt
deleted file mode 100644
index 0d6e190c..00000000
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/MoreVelocityPortfolio.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.capelin.portfolios
-
-import org.opendc.compute.workload.sampleByLoad
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.Portfolio
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
-
-/**
- * A [Portfolio] that explores the effect of adding more velocity to a cluster (e.g., faster machines).
- */
-public class MoreVelocityPortfolio : Portfolio {
- private val topologies =
- listOf(
- Topology("base"),
- Topology("rep-vel-ver-hom"),
- Topology("rep-vel-ver-het"),
- Topology("exp-vel-ver-hom"),
- Topology("exp-vel-ver-het"),
- )
-
- private val workloads =
- listOf(
- Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)),
- Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)),
- Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)),
- Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)),
- )
-
- private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true)
- private val allocationPolicy = "active-servers"
-
- override val scenarios: Iterable<Scenario> =
- topologies.flatMap { topology ->
- workloads.map { workload ->
- Scenario(
- topology,
- workload,
- operationalPhenomena,
- allocationPolicy,
- mapOf("topology" to topology.name, "workload" to workload.name),
- )
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/OperationalPhenomenaPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/OperationalPhenomenaPortfolio.kt
deleted file mode 100644
index 17c8bb48..00000000
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolios/OperationalPhenomenaPortfolio.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.capelin.portfolios
-
-import org.opendc.compute.workload.sampleByLoad
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.Portfolio
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
-
-/**
- * A [Portfolio] that explores the effect of operational phenomena on metrics.
- */
-public class OperationalPhenomenaPortfolio : Portfolio {
- private val topology = Topology("base")
- private val workloads =
- listOf(
- Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)),
- Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)),
- Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)),
- Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)),
- )
-
- private val phenomenas =
- listOf(
- OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
- OperationalPhenomena(failureFrequency = 0.0, hasInterference = true),
- OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = false),
- OperationalPhenomena(failureFrequency = 0.0, hasInterference = false),
- )
-
- private val allocationPolicies =
- listOf(
- "mem",
- "mem-inv",
- "core-mem",
- "core-mem-inv",
- "active-servers",
- "active-servers-inv",
- "random",
- )
-
- override val scenarios: Iterable<Scenario> =
- workloads.flatMap { workload ->
- phenomenas.flatMapIndexed { index, operationalPhenomena ->
- allocationPolicies.map { allocationPolicy ->
- Scenario(
- topology,
- workload,
- operationalPhenomena,
- allocationPolicy,
- mapOf("workload" to workload.name, "scheduler" to allocationPolicy, "phenomena" to index.toString()),
- )
- }
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinRunnerTest.kt b/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinRunnerTest.kt
deleted file mode 100644
index 4587f6dc..00000000
--- a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinRunnerTest.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.capelin
-
-import org.junit.jupiter.api.assertDoesNotThrow
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
-import java.io.File
-import java.nio.file.Files
-
-/**
- * Test suite for [CapelinRunner].
- */
-class CapelinRunnerTest {
- /**
- * The path to the environments.
- */
- private val envPath = File("src/test/resources/env")
-
- /**
- * The path to the traces.
- */
- private val tracePath = File("src/test/resources/trace")
-
- /**
- * Smoke test with output.
- * fixme: Fix failures and enable
- */
- fun testSmoke() {
- val outputPath = Files.createTempDirectory("output").toFile()
-
- try {
- val runner = CapelinRunner(envPath, tracePath, outputPath)
- val scenario =
- Scenario(
- Topology("topology"),
- Workload("bitbrains-small", trace("bitbrains-small")),
- OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
- "active-servers",
- )
-
- assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) }
- } finally {
- outputPath.delete()
- }
- }
-
- /**
- * Smoke test without output.
- * fixme: Fix failures and enable
- */
- fun testSmokeNoOutput() {
- val runner = CapelinRunner(envPath, tracePath, null)
- val scenario =
- Scenario(
- Topology("topology"),
- Workload("bitbrains-small", trace("bitbrains-small")),
- OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
- "active-servers",
- )
-
- assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt
deleted file mode 100644
index 93557500..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-@file:JvmName("GreenifierCli")
-
-package org.opendc.experiments.greenifier
-
-import com.github.ajalt.clikt.core.CliktCommand
-import com.github.ajalt.clikt.parameters.arguments.argument
-import com.github.ajalt.clikt.parameters.arguments.default
-import com.github.ajalt.clikt.parameters.options.associate
-import com.github.ajalt.clikt.parameters.options.default
-import com.github.ajalt.clikt.parameters.options.defaultLazy
-import com.github.ajalt.clikt.parameters.options.flag
-import com.github.ajalt.clikt.parameters.options.option
-import com.github.ajalt.clikt.parameters.types.choice
-import com.github.ajalt.clikt.parameters.types.file
-import com.github.ajalt.clikt.parameters.types.int
-import com.github.ajalt.clikt.parameters.types.long
-import me.tongfei.progressbar.ProgressBarBuilder
-import me.tongfei.progressbar.ProgressBarStyle
-import org.opendc.experiments.base.portfolio.model.Scenario
-import java.io.File
-import java.util.concurrent.ForkJoinPool
-import java.util.stream.LongStream
-
-/**
- * Main entrypoint of the application.
- */
-fun main(args: Array<String>): Unit = GreenifierCommand().main(args)
-
-/**
- * Represents the command for the Greenifier experiments.
- */
-internal class GreenifierCommand : CliktCommand(name = "greenifier") {
- /**
- * The path to the environment directory.
- */
- private val envPath by option("--env-path", help = "path to environment directory")
- .file(canBeDir = true, canBeFile = false)
- .defaultLazy { File("input/environments") }
-
- /**
- * The path to the trace directory.
- */
- private val tracePath by option("--trace-path", help = "path to trace directory")
- .file(canBeDir = true, canBeFile = false)
- .defaultLazy { File("input/traces") }
-
- /**
- * The path to the experiment output.
- */
- private val outputPath by option("-O", "--output", help = "path to experiment output")
- .file(canBeDir = true, canBeFile = false)
- .defaultLazy { File("output") }
-
- /**
- * Disable writing output.
- */
- private val disableOutput by option("--disable-output", help = "disable output").flag()
-
- /**
- * The number of threads to use for parallelism.
- */
- private val parallelism by option("-p", "--parallelism", help = "number of worker threads")
- .int()
- .default(Runtime.getRuntime().availableProcessors() - 1)
-
- /**
- * The number of repeats.
- */
- private val repeats by option("-r", "--repeats", help = "number of repeats")
- .int()
- .default(128)
-
- /**
- * The seed for seeding the random instances.
- */
- private val seed by option("-s", "--seed", help = "initial seed for randomness")
- .long()
- .default(0)
-
- /**
- * The portfolio to replay.
- */
- private val portfolio by argument(help = "portfolio to replay")
- .choice(
- "greenifier" to { GreenifierPortfolio() },
- )
- .default({ GreenifierPortfolio() })
-
- /**
- * The base partitions to use for the invocation
- */
- private val basePartitions: Map<String, String> by option("-P", "--base-partitions").associate()
-
- override fun run() {
- val runner = GreenifierRunner(envPath, tracePath, outputPath.takeUnless { disableOutput })
- val scenarios = portfolio().scenarios.toList()
-
- val pool = ForkJoinPool(parallelism)
-
- echo("Detected ${scenarios.size} scenarios [$repeats replications]")
-
- for (scenario in scenarios) {
- runScenario(runner, pool, scenario)
- }
-
- pool.shutdown()
- }
-
- /**
- * Run a single scenario.
- */
- private fun runScenario(
- runner: GreenifierRunner,
- pool: ForkJoinPool,
- scenario: Scenario,
- ) {
- val pb =
- ProgressBarBuilder()
- .setInitialMax(repeats.toLong())
- .setStyle(ProgressBarStyle.ASCII)
- .setTaskName("Simulating...")
- .build()
-
- pool.submit {
- LongStream.range(0, repeats.toLong())
- .parallel()
- .forEach { repeat ->
- val augmentedScenario = scenario.copy(partitions = basePartitions + scenario.partitions)
- runner.runScenario(augmentedScenario, seed + repeat)
- pb.step()
- }
-
- pb.close()
- }.join()
- }
-}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierPortfolio.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierPortfolio.kt
deleted file mode 100644
index f7452be2..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierPortfolio.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.greenifier
-
-import org.opendc.compute.workload.sampleByLoad
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.Portfolio
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
-
-/**
- * A [Portfolio] that explores the difference between horizontal and vertical scaling.
- */
-public class GreenifierPortfolio : Portfolio {
- private val topologies =
- listOf(
- Topology("single.json"),
- Topology("multi.json"),
- )
-
- private val workloads =
- listOf(
- Workload("bitbrains-small", trace("trace").sampleByLoad(1.0)),
- )
- private val operationalPhenomena = OperationalPhenomena(0.0, false)
- private val allocationPolicy = "active-servers"
-
- override val scenarios: Iterable<Scenario> =
- topologies.flatMap { topology ->
- workloads.map { workload ->
- Scenario(
- topology,
- workload,
- operationalPhenomena,
- allocationPolicy,
- mapOf("topology" to topology.name, "workload" to workload.name),
- )
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt
deleted file mode 100644
index bd855aac..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.greenifier
-
-import org.opendc.compute.service.ComputeService
-import org.opendc.compute.service.scheduler.createComputeScheduler
-import org.opendc.compute.simulator.failure.grid5000
-import org.opendc.compute.simulator.provisioner.Provisioner
-import org.opendc.compute.simulator.provisioner.registerComputeMonitor
-import org.opendc.compute.simulator.provisioner.setupComputeService
-import org.opendc.compute.simulator.provisioner.setupHosts
-import org.opendc.compute.telemetry.export.parquet.ParquetComputeMonitor
-import org.opendc.compute.topology.clusterTopology
-import org.opendc.compute.workload.ComputeWorkloadLoader
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.runner.replay
-import org.opendc.simulator.kotlin.runSimulation
-import java.io.File
-import java.time.Duration
-import java.util.Random
-import kotlin.math.roundToLong
-
-/**
- * Helper class for running the Greenifier experiments.
- *
- * @param envPath The path to the directory containing the environments.
- * @param tracePath The path to the directory containing the traces.
- * @param outputPath The path to the directory where the output should be written (or `null` if no output should be generated).
- */
-public class GreenifierRunner(
- private val envPath: File,
- tracePath: File,
- private val outputPath: File?,
-) {
- /**
- * The [ComputeWorkloadLoader] to use for loading the traces.
- */
- private val workloadLoader = ComputeWorkloadLoader(tracePath)
-
- /**
- * Run a single [scenario] with the specified seed.
- */
- fun runScenario(
- scenario: Scenario,
- seed: Long,
- ) = runSimulation {
- val serviceDomain = "compute.opendc.org"
- val topology = clusterTopology(File(envPath, scenario.topology.name))
-
- Provisioner(dispatcher, seed).use { provisioner ->
- provisioner.runSteps(
- setupComputeService(serviceDomain, { createComputeScheduler(scenario.allocationPolicy, Random(it.seeder.nextLong())) }),
- setupHosts(serviceDomain, topology, optimize = true),
- )
-
- if (outputPath != null) {
- val partitions = scenario.partitions + ("seed" to seed.toString())
- val partition = partitions.map { (k, v) -> "$k=$v" }.joinToString("/")
-
- provisioner.runStep(
- registerComputeMonitor(
- serviceDomain,
- ParquetComputeMonitor(
- outputPath,
- partition,
- bufferSize = 4096,
- ),
- ),
- )
- }
-
- val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!!
- val vms = scenario.workload.source.resolve(workloadLoader, Random(seed))
- val operationalPhenomena = scenario.operationalPhenomena
- val failureModel =
- if (operationalPhenomena.failureFrequency > 0) {
- grid5000(Duration.ofSeconds((operationalPhenomena.failureFrequency * 60).roundToLong()))
- } else {
- null
- }
-
- service.replay(timeSource, vms, seed, failureModel = failureModel, interference = operationalPhenomena.hasInterference)
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/benchmark/trace/meta.parquet b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/benchmark/trace/meta.parquet
deleted file mode 100644
index 2ca31107..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/benchmark/trace/meta.parquet
+++ /dev/null
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/benchmark/trace/trace.parquet b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/benchmark/trace/trace.parquet
deleted file mode 100644
index 34fa1c0c..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/benchmark/trace/trace.parquet
+++ /dev/null
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.txt b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.txt
deleted file mode 100644
index 6b347bff..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-ClusterID;ClusterName;Cores;Speed;Memory;numberOfHosts;memoryCapacityPerHost;coreCountPerHost
-A01;A01;32;3.2;2048;1;256;32
-B01;B01;48;2.93;1256;6;64;8
-C01;C01;32;3.2;2048;2;128;16
-
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.txt b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.txt
deleted file mode 100644
index 7c641f4b..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ClusterID;ClusterName;Cores;Speed;Memory;numberOfHosts;memoryCapacityPerHost;coreCountPerHost
-A01;A01;8;1;100;1;100;8
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierIntegrationTest.kt b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierIntegrationTest.kt
deleted file mode 100644
index 15f6cdf6..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierIntegrationTest.kt
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (c) 2020 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.greenifier
-
-import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.assertAll
-import org.opendc.compute.service.ComputeService
-import org.opendc.compute.service.scheduler.FilterScheduler
-import org.opendc.compute.service.scheduler.filters.ComputeFilter
-import org.opendc.compute.service.scheduler.filters.RamFilter
-import org.opendc.compute.service.scheduler.filters.VCpuFilter
-import org.opendc.compute.service.scheduler.weights.CoreRamWeigher
-import org.opendc.compute.simulator.failure.grid5000
-import org.opendc.compute.simulator.provisioner.Provisioner
-import org.opendc.compute.simulator.provisioner.registerComputeMonitor
-import org.opendc.compute.simulator.provisioner.setupComputeService
-import org.opendc.compute.simulator.provisioner.setupHosts
-import org.opendc.compute.telemetry.ComputeMonitor
-import org.opendc.compute.telemetry.table.HostTableReader
-import org.opendc.compute.telemetry.table.ServiceTableReader
-import org.opendc.compute.topology.clusterTopology
-import org.opendc.compute.topology.specs.HostSpec
-import org.opendc.compute.workload.ComputeWorkloadLoader
-import org.opendc.compute.workload.VirtualMachine
-import org.opendc.compute.workload.sampleByLoad
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.runner.replay
-import org.opendc.simulator.kotlin.runSimulation
-import java.io.File
-import java.time.Duration
-import java.util.Random
-
-/**
- * An integration test suite for the Greenifier experiments.
- */
-class GreenifierIntegrationTest {
- /**
- * The monitor used to keep track of the metrics.
- */
- private lateinit var monitor: TestComputeMonitor
-
- /**
- * The [FilterScheduler] to use for all experiments.
- */
- private lateinit var computeScheduler: FilterScheduler
-
- /**
- * The [ComputeWorkloadLoader] responsible for loading the traces.
- */
- private lateinit var workloadLoader: ComputeWorkloadLoader
-
- /**
- * Set up the experimental environment.
- */
- @BeforeEach
- fun setUp() {
- monitor = TestComputeMonitor()
- computeScheduler =
- FilterScheduler(
- filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)),
- weighers = listOf(CoreRamWeigher(multiplier = 1.0)),
- )
- workloadLoader = ComputeWorkloadLoader(File("src/test/resources/trace"))
- }
-
- /**
- * Test a large simulation setup.
- */
- @Test
- fun testLarge() =
- runSimulation {
- val seed = 0L
- val workload = createTestWorkload(1.0, seed)
- val topology = createTopology()
- val monitor = monitor
-
- Provisioner(dispatcher, seed).use { provisioner ->
- provisioner.runSteps(
- setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }),
- registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor),
- setupHosts(serviceDomain = "compute.opendc.org", topology),
- )
-
- val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
- service.replay(timeSource, workload, seed)
- }
-
- println(
- "Scheduler " +
- "Success=${monitor.attemptsSuccess} " +
- "Failure=${monitor.attemptsFailure} " +
- "Error=${monitor.attemptsError} " +
- "Pending=${monitor.serversPending} " +
- "Active=${monitor.serversActive}",
- )
-
- // Note that these values have been verified beforehand
- assertAll(
- { assertEquals(50, monitor.attemptsSuccess, "The scheduler should schedule 50 VMs") },
- { assertEquals(0, monitor.serversActive, "All VMs should finish after a run") },
- { assertEquals(0, monitor.attemptsFailure, "No VM should be unscheduled") },
- { assertEquals(0, monitor.serversPending, "No VM should not be in the queue") },
- { assertEquals(223379991650, monitor.idleTime) { "Incorrect idle time" } },
- { assertEquals(66977091124, monitor.activeTime) { "Incorrect active time" } },
- { assertEquals(3160267873, monitor.stealTime) { "Incorrect steal time" } },
- { assertEquals(0, monitor.lostTime) { "Incorrect lost time" } },
- { assertEquals(7.767237E9, monitor.energyUsage, 1E4) { "Incorrect power draw" } },
- )
- }
-
- /**
- * Test a small simulation setup.
- */
- @Test
- fun testSmall() =
- runSimulation {
- val seed = 1L
- val workload = createTestWorkload(0.25, seed)
- val topology = createTopology("single.json")
- val monitor = monitor
-
- Provisioner(dispatcher, seed).use { provisioner ->
- provisioner.runSteps(
- setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }),
- registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor),
- setupHosts(serviceDomain = "compute.opendc.org", topology),
- )
-
- val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
- service.replay(timeSource, workload, seed)
- }
-
- println(
- "Scheduler " +
- "Success=${monitor.attemptsSuccess} " +
- "Failure=${monitor.attemptsFailure} " +
- "Error=${monitor.attemptsError} " +
- "Pending=${monitor.serversPending} " +
- "Active=${monitor.serversActive}",
- )
-
- // Note that these values have been verified beforehand
- assertAll(
- { assertEquals(10996730092, monitor.idleTime) { "Idle time incorrect" } },
- { assertEquals(9741285381, monitor.activeTime) { "Active time incorrect" } },
- { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } },
- { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } },
- { assertEquals(7.933686E8, monitor.energyUsage, 1E4) { "Incorrect power draw" } },
- )
- }
-
- /**
- * Test a small simulation setup with interference.
- */
- @Test
- fun testInterference() =
- runSimulation {
- val seed = 0L
- val workload = createTestWorkload(1.0, seed)
- val topology = createTopology("single.json")
-
- Provisioner(dispatcher, seed).use { provisioner ->
- provisioner.runSteps(
- setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }),
- registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor),
- setupHosts(serviceDomain = "compute.opendc.org", topology),
- )
-
- val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
- service.replay(timeSource, workload, seed, interference = true)
- }
-
- println(
- "Scheduler " +
- "Success=${monitor.attemptsSuccess} " +
- "Failure=${monitor.attemptsFailure} " +
- "Error=${monitor.attemptsError} " +
- "Pending=${monitor.serversPending} " +
- "Active=${monitor.serversActive}",
- )
-
- // Note that these values have been verified beforehand
- assertAll(
- { assertEquals(42814948316, monitor.idleTime) { "Idle time incorrect" } },
- { assertEquals(40138266225, monitor.activeTime) { "Active time incorrect" } },
- { assertEquals(23489356981, monitor.stealTime) { "Steal time incorrect" } },
- { assertEquals(424267131, monitor.lostTime) { "Lost time incorrect" } },
- )
- }
-
- /**
- * Test a small simulation setup with failures.
- */
- @Test
- fun testFailures() =
- runSimulation {
- val seed = 0L
- val topology = createTopology("single.json")
- val workload = createTestWorkload(0.25, seed)
- val monitor = monitor
-
- Provisioner(dispatcher, seed).use { provisioner ->
- provisioner.runSteps(
- setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }),
- registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor),
- setupHosts(serviceDomain = "compute.opendc.org", topology),
- )
-
- val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
- service.replay(timeSource, workload, seed, failureModel = grid5000(Duration.ofDays(7)))
- }
-
- // Note that these values have been verified beforehand
- assertAll(
- { assertEquals(1404277711, monitor.idleTime) { "Idle time incorrect" } },
- { assertEquals(1478675712, monitor.activeTime) { "Active time incorrect" } },
- { assertEquals(152, monitor.stealTime) { "Steal time incorrect" } },
- { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } },
- { assertEquals(360369187, monitor.uptime) { "Uptime incorrect" } },
- )
- }
-
- /**
- * Obtain the trace reader for the test.
- */
- private fun createTestWorkload(
- fraction: Double,
- seed: Long,
- ): List<VirtualMachine> {
- val source = trace("bitbrains-small").sampleByLoad(fraction)
- return source.resolve(workloadLoader, Random(seed))
- }
-
- /**
- * Obtain the topology factory for the test.
- */
- private fun createTopology(name: String = "topology.json"): List<HostSpec> {
- val stream = checkNotNull(object {}.javaClass.getResourceAsStream("/env/$name"))
- return stream.use { clusterTopology(stream) }
- }
-
- class TestComputeMonitor : ComputeMonitor {
- var attemptsSuccess = 0
- var attemptsFailure = 0
- var attemptsError = 0
- var serversPending = 0
- var serversActive = 0
-
- override fun record(reader: ServiceTableReader) {
- attemptsSuccess = reader.attemptsSuccess
- attemptsFailure = reader.attemptsFailure
- attemptsError = reader.attemptsError
- serversPending = reader.serversPending
- serversActive = reader.serversActive
- }
-
- var idleTime = 0L
- var activeTime = 0L
- var stealTime = 0L
- var lostTime = 0L
- var energyUsage = 0.0
- var uptime = 0L
-
- override fun record(reader: HostTableReader) {
- idleTime += reader.cpuIdleTime
- activeTime += reader.cpuActiveTime
- stealTime += reader.cpuStealTime
- lostTime += reader.cpuLostTime
- energyUsage += reader.energyUsage
- uptime += reader.uptime
- }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierRunnerTest.kt b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierRunnerTest.kt
deleted file mode 100644
index b73317be..00000000
--- a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/greenifier/GreenifierRunnerTest.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.experiments.greenifier
-
-import org.junit.jupiter.api.assertDoesNotThrow
-import org.opendc.compute.workload.trace
-import org.opendc.experiments.base.portfolio.model.OperationalPhenomena
-import org.opendc.experiments.base.portfolio.model.Scenario
-import org.opendc.experiments.base.portfolio.model.Topology
-import org.opendc.experiments.base.portfolio.model.Workload
-import java.io.File
-import java.nio.file.Files
-
-/**
- * Test suite for [GreenifierRunner].
- */
-class GreenifierRunnerTest {
- /**
- * The path to the environments.
- */
- private val envPath = File("src/test/resources/env")
-
- /**
- * The path to the traces.
- */
- private val tracePath = File("src/test/resources/trace")
-
- /**
- * Smoke test with output.
- * fixme: Fix failures and enable
- */
- fun testSmoke() {
- val outputPath = Files.createTempDirectory("output").toFile()
-
- try {
- val runner = GreenifierRunner(envPath, tracePath, outputPath)
- val scenario =
- Scenario(
- Topology("topology.json"),
- Workload("bitbrains-small", trace("bitbrains-small")),
- OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
- "active-servers",
- )
-
- assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) }
- } finally {
- outputPath.delete()
- }
- }
-
- /**
- * Smoke test without output.
- * fixme: Fix failures and enable
- */
- fun testSmokeNoOutput() {
- val runner = GreenifierRunner(envPath, tracePath, null)
- val scenario =
- Scenario(
- Topology("topology"),
- Workload("bitbrains-small", trace("bitbrains-small")),
- OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
- "active-servers",
- )
-
- assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) }
- }
-}
diff --git a/opendc-experiments/opendc-experiments-capelin/build.gradle.kts b/opendc-experiments/opendc-experiments-portfolio/build.gradle.kts
index af37e352..c3ff33e0 100644
--- a/opendc-experiments/opendc-experiments-capelin/build.gradle.kts
+++ b/opendc-experiments/opendc-experiments-portfolio/build.gradle.kts
@@ -20,15 +20,17 @@
* SOFTWARE.
*/
-description = "Experiments for the Capelin work"
+description = "Experiments for the Portfolio work"
// Build configuration
plugins {
+ `kotlin-library-conventions`
`kotlin-conventions`
`testing-conventions`
`jacoco-conventions`
`benchmark-conventions`
distribution
+ kotlin("plugin.serialization") version "1.9.22"
}
dependencies {
@@ -39,30 +41,33 @@ dependencies {
implementation(libs.clikt)
implementation(libs.progressbar)
implementation(libs.kotlin.logging)
- implementation(libs.jackson.dataformat.csv)
+
+ implementation(libs.jackson.module.kotlin)
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
+
implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-telemetry")))
implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-topology")))
- implementation(project(mapOf("path" to ":opendc-experiments:opendc-experiments-base")))
implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-workload")))
+ implementation(project(mapOf("path" to ":opendc-experiments:opendc-experiments-base")))
runtimeOnly(projects.opendcTrace.opendcTraceOpendc)
runtimeOnly(libs.log4j.core)
runtimeOnly(libs.log4j.slf4j)
}
-val createCapelinApp by tasks.creating(CreateStartScripts::class) {
+val createPortfolioApp by tasks.creating(CreateStartScripts::class) {
dependsOn(tasks.jar)
- applicationName = "capelin"
- mainClass.set("org.opendc.experiments.capelin.CapelinCli")
+ applicationName = "portfolio"
+ mainClass.set("org.opendc.experiments.portfolio.portfolioCLI")
classpath = tasks.jar.get().outputs.files + configurations["runtimeClasspath"]
- outputDir = project.layout.buildDirectory.get().asFile.resolve("scripts")
+ outputDir = project.buildDir.resolve("scripts")
}
-// Create custom Capelin distribution
+// Create custom Greenifier distribution
distributions {
main {
- distributionBaseName.set("capelin")
+ distributionBaseName.set("portfolio")
contents {
from("README.md")
@@ -72,7 +77,7 @@ distributions {
}
into("bin") {
- from(createCapelinApp)
+ from(createPortfolioApp)
}
into("lib") {
@@ -80,8 +85,12 @@ distributions {
from(configurations["runtimeClasspath"])
}
- into("input") {
- from("input")
+ into("resources") {
+ from("src/main/resources")
+ }
+
+ into("Python_scripts") {
+ from("src/main/Python_scripts")
}
}
}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt b/opendc-experiments/opendc-experiments-portfolio/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt
index 85ee6b82..6cc6df36 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt
+++ b/opendc-experiments/opendc-experiments-portfolio/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt
@@ -31,8 +31,8 @@ import org.opendc.compute.service.scheduler.weights.CoreRamWeigher
import org.opendc.compute.simulator.provisioner.Provisioner
import org.opendc.compute.simulator.provisioner.setupComputeService
import org.opendc.compute.simulator.provisioner.setupHosts
+import org.opendc.compute.topology.HostSpec
import org.opendc.compute.topology.clusterTopology
-import org.opendc.compute.topology.specs.HostSpec
import org.opendc.compute.workload.ComputeWorkloadLoader
import org.opendc.compute.workload.VirtualMachine
import org.opendc.compute.workload.trace
diff --git a/opendc-experiments/opendc-experiments-capelin/src/jmh/resources/log4j2.xml b/opendc-experiments/opendc-experiments-portfolio/src/jmh/resources/log4j2.xml
index c496dd75..c496dd75 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/jmh/resources/log4j2.xml
+++ b/opendc-experiments/opendc-experiments-portfolio/src/jmh/resources/log4j2.xml
diff --git a/opendc-experiments/opendc-experiments-base/src/test/resources/env/topology.txt b/opendc-experiments/opendc-experiments-portfolio/src/jmh/resources/topology.txt
index 6b347bff..6b347bff 100644
--- a/opendc-experiments/opendc-experiments-base/src/test/resources/env/topology.txt
+++ b/opendc-experiments/opendc-experiments-portfolio/src/jmh/resources/topology.txt
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb b/opendc-experiments/opendc-experiments-portfolio/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb
index 792e5995..792e5995 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/OpenDCdemo.ipynb b/opendc-experiments/opendc-experiments-portfolio/src/main/Python_scripts/OpenDCdemo.ipynb
index 9bee908e..09ff26d6 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/OpenDCdemo.ipynb
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/Python_scripts/OpenDCdemo.ipynb
@@ -128,10 +128,10 @@
" <td>A01</td>\n",
" <td>A01</td>\n",
" <td>8</td>\n",
+ " <td>3.2</td>\n",
+ " <td>128</td>\n",
" <td>1</td>\n",
- " <td>100</td>\n",
- " <td>1</td>\n",
- " <td>100</td>\n",
+ " <td>128</td>\n",
" <td>8</td>\n",
" </tr>\n",
" </tbody>\n",
@@ -200,55 +200,55 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
- " <td>0</td>\n",
- " <td>2024-02-02</td>\n",
- " <td>86400000</td>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
" <td>1</td>\n",
- " <td>1000.0</td>\n",
+ " <td>0.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:45:46+00:00</td>\n",
+ " <td>300000</td>\n",
" <td>1</td>\n",
- " <td>2024-02-02</td>\n",
- " <td>86400000</td>\n",
- " <td>1</td>\n",
- " <td>1000.0</td>\n",
+ " <td>11.703998</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
- " <td>2</td>\n",
- " <td>2024-02-02</td>\n",
- " <td>86400000</td>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:55:46+00:00</td>\n",
+ " <td>600000</td>\n",
" <td>1</td>\n",
- " <td>1000.0</td>\n",
+ " <td>0.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
- " <td>3</td>\n",
- " <td>2024-02-02</td>\n",
- " <td>86400000</td>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 14:00:46+00:00</td>\n",
+ " <td>300000</td>\n",
" <td>1</td>\n",
- " <td>1000.0</td>\n",
+ " <td>11.703998</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
- " <td>4</td>\n",
- " <td>2024-02-02</td>\n",
- " <td>86400000</td>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 14:15:46+00:00</td>\n",
+ " <td>900000</td>\n",
" <td>1</td>\n",
- " <td>1000.0</td>\n",
+ " <td>0.000000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
- " id timestamp duration cpu_count cpu_usage\n",
- "0 0 2024-02-02 86400000 1 1000.0\n",
- "1 1 2024-02-02 86400000 1 1000.0\n",
- "2 2 2024-02-02 86400000 1 1000.0\n",
- "3 3 2024-02-02 86400000 1 1000.0\n",
- "4 4 2024-02-02 86400000 1 1000.0"
+ " id timestamp duration cpu_count cpu_usage\n",
+ "0 1019 2013-08-12 13:40:46+00:00 300000 1 0.000000\n",
+ "1 1019 2013-08-12 13:45:46+00:00 300000 1 11.703998\n",
+ "2 1019 2013-08-12 13:55:46+00:00 600000 1 0.000000\n",
+ "3 1019 2013-08-12 14:00:46+00:00 300000 1 11.703998\n",
+ "4 1019 2013-08-12 14:15:46+00:00 900000 1 0.000000"
]
},
"execution_count": 3,
@@ -257,7 +257,7 @@
}
],
"source": [
- "df_trace = pd.read_parquet(f\"{base_folder}/resources/benchmark/trace/trace.parquet\")\n",
+ "df_trace = pd.read_parquet(f\"{base_folder}/resources/bitbrains-small/trace/trace.parquet\")\n",
"df_trace.head()"
]
},
@@ -301,60 +301,67 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
- " <td>0</td>\n",
- " <td>2024-02-01</td>\n",
- " <td>2024-02-02</td>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
" <td>1</td>\n",
- " <td>1000.0</td>\n",
- " <td>100000</td>\n",
+ " <td>2926.000135</td>\n",
+ " <td>181352</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
+ " <td>1023</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
" <td>1</td>\n",
- " <td>2024-02-01</td>\n",
- " <td>2024-02-02</td>\n",
- " <td>1</td>\n",
- " <td>1000.0</td>\n",
- " <td>100000</td>\n",
+ " <td>2925.999560</td>\n",
+ " <td>260096</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
- " <td>2</td>\n",
- " <td>2024-02-01</td>\n",
- " <td>2024-02-02</td>\n",
+ " <td>1026</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
" <td>1</td>\n",
- " <td>1000.0</td>\n",
- " <td>100000</td>\n",
+ " <td>2925.999717</td>\n",
+ " <td>249972</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
- " <td>3</td>\n",
- " <td>2024-02-01</td>\n",
- " <td>2024-02-02</td>\n",
+ " <td>1052</td>\n",
+ " <td>2013-08-29 14:38:12+00:00</td>\n",
+ " <td>2013-09-05 07:09:07+00:00</td>\n",
" <td>1</td>\n",
- " <td>1000.0</td>\n",
- " <td>100000</td>\n",
+ " <td>2926.000107</td>\n",
+ " <td>131245</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
- " <td>4</td>\n",
- " <td>2024-02-01</td>\n",
- " <td>2024-02-02</td>\n",
+ " <td>1073</td>\n",
+ " <td>2013-08-21 11:07:12+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
" <td>1</td>\n",
- " <td>1000.0</td>\n",
- " <td>100000</td>\n",
+ " <td>2599.999649</td>\n",
+ " <td>179306</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
- " id start_time stop_time cpu_count cpu_capacity mem_capacity\n",
- "0 0 2024-02-01 2024-02-02 1 1000.0 100000\n",
- "1 1 2024-02-01 2024-02-02 1 1000.0 100000\n",
- "2 2 2024-02-01 2024-02-02 1 1000.0 100000\n",
- "3 3 2024-02-01 2024-02-02 1 1000.0 100000\n",
- "4 4 2024-02-01 2024-02-02 1 1000.0 100000"
+ " id start_time stop_time cpu_count \\\n",
+ "0 1019 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "1 1023 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "2 1026 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "3 1052 2013-08-29 14:38:12+00:00 2013-09-05 07:09:07+00:00 1 \n",
+ "4 1073 2013-08-21 11:07:12+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "\n",
+ " cpu_capacity mem_capacity \n",
+ "0 2926.000135 181352 \n",
+ "1 2925.999560 260096 \n",
+ "2 2925.999717 249972 \n",
+ "3 2926.000107 131245 \n",
+ "4 2599.999649 179306 "
]
},
"execution_count": 4,
@@ -363,7 +370,7 @@
}
],
"source": [
- "df_meta = pd.read_parquet(f\"{base_folder}/resources/benchmark/trace/meta.parquet\")\n",
+ "df_meta = pd.read_parquet(f\"{base_folder}/resources/bitbrains-small/trace/meta.parquet\")\n",
"df_meta.head()"
]
},
@@ -1088,390 +1095,6 @@
"output_file_path = Path(output_file)\n",
"df_trace_new.to_parquet(output_file_path, index=False)"
]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "d80ee1db",
- "metadata": {},
- "outputs": [],
- "source": [
- "df_server = pd.read_parquet(f\"../output/topology=topology.json/workload=benchmark/seed=0/server.parquet\")\n",
- "df_host = pd.read_parquet(f\"../output/topology=topology.json/workload=benchmark/seed=0/host.parquet\")\n",
- "df_service = pd.read_parquet(f\"../output/topology=topology.json/workload=benchmark/seed=0/service.parquet\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "4ec05a5b",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "<div>\n",
- "<style scoped>\n",
- " .dataframe tbody tr th:only-of-type {\n",
- " vertical-align: middle;\n",
- " }\n",
- "\n",
- " .dataframe tbody tr th {\n",
- " vertical-align: top;\n",
- " }\n",
- "\n",
- " .dataframe thead th {\n",
- " text-align: right;\n",
- " }\n",
- "</style>\n",
- "<table border=\"1\" class=\"dataframe\">\n",
- " <thead>\n",
- " <tr style=\"text-align: right;\">\n",
- " <th></th>\n",
- " <th>timestamp</th>\n",
- " <th>hosts_up</th>\n",
- " <th>hosts_down</th>\n",
- " <th>servers_pending</th>\n",
- " <th>servers_active</th>\n",
- " <th>attempts_success</th>\n",
- " <th>attempts_failure</th>\n",
- " <th>attempts_error</th>\n",
- " </tr>\n",
- " </thead>\n",
- " <tbody>\n",
- " <tr>\n",
- " <th>0</th>\n",
- " <td>300000</td>\n",
- " <td>2</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>10</td>\n",
- " <td>10</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " <tr>\n",
- " <th>1</th>\n",
- " <td>600000</td>\n",
- " <td>2</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>10</td>\n",
- " <td>10</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " <tr>\n",
- " <th>2</th>\n",
- " <td>900000</td>\n",
- " <td>2</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>10</td>\n",
- " <td>10</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " <tr>\n",
- " <th>3</th>\n",
- " <td>1200000</td>\n",
- " <td>2</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>10</td>\n",
- " <td>10</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " <tr>\n",
- " <th>4</th>\n",
- " <td>1500000</td>\n",
- " <td>2</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>10</td>\n",
- " <td>10</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " </tbody>\n",
- "</table>\n",
- "</div>"
- ],
- "text/plain": [
- " timestamp hosts_up hosts_down servers_pending servers_active \\\n",
- "0 300000 2 0 0 10 \n",
- "1 600000 2 0 0 10 \n",
- "2 900000 2 0 0 10 \n",
- "3 1200000 2 0 0 10 \n",
- "4 1500000 2 0 0 10 \n",
- "\n",
- " attempts_success attempts_failure attempts_error \n",
- "0 10 0 0 \n",
- "1 10 0 0 \n",
- "2 10 0 0 \n",
- "3 10 0 0 \n",
- "4 10 0 0 "
- ]
- },
- "execution_count": 6,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_service.head()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "7f147582",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "<div>\n",
- "<style scoped>\n",
- " .dataframe tbody tr th:only-of-type {\n",
- " vertical-align: middle;\n",
- " }\n",
- "\n",
- " .dataframe tbody tr th {\n",
- " vertical-align: top;\n",
- " }\n",
- "\n",
- " .dataframe thead th {\n",
- " text-align: right;\n",
- " }\n",
- "</style>\n",
- "<table border=\"1\" class=\"dataframe\">\n",
- " <thead>\n",
- " <tr style=\"text-align: right;\">\n",
- " <th></th>\n",
- " <th>timestamp</th>\n",
- " <th>host_id</th>\n",
- " <th>cpu_count</th>\n",
- " <th>mem_capacity</th>\n",
- " <th>guests_terminated</th>\n",
- " <th>guests_running</th>\n",
- " <th>guests_error</th>\n",
- " <th>guests_invalid</th>\n",
- " <th>cpu_limit</th>\n",
- " <th>cpu_usage</th>\n",
- " <th>...</th>\n",
- " <th>cpu_utilization</th>\n",
- " <th>cpu_time_active</th>\n",
- " <th>cpu_time_idle</th>\n",
- " <th>cpu_time_steal</th>\n",
- " <th>cpu_time_lost</th>\n",
- " <th>power_draw</th>\n",
- " <th>energy_usage</th>\n",
- " <th>uptime</th>\n",
- " <th>downtime</th>\n",
- " <th>boot_time</th>\n",
- " </tr>\n",
- " </thead>\n",
- " <tbody>\n",
- " <tr>\n",
- " <th>0</th>\n",
- " <td>300000</td>\n",
- " <td>e220a839-7b1d-cdaf-0000-000000000000</td>\n",
- " <td>6</td>\n",
- " <td>100000</td>\n",
- " <td>0</td>\n",
- " <td>5</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>8000.0</td>\n",
- " <td>5000.0</td>\n",
- " <td>...</td>\n",
- " <td>0.625</td>\n",
- " <td>1125002</td>\n",
- " <td>674998</td>\n",
- " <td>18</td>\n",
- " <td>0</td>\n",
- " <td>325.0</td>\n",
- " <td>97500.075</td>\n",
- " <td>300000</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " <tr>\n",
- " <th>1</th>\n",
- " <td>300000</td>\n",
- " <td>6e789e6a-a1b9-65f4-0000-000000000001</td>\n",
- " <td>4</td>\n",
- " <td>100000</td>\n",
- " <td>0</td>\n",
- " <td>5</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>4000.0</td>\n",
- " <td>4000.0</td>\n",
- " <td>...</td>\n",
- " <td>1.000</td>\n",
- " <td>1200000</td>\n",
- " <td>0</td>\n",
- " <td>300011</td>\n",
- " <td>0</td>\n",
- " <td>400.0</td>\n",
- " <td>120000.000</td>\n",
- " <td>300000</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " <tr>\n",
- " <th>2</th>\n",
- " <td>600000</td>\n",
- " <td>e220a839-7b1d-cdaf-0000-000000000000</td>\n",
- " <td>6</td>\n",
- " <td>100000</td>\n",
- " <td>0</td>\n",
- " <td>5</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>8000.0</td>\n",
- " <td>5000.0</td>\n",
- " <td>...</td>\n",
- " <td>0.625</td>\n",
- " <td>1125000</td>\n",
- " <td>675000</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>325.0</td>\n",
- " <td>97500.000</td>\n",
- " <td>300000</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " <tr>\n",
- " <th>3</th>\n",
- " <td>600000</td>\n",
- " <td>6e789e6a-a1b9-65f4-0000-000000000001</td>\n",
- " <td>4</td>\n",
- " <td>100000</td>\n",
- " <td>0</td>\n",
- " <td>5</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>4000.0</td>\n",
- " <td>4000.0</td>\n",
- " <td>...</td>\n",
- " <td>1.000</td>\n",
- " <td>1200000</td>\n",
- " <td>0</td>\n",
- " <td>300000</td>\n",
- " <td>0</td>\n",
- " <td>400.0</td>\n",
- " <td>120000.000</td>\n",
- " <td>300000</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " <tr>\n",
- " <th>4</th>\n",
- " <td>900000</td>\n",
- " <td>e220a839-7b1d-cdaf-0000-000000000000</td>\n",
- " <td>6</td>\n",
- " <td>100000</td>\n",
- " <td>0</td>\n",
- " <td>5</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>8000.0</td>\n",
- " <td>5000.0</td>\n",
- " <td>...</td>\n",
- " <td>0.625</td>\n",
- " <td>1125000</td>\n",
- " <td>675000</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " <td>325.0</td>\n",
- " <td>97500.000</td>\n",
- " <td>300000</td>\n",
- " <td>0</td>\n",
- " <td>0</td>\n",
- " </tr>\n",
- " </tbody>\n",
- "</table>\n",
- "<p>5 rows × 21 columns</p>\n",
- "</div>"
- ],
- "text/plain": [
- " timestamp host_id cpu_count mem_capacity \\\n",
- "0 300000 e220a839-7b1d-cdaf-0000-000000000000 6 100000 \n",
- "1 300000 6e789e6a-a1b9-65f4-0000-000000000001 4 100000 \n",
- "2 600000 e220a839-7b1d-cdaf-0000-000000000000 6 100000 \n",
- "3 600000 6e789e6a-a1b9-65f4-0000-000000000001 4 100000 \n",
- "4 900000 e220a839-7b1d-cdaf-0000-000000000000 6 100000 \n",
- "\n",
- " guests_terminated guests_running guests_error guests_invalid cpu_limit \\\n",
- "0 0 5 0 0 8000.0 \n",
- "1 0 5 0 0 4000.0 \n",
- "2 0 5 0 0 8000.0 \n",
- "3 0 5 0 0 4000.0 \n",
- "4 0 5 0 0 8000.0 \n",
- "\n",
- " cpu_usage ... cpu_utilization cpu_time_active cpu_time_idle \\\n",
- "0 5000.0 ... 0.625 1125002 674998 \n",
- "1 4000.0 ... 1.000 1200000 0 \n",
- "2 5000.0 ... 0.625 1125000 675000 \n",
- "3 4000.0 ... 1.000 1200000 0 \n",
- "4 5000.0 ... 0.625 1125000 675000 \n",
- "\n",
- " cpu_time_steal cpu_time_lost power_draw energy_usage uptime downtime \\\n",
- "0 18 0 325.0 97500.075 300000 0 \n",
- "1 300011 0 400.0 120000.000 300000 0 \n",
- "2 0 0 325.0 97500.000 300000 0 \n",
- "3 300000 0 400.0 120000.000 300000 0 \n",
- "4 0 0 325.0 97500.000 300000 0 \n",
- "\n",
- " boot_time \n",
- "0 0 \n",
- "1 0 \n",
- "2 0 \n",
- "3 0 \n",
- "4 0 \n",
- "\n",
- "[5 rows x 21 columns]"
- ]
- },
- "execution_count": 7,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_host.head()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "id": "678ede60",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "Index(['timestamp', 'host_id', 'cpu_count', 'mem_capacity',\n",
- " 'guests_terminated', 'guests_running', 'guests_error', 'guests_invalid',\n",
- " 'cpu_limit', 'cpu_usage', 'cpu_demand', 'cpu_utilization',\n",
- " 'cpu_time_active', 'cpu_time_idle', 'cpu_time_steal', 'cpu_time_lost',\n",
- " 'power_draw', 'energy_usage', 'uptime', 'downtime', 'boot_time'],\n",
- " dtype='object')"
- ]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_host.columns"
- ]
}
],
"metadata": {
diff --git a/opendc-experiments/opendc-experiments-portfolio/src/main/kotlin/org/opendc/experiments/scenario/ExamplePortfolio.kt b/opendc-experiments/opendc-experiments-portfolio/src/main/kotlin/org/opendc/experiments/scenario/ExamplePortfolio.kt
new file mode 100644
index 00000000..b5b174b6
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/kotlin/org/opendc/experiments/scenario/ExamplePortfolio.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.experiments.scenario
+
+import org.opendc.compute.service.scheduler.ComputeSchedulerEnum
+import org.opendc.experiments.base.models.portfolio.Portfolio
+import org.opendc.experiments.base.models.scenario.AllocationPolicySpec
+import org.opendc.experiments.base.models.scenario.FailureModelSpec
+import org.opendc.experiments.base.models.scenario.Scenario
+import org.opendc.experiments.base.models.scenario.ScenarioSpec
+import org.opendc.experiments.base.models.scenario.TopologySpec
+import org.opendc.experiments.base.models.scenario.WorkloadSpec
+import org.opendc.experiments.base.models.scenario.WorkloadTypes
+import org.opendc.experiments.base.models.scenario.getScenario
+
+/**
+ * A [Portfolio] that explores the difference between horizontal and vertical scaling.
+ */
+public fun getExamplePortfolio(): Portfolio {
+ val topologies =
+ listOf(
+ TopologySpec("resources/env/single.json"),
+ TopologySpec("resources/env/multi.json"),
+ )
+
+ val workloads =
+ listOf(
+ WorkloadSpec("resources/bitbrains-small", type = WorkloadTypes.ComputeWorkload),
+ )
+
+ val failureModel = FailureModelSpec(0.0)
+ val allocationPolicy = AllocationPolicySpec(ComputeSchedulerEnum.ActiveServers)
+
+ val scenarios: Iterable<Scenario> =
+ topologies.flatMap { topology ->
+ workloads.map { workload ->
+ getScenario(
+ ScenarioSpec(
+ topology,
+ workload,
+ allocationPolicy,
+ failureModel,
+ ),
+ )
+ }
+ }
+
+ return Portfolio(scenarios)
+}
diff --git a/opendc-experiments/opendc-experiments-portfolio/src/main/kotlin/org/opendc/experiments/scenario/PortfolioCli.kt b/opendc-experiments/opendc-experiments-portfolio/src/main/kotlin/org/opendc/experiments/scenario/PortfolioCli.kt
new file mode 100644
index 00000000..5f703164
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/kotlin/org/opendc/experiments/scenario/PortfolioCli.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+@file:JvmName("PortfolioCli")
+
+package org.opendc.experiments.portfolio
+
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.parameters.options.default
+import com.github.ajalt.clikt.parameters.options.defaultLazy
+import com.github.ajalt.clikt.parameters.options.option
+import com.github.ajalt.clikt.parameters.types.file
+import com.github.ajalt.clikt.parameters.types.int
+import org.opendc.experiments.base.models.portfolio.getPortfolio
+import org.opendc.experiments.base.runner.runPortfolio
+import java.io.File
+
+/**
+ * Main entrypoint of the application.
+ */
+public fun main(args: Array<String>): Unit = PortfolioCommand().main(args)
+
+/**
+ * Represents the command for the Portfolio experiments.
+ */
+internal class PortfolioCommand : CliktCommand(name = "portfolio") {
+ /**
+ * The path to the environment directory.
+ */
+ private val portfolioPath by option("--portfolio-path", help = "path to portfolio file")
+ .file(canBeDir = true, canBeFile = false)
+ .defaultLazy { File("resources/portfolio.json") }
+
+ /**
+ * The number of threads to use for parallelism.
+ */
+ private val parallelism by option("-p", "--parallelism", help = "number of worker threads")
+ .int()
+ .default(Runtime.getRuntime().availableProcessors() - 1)
+
+ override fun run() {
+ val portfolio = getPortfolio(portfolioPath)
+ runPortfolio(portfolio, parallelism)
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/interference-model.json b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/interference-model.json
index 51fc6366..51fc6366 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/interference-model.json
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/interference-model.json
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/trace/meta.parquet b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/trace/meta.parquet
index 9cded35f..9cded35f 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/trace/meta.parquet
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/trace/meta.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/trace/trace.parquet b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/trace/trace.parquet
index 9d953956..9d953956 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/main/resources/bitbrains-small/trace/trace.parquet
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/bitbrains-small/trace/trace.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/resources/env/topology.json b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/env/multi.json
index 721005b0..721005b0 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/test/resources/env/topology.json
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/env/multi.json
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.json b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/env/single.json
index a1c8d95a..a1c8d95a 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.json
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/env/single.json
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/resources/log4j2.xml b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/log4j2.xml
index e479f2ca..e479f2ca 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/main/resources/log4j2.xml
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/log4j2.xml
diff --git a/opendc-experiments/opendc-experiments-portfolio/src/main/resources/portfolio.json b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/portfolio.json
new file mode 100644
index 00000000..a1320b39
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-portfolio/src/main/resources/portfolio.json
@@ -0,0 +1,31 @@
+{
+ "scenarios": [
+ {
+ "runs": 5,
+ "topology": {
+ "pathToFile": "resources/env/single.json"
+ },
+ "workload": {
+ "pathToFile": "resources/bitbrains-small",
+ "type": "ComputeWorkload"
+ },
+ "allocationPolicy": {
+ "policyType": "Mem"
+ }
+ },
+ {
+ "runs": 5,
+ "name": "TESTTTT",
+ "topology": {
+ "pathToFile": "resources/env/single.json"
+ },
+ "workload": {
+ "pathToFile": "resources/bitbrains-small",
+ "type": "ComputeWorkload"
+ },
+ "allocationPolicy": {
+ "policyType": "Mem"
+ }
+ }
+ ]
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/test/resources/env/single.txt b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/env/single.txt
index 5642003d..5642003d 100644
--- a/opendc-experiments/opendc-experiments-base/src/test/resources/env/single.txt
+++ b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/env/single.txt
diff --git a/opendc-experiments/opendc-experiments-capelin/src/jmh/resources/topology.txt b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/env/topology.txt
index 6b347bff..6b347bff 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/jmh/resources/topology.txt
+++ b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/env/topology.txt
diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/interference-model.json b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/interference-model.json
index 51fc6366..51fc6366 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/interference-model.json
+++ b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/interference-model.json
diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/meta.parquet b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/meta.parquet
index 9cded35f..9cded35f 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/meta.parquet
+++ b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/meta.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/trace.parquet b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/trace.parquet
index 9d953956..9d953956 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/test/resources/trace/bitbrains-small/trace.parquet
+++ b/opendc-experiments/opendc-experiments-portfolio/src/test/resources/trace/bitbrains-small/trace.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts b/opendc-experiments/opendc-experiments-scenario/build.gradle.kts
index 45672545..bf0a504d 100644
--- a/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts
+++ b/opendc-experiments/opendc-experiments-scenario/build.gradle.kts
@@ -20,15 +20,17 @@
* SOFTWARE.
*/
-description = "Experiments for the Greenifier work"
+description = "Experiments for the Scenario work"
// Build configuration
plugins {
+ `kotlin-library-conventions`
`kotlin-conventions`
`testing-conventions`
`jacoco-conventions`
`benchmark-conventions`
distribution
+ kotlin("plugin.serialization") version "1.9.22"
}
dependencies {
@@ -39,7 +41,10 @@ dependencies {
implementation(libs.clikt)
implementation(libs.progressbar)
implementation(libs.kotlin.logging)
- implementation(libs.jackson.dataformat.csv)
+
+ implementation(libs.jackson.module.kotlin)
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
+
implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-telemetry")))
implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-topology")))
implementation(project(mapOf("path" to ":opendc-compute:opendc-compute-workload")))
@@ -50,19 +55,19 @@ dependencies {
runtimeOnly(libs.log4j.slf4j)
}
-val createGreenifierApp by tasks.creating(CreateStartScripts::class) {
+val createScenarioApp by tasks.creating(CreateStartScripts::class) {
dependsOn(tasks.jar)
- applicationName = "greenifier"
- mainClass.set("org.opendc.experiments.greenifier.GreenifierCli")
+ applicationName = "scenario"
+ mainClass.set("org.opendc.experiments.scenario.ScnearioCLI")
classpath = tasks.jar.get().outputs.files + configurations["runtimeClasspath"]
- outputDir = project.layout.buildDirectory.get().asFile.resolve("scripts")
+ outputDir = project.buildDir.resolve("scripts")
}
// Create custom Greenifier distribution
distributions {
main {
- distributionBaseName.set("greenifier")
+ distributionBaseName.set("scenario")
contents {
from("README.md")
@@ -72,7 +77,7 @@ distributions {
}
into("bin") {
- from(createGreenifierApp)
+ from(createScenarioApp)
}
into("lib") {
diff --git a/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt b/opendc-experiments/opendc-experiments-scenario/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt
index f0084ec5..6cc6df36 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt
+++ b/opendc-experiments/opendc-experiments-scenario/src/jmh/kotlin/org/opendc/experiments/greenifier/GreenifierBenchmarks.kt
@@ -20,7 +20,7 @@
* SOFTWARE.
*/
-package org.opendc.experiments.capelin
+package org.opendc.experiments.greenifier
import org.opendc.compute.service.ComputeService
import org.opendc.compute.service.scheduler.FilterScheduler
@@ -31,12 +31,12 @@ import org.opendc.compute.service.scheduler.weights.CoreRamWeigher
import org.opendc.compute.simulator.provisioner.Provisioner
import org.opendc.compute.simulator.provisioner.setupComputeService
import org.opendc.compute.simulator.provisioner.setupHosts
-import org.opendc.experiments.capelin.topology.clusterTopology
-import org.opendc.experiments.compute.ComputeWorkloadLoader
-import org.opendc.experiments.compute.VirtualMachine
-import org.opendc.experiments.compute.replay
-import org.opendc.experiments.compute.topology.HostSpec
-import org.opendc.experiments.compute.trace
+import org.opendc.compute.topology.HostSpec
+import org.opendc.compute.topology.clusterTopology
+import org.opendc.compute.workload.ComputeWorkloadLoader
+import org.opendc.compute.workload.VirtualMachine
+import org.opendc.compute.workload.trace
+import org.opendc.experiments.base.runner.replay
import org.opendc.simulator.kotlin.runSimulation
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Fork
@@ -51,13 +51,13 @@ import java.util.Random
import java.util.concurrent.TimeUnit
/**
- * Benchmark suite for the Capelin experiments.
+ * Benchmark suite for the Greenifier experiments.
*/
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
-class CapelinBenchmarks {
+class GreenifierBenchmarks {
private lateinit var vms: List<VirtualMachine>
private lateinit var topology: List<HostSpec>
@@ -72,7 +72,7 @@ class CapelinBenchmarks {
}
@Benchmark
- fun benchmarkCapelin() =
+ fun benchmarkGreenifier() =
runSimulation {
val serviceDomain = "compute.opendc.org"
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/log4j2.xml b/opendc-experiments/opendc-experiments-scenario/src/jmh/resources/log4j2.xml
index c496dd75..c496dd75 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/log4j2.xml
+++ b/opendc-experiments/opendc-experiments-scenario/src/jmh/resources/log4j2.xml
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/topology.txt b/opendc-experiments/opendc-experiments-scenario/src/jmh/resources/topology.txt
index 6b347bff..6b347bff 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/topology.txt
+++ b/opendc-experiments/opendc-experiments-scenario/src/jmh/resources/topology.txt
diff --git a/opendc-experiments/opendc-experiments-scenario/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb b/opendc-experiments/opendc-experiments-scenario/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb
new file mode 100644
index 00000000..792e5995
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb
@@ -0,0 +1,1379 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "18170001",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "from IPython.display import display, HTML\n",
+ "\n",
+ "base_folder = \"../../../../..\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "422f4d05",
+ "metadata": {},
+ "source": [
+ "## Topologies"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "a2d05361",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Topology name: multi\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>ClusterID</th>\n",
+ " <th>ClusterName</th>\n",
+ " <th>Cores</th>\n",
+ " <th>Speed</th>\n",
+ " <th>Memory</th>\n",
+ " <th>numberOfHosts</th>\n",
+ " <th>memoryCapacityPerHost</th>\n",
+ " <th>coreCountPerHost</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>A01</td>\n",
+ " <td>A01</td>\n",
+ " <td>32</td>\n",
+ " <td>3.20</td>\n",
+ " <td>2048</td>\n",
+ " <td>1</td>\n",
+ " <td>256</td>\n",
+ " <td>32</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>B01</td>\n",
+ " <td>B01</td>\n",
+ " <td>48</td>\n",
+ " <td>2.93</td>\n",
+ " <td>1256</td>\n",
+ " <td>6</td>\n",
+ " <td>64</td>\n",
+ " <td>8</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>C01</td>\n",
+ " <td>C01</td>\n",
+ " <td>32</td>\n",
+ " <td>3.20</td>\n",
+ " <td>2048</td>\n",
+ " <td>2</td>\n",
+ " <td>128</td>\n",
+ " <td>16</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>"
+ ],
+ "text/plain": [
+ "<IPython.core.display.HTML object>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Topology name: single\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>ClusterID</th>\n",
+ " <th>ClusterName</th>\n",
+ " <th>Cores</th>\n",
+ " <th>Speed</th>\n",
+ " <th>Memory</th>\n",
+ " <th>numberOfHosts</th>\n",
+ " <th>memoryCapacityPerHost</th>\n",
+ " <th>coreCountPerHost</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>A01</td>\n",
+ " <td>A01</td>\n",
+ " <td>8</td>\n",
+ " <td>3.2</td>\n",
+ " <td>128</td>\n",
+ " <td>1</td>\n",
+ " <td>128</td>\n",
+ " <td>8</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>"
+ ],
+ "text/plain": [
+ "<IPython.core.display.HTML object>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "def read_topology(topology_name):\n",
+ " print(f\"Topology name: {topology_name}\")\n",
+ " df = pd.read_csv(f\"{base_folder}/resources/env/{topology_name}.txt\", delimiter=\";\")\n",
+ " display(HTML(df.to_html()))\n",
+ " \n",
+ "read_topology(\"multi\")\n",
+ "read_topology(\"single\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8f4fe54d",
+ "metadata": {},
+ "source": [
+ "## Traces"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "fd17d88a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>id</th>\n",
+ " <th>timestamp</th>\n",
+ " <th>duration</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_usage</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>24015</th>\n",
+ " <td>1129</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>3.901332</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>34039</th>\n",
+ " <td>1138</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>20.799994</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>41348</th>\n",
+ " <td>1147</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>47515</th>\n",
+ " <td>1152</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>53661</th>\n",
+ " <td>116</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>158.003968</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>61559</th>\n",
+ " <td>141</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>2</td>\n",
+ " <td>56.569320</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>66380</th>\n",
+ " <td>190</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>8</td>\n",
+ " <td>14693.462756</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>73300</th>\n",
+ " <td>205</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>8</td>\n",
+ " <td>16185.864295</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>87698</th>\n",
+ " <td>244</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>8</td>\n",
+ " <td>95.333296</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>95284</th>\n",
+ " <td>272</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>8</td>\n",
+ " <td>164.666623</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>112192</th>\n",
+ " <td>323</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>2</td>\n",
+ " <td>152.533273</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>125958</th>\n",
+ " <td>378</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>2</td>\n",
+ " <td>76.266647</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>132745</th>\n",
+ " <td>379</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>2</td>\n",
+ " <td>109.199970</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>139492</th>\n",
+ " <td>449</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>154.266626</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>146840</th>\n",
+ " <td>463</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>131.733298</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>153699</th>\n",
+ " <td>466</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>131.733300</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>160537</th>\n",
+ " <td>467</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>185.466617</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>167694</th>\n",
+ " <td>501</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>157.733310</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>174746</th>\n",
+ " <td>506</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>183.733284</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>182064</th>\n",
+ " <td>550</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>1.733333</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>196282</th>\n",
+ " <td>607</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>247.866604</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>203796</th>\n",
+ " <td>626</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>10718.931688</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>206960</th>\n",
+ " <td>636</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>10781.331724</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>209842</th>\n",
+ " <td>677</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>2</td>\n",
+ " <td>181.999951</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>225939</th>\n",
+ " <td>740</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>32</td>\n",
+ " <td>2048.399624</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>234340</th>\n",
+ " <td>750</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>8</td>\n",
+ " <td>145.599961</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>252091</th>\n",
+ " <td>851</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>2</td>\n",
+ " <td>29.259993</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>256754</th>\n",
+ " <td>871</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>158.003974</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>265560</th>\n",
+ " <td>957</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>4</td>\n",
+ " <td>128.266613</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>268345</th>\n",
+ " <td>997</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>8</td>\n",
+ " <td>10235.198844</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " id timestamp duration cpu_count cpu_usage\n",
+ "0 1019 2013-08-12 13:40:46+00:00 300000 1 0.000000\n",
+ "24015 1129 2013-08-12 13:40:46+00:00 300000 1 3.901332\n",
+ "34039 1138 2013-08-12 13:40:46+00:00 300000 1 20.799994\n",
+ "41348 1147 2013-08-12 13:40:46+00:00 300000 1 0.000000\n",
+ "47515 1152 2013-08-12 13:40:46+00:00 300000 1 0.000000\n",
+ "53661 116 2013-08-12 13:40:46+00:00 300000 4 158.003968\n",
+ "61559 141 2013-08-12 13:40:46+00:00 300000 2 56.569320\n",
+ "66380 190 2013-08-12 13:40:46+00:00 300000 8 14693.462756\n",
+ "73300 205 2013-08-12 13:40:46+00:00 300000 8 16185.864295\n",
+ "87698 244 2013-08-12 13:40:46+00:00 300000 8 95.333296\n",
+ "95284 272 2013-08-12 13:40:46+00:00 300000 8 164.666623\n",
+ "112192 323 2013-08-12 13:40:46+00:00 300000 2 152.533273\n",
+ "125958 378 2013-08-12 13:40:46+00:00 300000 2 76.266647\n",
+ "132745 379 2013-08-12 13:40:46+00:00 300000 2 109.199970\n",
+ "139492 449 2013-08-12 13:40:46+00:00 300000 4 154.266626\n",
+ "146840 463 2013-08-12 13:40:46+00:00 300000 4 131.733298\n",
+ "153699 466 2013-08-12 13:40:46+00:00 300000 4 131.733300\n",
+ "160537 467 2013-08-12 13:40:46+00:00 300000 4 185.466617\n",
+ "167694 501 2013-08-12 13:40:46+00:00 300000 4 157.733310\n",
+ "174746 506 2013-08-12 13:40:46+00:00 300000 4 183.733284\n",
+ "182064 550 2013-08-12 13:40:46+00:00 300000 1 1.733333\n",
+ "196282 607 2013-08-12 13:40:46+00:00 300000 1 247.866604\n",
+ "203796 626 2013-08-12 13:40:46+00:00 300000 4 10718.931688\n",
+ "206960 636 2013-08-12 13:40:46+00:00 300000 4 10781.331724\n",
+ "209842 677 2013-08-12 13:40:46+00:00 300000 2 181.999951\n",
+ "225939 740 2013-08-12 13:40:46+00:00 300000 32 2048.399624\n",
+ "234340 750 2013-08-12 13:40:46+00:00 300000 8 145.599961\n",
+ "252091 851 2013-08-12 13:40:46+00:00 300000 2 29.259993\n",
+ "256754 871 2013-08-12 13:40:46+00:00 300000 4 158.003974\n",
+ "265560 957 2013-08-12 13:40:46+00:00 300000 4 128.266613\n",
+ "268345 997 2013-08-12 13:40:46+00:00 300000 8 10235.198844"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_trace = pd.read_parquet(f\"{base_folder}/resources/bitbrains-small/trace/trace.parquet\")\n",
+ "df_trace[df_trace[\"timestamp\"] == df_trace[\"timestamp\"].min()]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "346f097f",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>id</th>\n",
+ " <th>start_time</th>\n",
+ " <th>stop_time</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_capacity</th>\n",
+ " <th>mem_capacity</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2926.000135</td>\n",
+ " <td>181352</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1023</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2925.999560</td>\n",
+ " <td>260096</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1026</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2925.999717</td>\n",
+ " <td>249972</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1052</td>\n",
+ " <td>2013-08-29 14:38:12+00:00</td>\n",
+ " <td>2013-09-05 07:09:07+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2926.000107</td>\n",
+ " <td>131245</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1073</td>\n",
+ " <td>2013-08-21 11:07:12+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2599.999649</td>\n",
+ " <td>179306</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " id start_time stop_time cpu_count \\\n",
+ "0 1019 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "1 1023 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "2 1026 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "3 1052 2013-08-29 14:38:12+00:00 2013-09-05 07:09:07+00:00 1 \n",
+ "4 1073 2013-08-21 11:07:12+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "\n",
+ " cpu_capacity mem_capacity \n",
+ "0 2926.000135 181352 \n",
+ "1 2925.999560 260096 \n",
+ "2 2925.999717 249972 \n",
+ "3 2926.000107 131245 \n",
+ "4 2599.999649 179306 "
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_meta = pd.read_parquet(f\"{base_folder}/resources/bitbrains-small/trace/meta.parquet\")\n",
+ "df_meta.head()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "13bf9fdb",
+ "metadata": {},
+ "source": [
+ "# Lets run this in OpenDC!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c9766446",
+ "metadata": {},
+ "source": [
+ "## Resulting Files"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 89,
+ "id": "0d400ffd",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "output_folder = f\"{base_folder}/output\"\n",
+ "workload = \"workload=bitbrains-small\"\n",
+ "seed = \"seed=0\"\n",
+ "\n",
+ "df_host_single = pd.read_parquet(f\"{output_folder}/host/topology=single/{workload}/{seed}/data.parquet\")\n",
+ "df_host_multi = pd.read_parquet(f\"{output_folder}/host/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "\n",
+ "df_server_single = pd.read_parquet(f\"{output_folder}/server/topology=single/{workload}/{seed}/data.parquet\")\n",
+ "df_server_multi = pd.read_parquet(f\"{output_folder}/server/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "\n",
+ "df_service_single = pd.read_parquet(f\"{output_folder}/service/topology=single/{workload}/{seed}/data.parquet\")\n",
+ "df_service_multi = pd.read_parquet(f\"{output_folder}/service/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "\n",
+ "def add_absolute_timestamp(df, start_dt):\n",
+ " df[\"absolute_timestamp\"] = start_dt + (df[\"timestamp\"] - df[\"timestamp\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_host_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_host_single, df_meta[\"start_time\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_server_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_server_multi, df_meta[\"start_time\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_service_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_service_multi, df_meta[\"start_time\"].min())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 90,
+ "id": "a9a61332",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1080"
+ ]
+ },
+ "execution_count": 90,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "len(df_service_single)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 91,
+ "id": "e1d01f85",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1970-01-01 02:00:00+00:00 1\n",
+ "1970-03-02 12:00:00+00:00 1\n",
+ "1970-03-01 08:00:00+00:00 1\n",
+ "1970-03-01 10:00:00+00:00 1\n",
+ "1970-03-01 12:00:00+00:00 1\n",
+ " ..\n",
+ "1970-01-31 12:00:00+00:00 1\n",
+ "1970-01-31 14:00:00+00:00 1\n",
+ "1970-01-31 16:00:00+00:00 1\n",
+ "1970-01-31 18:00:00+00:00 1\n",
+ "1970-04-01 00:00:00+00:00 1\n",
+ "Name: timestamp, Length: 1080, dtype: int64"
+ ]
+ },
+ "execution_count": 91,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_host_single.timestamp.value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 103,
+ "id": "5b36f79c",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([50, 49, 47, 46, 45, 44, 34, 16, 14, 13, 12, 11, 10])"
+ ]
+ },
+ "execution_count": 103,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_server_single.timestamp.value_counts().unique()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 93,
+ "id": "699268f3",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1970-01-01 02:00:00+00:00 1\n",
+ "1970-03-02 12:00:00+00:00 1\n",
+ "1970-03-01 08:00:00+00:00 1\n",
+ "1970-03-01 10:00:00+00:00 1\n",
+ "1970-03-01 12:00:00+00:00 1\n",
+ " ..\n",
+ "1970-01-31 12:00:00+00:00 1\n",
+ "1970-01-31 14:00:00+00:00 1\n",
+ "1970-01-31 16:00:00+00:00 1\n",
+ "1970-01-31 18:00:00+00:00 1\n",
+ "1970-04-01 00:00:00+00:00 1\n",
+ "Name: timestamp, Length: 1080, dtype: int64"
+ ]
+ },
+ "execution_count": 93,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_service_single.timestamp.value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 104,
+ "id": "a32f9d66",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([44, 45, 46, 47, 49, 50, 34, 16, 14, 13, 12, 11, 10], dtype=int32)"
+ ]
+ },
+ "execution_count": 104,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(df_service_single.servers_active + df_service_single.servers_pending).unique() "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 102,
+ "id": "fe5cc9c0",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 102,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "set(d1) == set(d2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "09d31c91",
+ "metadata": {},
+ "source": [
+ "## Power Usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "id": "82f0a24a",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single topology: 2227322672.880138\n",
+ "multi topology: 5865185988.6738405\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"single topology: {df_host_single.power_total.sum()}\")\n",
+ "print(f\"multi topology: {df_host_multi.power_total.sum()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7ab3357d",
+ "metadata": {},
+ "source": [
+ "## CPU usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "id": "e94db3a6",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single topology: 0.575979511557793\n",
+ "multi topology: 0.34249306908883387\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"single topology: {df_host_single.cpu_utilization.mean()}\")\n",
+ "print(f\"multi topology: {df_host_multi.cpu_utilization.mean()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e000a260",
+ "metadata": {},
+ "source": [
+ "## CPU utilization"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "id": "8d7daa45",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single topology: 0.575979511557793\n",
+ "multi topology: 0.34249306908883387\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"single topology: {df_host_single.cpu_utilization.mean()}\")\n",
+ "print(f\"multi topology: {df_host_multi.cpu_utilization.mean()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ad97741c",
+ "metadata": {},
+ "source": [
+ "## Plotting Results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "id": "5df8f9aa",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAGwCAYAAABVdURTAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAwF0lEQVR4nO3de1xVdb7/8fcG3CByUxFQZydeMDMvlKjH2ykLo2xMa87EaImQWR21scgupqOWFV45lDkxOSp21XFST6dMU5JTXiZNRZtS8hJhCYiVoHgEhPX7w597IkDZmw0blq/n47Efj/Z3f9dan/XN2m+/67v2shiGYQgAAMAkPNxdAAAAgCsRbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKl4ubuAhlZRUaETJ07I399fFovF3eUAAIBaMAxDZ86cUbt27eThcfm5masu3Jw4cUI2m83dZQAAACccP35cv/nNby7b56oLN/7+/pIuDk5AQICbqwEAALVRVFQkm81m/x6/nKsu3Fy6FBUQEEC4AQCgianNkhIWFAMAAFMh3AAAAFMh3AAAAFO56tbcAADgrPLycpWVlbm7DNOyWq1XvM27Ngg3AABcgWEYysvL0+nTp91diql5eHioY8eOslqtddoP4QYAgCu4FGxCQkLk6+vLj8DWg0s/spubm6trrrmmTmNMuAEA4DLKy8vtwaZ169buLsfU2rRpoxMnTujChQtq1qyZ0/thQTEAAJdxaY2Nr6+vmysxv0uXo8rLy+u0H8INAAC1wKWo+ueqMSbcAAAAUyHcAAAAU2FBMQAAThqftrtBj7csvm+DHq86s2fP1vr165WZmVljn+zsbHXs2FH79u1TZGRkg9V2CeEGAAA4LT4+XqdPn9b69evtbTabTbm5uQoODnZLTYQbAADgUp6engoLC3Pb8VlzAwCASd1888169NFH9dhjj6lly5YKDQ3V0qVLVVxcrISEBPn7+6tLly766KOPJElpaWkKCgqqtI/169fXeBfT7NmztXLlSv33f/+3LBaLLBaLMjIylJ2dLYvFctlLV/WJmRsXq8v118ZwLRUAYC4rV67UU089pV27dmn16tX6z//8T61bt0533323nn32Wf3Xf/2Xxo4dq5ycHIf3PXXqVB08eFBFRUVasWKFJKlVq1Y6ceKEq0/DIczcAABgYr1799aMGTMUERGhadOmycfHR8HBwZowYYIiIiI0c+ZM/fjjjzpw4IDD+/bz81Pz5s3l7e2tsLAwhYWF1fm5UK5AuAEAwMR69epl/2dPT0+1bt1aPXv2tLeFhoZKkk6ePNngtdUXwg0AACb262c0WSyWSm2X1tNUVFTIw8NDhmFU6n/p8RNNCeEGAABIuvjgyjNnzqi4uNjedqVFwVartc7PgnI1wg0AAJAk9e/fX76+vnr22Wd19OhRvfPOO0pLS7vsNuHh4Tpw4ICysrJ06tSpRjHTw91SAAA4yWx3ubZq1UpvvfWWnnzySS1dulS33nqrZs+erYceeqjGbSZMmKCMjAxFRUXp7Nmz2rp1q8LDwxuu6GpYjF9fXDO5oqIiBQYGqrCwUAEBAS7fP7eCA4C5nD9/Xt9++606duwoHx8fd5djapcba0e+v7ksBQAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwDAVSY+Pl6jRo1y6T6zs7NlsViu+CyqhsDjFwAAcNY7sQ17vDGrXbKbl19+ucrTv82EcAMAwFUmMDDQ3SXUKy5LAQBgUn//+9/Vs2dPNW/eXK1bt1Z0dLSKi4urXJa6+eab9cc//lFPPfWUWrVqpbCwMM2ePbvSvg4dOqTBgwfLx8dH3bt315YtW2SxWLR+/foaj//Pf/5Td9xxh/z8/BQaGqqxY8fq1KlT9XOyv0C4AQDAhHJzczV69Gg98MADOnjwoDIyMnTPPffUeDlq5cqVatGihT7//HPNnz9fzz//vDZv3ixJKi8v16hRo+Tr66vPP/9cr7/+uqZPn37Z458+fVq33HKLbrjhBn3xxRfauHGj8vPzde+997r8XH+Ny1IAAJhQbm6uLly4oHvuuUcdOnSQJPXs2bPG/r169dKsWbMkSREREXr11VeVnp6uYcOGafPmzTp69KgyMjIUFhYmSXrxxRc1bNiwGvf36quv6oYbbtBLL71kb1u+fLlsNpu++eYbde3a1RWnWS1mbgAAMKHevXvr1ltvVc+ePfX73/9eS5cu1c8//1xj/169elV637ZtW508eVKSlJWVJZvNZg82ktSvX7/LHn///v3aunWr/Pz87K9u3bpJko4ePersadUKMzcAAJiQp6enNm/erB07dujjjz/W4sWLNX36dH3++efV9m/WrFml9xaLRRUVFU4f/+zZsxoxYoTmzZtX5bO2bds6vd/aINwAAGBSFotFgwYN0qBBgzRz5kx16NBB69atc3g/1157rY4fP678/HyFhoZKknbv3n3ZbW688Ua99957Cg8Pl5dXw8YNLksBAGBCn3/+uV566SV98cUXysnJ0dq1a1VQUKDrrrvO4X0NGzZMnTt31rhx43TgwAFt375dM2bMkHQxQFVn0qRJ+umnnzR69Gjt3r1bR48e1aZNm5SQkKDy8vI6nduVEG4AADChgIAAffrppxo+fLi6du2qGTNmaNGiRbrjjjsc3penp6fWr1+vs2fPqm/fvnrwwQftd0v5+PhUu027du20fft2lZeX67bbblPPnj312GOPKSgoSB4e9Rs/LIaZf6KwGkVFRQoMDFRhYaECAgJcvv/xaZefprucZfF9XVgJAMAVzp8/r2+//VYdO3as8Yv8arR9+3YNHjxYR44cUefOnV2yz8uNtSPf36y5AQAAV7Ru3Tr5+fkpIiJCR44c0ZQpUzRo0CCXBRtXItwAAIArOnPmjJ5++mnl5OQoODhY0dHRWrRokbvLqhbhBgAAXFFcXJzi4uLcXUatsKAYAACYCuEGAIBauMruv3ELV40x4QYAgMu49Mu9586dc3Ml5ldaWirp4q3nddEo1twsWbJECxYsUF5ennr37q3Fixdf8ZkVkrRq1SqNHj1aI0eOvOwj1wEAcJanp6eCgoLsz1ny9fWt8Yfr4LyKigoVFBTI19e3zr9o7PZws3r1aiUmJio1NVX9+/dXSkqKYmJilJWVpZCQkBq3y87O1tSpUzVkyJAGrBYAcDW69MDISwEH9cPDw0PXXHNNncOj28NNcnKyJkyYoISEBElSamqqPvzwQy1fvlzPPPNMtduUl5frvvvu03PPPafPPvtMp0+fbsCKAQBXG4vForZt2yokJERlZWXuLse0rFarS3692K3hprS0VHv27NG0adPsbR4eHoqOjtbOnTtr3O75559XSEiIxo8fr88+++yyxygpKVFJSYn9fVFRUd0LBwBclTw9Peu8HgT1z60Lik+dOqXy8nL7E0YvCQ0NVV5eXrXbbNu2TcuWLdPSpUtrdYykpCQFBgbaXzabrc51AwCAxqtJ3S115swZjR07VkuXLlVwcHCttpk2bZoKCwvtr+PHj9dzlQAAwJ3celkqODhYnp6eys/Pr9Sen59vX7z1S0ePHlV2drZGjBhhb6uoqJAkeXl5KSsrq8ozLry9veXt7V0P1QMAgMbIrTM3VqtVffr0UXp6ur2toqJC6enpGjBgQJX+3bp105dffqnMzEz766677tLQoUOVmZnJJScAAOD+u6USExM1btw4RUVFqV+/fkpJSVFxcbH97qm4uDi1b99eSUlJ8vHxUY8ePSptHxQUJElV2gEAwNXJ7eEmNjZWBQUFmjlzpvLy8hQZGamNGzfaFxnn5OS45LYwAABwdbAYV9nDMoqKihQYGKjCwkIFBAS4fP/j03Y7ve2y+L4urAQAAPNw5PubKREAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqhBsAAGAqjSLcLFmyROHh4fLx8VH//v21a9euGvuuXbtWUVFRCgoKUosWLRQZGak333yzAasFAACNmdvDzerVq5WYmKhZs2Zp79696t27t2JiYnTy5Mlq+7dq1UrTp0/Xzp07deDAASUkJCghIUGbNm1q4MoBAEBj5PZwk5ycrAkTJighIUHdu3dXamqqfH19tXz58mr733zzzbr77rt13XXXqXPnzpoyZYp69eqlbdu2NXDlAACgMXJruCktLdWePXsUHR1tb/Pw8FB0dLR27tx5xe0Nw1B6erqysrL07//+79X2KSkpUVFRUaUXAAAwL7eGm1OnTqm8vFyhoaGV2kNDQ5WXl1fjdoWFhfLz85PVatWdd96pxYsXa9iwYdX2TUpKUmBgoP1ls9lceg4AAKBxcftlKWf4+/srMzNTu3fv1osvvqjExERlZGRU23fatGkqLCy0v44fP96wxQIAgAbl5c6DBwcHy9PTU/n5+ZXa8/PzFRYWVuN2Hh4e6tKliyQpMjJSBw8eVFJSkm6++eYqfb29veXt7e3SugEAQOPl1pkbq9WqPn36KD093d5WUVGh9PR0DRgwoNb7qaioUElJSX2UCAAAmhi3ztxIUmJiosaNG6eoqCj169dPKSkpKi4uVkJCgiQpLi5O7du3V1JSkqSLa2iioqLUuXNnlZSUaMOGDXrzzTf12muvufM0AABAI+H2cBMbG6uCggLNnDlTeXl5ioyM1MaNG+2LjHNycuTh8a8JpuLiYk2cOFHff/+9mjdvrm7duumtt95SbGysu04BAAA0IhbDMAx3F9GQioqKFBgYqMLCQgUEBLh8/+PTdju97bL4vi6sBAAA83Dk+7tJ3i0FAABQE8INAAAwFbevuQEAAI3QO3VYyzpmtevqcAIzNwAAwFQINwAAwFQINwAAwFQINwAAwFQINwAAwFQINwAAwFQINwAAwFQINwAAwFQINwAAwFScCjfHjh1zdR0AAAAu4VS46dKli4YOHaq33npL58+fd3VNAAAATnMq3Ozdu1e9evVSYmKiwsLC9PDDD2vXrl2urg0AAMBhToWbyMhIvfzyyzpx4oSWL1+u3NxcDR48WD169FBycrIKCgpcXScAAECt1GlBsZeXl+655x6tWbNG8+bN05EjRzR16lTZbDbFxcUpNzfXVXUCAADUSp3CzRdffKGJEyeqbdu2Sk5O1tSpU3X06FFt3rxZJ06c0MiRI11VJwAAQK14ObNRcnKyVqxYoaysLA0fPlxvvPGGhg8fLg+Pi1mpY8eOSktLU3h4uCtrBQAAuCKnws1rr72mBx54QPHx8Wrbtm21fUJCQrRs2bI6FQcAAOAop8LN4cOHr9jHarVq3LhxzuweAADAaU6tuVmxYoXWrFlTpX3NmjVauXJlnYsCAABwllPhJikpScHBwVXaQ0JC9NJLL9W5KAAAAGc5FW5ycnLUsWPHKu0dOnRQTk5OnYsCAABwllPhJiQkRAcOHKjSvn//frVu3brORQEAADjLqXAzevRo/fGPf9TWrVtVXl6u8vJyffLJJ5oyZYr+8Ic/uLpGAACAWnPqbqk5c+YoOztbt956q7y8Lu6ioqJCcXFxrLkBAABu5VS4sVqtWr16tebMmaP9+/erefPm6tmzpzp06ODq+gAAABziVLi5pGvXruratauragEAAKgzp8JNeXm50tLSlJ6erpMnT6qioqLS55988olLigMAAHCUU+FmypQpSktL05133qkePXrIYrG4ui4AAACnOBVuVq1apb/97W8aPny4q+sBAACoE6duBbdarerSpYurawEAAKgzp8LNE088oZdfflmGYbi6HgAAgDpx6rLUtm3btHXrVn300Ue6/vrr1axZs0qfr1271iXFAQAAOMqpcBMUFKS7777b1bUAAADUmVPhZsWKFa6uAwAAwCWcWnMjSRcuXNCWLVv0l7/8RWfOnJEknThxQmfPnnVZcQAAAI5yaubmu+++0+23366cnByVlJRo2LBh8vf317x581RSUqLU1FRX1wkAAFArTs3cTJkyRVFRUfr555/VvHlze/vdd9+t9PR0lxUHAADgKKdmbj777DPt2LFDVqu1Unt4eLh++OEHlxQGAADgDKdmbioqKlReXl6l/fvvv5e/v3+diwIAAHCWU+HmtttuU0pKiv29xWLR2bNnNWvWLB7JAAAA3Mqpy1KLFi1STEyMunfvrvPnz2vMmDE6fPiwgoOD9e6777q6RgAAgFpzKtz85je/0f79+7Vq1SodOHBAZ8+e1fjx43XfffdVWmAMAADQ0JwKN5Lk5eWl+++/35W1AAAA1JlT4eaNN9647OdxcXFOFQMAAFBXToWbKVOmVHpfVlamc+fOyWq1ytfXl3ADAADcxqm7pX7++edKr7NnzyorK0uDBw9mQTEAAHArp58t9WsRERGaO3dulVkdAACAhuSycCNdXGR84sQJV+4SAADAIU6tuXn//fcrvTcMQ7m5uXr11Vc1aNAglxQGAADgDKfCzahRoyq9t1gsatOmjW655RYtWrTIFXUBAAA4xalwU1FR4eo6AAAAXMKla24AAADczamZm8TExFr3TU5OduYQAAAATnEq3Ozbt0/79u1TWVmZrr32WknSN998I09PT9144432fhaLxTVVAgAA1JJT4WbEiBHy9/fXypUr1bJlS0kXf9gvISFBQ4YM0RNPPOHSIgEAAGrLqTU3ixYtUlJSkj3YSFLLli31wgsvcLcUAABwK6fCTVFRkQoKCqq0FxQU6MyZM3UuCgAAwFlOhZu7775bCQkJWrt2rb7//nt9//33eu+99zR+/Hjdc889rq4RAACg1pxac5OamqqpU6dqzJgxKisru7gjLy+NHz9eCxYscGmBAAAAjnAq3Pj6+urPf/6zFixYoKNHj0qSOnfurBYtWri0OAAAAEfV6Uf8cnNzlZubq4iICLVo0UKGYbiqLgAAAKc4FW5+/PFH3XrrreratauGDx+u3NxcSdL48eO5DRwAALiVU+Hm8ccfV7NmzZSTkyNfX197e2xsrDZu3Oiy4gAAABzl1Jqbjz/+WJs2bdJvfvObSu0RERH67rvvXFIYAACAM5yauSkuLq40Y3PJTz/9JG9v7zoXBQAA4Cynws2QIUP0xhtv2N9bLBZVVFRo/vz5Gjp0qMP7W7JkicLDw+Xj46P+/ftr165dNfZdunSphgwZopYtW6ply5aKjo6+bH8AAHB1cSrczJ8/X6+//rruuOMOlZaW6qmnnlKPHj306aefat68eQ7ta/Xq1UpMTNSsWbO0d+9e9e7dWzExMTp58mS1/TMyMjR69Ght3bpVO3fulM1m02233aYffvjBmVMBAAAm41S46dGjh7755hsNHjxYI0eOVHFxse655x7t27dPnTt3dmhfycnJmjBhghISEtS9e3elpqbK19dXy5cvr7b/22+/rYkTJyoyMlLdunXTX//6V1VUVCg9Pd2ZUwEAACbj8ILisrIy3X777UpNTdX06dPrdPDS0lLt2bNH06ZNs7d5eHgoOjpaO3furNU+zp07p7KyMrVq1araz0tKSlRSUmJ/X1RUVKeaAQBA4+bwzE2zZs104MABlxz81KlTKi8vV2hoaKX20NBQ5eXl1WofTz/9tNq1a6fo6OhqP09KSlJgYKD9ZbPZ6lw3AABovJy6LHX//fdr2bJlrq7FYXPnztWqVau0bt06+fj4VNtn2rRpKiwstL+OHz/ewFUCAICG5NTv3Fy4cEHLly/Xli1b1KdPnyrPlEpOTq7VfoKDg+Xp6an8/PxK7fn5+QoLC7vstgsXLtTcuXO1ZcsW9erVq8Z+3t7e3J4OAMBVxKFwc+zYMYWHh+uf//ynbrzxRknSN998U6mPxWKp9f6sVqv69Omj9PR0jRo1SpLsi4MnT55c43bz58/Xiy++qE2bNikqKsqRUwAAACbnULiJiIhQbm6utm7dKuni4xZeeeWVKmtmHJGYmKhx48YpKipK/fr1U0pKioqLi5WQkCBJiouLU/v27ZWUlCRJmjdvnmbOnKl33nlH4eHh9rU5fn5+8vPzc7oOAABgDg6Fm18/9fujjz5ScXFxnQqIjY1VQUGBZs6cqby8PEVGRmrjxo32wJSTkyMPj38tDXrttddUWlqq//iP/6i0n1mzZmn27Nl1qgUAADR9Tq25ueTXYcdZkydPrvEyVEZGRqX32dnZLjkmAAAwJ4fulrJYLFXW1DiyxgYAAKC+OXxZKj4+3n730fnz5/XII49UuVtq7dq1rqsQAADAAQ6Fm3HjxlV6f//997u0GAAAgLpyKNysWLGivuoAAABwCad+oRgAAKCxItwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABT8XJ3AQAAoGbj03Y7ve2y+L4urKTpYOYGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYitvDzZIlSxQeHi4fHx/1799fu3btqrHvV199pd/97ncKDw+XxWJRSkpKwxUKAACaBLeGm9WrVysxMVGzZs3S3r171bt3b8XExOjkyZPV9j937pw6deqkuXPnKiwsrIGrBQAATYFbw01ycrImTJighIQEde/eXampqfL19dXy5cur7d+3b18tWLBAf/jDH+Tt7V2rY5SUlKioqKjSCwAAmJfbwk1paan27Nmj6OjofxXj4aHo6Gjt3LnTZcdJSkpSYGCg/WWz2Vy2bwAA0Pi4LdycOnVK5eXlCg0NrdQeGhqqvLw8lx1n2rRpKiwstL+OHz/usn0DAIDGx8vdBdQ3b2/vWl/CAgAATZ/bZm6Cg4Pl6emp/Pz8Su35+fksFgYAAE5zW7ixWq3q06eP0tPT7W0VFRVKT0/XgAED3FUWAABo4tx6WSoxMVHjxo1TVFSU+vXrp5SUFBUXFyshIUGSFBcXp/bt2yspKUnSxUXIX3/9tf2ff/jhB2VmZsrPz09dunRx23kAAIDGw63hJjY2VgUFBZo5c6by8vIUGRmpjRs32hcZ5+TkyMPjX5NLJ06c0A033GB/v3DhQi1cuFA33XSTMjIyGrp8AADQCLl9QfHkyZM1efLkaj/7dWAJDw+XYRgNUBUAAGiq3P74BQAAAFci3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFMh3AAAAFPxcncBAACgfoxP2+30tsusLiykgTFzAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVwAwAATIVfKG5M3ol1ftsxq11XBwAATRgzNwAAwFQINwAAwFS4LAWYXV0ud9YFl0oBuAnhBmgK3BVQAKAJ4rIUAAAwFWZuXOzR/BnOb2wLclkdAABcrZi5AQAApkK4AQAApsJlKdTJ+LTdTm+7LL6vCysBAOAiZm4AAICpEG4AAICpcFkKAIBGrC534S4OfcGFlTQdhBuzaIIP3WS9DgA0XpnHTzu9baTLqnAO4QZA49MEwzqAxoNwAzSUq+0RClfb+QJoNFhQDAAATIVwAwAATIXLUgAA1Dcu0zYoZm4AAICpEG4AAICpEG4AAICpEG4AAICpsKAYTRK/bowa8QOAwFWPmRsAAGAqzNw0InV6joctyGV1AADQlBFuAACoZ3X5yyscx2UpAABgKszcAI7gV0YBoNFj5gYAAJgK4QYAAJgKl6Vw1anTb+RYXVgIAKBeEG4A4BJ+ABAwBcINrjqP5s9wfmN+TwgAGj3CDdymLiFjcegLLqwEAGAmLCgGAACmwswN6rTAtk6XeJogHpEBAI0f4QYAgFrInBfj7hJQS4QbAHAF7rQCGg3CDZqkq+1yWF1wKa0JqEMwqtO/36c3Ob0tYQ6NGeHGJOryP7hHRVBA40IgA1zjav2LIOEGQL2oS0Bx13GvumDURB8EW6dfGY/v68JK0Fg1inCzZMkSLViwQHl5eerdu7cWL16sfv361dh/zZo1+tOf/qTs7GxFRERo3rx5Gj58eANWDFwd3BVQ0PjV6c9GXRfm1uF3rlgUfHVwe7hZvXq1EhMTlZqaqv79+yslJUUxMTHKyspSSEhIlf47duzQ6NGjlZSUpN/+9rd65513NGrUKO3du1c9evRwwxkAtcOMQuPnrn9HTXGWy52u1kstqD2LYRiGOwvo37+/+vbtq1dffVWSVFFRIZvNpkcffVTPPPNMlf6xsbEqLi7WBx98YG/7t3/7N0VGRio1NfWKxysqKlJgYKAKCwsVEBDguhP5//hbAQDgalenxeo1cOT7260zN6WlpdqzZ4+mTZtmb/Pw8FB0dLR27txZ7TY7d+5UYmJipbaYmBitX7++2v4lJSUqKSmxvy8sLJR0cZDqw9nzF+plvwAANBX18R17aZ+1mZNxa7g5deqUysvLFRoaWqk9NDRUhw4dqnabvLy8avvn5eVV2z8pKUnPPfdclXabzeZk1QAA4LJmB9bbrs+cOaPAwMvv3+1rburbtGnTKs30VFRU6KefflLr1q1lsVhceqyioiLZbDYdP368Xi554SLGuWEwzg2DcW44jHXDqK9xNgxDZ86cUbt27a7Y163hJjg4WJ6ensrPz6/Unp+fr7CwsGq3CQsLc6i/t7e3vL29K7UFBQU5X3QtBAQE8B9OA2CcGwbj3DAY54bDWDeM+hjnK83YXOLWp4JbrVb16dNH6enp9raKigqlp6drwIAB1W4zYMCASv0lafPmzTX2BwAAVxe3X5ZKTEzUuHHjFBUVpX79+iklJUXFxcVKSEiQJMXFxal9+/ZKSkqSJE2ZMkU33XSTFi1apDvvvFOrVq3SF198oddff92dpwEAABoJt4eb2NhYFRQUaObMmcrLy1NkZKQ2btxoXzSck5MjD49/TTANHDhQ77zzjmbMmKFnn31WERERWr9+faP4jRtvb2/NmjWrymUwuBbj3DAY54bBODccxrphNIZxdvvv3AAAALiSW9fcAAAAuBrhBgAAmArhBgAAmArhBgAAmArhxkFLlixReHi4fHx81L9/f+3ateuy/desWaNu3brJx8dHPXv21IYNGxqo0qbNkXFeunSphgwZopYtW6ply5aKjo6+4r8XXOTon+dLVq1aJYvFolGjRtVvgSbh6DifPn1akyZNUtu2beXt7a2uXbvy/45acHScU1JSdO2116p58+ay2Wx6/PHHdf78+Qaqtmn69NNPNWLECLVr104Wi6XG5zr+UkZGhm688UZ5e3urS5cuSktLq/c6ZaDWVq1aZVitVmP58uXGV199ZUyYMMEICgoy8vPzq+2/fft2w9PT05g/f77x9ddfGzNmzDCaNWtmfPnllw1cedPi6DiPGTPGWLJkibFv3z7j4MGDRnx8vBEYGGh8//33DVx50+LoOF/y7bffGu3btzeGDBlijBw5smGKbcIcHeeSkhIjKirKGD58uLFt2zbj22+/NTIyMozMzMwGrrxpcXSc3377bcPb29t4++23jW+//dbYtGmT0bZtW+Pxxx9v4Mqblg0bNhjTp0831q5da0gy1q1bd9n+x44dM3x9fY3ExETj66+/NhYvXmx4enoaGzdurNc6CTcO6NevnzFp0iT7+/LycqNdu3ZGUlJStf3vvfde484776zU1r9/f+Phhx+u1zqbOkfH+dcuXLhg+Pv7GytXrqyvEk3BmXG+cOGCMXDgQOOvf/2rMW7cOMJNLTg6zq+99prRqVMno7S0tKFKNAVHx3nSpEnGLbfcUqktMTHRGDRoUL3WaSa1CTdPPfWUcf3111dqi42NNWJiYuqxMsPgslQtlZaWas+ePYqOjra3eXh4KDo6Wjt37qx2m507d1bqL0kxMTE19odz4/xr586dU1lZmVq1alVfZTZ5zo7z888/r5CQEI0fP74hymzynBnn999/XwMGDNCkSZMUGhqqHj166KWXXlJ5eXlDld3kODPOAwcO1J49e+yXro4dO6YNGzZo+PDhDVLz1cJd34Nu/4XipuLUqVMqLy+3/3LyJaGhoTp06FC12+Tl5VXbPy8vr97qbOqcGedfe/rpp9WuXbsq/0HhX5wZ523btmnZsmXKzMxsgArNwZlxPnbsmD755BPdd9992rBhg44cOaKJEyeqrKxMs2bNaoiymxxnxnnMmDE6deqUBg8eLMMwdOHCBT3yyCN69tlnG6Lkq0ZN34NFRUX6v//7PzVv3rxejsvMDUxl7ty5WrVqldatWycfHx93l2MaZ86c0dixY7V06VIFBwe7uxxTq6ioUEhIiF5//XX16dNHsbGxmj59ulJTU91dmqlk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",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "data = df_host_multi.cpu_utilization\n",
+ "plt.hist(data, weights=np.ones_like(data) / len(data),\n",
+ " alpha=0.7, label=\"multi\", bins=30)\n",
+ "\n",
+ "\n",
+ "data = df_host_single.cpu_utilization\n",
+ "plt.hist(data, weights=np.ones_like(data) / len(data),\n",
+ " alpha=0.7, label=\"single\", bins=30)\n",
+ "\n",
+ "plt.xlabel(\"CPU utilization\")\n",
+ "plt.ylabel(\"Frequency\")\n",
+ "plt.legend()\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 105,
+ "id": "42c0c638",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "<matplotlib.legend.Legend at 0x7f6fc2d09510>"
+ ]
+ },
+ "execution_count": 105,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGdCAYAAACyzRGfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA/I0lEQVR4nO3deXRU9f3/8ddkTyALAZIQk2BYBNSgiIARRZRIAKUgaAVThRbxR0UtxgpSFQxKsfpFcQG0YMG2RK0IqKgghrKo7LKrURABla1qFoIJIXN/f4S5MLLNZCYzN5Pn45w5MHNv7n3PJWFe+WzXZhiGIQAAAB8J8ncBAACgfiF8AAAAnyJ8AAAAnyJ8AAAAnyJ8AAAAnyJ8AAAAnyJ8AAAAnyJ8AAAAnwrxdwG/Zrfb9cMPPyg6Olo2m83f5QAAABcYhqHS0lIlJycrKOjsbRuWCx8//PCDUlNT/V0GAACogb179yolJeWs+1gufERHR0uqLj4mJsbP1QAAAFeUlJQoNTXV/Bw/G8uFD0dXS0xMDOEDAIA6xpUhEww4BQAAPkX4AAAAPkX4AAAAPmW5MR8AgLOrqqpSZWWlv8tAPRQaGqrg4GCPj0P4AIA65PDhw/ruu+9kGIa/S0E9ZLPZlJKSooYNG3p0HMIHANQRVVVV+u677xQVFaWmTZuyECN8yjAMHTp0SN99951at27tUQsI4QMA6ojKykoZhqGmTZsqMjLS3+WgHmratKm+/fZbVVZWehQ+GHAKAHUMLR7wF2997xE+AACAT7kVPqZPn6727dubq49mZmbqgw8+MLd3795dNpvN6TFixAivFw0AQH23bNky2Ww2FRUVSZJmz56tuLg4v9bkKrfGfKSkpOjJJ59U69atZRiGXn31VfXr108bN27URRddJEkaPny4JkyYYH5NVFSUdysGAACnuPXWW9WnTx9/l+ESt8JH3759nZ5PnDhR06dP1+rVq83wERUVpaSkJO9VCADASSorKxUaGur14x49elRhYWFeP66vREZG1pmByDWe7VJVVaU333xTZWVlyszMNF+fM2eO/v3vfyspKUl9+/bVo48+etbWj4qKClVUVJjPS0pKaloS6on3tuzT+t0/+bsMJ9e2SVC3C5r6uwzAkubOnau8vDzt2LFDUVFR6tChg95++201aNBAkjRz5kxNnjxZu3bt0vnnn6/77rtPd999tyTp22+/VXp6ul5//XVNmzZNa9as0VNPPaUxY8Zo3rx56t27t3me+fPn64477tCBAwcUFRWlvXv36oEHHtCHH36ooKAgXX311Xruued0/vnnS5KGDh2qoqIiderUSVOnTlV4eLh27dqladOm6dlnn9XevXsVGxurq6++WnPnzj3te5s9e7ZGjRql2bNn68EHH9TevXt1zTXXaObMmUpNTTX3e/vtt5WXl6fPP/9cycnJGjJkiB5++GGFhFR/DNtsNs2YMUPvvfeeFi9erPPOO0+TJ0/Wb37zG/MY77//vkaNGqW9e/fqiiuu0JAhQ05bi6Mb5rHHHtOCBQv0wAMP6NFHH9XPP/+s3r17a8aMGeadZ0tLSzVixAgtWLBAMTExGj16tN5++21deumlmjJlSs3/0c/B7fCxdetWZWZmqry8XA0bNtT8+fN14YUXSpJuu+02NW/eXMnJydqyZYvGjBmjwsJCzZs374zHmzRpkvLy8mr+DlCvHK44pvte36gqu7UWWHp38w9a/8j1/i4D9YxhGPqlssov544MDXZp5sO+ffs0ePBgPfXUU7rppptUWlqqlStXmoukzZkzR+PGjdOLL76oDh06aOPGjRo+fLgaNGjg9OH60EMPafLkyerQoYMiIiK0cuVK5efnO4WPOXPmqH///oqKilJlZaWys7OVmZmplStXKiQkRE888YR69eqlLVu2mC0cBQUFiomJ0ZIlSyRJ69ev13333ad//etfuvLKK/XTTz9p5cqVZ32PR44c0cSJE/XPf/5TYWFhuvvuuzVo0CB98sknkqSVK1fqjjvu0PPPP6+rr75aO3fu1F133SVJGj9+vHmcvLw8PfXUU3r66af1wgsvKCcnR7t371Z8fLz27t2rAQMGaOTIkbrrrru0fv16PfDAA+e8/jt37tSCBQu0cOFC/fzzz/rtb3+rJ598UhMnTpQk5ebm6pNPPtE777yjxMREjRs3Tp999pkuvfTScx7bE26HjzZt2mjTpk0qLi7W3LlzNWTIEC1fvlwXXniheTElKSMjQ82aNVOPHj20c+dOtWzZ8rTHGzt2rHJzc83nJSUlTmkROFnJL5WqshsKDrJpxDUt/F2OSsuP6Z+rdquswj8fAKjffqms0oXjFvvl3J9PyFZU2Lk/Qvbt26djx45pwIABat68uaTqzweH8ePHa/LkyRowYIAkKT09XZ9//rlefvllp/AxatQocx9JysnJ0e23364jR44oKipKJSUleu+99zR//nxJ0htvvCG73a6ZM2eaIWnWrFmKi4vTsmXL1LNnT0lSgwYNNHPmTDOMzJs3Tw0aNNCNN96o6OhoNW/eXB06dDjre6ysrNSLL76oLl26SJJeffVVtWvXTmvXrlXnzp2Vl5enhx56yHw/LVq00OOPP67Ro0c7hY+hQ4dq8ODBkqS//vWvev7557V27Vr16tVL06dPV8uWLTV58mRJ1Z/FW7du1d/+9rez1ma32zV79myzpeP2229XQUGBJk6cqNLSUr366qvKz89Xjx49zGuUnJx81mN6g9vhIywsTK1atZIkdezYUevWrdNzzz2nl19++ZR9Hf8QO3bsOGP4CA8PV3h4uLtloJ46crT6Q75BWLAezG7r52qk734+on+u2i1D1mqJAazikksuUY8ePZSRkaHs7Gz17NlTN998sxo1aqSysjLt3LlTw4YN0/Dhw82vOXbsmGJjY52Oc/nllzs979Onj0JDQ/XOO+9o0KBBeuuttxQTE6OsrCxJ0ubNm7Vjxw7zQ9ehvLxcO3fuNJ9nZGQ4jfO4/vrr1bx5c7Vo0UK9evVSr169dNNNN511+EBISIg6depkPm/btq3i4uL0xRdfqHPnztq8ebM++eQTs7VBqh66UF5eboYnSWrfvr25vUGDBoqJidHBgwclSV988YX5mepw8pCHMzn//POdrkGzZs3MY37zzTeqrKxU586dze2xsbFq06bNOY/rKY9XOLXb7U5jNk62adMmSdVvFvCGX46HD1d+4/IlbrMBf4gMDdbnE7L9dm5XBAcHa8mSJfr000/14Ycf6oUXXtDDDz+sNWvWmB+6M2bMOOWD9derZzrGhziEhYXp5ptvVn5+vgYNGqT8/Hzdeuut5hiKw4cPq2PHjpozZ84pNTVtemJ81q+PGx0drc8++0zLli3Thx9+qHHjxumxxx7TunXrajyN9fDhw8rLy3NquXGIiIgw//7rQbQ2m012u71G56zNY3qDW/+Djx07Vr1791ZaWppKS0uVn5+vZcuWafHixdq5c6fy8/PVp08fNW7cWFu2bNH999+vbt26OaU5wBNHjh6TJEWFeX5XRW9gpUn4k81ms1wQPx2bzaauXbuqa9euGjdunJo3b6758+crNzdXycnJ+uabb5STk+P2cXNycnT99ddr+/btWrp0qZ544glz22WXXaY33nhDCQkJiomJceu4ISEhysrKUlZWlsaPH6+4uDgtXbr0tOFBqm6pWb9+vdmCUFhYqKKiIrVr186spbCw0Ow1qIl27drpnXfecXpt9erVNT6eVN39ExoaqnXr1iktLU2SVFxcrK+++krdunXz6Njn4tZ37cGDB3XHHXdo3759io2NVfv27bV48WJdf/312rt3rz766CNNmTJFZWVlSk1N1cCBA/XII4/UVu2oh44cH1wXaZXwcfxPGj6A01uzZo0KCgrUs2dPJSQkaM2aNTp06JD5wZyXl6f77rtPsbGx6tWrlyoqKrR+/Xr9/PPPTuMBT6dbt25KSkpSTk6O0tPTnVpPcnJy9PTTT6tfv36aMGGCUlJStHv3bs2bN0+jR49WSkrKaY+5cOFCffPNN+rWrZsaNWqk999/X3a7/axdEaGhobr33nv1/PPPKyQkRPfcc4+uuOIKM4yMGzdON954o9LS0nTzzTcrKChImzdv1rZt25wC09mMGDFCkydP1oMPPqg777xTGzZs0OzZs1362jOJjo7WkCFD9OCDDyo+Pl4JCQkaP368goKCav0XK7fCxyuvvHLGbampqVq+fLnHBQFnc6TCMebD+r/tAZBiYmK0YsUKTZkyRSUlJWrevLkmT55szlK58847FRUVpaeffloPPvigGjRooIyMDI0aNeqcx7bZbOZMmnHjxjlti4qK0ooVKzRmzBgNGDBApaWlOu+889SjR4+ztoTExcVp3rx5euyxx1ReXq7WrVvrtddeM9eyOp2oqCiNGTNGt912m77//ntdffXVTp+X2dnZWrhwoSZMmKC//e1vCg0NVdu2bXXnnXee8z06pKWl6a233tL999+vF154QZ07d9Zf//pX/eEPf3D5GKfzzDPPaMSIEbrxxhvNqbZ79+516g6qDTbDsFZvdUlJiWJjY1VcXOx2UxkC35vr9+rBuVt0zQVN9eofOp/7C2rZvuJflDlpqcKCg/TVxN7n/gLAA+Xl5dq1a5fS09Nr/cMBrvn12hp1XVlZmbnGyLBhw07ZfrbvQXc+v/n1EXWKY00Dq4z5cGC2C4C6aOPGjfryyy/VuXNnFRcXm7dH6devX62el/ABj0x493Plr93ts9kejsXFrDPmgwGnAOq2//u//1NhYaHCwsLUsWNHrVy5Uk2aNKnVcxI+4JF3Nn+v8krfT9u6LK2Rz895NtbqvATgK0OHDtXQoUP9XUaNdejQQRs2bPD5eQkf8MjRY9XBI//OLkpr7Js7GEeEBqtJQ2ssTMdMWwBwH+EDHjl2vBskpVGUUhr5JnxYCVNtAcB9Qf4uAHVbZVV1y0doCE0AAADXED5QY4ZhqLKq+nf+kKB6+q10PHNZbMY6AFhaPf3EgDccO+m29mHB9ftbiegBAK6r358Y8Iijy0WSQoLrZ7cLU20BwH2ED9SYo8tFkkLracuHzex28W8dAALL0KFD1b9/f3+XUWuY7YIaO7nlI7SetnwAgCe+/fZbpaena+PGjbr00kvN15977rmAHktG+ECNHTMHm9rq7a3l6+e7BvyrsrJSoaGhXj/u0aNHFRYW5vXj1kRsbKy/S6hV9bOtHF5hTrOtp10uvxbIv6UAnpg7d64yMjIUGRmpxo0bKysrS2VlZeb2mTNnql27doqIiFDbtm01bdo0c9u3334rm82mN954Q9dcc40iIiI0ffp0RUZG6oMPPnA6z/z58xUdHa0jR45Ikvbu3avf/va3iouLU3x8vPr166dvv/3W3N/RtTFx4kQlJyerTZs2kqRp06apdevWioiIUGJiom6++eYzvrcff/xRgwcP1nnnnaeoqChlZGTotddec9rHbrfrqaeeUqtWrRQeHq60tDRNnDhRkpSeni6peqVRm82m7t27O9UmSX//+9+VnJwsu915Nel+/fo53dX27bff1mWXXaaIiAi1aNFCeXl5Onbs2Blr9ydaPlBjJ8JH/f39v762+MAiDEOqPOKfc4dGubTE7759+8zb3t90000qLS3VypUrzbA+Z84cjRs3Ti+++KI6dOigjRs3avjw4WrQoIGGDBliHuehhx7S5MmT1aFDB0VERGjlypXKz89X794n7iY9Z84c9e/fX1FRUaqsrFR2drYyMzO1cuVKhYSE6IknnlCvXr20ZcsWs4WjoKBAMTExWrJkiSRp/fr1uu+++/Svf/1LV155pX766SetXLnyjO+vvLxcHTt21JgxYxQTE6P33ntPt99+u1q2bKnOnavvvD127FjNmDFDzz77rK666irt27dPX375pSRp7dq16ty5sz766CNddNFFp215ueWWW3Tvvffqv//9r3r06CFJ+umnn7Ro0SK9//77kqSVK1fqjjvu0PPPP6+rr75aO3fu1F133SVJGj9+/Dn/nXyN8IEacww4peWjmmGw3Dp8rPKI9Ndk/5z7Lz9IYQ3Oudu+fft07NgxDRgwQM2bN5ckZWRkmNvHjx+vyZMna8CAAZKqWwI+//xzvfzyy07hY9SoUeY+kpSTk6Pbb79dR44cUVRUlEpKSvTee+9p/vz5kqQ33nhDdrtdM2fONH9JmDVrluLi4rRs2TL17NlTktSgQQPNnDnT/NCfN2+eGjRooBtvvFHR0dFq3ry5OnTocMb3d9555+nPf/6z+fzee+/V4sWL9Z///EedO3dWaWmpnnvuOb344ovm+2nZsqWuuuoqSVLTpk0lSY0bN1ZSUtJpz9GoUSP17t1b+fn5ZviYO3eumjRpomuvvVaSlJeXp4ceesg8R4sWLfT4449r9OjRlgwffGqgxuh2YcwHcC6XXHKJevTooYyMDN1yyy2aMWOGfv75Z0lSWVmZdu7cqWHDhqlhw4bm44knntDOnTudjnP55Zc7Pe/Tp49CQ0P1zjvvSJLeeustxcTEKCsrS5K0efNm7dixQ9HR0eZx4+PjVV5e7nTsjIwMp9aG66+/Xs2bN1eLFi10++23a86cOWY3zulUVVXp8ccfV0ZGhuLj49WwYUMtXrxYe/bskSR98cUXqqioMENDTeXk5Oitt95SRUWFpOpWnkGDBino+AKPmzdv1oQJE5yu4/Dhw7Vv376z1u8vtHygxhzho76u8SE5t3Qw4gM+FxpV3QLhr3O7IDg4WEuWLNGnn36qDz/8UC+88IIefvhhrVmzRlFR1ceYMWOGunTpcsrXnaxBA+dWlrCwMN18883Kz8/XoEGDlJ+fr1tvvVUhIdUfa4cPH1bHjh01Z86cU2pytDac7rjR0dH67LPPtGzZMn344YcaN26cHnvsMa1bt05xcXGnHOvpp5/Wc889pylTpigjI0MNGjTQqFGjdPToUUlSZGSkS9fpXPr27SvDMPTee++pU6dOWrlypZ599llz++HDh5WXl+fUOuQQERHhlRq8ifCBGnN0u9T31U0dqvuw628Qgx/YbC51ffibzWZT165d1bVrV40bN07NmzfX/PnzlZubq+TkZH3zzTfKyclx+7g5OTm6/vrrtX37di1dulRPPPGEue2yyy7TG2+8oYSEBMXExLh13JCQEGVlZSkrK0vjx49XXFycli5detoP9k8++UT9+vXT7373O0nVg0u/+uorXXjhhZKk1q1bKzIyUgUFBbrzzjtP+XpHq0tVVdVZa4qIiNCAAQM0Z84c7dixQ23atNFll13m9H4LCwvVqlUrt96rvxA+UGPH6HZhhVPgHNasWaOCggL17NlTCQkJWrNmjQ4dOqR27dpJqh6rcN999yk2Nla9evVSRUWF1q9fr59//lm5ublnPXa3bt2UlJSknJwcpaenO7We5OTk6Omnn1a/fv00YcIEpaSkaPfu3Zo3b55Gjx6tlJSU0x5z4cKF+uabb9StWzc1atRI77//vux2uzkT5tdat26tuXPn6tNPP1WjRo30zDPP6MCBA2b4iIiI0JgxYzR69GiFhYWpa9euOnTokLZv365hw4YpISFBkZGRWrRokVJSUhQREXHGabY5OTm68cYbtX37djPsOIwbN0433nij0tLSdPPNNysoKEibN2/Wtm3bnEKZVdTfTw147CjdLk7odgFOFRMToxUrVqhPnz664IIL9Mgjj2jy5MnmLJU777xTM2fO1KxZs5SRkaFrrrlGs2fPNqegno3NZtPgwYO1efPmU1pOoqKitGLFCqWlpWnAgAFq166dhg0bpvLy8rO2hMTFxWnevHm67rrr1K5dO7300kt67bXXdNFFF512/0ceeUSXXXaZsrOz1b17dyUlJZ2yMumjjz6qBx54QOPGjVO7du1066236uDBg5KqW1mef/55vfzyy0pOTla/fv3OWNt1112n+Ph4FRYW6rbbbnPalp2drYULF+rDDz9Up06ddMUVV+jZZ581B/lajc2w2OIEJSUlio2NVXFxsdtNZfXB25u+15zVe2S3wD9b0S+V2nHwsC5NjdOCkV39XY5fFP9SqUvyPpQkfT2xd71uBULtKy8v165du5Senm7JfnwEvrN9D7rz+U23Sx3zfMHX2nmo7Nw7+tB5jbwzoKouchpw6v88CAB1AuGjjjlcUb1a3cN92ik13v8f+kE2mzJbNvZ3GZZg0PECAC4hfNQxR45Wj4i+rl2CWjZt6OdqwGgXAHAfHdR1iGEY+uV4+IgKCz7H3vA1ul0AwDWEjzrkaJVdx+zVn3BRYTRaWQH3dgEA9xE+6hBHq4dEywdQn1lskiLqEW997xE+6hDHeI/QYBtTOi2Cdg/4kmPJccfS3YCvOb73fr38vbtou69DHOEjMpRWD6tgqi18KSQkRFFRUTp06JBCQ0PNm4oBvmC323Xo0CFFRUWZ99CpKcKHhVXZDe3632HzQ+3rg4clMd7Dqphqi9pms9nUrFkz7dq1S7t37/Z3OaiHgoKClJaW5vF4Nz7FLOzuORu0ePuBU15nvId1cG8X+FpYWJhat25N1wv8IiwszCstboQPC9v6XbEkKSYiRCHHx3gE2aTfdkr1Z1k4A7pd4CtBQUEsr446jfBhYWXHx3jMu/tKtUqI9nM1OB1m2gKA+xitZGGOqbWRjPGoE2j4AADXED4s6liV3bxlfRSzW+oE1l4AANcQPizqSOWJBcUiGWBqWXS7AID73Aof06dPV/v27RUTE6OYmBhlZmbqgw8+MLeXl5dr5MiRaty4sRo2bKiBAwfqwIFTZ2vg3BxdLkE2KTyEjFgX0O4BAK5x61MtJSVFTz75pDZs2KD169fruuuuU79+/bR9+3ZJ0v333693331Xb775ppYvX64ffvhBAwYMqJXCA90R8wZyIdw/xMKYagsA7nNrJGPfvn2dnk+cOFHTp0/X6tWrlZKSoldeeUX5+fm67rrrJEmzZs1Su3bttHr1al1xxRXeq7oeOHL0mCTW9KhLGPIBAK6p8TSKqqoqvfnmmyorK1NmZqY2bNigyspKZWVlmfu0bdtWaWlpWrVq1RnDR0VFhSoqKsznJSUlNS2pTtuw+ye9t2W/uUrmwdLqa0L4sDanRinCBwC4xO3wsXXrVmVmZqq8vFwNGzbU/PnzdeGFF2rTpk0KCwtTXFyc0/6JiYnav3//GY83adIk5eXluV14oBnz1lbtOL58+smaNAz3QzVwFZ0uAOA+t8NHmzZttGnTJhUXF2vu3LkaMmSIli9fXuMCxo4dq9zcXPN5SUmJUlPr3wqeP5VVL5U8uHOa4huESpKCbDbd0L6ZP8uCG7i3CwC4xu3wERYWplatWkmSOnbsqHXr1um5557TrbfeqqNHj6qoqMip9ePAgQNKSko64/HCw8MVHs5v944xHnd3b6nU+Cg/VwNXMRgYANzn8RxOu92uiooKdezYUaGhoSooKDC3FRYWas+ePcrMzPT0NAHNbjdUXlm9oBhretRdDDgFANe41fIxduxY9e7dW2lpaSotLVV+fr6WLVumxYsXKzY2VsOGDVNubq7i4+MVExOje++9V5mZmcx0OYdfTlpQjAGmdQvtHgDgPrfCx8GDB3XHHXdo3759io2NVfv27bV48WJdf/31kqRnn31WQUFBGjhwoCoqKpSdna1p06bVSuGBxLGmhyRFhBA+6pKTe11o+AAA17gVPl555ZWzbo+IiNDUqVM1depUj4qqb05e0yMoiN+l6yru7QIArmHdbgs4sZoprR51DQNOAcB9hA8LcIQPBpvWbbR7AIBrarzCaV2z639l6jVlhb/LOC378eb6qNB6888BAKjH6s2nnWEYqjhm93cZZ3VZ80b+LgE1YLNVT7NlyAcAuKbehI/U+Ch9POZaf5dxRsFBNiXFRPi7DHiAFU4BwDX1JnyEBgcppRErh8L7bGK8BwC4gwGngLeQQADAJYQPwENMtwUA9xA+AC+h4QMAXEP4ADzkaPdgtgsAuIbwAXiIXhcAcA/hA/ASptoCgGsIH4CHbKLpAwDcQfgAvIQxHwDgGsIH4KnjDR9kDwBwDeED8BCdLgDgHsIH4CUG/S4A4BLCB+AhptoCgHsIH4CX0PABAK4hfAAeYqotALiH8AEAAHyK8AF4yDHmg24XAHAN4QPwEJ0uAOAewgfgJdzbBQBcQ/gAPGRjri0AuIXwAXgJYz4AwDWED8BDjnYPsgcAuIbwAXiKXhcAcAvhA/AS7u0CAK4hfAAeouEDANxD+AC8hHYPAHAN4QPwkGOqLb0uAOAawgcAAPApwgfgoRNrjNH0AQCuIHwAHmLAKQC4h/ABeAljPgDANYQPwEPc2wUA3ONW+Jg0aZI6deqk6OhoJSQkqH///iosLHTap3v37rLZbE6PESNGeLVowIpo+AAA17gVPpYvX66RI0dq9erVWrJkiSorK9WzZ0+VlZU57Td8+HDt27fPfDz11FNeLRqwEvPeLqQPAHBJiDs7L1q0yOn57NmzlZCQoA0bNqhbt27m61FRUUpKSvJOhYDF0esCAO7xaMxHcXGxJCk+Pt7p9Tlz5qhJkya6+OKLNXbsWB05cuSMx6ioqFBJSYnTA6iLDDpeAMAlbrV8nMxut2vUqFHq2rWrLr74YvP12267Tc2bN1dycrK2bNmiMWPGqLCwUPPmzTvtcSZNmqS8vLyalgFYAE0fAOCOGoePkSNHatu2bfr444+dXr/rrrvMv2dkZKhZs2bq0aOHdu7cqZYtW55ynLFjxyo3N9d8XlJSotTU1JqWBfgNYz4AwDU1Ch/33HOPFi5cqBUrViglJeWs+3bp0kWStGPHjtOGj/DwcIWHh9ekDMASHGM+CB8A4Bq3wodhGLr33ns1f/58LVu2TOnp6ef8mk2bNkmSmjVrVqMCAQBAYHErfIwcOVL5+fl6++23FR0drf3790uSYmNjFRkZqZ07dyo/P199+vRR48aNtWXLFt1///3q1q2b2rdvXytvAPA3c6otA04BwCVuhY/p06dLql5I7GSzZs3S0KFDFRYWpo8++khTpkxRWVmZUlNTNXDgQD3yyCNeKxiwGqbaAoB73O52OZvU1FQtX77co4KAuooxHwDgGu7tAnjIxlRbAHAL4QMAAPgU4QPwEFNtAcA9hA/AQ3S6AIB7CB+AlzDVFgBcQ/gAPGQ73u9CtwsAuIbwAQAAfIrwAXgJDR8A4BrCBwAA8CnCB+ChE1NtafsAAFcQPgAPcW8XAHAP4QPwEto9AMA1hA/AQ457u9DrAgCuIXwAAACfInwAHjox5oOmDwBwBeED8BDjTQHAPYQPwEsY8wEAriF8AB4y7+3i5zoAoK4gfAAAAJ8ifAAecoz5oNsFAFxD+AA8xYhTAHAL4QPwEu7tAgCuIXwAHjK7XfxaBQDUHYQPAADgU4QPwEPmVFuaPgDAJYQPAADgU4QPwEMnxnzQ9AEAriB8AB6yMeIUANxC+AAAAD5F+AA8ZBP3dgEAdxA+AACATxE+AA85xnww1RYAXEP4AAAAPkX4ALyEqbYA4BrCB+AhVjgFAPcQPgAAgE8RPgAPscYYALjHrfAxadIkderUSdHR0UpISFD//v1VWFjotE95eblGjhypxo0bq2HDhho4cKAOHDjg1aIBAEDd5Vb4WL58uUaOHKnVq1dryZIlqqysVM+ePVVWVmbuc//99+vdd9/Vm2++qeXLl+uHH37QgAEDvF44YBUnptrS9gEArghxZ+dFixY5PZ89e7YSEhK0YcMGdevWTcXFxXrllVeUn5+v6667TpI0a9YstWvXTqtXr9YVV1zhvcoBizDDh3/LAIA6w6MxH8XFxZKk+Ph4SdKGDRtUWVmprKwsc5+2bdsqLS1Nq1atOu0xKioqVFJS4vQAAACBq8bhw263a9SoUeratasuvvhiSdL+/fsVFhamuLg4p30TExO1f//+0x5n0qRJio2NNR+pqak1LQnwC8e9XWj6AADX1Dh8jBw5Utu2bdPrr7/uUQFjx45VcXGx+di7d69HxwMAANbm1pgPh3vuuUcLFy7UihUrlJKSYr6elJSko0ePqqioyKn148CBA0pKSjrtscLDwxUeHl6TMgBLODHmg6YPAHCFWy0fhmHonnvu0fz587V06VKlp6c7be/YsaNCQ0NVUFBgvlZYWKg9e/YoMzPTOxUDFmOu80H2AACXuNXyMXLkSOXn5+vtt99WdHS0OY4jNjZWkZGRio2N1bBhw5Sbm6v4+HjFxMTo3nvvVWZmJjNdAACAJDfDx/Tp0yVJ3bt3d3p91qxZGjp0qCTp2WefVVBQkAYOHKiKigplZ2dr2rRpXikWsCTu7QIAbnErfLiyiFJERISmTp2qqVOn1rgoAAAQuLi3C+Ah7u0CAO4hfAAAAJ8ifAAe4t4uAOAewgfgIbpdAMA9hA8AAOBThA/AQzam2gKAWwgfAADApwgfgIds5t9o+gAAVxA+AA+dmO3i3zoAoK4gfAAAAJ8ifAAesh3veKHhAwBc49a9XQCcKtJepr5Bnyp5917JHufvchBIoppILa+VgoL9XQngVYQPwENDS1/WtWEfSutV/QC86dY5Ursb/V0F4FWED8BDjar+J0kqiblAMU1T/VwNAsb+rVLZQal0n78rAbyO8AF4KFh2SdKONsN12Q13+bkaBIy37pS2vilVHfV3JYDXMeAU8FCwqiRJdtEvDy8KCq3+k/CBAET4ADwUdDx8GDbCB7wo2BE+jvm3DqAWED4ADwUb1d0u9iB6MeFFwbR8IHARPgAPBR0f82Hw4wRvCg6r/tNe6d86gFrA/5aAh8wxH3S7wJscLWlVhA8EHsIH4KFg4/iYDxaCgjc5Wj4IHwhAhA/AQ2a3i40xH/AixnwggBE+AA8FmVNt+XGCFznCB2M+EID43xLwkNntQssHvIluFwQwwgfgIbPlw8aPE7yIRcYQwPjfEvCQY8wHs13gVeaYD1o+EHgIH4CHTnS7ED7gRYQPBDDCB+Ah7u2CWsEiYwhghA/AQzaDqbaoBSwyhgBG+AA8FMyAU9QGZrsggPGrGuChoONjPg6WHdPXB0r9XA0CRYPSY0qWpIoS6eCX/ivEFiQ1bimxgi+8iPABeMjR8jF1+W6NX17i52oQKLoFfal/hkk6+Lk0rYt/i7logHTLLP/WgIBC+AA8YRgKPj7VNjoqQsdsYX4uCIFia/kF2mJP1wXhRYoI9VOXXtUxqaJY2r/VP+dHwCJ8AJ44PthUkgr+fJ0UFe/HYhBI/t+/1us32yfqiT4X63dXNPdPEd+tl2b2YKEzeB0j5ABP2I+d+Dt94qgFhj9P7phxc/L3OeAFhA/AE/aqE38PoiER3mOTzd8lnDTjhpYPeBfhA/CEU8sH4QPeY3NkD8OPbR/B3F8GtcPt8LFixQr17dtXycnJstlsWrBggdP2oUOHymazOT169erlrXoBazk5fLC8OmqBX7tdzPBBtwu8y+3wUVZWpksuuURTp0494z69evXSvn37zMdrr73mUZGAZZ004JQxH/AmmwV6XbizLmqL2+3EvXv3Vu/evc+6T3h4uJKSkmpcFFBnOFo+bMEW+bRAoHCM+fBnr4vT/WUMg+9xeE2tdFIvW7ZMCQkJatSoka677jo98cQTaty48Wn3raioUEVFhfm8pKSWFmk6fEhaObl2jo366+jxFU0Z74FaYvh1zMdJ39f2Yye6YQAPef1/zF69emnAgAFKT0/Xzp079Ze//EW9e/fWqlWrFBx8arP0pEmTlJeX5+0yTlVeLK2ZXvvnQf0UEevvChBojjcy+HfMx0mL5lVVEj7gNV4PH4MGDTL/npGRofbt26tly5ZatmyZevToccr+Y8eOVW5urvm8pKREqamp3i5LimwkXf2A948LSFKrLH9XgABjiQ4Op/BxVFKU30pBYKn1tuIWLVqoSZMm2rFjx2nDR3h4uMLDw2u7DKlBY6nHuNo/DwB4gc1mgTEfQb/qdgG8pNbX+fjuu+/0448/qlmzZrV9KgAIOH7tdrHZmPGCWuF2y8fhw4e1Y8cO8/muXbu0adMmxcfHKz4+Xnl5eRo4cKCSkpK0c+dOjR49Wq1atVJ2drZXCweAQGaJbhepepyHvZLwAa9yO3ysX79e1157rfncMV5jyJAhmj59urZs2aJXX31VRUVFSk5OVs+ePfX444/7pmsFAAKEY1arX2e7SNXho1IsNAavcjt8dO/e/aw/DIsXL/aoIACAhdDtglrAvV0AwIKs0+1y0kJjgJcQPgDAgiwx20U6sdBYFeED3kP4AAALM/w73+VEy8c/eklT2ktFe/1bDwIC4QMALMgy3S4pnar/tFdKRbul3Z/6tx4EBMIHAFiROdvFv2Wo/3Tp/u1Sy+uqn1eW+bceBATCBwBYmL+zh2w2KTZFijp+c9CjR/xbDwIC4QMALMgmiww4dQhrUP3nUVo+4DnCBwBYkM0ygz6OCz0ePuh2gRcQPgDAghzZw++zXRzCjt/Rlm4XeAHhAwAszDLdLqHHw0cl4QOeI3wAgAVZrtuFMR/wIrfv7QIAqH0266z0Uc3R8vHTTmnz6/6txZ8SLpSatfd3FXUe4QMALMzvd7V1iIit/nP/Vmn+//NvLf4UHC79+SspMs7fldRphA8AsCDLdbu0vE669HdS6T5/V+I/u5ZLVRVS2SHCh4cIHwBgQTarrHDqEN5Q6j/V31X41+S21eGLcS8eY8ApAFiYVbIHxIwfLyJ8AIAlWWyFU7DWiRcRPgDAgiw35gOs8upFhA8AsDDLrHAKWj68iPABABZkLq9O9rAOc6G1w/6tIwAQPgDAguh2sSCz24WWD08x1RYALMixwikNHxbi6HZZO0P68v0TrydfKvV6ksToBsIHAFgZ/S7W0Si9+s/ivdUPh72rpSv+KDU63y9l1UWEDwCwIH6JtqAr/iglXuS8yNg790rlRVJFqd/KqosIHwBgQeaAU79WASfBoVKrHs6vLXm0OnwwA8YtDDgFAAuj18XiWPujRggfAGBBNvpd6gZz+i3hwx2EDwCwMBYZszgWHqsRwgcAWBjdLhZHt0uNED4AwIIcvS5kD4uj5aNGmO0CABZkE2M+6oTQ4+GjaI908MsTr8eeJ4VH+6emOoDwAQAWZLZ80PRhbWENq/9c+3L1wyGykTRqmxTe0D91WRzdLgBgYQw4tbh2faW45lJU4xMP2aRffnZeBRVOaPkAAAui06WOaJ4pjdri/NqUjOpuGKbfnhEtHwBgQTaWOK27Qln741wIHwBgYWSPOsgxA6aSGTBnQvgAAAtihdM6zDEDhpaPM3I7fKxYsUJ9+/ZVcnKybDabFixY4LTdMAyNGzdOzZo1U2RkpLKysvT11197q14AqBfMXhemu9Q9jiXXafk4I7fDR1lZmS655BJNnTr1tNufeuopPf/883rppZe0Zs0aNWjQQNnZ2SovL/e4WACob8gedVAoC4+di9uzXXr37q3evXufdpthGJoyZYoeeeQR9evXT5L0z3/+U4mJiVqwYIEGDRrkWbUAUF/Q61J3hbHk+rl4dartrl27tH//fmVlZZmvxcbGqkuXLlq1atVpw0dFRYUqKirM5yUlJd4sCQDqJMcKpzR81EGO8PHle9LhQ/6t5UwaNJG6/dlvp/dq+Ni/f78kKTEx0en1xMREc9uvTZo0SXl5ed4sAwACBt0udVCDptV/fr+h+mFFjVsHTvioibFjxyo3N9d8XlJSotTUVD9WBAD+d+LGcqSPOqfTMCkoWKoo9XclZxbV2K+n92r4SEpKkiQdOHBAzZo1M18/cOCALr300tN+TXh4uMLDw71ZBgDUeQz5qMMiG0lX3e/vKizNq+t8pKenKykpSQUFBeZrJSUlWrNmjTIzM715KgAIaNxYDoHM7ZaPw4cPa8eOHebzXbt2adOmTYqPj1daWppGjRqlJ554Qq1bt1Z6eroeffRRJScnq3///t6sGwAA1FFuh4/169fr2muvNZ87xmsMGTJEs2fP1ujRo1VWVqa77rpLRUVFuuqqq7Ro0SJFRER4r2oACHA2Ol4QwNwOH927dz/rins2m00TJkzQhAkTPCoMAOqzE90u9Lsg8HBvFwCwMKIHAhHhAwAsiE4XBDLCBwBY0fF+F3pdEIgIHwBgYSwyhkBE+AAAC3J0u9DygUBE+AAAC7Ix6AMBjPABABZGwwcCEeEDACzIscgY3S4IRIQPALAgul0QyAgfAGBBJ7IHTR8IPIQPALAwul0QiAgfAGBBdLsgkBE+AMCCbKxwigBG+AAAC2OFUwQiwgcAAPApwgcAWJBjzAfdLghEhA8AsDCyBwIR4QMALIgVThHICB8AYEFMtUUgI3wAgAU5sgezXRCICB8AYGVkDwQgwgcAWBDdLghkhA8AsCBzwKmf6wBqA+EDACzMYLoLAhDhAwAsiG4XBDLCBwBYGO0eCESEDwCwMHpdEIgIHwBgQTYbA04RuAgfAGBBDPlAICN8AICFMdsFgYjwAQAW5JjtQvRAICJ8AIAF0e2CQEb4AAALstH0gQBG+AAAC+OutghEhA8AsCBWOEUgI3wAgAU5sgeTXRCICB8AYGGEDwQir4ePxx57TDabzenRtm1bb58GAAIb/S4IYCG1cdCLLrpIH3300YmThNTKaQAgYJndLgw4RQCqlVQQEhKipKSk2jg0ANQrdLsgENXKmI+vv/5aycnJatGihXJycrRnz54z7ltRUaGSkhKnBwDUdyzzgUDm9fDRpUsXzZ49W4sWLdL06dO1a9cuXX311SotLT3t/pMmTVJsbKz5SE1N9XZJAFDn2FjjFAHM6+Gjd+/euuWWW9S+fXtlZ2fr/fffV1FRkf7zn/+cdv+xY8equLjYfOzdu9fbJQFAnWO2fND0gQBU6yNB4+LidMEFF2jHjh2n3R4eHq7w8PDaLgMA6ijSBwJPra/zcfjwYe3cuVPNmjWr7VMBQMCg0wWBzOvh489//rOWL1+ub7/9Vp9++qluuukmBQcHa/Dgwd4+FQAELLpdEMi83u3y3XffafDgwfrxxx/VtGlTXXXVVVq9erWaNm3q7VMBQMAjeyAQeT18vP76694+JADUO8x2QSDj3i4AYEVmtwttHwg8hA8AsDCiBwIR4QMALIhOFwQywgcAWJDt+HQXel0QiAgfAGBhZA8EIsIHAFiQo9uFAacIRIQPALAgG4M+EMAIHwBgQYQPBDLCBwBYGL0uCESEDwCwIFY4RSAjfACABZk3lmO+CwIQ4QMALIxuFwQiwgcAAPApwgcAWBArnCKQET4AwMIY84FARPgAAAs6scKpX8sAagXhAwAsiEXGEMgIHwBgYTR8IBARPgDAgsxFxkgfCECEDwCwILpdEMgIHwBgQeaAU5o+EIAIHwBgYcx2QSAifACABdHtgkBG+AAASzq+wqmfqwBqA+EDACzMoN8FAYjwAQAWRLcLAhnhAwAs6MRsFyDwED4AwMLodUEgInwAgAXZbAw4ReAifACABTHkA4GM8AEAFmQOOKXfBQGI8AEAFkb0QCAifACABTHVFoGM8AEAFmRzrHBK0wcCEOEDACyMu9oiEBE+AMCK6HZBACN8AIAFMdkFgazWwsfUqVN1/vnnKyIiQl26dNHatWtr61QAELAIHwhEtRI+3njjDeXm5mr8+PH67LPPdMkllyg7O1sHDx6sjdMBQMBhhVMEsloJH88884yGDx+u3//+97rwwgv10ksvKSoqSv/4xz9q43QAEHAY8oFAFuLtAx49elQbNmzQ2LFjzdeCgoKUlZWlVatWnbJ/RUWFKioqzOclJSXeLgkA6qz9xb8o793t/i4DAaZJw3CNvLaV387v9fDxv//9T1VVVUpMTHR6PTExUV9++eUp+0+aNEl5eXneLgMA6rSYyFBJ0s9HKjXrk2/9WwwCToumDQIrfLhr7Nixys3NNZ+XlJQoNTXVjxUBgP9dkhKrSQMy9N3PR/xdCgJQo6gwv57f6+GjSZMmCg4O1oEDB5xeP3DggJKSkk7ZPzw8XOHh4d4uAwDqNJvNpsGd0/xdBlArvD7gNCwsTB07dlRBQYH5mt1uV0FBgTIzM719OgAAUMfUSrdLbm6uhgwZossvv1ydO3fWlClTVFZWpt///ve1cToAAFCH1Er4uPXWW3Xo0CGNGzdO+/fv16WXXqpFixadMggVAADUPzbDsNb6eSUlJYqNjVVxcbFiYmL8XQ4AAHCBO5/f3NsFAAD4FOEDAAD4FOEDAAD4FOEDAAD4FOEDAAD4FOEDAAD4FOEDAAD4FOEDAAD4FOEDAAD4VK0sr+4Jx4KrJSUlfq4EAAC4yvG57crC6ZYLH6WlpZKk1NRUP1cCAADcVVpaqtjY2LPuY7l7u9jtdv3www+Kjo6WzWbz6rFLSkqUmpqqvXv3ct8YD3EtvYvr6V1cT+/hWnpXIF9PwzBUWlqq5ORkBQWdfVSH5Vo+goKClJKSUqvniImJCbh/dH/hWnoX19O7uJ7ew7X0rkC9nudq8XBgwCkAAPApwgcAAPCpehU+wsPDNX78eIWHh/u7lDqPa+ldXE/v4np6D9fSu7ie1Sw34BQAAAS2etXyAQAA/I/wAQAAfIrwAQAAfIrwAQAAfKrehI+pU6fq/PPPV0REhLp06aK1a9f6uyTLmTRpkjp16qTo6GglJCSof//+KiwsdNqnvLxcI0eOVOPGjdWwYUMNHDhQBw4ccNpnz549uuGGGxQVFaWEhAQ9+OCDOnbsmC/fiiU9+eSTstlsGjVqlPka19N133//vX73u9+pcePGioyMVEZGhtavX29uNwxD48aNU7NmzRQZGamsrCx9/fXXTsf46aeflJOTo5iYGMXFxWnYsGE6fPiwr9+K31VVVenRRx9Venq6IiMj1bJlSz3++ONO9+Tgep7ZihUr1LdvXyUnJ8tms2nBggVO27117bZs2aKrr75aERERSk1N1VNPPVXbb813jHrg9ddfN8LCwox//OMfxvbt243hw4cbcXFxxoEDB/xdmqVkZ2cbs2bNMrZt22Zs2rTJ6NOnj5GWlmYcPnzY3GfEiBFGamqqUVBQYKxfv9644oorjCuvvNLcfuzYMePiiy82srKyjI0bNxrvv/++0aRJE2Ps2LH+eEuWsXbtWuP888832rdvb/zpT38yX+d6uuann34ymjdvbgwdOtRYs2aN8c033xiLFy82duzYYe7z5JNPGrGxscaCBQuMzZs3G7/5zW+M9PR045dffjH36dWrl3HJJZcYq1evNlauXGm0atXKGDx4sD/ekl9NnDjRaNy4sbFw4UJj165dxptvvmk0bNjQeO6558x9uJ5n9v777xsPP/ywMW/ePEOSMX/+fKft3rh2xcXFRmJiopGTk2Ns27bNeO2114zIyEjj5Zdf9tXbrFX1Inx07tzZGDlypPm8qqrKSE5ONiZNmuTHqqzv4MGDhiRj+fLlhmEYRlFRkREaGmq8+eab5j5ffPGFIclYtWqVYRjVP5RBQUHG/v37zX2mT59uxMTEGBUVFb59AxZRWlpqtG7d2liyZIlxzTXXmOGD6+m6MWPGGFddddUZt9vtdiMpKcl4+umnzdeKioqM8PBw47XXXjMMwzA+//xzQ5Kxbt06c58PPvjAsNlsxvfff197xVvQDTfcYPzhD39wem3AgAFGTk6OYRhcT3f8Onx469pNmzbNaNSokdPP+ZgxY4w2bdrU8jvyjYDvdjl69Kg2bNigrKws87WgoCBlZWVp1apVfqzM+oqLiyVJ8fHxkqQNGzaosrLS6Vq2bdtWaWlp5rVctWqVMjIylJiYaO6TnZ2tkpISbd++3YfVW8fIkSN1ww03OF03ievpjnfeeUeXX365brnlFiUkJKhDhw6aMWOGuX3Xrl3av3+/07WMjY1Vly5dnK5lXFycLr/8cnOfrKwsBQUFac2aNb57MxZw5ZVXqqCgQF999ZUkafPmzfr444/Vu3dvSVxPT3jr2q1atUrdunVTWFiYuU92drYKCwv1888/++jd1B7L3VjO2/73v/+pqqrK6T9vSUpMTNSXX37pp6qsz263a9SoUeratasuvvhiSdL+/fsVFhamuLg4p30TExO1f/9+c5/TXWvHtvrm9ddf12effaZ169adso3r6bpvvvlG06dPV25urv7yl79o3bp1uu+++xQWFqYhQ4aY1+J01+rka5mQkOC0PSQkRPHx8fXqWkrSQw89pJKSErVt21bBwcGqqqrSxIkTlZOTI0lcTw9469rt379f6enppxzDsa1Ro0a1Ur+vBHz4QM2MHDlS27Zt08cff+zvUuqsvXv36k9/+pOWLFmiiIgIf5dTp9ntdl1++eX661//Kknq0KGDtm3bppdeeklDhgzxc3V1z3/+8x/NmTNH+fn5uuiii7Rp0yaNGjVKycnJXE/4RMB3uzRp0kTBwcGnzCA4cOCAkpKS/FSVtd1zzz1auHCh/vvf/yolJcV8PSkpSUePHlVRUZHT/idfy6SkpNNea8e2+mTDhg06ePCgLrvsMoWEhCgkJETLly/X888/r5CQECUmJnI9XdSsWTNdeOGFTq+1a9dOe/bskXTiWpzt5zwpKUkHDx502n7s2DH99NNP9epaStKDDz6ohx56SIMGDVJGRoZuv/123X///Zo0aZIkrqcnvHXtAv1nP+DDR1hYmDp27KiCggLzNbvdroKCAmVmZvqxMusxDEP33HOP5s+fr6VLl57S5NexY0eFhoY6XcvCwkLt2bPHvJaZmZnaunWr0w/WkiVLFBMTc8qHR6Dr0aOHtm7dqk2bNpmPyy+/XDk5OebfuZ6u6dq16ynTvr/66is1b95ckpSenq6kpCSna1lSUqI1a9Y4XcuioiJt2LDB3Gfp0qWy2+3q0qWLD96FdRw5ckRBQc7//QcHB8tut0vienrCW9cuMzNTK1asUGVlpbnPkiVL1KZNmzrf5SKp/ky1DQ8PN2bPnm18/vnnxl133WXExcU5zSCAYfzxj380YmNjjWXLlhn79u0zH0eOHDH3GTFihJGWlmYsXbrUWL9+vZGZmWlkZmaa2x1TQ3v27Gls2rTJWLRokdG0adN6NzX0TE6e7WIYXE9XrV271ggJCTEmTpxofP3118acOXOMqKgo49///re5z5NPPmnExcUZb7/9trFlyxajX79+p53e2KFDB2PNmjXGxx9/bLRu3bpeTA39tSFDhhjnnXeeOdV23rx5RpMmTYzRo0eb+3A9z6y0tNTYuHGjsXHjRkOS8cwzzxgbN240du/ebRiGd65dUVGRkZiYaNx+++3Gtm3bjNdff92Iiopiqm1d88ILLxhpaWlGWFiY0blzZ2P16tX+LslyJJ32MWvWLHOfX375xbj77ruNRo0aGVFRUcZNN91k7Nu3z+k43377rdG7d28jMjLSaNKkifHAAw8YlZWVPn431vTr8MH1dN27775rXHzxxUZ4eLjRtm1b4+9//7vTdrvdbjz66KNGYmKiER4ebvTo0cMoLCx02ufHH380Bg8ebDRs2NCIiYkxfv/73xulpaW+fBuWUFJSYvzpT38y0tLSjIiICKNFixbGww8/7DStk+t5Zv/9739P+3/lkCFDDMPw3rXbvHmzcdVVVxnh4eHGeeedZzz55JO+eou1zmYYJy1pBwAAUMsCfswHAACwFsIHAADwKcIHAADwKcIHAADwKcIHAADwKcIHAADwKcIHAADwKcIHAADwKcIHAADwKcIHAADwKcIHAADwKcIHAADwqf8Py+HuSop9F4wAAAAASUVORK5CYII=",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "plt.plot(df_service_single.servers_pending, label=\"servers pending\")\n",
+ "plt.plot(df_service_single.servers_active, label=\"servers active\")\n",
+ "\n",
+ "plt.legend()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 106,
+ "id": "1a688c2d",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "<matplotlib.legend.Legend at 0x7f6fc2cc7ca0>"
+ ]
+ },
+ "execution_count": 106,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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XNvXn9p48sknFRMTo969e0uSFixYoJ49eyowMFBRUVEaM2bMOft2/PhxjR8/XpdccomCg4OVnJys1157zW0bp9Op3//+9+rRo4cCAgIUFxenJ598UpJcv9579dVXy2az6frrr3erTZL++7//WzExMXI6nW7tZmRk6Be/+IXr+VtvvaV+/fopMDBQ3bt316xZs3Tq1Klz1u5JjHwAgLcyRqo96ZnX9guWbLYLblZUVKTx48fr97//vW699VZVVFRow4YNrlMKS5Ys0YwZM/TCCy/o6quv1rZt23TnnXeqY8eOmjhxoqudRx55RPPmzdPVV1+twMBAbdiwQTk5ORoxYoRrmyVLlmjkyJEKDg5WbW2t0tPTlZqaqg0bNsjX11dz5szR8OHDtXPnTtcIR25urux2uz788ENJ0pYtW3Tvvffqz3/+s37wgx/om2++0YYNG87Zv6qqKqWkpOjhhx+W3W7Xu+++qwkTJuiyyy7TgAEDJEnTp0/XwoULNX/+fF177bUqKirSvn37JEmffvqpBgwYoI8++khXXHHFWUdefvKTn2jq1Kn629/+pqFDh0qSvvnmG73//vtatWqVJGnDhg36+c9/rueee04//OEPdeDAAd11112SpJkzZ17wOFmN33YBAC9xxu9q1FRKv43xTDGPFkr+HS+42WeffaaUlBQdOnRI8fHxZ6zv0aOHfvOb32j8+PGuZXPmzNGqVav0ySefuOZEPPPMM7rvvvtc26xcuVITJkxQSUmJgoODVV5erqioKK1YsULDhw/Xq6++qjlz5ujzzz+X7Z8hqaamRmFhYVq5cqWGDRumSZMm6f3339fhw4ddX/rLly/X7bffrq+//lqhoaFNemtuvvlmJSYm6g9/+IMqKioUERGhF154QXfccccZ255rzsekSZNUWlqqlStXSpJGjhypzp0765VXXpFUNxoya9YsHTlyRD4+PkpLS9PQoUM1ffp0VxuvvvqqHnroIRUWFjapH2fDb7sAAFq9vn37aujQoUpOTtZPfvITLVy4UN9++60kqbKyUgcOHNDkyZMVEhLiesyZM0cHDhxwa+eaa65xe37TTTfJz89Pf/3rXyVJy5Ytk91uV1pamiRpx44d2r9/v0JDQ13thoeHq6qqyq3t5ORkt9GGG2+8UfHx8erevbsmTJigJUuWuE7jnM3p06f1m9/8RsnJyQoPD1dISIg++OADHT58WJL0+eefq7q62jVi0VSZmZlatmyZqqurJdWN8owbN04+Pj6u/s6ePdvtfbzzzjtVVFR03vo9hdMuAOCt/ILrRiA89doN0KFDB3344Yf65JNPtHr1aj3//PN67LHHtGnTJgUH17WxcOFCDRw48Iz9/l3Hju6jLP7+/hozZoxycnI0btw45eTkaOzYsfL1rftaO3HihFJSUrRkyZIzaoqIiDhnu6Ghofrss8+0du1arV69WjNmzNATTzyhzZs3Kyws7Iy2nnrqKT377LN65plnlJycrI4dO2ratGmqqamRJAUFBTXofbqQW265RcYYvfvuu+rfv782bNig+fPnu9afOHFCs2bN0qhRo87YtzX+AjLhAwC8lc3WoFMfnmaz2TR48GANHjxYM2bMUHx8vFasWKGsrCzFxMTo4MGDyszMbHS7mZmZuvHGG7Vnzx6tWbNGc+bMca3r16+fli5dqsjIyEafwvf19VVaWprS0tI0c+ZMhYWFac2aNWf9Yv/73/+ujIwM/exnP5NUN7n0iy++0OWXXy5J6tmzp4KCgpSbm3vW0y71oy6nT58+b02BgYEaNWqUlixZov3796t3797q16+fW3/z8/PVo0ePRvXVUwgfAIAWs2nTJuXm5mrYsGGKjIzUpk2bdOzYMSUlJUmSZs2apXvvvVcOh0PDhw9XdXW1tmzZom+//VZZWVnnbXvIkCGKjo5WZmamEhIS3EZPMjMz9dRTTykjI0OzZ89Wt27d9NVXX2n58uV66KGH1K1bt7O2+c477+jgwYMaMmSIOnXqpFWrVsnpdLquhPm+nj176s0339Qnn3yiTp066emnn1ZJSYkrfAQGBurhhx/WQw89JH9/fw0ePFjHjh3Tnj17NHnyZEVGRiooKEjvv/++unXrpsDAwHNeZpuZmambb75Ze/bscYWdejNmzNDNN9+suLg4jRkzRj4+PtqxY4d2797tFspaC+Z8AABajN1u1/r163XTTTepV69eevzxxzVv3jzXVSp33HGHFi1apMWLFys5OVnXXXedsrOzXZegno/NZtP48eO1Y8eOM0ZOgoODtX79esXFxWnUqFFKSkrS5MmTVVVVdd6RkLCwMC1fvlw33HCDkpKS9PLLL+u1117TFVdccdbtH3/8cfXr10/p6em6/vrrFR0dfcadSX/961/rgQce0IwZM5SUlKSxY8fq6NGjkupGWZ577jn98Y9/VExMjDIyMs5Z2w033KDw8HDl5+frtttuc1uXnp6ud955R6tXr1b//v01aNAgzZ8//6yTfFsDrnYBAC9xvisNACtwtQsAAPBKhA8AAGApwgcAALAU4QMAAFiK8AEAACxF+AAAL9PKLlJEO9Jcnz3CBwB4ifpbjtffuhuwWv1n7/u3v28s7nAKAF7C19dXwcHBOnbsmPz8/Fw/KgZYwel06tixYwoODnb9hk5TET4AwEvYbDZ17dpVBQUF+uqrrzxdDtohHx8fxcXFyWazXVQ7hA8A8CL+/v7q2bMnp17gEf7+/s0y4kb4AAAv4+Pjw+3V4dU4YQgAACxF+AAAAJYifAAAAEs1KnzMnTtX/fv3V2hoqCIjIzVy5Ejl5+e7bVNVVaUpU6aoc+fOCgkJ0ejRo1VSUtKsRQMAAO/VqPCxbt06TZkyRRs3btSHH36o2tpaDRs2TJWVla5t7r//fr399tt64403tG7dOhUWFmrUqFHNXjgAAPBONnMR90o9duyYIiMjtW7dOg0ZMkRlZWWKiIhQTk6OxowZI0nat2+fkpKSlJeXp0GDBl2wzfLycjkcDpWVlclutze1NAAAYKHGfH9f1JyPsrIySVJ4eLgkaevWraqtrVVaWpprm8TERMXFxSkvL++sbVRXV6u8vNztAQAA2q4mhw+n06lp06Zp8ODBuvLKKyVJxcXF8vf3V1hYmNu2UVFRKi4uPms7c+fOlcPhcD1iY2ObWhIAAPACTQ4fU6ZM0e7du/X6669fVAHTp09XWVmZ63HkyJGLag8AALRuTbrD6T333KN33nlH69evV7du3VzLo6OjVVNTo9LSUrfRj5KSEkVHR5+1rYCAAAUEBDSlDAAA4IUaNfJhjNE999yjFStWaM2aNUpISHBbn5KSIj8/P+Xm5rqW5efn6/Dhw0pNTW2eigEAgFdr1MjHlClTlJOTo7feekuhoaGueRwOh0NBQUFyOByaPHmysrKyFB4eLrvdrqlTpyo1NbVBV7oAAIC2r1GX2p7rJ3QXL16sSZMmSaq7ydgDDzyg1157TdXV1UpPT9eCBQvOedrl+7jUFgAA79OY7++Lus9HSyB8AADgfSy7zwcAAEBjET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAACxF+AAAAJYifAAAAEsRPgAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAACxF+AAAAJYifAAAAEsRPgAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAACxF+AAAAJZqdPhYv369brnlFsXExMhms2nlypVu640xmjFjhrp27aqgoCClpaXpyy+/bK56AQCAl2t0+KisrFTfvn314osvnnX973//ez333HN6+eWXtWnTJnXs2FHp6emqqqq66GIBAID3823sDiNGjNCIESPOus4Yo2eeeUaPP/64MjIyJEn/+7//q6ioKK1cuVLjxo27uGoBAIDXa9Y5HwUFBSouLlZaWpprmcPh0MCBA5WXl3fWfaqrq1VeXu72AAAAbVezho/i4mJJUlRUlNvyqKgo17rvmzt3rhwOh+sRGxvbnCUBAIBWxuNXu0yfPl1lZWWux5EjRzxdEgAAaEHNGj6io6MlSSUlJW7LS0pKXOu+LyAgQHa73e0BAADarmYNHwkJCYqOjlZubq5rWXl5uTZt2qTU1NTmfCkAAOClGn21y4kTJ7R//37X84KCAm3fvl3h4eGKi4vTtGnTNGfOHPXs2VMJCQn69a9/rZiYGI0cObI56wYAAF6q0eFjy5Yt+tGPfuR6npWVJUmaOHGisrOz9dBDD6myslJ33XWXSktLde211+r9999XYGBg81UNAAC8ls0YYzxdxL8rLy+Xw+FQWVkZ8z8AAPASjfn+9vjVLgAAoH0hfAAAAEsRPgAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAACxF+AAAAJYifAAAAEsRPgAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAACxF+AAAAJYifAAAAEsRPgAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALNVi4ePFF1/UpZdeqsDAQA0cOFCffvppS70UAADwIi0SPpYuXaqsrCzNnDlTn332mfr27av09HQdPXq0JV4OAAB4EZsxxjR3owMHDlT//v31wgsvSJKcTqdiY2M1depUPfLII+fdt7y8XA6HQ2VlZbLb7c1WkzFG39Webrb2AADwZkF+HWSz2ZqtvcZ8f/s226v+U01NjbZu3arp06e7lvn4+CgtLU15eXlnbF9dXa3q6mrX8/Ly8uYuSZL0Xe1pXT7jgxZpGwAAb7N3drqC/Zs9BjRIs592+cc//qHTp08rKirKbXlUVJSKi4vP2H7u3LlyOByuR2xsbHOXBAAAWhHPRJ5/M336dGVlZbmel5eXt0gACfLroL2z05u9XQAAvFGQXwePvXazh48uXbqoQ4cOKikpcVteUlKi6OjoM7YPCAhQQEBAc5dxBpvN5rHhJQAA8C/NftrF399fKSkpys3NdS1zOp3Kzc1Vampqc78cAADwMi0yFJCVlaWJEyfqmmuu0YABA/TMM8+osrJSt99+e0u8HAAA8CItEj7Gjh2rY8eOacaMGSouLtZVV12l999//4xJqAAAoP1pkft8XIyWus8HAABoOY35/ua3XQAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApVrdz7zW33C1vLzcw5UAAICGqv/ebsiN01td+KioqJAkxcbGergSAADQWBUVFXI4HOfdptX9tovT6VRhYaFCQ0Nls9mate3y8nLFxsbqyJEj7e53Y9pr39trvyX6Tt/pe3vRWvptjFFFRYViYmLk43P+WR2tbuTDx8dH3bp1a9HXsNvt7eqD+e/aa9/ba78l+k7f25/22vfW0O8LjXjUY8IpAACwFOEDAABYql2Fj4CAAM2cOVMBAQGeLsVy7bXv7bXfEn2n7/S9vfDGfre6CacAAKBta1cjHwAAwPMIHwAAwFKEDwAAYCnCBwAAsFS7CR8vvviiLr30UgUGBmrgwIH69NNPPV1Ss3viiSdks9ncHomJia71VVVVmjJlijp37qyQkBCNHj1aJSUlHqy46davX69bbrlFMTExstlsWrlypdt6Y4xmzJihrl27KigoSGlpafryyy/dtvnmm2+UmZkpu92usLAwTZ48WSdOnLCwF01zob5PmjTpjM/B8OHD3bbxxr7PnTtX/fv3V2hoqCIjIzVy5Ejl5+e7bdOQz/jhw4f14x//WMHBwYqMjNSDDz6oU6dOWdmVRmtI36+//vozjvvdd9/tto039v2ll15Snz59XDfQSk1N1Xvvveda31aP+YX67fXH27QDr7/+uvH39zf/8z//Y/bs2WPuvPNOExYWZkpKSjxdWrOaOXOmueKKK0xRUZHrcezYMdf6u+++28TGxprc3FyzZcsWM2jQIPODH/zAgxU33apVq8xjjz1mli9fbiSZFStWuK3/3e9+ZxwOh1m5cqXZsWOH+Y//+A+TkJBgvvvuO9c2w4cPN3379jUbN240GzZsMD169DDjx4+3uCeNd6G+T5w40QwfPtztc/DNN9+4beONfU9PTzeLFy82u3fvNtu3bzc33XSTiYuLMydOnHBtc6HP+KlTp8yVV15p0tLSzLZt28yqVatMly5dzPTp0z3RpQZrSN+vu+46c+edd7od97KyMtd6b+37X//6V/Puu++aL774wuTn55tHH33U+Pn5md27dxtj2u4xv1C/vf14t4vwMWDAADNlyhTX89OnT5uYmBgzd+5cD1bV/GbOnGn69u171nWlpaXGz8/PvPHGG65ln3/+uZFk8vLyLKqwZXz/C9jpdJro6Gjz1FNPuZaVlpaagIAA89prrxljjNm7d6+RZDZv3uza5r333jM2m8383//9n2W1X6xzhY+MjIxz7tNW+n706FEjyaxbt84Y07DP+KpVq4yPj48pLi52bfPSSy8Zu91uqqurre3ARfh+342p+zK67777zrlPW+m7McZ06tTJLFq0qF0dc2P+1W9jvP94t/nTLjU1Ndq6davS0tJcy3x8fJSWlqa8vDwPVtYyvvzyS8XExKh79+7KzMzU4cOHJUlbt25VbW2t2/uQmJiouLi4Nvc+FBQUqLi42K2vDodDAwcOdPU1Ly9PYWFhuuaaa1zbpKWlycfHR5s2bbK85ua2du1aRUZGqnfv3vrlL3+p48ePu9a1lb6XlZVJksLDwyU17DOel5en5ORkRUVFubZJT09XeXm59uzZY2H1F+f7fa+3ZMkSdenSRVdeeaWmT5+ukydPuta1hb6fPn1ar7/+uiorK5Wamtpujvn3+13Pm493q/thueb2j3/8Q6dPn3Y7AJIUFRWlffv2eaiqljFw4EBlZ2erd+/eKioq0qxZs/TDH/5Qu3fvVnFxsfz9/RUWFua2T1RUlIqLiz1TcAup78/Zjnn9uuLiYkVGRrqt9/X1VXh4uNe/H8OHD9eoUaOUkJCgAwcO6NFHH9WIESOUl5enDh06tIm+O51OTZs2TYMHD9aVV14pSQ36jBcXF5/1c1G/zhucre+SdNtttyk+Pl4xMTHauXOnHn74YeXn52v58uWSvLvvu3btUmpqqqqqqhQSEqIVK1bo8ssv1/bt29v0MT9XvyXvP95tPny0JyNGjHD93adPHw0cOFDx8fH6y1/+oqCgIA9WBiuNGzfO9XdycrL69Omjyy67TGvXrtXQoUM9WFnzmTJlinbv3q2PP/7Y06VY7lx9v+uuu1x/Jycnq2vXrho6dKgOHDigyy67zOoym1Xv3r21fft2lZWV6c0339TEiRO1bt06T5fV4s7V78svv9zrj3ebP+3SpUsXdejQ4YzZzyUlJYqOjvZQVdYICwtTr169tH//fkVHR6umpkalpaVu27TF96G+P+c75tHR0Tp69Kjb+lOnTumbb75pc+9H9+7d1aVLF+3fv1+S9/f9nnvu0TvvvKO//e1v6tatm2t5Qz7j0dHRZ/1c1K9r7c7V97MZOHCgJLkdd2/tu7+/v3r06KGUlBTNnTtXffv21bPPPtvmj/m5+n023na823z48Pf3V0pKinJzc13LnE6ncnNz3c6dtUUnTpzQgQMH1LVrV6WkpMjPz8/tfcjPz9fhw4fb3PuQkJCg6Ohot76Wl5dr06ZNrr6mpqaqtLRUW7dudW2zZs0aOZ1O13/EbcXXX3+t48ePq2vXrpK8t+/GGN1zzz1asWKF1qxZo4SEBLf1DfmMp6amateuXW7h68MPP5TdbncNZ7dGF+r72Wzfvl2S3I67N/b9bJxOp6qrq9v0MT+b+n6fjdcdb0/PeLXC66+/bgICAkx2drbZu3evueuuu0xYWJjbLOC24IEHHjBr1641BQUF5u9//7tJS0szXbp0MUePHjXG1F2SFhcXZ9asWWO2bNliUlNTTWpqqoerbpqKigqzbds2s23bNiPJPP3002bbtm3mq6++MsbUXWobFhZm3nrrLbNz506TkZFx1kttr776arNp0ybz8ccfm549e7b6y02NOX/fKyoqzK9+9SuTl5dnCgoKzEcffWT69etnevbsaaqqqlxteGPff/nLXxqHw2HWrl3rdnnhyZMnXdtc6DNef/nhsGHDzPbt2837779vIiIiWs3lh+dyob7v37/fzJ4922zZssUUFBSYt956y3Tv3t0MGTLE1Ya39v2RRx4x69atMwUFBWbnzp3mkUceMTabzaxevdoY03aP+fn63RaOd7sIH8YY8/zzz5u4uDjj7+9vBgwYYDZu3Ojpkprd2LFjTdeuXY2/v7+55JJLzNixY83+/ftd67/77jvzn//5n6ZTp04mODjY3HrrraaoqMiDFTfd3/72NyPpjMfEiRONMXWX2/761782UVFRJiAgwAwdOtTk5+e7tXH8+HEzfvx4ExISYux2u7n99ttNRUWFB3rTOOfr+8mTJ82wYcNMRESE8fPzM/Hx8ebOO+88I2h7Y9/P1mdJZvHixa5tGvIZP3TokBkxYoQJCgoyXbp0MQ888ICpra21uDeNc6G+Hz582AwZMsSEh4ebgIAA06NHD/Pggw+63ffBGO/s+y9+8QsTHx9v/P39TUREhBk6dKgreBjTdo/5+frdFo63zRhjrBtnAQAA7V2bn/MBAABaF8IHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAACz1//FE1rsSXc9sAAAAAElFTkSuQmCC",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "plt.plot(df_service_multi.servers_pending, label=\"servers pending\")\n",
+ "plt.plot(df_service_multi.servers_active, label=\"servers active\")\n",
+ "\n",
+ "plt.legend()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 117,
+ "id": "dc4e17cd",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>timestamp</th>\n",
+ " <th>host_id</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>mem_capacity</th>\n",
+ " <th>guests_terminated</th>\n",
+ " <th>guests_running</th>\n",
+ " <th>guests_error</th>\n",
+ " <th>guests_invalid</th>\n",
+ " <th>cpu_limit</th>\n",
+ " <th>cpu_usage</th>\n",
+ " <th>...</th>\n",
+ " <th>cpu_utilization</th>\n",
+ " <th>cpu_time_active</th>\n",
+ " <th>cpu_time_idle</th>\n",
+ " <th>cpu_time_steal</th>\n",
+ " <th>cpu_time_lost</th>\n",
+ " <th>power_total</th>\n",
+ " <th>uptime</th>\n",
+ " <th>downtime</th>\n",
+ " <th>boot_time</th>\n",
+ " <th>absolute_timestamp</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1970-01-01 02:00:00+00:00</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>8</td>\n",
+ " <td>128000</td>\n",
+ " <td>0</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>9446.762695</td>\n",
+ " <td>...</td>\n",
+ " <td>0.369014</td>\n",
+ " <td>13838</td>\n",
+ " <td>43761</td>\n",
+ " <td>1008</td>\n",
+ " <td>0</td>\n",
+ " <td>1.699475e+06</td>\n",
+ " <td>7200000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1970-01-01 04:00:00+00:00</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>8</td>\n",
+ " <td>128000</td>\n",
+ " <td>0</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>25600.000000</td>\n",
+ " <td>...</td>\n",
+ " <td>1.000000</td>\n",
+ " <td>57592</td>\n",
+ " <td>8</td>\n",
+ " <td>73720</td>\n",
+ " <td>0</td>\n",
+ " <td>2.519850e+06</td>\n",
+ " <td>7200000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 15:35:46+00:00</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1970-01-01 06:00:00+00:00</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>8</td>\n",
+ " <td>128000</td>\n",
+ " <td>0</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>25600.000000</td>\n",
+ " <td>...</td>\n",
+ " <td>1.000000</td>\n",
+ " <td>57600</td>\n",
+ " <td>0</td>\n",
+ " <td>81518</td>\n",
+ " <td>0</td>\n",
+ " <td>2.520000e+06</td>\n",
+ " <td>7200000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 17:35:46+00:00</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1970-01-01 08:00:00+00:00</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>8</td>\n",
+ " <td>128000</td>\n",
+ " <td>0</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>25600.000000</td>\n",
+ " <td>...</td>\n",
+ " <td>1.000000</td>\n",
+ " <td>57592</td>\n",
+ " <td>8</td>\n",
+ " <td>73134</td>\n",
+ " <td>0</td>\n",
+ " <td>2.519850e+06</td>\n",
+ " <td>7200000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 19:35:46+00:00</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1970-01-01 10:00:00+00:00</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>8</td>\n",
+ " <td>128000</td>\n",
+ " <td>0</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>25600.000000</td>\n",
+ " <td>...</td>\n",
+ " <td>1.000000</td>\n",
+ " <td>57592</td>\n",
+ " <td>8</td>\n",
+ " <td>74832</td>\n",
+ " <td>0</td>\n",
+ " <td>2.519850e+06</td>\n",
+ " <td>7200000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 21:35:46+00:00</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "<p>5 rows × 21 columns</p>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " timestamp \\\n",
+ "0 1970-01-01 02:00:00+00:00 \n",
+ "1 1970-01-01 04:00:00+00:00 \n",
+ "2 1970-01-01 06:00:00+00:00 \n",
+ "3 1970-01-01 08:00:00+00:00 \n",
+ "4 1970-01-01 10:00:00+00:00 \n",
+ "\n",
+ " host_id cpu_count mem_capacity \\\n",
+ "0 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 8 128000 \n",
+ "1 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 8 128000 \n",
+ "2 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 8 128000 \n",
+ "3 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 8 128000 \n",
+ "4 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 8 128000 \n",
+ "\n",
+ " guests_terminated guests_running guests_error guests_invalid cpu_limit \\\n",
+ "0 0 15 0 0 25600.0 \n",
+ "1 0 15 0 0 25600.0 \n",
+ "2 0 15 0 0 25600.0 \n",
+ "3 0 15 0 0 25600.0 \n",
+ "4 0 15 0 0 25600.0 \n",
+ "\n",
+ " cpu_usage ... cpu_utilization cpu_time_active cpu_time_idle \\\n",
+ "0 9446.762695 ... 0.369014 13838 43761 \n",
+ "1 25600.000000 ... 1.000000 57592 8 \n",
+ "2 25600.000000 ... 1.000000 57600 0 \n",
+ "3 25600.000000 ... 1.000000 57592 8 \n",
+ "4 25600.000000 ... 1.000000 57592 8 \n",
+ "\n",
+ " cpu_time_steal cpu_time_lost power_total uptime downtime \\\n",
+ "0 1008 0 1.699475e+06 7200000 0 \n",
+ "1 73720 0 2.519850e+06 7200000 0 \n",
+ "2 81518 0 2.520000e+06 7200000 0 \n",
+ "3 73134 0 2.519850e+06 7200000 0 \n",
+ "4 74832 0 2.519850e+06 7200000 0 \n",
+ "\n",
+ " boot_time absolute_timestamp \n",
+ "0 1970-01-01 00:00:00+00:00 2013-08-12 13:35:46+00:00 \n",
+ "1 1970-01-01 00:00:00+00:00 2013-08-12 15:35:46+00:00 \n",
+ "2 1970-01-01 00:00:00+00:00 2013-08-12 17:35:46+00:00 \n",
+ "3 1970-01-01 00:00:00+00:00 2013-08-12 19:35:46+00:00 \n",
+ "4 1970-01-01 00:00:00+00:00 2013-08-12 21:35:46+00:00 \n",
+ "\n",
+ "[5 rows x 21 columns]"
+ ]
+ },
+ "execution_count": 117,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_host_single.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 120,
+ "id": "b0e6c7bf",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[]"
+ ]
+ },
+ "execution_count": 120,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "utilization = df_host_single.cpu_utilization.to_numpy()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 122,
+ "id": "aea7b79d",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[<matplotlib.lines.Line2D at 0x7f6f870ccfa0>]"
+ ]
+ },
+ "execution_count": 122,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "window = 100\n",
+ "avg_utilization = []\n",
+ "\n",
+ "for ind in range(len(utilization) - window + 1):\n",
+ " avg_utilization.append(np.mean(utilization[ind:ind+window]))\n",
+ " \n",
+ "plt.plot(avg_utilization)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "id": "575f824b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sum_util = []\n",
+ "\n",
+ "last_util = 0\n",
+ "for util in utilization:\n",
+ " sum_util.append(util + last_util)\n",
+ " last_util = sum_util[-1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "id": "94d64f88",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[<matplotlib.lines.Line2D at 0x7fd9366b70d0>]"
+ ]
+ },
+ "execution_count": 49,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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26irqduGPY1r//79ceDAAeTk5EivmJgYjBgxQvr35jYmAHDPPfdcNb3AkSNHEBoaCgBo3749AgMDbfo1GAzYuXOnzbiUlZUhOztbqtmwYQOsVivi4uKkmi1btsBsNks16enp6Ny5M1q0aCHV3Gjs7qSqqioolba/Zp2cnGC1WgE033GpY0/7X59e7qS6gHP06FGsX78evr6+Nuub67g0mNxXPjuaZcuWCbVaLVJTU8WhQ4fE888/L3x8fGzupGkqxo8fL7y9vcWmTZvEuXPnpFdVVZVUM27cOBESEiI2bNggdu/eLbRardBqtdL6utulBwwYIHJycsTatWtFq1atrnm79JQpU0ReXp6YP3/+NW+XttdxvfLuKiGa55hkZWUJZ2dn8fbbb4ujR4+KpUuXCnd3d7FkyRKpZvbs2cLHx0f89NNPYv/+/eKxxx675q3CvXr1Ejt37hRbt24VHTt2tLkltqysTAQEBIhRo0aJ3NxcsWzZMuHu7n7VLbHOzs7i/fffF3l5eWLmzJmy3UKelJQk2rRpI91C/v333ws/Pz8xdepUqcbRx6W8vFzs3btX7N27VwAQH374odi7d690l5A97X99erkT42IymcSjjz4q2rZtK3Jycmx+/155p5QjjktjY8i5DT799FMREhIiVCqViI2NFTt27JC7pZsC4JqvRYsWSTXV1dXixRdfFC1atBDu7u7i8ccfF+fOnbPZzsmTJ8XAgQOFm5ub8PPzE6+88oowm802NRs3bhQ9e/YUKpVKdOjQweY76tjruP4+5DTXMVm1apXo1q2bUKvVIjw8XHzxxRc2661Wq3j99ddFQECAUKvVon///iI/P9+m5uLFi2L48OHC09NTaDQaMXr0aFFeXm5Ts2/fPtG3b1+hVqtFmzZtxOzZs6/qZcWKFaJTp05CpVKJrl27itWrVzf+DteDwWAQEydOFCEhIcLV1VV06NBB/P3vf7c5UDn6uGzcuPGav0eSkpKEEPa1//XppbHcaFwKCgqu+/t348aNDj0ujU0hxBVTbxIRERE5CF6TQ0RERA6JIYeIiIgcEkMOEREROSSGHCIiInJIDDlERETkkBhyiIiIyCEx5BAREZFDYsghIiIih8SQQ0RERA6JIYeIiIgcEkMOEREROSSGHCIiInJI/wc327Uoo92xXgAAAABJRU5ErkJggg==",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plt.plot(sum_util)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7962b16f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "output_file = \"../Python_scripts/meta_small.parquet\"\n",
+ "output_file_path = Path(output_file)\n",
+ "\n",
+ "df_meta_new.to_parquet(output_file_path, index=False)\n",
+ "\n",
+ "output_file = \"../Python_scripts/trace_small.parquet\"\n",
+ "output_file_path = Path(output_file)\n",
+ "df_trace_new.to_parquet(output_file_path, index=False)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/opendc-experiments/opendc-experiments-scenario/src/main/Python_scripts/OpenDCdemo.ipynb b/opendc-experiments/opendc-experiments-scenario/src/main/Python_scripts/OpenDCdemo.ipynb
new file mode 100644
index 00000000..09ff26d6
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/Python_scripts/OpenDCdemo.ipynb
@@ -0,0 +1,1121 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "18170001",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "from IPython.display import display, HTML\n",
+ "\n",
+ "base_folder = \"../\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "422f4d05",
+ "metadata": {},
+ "source": [
+ "## Topologies"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "a2d05361",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Topology name: multi\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>ClusterID</th>\n",
+ " <th>ClusterName</th>\n",
+ " <th>Cores</th>\n",
+ " <th>Speed</th>\n",
+ " <th>Memory</th>\n",
+ " <th>numberOfHosts</th>\n",
+ " <th>memoryCapacityPerHost</th>\n",
+ " <th>coreCountPerHost</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>A01</td>\n",
+ " <td>A01</td>\n",
+ " <td>32</td>\n",
+ " <td>3.20</td>\n",
+ " <td>2048</td>\n",
+ " <td>1</td>\n",
+ " <td>256</td>\n",
+ " <td>32</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>B01</td>\n",
+ " <td>B01</td>\n",
+ " <td>48</td>\n",
+ " <td>2.93</td>\n",
+ " <td>1256</td>\n",
+ " <td>6</td>\n",
+ " <td>64</td>\n",
+ " <td>8</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>C01</td>\n",
+ " <td>C01</td>\n",
+ " <td>32</td>\n",
+ " <td>3.20</td>\n",
+ " <td>2048</td>\n",
+ " <td>2</td>\n",
+ " <td>128</td>\n",
+ " <td>16</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>"
+ ],
+ "text/plain": [
+ "<IPython.core.display.HTML object>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Topology name: single\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>ClusterID</th>\n",
+ " <th>ClusterName</th>\n",
+ " <th>Cores</th>\n",
+ " <th>Speed</th>\n",
+ " <th>Memory</th>\n",
+ " <th>numberOfHosts</th>\n",
+ " <th>memoryCapacityPerHost</th>\n",
+ " <th>coreCountPerHost</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>A01</td>\n",
+ " <td>A01</td>\n",
+ " <td>8</td>\n",
+ " <td>3.2</td>\n",
+ " <td>128</td>\n",
+ " <td>1</td>\n",
+ " <td>128</td>\n",
+ " <td>8</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>"
+ ],
+ "text/plain": [
+ "<IPython.core.display.HTML object>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "def read_topology(topology_name):\n",
+ " print(f\"Topology name: {topology_name}\")\n",
+ " df = pd.read_csv(f\"{base_folder}/resources/env/{topology_name}.txt\", delimiter=\";\")\n",
+ " display(HTML(df.to_html()))\n",
+ " \n",
+ "read_topology(\"multi\")\n",
+ "read_topology(\"single\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8f4fe54d",
+ "metadata": {},
+ "source": [
+ "## Traces"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "fd17d88a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>id</th>\n",
+ " <th>timestamp</th>\n",
+ " <th>duration</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_usage</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:45:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>11.703998</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:55:46+00:00</td>\n",
+ " <td>600000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 14:00:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>11.703998</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 14:15:46+00:00</td>\n",
+ " <td>900000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " id timestamp duration cpu_count cpu_usage\n",
+ "0 1019 2013-08-12 13:40:46+00:00 300000 1 0.000000\n",
+ "1 1019 2013-08-12 13:45:46+00:00 300000 1 11.703998\n",
+ "2 1019 2013-08-12 13:55:46+00:00 600000 1 0.000000\n",
+ "3 1019 2013-08-12 14:00:46+00:00 300000 1 11.703998\n",
+ "4 1019 2013-08-12 14:15:46+00:00 900000 1 0.000000"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_trace = pd.read_parquet(f\"{base_folder}/resources/bitbrains-small/trace/trace.parquet\")\n",
+ "df_trace.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "346f097f",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>id</th>\n",
+ " <th>start_time</th>\n",
+ " <th>stop_time</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_capacity</th>\n",
+ " <th>mem_capacity</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2926.000135</td>\n",
+ " <td>181352</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1023</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2925.999560</td>\n",
+ " <td>260096</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1026</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2925.999717</td>\n",
+ " <td>249972</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1052</td>\n",
+ " <td>2013-08-29 14:38:12+00:00</td>\n",
+ " <td>2013-09-05 07:09:07+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2926.000107</td>\n",
+ " <td>131245</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1073</td>\n",
+ " <td>2013-08-21 11:07:12+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2599.999649</td>\n",
+ " <td>179306</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " id start_time stop_time cpu_count \\\n",
+ "0 1019 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "1 1023 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "2 1026 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "3 1052 2013-08-29 14:38:12+00:00 2013-09-05 07:09:07+00:00 1 \n",
+ "4 1073 2013-08-21 11:07:12+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "\n",
+ " cpu_capacity mem_capacity \n",
+ "0 2926.000135 181352 \n",
+ "1 2925.999560 260096 \n",
+ "2 2925.999717 249972 \n",
+ "3 2926.000107 131245 \n",
+ "4 2599.999649 179306 "
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_meta = pd.read_parquet(f\"{base_folder}/resources/bitbrains-small/trace/meta.parquet\")\n",
+ "df_meta.head()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "13bf9fdb",
+ "metadata": {},
+ "source": [
+ "# Lets run this in OpenDC!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c9766446",
+ "metadata": {},
+ "source": [
+ "## Resulting Files"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "0d400ffd",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "TypeError",
+ "evalue": "Addition/subtraction of integers and integer-arrays with Timestamp is no longer supported. Instead of adding/subtracting `n`, use `n * obj.freq`",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[8], line 17\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21madd_absolute_timestamp\u001b[39m(df, start_dt):\n\u001b[1;32m 15\u001b[0m df[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mabsolute_timestamp\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m start_dt \u001b[38;5;241m+\u001b[39m (df[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtimestamp\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m-\u001b[39m df[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtimestamp\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mmin())\n\u001b[0;32m---> 17\u001b[0m \u001b[43madd_absolute_timestamp\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdf_host_single\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdf_meta\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstart_time\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmin\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 18\u001b[0m add_absolute_timestamp(df_host_single, df_meta[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstart_time\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mmin())\n\u001b[1;32m 20\u001b[0m add_absolute_timestamp(df_server_single, df_meta[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstart_time\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mmin())\n",
+ "Cell \u001b[0;32mIn[8], line 15\u001b[0m, in \u001b[0;36madd_absolute_timestamp\u001b[0;34m(df, start_dt)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21madd_absolute_timestamp\u001b[39m(df, start_dt):\n\u001b[0;32m---> 15\u001b[0m df[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mabsolute_timestamp\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[43mstart_dt\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43mdf\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtimestamp\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mdf\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtimestamp\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmin\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/ops/common.py:72\u001b[0m, in \u001b[0;36m_unpack_zerodim_and_defer.<locals>.new_method\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mNotImplemented\u001b[39m\n\u001b[1;32m 70\u001b[0m other \u001b[38;5;241m=\u001b[39m item_from_zerodim(other)\n\u001b[0;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmethod\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mother\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/arraylike.py:107\u001b[0m, in \u001b[0;36mOpsMixin.__radd__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[38;5;129m@unpack_zerodim_and_defer\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__radd__\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 106\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__radd__\u001b[39m(\u001b[38;5;28mself\u001b[39m, other):\n\u001b[0;32m--> 107\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_arith_method\u001b[49m\u001b[43m(\u001b[49m\u001b[43mother\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mroperator\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mradd\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/series.py:6262\u001b[0m, in \u001b[0;36mSeries._arith_method\u001b[0;34m(self, other, op)\u001b[0m\n\u001b[1;32m 6260\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_arith_method\u001b[39m(\u001b[38;5;28mself\u001b[39m, other, op):\n\u001b[1;32m 6261\u001b[0m \u001b[38;5;28mself\u001b[39m, other \u001b[38;5;241m=\u001b[39m ops\u001b[38;5;241m.\u001b[39malign_method_SERIES(\u001b[38;5;28mself\u001b[39m, other)\n\u001b[0;32m-> 6262\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mbase\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mIndexOpsMixin\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_arith_method\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mother\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/base.py:1325\u001b[0m, in \u001b[0;36mIndexOpsMixin._arith_method\u001b[0;34m(self, other, op)\u001b[0m\n\u001b[1;32m 1322\u001b[0m rvalues \u001b[38;5;241m=\u001b[39m ensure_wrapped_if_datetimelike(rvalues)\n\u001b[1;32m 1324\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m np\u001b[38;5;241m.\u001b[39merrstate(\u001b[38;5;28mall\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mignore\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m-> 1325\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mops\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marithmetic_op\u001b[49m\u001b[43m(\u001b[49m\u001b[43mlvalues\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrvalues\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1327\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_construct_result(result, name\u001b[38;5;241m=\u001b[39mres_name)\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/ops/array_ops.py:218\u001b[0m, in \u001b[0;36marithmetic_op\u001b[0;34m(left, right, op)\u001b[0m\n\u001b[1;32m 205\u001b[0m \u001b[38;5;66;03m# NB: We assume that extract_array and ensure_wrapped_if_datetimelike\u001b[39;00m\n\u001b[1;32m 206\u001b[0m \u001b[38;5;66;03m# have already been called on `left` and `right`,\u001b[39;00m\n\u001b[1;32m 207\u001b[0m \u001b[38;5;66;03m# and `maybe_prepare_scalar_for_op` has already been called on `right`\u001b[39;00m\n\u001b[1;32m 208\u001b[0m \u001b[38;5;66;03m# We need to special-case datetime64/timedelta64 dtypes (e.g. because numpy\u001b[39;00m\n\u001b[1;32m 209\u001b[0m \u001b[38;5;66;03m# casts integer dtypes to timedelta64 when operating with timedelta64 - GH#22390)\u001b[39;00m\n\u001b[1;32m 211\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[1;32m 212\u001b[0m should_extension_dispatch(left, right)\n\u001b[1;32m 213\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(right, (Timedelta, BaseOffset, Timestamp))\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 216\u001b[0m \u001b[38;5;66;03m# Timedelta/Timestamp and other custom scalars are included in the check\u001b[39;00m\n\u001b[1;32m 217\u001b[0m \u001b[38;5;66;03m# because numexpr will fail on it, see GH#31457\u001b[39;00m\n\u001b[0;32m--> 218\u001b[0m res_values \u001b[38;5;241m=\u001b[39m \u001b[43mop\u001b[49m\u001b[43m(\u001b[49m\u001b[43mleft\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mright\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 219\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 220\u001b[0m \u001b[38;5;66;03m# TODO we should handle EAs consistently and move this check before the if/else\u001b[39;00m\n\u001b[1;32m 221\u001b[0m \u001b[38;5;66;03m# (https://github.com/pandas-dev/pandas/issues/41165)\u001b[39;00m\n\u001b[1;32m 222\u001b[0m _bool_arith_check(op, left, right)\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/roperator.py:11\u001b[0m, in \u001b[0;36mradd\u001b[0;34m(left, right)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mradd\u001b[39m(left, right):\n\u001b[0;32m---> 11\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mright\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mleft\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/_libs/tslibs/timestamps.pyx:504\u001b[0m, in \u001b[0;36mpandas._libs.tslibs.timestamps._Timestamp.__add__\u001b[0;34m()\u001b[0m\n",
+ "\u001b[0;31mTypeError\u001b[0m: Addition/subtraction of integers and integer-arrays with Timestamp is no longer supported. Instead of adding/subtracting `n`, use `n * obj.freq`"
+ ]
+ }
+ ],
+ "source": [
+ "output_folder = f\"{base_folder}/output\"\n",
+ "workload = \"workload=bitbrains-small\"\n",
+ "seed = \"seed=0\"\n",
+ "\n",
+ "df_host_single = pd.read_parquet(f\"{output_folder}/topology=single/{workload}/{seed}/host.parquet\")\n",
+ "df_host_multi = pd.read_parquet(f\"{output_folder}/topology=multi/{workload}/{seed}/host.parquet\")\n",
+ "\n",
+ "df_server_single = pd.read_parquet(f\"{output_folder}/topology=single/{workload}/{seed}/server.parquet\")\n",
+ "df_server_multi = pd.read_parquet(f\"{output_folder}/topology=multi/{workload}/{seed}/server.parquet\")\n",
+ "\n",
+ "df_service_single = pd.read_parquet(f\"{output_folder}/topology=single/{workload}/{seed}/service.parquet\")\n",
+ "df_service_multi = pd.read_parquet(f\"{output_folder}/topology=multi/{workload}/{seed}/service.parquet\")\n",
+ "\n",
+ "def add_absolute_timestamp(df, start_dt):\n",
+ " df[\"absolute_timestamp\"] = start_dt + (df[\"timestamp\"] - df[\"timestamp\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_host_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_host_single, df_meta[\"start_time\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_server_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_server_multi, df_meta[\"start_time\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_service_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_service_multi, df_meta[\"start_time\"].min())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "a9a61332",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_host_single = pd.read_parquet(f\"{output_folder}/topology=single/{workload}/{seed}/host.parquet\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "id": "d6fb41d9",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Timedelta('0 days 00:05:00')"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pd.Timedelta(300000, unit=\"ms\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "id": "3c271734",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "AttributeError",
+ "evalue": "Can only use .dt accessor with datetimelike values",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[30], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mdf_host_single\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtimestamp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdt\u001b[49m\u001b[38;5;241m.\u001b[39mto_pytimedelta()\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/generic.py:5907\u001b[0m, in \u001b[0;36mNDFrame.__getattr__\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 5900\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[1;32m 5901\u001b[0m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_internal_names_set\n\u001b[1;32m 5902\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_metadata\n\u001b[1;32m 5903\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m name \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_accessors\n\u001b[1;32m 5904\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_info_axis\u001b[38;5;241m.\u001b[39m_can_hold_identifiers_and_holds_name(name)\n\u001b[1;32m 5905\u001b[0m ):\n\u001b[1;32m 5906\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m[name]\n\u001b[0;32m-> 5907\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mobject\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__getattribute__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/accessor.py:183\u001b[0m, in \u001b[0;36mCachedAccessor.__get__\u001b[0;34m(self, obj, cls)\u001b[0m\n\u001b[1;32m 180\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 181\u001b[0m \u001b[38;5;66;03m# we're accessing the attribute of the class, i.e., Dataset.geo\u001b[39;00m\n\u001b[1;32m 182\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_accessor\n\u001b[0;32m--> 183\u001b[0m accessor_obj \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_accessor\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 184\u001b[0m \u001b[38;5;66;03m# Replace the property with the accessor object. Inspired by:\u001b[39;00m\n\u001b[1;32m 185\u001b[0m \u001b[38;5;66;03m# https://www.pydanny.com/cached-property.html\u001b[39;00m\n\u001b[1;32m 186\u001b[0m \u001b[38;5;66;03m# We need to use object.__setattr__ because we overwrite __setattr__ on\u001b[39;00m\n\u001b[1;32m 187\u001b[0m \u001b[38;5;66;03m# NDFrame\u001b[39;00m\n\u001b[1;32m 188\u001b[0m \u001b[38;5;28mobject\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;21m__setattr__\u001b[39m(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_name, accessor_obj)\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/indexes/accessors.py:513\u001b[0m, in \u001b[0;36mCombinedDatetimelikeProperties.__new__\u001b[0;34m(cls, data)\u001b[0m\n\u001b[1;32m 510\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m is_period_dtype(data\u001b[38;5;241m.\u001b[39mdtype):\n\u001b[1;32m 511\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m PeriodProperties(data, orig)\n\u001b[0;32m--> 513\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCan only use .dt accessor with datetimelike values\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
+ "\u001b[0;31mAttributeError\u001b[0m: Can only use .dt accessor with datetimelike values"
+ ]
+ }
+ ],
+ "source": [
+ "df_host_single.timestamp.to_pytimedelta()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "89977c44",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0 1970-01-01 00:05:00+00:00\n",
+ "1 1970-01-01 00:10:00+00:00\n",
+ "2 1970-01-01 00:15:00+00:00\n",
+ "3 1970-01-01 00:20:00+00:00\n",
+ "4 1970-01-01 00:25:00+00:00\n",
+ " ... \n",
+ "25918 1970-03-31 23:55:00+00:00\n",
+ "25919 1970-04-01 00:00:00+00:00\n",
+ "25920 1970-04-01 00:05:00+00:00\n",
+ "25921 1970-04-01 00:10:00+00:00\n",
+ "25922 1970-04-01 00:14:12+00:00\n",
+ "Name: timestamp, Length: 25923, dtype: datetime64[ns, UTC]"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_host_single.timestamp"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 148,
+ "id": "eadd08e4",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1970-01-01 00:05:00+00:00 1\n",
+ "1970-03-01 23:55:00+00:00 1\n",
+ "1970-03-02 00:45:00+00:00 1\n",
+ "1970-03-02 00:40:00+00:00 1\n",
+ "1970-03-02 00:35:00+00:00 1\n",
+ " ..\n",
+ "1970-01-30 23:50:00+00:00 1\n",
+ "1970-01-30 23:45:00+00:00 1\n",
+ "1970-01-30 23:40:00+00:00 1\n",
+ "1970-01-30 23:35:00+00:00 1\n",
+ "1970-04-01 00:10:00+00:00 1\n",
+ "Name: timestamp, Length: 25922, dtype: int64"
+ ]
+ },
+ "execution_count": 148,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_service_single.timestamp.value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 104,
+ "id": "a32f9d66",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([44, 45, 46, 47, 49, 50, 34, 16, 14, 13, 12, 11, 10], dtype=int32)"
+ ]
+ },
+ "execution_count": 104,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(df_service_single.servers_active + df_service_single.servers_pending).unique() "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 102,
+ "id": "16f4a6b6",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 102,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "set(d1) == set(d2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "09d31c91",
+ "metadata": {},
+ "source": [
+ "## Power Usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 150,
+ "id": "82f0a24a",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single topology: 2227253755.2781296\n",
+ "multi topology: 5864872551.731657\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"single topology: {df_host_single.power_total.sum()}\")\n",
+ "print(f\"multi topology: {df_host_multi.power_total.sum()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7ab3357d",
+ "metadata": {},
+ "source": [
+ "## CPU usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 151,
+ "id": "e94db3a6",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single topology: 0.5760561514665646\n",
+ "multi topology: 0.3425398748402685\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"single topology: {df_host_single.cpu_utilization.mean()}\")\n",
+ "print(f\"multi topology: {df_host_multi.cpu_utilization.mean()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e000a260",
+ "metadata": {},
+ "source": [
+ "## CPU utilization"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 152,
+ "id": "8d7daa45",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single topology: 0.5760561514665646\n",
+ "multi topology: 0.3425398748402685\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"single topology: {df_host_single.cpu_utilization.mean()}\")\n",
+ "print(f\"multi topology: {df_host_multi.cpu_utilization.mean()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ad97741c",
+ "metadata": {},
+ "source": [
+ "## Plotting Results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 153,
+ "id": "5df8f9aa",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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8nJ98sknmjJliv7whz+4ukYAAIBac+puqTlz5ig7O1u33nqrvLwu7qKiokJxcXGsuQEAAG7lVLixWq1avXq15syZo/3796t58+bq2bOnOnTo4Or6AAAAHOJUuLmka9eu6tq1q6tqAQAAqDOnwk15ebnS0tKUnp6ukydPqqKiotLnn3zyiUuKAwAAcJRT4WbKlClKS0vTnXfeqR49eshisbi6LgAAAKc4FW5WrVqlv/3tbxo+fLir6wEAAKgTp24Ft1qt6tKli6trAQAAqDOnws0TTzyhl19+WYZhuLoeAACAOnHqstS2bdu0detWffTRR7r++uvVrFmzSp+vXbvWJcUBAAA4yqlwExQUpLvvvtvVtQAAANSZU+FmxYoVrq4DAADAJZxacyNJFy5c0JYtW/SXv/xFZ86ckSSdOHFCZ8+edVlxAAAAjnJq5ua7777T7bffrpycHJWUlGjYsGHy9/fXvHnzVFJSotTUVFfXCQAAUCtOzdxMmTJFUVFR+vnnn9W8eXN7+91336309HSXFQcAAOAop2ZuPvvsM+3YsUNWq7VSe3h4uH744QeXFAYAAOAMp2ZuKioqVF5eXqX9+++/l7+/f52LAgAAcJZT4ea2225TSkqK/b3FYtHZs2c1a9YsHskAAADcyqnLUosWLVJMTIy6d++u8+fPa8yYMTp8+LCCg4P17rvvurpGAACAWnMq3PzmN7/R/v37tWrVKh04cEBnz57V+PHjdd9991VaYAwAANDQnAo3kuTl5aX777/flbUAAADUmVPh5o033rjs53FxcU4VAwAAUFdOhZspU6ZUel9WVqZz587JarXK19eXcAMAANzGqbulfv7550qvs2fPKisrS4MHD2ZBMQAAcCunny31axEREZo7d26VWR0AAICG5LJwI11cZHzixAlX7hIAAMAhTq25ef/99yu9NwxDubm5evXVVzVo0CCXFAYAAOAMp8LNqFGjKr23WCxq06aNbrnlFi1atMgVdQEAADjFqXBTUVHh6joAAABcwqVrbgAAANzNqZmbxMTEWvdNTk525hAAAABOcSrc7Nu3T/v27VNZWZmuvfZaSdI333wjT09P3XjjjfZ+FovFNVUCAADUklPhZsSIEfL399fKlSvVsmVLSRd/2C8hIUFDhgzRE0884dIiAQAAasupNTeLFi1SUlKSPdhIUsuWLfXCCy9w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loqOjr7ivxdc5Oif50tWrVoli8WiUaNG1W+BJuHoOJ8+fVqTJk1S27Zt5e3tra5du/L/jlpwdJxTUlJ07bXXqnnz5rLZbHr88cd1/vz5Bqq2afr00081YsQItWvXThaLpcbnOv5SRkaGbrzxRnl7e6tLly5KS0ur9zploNZWrVplWK1WY/ny5cZXX31lTJgwwQgKCjLy8/Or7b99+3bD09PTmD9/vvH1118bM2bMMJo1a2Z8+eWXDVx50+LoOI8ZM8ZYsmSJsW/fPuPgwYNGfHy8ERgYaHz//fcNXHnT4ug4X/Ltt98a7du3N4YMGWKMHDmyYYptwhwd55KSEiMqKsoYPny4sW3bNuPbb781MjIyjMzMzAauvGlxdJzffvttw9vb23j77beNb7/91ti0aZPRtm1b4/HHH2/gypuWDRs2GNOnTzfWrl1rSDLWrVt32f7Hjh0zfH19jcTEROPrr782Fi9ebHh6ehobN26s1zoJNw7o16+fMWnSJPv78vJyo127dkZSUlK1/e+9917jzjvvrNTWv39/4+GHH67XOps6R8f51y5cuGD4+/sbK1eurK8STcGZcb5w4YIxcOBA469//asxbtw4wk0tODrOr732mtGpUyejtLS0oUo0BUfHedKkScYtt9xSqS0xMdEYNGhQvdZpJrUJN0899ZRx/fXXV2qLjY01YmJi6rEyw+CyVC2VlpZqz549io6Otrd5eHgoOjpaO3furHabnTt3VuovSTExMTX2h3Pj/Gvnzp1TWVmZWrVqVV9lNnnOjvPzzz+vkJAQjR8/viHKbPKcGef3339fAwYM0KRJkxQaGqoePXropZdeUnl5eUOV3eQ4M84DBw7Unj177Jeujh07pg0bNmj48OENUvPVwl3fg27/heKm4tSpUyovL7f/cvIloaGhOnToULXb5OXlVds/Ly+v3ups6pwZ5197+umn1a5duyr/QeFfnBnnbdu2admyZcrMzGyACs3BmXE+duyYPvnkE913333asGGDjhw5ookTJ6qsrEyzZs1qiLKbHGfGecyYMTp16pQGDx4swzB04cIFPfLII3r22WcbouSrRk3fg0VFRfq///s/NW/evF6Oy8wNTGXu3LlatWqV1q1bJx8fH3eXYxpnzpzR2LFjtXTpUgUHB7u7HFOrqKhQSEiIXn/9dfXp00exsbGaPn26UlNT3V2a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p6dntZe9JMnD4+Lft34ZtH55CceV8vLydNddd+nBBx/U+PHj7e3FxcWKiYlRTEyM3n77bbVp00Y5OTmKiYmpl8tuzZo1q/TeYrE4HDQBVMbMDQBJF+/0iYmJ0ZIlS1RcXFzl81/f9hwcHKwuXbooLCysUrCRpGuvvVZfffWVSkpKKrXv3btXHTt2rPKFfkmbNm0kqdKC2l//No3ValV5eXltT6ta58+f18iRI9WtWzclJydX+uzQoUP68ccfNXfuXA0ZMkTdunWrMpNitVol6bJ1XHfddbpw4YI+//xze9uPP/6orKwsde/evU71A7g8wg0AuyVLlqi8vFz9+vXTe++9p8OHD+vgwYN65ZVXNGDAgFrv57777pPFYlFcXJz27NmjI0eOaPny5UpJSdETTzxR43ZdunSRzWbT7NmzdfjwYX344YdatGhRpT7h4eE6e/as0tPTderUKZ07d87h83z44Yd1/PhxvfLKKyooKFBeXp7y8vJUWlqqa665RlarVYsXL9axY8f0/vvvV/ntmg4dOshiseiDDz5QQUGBzp49W+UYERERGjlypCZMmKBt27Zp//79uv/++9W+fXuNHDnS4ZoB1B7hBoBdp06dtHfvXg0dOlRPPPGEevTooWHDhik9PV2vvfZarfcTFBSkzz77TGVlZbrrrrsUGRmpV155RcnJyXr44Ydr3K5Zs2Z69913dejQIfXq1Uvz5s3TCy+8UKnPwIED9cgjjyg2NlZt2rTR/PnzHT7P//3f/1Vubq66d++utm3b2l87duxQmzZtlJaWpjVr1qh79+6aO3euFi5cWGn79u3b67nnntMzzzyj0NBQTZ48udrjrFixQn369NFvf/tbDRgwQIZhaMOGDTXOXAFwDYvBxV0AAGAizNwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABTIdwAAABT+X8rUl+oucHktAAAAABJRU5ErkJggg==",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "data = df_host_multi.cpu_utilization\n",
+ "plt.hist(data, weights=np.ones_like(data) / len(data),\n",
+ " alpha=0.7, label=\"multi\", bins=30)\n",
+ "\n",
+ "\n",
+ "data = df_host_single.cpu_utilization\n",
+ "plt.hist(data, weights=np.ones_like(data) / len(data),\n",
+ " alpha=0.7, label=\"single\", bins=30)\n",
+ "\n",
+ "plt.xlabel(\"CPU utilization\")\n",
+ "plt.ylabel(\"Frequency\")\n",
+ "plt.legend()\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 154,
+ "id": "42c0c638",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "<matplotlib.legend.Legend at 0x7f6fc2cc78b0>"
+ ]
+ },
+ "execution_count": 154,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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qvBhtVo1c+ZMWa1WV5fS4NC2tYe2rT20be2hbWtPQ2jbOjfgFAAANGyNqucDAAC4HuEDAACYivABAABMRfgAAACmajThY+HChbruuuvk5eWlvn376pNPPnF1SXXKE088IYvFYvfo2LGjbXtxcbGmTJmiFi1aqFmzZho1apTy8vLsjnHkyBH96le/ko+Pj4KCgjRt2jRduHDBbp/MzEz17NlTVqtV7dq107Jly8x4e6basmWLhg0bprCwMFksFq1evdpuu2EYSk1NVWhoqLy9vRUXF6evvvrKbp9Tp04pKSlJfn5+CggI0IQJE3T27Fm7ffbv368BAwbIy8tLERERmjdv3mW1vPXWW+rYsaO8vLwUExOjdevWOf39mulabTt+/PjLfo8TEhLs9qFtKzd37lz17t1bvr6+CgoK0ogRI5SVlWW3j5mfAw3pM7sqbTto0KDLfncnT55st0+DalujEXj99dcNT09P4+9//7tx8OBBY+LEiUZAQICRl5fn6tLqjJkzZxpdunQxjh07ZnucPHnStn3y5MlGRESEkZGRYezatcvo16+fcdNNN9m2X7hwwejatasRFxdn7Nmzx1i3bp3RsmVLIyUlxbbP119/bfj4+BjJycnGoUOHjBdeeMFo0qSJsX79elPfa21bt26d8eijjxorV640JBmrVq2y2/7UU08Z/v7+xurVq419+/YZv/71r43o6Gjjxx9/tO2TkJBgdO/e3dixY4exdetWo127dsbYsWNt2/Pz843g4GAjKSnJOHDggPHaa68Z3t7exksvvWTbZ9u2bUaTJk2MefPmGYcOHTIee+wxw8PDw/j8889rvQ1qy7Xadty4cUZCQoLd7/GpU6fs9qFtKxcfH28sXbrUOHDggLF3715j6NChRmRkpHH27FnbPmZ9DjS0z+yqtO0tt9xiTJw40e53Nz8/37a9obVtowgfffr0MaZMmWJ7XlZWZoSFhRlz5851YVV1y8yZM43u3btXuu3MmTOGh4eH8dZbb9le++KLLwxJxvbt2w3DuPil4ObmZhw/fty2z+LFiw0/Pz+jpKTEMAzDmD59utGlSxe7Y48ePdqIj4938rupO375BVleXm6EhIQYTz/9tO21M2fOGFar1XjttdcMwzCMQ4cOGZKMTz/91LbPe++9Z1gsFuM///mPYRiGsWjRIqN58+a2tjUMw5gxY4bR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",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "plt.plot(df_service_single.servers_pending, label=\"servers pending\")\n",
+ "plt.plot(df_service_single.servers_active, label=\"servers active\")\n",
+ "\n",
+ "plt.legend()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 155,
+ "id": "1a688c2d",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "<matplotlib.legend.Legend at 0x7f6fc02a5db0>"
+ ]
+ },
+ "execution_count": 155,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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mZmZF+y7b9++iomJUXp6uuLj411GT9LT0/XUU09p6NChmjFjhlq1aqV//vOfWrFihSZNmqRWrVqds89Vq1bpwIED6tu3r5o1a6Z3331X1dXVFzwU4e/vr/Hjx2vevHny8/PTfffdp169eplhZOrUqRoyZIji4uJ0++23y8fHRzt37tSuXbtcAtOF3HvvvZozZ44efPBB3X333crLy1NWVtZFLXs+4eHhGjNmjB588EFFRkYqKipK06ZNk4+PD7dRB4B6wjkfFrDb7dqwYYMGDx6sq666Sg8//LDmzJmjQYMGSZLuvvtuLV68WEuWLFFSUpJuuOEGZWVlKT4+/mf7ttlsGjVqlHbu3HnWyElISIg2bNiguLg4paWlKTExUePGjVNZWdkFR0IcDodWrFihfv36KTExUS+88IJeeeUVXXPNNeddJiQkRA899JB++ctfqnfv3goLC9Py5cvN+ampqVq1apU++OADde/eXb169dLcuXPNE3AvRlxcnF5//XW98cYb6tSpk1544QU98cQTF738+Tz99NNKTk7WkCFDlJKSot69e5uXPQMA6p7NuNjrRS3idDoVERGhkpKSs74gy8rKdPDgQcXHx/PF0IBkZWVp4sSJ5i3Ovd3Jkyd1+eWXa86cORo3btxZ8/k9BOC19q+V/nqbFH2t9LuP67TrC31//xSHXdDkffrpp/riiy/Uo0cPlZSUaMaMGZKkoUOHergyAKhrDeNwMuEDkPR///d/ys/PV0BAgLp27aqNGzeqRYsWni4LABolwgcu2dixYzV27FhPl1FrnTt3Vl5enqfLAIAmgxNOAQCApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfhAgzV27FgNGzbM02UAAOoY9/mAxx06dEjx8fH69NNPdd1115nTn332WTWwu/8DQOPg4X9bCR9eqrKyUv7+/nXeb0VFhQICAuq839qIiIjwdAkAgHrAYReLvPbaa0pKSlJwcLCaN2+ulJQUnTx50py/ePFi80mqCQkJWrBggTnv0KFDstlsWr58uW644QYFBQVp4cKFCg4O1nvvveeynpUrVyo8PFynTp2SJB05ckR33nmnHA6HIiMjNXToUB06dMhsX3NoY+bMmYqNjVWHDh0kSQsWLFD79u0VFBSk6Oho3X777efdtm+//VajRo3S5ZdfrpCQECUlJemVV15xaVNdXa0nn3xS7dq1U2BgoOLi4jRz5kxJMp/e27lzZ9lsNt14440utUnSn/70J8XGxqq6utql36FDh+o3v/mN+f7NN99Uly5dFBQUpLZt22r69Ok6ffr0eWsHAFjP+0c+DEOqPOWZdfuHSLaff0hPQUGBRo0apSeffFK33XabSktLtXHjRvOQwtKlSzV16lQ9//zz6ty5sz799FPdc889Cg0N1ZgxY8x+Jk+erDlz5qhz584KCgrSxo0blZ2drUGDBpltli5dqmHDhikkJESVlZVKTU1VcnKyNm7cKD8/Pz3++OMaOHCgPvvsM3OEIycnR3a7XWvWrJEkbdu2Tffff7/++te/6he/+IWOHz+ujRs3nnf7ysrK1LVrVz300EOy2+165513NHr0aF155ZXq0aOHJGnKlClatGiR5s6dqz59+qigoEBffPGFJGnLli3q0aOH/vGPf+iaa64558jLHXfcofHjx+vDDz9U//79JUnHjx/X6tWr9e6770qSNm7cqF//+teaN2+err/+eu3fv1+//e1vJUnTpk372f0EALCG94ePylPSE7GeWff/HJUCQn+2WUFBgU6fPq20tDS1adNGkpSUlGTOnzZtmubMmaO0tDRJZ0YC9uzZoxdffNElfEycONFsI0np6ekaPXq0Tp06pZCQEDmdTr3zzjtauXKlJGn58uWqrq7W4sWLZftPSFqyZIkcDofWrVunm2++WZIUGhqqxYsXm1/6K1asUGhoqIYMGaLw8HC1adNGnTt3Pu/2XX755frDH/5gvh8/frzef/99/f3vf1ePHj1UWlqqZ599Vs8//7y5PVdeeaX69OkjSbrsssskSc2bN1dMTMw519GsWTMNGjRI2dnZZvh47bXX1KJFC910002SpOnTp2vy5MnmOtq2bavHHntMkyZNInwAQAPCYRcLdOrUSf3791dSUpLuuOMOLVq0SN99950k6eTJk9q/f7/GjRunsLAw8/X4449r//79Lv1069bN5f3gwYPl7++vt956S5L0+uuvy263KyUlRZK0c+dO7du3T+Hh4Wa/kZGRKisrc+k7KSnJZbRhwIABatOmjdq2bavRo0dr6dKl5mGcc6mqqtJjjz2mpKQkRUZGKiwsTO+//74OHz4sSdq7d6/Ky8vN0FBb6enpev3111VeXi7pzCjPyJEj5ePjY27vjBkzXD7H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AACApQgfAAA0NRfxuJT6RPgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAQFNhs3m6AkmEDwAAYDHCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAGhyDI+unfABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAJoMm6cLkET4AAAAFiN8AAAASxE+AACApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAQFNjGB5dvdvhY8OGDbr11lsVGxsrm82mN954w2W+YRiaOnWqWrZsqeDgYKWkpOirr76qq3oBAICXczt8nDx5Up06ddL8+fPPOf/JJ5/UvHnz9MILLyg3N1ehoaFKTU1VWVnZJRcLAAC8n5+7CwwaNEiDBg065zzDMPTMM8/o4Ycf1tChQyVJf/nLXxQdHa033nhDI0eOvLRqAQCA16vTcz4OHjyowsJCpaSkmNMiIiLUs2dPbdq06ZzLlJeXy+l0urwAAEDjVafho7CwUJIUHR3tMj06Otqc91OzZs1SRESE+WrdunVdlgQAAGrYbJ6uQFIDuNplypQpKikpMV9HjhzxdEkAAKAe1Wn4iImJkSQVFRW5TC8qKjLn/VRgYKDsdrvLCwAANF51Gj7i4+MVExOjnJwcc5rT6VRubq6Sk5PrclUAAMBLuX21y4kTJ7Rv3z7z/cGDB7Vjxw5FRkYqLi5OEydO1OOPP6727dsrPj5ejzzyiGJjYzVs2LC6rBsAAHgpt8PHtm3bdNNNN5nvMzMzJUljxoxRVlaWJk2apJMnT+q3v/2tiouL1adPH61evVpBQUF1VzUAAPBaNsPw8D1Wf8LpdCoiIkIlJSWc/wEAQF069JGUdYvUooN035Y67dqd72+PX+0CAACaFsIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAQJPj2ce6ET4AAIClCB8AAMBShA8AAJoMm6cLkET4AAAAFiN8AAAASxE+AACApQgfAADAUoQP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aPSHXVq0aCFfX9+zztYvKipSTEyMh6pq/O677z6tWrVKH374oVq1amVOj4mJUUVFhYqLi13a/3h/xMTEnHN/1cy7UBu73c7/qi8gLy9Px44dU5cuXeTn5yc/Pz+tX79e8+bNk5+fn6Kjo9k/HtSyZUtdffXVLtMSExN1+PBhST98vhf69ywmJkbHjh1zmX/69GkdP37crX2Ic3vwwQc1efJkjRw5UklJSRo9erQeeOABzZo1SxL76GI1+vAREBCgrl27Kicnx5xWXV2tnJwcJScne7CyxskwDN13331auXKl1q5de9awYdeuXeXv7++yP/Lz83X48GFzfyQnJ+vzzz93+cu5Zs0a2e128x/m5ORklz5q2rBPL6x///76/PPPtWPHDvPVrVs3paenm39m/3hO7969z7o0/csvv1SbNm0kSfHx8YqJiXH5bJ1Op3Jzc132T3FxsfLy8sw2a9euVXV1tXr27Gm22bBhgyorK802a9asUYcOHbx+OL++nTp1Sj4+rl+dvr6+qq6ulsQ+umiePuPVCsuWLTMCAwONrKwsY8+ePcZvf/tbw+FwuJytj7rxu9/9zoiIiDDWrVtnFBQUmK9Tp06Zbe69914jLi7OWLt2rbFt2zYjOTnZSE5ONufXXMp58803Gzt27DBWr15tXHbZZee8lPPBBx809u7da8yfP59LOWvpx1e7GAb7x5O2bNli+Pn5GTNnzjS++uorY+nSpUZISIjxt7/9zWwze/Zsw+FwGG+++abx2WefGUOHDj3nZZydO3c2cnNzjY8++sho3769y2WcxcXFRnR0tDF69Ghj165dxrJly4yQkJBGcxlnfRozZoxx+eWXm5farlixwmjRooUxadIksw376Oc1ifBhGIbx3HPPGXFxcUZAQIDRo0cPY/PmzZ4uqVGSdM7XkiVLzDbff/+98d///d9Gs2bNjJCQEOO2224zCgoKXPo5dOiQMWjQICM4ONho0aKF8fvf/96orKx0afPhhx8a1113nREQEGC0bdvWZR24eD8NH+wfz3r77beNa6+91ggMDDQSEhKMP/3pTy7zq6urjUceecSIjo42AgMDjf79+xv5+fkubb799ltj1KhRRlhYmGG324277rrLKC0tdWmzc+dOo0+fPkZgYKBx+eWXG7Nnz673bWsMnE6nMWHCBCMuLs4ICgoy2rZta/zxj390uSSWffTzbIbxo9uyAQAA1LNGf84HAABoWAgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALDU/wfbnjItaCtKlQAAAABJRU5ErkJggg==",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "plt.plot(df_service_multi.servers_pending, label=\"servers pending\")\n",
+ "plt.plot(df_service_multi.servers_active, label=\"servers active\")\n",
+ "\n",
+ "plt.legend()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 156,
+ "id": "dc4e17cd",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>timestamp</th>\n",
+ " <th>server_id</th>\n",
+ " <th>host_id</th>\n",
+ " <th>mem_capacity</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_limit</th>\n",
+ " <th>cpu_time_active</th>\n",
+ " <th>cpu_time_idle</th>\n",
+ " <th>cpu_time_steal</th>\n",
+ " <th>cpu_time_lost</th>\n",
+ " <th>uptime</th>\n",
+ " <th>downtime</th>\n",
+ " <th>provision_time</th>\n",
+ " <th>boot_time</th>\n",
+ " <th>absolute_timestamp</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1970-01-01 00:05:00+00:00</td>\n",
+ " <td>b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x06\\xc4]\\x1...</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>181</td>\n",
+ " <td>1</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>0</td>\n",
+ " <td>2624</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>300000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1970-01-01 00:05:00+00:00</td>\n",
+ " <td>b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1b9\\x89jQ\\...</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>260</td>\n",
+ " <td>1</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>0</td>\n",
+ " <td>2624</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>300000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1970-01-01 00:05:00+00:00</td>\n",
+ " <td>b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00,\\x82\\x9a\\xb...</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>250</td>\n",
+ " <td>1</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>2</td>\n",
+ " <td>2622</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>300000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1970-01-01 00:05:00+00:00</td>\n",
+ " <td>b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00&gt;\\xe5x\\x90A\\...</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>125</td>\n",
+ " <td>1</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>0</td>\n",
+ " <td>2624</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>300000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1970-01-01 00:05:00+00:00</td>\n",
+ " <td>b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00e~\\xec\\xdd&lt;\\...</td>\n",
+ " <td>b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\...</td>\n",
+ " <td>157</td>\n",
+ " <td>1</td>\n",
+ " <td>25600.0</td>\n",
+ " <td>2</td>\n",
+ " <td>2951</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>300000</td>\n",
+ " <td>0</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>1970-01-01 00:00:00+00:00</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " timestamp \\\n",
+ "0 1970-01-01 00:05:00+00:00 \n",
+ "1 1970-01-01 00:05:00+00:00 \n",
+ "2 1970-01-01 00:05:00+00:00 \n",
+ "3 1970-01-01 00:05:00+00:00 \n",
+ "4 1970-01-01 00:05:00+00:00 \n",
+ "\n",
+ " server_id \\\n",
+ "0 b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x06\\xc4]\\x1... \n",
+ "1 b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1b9\\x89jQ\\... \n",
+ "2 b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00,\\x82\\x9a\\xb... \n",
+ "3 b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00>\\xe5x\\x90A\\... \n",
+ "4 b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00e~\\xec\\xdd<\\... \n",
+ "\n",
+ " host_id mem_capacity cpu_count \\\n",
+ "0 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 181 1 \n",
+ "1 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 260 1 \n",
+ "2 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 250 1 \n",
+ "3 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 125 1 \n",
+ "4 b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\... 157 1 \n",
+ "\n",
+ " cpu_limit cpu_time_active cpu_time_idle cpu_time_steal cpu_time_lost \\\n",
+ "0 25600.0 0 2624 0 0 \n",
+ "1 25600.0 0 2624 0 0 \n",
+ "2 25600.0 2 2622 0 0 \n",
+ "3 25600.0 0 2624 0 0 \n",
+ "4 25600.0 2 2951 0 0 \n",
+ "\n",
+ " uptime downtime provision_time boot_time \\\n",
+ "0 300000 0 1970-01-01 00:00:00+00:00 1970-01-01 00:00:00+00:00 \n",
+ "1 300000 0 1970-01-01 00:00:00+00:00 1970-01-01 00:00:00+00:00 \n",
+ "2 300000 0 1970-01-01 00:00:00+00:00 1970-01-01 00:00:00+00:00 \n",
+ "3 300000 0 1970-01-01 00:00:00+00:00 1970-01-01 00:00:00+00:00 \n",
+ "4 300000 0 1970-01-01 00:00:00+00:00 1970-01-01 00:00:00+00:00 \n",
+ "\n",
+ " absolute_timestamp \n",
+ "0 2013-08-12 13:35:46+00:00 \n",
+ "1 2013-08-12 13:35:46+00:00 \n",
+ "2 2013-08-12 13:35:46+00:00 \n",
+ "3 2013-08-12 13:35:46+00:00 \n",
+ "4 2013-08-12 13:35:46+00:00 "
+ ]
+ },
+ "execution_count": 156,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_server_single.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 157,
+ "id": "b0e6c7bf",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "utilization = df_host_single.cpu_utilization.to_numpy()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 161,
+ "id": "aea7b79d",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[<matplotlib.lines.Line2D at 0x7f6f842c6470>]"
+ ]
+ },
+ "execution_count": 161,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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8isMN0RERORXGG6IiIjIrzDcEBERkV9huCEiIiK/wnBDREREfoXhhoiIiPwKww0RERH5FZ+oCi4IAgBAp9N5uCVEREQ0UOLntvg57i4+EW6am5sBACkpKR5uCRERETmrubkZERERbrueTHB3nBoEs9mMixcvIiwsDDKZzGXvq9PpkJKSgrKyMoSHh7vsfal/vPeewfvuGbzvnsH77hm29z0sLAzNzc1ISkqCXO6+mTA+0XMjl8sxatSoYXv/8PBw/sX3EN57z+B99wzed8/gffcM8b67s8dGxAnFRERE5FcYboiIiMivjOhwo1KpsHbtWqhUKk83ZcThvfcM3nfP4H33DN53z/CG++4TE4qJiIiIBmpE99wQERGR/2G4ISIiIr/CcENERER+heGGiIiI/MqIDjcbNmxAamoq1Go1srKysG/fPk83yWf89re/hUwms/uaPHmy9HpHRwfuu+8+xMTEIDQ0FD/+8Y9RXV1t9x6lpaVYsmQJgoODER8fj4ceeghGo9HumN27d+OSSy6BSqXC+PHj8dZbb7njx/MaX331FZYuXYqkpCTIZDJ8/PHHdq8LgoA1a9YgMTERQUFByMnJwZkzZ+yOaWhowG233Ybw8HBERkbirrvuQktLi90xR44cwbx586BWq5GSkoIXXnihR1s++OADTJ48GWq1GjNmzMC2bdtc/vN6k/7u/Z133tnj38CiRYvsjuG9d866deswZ84chIWFIT4+HjfeeCOKiorsjnHn75aR9BkxkHu/YMGCHn/n77nnHrtjvObeCyPUe++9JyiVSmHTpk3C8ePHhRUrVgiRkZFCdXW1p5vmE9auXStMmzZNqKyslL5qa2ul1++55x4hJSVFyM/PF/bv3y9ceumlwmWXXSa9bjQahenTpws5OTnCwYMHhW3btgmxsbHC6tWrpWOKi4uF4OBgIS8vTzhx4oTwyiuvCAqFQti+fbtbf1ZP2rZtm/D4448LW7ZsEQAIH330kd3rzz//vBARESF8/PHHwuHDh4Uf/vCHwtixY4X29nbpmEWLFgnp6enCd999J3z99dfC+PHjhVtuuUV6XavVChqNRrjtttuEY8eOCe+++64QFBQkvPbaa9Ix33zzjaBQKIQXXnhBOHHihPDEE08IgYGBwtGjR4f9HnhKf/f+jjvuEBYtWmT3b6ChocHuGN575yxcuFB48803hWPHjgmHDh0SFi9eLIwePVpoaWmRjnHX75aR9hkxkHt/5ZVXCitWrLD7O6/VaqXXvenej9hwM3fuXOG+++6TvjeZTEJSUpKwbt06D7bKd6xdu1ZIT093+FpTU5MQGBgofPDBB9JzJ0+eFAAIBQUFgiBYPjjkcrlQVVUlHfPqq68K4eHhgl6vFwRBEB5++GFh2rRpdu+9bNkyYeHChS7+aXxD9w9Ys9ksJCQkCL///e+l55qamgSVSiW8++67giAIwokTJwQAwvfffy8d8+mnnwoymUyoqKgQBEEQ/vKXvwhRUVHSfRcEQXjkkUeESZMmSd/ffPPNwpIlS+zak5WVJfzqV79y6c/orXoLNzfccEOv5/DeD11NTY0AQPjyyy8FQXDv75aR/hnR/d4LgiXc3H///b2e4033fkQOSxkMBhQWFiInJ0d6Ti6XIycnBwUFBR5smW85c+YMkpKSkJaWhttuuw2lpaUAgMLCQnR2dtrd38mTJ2P06NHS/S0oKMCMGTOg0WikYxYuXAidTofjx49Lx9i+h3gM/4wszp8/j6qqKrt7FBERgaysLLv7HBkZidmzZ0vH5OTkQC6XY+/evdIx8+fPh1KplI5ZuHAhioqK0NjYKB3DP4uedu/ejfj4eEyaNAkrV65EfX299Brv/dBptVoAQHR0NAD3/W7hZ0TPey/6xz/+gdjYWEyfPh2rV69GW1ub9Jo33XufKJzpanV1dTCZTHZ/AACg0Whw6tQpD7XKt2RlZeGtt97CpEmTUFlZiaeeegrz5s3DsWPHUFVVBaVSicjISLtzNBoNqqqqAABVVVUO77/4Wl/H6HQ6tLe3IygoaJh+Ot8g3idH98j2HsbHx9u9HhAQgOjoaLtjxo4d2+M9xNeioqJ6/bMQ32MkWrRoEX70ox9h7NixOHfuHB577DFcf/31KCgogEKh4L0fIrPZjF//+te4/PLLMX36dABw2++WxsbGEf0Z4ejeA8Ctt96KMWPGICkpCUeOHMEjjzyCoqIibNmyBYB33fsRGW5o6K6//nrp8cyZM5GVlYUxY8bg/fffH/Ghg0aGn/3sZ9LjGTNmYObMmRg3bhx2796Na665xoMt8w/33Xcfjh07hj179ni6KSNOb/f+l7/8pfR4xowZSExMxDXXXINz585h3Lhx7m5mn0bksFRsbCwUCkWPGfbV1dVISEjwUKt8W2RkJCZOnIizZ88iISEBBoMBTU1NdsfY3t+EhASH9198ra9jwsPDGaDQdZ/6+nuckJCAmpoau9eNRiMaGhpc8mfBfy9d0tLSEBsbi7NnzwLgvR+KVatW4b///S927dqFUaNGSc+763fLSP6M6O3eO5KVlQUAdn/nveXej8hwo1QqkZmZifz8fOk5s9mM/Px8ZGdne7BlvqulpQXnzp1DYmIiMjMzERgYaHd/i4qKUFpaKt3f7OxsHD161O6X/44dOxAeHo6pU6dKx9i+h3gM/4wsxo4di4SEBLt7pNPpsHfvXrv73NTUhMLCQumYnTt3wmw2S7+YsrOz8dVXX6Gzs1M6ZseOHZg0aRKioqKkY/hn0bfy8nLU19cjMTERAO/9YAiCgFWrVuGjjz7Czp07ewzZuet3y0j8jOjv3jty6NAhALD7O+81937AU4/9zHvvvSeoVCrhrbfeEk6cOCH88pe/FCIjI+1meVPvHnzwQWH37t3C+fPnhW+++UbIyckRYmNjhZqaGkEQLMs1R48eLezcuVPYv3+/kJ2dLWRnZ0vni0sGr7vuOuHQoUPC9u3bhbi4OIdLBh966CHh5MmTwoYNG0bcUvDm5mbh4MGDwsGDBwUAwvr164WDBw8KFy5cEATBshQ8MjJS+OSTT4QjR44IN9xwg8Ol4LNmzRL27t0r7NmzR5gwYYLdcuSmpiZBo9EIt99+u3Ds2DHhvffeE4KDg3ssRw4ICBBefPFF4eTJk8LatWv9djmyqK9739zcLPzmN78RCgoKhPPnzwtffPGFcMkllwgTJkwQOjo6pPfgvXfOypUrhYiICGH37t12y43b2tqkY9z1u2WkfUb0d+/Pnj0rPP3008L+/fuF8+fPC5988omQlpYmzJ8/X3oPb7r3IzbcCIIgvPLKK8Lo0aMFpVIpzJ07V/juu+883SSfsWzZMiExMVFQKpVCcnKysGzZMuHs2bPS6+3t7cK9994rREVFCcHBwcJNN90kVFZW2r1HSUmJcP311wtBQUFCbGys8OCDDwqdnZ12x+zatUvIyMgQlEqlkJaWJrz55pvu+PG8xq5duwQAPb7uuOMOQRAsy8GffPJJQaPRCCqVSrjmmmuEoqIiu/eor68XbrnlFiE0NFQIDw8XcnNzhebmZrtjDh8+LFxxxRWCSqUSkpOTheeff75HW95//31h4sSJglKpFKZNmyZs3bp12H5ub9DXvW9raxOuu+46IS4uTggMDBTGjBkjrFixoscvX9575zi63wDs/t2783fLSPqM6O/el5aWCvPnzxeio6MFlUoljB8/XnjooYfs9rkRBO+59zLrD0VERETkF0bknBsiIiLyXww3RERE5FcYboiIiMivMNwQERGRX2G4ISIiIr/CcENERER+heGGiIiI/ArDDREREfkVhhsiIiLyKww3RERE5FcYboiIiMivMNwQERGRX/n/RF6Ek9Jt8GMAAAAASUVORK5CYII=",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "window = 2000\n",
+ "avg_utilization = []\n",
+ "\n",
+ "for ind in range(len(utilization) - window + 1):\n",
+ " avg_utilization.append(np.mean(utilization[ind:ind+window]))\n",
+ " \n",
+ "plt.plot(avg_utilization)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 129,
+ "id": "575f824b",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[<matplotlib.lines.Line2D at 0x7f6f872c7fa0>]"
+ ]
+ },
+ "execution_count": 129,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "sum_util = []\n",
+ "\n",
+ "last_util = 0\n",
+ "for util in utilization:\n",
+ " sum_util.append(util + last_util)\n",
+ " last_util = sum_util[-1]\n",
+ " \n",
+ "plt.plot(sum_util)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d99dfce2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "output_file = \"../Python_scripts/meta_small.parquet\"\n",
+ "output_file_path = Path(output_file)\n",
+ "\n",
+ "df_meta_new.to_parquet(output_file_path, index=False)\n",
+ "\n",
+ "output_file = \"../Python_scripts/trace_small.parquet\"\n",
+ "output_file_path = Path(output_file)\n",
+ "df_trace_new.to_parquet(output_file_path, index=False)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/opendc-experiments/opendc-experiments-scenario/src/main/kotlin/org/opendc/experiments/scenario/ScenarioCli.kt b/opendc-experiments/opendc-experiments-scenario/src/main/kotlin/org/opendc/experiments/scenario/ScenarioCli.kt
new file mode 100644
index 00000000..b2603375
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/kotlin/org/opendc/experiments/scenario/ScenarioCli.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+@file:JvmName("ScenarioCli")
+
+package org.opendc.experiments.scenario
+
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.parameters.options.default
+import com.github.ajalt.clikt.parameters.options.defaultLazy
+import com.github.ajalt.clikt.parameters.options.option
+import com.github.ajalt.clikt.parameters.types.file
+import com.github.ajalt.clikt.parameters.types.int
+import org.opendc.experiments.base.models.scenario.getScenario
+import org.opendc.experiments.base.runner.runScenario
+import java.io.File
+
+/**
+ * Main entrypoint of the application.
+ */
+public fun main(args: Array<String>): Unit = ScenarioCommand().main(args)
+
+/**
+ * Represents the command for the Scenario experiments.
+ */
+internal class ScenarioCommand : CliktCommand(name = "scenario") {
+ /**
+ * The path to the environment directory.
+ */
+ private val scenarioPath by option("--scenario-path", help = "path to scenario file")
+ .file(canBeDir = true, canBeFile = false)
+ .defaultLazy { File("resources/scenario.json") }
+
+ /**
+ * The number of threads to use for parallelism.
+ */
+ private val parallelism by option("-p", "--parallelism", help = "number of worker threads")
+ .int()
+ .default(Runtime.getRuntime().availableProcessors() - 1)
+
+ override fun run() {
+ val scenario = getScenario(scenarioPath)
+ runScenario(scenario, parallelism)
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/interference-model.json b/opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/interference-model.json
index 51fc6366..51fc6366 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/interference-model.json
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/interference-model.json
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/meta.parquet b/opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/trace/meta.parquet
index 9cded35f..9cded35f 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/meta.parquet
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/trace/meta.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/trace.parquet b/opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/trace/trace.parquet
index 9d953956..9d953956 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/trace.parquet
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/resources/bitbrains-small/trace/trace.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/topology.json b/opendc-experiments/opendc-experiments-scenario/src/main/resources/env/multi.json
index 721005b0..721005b0 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/topology.json
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/resources/env/multi.json
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/single.json b/opendc-experiments/opendc-experiments-scenario/src/main/resources/env/single.json
index a1c8d95a..a1c8d95a 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/single.json
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/resources/env/single.json
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/log4j2.xml b/opendc-experiments/opendc-experiments-scenario/src/main/resources/log4j2.xml
index e479f2ca..e479f2ca 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/log4j2.xml
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/resources/log4j2.xml
diff --git a/opendc-experiments/opendc-experiments-scenario/src/main/resources/scenario.json b/opendc-experiments/opendc-experiments-scenario/src/main/resources/scenario.json
new file mode 100644
index 00000000..854d9b8f
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-scenario/src/main/resources/scenario.json
@@ -0,0 +1,13 @@
+{
+ "runs": 5,
+ "topology": {
+ "pathToFile": "resources/env/single.json"
+ },
+ "workload": {
+ "pathToFile": "resources/bitbrains-small",
+ "type": "ComputeWorkload"
+ },
+ "allocationPolicy": {
+ "policyType": "Mem"
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/resources/env/single.txt b/opendc-experiments/opendc-experiments-scenario/src/test/resources/env/single.txt
index 5642003d..5642003d 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/main/resources/env/single.txt
+++ b/opendc-experiments/opendc-experiments-scenario/src/test/resources/env/single.txt
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/resources/env/multi.txt b/opendc-experiments/opendc-experiments-scenario/src/test/resources/env/topology.txt
index 6b347bff..6b347bff 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/main/resources/env/multi.txt
+++ b/opendc-experiments/opendc-experiments-scenario/src/test/resources/env/topology.txt
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/interference-model.json b/opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/interference-model.json
index 51fc6366..51fc6366 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/interference-model.json
+++ b/opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/interference-model.json
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/meta.parquet b/opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/meta.parquet
index 9cded35f..9cded35f 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/meta.parquet
+++ b/opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/meta.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/trace.parquet b/opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/trace.parquet
index 9d953956..9d953956 100644
--- a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/trace.parquet
+++ b/opendc-experiments/opendc-experiments-scenario/src/test/resources/trace/bitbrains-small/trace.parquet
Binary files differ