Source code for eclypse.placement.strategies.first_fit
"""Module for a First Fit placement strategy.
It overrides the `place` method of the
PlacementStrategy class to place services of an application on infrastructure nodes
based on the first node that satisfies the requirements of the service.
"""
from __future__ import annotations
import random as rnd
from typing import (
TYPE_CHECKING,
Any,
)
from .strategy import PlacementStrategy
if TYPE_CHECKING:
from collections.abc import (
Callable,
)
from eclypse.graph import (
Application,
Infrastructure,
)
from eclypse.placement import (
Placement,
PlacementView,
)
[docs]
class FirstFitStrategy(PlacementStrategy):
"""FirstFitStrategy class.
A placement strategy that places services onto the first node that satisfies the
requirements.
"""
[docs]
def __init__(self, sort_fn: Callable[[Any], Any] | None = None):
"""Initializes the FirstFit placement strategy.
Args:
sort_fn (Callable[[Any], Any] | None, optional): A function to sort \
the infrastructure nodes. Defaults to None.
"""
self.sort_fn = sort_fn
super().__init__()
[docs]
def place(
self,
infrastructure: Infrastructure,
application: Application,
_: dict[str, Placement],
placement_view: PlacementView,
) -> dict[str, str]:
"""Performs the placement according to a first-fit logic.
Places the services of an application on the infrastructure nodes based on
the first node that satisfies the requirements of the service.
Args:
infrastructure (Infrastructure):
The infrastructure to place the application on.
application (Application): The application to place on the infrastructure.
_ (dict[str, Placement]):
The placement of all the applications in the simulations.
placement_view (PlacementView):
The snapshot of the current state of the infrastructure.
Returns:
dict[str, str]: A mapping of services to infrastructure nodes.
"""
if not self.is_feasible(infrastructure, application):
return {}
mapping = {}
infrastructure_nodes = list(placement_view.residual.nodes(data=True))
if self.sort_fn:
infrastructure_nodes.sort(key=self.sort_fn)
else:
rnd.shuffle(infrastructure_nodes)
for service, sattr in application.nodes(data=True):
for idx, (node, nattr) in enumerate(infrastructure_nodes):
if infrastructure.node_assets.satisfies(nattr, sattr):
mapping[service] = node
infrastructure_nodes[idx] = (
node,
infrastructure.node_assets.consume(nattr, sattr),
)
break
return mapping