diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2019-05-15 22:37:35 +0200 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2019-05-15 22:43:59 +0200 |
| commit | 7c8a3fd217418c6f956a9315eb13c2a31a9f85a0 (patch) | |
| tree | e10534cd58f4e2b9a568fbf14e56166fa7585863 /opendc-core/src | |
| parent | b0b1577ace36022faec1a4ed0369f1c1271d5ccd (diff) | |
feat: Add initial version of OpenDC simulation model
This change adds the initial version of the port of the OpenDC
simulation model to version 2.x of the simulator.
The simulation model has been reworked to support immutability and
event-driven simulation, with speed-ups up to 75x.
Diffstat (limited to 'opendc-core/src')
34 files changed, 2546 insertions, 0 deletions
diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Broker.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Broker.kt new file mode 100644 index 00000000..0e2adb40 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Broker.kt @@ -0,0 +1,40 @@ +/* + * 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.model + +import com.atlarge.odcsim.Behavior + +/** + * A broker acting on the various cloud platforms on behalf of the user. + */ +interface Broker { + /** + * Build the runtime behavior of the [Broker]. + * + * @param platforms A list of available cloud platforms. + * @return The runtime behavior of the broker. + */ + operator fun invoke(platforms: List<PlatformRef>): Behavior<*> +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Cluster.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Cluster.kt new file mode 100644 index 00000000..1ed11e87 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Cluster.kt @@ -0,0 +1,56 @@ +/* + * 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.model + +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.empty +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.unsafeCast +import com.atlarge.opendc.model.resources.compute.host.Host +import com.atlarge.opendc.model.services.resources.ResourceManagerRef +import java.util.UUID + +/** + * A logical grouping of heterogeneous hosts and primary storage within a zone. + * + * @property uid The unique identifier of the cluster. + * @property name The name of this cluster. + * @property hosts The machines in this cluster. + */ +data class Cluster(override val uid: UUID, override val name: String, val hosts: List<Host>) : Identity { + /** + * Build the runtime [Behavior] of this cluster of hosts. + * + * @param manager The manager of the cluster. + */ + operator fun invoke(manager: ResourceManagerRef): Behavior<Nothing> = setup { ctx -> + // Launch all hosts in the cluster + for (host in hosts) { + ctx.spawn(host(manager.unsafeCast()), name = host.name) + } + + empty() + } +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Environment.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Environment.kt new file mode 100644 index 00000000..b7fe33db --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Environment.kt @@ -0,0 +1,36 @@ +/* + * 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.model + +/** + * A description of a large-scale computing environment. This description includes including key size and topology + * information of the environment, types of resources, but also various operational and management rules such as + * scheduled maintenance, allocation and other constraints. + * + * @property name The name of the environment. + * @property description A small textual description about the environment that is being modeled + * @property platforms The cloud platforms (such as AWS or GCE) in this environment. + */ +data class Environment(val name: String, val description: String?, val platforms: List<Platform>) diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Identity.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Identity.kt new file mode 100644 index 00000000..2455d138 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Identity.kt @@ -0,0 +1,42 @@ +/* + * 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.model + +import java.util.UUID + +/** + * An object that has a unique identity. + */ +interface Identity { + /** + * A unique, opaque, system-generated value, representing the object. + */ + val uid: UUID + + /** + * A non-empty, human-readable string representing the object. + */ + val name: String +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Model.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Model.kt new file mode 100644 index 00000000..098bfbde --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Model.kt @@ -0,0 +1,71 @@ +/* + * 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.model + +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.Terminated +import com.atlarge.odcsim.receiveSignal +import com.atlarge.odcsim.same +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.stopped +import com.atlarge.odcsim.unhandled + +/** + * A simulation model for large-scale simulation of datacenter infrastructure, built with the *odcsim* API. + * + * @property environment The environment in which brokers operate. + * @property brokers The brokers acting on the cloud platforms. + */ +data class Model(val environment: Environment, val brokers: List<Broker>) { + /** + * Build the runtime behavior of the universe. + */ + operator fun invoke(): Behavior<Nothing> = setup { ctx -> + // Setup the environment + val platforms = environment.platforms.map { platform -> + ctx.spawn(platform(), name = platform.name) + } + + // Launch all brokers + val remaining = mutableSetOf<ActorRef<*>>() + for (broker in brokers) { + val ref = ctx.spawnAnonymous(broker(platforms)) + ctx.watch(ref) + remaining += ref + } + + receiveSignal { _, signal -> + when (signal) { + is Terminated -> { + remaining -= signal.ref + if (remaining.isEmpty()) stopped() else same() + } + else -> + unhandled() + } + } + } +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Platform.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Platform.kt new file mode 100644 index 00000000..129ee3a9 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Platform.kt @@ -0,0 +1,103 @@ +/* + * 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.model + +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.coroutines.actorContext +import com.atlarge.odcsim.coroutines.dsl.ask +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.same +import com.atlarge.odcsim.setup +import java.util.UUID + +/** + * A representation of a cloud platform such as Amazon Web Services (AWS), Microsoft Azure or Google Cloud. + * + * @property uid The unique identifier of this datacenter. + * @property name the name of the platform. + * @property zones The availability zones available on this platform. + */ +data class Platform(override val uid: UUID, override val name: String, val zones: List<Zone>) : Identity { + /** + * Build the runtime [Behavior] of this cloud platform. + */ + operator fun invoke(): Behavior<PlatformMessage> = setup { ctx -> + ctx.log.info("Starting cloud platform {} [{}] with {} zones", name, uid, zones.size) + + // Launch all zones of the cloud platform + val zoneInstances = zones.associateWith { zone -> + ctx.spawn(zone(), name = zone.name) + } + + receiveMessage { msg -> + when (msg) { + is PlatformMessage.ListZones -> { + ctx.send(msg.replyTo, PlatformResponse.Zones(ctx.self, zoneInstances.mapKeys { it.key.name })) + same() + } + } + } + } +} + +/** + * A reference to the actor managing the zone. + */ +typealias PlatformRef = ActorRef<PlatformMessage> + +/** + * A message protocol for communicating with a cloud platform. + */ +sealed class PlatformMessage { + /** + * Request the available zones on this platform. + * + * @property replyTo The actor address to send the response to. + */ + data class ListZones(val replyTo: ActorRef<PlatformResponse.Zones>) : PlatformMessage() +} + +/** + * A message protocol used by platform actors to respond to [PlatformMessage]s. + */ +sealed class PlatformResponse { + /** + * The zones available on this cloud platform. + * + * @property platform The reference to the cloud platform these are the zones of. + * @property zones The zones in this cloud platform. + */ + data class Zones(val platform: PlatformRef, val zones: Map<String, ZoneRef>) : PlatformResponse() +} + +/** + * Retrieve the available zones of a platform. + */ +suspend fun PlatformRef.zones(): Map<String, ZoneRef> { + val ctx = actorContext<Any>() + val zones: PlatformResponse.Zones = ctx.ask(this) { PlatformMessage.ListZones(it) } + return zones.zones +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/User.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/User.kt new file mode 100644 index 00000000..502f7a74 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/User.kt @@ -0,0 +1,35 @@ +/* + * 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.model + +/** + * A user of the cloud network. + */ +interface User : Identity { + /** + * The name of the user. + */ + override val name: String +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Zone.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Zone.kt new file mode 100644 index 00000000..b1d00dac --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Zone.kt @@ -0,0 +1,212 @@ +/* + * 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.model + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.ReceivingBehavior +import com.atlarge.odcsim.Signal +import com.atlarge.odcsim.Terminated +import com.atlarge.odcsim.coroutines.actorContext +import com.atlarge.odcsim.coroutines.dsl.ask +import com.atlarge.odcsim.same +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.unhandled +import com.atlarge.opendc.model.services.Service +import com.atlarge.opendc.model.services.ServiceProvider +import java.util.ArrayDeque +import java.util.UUID + +/** + * An isolated location within a datacenter region from which public cloud services operate, roughly equivalent to a + * single datacenter. Zones contain one or more clusters and secondary storage. + * + * This class models *only* the static information of a zone, with dynamic information being contained within the zone's + * actor. During runtime, it's actor acts as a registry for all the cloud services provided by the zone. + * + * @property uid The unique identifier of this availability zone + * @property name The name of the zone within its platform. + * @property services The initial set of services provided by the zone. + * @property clusters The clusters of machines in this zone. + */ +data class Zone( + override val uid: UUID, + override val name: String, + val services: Set<ServiceProvider>, + val clusters: List<Cluster> +) : Identity { + /** + * Build the runtime [Behavior] of this datacenter. + */ + operator fun invoke(): Behavior<ZoneMessage> = setup { ctx -> + ctx.log.info("Starting zone {} [{}]", name, uid) + + // Launch all services of the zone + val instances: MutableMap<Service<*>, ActorRef<*>> = mutableMapOf() + validateDependencies(services) + + for (provider in services) { + val ref = ctx.spawn(provider(this, ctx.self), name = "${provider.name}-${provider.uid}") + ctx.watch(ref) + provider.provides.forEach { instances[it] = ref } + } + + object : ReceivingBehavior<ZoneMessage>() { + override fun receive(ctx: ActorContext<ZoneMessage>, msg: ZoneMessage): Behavior<ZoneMessage> { + return when (msg) { + is ZoneMessage.Find -> { + ctx.send(msg.replyTo, ZoneResponse.Listing(ctx.self, msg.key, instances[msg.key])) + same() + } + } + } + + override fun receiveSignal(ctx: ActorContext<ZoneMessage>, signal: Signal): Behavior<ZoneMessage> { + return when (signal) { + is Terminated -> { + instances.values.remove(signal.ref) + same() + } + else -> + unhandled() + } + } + } + } + + /** + * Validate the service for unsatisfiable dependencies. + */ + private fun validateDependencies(providers: Set<ServiceProvider>) { + val providersByKey = HashMap<Service<*>, ServiceProvider>() + for (provider in providers) { + if (provider.provides.isEmpty()) { + throw IllegalArgumentException(("Service provider $provider does not provide any service.")) + } + for (key in provider.provides) { + if (key in providersByKey) { + throw IllegalArgumentException("Multiple providers for service $key") + } + providersByKey[key] = provider + } + } + + val visited = HashSet<ServiceProvider>() + val queue = ArrayDeque(providers) + while (queue.isNotEmpty()) { + val service = queue.poll() + visited.add(service) + + for (dependencyKey in service.dependencies) { + val dependency = providersByKey[dependencyKey] + ?: throw IllegalArgumentException("Dependency $dependencyKey not satisfied for service $service") + if (dependency !in visited) { + queue.add(dependency) + } + } + } + } + + override fun equals(other: Any?): Boolean = other is Zone && uid == other.uid + override fun hashCode(): Int = uid.hashCode() +} + +/** + * A reference to the actor managing the zone. + */ +typealias ZoneRef = ActorRef<ZoneMessage> + +/** + * A message protocol for communicating with the service registry + */ +sealed class ZoneMessage { + /** + * Lookup the specified service in this availability zone. + * + * @property key The key of the service to lookup. + * @property replyTo The address to reply to. + */ + data class Find( + val key: Service<*>, + val replyTo: ActorRef<ZoneResponse.Listing> + ) : ZoneMessage() +} + +/** + * A message protocol used by service registry actors to respond to [ZoneMessage]s. + */ +sealed class ZoneResponse { + /** + * The response sent when looking up services in a zone. + * + * @property zone The zone from which the response originates. + * @property key The key of the service that was looked up. + * @property ref The reference to the service or `null` if it is not present in the zone. + */ + data class Listing( + val zone: ZoneRef, + val key: Service<*>, + private val ref: ActorRef<*>? + ) : ZoneResponse() { + /** + * A flag to indicate whether the service is present. + */ + val isPresent: Boolean + get() = ref != null + + /** + * Determine whether this listing is for the specified key. + * + * @param key The key to check for. + * @return `true` if the listing is for this key, `false` otherwise. + */ + fun isForKey(key: Service<*>): Boolean = key == this.key + + /** + * Extract the result from the service lookup. + * + * @param key The key of the lookup. + * @return The reference to the service or `null` if it is not present in the zone. + */ + operator fun <T : Any> invoke(key: Service<T>): ActorRef<T>? { + require(this.key == key) { "Invalid key" } + @Suppress("UNCHECKED_CAST") + return ref as? ActorRef<T> + } + } +} + +/** + * Find the reference to the specified [ServiceProvider]. + * + * @param key The key of the service to find. + * @throws IllegalArgumentException if the service is not found. + */ +suspend fun <T : Any> ZoneRef.find(key: Service<T>): ActorRef<T> { + val ctx = actorContext<Any>() + val listing: ZoneResponse.Listing = ctx.ask(this) { ZoneMessage.Find(key, it) } + return listing(key) ?: throw IllegalArgumentException("Unknown key $key") +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/Machine.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/Machine.kt new file mode 100644 index 00000000..08d94e14 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/Machine.kt @@ -0,0 +1,105 @@ +/* + * 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.model.resources.compute + +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Behavior +import com.atlarge.opendc.model.Identity +import com.atlarge.opendc.model.resources.compute.supervision.MachineSupervisionEvent +import com.atlarge.opendc.model.workload.application.Application +import com.atlarge.opendc.model.workload.application.Pid + +/** + * A generic representation of a compute node (either physical or virtual) that is able to run [Application]s. + */ +interface Machine : Identity { + /** + * The details of the machine in key/value pairs. + */ + val details: Map<String, Any> + + /** + * Build the runtime [Behavior] of this compute resource, accepting messages of [MachineMessage]. + * + * @param supervisor The supervisor of the machine. + */ + operator fun invoke(supervisor: ActorRef<MachineSupervisionEvent>): Behavior<MachineMessage> +} + +/** + * A reference to a machine instance that accepts messages of type [MachineMessage]. + */ +typealias MachineRef = ActorRef<MachineMessage> + +/** + * A message protocol for communicating with machine instances. + */ +sealed class MachineMessage { + /** + * Launch the specified [Application] on the machine instance. + * + * @property application The application to submit. + * @property key The key to identify this submission. + * @property broker The broker of the process to spawn. + */ + data class Submit( + val application: Application, + val key: Any, + val broker: ActorRef<MachineEvent> + ) : MachineMessage() +} + +/** + * A message protocol used by machine instances to respond to [MachineMessage]s. + */ +sealed class MachineEvent { + /** + * Indicate that an [Application] was spawned on a machine instance. + * + * @property instance The machine instance to which the application was submitted. + * @property application The application that has been submitted. + * @property key The key used to identify the submission. + * @property pid The spawned application instance. + */ + data class Submitted( + val instance: MachineRef, + val application: Application, + val key: Any, + val pid: Pid + ) : MachineEvent() + + /** + * Indicate that an [Application] has terminated on the specified machine. + * + * @property instance The machine instance to which the application was submitted. + * @property pid The reference to the application instance that has terminated. + * @property status The exit code of the task, where zero means successful. + */ + data class Terminated( + val instance: MachineRef, + val pid: Pid, + val status: Int = 0 + ) : MachineEvent() +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/MachineStatus.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/MachineStatus.kt new file mode 100644 index 00000000..f4fe9494 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/MachineStatus.kt @@ -0,0 +1,33 @@ +/* + * 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.model.resources.compute + +/** + * The status of a machine. + */ +enum class MachineStatus { + HALT, + RUNNING +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingElement.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingElement.kt new file mode 100644 index 00000000..9e5b1b71 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingElement.kt @@ -0,0 +1,33 @@ +/* + * 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.model.resources.compute + +/** + * A logical core in a CPU. + * + * @property id The identifier of the core within the CPU. + * @property unit The [ProcessingUnit] the core is part of. + */ +data class ProcessingElement(val id: Int, val unit: ProcessingUnit) diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingUnit.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingUnit.kt new file mode 100644 index 00000000..025087a4 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingUnit.kt @@ -0,0 +1,44 @@ +/* + * 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.model.resources.compute + +/** + * A processing unit of a compute resource, either virtual or physical. + * + * @property vendor The vendor string of the cpu. + * @property family The cpu family number. + * @property model The model number of the cpu. + * @property modelName The name of the cpu model. + * @property clockRate The clock speed of the cpu in MHz. + * @property cores The number of logical cores in the cpu. + */ +data class ProcessingUnit( + val vendor: String, + val family: Int, + val model: Int, + val modelName: String, + val clockRate: Double, + val cores: Int +) diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/host/Host.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/host/Host.kt new file mode 100644 index 00000000..ca7ea204 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/host/Host.kt @@ -0,0 +1,86 @@ +/* + * 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.model.resources.compute.host + +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.join +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.same +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.unhandled +import com.atlarge.odcsim.withTimers +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.MachineMessage +import com.atlarge.opendc.model.resources.compute.ProcessingElement +import com.atlarge.opendc.model.resources.compute.scheduling.MachineScheduler +import com.atlarge.opendc.model.resources.compute.supervision.MachineSupervisionEvent +import com.atlarge.opendc.model.workload.application.Application +import com.atlarge.opendc.model.workload.application.ProcessSupervisor +import java.util.UUID + +/** + * A physical compute node in a datacenter that is able to run [Application]s. + * + * @property uid The unique identifier of this machine. + * @property name The name of the machine. + * @property scheduler The process scheduler of this machine. + * @property cores The list of processing elements in the machine. + * @property details The details of this host. + */ +data class Host( + override val uid: UUID, + override val name: String, + val scheduler: MachineScheduler, + val cores: List<ProcessingElement>, + override val details: Map<String, Any> = emptyMap() +) : Machine { + /** + * Build the [Behavior] for a physical machine. + */ + override fun invoke(supervisor: ActorRef<MachineSupervisionEvent>): Behavior<MachineMessage> = setup { ctx -> + ctx.send(supervisor, MachineSupervisionEvent.Announce(this, ctx.self)) + ctx.send(supervisor, MachineSupervisionEvent.Up(ctx.self)) + + withTimers { timers -> + val sched = scheduler(this, ctx, timers) + sched.updateResources(cores) + receiveMessage<Any> { msg -> + when (msg) { + is MachineMessage.Submit -> { + sched.submit(msg.application, msg.key, msg.broker) + same() + } + else -> + unhandled() + } + }.join(ProcessSupervisor(sched).unsafeCast()).narrow() + } + } + + override fun equals(other: Any?): Boolean = other is Machine && uid == other.uid + + override fun hashCode(): Int = uid.hashCode() +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineScheduler.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineScheduler.kt new file mode 100644 index 00000000..de14f0fe --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineScheduler.kt @@ -0,0 +1,48 @@ +/* + * 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.model.resources.compute.scheduling + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.TimerScheduler +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.MachineMessage + +/** + * A factory interface for constructing a [MachineSchedulerLogic]. + */ +interface MachineScheduler { + /** + * Construct a [MachineSchedulerLogic] in the given [ActorContext]. + * + * @param machine The machine to create the scheduler for. + * @param ctx The actor context to construct a scheduler for. + * @param scheduler The timer scheduler to use. + */ + operator fun invoke( + machine: Machine, + ctx: ActorContext<MachineMessage>, + scheduler: TimerScheduler<MachineMessage> + ): MachineSchedulerLogic +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineSchedulerLogic.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineSchedulerLogic.kt new file mode 100644 index 00000000..879d6ed8 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineSchedulerLogic.kt @@ -0,0 +1,64 @@ +/* + * 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.model.resources.compute.scheduling + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.TimerScheduler +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.MachineEvent +import com.atlarge.opendc.model.resources.compute.MachineMessage +import com.atlarge.opendc.model.resources.compute.ProcessingElement +import com.atlarge.opendc.model.workload.application.Application +import com.atlarge.opendc.model.workload.application.ProcessSupervisor + +/** + * A scheduler that distributes processes over processing elements in a machine. + * + * @property machine The machine to create the scheduler for. + * @property ctx The context in which the scheduler runs. + * @property scheduler The timer scheduler to use. + */ +abstract class MachineSchedulerLogic( + protected val machine: Machine, + protected val ctx: ActorContext<MachineMessage>, + protected val scheduler: TimerScheduler<MachineMessage> +) : ProcessSupervisor { + /** + * Update the available resources in the machine. + * + * @param cores The available processing cores for the scheduler. + */ + abstract fun updateResources(cores: List<ProcessingElement>) + + /** + * Submit the specified application for scheduling. + * + * @param application The application to submit. + * @param key The key to identify the application instance. + * @param handler The broker of this application. + */ + abstract fun submit(application: Application, key: Any, handler: ActorRef<MachineEvent>) +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessObserver.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessObserver.kt new file mode 100644 index 00000000..11262d79 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessObserver.kt @@ -0,0 +1,68 @@ +/* + * 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.model.resources.compute.scheduling + +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.same +import com.atlarge.opendc.model.resources.compute.MachineEvent +import com.atlarge.opendc.model.resources.compute.MachineRef +import com.atlarge.opendc.model.workload.application.Application +import com.atlarge.opendc.model.workload.application.Pid + +/** + * An interface for observing processes. + */ +interface ProcessObserver { + /** + * This method is invoked when the setup of an application completed successfully. + * + * @param pid The process id of the process that has been initialized. + */ + fun onSubmission(instance: MachineRef, application: Application, key: Any, pid: Pid) + + /** + * This method is invoked when a process exits. + * + * @property pid A reference to the application instance. + * @property status The exit code of the task, where zero means successful. + */ + fun onTermination(instance: MachineRef, pid: Pid, status: Int) + + companion object { + /** + * Create the [Behavior] for a [ProcessObserver]. + * + * @param observer The observer to create the behavior for. + */ + operator fun invoke(observer: ProcessObserver): Behavior<MachineEvent> = receiveMessage { msg -> + when (msg) { + is MachineEvent.Submitted -> observer.onSubmission(msg.instance, msg.application, msg.key, msg.pid) + is MachineEvent.Terminated -> observer.onTermination(msg.instance, msg.pid, msg.status) + } + same() + } + } +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessState.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessState.kt new file mode 100644 index 00000000..4c685fc4 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessState.kt @@ -0,0 +1,50 @@ +/* + * 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.model.resources.compute.scheduling + +/** + * An enumeration of the distinct states of an application instance (process). + */ +enum class ProcessState { + /** + * Default state of a process, where the task is waiting to be assigned and installed on a machine. + */ + CREATED, + + /** + * State to indicate that the process is waiting to be ran. + */ + READY, + + /** + * State to indicate that the process is currently running. + */ + RUNNING, + + /** + * State to indicate that the process has been terminated, either successfully or due to failure. + */ + TERMINATED, +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessView.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessView.kt new file mode 100644 index 00000000..bc88e01f --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessView.kt @@ -0,0 +1,53 @@ +/* + * 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.model.resources.compute.scheduling + +import com.atlarge.odcsim.ActorRef +import com.atlarge.opendc.model.resources.compute.MachineEvent +import com.atlarge.opendc.model.workload.application.Application +import com.atlarge.opendc.model.workload.application.Pid +import com.atlarge.opendc.model.workload.application.ProcessMessage + +/** + * A process represents a application instance running on a particular machine from the machine scheduler's point of + * view. + * + * @property application The application this is an instance of. + * @property broker The broker of the process, which is informed about its progress. + * @property pid The reference to the application instance. + * @property state The state of the process. + */ +data class ProcessView( + val application: Application, + val broker: ActorRef<MachineEvent>, + val pid: Pid, + var state: ProcessState = ProcessState.CREATED +) { + /** + * The slice of processing elements allocated for the process. Available as soon as the state + * becomes [ProcessState.RUNNING] + */ + lateinit var allocation: ProcessMessage.Allocation +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/SpaceSharedMachineScheduler.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/SpaceSharedMachineScheduler.kt new file mode 100644 index 00000000..457c7070 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/SpaceSharedMachineScheduler.kt @@ -0,0 +1,178 @@ +/* + * 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.model.resources.compute.scheduling + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Instant +import com.atlarge.odcsim.TimerScheduler +import com.atlarge.odcsim.unsafeCast +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.MachineEvent +import com.atlarge.opendc.model.resources.compute.MachineMessage +import com.atlarge.opendc.model.resources.compute.ProcessingElement +import com.atlarge.opendc.model.workload.application.Application +import com.atlarge.opendc.model.workload.application.Pid +import com.atlarge.opendc.model.workload.application.ProcessMessage +import com.atlarge.opendc.model.workload.application.ProcessSupervisor +import java.util.ArrayDeque +import java.util.UUID + +/** + * A machine scheduling policy where processes are space-shared on the machine. + * + * Space-sharing for machine scheduling means that all running processes will be allocated a separate set of the + * [ProcessingElement]s in a [Machine]. Applications are scheduled on the machine in first-in-first-out (FIFO) order, + * thus larger applications may block smaller tasks from proceeding, while space is available (no backfilling). + * + * @property machine The machine to create the scheduler for. + * @property ctx The context in which the scheduler runs. + * @property scheduler The timer scheduler to use. + */ +class SpaceSharedMachineScheduler( + machine: Machine, + ctx: ActorContext<MachineMessage>, + scheduler: TimerScheduler<MachineMessage> +) : MachineSchedulerLogic(machine, ctx, scheduler), ProcessSupervisor { + private var cores = 0 + private val available = ArrayDeque<ProcessingElement>() + private val queue = ArrayDeque<ActorRef<ProcessMessage>>() + private val running = LinkedHashSet<ActorRef<ProcessMessage>>() + private val processes = HashMap<ActorRef<ProcessMessage>, ProcessView>() + + override fun updateResources(cores: List<ProcessingElement>) { + available.addAll(cores) + this.cores = cores.size + + // Add all running tasks in front of the queue + running.reversed().forEach { queue.addFirst(it) } + running.clear() + + reschedule() + } + + override fun submit(application: Application, key: Any, handler: ActorRef<MachineEvent>) { + // Create application instance on the machine + val pid = ctx.spawn(application(), name = application.name + ":" + application.uid + ":" + UUID.randomUUID().toString()) + processes[pid] = ProcessView(application, handler, pid) + + // Setup the task + ctx.send(pid, ProcessMessage.Setup(machine, ctx.self.unsafeCast())) + + // Inform the owner that the task has been submitted + ctx.send(handler, MachineEvent.Submitted(ctx.self, application, key, pid)) + } + + /** + * Reschedule the tasks on this machine. + */ + private fun reschedule() { + while (queue.isNotEmpty()) { + val pid = queue.peek() + val process = processes[pid]!! + + if (process.application.cores >= cores) { + // The task will never fit on the machine + // TODO Fail task + ctx.log.warn("Process {} will not fit in machine: dropping.", process) + queue.remove() + return + } else if (process.application.cores > available.size) { + // The task will not fit at the moment + // Try again if resources become available + ctx.log.debug("Application queued: not enough processing elements available [requested={}, available={}]", + process.application.cores, available.size) + return + } + queue.remove() + + // Compute the available resources + val resources = List(process.application.cores) { + val pe = available.poll() + Pair(pe, 1.0) + }.toMap() + process.state = ProcessState.RUNNING + process.allocation = ProcessMessage.Allocation(resources, Instant.POSITIVE_INFINITY) + running += pid + + ctx.send(pid, process.allocation) + } + } + + override fun onReady(pid: Pid) { + val process = processes[pid]!! + + // Schedule the task if it has been setup + queue.add(pid) + process.state = ProcessState.READY + + reschedule() + } + + override fun onConsume(pid: Pid, utilization: Map<ProcessingElement, Double>, until: Instant) { + val process = processes[pid]!! + val allocation = process.allocation + + if (until > allocation.until) { + // Tasks are not allowed to extend allocation provided by the machine + // TODO Fail the task + ctx.log.warn("Task {} must not extend allocation provided by the machine", pid) + } else if (until < allocation.until) { + // Shrink allocation + process.allocation = allocation.copy(until = until) + } + + // Reschedule the process after the allocation expires + scheduler.after(pid, process.allocation.until - ctx.time) { + // We just extend the allocation + process.allocation = process.allocation.copy(until = Instant.POSITIVE_INFINITY) + ctx.send(pid, process.allocation) + } + } + + override fun onExit(pid: Pid, status: Int) { + val process = processes.remove(pid)!! + running -= pid + scheduler.cancel(pid) + process.allocation.resources.keys.forEach { available.add(it) } + + ctx.log.debug("Application {} terminated with status {}", pid, status) + + // Inform the owner that the task has terminated + ctx.send(process.broker, MachineEvent.Terminated(ctx.self, pid, status)) + + reschedule() + } + + companion object : MachineScheduler { + override fun invoke( + machine: Machine, + ctx: ActorContext<MachineMessage>, + scheduler: TimerScheduler<MachineMessage> + ): MachineSchedulerLogic { + return SpaceSharedMachineScheduler(machine, ctx, scheduler) + } + } +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisionEvent.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisionEvent.kt new file mode 100644 index 00000000..2b3fae3d --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisionEvent.kt @@ -0,0 +1,49 @@ +/* + * 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.model.resources.compute.supervision + +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.MachineRef + +/** + * A supervision protocol for [Machine] instances. + */ +sealed class MachineSupervisionEvent { + /** + * Initialization message to introduce to the supervisor a new machine by specifying its static information and + * address. + * + * @property machine The machine that is being announced. + * @property ref The address to talk to the host. + */ + data class Announce(val machine: Machine, val ref: MachineRef) : MachineSupervisionEvent() + + /** + * Indicate that the specified machine has booted up. + * + * @property ref The address to talk to the machine. + */ + data class Up(val ref: MachineRef) : MachineSupervisionEvent() +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisor.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisor.kt new file mode 100644 index 00000000..c3607a22 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisor.kt @@ -0,0 +1,67 @@ +/* + * 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.model.resources.compute.supervision + +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.same +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.MachineRef + +/** + * An interface for supervising [Machine] instances. + */ +interface MachineSupervisor { + /** + * This method is invoked when a new machine is introduced to the supervisor by specifying its static information + * and address. + * + * @param machine The machine that is being announced. + * @param ref The address to talk to the host. + */ + fun onAnnounce(machine: Machine, ref: MachineRef) + + /** + * This method is invoked when a process exits. + * + * @param ref The address to talk to the machine. + */ + fun onUp(ref: MachineRef) + + companion object { + /** + * Create the [Behavior] for a [MachineSupervisor]. + * + * @param supervisor The supervisor to create the behavior for. + */ + operator fun invoke(supervisor: MachineSupervisor): Behavior<MachineSupervisionEvent> = receiveMessage { msg -> + when (msg) { + is MachineSupervisionEvent.Announce -> supervisor.onAnnounce(msg.machine, msg.ref) + is MachineSupervisionEvent.Up -> supervisor.onUp(msg.ref) + } + same() + } + } +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/Service.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/Service.kt new file mode 100644 index 00000000..e8b25b88 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/Service.kt @@ -0,0 +1,47 @@ +/* + * 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.model.services + +import com.atlarge.opendc.model.Identity +import java.util.UUID + +/** + * An interface for identifying service implementations of the same type (providing the same service). + * + * @param T The shape of the messages the service responds to. + */ +interface Service<T : Any> : Identity + +/** + * Helper class for constructing a [Service]. + * + * @property uid The unique identifier of the service. + * @property name The name of the service. + */ +abstract class AbstractService<T : Any>(override val uid: UUID, override val name: String) : Service<T> { + override fun equals(other: Any?): Boolean = other is Service<*> && uid == other.uid + override fun hashCode(): Int = uid.hashCode() + override fun toString(): String = "Service[uid=$uid,name=$name]" +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceMap.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceMap.kt new file mode 100644 index 00000000..d91208bf --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceMap.kt @@ -0,0 +1,49 @@ +/* + * 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.model.services + +import com.atlarge.odcsim.ActorRef + +/** + * A map containing services. + */ +interface ServiceMap { + /** + * Determine if this map contains the service with the specified [Service]. + * + * @param key The key of the service to check for. + * @return `true` if the service is in the map, `false` otherwise. + */ + operator fun contains(key: Service<*>): Boolean + + /** + * Obtain the service with the specified [Service]. + * + * @param key The key of the service to obtain. + * @return The references to the service. + * @throws IllegalArgumentException if the key does not exists in the map. + */ + operator fun <T : Any> get(key: Service<T>): ActorRef<T> +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceProvider.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceProvider.kt new file mode 100644 index 00000000..1bf5b22e --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceProvider.kt @@ -0,0 +1,64 @@ +/* + * 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.model.services + +import com.atlarge.odcsim.Behavior +import com.atlarge.opendc.model.Identity +import com.atlarge.opendc.model.Zone +import com.atlarge.opendc.model.ZoneRef +import java.util.UUID + +/** + * An abstract representation of a cloud service implementation provided by a cloud platform. + */ +interface ServiceProvider : Identity { + /** + * The unique identifier of the service implementation. + */ + override val uid: UUID + + /** + * The name of the service implementation. + */ + override val name: String + + /** + * The set of services provided by this [ServiceProvider]. + */ + val provides: Set<Service<*>> + + /** + * The dependencies of the service implementation. + */ + val dependencies: Set<Service<*>> + + /** + * Build the runtime [Behavior] for this service. + * + * @param zone The zone model for which the service should be build. + * @param ref The runtime reference to the zone's actor for communication. + */ + operator fun invoke(zone: Zone, ref: ZoneRef): Behavior<*> +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/ProvisioningService.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/ProvisioningService.kt new file mode 100644 index 00000000..22b70f35 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/ProvisioningService.kt @@ -0,0 +1,77 @@ +/* + * 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.model.services.provisioning + +import com.atlarge.odcsim.ActorRef +import com.atlarge.opendc.model.Zone +import com.atlarge.opendc.model.services.AbstractService +import com.atlarge.opendc.model.services.Service +import com.atlarge.opendc.model.services.ServiceProvider +import com.atlarge.opendc.model.services.resources.HostView +import java.util.UUID + +/** + * A cloud platform service that provisions the native resources on the platform. + * + * This service assumes control over all hosts in its [Zone]. + */ +abstract class ProvisioningService : ServiceProvider { + override val provides: Set<Service<*>> = setOf(ProvisioningService) + + /** + * The service key of the provisioner service. + */ + companion object : AbstractService<ProvisioningMessage>(UUID.randomUUID(), "provisioner") +} + +/** + * A message protocol for communicating to the resource provisioner. + */ +sealed class ProvisioningMessage { + /** + * Request the specified number of resources from the provisioner. + * + * @property numHosts The number of hosts to request from the provisioner. + * @property replyTo The actor to reply to. + */ + data class Request(val numHosts: Int, val replyTo: ActorRef<ProvisioningResponse.Lease>) : ProvisioningMessage() + + /** + * Release the specified resource [ProvisioningResponse.Lease]. + * + * @property lease The lease to release. + */ + data class Release(val lease: ProvisioningResponse.Lease) : ProvisioningMessage() +} + +/** + * A message protocol used by the resource provisioner to respond to [ProvisioningMessage]s. + */ +sealed class ProvisioningResponse { + /** + * A lease for the specified hosts. + */ + data class Lease(val hosts: List<HostView>) : ProvisioningResponse() +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/SimpleProvisioningService.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/SimpleProvisioningService.kt new file mode 100644 index 00000000..1ddc1b69 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/SimpleProvisioningService.kt @@ -0,0 +1,125 @@ +/* + * 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.model.services.provisioning + +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.StashBuffer +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.same +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.unhandled +import com.atlarge.odcsim.unsafeCast +import com.atlarge.opendc.model.Zone +import com.atlarge.opendc.model.ZoneMessage +import com.atlarge.opendc.model.ZoneRef +import com.atlarge.opendc.model.ZoneResponse +import com.atlarge.opendc.model.resources.compute.MachineRef +import com.atlarge.opendc.model.services.Service +import com.atlarge.opendc.model.services.resources.HostView +import com.atlarge.opendc.model.services.resources.ResourceManagementMessage +import com.atlarge.opendc.model.services.resources.ResourceManagementResponse +import com.atlarge.opendc.model.services.resources.ResourceManagementService +import com.atlarge.opendc.model.services.resources.ResourceManagerRef +import java.util.ArrayDeque +import java.util.UUID + +/** + * A cloud platform service that provisions the native resources on the platform. + * + * This service assumes control over all hosts in its [Zone]. + */ +object SimpleProvisioningService : ProvisioningService() { + override val uid: UUID = UUID.randomUUID() + override val name: String = "simple-provisioner" + override val dependencies: Set<Service<*>> = setOf(ResourceManagementService) + + /** + * Build the runtime [Behavior] for the resource provisioner, responding to messages of shape [ProvisioningMessage]. + */ + override fun invoke(zone: Zone, ref: ZoneRef): Behavior<ProvisioningMessage> = setup { ctx -> + val buffer = StashBuffer<Any>(capacity = 30) + ctx.send(ref, ZoneMessage.Find(ResourceManagementService, ctx.self.unsafeCast())) + + receiveMessage<Any> { msg -> + when (msg) { + is ZoneResponse.Listing -> { + val service = msg(ResourceManagementService) ?: throw IllegalStateException("Resource management service not available") + buffer.unstashAll(ctx.unsafeCast(), active(zone, service).unsafeCast()) + } + else -> { + buffer.stash(msg) + same() + } + } + }.narrow() + } + + private fun active(zone: Zone, manager: ResourceManagerRef) = setup<ProvisioningMessage> { ctx -> + val hosts = mutableMapOf<MachineRef, HostView>() + val available = ArrayDeque<HostView>() + val leases = mutableSetOf<ProvisioningResponse.Lease>() + + // Subscribe to all machines in the zone + for (cluster in zone.clusters) { + for (host in cluster.hosts) { + ctx.send(manager, ResourceManagementMessage.Lookup(host, ctx.self.unsafeCast())) + } + } + + receiveMessage<Any> { msg -> + when (msg) { + is ProvisioningMessage.Request -> { + ctx.log.info("Provisioning {} hosts", msg.numHosts) + val leaseHosts = mutableListOf<HostView>() + while (available.isNotEmpty() && leaseHosts.size < msg.numHosts) { + leaseHosts += available.poll() + } + val lease = ProvisioningResponse.Lease(leaseHosts) + leases += lease + ctx.send(msg.replyTo, lease) + same() + } + is ProvisioningMessage.Release -> { + val lease = msg.lease + if (lease in leases) { + return@receiveMessage same() + } + available.addAll(lease.hosts) + leases -= lease + same() + } + is ResourceManagementResponse.Listing -> { + if (msg.instance != null) { + hosts[msg.instance.ref] = msg.instance + available.add(msg.instance) + } + same() + } + else -> + unhandled() + } + }.narrow() + } +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/HostView.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/HostView.kt new file mode 100644 index 00000000..943461cd --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/HostView.kt @@ -0,0 +1,42 @@ +/* + * 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.model.services.resources + +import com.atlarge.opendc.model.resources.compute.MachineRef +import com.atlarge.opendc.model.resources.compute.MachineStatus +import com.atlarge.opendc.model.resources.compute.host.Host + +/** + * The dynamic information of a [Host] instance that is being tracked by the [ResourceManagementService]. This means + * that information may not be up-to-date. + * + * @property host The static information of the host. + * @property ref The reference to the host's actor. + * @property status The status of the machine. + */ +data class HostView(val host: Host, val ref: MachineRef, val status: MachineStatus = MachineStatus.HALT) { + override fun equals(other: Any?): Boolean = other is HostView && host.uid == other.host.uid + override fun hashCode(): Int = host.uid.hashCode() +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/ResourceManagementService.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/ResourceManagementService.kt new file mode 100644 index 00000000..5e38c6da --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/ResourceManagementService.kt @@ -0,0 +1,121 @@ +/* + * 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.model.services.resources + +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.same +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.unhandled +import com.atlarge.opendc.model.Zone +import com.atlarge.opendc.model.ZoneRef +import com.atlarge.opendc.model.resources.compute.MachineRef +import com.atlarge.opendc.model.resources.compute.MachineStatus +import com.atlarge.opendc.model.resources.compute.host.Host +import com.atlarge.opendc.model.resources.compute.supervision.MachineSupervisionEvent +import com.atlarge.opendc.model.services.Service +import com.atlarge.opendc.model.services.ServiceProvider +import java.util.UUID + +/** + * A cloud platform service that manages the native resources on the platform. + * + * This service assumes control over all hosts in its [Zone]. + */ +object ResourceManagementService : ServiceProvider, Service<ResourceManagementMessage> { + override val uid: UUID = UUID.randomUUID() + override val name: String = "resource-manager" + override val provides: Set<Service<*>> = setOf(ResourceManagementService) + override val dependencies: Set<Service<*>> = emptySet() + + /** + * Build the runtime behavior of the [ResourceManagementService]. + */ + override fun invoke(zone: Zone, ref: ZoneRef): Behavior<ResourceManagementMessage> = setup { ctx -> + // Launch the clusters of the zone + for (cluster in zone.clusters) { + ctx.spawn(cluster(ctx.self), name = "${cluster.name}-${cluster.uid}") + } + + val hosts = mutableMapOf<MachineRef, HostView>() + receiveMessage<Any> { msg -> + when (msg) { + is MachineSupervisionEvent.Announce -> { + val host = msg.machine as? Host + if (host != null) { + hosts[msg.ref] = HostView(host, msg.ref) + } + same() + } + is MachineSupervisionEvent.Up -> { + hosts.computeIfPresent(msg.ref) { _, value -> + value.copy(status = MachineStatus.RUNNING) + } + same() + } + is ResourceManagementMessage.Lookup -> { + ctx.send(msg.replyTo, ResourceManagementResponse.Listing(hosts.values.find { it.host == msg.host })) + same() + } + else -> + unhandled() + } + }.narrow() + } +} + +/** + * A reference to the resource manager of a zone. + */ +typealias ResourceManagerRef = ActorRef<ResourceManagementMessage> + +/** + * A message protocol for communicating to the resource manager. + */ +sealed class ResourceManagementMessage { + /** + * Lookup the specified [Host]. + * + * @property host The host to lookup. + * @property replyTo The address to sent the response to. + */ + data class Lookup( + val host: Host, + val replyTo: ActorRef<ResourceManagementResponse.Listing> + ) : ResourceManagementMessage() +} + +/** + * A message protocol used by the resource manager to respond to [ResourceManagementMessage]s. + */ +sealed class ResourceManagementResponse { + /** + * A response to a [ResourceManagementMessage.Lookup] request. + * + * @property instance The instance that was found or `null` if it does not exist. + */ + data class Listing(val instance: HostView?) : ResourceManagementResponse() +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/Workload.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/Workload.kt new file mode 100644 index 00000000..c1215715 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/Workload.kt @@ -0,0 +1,39 @@ +/* + * 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.model.workload + +import com.atlarge.opendc.model.Identity +import com.atlarge.opendc.model.User + +/** + * A high-level abstraction that represents the actual work that a set of compute resources perform, such + * as running an application on a machine or a whole workflow running multiple tasks on numerous machines. + */ +interface Workload : Identity { + /** + * The owner of this workload. + */ + val owner: User +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Application.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Application.kt new file mode 100644 index 00000000..00ab98b6 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Application.kt @@ -0,0 +1,47 @@ +/* + * 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.model.workload.application + +import com.atlarge.odcsim.Behavior +import com.atlarge.opendc.model.workload.Workload + +/** + * A generic representation of a workload that can directly be executed by physical or virtual compute resources, + * such as a web server application. + */ +interface Application : Workload { + /** + * The number of processing elements required by the task. + */ + val cores: Int + + /** + * Build the runtime [Behavior] of an application, accepting messages of [ProcessMessage]. + * + * This is a model for the runtime behavior of an application instance (process) that describes how an application + * instance consumes the allocated resources on a machine. + */ + operator fun invoke(): Behavior<ProcessMessage> +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplication.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplication.kt new file mode 100644 index 00000000..60a896d4 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplication.kt @@ -0,0 +1,164 @@ +/* + * 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.model.workload.application + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.DeferredBehavior +import com.atlarge.odcsim.Instant +import com.atlarge.odcsim.receive +import com.atlarge.odcsim.stopped +import com.atlarge.odcsim.unhandled +import com.atlarge.opendc.model.User +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.ProcessingElement +import java.lang.Double.min +import java.util.UUID +import kotlin.math.ceil + +/** + * An [Application] implementation that models applications performing a static number of floating point operations + * ([flops]) on a compute resource. + * + * @property uid A unique identifier for this application. + * @property name The name of the application. + * @property owner The owner of this application. + * @property cores The number of cores needed for this application. + * @property flops The number of floating point operations to perform for this task. + */ +class FlopsApplication( + override val uid: UUID, + override val name: String, + override val owner: User, + override val cores: Int, + val flops: Long +) : Application { + + init { + require(flops >= 0) { "Negative number of flops" } + } + + /** + * Build the runtime [Behavior] based on a number of floating point operations to execute. + */ + override fun invoke(): Behavior<ProcessMessage> = object : DeferredBehavior<ProcessMessage>() { + /** + * The remaining number of floating point operations to execute. + */ + var remaining = flops + + /** + * The machine to which the task is assigned. + */ + lateinit var machine: Machine + + /** + * The reference to the machine instance. + */ + lateinit var ref: ActorRef<ProcessEvent> + + /** + * The start of the last allocation + */ + var start: Instant = 0.0 + + /** + * The given resource allocation. + */ + lateinit var allocation: Map<ProcessingElement, Double> + + override fun invoke(ctx: ActorContext<ProcessMessage>) = created() + + /** + * Handle the initial, created state of a task instance. + */ + private fun created(): Behavior<ProcessMessage> = receive { ctx, msg -> + when (msg) { + is ProcessMessage.Setup -> { + machine = msg.machine + ref = msg.ref + /* TODO implement setup time */ + ctx.send(ref, ProcessEvent.Ready(ctx.self)) + ready().narrow() + } + else -> unhandled() + } + } + + /** + * Handle the ready state of a task instance. + */ + private fun ready(): Behavior<Any> = receive { ctx, msg -> + when (msg) { + is ProcessMessage.Allocation -> { + processAllocation(ctx, msg.resources, msg.until) + running() + } + else -> unhandled() + } + } + + /** + * Handle the running state of a task instance. + */ + private fun running(): Behavior<Any> = receive { ctx, msg -> + when (msg) { + is ProcessMessage.Allocation -> { + /* Compute the consumption of flops */ + val consumed = allocation.asSequence() + .map { (key, value) -> key.unit.clockRate * value * (ctx.time - start) } + .sum() + // Ceil to prevent consumed flops being rounded to 0 + remaining -= ceil(consumed).toLong() + + /* Test whether all flops have been consumed and the task is finished */ + if (remaining <= 0) { + ctx.send(ref, ProcessEvent.Exit(ctx.self, 0)) + return@receive stopped() + } + + processAllocation(ctx, msg.resources, msg.until) + running().narrow() + } + else -> unhandled() + } + } + + private fun processAllocation(ctx: ActorContext<Any>, resources: Map<ProcessingElement, Double>, until: Instant) { + start = ctx.time + allocation = resources + .asSequence() + .take(cores) + .associateBy({ it.key }, { it.value }) + + val speed = allocation.asSequence() + .map { (key, value) -> key.unit.clockRate * value } + .average() + val finishedAt = ctx.time + remaining / speed + ctx.send(ref, ProcessEvent.Consume(ctx.self, allocation, min(finishedAt, until))) + } + } +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Process.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Process.kt new file mode 100644 index 00000000..a78b8572 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Process.kt @@ -0,0 +1,91 @@ +/* + * 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.model.workload.application + +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Instant +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.ProcessingElement + +/** + * The process id (pid) is a reference to the application instance (process) that accepts messages of + * type [ProcessMessage]. + */ +typealias Pid = ActorRef<ProcessMessage> + +/** + * A message protocol for actors to communicate with task instances (called processes). + */ +sealed class ProcessMessage { + /** + * Indicate that the task should be installed to the specified machine. + * + * @property machine The machine to install the task. + * @property ref The reference to the machine instance. + */ + data class Setup(val machine: Machine, val ref: ActorRef<ProcessEvent>) : ProcessMessage() + + /** + * Indicate an allocation of compute resources on a machine for a certain duration. + * The task may assume that the reservation occurs after installation on the same machine. + * + * @property resources The cpu cores (and the utilization percentages) allocated for the task. + * @property until The point in time till which the reservation is valid. + */ + data class Allocation(val resources: Map<ProcessingElement, Double>, val until: Instant) : ProcessMessage() +} + +/** + * The message protocol used by application instances respond to [ProcessMessage]s. + */ +sealed class ProcessEvent { + /** + * Indicate that the process is ready to start processing. + * + * @property pid A reference to the application instance. + */ + data class Ready(val pid: Pid) : ProcessEvent() + + /** + * Indicate the estimated resource utilization of the task until a specified point in time. + * + * @property pid A reference to the application instance of the represented utilization. + * @property utilization The utilization of the cpu cores as a percentage. + * @property until The point in time until which the utilization is valid. + */ + data class Consume( + val pid: Pid, + val utilization: Map<ProcessingElement, Double>, + val until: Instant + ) : ProcessEvent() + + /** + * Indicate that a process has been terminated. + * + * @property pid A reference to the application instance. + * @property status The exit code of the task, where zero means successful. + */ + data class Exit(val pid: Pid, val status: Int) : ProcessEvent() +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/ProcessSupervisor.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/ProcessSupervisor.kt new file mode 100644 index 00000000..2952c82e --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/ProcessSupervisor.kt @@ -0,0 +1,79 @@ +/* + * 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.model.workload.application + +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.Instant +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.same +import com.atlarge.opendc.model.resources.compute.ProcessingElement + +/** + * An interface for supervising processes. + */ +interface ProcessSupervisor { + /** + * This method is invoked when the setup of an application completed successfully. + * + * @param pid The process id of the process that has been initialized. + */ + fun onReady(pid: Pid) {} + + /** + * This method is invoked when a process informs the machine that it is running with the + * estimated resource utilization until a specified point in time. + * + * @param pid The process id of the process that is running. + * @param utilization The utilization of the cpu cores as a percentage. + * @param until The point in time until which the utilization is valid. + */ + fun onConsume(pid: Pid, utilization: Map<ProcessingElement, Double>, until: Instant) {} + + /** + * This method is invoked when a process exits. + * + * @property pid A reference to the application instance. + * @property status The exit code of the task, where zero means successful. + */ + fun onExit(pid: Pid, status: Int) {} + + companion object { + /** + * Create the [Behavior] for a [ProcessSupervisor]. + * + * @param supervisor The supervisor to create the behavior for. + */ + operator fun invoke(supervisor: ProcessSupervisor): Behavior<ProcessEvent> { + return receiveMessage { msg -> + when (msg) { + is ProcessEvent.Ready -> supervisor.onReady(msg.pid) + is ProcessEvent.Consume -> supervisor.onConsume(msg.pid, msg.utilization, msg.until) + is ProcessEvent.Exit -> supervisor.onExit(msg.pid, msg.status) + } + same() + } + } + } +} diff --git a/opendc-core/src/test/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplicationTest.kt b/opendc-core/src/test/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplicationTest.kt new file mode 100644 index 00000000..2cde1b6f --- /dev/null +++ b/opendc-core/src/test/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplicationTest.kt @@ -0,0 +1,128 @@ +/* + * 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.model.workload.application + +import com.atlarge.odcsim.testkit.BehaviorTestKit +import com.atlarge.opendc.model.User +import com.atlarge.opendc.model.resources.compute.Machine +import com.atlarge.opendc.model.resources.compute.ProcessingElement +import com.atlarge.opendc.model.resources.compute.ProcessingUnit +import com.nhaarman.mockitokotlin2.mock +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import java.util.UUID + +/** + * A test suite for the [FlopsApplication]. + */ +@DisplayName("FlopsApplication") +internal class FlopsApplicationTest { + private val flops = 10000L + private val cores = 2 + private val machine: Machine = mock() + private val user: User = mock() + private val cpu: ProcessingUnit = ProcessingUnit("Intel", 6, 8600, "Intel(R) Core(TM) i7-6920HQ CPU @ 2.90GHz", 2900.0, 1) + + private lateinit var application: FlopsApplication + + @BeforeEach + fun setUp() { + application = FlopsApplication(UUID.randomUUID(), "java", user, cores, flops) + } + + @Test + fun `should become ready after triggering installation`() { + val test = BehaviorTestKit(application()) + val inbox = test.createInbox<ProcessEvent>() + test.run(ProcessMessage.Setup(machine, inbox.ref)) + inbox.expectMessage(ProcessEvent.Ready(test.ref)) + } + + @Test + fun `should not respond to setup request after being created`() { + val test = BehaviorTestKit(application()) + val inbox = test.createInbox<ProcessEvent>() + + // Setup Machine + test.run(ProcessMessage.Setup(machine, inbox.ref)) + inbox.clear() + + // Try again + assertFalse(test.run(ProcessMessage.Setup(machine, inbox.ref))) + } + + @Test + fun `should respond to allocation with consumption`() { + val test = BehaviorTestKit(application()) + val inbox = test.createInbox<ProcessEvent>() + + val allocation = ProcessMessage.Allocation(mapOf(ProcessingElement(0, cpu) to 0.5), until = 10.0) + + // Setup Machine + test.run(ProcessMessage.Setup(machine, inbox.ref)) + inbox.clear() + + // Test allocation + test.run(allocation) + val msg = inbox.receiveMessage() + assertTrue(msg is ProcessEvent.Consume) + } + + @Test + fun `should inform the machine that it finished processing`() { + val test = BehaviorTestKit(application()) + val inbox = test.createInbox<ProcessEvent>() + val allocation = ProcessMessage.Allocation(mapOf(ProcessingElement(0, cpu) to 0.5), until = 10.0) + + // Setup + test.run(ProcessMessage.Setup(machine, inbox.ref)) + test.run(allocation) + test.runTo(10.0) + inbox.clear() + + test.run(allocation) + inbox.expectMessage(ProcessEvent.Exit(test.ref, 0)) + } + + @Test + fun `should be able to update its utilization`() { + val test = BehaviorTestKit(application()) + val inbox = test.createInbox<ProcessEvent>() + val allocation1 = ProcessMessage.Allocation(mapOf(ProcessingElement(0, cpu) to 0.5), until = 10.0) + val allocation2 = ProcessMessage.Allocation(mapOf(ProcessingElement(0, cpu) to 0.25), until = 10.0) + + // Setup + test.run(ProcessMessage.Setup(machine, inbox.ref)) + test.run(allocation1) + test.runTo(5.0) + inbox.clear() + + test.run(allocation2) + assertTrue(inbox.receiveMessage() is ProcessEvent.Consume) + } +} |
