Пример #1
0
def greedy_least_batch():
    """ Maintenance strategy proposed in [1]. It is designed to
    minimize the number of maintenance steps necessary to update
    the data center.

    References
    ==========
    [1] Zheng, Zeyu, et al. "Least maintenance batch scheduling in cloud
    data center networks." IEEE communications letters 18.6 (2014): 901-904.
    """

    # Patching servers nonupdated servers that are not hosting VMs
    servers_to_patch = Server.ready_to_patch()
    if len(servers_to_patch) > 0:
        servers_patch_duration = []

        for server in servers_to_patch:
            patch_duration = server.update()
            servers_patch_duration.append(patch_duration)

        # As servers are updated simultaneously, we don't need to call the function
        # that quantifies the server maintenance duration for each server being patched
        yield SimulationEnvironment.first().env.timeout(max(servers_patch_duration))


    # Migrating VMs
    else:
        servers_being_emptied = []

        # Sorts the servers to empty based on their occupation rate (ascending)
        servers_to_empty = sorted(Server.nonupdated(), key=lambda sv: sv.occupation_rate())

        for server in servers_to_empty:
            # We consider as candidate hosts for the VMs every server
            # not being emptied in the current iteration
            candidate_servers = [cand_server for cand_server in Server.all()
                if cand_server not in servers_being_emptied and cand_server != server]

            vms = [vm for vm in server.virtual_machines]

            if Server.can_host_vms(candidate_servers, vms):
                for _ in range(len(server.virtual_machines)):
                    vm = vms.pop(0)

                    # Sorting servers by update status (updated ones first) and demand (more occupied ones first)
                    candidate_servers = sorted(candidate_servers, key=lambda cand_server:
                        (-cand_server.updated, -cand_server.occupation_rate()))

                    # Using a First-Fit strategy to select a candidate host for each VM
                    for cand_server in candidate_servers:
                        if cand_server.has_capacity_to_host(vm):
                            yield SimulationEnvironment.first().env.timeout(vm.migrate(cand_server))
                            break

            if len(server.virtual_machines) == 0:
                servers_being_emptied.append(server)
    def run(self, tasks):
        """ Triggers the set of events that ocurr during the simulation.
        """

        # The simulation goes on until all servers got the update
        while len(Server.nonupdated()) > 0:
            #########################################
            ## Running a set of user-defined tasks ##
            #########################################
            yield self.env.process(tasks())

            ####################################################################################
            ## Collecting simulation metrics for the current step and moving to the next step ##
            ####################################################################################
            self.collect_metrics()
            self.maintenance_step += 1
Пример #3
0
def worst_fit_like():
    """
    Worst-Fit-like maintenance strategy (presented by Severo et al.)
    ====================================================================
    Note: We use the term "empty" to refer to servers that are not hosting VMs.

    The maintenance process is divided in two tasks:
    (i) Patching empty servers (lines 25-35)
    (ii) Migrating VMs to empty more servers (lines 40-72)

    When choosing which servers will host the VMs, this
    strategy uses the Worst-Fit Decreasing heuristic.
    """

    # Patching servers nonupdated servers that are not hosting VMs
    servers_to_patch = Server.ready_to_patch()
    if len(servers_to_patch) > 0:
        servers_patch_duration = []

        for server in servers_to_patch:
            patch_duration = server.update()
            servers_patch_duration.append(patch_duration)

        # As servers are updated simultaneously, we don't need to call the function
        # that quantifies the server maintenance duration for each server being patched
        yield SimulationEnvironment.first().env.timeout(
            max(servers_patch_duration))

    # (ii) Migrating VMs to empty more servers
    else:
        servers_being_emptied = []

        # Getting the list of servers that still need to receive the patch
        servers_to_empty = Server.nonupdated()

        for server in servers_to_empty:
            # We consider as candidate hosts for the VMs all Server
            # objects not being emptied in the current maintenance step
            candidate_servers = [
                cand_server for cand_server in Server.all()
                if cand_server not in servers_being_emptied
                and cand_server != server
            ]

            # Sorting VMs by its demand (decreasing)
            vms = [vm for vm in server.virtual_machines]
            vms = sorted(vms, key=lambda vm: -vm.demand())

            for _ in range(len(server.virtual_machines)):
                vm = vms.pop(0)

                # Sorting servers (bins) to align with Worst-Fit's idea,
                # which is prioritizing servers with less space remaining
                candidate_servers = sorted(
                    candidate_servers, key=lambda cand: cand.occupation_rate())

                # Migrating VMs using the Worst-Fit heuristic
                for cand_server in candidate_servers:
                    if cand_server.has_capacity_to_host(vm):
                        # Migrating the VM and storing the migration duration to allow future analysis
                        yield SimulationEnvironment.first().env.timeout(
                            vm.migrate(cand_server))
                        break

            if len(server.virtual_machines) == 0:
                servers_being_emptied.append(server)
Пример #4
0
def vulnerability_surface(env, maintenance_data):
    """
    Maintenance strategy proposed by Severo et al. 2020
    ===================================================
    Note: We use the term "empty" to refer to servers that are not hosting VMs

    When choosing which servers to empty, this heuristic prioritizes servers that
    take less time to be emptied, which can be achieved by having a small number
    of VMs or by hosting small VMs (that will take a negligible time to be migrated).

    The maintenance process is divided in two tasks:
    (i) Patching empty servers (lines 31-38)
    (ii) Migrating VMs to empty more servers (lines 42-90)

    Parameters
    ==========
    env : SimPy.Environment
        Used to quantity the amount of simulation time spent by the migration

    maintenance_data : List
        Object that will be filled during the maintenance, storing metrics on each maintenance step
    """

    while len(Server.nonupdated()) > 0:
        # Patching servers nonupdated servers that are not hosting VMs
        servers_to_patch = Server.ready_to_patch()
        if len(servers_to_patch) > 0:
            for server in servers_to_patch:
                server.updated = True

            # As servers are updated simultaneously, we don't need to call the function
            # that quantifies the server maintenance duration for each server being patched
            yield env.process(server_update(env, constants.PATCHING_TIME))

        # Migrating VMs

        servers_being_emptied = []

        # Sorts the servers to empty based on its update score. This score considers
        # the amount of time needed to migrate all VMs hosted by the server
        servers_to_empty = sorted(
            Server.nonupdated(),
            key=lambda cand_server: cand_server.update_cost())

        migrations_data = [
        ]  # Stores data on the migrations performed to allow future analysis

        for server in servers_to_empty:

            vms_to_migrate = len(server.virtual_machines)
            servers_checked = 0

            # We consider as candidate hosts for the VMs every server
            # not being emptied in the current iteration
            candidate_servers = [
                cand_server for cand_server in Server.all()
                if cand_server not in servers_being_emptied
                and cand_server != server
            ]

            while len(server.virtual_machines
                      ) > 0 and servers_checked < vms_to_migrate * len(
                          candidate_servers):
                # Sorting VMs by its demand (decreasing)
                vms = sorted(
                    server.virtual_machines,
                    key=lambda vm:
                    (-vm.cpu_demand, -vm.memory_demand, -vm.disk_demand))

                vm = server.virtual_machines[0]

                # Sorting servers by update status (updated ones first) and demand (decreasing)
                candidate_servers = sorted(
                    candidate_servers,
                    key=lambda cand_server:
                    (-cand_server.updated, -cand_server.cpu_demand,
                     -cand_server.memory_demand, -cand_server.disk_demand))

                if Server.can_host_vms(candidate_servers, vms):
                    # Using a First-Fit Decreasing strategy to select a candidate host for each VM
                    for cand_server in candidate_servers:
                        servers_checked += 1
                        if cand_server.has_capacity_to_host(vm):

                            # Migrating the VM and storing the migration duration to allow future analysis
                            migration_duration = yield env.process(
                                vm.migrate(env, cand_server))

                            migrations_data.append({
                                'origin':
                                server,
                                'destination':
                                cand_server,
                                'vm':
                                vm,
                                'duration':
                                migration_duration
                            })

                            break
                else:
                    break

            if len(server.virtual_machines) == 0:
                servers_being_emptied.append(server)

        # Collecting metrics gathered in the current maintenance step (i.e., outer while loop iteration)
        maintenance_data.append(
            collect_metrics(env, 'VS Heuristic', servers_to_patch,
                            servers_being_emptied, migrations_data))
Пример #5
0
def first_fit(env, maintenance_data):
    """
    First-Fit like maintenance strategy (presented by Severo et al. 2020)
    ====================================================================
    Note: We use the term "empty" to refer to servers that are not hosting VMs.

    The maintenance process is divided in two tasks:
    (i) Patching empty servers (lines 29-36)
    (ii) Migrating VMs to empty more servers (lines 40-73)

    When choosing which servers will host the VMs, this strategy uses the First-Fit heuristic.

    Parameters
    ==========
    env : SimPy.Environment
        Used to quantity the amount of simulation time spent by the migration

    maintenance_data : List
        Object that will be filled during the maintenance, storing metrics on each maintenance step
    """

    while len(Server.nonupdated()) > 0:
        # (i) Patching servers nonupdated servers that are not hosting VMs (lines 30-37)
        servers_to_patch = Server.ready_to_patch()
        if len(servers_to_patch) > 0:
            for server in servers_to_patch:
                server.updated = True

            # As servers are updated simultaneously, we don't need to call the function
            # that quantifies the server maintenance duration for each server being patched
            yield env.process(server_update(env, constants.PATCHING_TIME))

        # (ii) Migrating VMs to empty more servers (lines 41-74)

        servers_being_emptied = []

        # Getting the list of servers that still need to receive the patch
        servers_to_empty = Server.nonupdated()
        migrations_data = [
        ]  # Stores data on the migrations performed to allow future analysis

        for server in servers_to_empty:

            candidate_servers = [
                cand_server for cand_server in Server.all()
                if cand_server != server
                and cand_server not in servers_being_emptied
            ]

            vms_to_migrate = len(server.virtual_machines)
            servers_checked = 0

            while len(server.virtual_machines
                      ) > 0 and servers_checked <= vms_to_migrate * len(
                          candidate_servers):

                vm = server.virtual_machines[0]

                # Migrating VMs using the First-Fit heuristic, which suggests the
                # migration of VMs to the first server that has resources to host it
                for cand_server in candidate_servers:
                    servers_checked += 1
                    if cand_server.has_capacity_to_host(vm):

                        # Migrating the VM and storing the migration duration to allow future analysis
                        migration_duration = yield env.process(
                            vm.migrate(env, cand_server))

                        migrations_data.append({
                            'origin': server,
                            'destination': cand_server,
                            'vm': vm,
                            'duration': migration_duration
                        })

                        break

            if len(server.virtual_machines) == 0:
                servers_being_emptied.append(server)

        # Collecting metrics gathered in the current maintenance step (i.e., outer while loop iteration)
        maintenance_data.append(
            collect_metrics(env, 'First-Fit', servers_to_patch,
                            servers_being_emptied, migrations_data))
Пример #6
0
def collect_metrics(env, strategy, servers_patched, servers_being_emptied,
                    migrations_data):
    """ Gather metrics from the current maintenance step.

    Supported metrics:
        - Simulation steps
        - Number of servers being updated
        - Number of servers being emptied
        - Number of updated servers
        - Number of nonupdated servers
        - Vulnerability Surface (Severo et al. 2020)
        - Number of VM migrations
        - Overall migrations duration (amount of time spent with migrations in the current step)
        - Longer migration
        - Shorter migration
        - Average migration duration
        - Servers occupation rate
        - Servers consolidation rate

    Parameters
    ==========
    env : SimPy.Environment
        Used to quantity the amount of simulation time spent by the migration

    strategy : String
        Name of the used maintenance strategy

    servers_patched : List
        List of servers updated in the current maintenance step

    servers_being_emptied : List
        List of servers being emptied in the current maintenance step

    migrations_data : List
        Information on each migration performed in the current maintenance step

    Returns
    =======
    output : Dictionary
        List of metrics collected during the current maintenance step
    """

    output = {}

    # Number of simulation steps
    output['simulation_steps'] = env.now

    # Name of the used maintenance strategy
    output['strategy'] = strategy

    # Other simulation metrics
    output['metrics'] = {}

    # Number of updated and nonupdated servers
    output['metrics']['updated_servers'] = len(Server.updated())
    output['metrics']['nonupdated_servers'] = len(Server.nonupdated())

    # Vulnerability Surface (Severo et al. 2020) = Number of non-updated servers * Elapsed time
    output['metrics']['vulnerability_surface'] = env.now * output['metrics'][
        'nonupdated_servers']

    # Gathering VM migration metrics
    output['metrics']['vm_migrations'] = 0
    output['metrics']['migrations_duration'] = 0
    output['metrics']['longer_migration'] = 0
    output['metrics']['shorter_migration'] = 0
    output['metrics']['avg_migration_duration'] = 0

    if len(migrations_data) > 0:
        # Number of VM migrations performed in this interval
        output['metrics']['vm_migrations'] = len(migrations_data)

        # Time spent performing VM migrations
        migrations_duration = sum(migr['duration'] for migr in migrations_data)
        output['metrics']['migrations_duration'] = migrations_duration

        # Longer migration
        output['metrics']['longer_migration'] = max(
            migr['duration'] for migr in migrations_data)

        # Shorter migration
        output['metrics']['shorter_migration'] = min(
            migr['duration'] for migr in migrations_data)

        # Average migration duration
        output['metrics'][
            'avg_migration_duration'] = migrations_duration / len(
                migrations_data)

    # Gathering server-related metrics
    # Occupation rate
    aggregated_occupation_rate = sum(sv.occupation_rate()
                                     for sv in Server.all())
    output['metrics']['occupation_rate'] = aggregated_occupation_rate / len(
        Server.used_servers())

    # Consolidation rate
    output['metrics']['consolidation_rate'] = Server.consolidation_rate()

    # Servers being updated
    output['metrics']['servers_being_updated'] = len(servers_patched)
    # Servers being emptied
    output['metrics']['servers_being_emptied'] = len(servers_being_emptied)

    return (output)
Пример #7
0
def salus():
    """ Salus is the Roman goddess of safety. This maintenance
    strategy was proposed by Severo et al. in 2020.
    
    Note: We use the term "empty" to refer to servers that are not hosting VMs

    When choosing which servers to empty, this heuristic prioritizes servers with
    a smaller update cost, which takes into account multiple factors such as server
    capacity and server's patch duration.

    The maintenance process is divided in two tasks:
    (i) Patching empty servers (lines 28-36)
    (ii) Migrating VMs to empty more servers (lines 41-74)
    """

    # Patching servers nonupdated servers that are not hosting VMs
    servers_to_patch = Server.ready_to_patch()
    if len(servers_to_patch) > 0:
        servers_patch_duration = []

        for server in servers_to_patch:
            patch_duration = server.update()
            servers_patch_duration.append(patch_duration)

        # As servers are updated simultaneously, we don't need to call the function
        # that quantifies the server maintenance duration for each server being patched
        yield SimulationEnvironment.first().env.timeout(max(servers_patch_duration))


    # Migrating VMs
    else:
        servers_being_emptied = []

        # Sorts the servers to empty based on its update score. This score considers the amount of time
        # needed to update the server (including VM migrations to draining the server) and its capacity
        servers_to_empty = sorted(Server.nonupdated(), key=lambda sv:
            (sv.maintenance_duration() * (1/(sv.capacity()+1))) ** (1/2))


        for server in servers_to_empty:
            # We consider as candidate hosts for the VMs all Server
            # objects not being emptied in the current maintenance step
            candidate_servers = [cand_server for cand_server in Server.all()
                if cand_server not in servers_being_emptied and cand_server != server]

            # Sorting VMs by its demand (decreasing)
            vms = [vm for vm in server.virtual_machines]
            vms = sorted(vms, key=lambda vm: -vm.demand())

            if Server.can_host_vms(candidate_servers, vms):
                for _ in range(len(server.virtual_machines)):
                    vm = vms.pop(0)

                    # Sorting servers by update status (updated ones first) and demand (decreasing)
                    candidate_servers = sorted(candidate_servers, key=lambda sv:
                        (-sv.updated, -sv.occupation_rate()))

                    # Using a Best-Fit Decreasing strategy to select a candidate host for each VM
                    for cand_server in candidate_servers:
                        if cand_server.has_capacity_to_host(vm):
                            yield SimulationEnvironment.first().env.timeout(vm.migrate(cand_server))
                            break

            if len(server.virtual_machines) == 0:
                servers_being_emptied.append(server)