summaryrefslogtreecommitdiff
path: root/opendc-experiments/opendc-experiments-base/src/main/kotlin
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-experiments/opendc-experiments-base/src/main/kotlin')
-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.kt57
-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
10 files changed, 490 insertions, 58 deletions
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-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioFactories.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioFactories.kt
new file mode 100644
index 00000000..d806e95e
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/models/scenario/ScenarioFactories.kt
@@ -0,0 +1,57 @@
+/*
+ * 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 org.opendc.compute.simulator.failure.getFailureModel
+import org.opendc.compute.topology.clusterTopology
+import java.io.File
+
+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)
+ }
+ }