diff --git a/neat/locals/vm_selection/algorithms.py b/neat/locals/vm_selection/algorithms.py index 8588a2a..fe50760 100644 --- a/neat/locals/vm_selection/algorithms.py +++ b/neat/locals/vm_selection/algorithms.py @@ -84,6 +84,28 @@ def minimum_migration_time_factory(time_step, migration_time, params): ([minimum_migration_time(vms_ram)], {}) +@contract +def minimum_migration_time_max_cpu_factory(time_step, migration_time, params): + """ Creates the minimum migration time / max CPU usage VM selection algorithm. + + :param time_step: The length of the simulation time step in seconds. + :type time_step: int,>=0 + + :param migration_time: The VM migration time in time seconds. + :type migration_time: float,>=0 + + :param params: A dictionary containing the algorithm's parameters. + :type params: dict(str: *) + + :return: A function implementing the minimum migration time / max CPU VM selection. + :rtype: function + """ + return lambda vms_cpu, vms_ram, state=None: \ + ([minimum_migration_time_max_cpu(params['last_n'], + vms_cpu, + vms_ram)], {}) + + @contract def minimum_migration_time(vms_ram): """ Selects the VM with the minimum RAM usage. @@ -126,3 +148,33 @@ def random(vms_cpu): :rtype: str """ return choice(vms_cpu.keys()) + + +@contract +def minimum_migration_time_max_cpu(last_n, vms_cpu, vms_ram): + """ Selects the VM with the minimum RAM and maximum CPU usage. + + :param last_n: The number of last CPU utilization values to average. + :type last_n: int,>0 + + :param vms_cpu: A map of VM UUID and their CPU utilization histories. + :type vms_cpu: dict(str: list) + + :param vms_ram: A map of VM UUID and their RAM usage data. + :type vms_ram: dict(str: number) + + :return: A VM to migrate from the host. + :rtype: str + """ + min_ram = min(vms_ram.values()) + max_cpu = 0 + selected_vm = None + for vm, cpu in vms_cpu.items(): + if vms_ram[vm] > min_ram: + continue + vals = cpu[-last_n:] + avg = sum(vals) / len(vals) + if max_cpu < avg: + max_cpu = avg + selected_vm = vm + return selected_vm diff --git a/tests/locals/vm_selection/test_algorithms.py b/tests/locals/vm_selection/test_algorithms.py index a205952..0878368 100644 --- a/tests/locals/vm_selection/test_algorithms.py +++ b/tests/locals/vm_selection/test_algorithms.py @@ -69,6 +69,53 @@ class Selection(TestCase): expect(selection).choice(x.keys()).and_return(vm).once() assert alg(x, dict()) == ([vm], {}) + @qc(10) + def minimum_migration_time_max_cpu_factory( + x=dict_( + keys=str_(of='abc123-', min_length=36, max_length=36), + values=tuple_(list_(of=int_(min=0, max=3000), + min_length=1, max_length=10), + int_(min=0, max=3000)), + min_length=1, max_length=5 + ), + last_n=int_(min=1, max=10) + ): + alg = selection.minimum_migration_time_max_cpu_factory( + 300, 20., {'last_n': last_n}) + vms_cpu = dict((k, v[0]) for k, v in x.items()) + vms_ram = dict((k, v[1]) for k, v in x.items()) + min_ram = min(vms_ram.values()) + min_ram_vms_cpu = dict((k, sum(v[-last_n:]) / len(v[-last_n:])) + for k, v in vms_cpu.items() + if vms_ram[k] == min_ram and len(v[-last_n:]) > 0) + values = min_ram_vms_cpu.values() + vm_index = values.index(max(values)) + vm = min_ram_vms_cpu.keys()[vm_index] + assert alg(vms_cpu, vms_ram) == ([vm], {}) + + @qc(10) + def minimum_migration_time_max_cpu_factory_equal_ram( + vms_cpu=dict_( + keys=str_(of='abc123-', min_length=36, max_length=36), + values=list_(of=int_(min=0, max=3000), + min_length=1, max_length=10), + min_length=1, max_length=5 + ), + ram=int_(min=1000, max=3000), + last_n=int_(min=1, max=10) + ): + alg = selection.minimum_migration_time_max_cpu_factory( + 300, 20., {'last_n': last_n}) + vms_ram = dict((k, ram) for k, _ in vms_cpu.items()) + min_ram = min(vms_ram.values()) + min_ram_vms_cpu = dict((k, sum(v[-last_n:]) / len(v[-last_n:])) + for k, v in vms_cpu.items() + if vms_ram[k] == min_ram and len(v[-last_n:]) > 0) + values = min_ram_vms_cpu.values() + vm_index = values.index(max(values)) + vm = min_ram_vms_cpu.keys()[vm_index] + assert alg(vms_cpu, vms_ram) == ([vm], {}) + @qc(10) def minimum_migration_time( x=dict_( @@ -111,3 +158,48 @@ class Selection(TestCase): vm = x.keys()[random.randrange(len(x))] expect(selection).choice(x.keys()).and_return(vm).once() assert selection.random(x) == vm + + @qc + def minimum_migration_time_max_cpu( + x=dict_( + keys=str_(of='abc123-', min_length=36, max_length=36), + values=tuple_(list_(of=int_(min=0, max=3000), + min_length=1, max_length=10), + int_(min=0, max=3000)), + min_length=1, max_length=5 + ), + last_n=int_(min=1, max=10) + ): + vms_cpu = dict((k, v[0]) for k, v in x.items()) + vms_ram = dict((k, v[1]) for k, v in x.items()) + min_ram = min(vms_ram.values()) + min_ram_vms_cpu = dict((k, sum(v[-last_n:]) / len(v[-last_n:])) + for k, v in vms_cpu.items() + if vms_ram[k] == min_ram and len(v[-last_n:]) > 0) + values = min_ram_vms_cpu.values() + vm_index = values.index(max(values)) + vm = min_ram_vms_cpu.keys()[vm_index] + assert selection.minimum_migration_time_max_cpu( + last_n, vms_cpu, vms_ram) == vm + + @qc(1) + def minimum_migration_time_max_cpu_equal_ram( + vms_cpu=dict_( + keys=str_(of='abc123-', min_length=36, max_length=36), + values=list_(of=int_(min=0, max=3000), + min_length=1, max_length=10), + min_length=1, max_length=5 + ), + ram=int_(min=1000, max=3000), + last_n=int_(min=1, max=10) + ): + vms_ram = dict((k, ram) for k, _ in vms_cpu.items()) + min_ram = min(vms_ram.values()) + min_ram_vms_cpu = dict((k, sum(v[-last_n:]) / len(v[-last_n:])) + for k, v in vms_cpu.items() + if vms_ram[k] == min_ram and len(v[-last_n:]) > 0) + values = min_ram_vms_cpu.values() + vm_index = values.index(max(values)) + vm = min_ram_vms_cpu.keys()[vm_index] + assert selection.minimum_migration_time_max_cpu( + last_n, vms_cpu, vms_ram) == vm