Commit f4858f30 authored by Michael Pfeiffer's avatar Michael Pfeiffer
Browse files

Initial import.

parents
Loading
Loading
Loading
Loading

.gitignore

0 → 100644
+2 −0
Original line number Diff line number Diff line
.idea
__pycache__

LICENSE

0 → 100644
+25 −0
Original line number Diff line number Diff line
BSD 2-Clause License

Copyright (c) 2019, Michael Pfeiffer
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

0 → 100644
+33 −0
Original line number Diff line number Diff line
Tenant separation simulation
============================

DES simulating the overhead of a IaaS cloud platform with hardware-separated tenants.

Dependencies
------------

* [Python](https://www.python.org/) >= 3.5
* [Numpy](https://www.numpy.org/)
* [SciPy](https://www.scipy.org/)
* [Pandas](https://pandas.pydata.org/)
* [Matplotlib](https://matplotlib.org/)

Usage
-----

Scenario parameters can be configured in the `SCENARIOS` dictionary in `main.py`.
The simulation can be started with:
```
./main.py
```

For each scenario, the four migration strategies (None, Migration, Separation, Separation+Migration) are simulated in 32 runs.
All available CPU cores are used.
The results are saved to CSV files.

Afterwards, the CSV files can be aggregated by running:
```
./plot.py
```

Some provisional plots are created as well.

cloud.py

0 → 100644
+144 −0
Original line number Diff line number Diff line
import math
import random

from host import Host
from vm import VM


class Cloud:
    def __init__(self, sim):
        self.sim = sim
        self.strategy = sim.strategy

        self.hosts = {}
        self.vms = set()
        self.tenants = {}
        self.record_state()

    def record_state(self):
        num_vms = len(self.vms)
        used_hosts = sum(len(t) for t in self.hosts.values())
        active_tenants = len(self.tenants)

        wasted_hosts = 0
        for tenant_hosts in self.hosts.values():
            wasted_hosts += len(tenant_hosts) - math.ceil(sum(h.load for h in tenant_hosts) / Host.host_size)

        self.sim.stats.record_cloud_state(self.sim.clock, num_vms, used_hosts, active_tenants, wasted_hosts)

    def add_host(self, tenant):
        h = Host(self)
        if tenant not in self.hosts:
            self.hosts[tenant] = {h, }
        else:
            self.hosts[tenant].add(h)
        return h

    def _best_hosts(self, tid, size):
        best_room = Host.host_size
        best_hosts = []

        for h in self.hosts[tid]:
            room = Host.host_size - h.load

            if room == best_room:
                best_hosts.append(h)
            elif size <= room < best_room:
                best_room = room
                best_hosts = [h]

        return best_hosts

    def find_free_host(self, size, tenant):
        if self.strategy in ('sep', 'sepmig'):
            tid = tenant
        else:
            tid = 0

        if tid not in self.hosts:
            return self.add_host(tid)

        best_hosts = self._best_hosts(tid, size)

        if len(best_hosts) > 0:
            return random.choice(best_hosts)
        else:
            return self.add_host(tid)

    def create_vm(self):
        size = self.sim.vm_sizes_distribution.rvs()
        tenant = self.sim.vm_tenant_distribution.rvs()

        if tenant not in self.tenants:
            self.tenants[tenant] = 1
        else:
            self.tenants[tenant] += 1

        self.sim.stats.record_vm_creation(size, tenant)
        host = self.find_free_host(size, tenant)

        vm = VM(size, tenant, host)
        self.vms.add(vm)
        host.add(vm)
        self.record_state()

        self.sim.schedule_vm_deletion(vm)
        self.sim.schedule_vm_creation(self)

    def delete_vm(self, vm):
        self.vms.remove(vm)

        self.tenants[vm.tenant] -= 1
        if self.tenants[vm.tenant] == 0:
            del self.tenants[vm.tenant]

        if self.strategy in ('sep', 'sepmig'):
            tid = vm.tenant
        else:
            tid = 0

        if len(vm.host.vms) == 0:
            self.hosts[tid].remove(vm.host)
            if len(self.hosts[tid]) == 0:
                del self.hosts[tid]

        if self.strategy in ('mig', 'sepmig'):
            if tid in self.hosts and len(self.hosts[tid]) > 1:
                self.compact_tenant(tid)

        self.record_state()

    def compact_tenant(self, tid):
        min_load = Host.host_size
        min_hosts = []

        for h in self.hosts[tid]:
            if h.load == min_load:
                min_hosts.append(h)
            elif h.load < min_load:
                min_load = h.load
                min_hosts = [h]

        old_host = random.choice(min_hosts)
        vm = random.sample(old_host.vms, 1)[0]

        best_hosts = self._best_hosts(tid, vm.size)
        if len(best_hosts) == 0:
            return

        new_host = random.choice(best_hosts)
        if new_host.load <= old_host.load:
            return

        old_host.vms.remove(vm)
        old_host.load -= vm.size
        old_host.tenants[vm.tenant] -= 1
        if old_host.tenants[vm.tenant] == 0:
            del old_host.tenants[vm.tenant]
        if len(old_host.vms) == 0:
            self.hosts[tid].remove(old_host)
            if len(self.hosts[tid]) == 0:
                raise RuntimeError('Tenant should not be empty')

        vm.host = new_host
        new_host.add(vm)

host.py

0 → 100644
+27 −0
Original line number Diff line number Diff line
class Host:
    host_size = 32

    def __init__(self, cloud):
        self.cloud = cloud
        self.vms = set()
        self.load = 0
        self.tenants = {}

    def add(self, vm):
        self.vms.add(vm)
        self.load += vm.size

        if vm.tenant not in self.tenants:
            self.tenants[vm.tenant] = 1
        else:
            self.tenants[vm.tenant] += 1

    def delete(self, vm):
        self.vms.remove(vm)
        self.load -= vm.size
        self.tenants[vm.tenant] -= 1

        if self.tenants[vm.tenant] == 0:
            del self.tenants[vm.tenant]

        self.cloud.delete_vm(vm)