summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorgios Andreadis <g.andreadis@student.tudelft.nl>2020-03-11 12:16:10 +0100
committerGeorgios Andreadis <g.andreadis@student.tudelft.nl>2020-03-11 12:16:10 +0100
commit1ccfcb28bb91c9dc456a1f324a0be6300086eb28 (patch)
treeb03bbf6dc69cc2d2c7fcfda1ad0b081c91443312
parent2dd2bfe87bcbe368f46ce8e975ccccbfac2c0560 (diff)
parent1200816b7bf5c76b6bbb91b7e4555e9f04ea1af9 (diff)
Merge branch 'feat/2.x-power-model' into '2.x'
Implement basic power usage model See merge request opendc/opendc-simulator!36
-rw-r--r--buildSrc/build.gradle.kts6
-rw-r--r--buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts1
-rw-r--r--buildSrc/src/main/kotlin/library.kt8
-rw-r--r--odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/signal/Signal.kt90
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt18
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt41
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt45
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt8
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt2
-rw-r--r--opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt2
-rw-r--r--opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt4
-rw-r--r--opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt4
-rw-r--r--opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/PowerModel.kt32
-rw-r--r--opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt37
-rw-r--r--opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt15
-rw-r--r--opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt10
-rw-r--r--opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt3
-rw-r--r--opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt13
18 files changed, 307 insertions, 32 deletions
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index c56e7e95..6ed95c71 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -40,7 +40,7 @@ repositories {
}
dependencies {
- implementation(kotlin("gradle-plugin", "1.3.61"))
- implementation("org.jlleitschuh.gradle:ktlint-gradle:9.1.1")
- implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.10.0")
+ implementation(kotlin("gradle-plugin", version = "1.3.70"))
+ implementation("org.jlleitschuh.gradle:ktlint-gradle:9.2.1")
+ implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.10.1")
}
diff --git a/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts b/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts
index 8b71ad91..c0c37a09 100644
--- a/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts
+++ b/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts
@@ -42,6 +42,7 @@ java {
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = "1.8"
+ kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
tasks.test {
diff --git a/buildSrc/src/main/kotlin/library.kt b/buildSrc/src/main/kotlin/library.kt
index 9532122d..6333e351 100644
--- a/buildSrc/src/main/kotlin/library.kt
+++ b/buildSrc/src/main/kotlin/library.kt
@@ -30,20 +30,20 @@ object Library {
/**
* The library for testing the projects.
*/
- val JUNIT_JUPITER = "5.5.2"
+ val JUNIT_JUPITER = "5.6.0"
/**
* The library for hosting the tests.
*/
- val JUNIT_PLATFORM = "1.5.2"
+ val JUNIT_PLATFORM = "1.6.0"
/**
* Logging facade.
*/
- val SLF4J = "1.7.29"
+ val SLF4J = "1.7.30"
/**
* Kotlin coroutines support
*/
- val KOTLINX_COROUTINES = "1.3.3"
+ val KOTLINX_COROUTINES = "1.3.4"
}
diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/signal/Signal.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/signal/Signal.kt
new file mode 100644
index 00000000..da6298a3
--- /dev/null
+++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/signal/Signal.kt
@@ -0,0 +1,90 @@
+/*
+ * MIT License
+ *
+ * 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 com.atlarge.odcsim.signal
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.channels.BroadcastChannel
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.asFlow
+
+/**
+ * A [Flow] that contains a single value that changes over time.
+ *
+ * This class exists to implement the DataFlow/StateFlow functionality that will be implemented in `kotlinx-coroutines`
+ * in the future, but is not available yet.
+ * See: https://github.com/Kotlin/kotlinx.coroutines/pull/1354
+ */
+public interface Signal<T> : Flow<T> {
+ /**
+ * The current value of this signal.
+ *
+ * Setting a value that is [equal][Any.equals] to the previous one does nothing.
+ */
+ public var value: T
+}
+
+/**
+ * Creates a [Signal] with a given initial [value].
+ */
+@Suppress("FunctionName")
+public fun <T> Signal(value: T): Signal<T> = SignalImpl(value)
+
+/**
+ * Internal implementation of the [Signal] interface.
+ */
+private class SignalImpl<T>(initialValue: T) : Signal<T> {
+ /**
+ * The [BroadcastChannel] to back this signal.
+ */
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private val chan = BroadcastChannel<T>(Channel.CONFLATED)
+
+ /**
+ * The internal [Flow] backing this signal.
+ */
+ @OptIn(FlowPreview::class)
+ private val flow = chan.asFlow()
+
+ init {
+ @OptIn(ExperimentalCoroutinesApi::class)
+ chan.offer(initialValue)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ public override var value: T = initialValue
+ set(value) {
+ if (field != value) {
+ chan.offer(value)
+ field = value
+ }
+ }
+
+ @InternalCoroutinesApi
+ override suspend fun collect(collector: FlowCollector<T>) = flow.collect(collector)
+}
diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt
index 1330158e..1214dd36 100644
--- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt
+++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt
@@ -24,15 +24,26 @@
package com.atlarge.opendc.compute.metal.driver
+import com.atlarge.opendc.compute.core.Server
import com.atlarge.opendc.compute.core.image.Image
import com.atlarge.opendc.compute.core.monitor.ServerMonitor
import com.atlarge.opendc.compute.metal.Node
import com.atlarge.opendc.compute.metal.PowerState
+import com.atlarge.opendc.core.power.Powerable
+import com.atlarge.opendc.core.services.AbstractServiceKey
+import kotlinx.coroutines.flow.Flow
+import java.util.UUID
/**
* A driver interface for the management interface of a bare-metal compute node.
*/
-public interface BareMetalDriver {
+public interface BareMetalDriver : Powerable {
+ /**
+ * The amount of work done by the machine in percentage with respect to the total amount of processing power
+ * available.
+ */
+ public val usage: Flow<Double>
+
/**
* Initialize the driver.
*/
@@ -55,4 +66,9 @@ public interface BareMetalDriver {
* Obtain the state of the compute node.
*/
public suspend fun refresh(): Node
+
+ /**
+ * A key that allows access to the [BareMetalDriver] instance from a [Server] that runs on the bare-metal machine.
+ */
+ companion object Key : AbstractServiceKey<BareMetalDriver>(UUID.randomUUID(), "bare-metal:driver")
}
diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt
index fcdc9363..cd3e9a48 100644
--- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt
+++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt
@@ -25,6 +25,7 @@
package com.atlarge.opendc.compute.metal.driver
import com.atlarge.odcsim.Domain
+import com.atlarge.odcsim.signal.Signal
import com.atlarge.odcsim.simulationContext
import com.atlarge.opendc.compute.core.ProcessingUnit
import com.atlarge.opendc.compute.core.Server
@@ -37,9 +38,12 @@ import com.atlarge.opendc.compute.core.image.Image
import com.atlarge.opendc.compute.core.monitor.ServerMonitor
import com.atlarge.opendc.compute.metal.Node
import com.atlarge.opendc.compute.metal.PowerState
+import com.atlarge.opendc.compute.metal.power.ConstantPowerModel
+import com.atlarge.opendc.core.power.PowerModel
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import java.util.UUID
import kotlin.math.ceil
@@ -50,18 +54,20 @@ import kotlinx.coroutines.withContext
/**
* A basic implementation of the [BareMetalDriver] that simulates an [Image] running on a bare-metal machine.
*
+ * @param domain The simulation domain the driver runs in.
* @param uid The unique identifier of the machine.
* @param name An optional name of the machine.
* @param cpus The CPUs available to the bare metal machine.
* @param memoryUnits The memory units in this machine.
- * @param domain The simulation domain the driver runs in.
+ * @param powerModel The power model of this machine.
*/
public class SimpleBareMetalDriver(
+ private val domain: Domain,
uid: UUID,
name: String,
val cpus: List<ProcessingUnit>,
val memoryUnits: List<MemoryUnit>,
- private val domain: Domain
+ powerModel: PowerModel<SimpleBareMetalDriver> = ConstantPowerModel(0.0)
) : BareMetalDriver {
/**
* The monitor to use.
@@ -83,6 +89,15 @@ public class SimpleBareMetalDriver(
*/
private var job: Job? = null
+ /**
+ * The signal containing the load of the server.
+ */
+ private val usageSignal = Signal(0.0)
+
+ override val usage: Flow<Double> = usageSignal
+
+ override val powerDraw: Flow<Double> = powerModel(this)
+
override suspend fun init(monitor: ServerMonitor): Node = withContext(domain.coroutineContext) {
this@SimpleBareMetalDriver.monitor = monitor
return@withContext node
@@ -104,6 +119,7 @@ public class SimpleBareMetalDriver(
PowerState.POWER_ON to PowerState.POWER_ON -> node.server
else -> throw IllegalStateException()
}
+ server?.serviceRegistry?.set(BareMetalDriver.Key, this@SimpleBareMetalDriver)
node = node.copy(powerState = powerState, server = server)
if (powerState != previousPowerState && server != null) {
@@ -167,11 +183,18 @@ public class SimpleBareMetalDriver(
domain.launch { monitor.onUpdate(server, previousState) }
}
+ private var flush: Job? = null
+
override suspend fun run(burst: LongArray, limit: DoubleArray, deadline: Long) {
require(burst.size == limit.size) { "Array dimensions do not match" }
+ // If run is called in at the same timestamp as the previous call, cancel the load flush
+ flush?.cancel()
+ flush = null
+
val start = simulationContext.clock.millis()
var duration = max(0, deadline - start)
+ var totalUsage = 0.0
// Determine the duration of the first CPU to finish
for (i in 0 until min(cpus.size, burst.size)) {
@@ -179,19 +202,31 @@ public class SimpleBareMetalDriver(
val usage = min(limit[i], cpu.frequency) * 1_000_000 // Usage from MHz to Hz
val cpuDuration = ceil(burst[i] / usage * 1000).toLong() // Convert from seconds to milliseconds
+ totalUsage += usage / (cpu.frequency * 1_000_000)
+
if (cpuDuration != 0L) { // We only wait for processor cores with a non-zero burst
duration = min(duration, cpuDuration)
}
}
+ usageSignal.value = totalUsage / cpus.size
+
try {
delay(duration)
} catch (_: CancellationException) {
// On cancellation, we compute and return the remaining burst
}
-
val end = simulationContext.clock.millis()
+ // Flush the load if the do not receive a new run call for the same timestamp
+ flush = domain.launch {
+ delay(1)
+ usageSignal.value = 0.0
+ }
+ flush!!.invokeOnCompletion {
+ flush = null
+ }
+
// Write back the remaining burst time
for (i in 0 until min(cpus.size, burst.size)) {
val usage = min(limit[i], cpus[i].frequency) * 1_000_000
diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt
new file mode 100644
index 00000000..9ddbe08e
--- /dev/null
+++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt
@@ -0,0 +1,45 @@
+/*
+ * MIT License
+ *
+ * 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 com.atlarge.opendc.compute.metal.power
+
+import com.atlarge.opendc.compute.metal.driver.BareMetalDriver
+import com.atlarge.opendc.core.power.PowerModel
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/**
+ * A power model which emits a single value.
+ */
+public fun ConstantPowerModel(value: Double): PowerModel<BareMetalDriver> = { _ -> flowOf(value) }
+
+/**
+ * A power model that assumes a naive linear relation between power usage and host CPU utilization.
+ *
+ * @param idle The power draw in Watts on idle.
+ * @param max The maximum power draw in Watts of the server.
+ */
+public fun LinearLoadPowerModel(idle: Double, max: Double): PowerModel<BareMetalDriver> = { driver ->
+ driver.usage.map { load -> (max - idle) * load + idle }
+}
diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt
index 6fe11c28..05e1ab90 100644
--- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt
+++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt
@@ -155,10 +155,10 @@ class HypervisorVirtDriver(
}
}
- val granted = burst.clone()
+ val remainder = burst.clone()
// We run the total burst on the host processor. Note that this call may be cancelled at any moment in
// time, so not all of the burst may be executed.
- hostContext.run(granted, usage, deadline)
+ hostContext.run(remainder, usage, deadline)
val end = simulationContext.clock.millis()
// No work was performed
@@ -178,7 +178,7 @@ class HypervisorVirtDriver(
val fraction = actualUsage / usage[i]
// Compute the burst time that the VM was actually granted
- val grantedBurst = max(0, actualBurst - ceil(burst[i] * fraction).toLong())
+ val grantedBurst = max(0, actualBurst - ceil(remainder[i] * fraction).toLong())
// Compute remaining burst time to be executed for the request
vm.requestedBurst[i] = max(0, vm.requestedBurst[i] - grantedBurst)
@@ -194,7 +194,7 @@ class HypervisorVirtDriver(
monitor.onSliceFinish(
end,
burst[i],
- granted[i],
+ remainder[i],
vms.size,
hostContext.server
)
diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt
index e259a3c0..1e3981f6 100644
--- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt
+++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt
@@ -15,7 +15,7 @@ interface HypervisorMonitor {
* @param numberOfDeployedImages The number of images deployed on this hypervisor.
* @param hostServer The server hosting this hypervisor.
*/
- fun onSliceFinish(
+ suspend fun onSliceFinish(
time: Long,
requestedBurst: Long,
grantedBurst: Long,
diff --git a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt
index 84b16b68..f3796028 100644
--- a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt
+++ b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt
@@ -55,7 +55,7 @@ internal class SimpleBareMetalDriverTest {
val dom = root.newDomain(name = "driver")
val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4)
val cpus = List(4) { ProcessingUnit(cpuNode, it, 2400.0) }
- val driver = SimpleBareMetalDriver(UUID.randomUUID(), "test", cpus, emptyList(), dom)
+ val driver = SimpleBareMetalDriver(dom, UUID.randomUUID(), "test", cpus, emptyList())
val monitor = object : ServerMonitor {
override suspend fun onUpdate(server: Server, previousState: ServerState) {
diff --git a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt
index d5366552..a837130d 100644
--- a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt
+++ b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt
@@ -62,8 +62,8 @@ internal class SimpleProvisioningServiceTest {
val dom = root.newDomain("provisioner")
val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4)
- val cpus = List(5) { ProcessingUnit(cpuNode, it, 2400.0) }
- val driver = SimpleBareMetalDriver(UUID.randomUUID(), "test", cpus, emptyList(), dom)
+ val cpus = List(4) { ProcessingUnit(cpuNode, it, 2400.0) }
+ val driver = SimpleBareMetalDriver(dom, UUID.randomUUID(), "test", cpus, emptyList())
val provisioner = SimpleProvisioningService(dom)
provisioner.create(driver)
diff --git a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt
index 6cfb2317..2a841711 100644
--- a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt
+++ b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt
@@ -59,7 +59,7 @@ internal class HypervisorTest {
root.launch {
val vmm = HypervisorImage(object : HypervisorMonitor {
- override fun onSliceFinish(
+ override suspend fun onSliceFinish(
time: Long,
requestedBurst: Long,
grantedBurst: Long,
@@ -81,7 +81,7 @@ internal class HypervisorTest {
val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4)
val cpus = List(4) { ProcessingUnit(cpuNode, it, 2000.0) }
- val metalDriver = SimpleBareMetalDriver(UUID.randomUUID(), "test", cpus, emptyList(), driverDom)
+ val metalDriver = SimpleBareMetalDriver(driverDom, UUID.randomUUID(), "test", cpus, emptyList())
metalDriver.init(monitor)
metalDriver.setImage(vmm)
diff --git a/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/PowerModel.kt b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/PowerModel.kt
new file mode 100644
index 00000000..51c9f379
--- /dev/null
+++ b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/PowerModel.kt
@@ -0,0 +1,32 @@
+/*
+ * MIT License
+ *
+ * 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 com.atlarge.opendc.core.power
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * A model for computing the power draw based on some value.
+ */
+public typealias PowerModel<T> = (T) -> Flow<Double>
diff --git a/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt
new file mode 100644
index 00000000..4473a571
--- /dev/null
+++ b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt
@@ -0,0 +1,37 @@
+/*
+ * MIT License
+ *
+ * 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 com.atlarge.opendc.core.power
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * An entity that is uses power from some power source.
+ */
+public interface Powerable {
+ /**
+ * The power draw at the device's power supply in watts (W).w
+ */
+ val powerDraw: Flow<Double>
+}
diff --git a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt
index e095d300..55a0ce75 100644
--- a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt
+++ b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt
@@ -1,7 +1,9 @@
package com.atlarge.opendc.experiments.sc20
import com.atlarge.opendc.compute.core.Server
+import com.atlarge.opendc.compute.metal.driver.BareMetalDriver
import com.atlarge.opendc.compute.virt.monitor.HypervisorMonitor
+import kotlinx.coroutines.flow.first
import java.io.BufferedWriter
import java.io.Closeable
import java.io.FileWriter
@@ -10,20 +12,27 @@ class Sc20HypervisorMonitor : HypervisorMonitor, Closeable {
private val outputFile = BufferedWriter(FileWriter("sc20-experiment-results.csv"))
init {
- outputFile.write("time,requestedBurst,grantedBurst,numberOfDeployedImages,server\n")
+ outputFile.write("time,requestedBurst,grantedBurst,numberOfDeployedImages,server,hostUsage,powerDraw\n")
}
- override fun onSliceFinish(
+ override suspend fun onSliceFinish(
time: Long,
requestedBurst: Long,
grantedBurst: Long,
numberOfDeployedImages: Int,
hostServer: Server
) {
- outputFile.write("$time,$requestedBurst,$grantedBurst,$numberOfDeployedImages,$numberOfDeployedImages,${hostServer.uid}\n")
+ // Assume for now that the host is not virtualized and measure the current power draw
+ val driver = hostServer.serviceRegistry[BareMetalDriver.Key]
+ val usage = driver.usage.first()
+ val powerDraw = driver.powerDraw.first()
+
+ outputFile.write("$time,$requestedBurst,$grantedBurst,$numberOfDeployedImages,$numberOfDeployedImages,${hostServer.uid},$usage,$powerDraw")
+ outputFile.newLine()
}
override fun close() {
+ outputFile.flush()
outputFile.close()
}
}
diff --git a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt
index f4be75fa..76f7b600 100644
--- a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt
+++ b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt
@@ -36,7 +36,6 @@ import com.atlarge.opendc.compute.virt.service.allocation.AvailableMemoryAllocat
import com.atlarge.opendc.format.environment.sc20.Sc20EnvironmentReader
import com.atlarge.opendc.format.trace.sc20.Sc20PerformanceInterferenceReader
import com.atlarge.opendc.format.trace.vm.VmTraceReader
-import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -52,8 +51,8 @@ fun main(args: Array<String>) {
println("error: Please provide path to directory containing VM trace files")
return
}
- val token = Channel<Boolean>()
+ val hypervisorMonitor = Sc20HypervisorMonitor()
val monitor = object : ServerMonitor {
override suspend fun onUpdate(server: Server, previousState: ServerState) {
println(server)
@@ -78,7 +77,7 @@ fun main(args: Array<String>) {
AvailableMemoryAllocationPolicy(),
simulationContext,
environment.platforms[0].zones[0].services[ProvisioningService.Key],
- Sc20HypervisorMonitor()
+ hypervisorMonitor
)
val reader = VmTraceReader(File(args[0]), performanceInterferenceModel)
@@ -89,8 +88,6 @@ fun main(args: Array<String>) {
scheduler.deploy(workload.image, monitor, Flavor(workload.image.cores, workload.image.requiredMemory))
}
- token.receive()
-
println(simulationContext.clock.instant())
}
@@ -98,4 +95,7 @@ fun main(args: Array<String>) {
system.run()
system.terminate()
}
+
+ // Explicitly close the monitor to flush its buffer
+ hypervisorMonitor.close()
}
diff --git a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt
index 942a8c72..0d4bd125 100644
--- a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt
+++ b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt
@@ -77,8 +77,7 @@ class Sc18EnvironmentReader(input: InputStream, mapper: ObjectMapper = jacksonOb
else -> throw IllegalArgumentException("The cpu id $id is not recognized")
}
}
- SimpleBareMetalDriver(UUID.randomUUID(), "node-${counter++}", cores, listOf(MemoryUnit("", "", 2300.0, 16000)),
- dom.newDomain("node-$counter"))
+ SimpleBareMetalDriver(dom.newDomain("node-$counter"), UUID.randomUUID(), "node-${counter++}", cores, listOf(MemoryUnit("", "", 2300.0, 16000)))
}
}
}
diff --git a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt
index b33a5e93..a954a308 100644
--- a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt
+++ b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt
@@ -29,6 +29,7 @@ import com.atlarge.opendc.compute.core.MemoryUnit
import com.atlarge.opendc.compute.core.ProcessingNode
import com.atlarge.opendc.compute.core.ProcessingUnit
import com.atlarge.opendc.compute.metal.driver.SimpleBareMetalDriver
+import com.atlarge.opendc.compute.metal.power.LinearLoadPowerModel
import com.atlarge.opendc.compute.metal.service.ProvisioningService
import com.atlarge.opendc.compute.metal.service.SimpleProvisioningService
import com.atlarge.opendc.core.Environment
@@ -80,7 +81,17 @@ class Sc20EnvironmentReader(input: InputStream, mapper: ObjectMapper = jacksonOb
else -> throw IllegalArgumentException("The cpu id $id is not recognized")
}
}
- SimpleBareMetalDriver(UUID.randomUUID(), "node-${counter++}", cores, memories, dom.newDomain("node-$counter"))
+ SimpleBareMetalDriver(
+ dom.newDomain("node-$counter"),
+ UUID.randomUUID(),
+ "node-${counter++}",
+ cores,
+ memories,
+ // For now we assume a simple linear load model with an idle draw of ~200W and a maximum
+ // power draw of 350W.
+ // Source: https://stackoverflow.com/questions/6128960
+ LinearLoadPowerModel(200.0, 350.0)
+ )
}
}
}