diff options
25 files changed, 317 insertions, 87 deletions
diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/ServerFlavor.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/Flavor.kt index d57dadf9..ff5060de 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/ServerFlavor.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/Flavor.kt @@ -25,18 +25,17 @@ package com.atlarge.opendc.compute.core /** - * (Virtual) hardware configuration of a server. + * Flavors define the compute and memory capacity of [Server] instance. o put it simply, a flavor is an available + * hardware configuration for a server. It defines the size of a virtual server that can be launched. */ -public data class ServerFlavor( +public data class Flavor( /** - * The processing units of this machine. + * The number of (virtual) processing cores to use. */ - public val cpus: List<ProcessingUnit>, + public val cpuCount: Int, /** - * Key and value pairs that can be used to describe the specification of the server which is more than just about - * CPU, disk and RAM. For example, it can be used to indicate that the server created by this flavor has PCI - * devices, etc. + * The amount of RAM available to the server (in MB). */ - public val details: Map<String, Any> = emptyMap() + public val memorySize: Long ) diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/MemoryUnit.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/MemoryUnit.kt new file mode 100644 index 00000000..ce57fc72 --- /dev/null +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/MemoryUnit.kt @@ -0,0 +1,40 @@ +/* + * 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.core + +/** + * A memory unit of a compute resource, either virtual or physical. + * + * @property vendor The vendor string of the memory. + * @property modelName The name of the memory model. + * @property speed The access speed of the memory in MHz. + * @property size The size of the memory unit in MBs. + */ +public data class MemoryUnit( + public val vendor: String, + public val modelName: String, + public val speed: Double, + public val size: Long +) diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/Server.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/Server.kt index cb11cfc7..86ec9a5b 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/Server.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/Server.kt @@ -53,7 +53,7 @@ public data class Server( /** * The hardware configuration of the server. */ - public val flavor: ServerFlavor, + public val flavor: Flavor, /** * The image running on the server. diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/FlopsApplicationImage.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/FlopsApplicationImage.kt index f09adc84..3576b488 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/FlopsApplicationImage.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/FlopsApplicationImage.kt @@ -60,7 +60,7 @@ class FlopsApplicationImage( * Execute the runtime behavior based on a number of floating point operations to execute. */ override suspend fun invoke(ctx: ServerContext) { - val cores = min(this.cores, ctx.server.flavor.cpus.sumBy { it.cores }) + val cores = min(this.cores, ctx.server.flavor.cpuCount) val req = flops / cores coroutineScope { diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/VmImage.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/VmImage.kt index b7eacd88..257b6149 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/VmImage.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/VmImage.kt @@ -13,7 +13,8 @@ class VmImage( public override val name: String, public override val tags: TagContainer, public val flopsHistory: List<FlopsHistoryFragment>, - public val cores: Int + public val cores: Int, + public val requiredMemory: Long ) : Image { override suspend fun invoke(ctx: ServerContext) { @@ -21,7 +22,7 @@ class VmImage( if (fragment.flops == 0L) { delay(fragment.duration) } else { - val cores = min(this.cores, ctx.server.flavor.cpus.sumBy { it.cores }) + val cores = min(this.cores, ctx.server.flavor.cpuCount) val req = fragment.flops / cores coroutineScope { 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 29573007..202c30e9 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 @@ -28,7 +28,8 @@ import com.atlarge.odcsim.ProcessContext import com.atlarge.odcsim.processContext import com.atlarge.opendc.compute.core.ProcessingUnit import com.atlarge.opendc.compute.core.Server -import com.atlarge.opendc.compute.core.ServerFlavor +import com.atlarge.opendc.compute.core.Flavor +import com.atlarge.opendc.compute.core.MemoryUnit import com.atlarge.opendc.compute.core.ServerState import com.atlarge.opendc.compute.core.execution.ProcessorContext import com.atlarge.opendc.compute.core.execution.ServerManagementContext @@ -49,12 +50,13 @@ import kotlin.math.min * * @param uid The unique identifier of the machine. * @param name An optional name of the machine. - * @param flavor The hardware configuration of the machine. + * @param cpuNodes The CPU nodes/packages available to the bare metal machine. */ public class SimpleBareMetalDriver( uid: UUID, name: String, - private val flavor: ServerFlavor + val cpuNodes: List<ProcessingUnit>, + val memoryUnits: List<MemoryUnit> ) : BareMetalDriver { /** * The monitor to use. @@ -66,6 +68,11 @@ public class SimpleBareMetalDriver( */ private var node: Node = Node(uid, name, PowerState.POWER_OFF, EmptyImage, null) + /** + * The flavor that corresponds to this machine. + */ + private val flavor = Flavor(cpuNodes.sumBy { it.cores }, memoryUnits.map { it.size }.sum()) + override suspend fun init(monitor: ServerMonitor): Node { this.monitor = monitor return node @@ -141,7 +148,7 @@ public class SimpleBareMetalDriver( private var initialized: Boolean = false private lateinit var ctx: ProcessContext - override val cpus: List<ProcessorContextImpl> = flavor.cpus + override val cpus: List<ProcessorContextImpl> = cpuNodes .asSequence() .flatMap { cpu -> generateSequence { ProcessorContextImpl(cpu) }.take(cpu.cores) diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/VirtDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/VirtDriver.kt index 3541b414..68b8e541 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/VirtDriver.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/VirtDriver.kt @@ -25,7 +25,7 @@ package com.atlarge.opendc.compute.virt.driver import com.atlarge.opendc.compute.core.Server -import com.atlarge.opendc.compute.core.ServerFlavor +import com.atlarge.opendc.compute.core.Flavor import com.atlarge.opendc.compute.core.image.Image import com.atlarge.opendc.compute.core.monitor.ServerMonitor import com.atlarge.opendc.core.services.AbstractServiceKey @@ -44,7 +44,7 @@ public interface VirtDriver { * @param flavor The flavor of the server which this driver is controlling. * @return The virtual server spawned by this method. */ - public suspend fun spawn(image: Image, monitor: ServerMonitor, flavor: ServerFlavor): Server + public suspend fun spawn(image: Image, monitor: ServerMonitor, flavor: Flavor): Server /** * Returns the number of spawned images on the server managed by this driver. diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorImage.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorImage.kt index fc7322db..b7848cf3 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorImage.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorImage.kt @@ -43,7 +43,7 @@ class HypervisorImage( override val tags: TagContainer = emptyMap() override suspend fun invoke(ctx: ServerContext) { - val driver = HypervisorVirtDriver(VmSchedulerImpl(ctx, hypervisorMonitor)) + val driver = HypervisorVirtDriver(ctx, VmSchedulerImpl(ctx, hypervisorMonitor)) ctx.publishService(VirtDriver.Key, driver) 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 9ae71f14..65ec75a2 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 @@ -27,9 +27,10 @@ package com.atlarge.opendc.compute.virt.driver.hypervisor import com.atlarge.odcsim.ProcessContext import com.atlarge.odcsim.processContext import com.atlarge.opendc.compute.core.Server -import com.atlarge.opendc.compute.core.ServerFlavor +import com.atlarge.opendc.compute.core.Flavor import com.atlarge.opendc.compute.core.ServerState import com.atlarge.opendc.compute.core.execution.ProcessorContext +import com.atlarge.opendc.compute.core.execution.ServerContext import com.atlarge.opendc.compute.core.execution.ServerManagementContext import com.atlarge.opendc.compute.core.image.Image import com.atlarge.opendc.compute.core.monitor.ServerMonitor @@ -41,15 +42,29 @@ import java.util.UUID /** * A [VirtDriver] that is backed by a simple hypervisor implementation. */ -class HypervisorVirtDriver(private val scheduler: VmScheduler) : VirtDriver { +class HypervisorVirtDriver( + private val hostContext: ServerContext, + private val scheduler: VmScheduler +) : VirtDriver { /** * A set for tracking the VM context objects. */ internal val vms: MutableSet<VmServerContext> = mutableSetOf() - override suspend fun spawn(image: Image, monitor: ServerMonitor, flavor: ServerFlavor): Server { + /** + * Current total memory use of the images on this hypervisor. + */ + private var memoryAvailable: Long = hostContext.server.flavor.memorySize + + override suspend fun spawn(image: Image, monitor: ServerMonitor, flavor: Flavor): Server { + val requiredMemory = flavor.memorySize + if (memoryAvailable - requiredMemory < 0) { + throw InsufficientMemoryOnServerException() + } + val server = Server(UUID.randomUUID(), "<unnamed>", emptyMap(), flavor, image, ServerState.BUILD) - vms.add(VmServerContext(server, monitor, flavor, processContext)) + memoryAvailable -= requiredMemory + vms.add(VmServerContext(server, monitor, processContext)) return server } @@ -60,7 +75,6 @@ class HypervisorVirtDriver(private val scheduler: VmScheduler) : VirtDriver { internal inner class VmServerContext( override var server: Server, val monitor: ServerMonitor, - flavor: ServerFlavor, ctx: ProcessContext ) : ServerManagementContext { private var initialized: Boolean = false @@ -75,7 +89,7 @@ class HypervisorVirtDriver(private val scheduler: VmScheduler) : VirtDriver { } } - override val cpus: List<ProcessorContext> = scheduler.createVirtualCpus(flavor) + override val cpus: List<ProcessorContext> = scheduler.createVirtualCpus(server.flavor) override suspend fun init() { if (initialized) { @@ -92,6 +106,7 @@ class HypervisorVirtDriver(private val scheduler: VmScheduler) : VirtDriver { val previousState = server.state val state = if (cause == null) ServerState.SHUTOFF else ServerState.ERROR server = server.copy(state = state) + memoryAvailable += server.flavor.memorySize monitor.onUpdate(server, previousState) initialized = false } diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/InsufficientMemoryOnServerException.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/InsufficientMemoryOnServerException.kt new file mode 100644 index 00000000..926234b5 --- /dev/null +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/InsufficientMemoryOnServerException.kt @@ -0,0 +1,3 @@ +package com.atlarge.opendc.compute.virt.driver.hypervisor + +public class InsufficientMemoryOnServerException : IllegalStateException("Insufficient memory left on server.") diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/VmScheduler.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/VmScheduler.kt index f02ac2b3..7b00d99c 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/VmScheduler.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/VmScheduler.kt @@ -24,7 +24,7 @@ package com.atlarge.opendc.compute.virt.driver.hypervisor -import com.atlarge.opendc.compute.core.ServerFlavor +import com.atlarge.opendc.compute.core.Flavor import com.atlarge.opendc.compute.core.execution.ProcessorContext /** @@ -34,5 +34,5 @@ public interface VmScheduler { /** * Create the virtual CPUs for the specified [flavor]. */ - fun createVirtualCpus(flavor: ServerFlavor): List<ProcessorContext> + fun createVirtualCpus(flavor: Flavor): List<ProcessorContext> } diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/VmSchedulerImpl.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/VmSchedulerImpl.kt index b6be935e..dfed3d58 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/VmSchedulerImpl.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/VmSchedulerImpl.kt @@ -26,7 +26,7 @@ package com.atlarge.opendc.compute.virt.driver.hypervisor import com.atlarge.odcsim.processContext import com.atlarge.opendc.compute.core.ProcessingUnit -import com.atlarge.opendc.compute.core.ServerFlavor +import com.atlarge.opendc.compute.core.Flavor import com.atlarge.opendc.compute.core.execution.ProcessorContext import com.atlarge.opendc.compute.core.execution.ServerContext import com.atlarge.opendc.compute.virt.monitor.HypervisorMonitor @@ -53,9 +53,10 @@ public class VmSchedulerImpl( */ private val cpus = hostContext.cpus.map { HostProcessorContext(it, hostContext, hypervisorMonitor) } - override fun createVirtualCpus(flavor: ServerFlavor): List<ProcessorContext> { + override fun createVirtualCpus(flavor: Flavor): List<ProcessorContext> { + // TODO At the moment, the first N cores get filled the first. Distribute over all cores instead return cpus.asSequence() - .take(flavor.cpus.sumBy { it.cores }) + .take(flavor.cpuCount) .map { VirtualProcessorContext(it) } .toList() } 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 d4d71aaa..e259a3c0 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 @@ -10,15 +10,15 @@ interface HypervisorMonitor { * Invoked after a scheduling slice has finished processed. * * @param time The current time (in ms). - * @param totalRequestedBurst The total requested CPU time (can be above capacity). - * @param totalGrantedBurst The actual total granted capacity. + * @param requestedBurst The total requested CPU time (can be above capacity). + * @param grantedBurst The actual total granted capacity. * @param numberOfDeployedImages The number of images deployed on this hypervisor. * @param hostServer The server hosting this hypervisor. */ fun onSliceFinish( time: Long, - totalRequestedBurst: Long, - totalGrantedBurst: Long, + requestedBurst: Long, + grantedBurst: Long, numberOfDeployedImages: Int, hostServer: Server ) diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/service/SimpleVirtProvisioningService.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/service/SimpleVirtProvisioningService.kt index baf3f9ef..ef1528d9 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/service/SimpleVirtProvisioningService.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/service/SimpleVirtProvisioningService.kt @@ -1,14 +1,16 @@ package com.atlarge.opendc.compute.virt.service import com.atlarge.odcsim.ProcessContext +import com.atlarge.opendc.compute.core.Flavor import com.atlarge.opendc.compute.core.Server import com.atlarge.opendc.compute.core.ServerState 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.service.ProvisioningService -import com.atlarge.opendc.compute.virt.driver.hypervisor.HypervisorImage import com.atlarge.opendc.compute.virt.driver.VirtDriver +import com.atlarge.opendc.compute.virt.driver.hypervisor.HypervisorImage +import com.atlarge.opendc.compute.virt.driver.hypervisor.InsufficientMemoryOnServerException import com.atlarge.opendc.compute.virt.monitor.HypervisorMonitor import kotlinx.coroutines.launch @@ -63,8 +65,8 @@ class SimpleVirtProvisioningService( } } - override suspend fun deploy(image: Image, monitor: ServerMonitor) { - val vmInstance = ImageView(image, monitor) + override suspend fun deploy(image: Image, monitor: ServerMonitor, flavor: Flavor) { + val vmInstance = ImageView(image, monitor, flavor) incomingImages += vmInstance requestCycle() } @@ -85,16 +87,20 @@ class SimpleVirtProvisioningService( it.server!!.serviceRegistry[VirtDriver.Key].getNumberOfSpawnedImages() } - imageInstance.server = selectedNode?.server!!.serviceRegistry[VirtDriver.Key].spawn( - imageInstance.image, - imageInstance.monitor, - nodes[0].server!!.flavor - ) + try { + imageInstance.server = selectedNode?.server!!.serviceRegistry[VirtDriver.Key].spawn( + imageInstance.image, + imageInstance.monitor, + imageInstance.flavor + ) + activeImages += imageInstance + imagesByServer.putIfAbsent(imageInstance.server!!, mutableSetOf()) + imagesByServer[imageInstance.server!!]!!.add(imageInstance) + } catch (e: InsufficientMemoryOnServerException) { + println("Unable to deploy image due to insufficient memory") + } incomingImages -= imageInstance - activeImages += imageInstance - imagesByServer.putIfAbsent(imageInstance.server!!, mutableSetOf()) - imagesByServer[imageInstance.server!!]!!.add(imageInstance) } } @@ -113,6 +119,7 @@ class SimpleVirtProvisioningService( class ImageView( val image: Image, val monitor: ServerMonitor, + val flavor: Flavor, var server: Server? = null ) } diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/service/VirtProvisioningService.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/service/VirtProvisioningService.kt index 8e0e1137..fb087f9d 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/service/VirtProvisioningService.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/service/VirtProvisioningService.kt @@ -1,5 +1,6 @@ package com.atlarge.opendc.compute.virt.service +import com.atlarge.opendc.compute.core.Flavor import com.atlarge.opendc.compute.core.image.Image import com.atlarge.opendc.compute.core.monitor.ServerMonitor @@ -9,6 +10,10 @@ import com.atlarge.opendc.compute.core.monitor.ServerMonitor interface VirtProvisioningService { /** * Submit the specified [Image] to the provisioning service. + * + * @param image The image to be deployed. + * @param monitor The monitor to inform on events. + * @param flavor The flavor of the machine instance to run this [image] on. */ - public suspend fun deploy(image: Image, monitor: ServerMonitor) + public suspend fun deploy(image: Image, monitor: ServerMonitor, flavor: Flavor) } 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 05e943e2..dc4f8078 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 @@ -27,7 +27,6 @@ package com.atlarge.opendc.compute.metal.driver import com.atlarge.odcsim.SimulationEngineProvider import com.atlarge.opendc.compute.core.ProcessingUnit import com.atlarge.opendc.compute.core.Server -import com.atlarge.opendc.compute.core.ServerFlavor import com.atlarge.opendc.compute.core.ServerState import com.atlarge.opendc.compute.core.image.FlopsApplicationImage import com.atlarge.opendc.compute.core.monitor.ServerMonitor @@ -46,14 +45,13 @@ internal class SimpleBareMetalDriverTest { fun smoke() { val provider = ServiceLoader.load(SimulationEngineProvider::class.java).first() val system = provider({ _ -> - val flavor = ServerFlavor(listOf(ProcessingUnit("Intel", "Xeon", "amd64", 2300.0, 4))) val image = FlopsApplicationImage(UUID.randomUUID(), "<unnamed>", emptyMap(), 1000, 2) val monitor = object : ServerMonitor { override suspend fun onUpdate(server: Server, previousState: ServerState) { println(server) } } - val driver = SimpleBareMetalDriver(UUID.randomUUID(), "test", flavor) + val driver = SimpleBareMetalDriver(UUID.randomUUID(), "test", listOf(ProcessingUnit("Intel", "Xeon", "amd64", 2300.0, 4)), emptyList()) driver.init(monitor) driver.setImage(image) 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 a2112657..85e3383c 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 @@ -27,7 +27,6 @@ package com.atlarge.opendc.compute.metal.service import com.atlarge.odcsim.SimulationEngineProvider import com.atlarge.opendc.compute.core.ProcessingUnit import com.atlarge.opendc.compute.core.Server -import com.atlarge.opendc.compute.core.ServerFlavor import com.atlarge.opendc.compute.core.ServerState import com.atlarge.opendc.compute.core.image.FlopsApplicationImage import com.atlarge.opendc.compute.core.monitor.ServerMonitor @@ -49,14 +48,13 @@ internal class SimpleProvisioningServiceTest { fun smoke() { val provider = ServiceLoader.load(SimulationEngineProvider::class.java).first() val system = provider({ _ -> - val flavor = ServerFlavor(listOf(ProcessingUnit("Intel", "Xeon", "amd64", 2300.0, 4))) val image = FlopsApplicationImage(UUID.randomUUID(), "<unnamed>", emptyMap(), 1000, 2) val monitor = object : ServerMonitor { override suspend fun onUpdate(server: Server, previousState: ServerState) { println(server) } } - val driver = SimpleBareMetalDriver(UUID.randomUUID(), "test", flavor) + val driver = SimpleBareMetalDriver(UUID.randomUUID(), "test", listOf(ProcessingUnit("Intel", "Xeon", "amd64", 2300.0, 4)), emptyList()) val provisioner = SimpleProvisioningService() 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 ce0ed10d..f59f4830 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 @@ -28,7 +28,7 @@ import com.atlarge.odcsim.SimulationEngineProvider import com.atlarge.odcsim.processContext import com.atlarge.opendc.compute.core.ProcessingUnit import com.atlarge.opendc.compute.core.Server -import com.atlarge.opendc.compute.core.ServerFlavor +import com.atlarge.opendc.compute.core.Flavor import com.atlarge.opendc.compute.core.ServerState import com.atlarge.opendc.compute.core.image.FlopsApplicationImage import com.atlarge.opendc.compute.core.monitor.ServerMonitor @@ -53,12 +53,11 @@ internal class HypervisorTest { fun smoke() { val provider = ServiceLoader.load(SimulationEngineProvider::class.java).first() val system = provider({ _ -> - val metalFlavor = ServerFlavor(listOf(ProcessingUnit("Intel", "Xeon", "amd64", 2000.0, 1))) val vmm = HypervisorImage(object : HypervisorMonitor { override fun onSliceFinish( time: Long, - totalRequestedBurst: Long, - totalGrantedBurst: Long, + requestedBurst: Long, + grantedBurst: Long, numberOfDeployedImages: Int, hostServer: Server ) { @@ -72,16 +71,17 @@ internal class HypervisorTest { println("[${processContext.clock.millis()}]: $server") } } - val metalDriver = SimpleBareMetalDriver(UUID.randomUUID(), "test", metalFlavor) + val metalDriver = SimpleBareMetalDriver(UUID.randomUUID(), "test", listOf(ProcessingUnit("Intel", "Xeon", "amd64", 2000.0, 1)), emptyList()) metalDriver.init(monitor) metalDriver.setImage(vmm) metalDriver.setPower(PowerState.POWER_ON) delay(5) + val flavor = Flavor(1, 0) val vmDriver = metalDriver.refresh().server!!.serviceRegistry[VirtDriver] - vmDriver.spawn(workloadA, monitor, metalFlavor) - vmDriver.spawn(workloadB, monitor, metalFlavor) + vmDriver.spawn(workloadA, monitor, flavor) + vmDriver.spawn(workloadB, monitor, flavor) }, name = "sim") runBlocking { 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 7b1c2dbf..fc0c2686 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 @@ -8,16 +8,16 @@ class Sc20HypervisorMonitor : HypervisorMonitor { private val outputFile = File("sc20-experiment-results.csv") init { - outputFile.writeText("time,totalRequestedBurst,totalGrantedBurst,numberOfDeployedImages,server\n") + outputFile.writeText("time,requestedBurst,grantedBurst,numberOfDeployedImages,server\n") } override fun onSliceFinish( time: Long, - totalRequestedBurst: Long, - totalGrantedBurst: Long, + requestedBurst: Long, + grantedBurst: Long, numberOfDeployedImages: Int, hostServer: Server ) { - outputFile.appendText("$time,$totalRequestedBurst,$totalGrantedBurst,$numberOfDeployedImages,$numberOfDeployedImages,${hostServer.uid}\n") + outputFile.appendText("$time,$requestedBurst,$grantedBurst,$numberOfDeployedImages,$numberOfDeployedImages,${hostServer.uid}\n") } } 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 c7d7ac51..439412ba 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 @@ -25,12 +25,13 @@ package com.atlarge.opendc.experiments.sc20 import com.atlarge.odcsim.SimulationEngineProvider +import com.atlarge.opendc.compute.core.Flavor import com.atlarge.opendc.compute.core.Server import com.atlarge.opendc.compute.core.ServerState import com.atlarge.opendc.compute.core.monitor.ServerMonitor import com.atlarge.opendc.compute.metal.service.ProvisioningService import com.atlarge.opendc.compute.virt.service.SimpleVirtProvisioningService -import com.atlarge.opendc.format.environment.sc18.Sc18EnvironmentReader +import com.atlarge.opendc.format.environment.sc20.Sc20EnvironmentReader import com.atlarge.opendc.format.trace.vm.VmTraceReader import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay @@ -48,7 +49,7 @@ fun main(args: Array<String>) { return } - val environment = Sc18EnvironmentReader(object {}.javaClass.getResourceAsStream("/env/setup-test.json")) + val environment = Sc20EnvironmentReader(object {}.javaClass.getResourceAsStream("/env/setup-test.json")) .use { it.read() } val token = Channel<Boolean>() @@ -73,7 +74,7 @@ fun main(args: Array<String>) { while (reader.hasNext()) { val (time, workload) = reader.next() delay(max(0, time * 1000 - ctx.clock.millis())) - scheduler.deploy(workload.image, monitor) + scheduler.deploy(workload.image, monitor, Flavor(workload.image.cores, workload.image.requiredMemory)) } token.receive() diff --git a/opendc/opendc-experiments-sc20/src/main/resources/env/setup-test.json b/opendc/opendc-experiments-sc20/src/main/resources/env/setup-test.json index 0965b250..02a1d25b 100644 --- a/opendc/opendc-experiments-sc20/src/main/resources/env/setup-test.json +++ b/opendc/opendc-experiments-sc20/src/main/resources/env/setup-test.json @@ -7,27 +7,27 @@ { "type": "RACK", "machines": [ - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]} + {"cpus": [2], "memories": [1, 1, 1, 1]}, {"cpus": [2], "memories": [1, 1, 1, 1]}, + {"cpus": [2], "memories": [1, 1, 1, 1]}, {"cpus": [2], "memories": [1, 1, 1, 1]}, + {"cpus": [2], "memories": [1, 1, 1, 1]}, {"cpus": [2], "memories": [1, 1, 1, 1]}, + {"cpus": [2], "memories": [1, 1, 1, 1]}, {"cpus": [2], "memories": [1, 1, 1, 1]}, + {"cpus": [2], "memories": [1, 1, 1, 1]}, {"cpus": [2], "memories": [1, 1, 1, 1]}, + {"cpus": [2], "memories": [1, 1, 1, 1]}, {"cpus": [2], "memories": [1, 1, 1, 1]}, + {"cpus": [2], "memories": [1, 1, 1, 1]}, {"cpus": [2], "memories": [1, 1, 1, 1]}, + {"cpus": [2], "memories": [1, 1, 1, 1]}, {"cpus": [2], "memories": [1, 1, 1, 1]} ] }, { "type": "RACK", "machines": [ - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]} + {"cpus": [1], "memories": [1, 1, 1, 1]}, {"cpus": [1], "memories": [1, 1, 1, 1]}, + {"cpus": [1], "memories": [1, 1, 1, 1]}, {"cpus": [1], "memories": [1, 1, 1, 1]}, + {"cpus": [1], "memories": [1, 1, 1, 1]}, {"cpus": [1], "memories": [1, 1, 1, 1]}, + {"cpus": [1], "memories": [1, 1, 1, 1]}, {"cpus": [1], "memories": [1, 1, 1, 1]}, + {"cpus": [1], "memories": [1, 1, 1, 1]}, {"cpus": [1], "memories": [1, 1, 1, 1]}, + {"cpus": [1], "memories": [1, 1, 1, 1]}, {"cpus": [1], "memories": [1, 1, 1, 1]}, + {"cpus": [1], "memories": [1, 1, 1, 1]}, {"cpus": [1], "memories": [1, 1, 1, 1]}, + {"cpus": [1], "memories": [1, 1, 1, 1]}, {"cpus": [1], "memories": [1, 1, 1, 1]} ] } ] 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 55061492..ac44337a 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 @@ -25,7 +25,7 @@ package com.atlarge.opendc.format.environment.sc18 import com.atlarge.opendc.compute.core.ProcessingUnit -import com.atlarge.opendc.compute.core.ServerFlavor +import com.atlarge.opendc.compute.core.MemoryUnit import com.atlarge.opendc.compute.metal.driver.SimpleBareMetalDriver import com.atlarge.opendc.compute.metal.service.ProvisioningService import com.atlarge.opendc.compute.metal.service.SimpleProvisioningService @@ -69,8 +69,7 @@ class Sc18EnvironmentReader(input: InputStream, mapper: ObjectMapper = jacksonOb else -> throw IllegalArgumentException("The cpu id $id is not recognized") } } - val flavor = ServerFlavor(cores) - SimpleBareMetalDriver(UUID.randomUUID(), "node-${counter++}", flavor) + SimpleBareMetalDriver(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/Model.kt b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Model.kt new file mode 100644 index 00000000..3ef1d9eb --- /dev/null +++ b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Model.kt @@ -0,0 +1,45 @@ +package com.atlarge.opendc.format.environment.sc20 + +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo + +/** + * A datacenter setup. + * + * @property name The name of the setup. + * @property rooms The rooms in the datacenter. + */ +internal data class Setup(val name: String, val rooms: List<Room>) + +/** + * A room in a datacenter. + * + * @property type The type of room in the datacenter. + * @property objects The objects in the room. + */ +internal data class Room(val type: String, val objects: List<RoomObject>) + +/** + * An object in a [Room]. + * + * @property type The type of the room object. + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes(value = [JsonSubTypes.Type(name = "RACK", value = RoomObject.Rack::class)]) +internal sealed class RoomObject(val type: String) { + /** + * A rack in a server room. + * + * @property machines The machines in the rack. + */ + internal data class Rack(val machines: List<Machine>) : RoomObject("RACK") +} + +/** + * A machine in the setup that consists of the specified CPU's represented as + * integer identifiers and ethernet speed. + * + * @property cpus The CPUs in the machine represented as integer identifiers. + * @property memories The memories in the machine represented as integer identifiers. + */ +internal data class Machine(val cpus: List<Int>, val memories: List<Int>) 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 new file mode 100644 index 00000000..5eb711cc --- /dev/null +++ b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt @@ -0,0 +1,106 @@ +/* + * MIT License + * + * Copyright (c) 2019 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.format.environment.sc20 + +import com.atlarge.opendc.compute.core.MemoryUnit +import com.atlarge.opendc.compute.core.ProcessingUnit +import com.atlarge.opendc.compute.metal.driver.SimpleBareMetalDriver +import com.atlarge.opendc.compute.metal.service.ProvisioningService +import com.atlarge.opendc.compute.metal.service.SimpleProvisioningService +import com.atlarge.opendc.core.Environment +import com.atlarge.opendc.core.Platform +import com.atlarge.opendc.core.Zone +import com.atlarge.opendc.core.services.ServiceRegistryImpl +import com.atlarge.opendc.format.environment.EnvironmentReader +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.coroutines.runBlocking +import java.io.InputStream +import java.util.UUID + +/** + * A parser for the JSON experiment setup files used for the SC20 paper. + * + * @param input The input stream to read from. + * @param mapper The Jackson object mapper to use. + */ +class Sc20EnvironmentReader(input: InputStream, mapper: ObjectMapper = jacksonObjectMapper()) : EnvironmentReader { + /** + * The environment that was read from the file. + */ + private val environment: Environment + + init { + val setup = mapper.readValue<Setup>(input) + var counter = 0 + val nodes = setup.rooms.flatMap { room -> + room.objects.flatMap { roomObject -> + when (roomObject) { + is RoomObject.Rack -> { + roomObject.machines.map { machine -> + val cores = machine.cpus.map { id -> + when (id) { + 1 -> ProcessingUnit("Intel", "Core(TM) i7-6920HQ", "amd64", 4100.0, 4) + 2 -> ProcessingUnit("Intel", "Core(TM) I7-6920HQ", "amd64", 3500.0, 2) + else -> throw IllegalArgumentException("The cpu id $id is not recognized") + } + } + val memories = machine.memories.map { id -> + when (id) { + 1 -> MemoryUnit("Samsung", "PC DRAM K4A4G045WD", 1600.0, 4_000L) + else -> throw IllegalArgumentException("The cpu id $id is not recognized") + } + } + SimpleBareMetalDriver(UUID.randomUUID(), "node-${counter++}", cores, memories) + } + } + } + } + } + + val provisioningService = SimpleProvisioningService() + runBlocking { + for (node in nodes) { + provisioningService.create(node) + } + } + + val serviceRegistry = ServiceRegistryImpl() + serviceRegistry[ProvisioningService.Key] = provisioningService + + val platform = Platform( + UUID.randomUUID(), "sc20-platform", listOf( + Zone(UUID.randomUUID(), "zone", serviceRegistry) + ) + ) + + environment = Environment(setup.name, null, listOf(platform)) + } + + override fun read(): Environment = environment + + override fun close() {} +} diff --git a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/vm/VmTraceReader.kt b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/vm/VmTraceReader.kt index f4ed0f57..c3db9d33 100644 --- a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/vm/VmTraceReader.kt +++ b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/vm/VmTraceReader.kt @@ -55,6 +55,7 @@ class VmTraceReader(traceDirectory: File) : TraceReader<VmWorkload> { var timestampCol = 0 var coreCol = 0 var cpuUsageCol = 0 + var provisionedMemoryCol = 0 val traceInterval = 5 * 60 * 1000L traceDirectory.walk() @@ -64,6 +65,8 @@ class VmTraceReader(traceDirectory: File) : TraceReader<VmWorkload> { val flopsHistory = mutableListOf<FlopsHistoryFragment>() var vmId = -1L var cores = -1 + var requiredMemory = -1L + BufferedReader(FileReader(vmFile)).use { reader -> reader.lineSequence() .filter { line -> @@ -79,6 +82,7 @@ class VmTraceReader(traceDirectory: File) : TraceReader<VmWorkload> { timestampCol = header["Timestamp [ms]"]!! coreCol = header["CPU cores"]!! cpuUsageCol = header["CPU usage [MHZ]"]!! + provisionedMemoryCol = header["Memory capacity provisioned [KB]"]!! return@forEachIndexed } @@ -86,6 +90,7 @@ class VmTraceReader(traceDirectory: File) : TraceReader<VmWorkload> { val timestamp = values[timestampCol].trim().toLong() - 5 * 60 cores = values[coreCol].trim().toInt() val cpuUsage = values[cpuUsageCol].trim().toDouble() + requiredMemory = (values[provisionedMemoryCol].trim().toDouble() / 1000).toLong() val flops: Long = (cpuUsage * cores * 1_000_000L * 5 * 60).toLong() @@ -110,8 +115,8 @@ class VmTraceReader(traceDirectory: File) : TraceReader<VmWorkload> { val uuid = UUID(0L, vmId) val vmWorkload = VmWorkload( - uuid, "<unnamed>", UnnamedUser, - VmImage(uuid, "<unnamed>", emptyMap(), flopsHistory, cores) + uuid, "VM Workload $vmId", UnnamedUser, + VmImage(uuid, vmId.toString(), emptyMap(), flopsHistory, cores, requiredMemory) ) entries[vmId] = TraceEntryImpl( flopsHistory.firstOrNull()?.tick ?: -1, |
