|
38 | 38 | from fim.pluggable import PluggableRegistry, PluggableType
|
39 | 39 | from fim.slivers.attached_components import ComponentSliver, ComponentType
|
40 | 40 | from fim.slivers.base_sliver import BaseSliver
|
41 |
| -from fim.slivers.capacities_labels import Labels |
| 41 | +from fim.slivers.capacities_labels import Labels, Capacities |
42 | 42 | from fim.slivers.interface_info import InterfaceSliver, InterfaceType
|
43 | 43 | from fim.slivers.network_node import NodeSliver, NodeType
|
44 | 44 | from fim.slivers.network_service import NetworkServiceSliver, ServiceType, NSLayer
|
@@ -516,7 +516,6 @@ def __candidate_nodes(self, *, sliver: NodeSliver) -> List[str]:
|
516 | 516 | props=node_props,
|
517 | 517 | comps=sliver.attached_components_info)
|
518 | 518 |
|
519 |
| - # Skip nodes without any delegations which would be data-switch in this case |
520 | 519 | if sliver.get_type() == NodeType.Switch:
|
521 | 520 | exclude = []
|
522 | 521 | for n in result:
|
@@ -558,6 +557,45 @@ def __prune_nodes_in_maintenance(self, node_id_list: List[str], site: str, reser
|
558 | 557 |
|
559 | 558 | return node_id_list
|
560 | 559 |
|
| 560 | + def __reshuffle_nodes(self, node_id_list: List[str], node_id_to_reservations: dict, |
| 561 | + term: Term) -> List[str]: |
| 562 | + """ |
| 563 | + Reshuffles nodes based on their usage compared to a given threshold. |
| 564 | +
|
| 565 | + @param: node_id_list (list): List of node_ids |
| 566 | +
|
| 567 | + @return: list: Reshuffled list of nodes, with nodes exceeding the threshold shuffled separately. |
| 568 | + """ |
| 569 | + if len(node_id_list) == 1: |
| 570 | + return node_id_list |
| 571 | + |
| 572 | + enabled, threshold = self.get_core_capacity_threshold() |
| 573 | + if not enabled: |
| 574 | + return node_id_list |
| 575 | + |
| 576 | + # Separate nodes based on whether their usage exceeds the threshold |
| 577 | + above_threshold = [] |
| 578 | + below_threshold = [] |
| 579 | + |
| 580 | + for node_id in node_id_list: |
| 581 | + node, total, allocated = self.get_node_capacities(node_id=node_id, |
| 582 | + node_id_to_reservations=node_id_to_reservations, |
| 583 | + term=term) |
| 584 | + if total and allocated: |
| 585 | + self.logger.debug(f"Allocated: {allocated} Total: {total}") |
| 586 | + cpu_usage_percent = int(((allocated.core * 100)/ total.core)) |
| 587 | + self.logger.debug(f"CPU Usage for {node.get_name()}: {cpu_usage_percent}; " |
| 588 | + f"threshold: {threshold}") |
| 589 | + if cpu_usage_percent < threshold: |
| 590 | + below_threshold.append(node_id) |
| 591 | + else: |
| 592 | + above_threshold.append(node_id) |
| 593 | + |
| 594 | + # Combine both shuffled lists (you can choose the order of combining) |
| 595 | + reshuffled_nodes = below_threshold + above_threshold |
| 596 | + |
| 597 | + return reshuffled_nodes |
| 598 | + |
561 | 599 | def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dict, inv: NetworkNodeInventory,
|
562 | 600 | reservation: ABCBrokerReservation, term: Term, sliver: NodeSliver,
|
563 | 601 | operation: ReservationOperation = ReservationOperation.Create) -> Tuple[str, BaseSliver, Any]:
|
@@ -634,6 +672,13 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod
|
634 | 672 | node_id_list = self.__candidate_nodes(sliver=sliver)
|
635 | 673 | if self.get_algorithm_type(site=sliver.site) == BrokerAllocationAlgorithm.Random:
|
636 | 674 | random.shuffle(node_id_list)
|
| 675 | + else: |
| 676 | + # Reshuffle Nodes based on CPU Threshold only for VMs when no specific host is specified |
| 677 | + if sliver.get_type() == NodeType.VM and (sliver.labels is None or |
| 678 | + (sliver.labels and sliver.labels.instance_parent is None)): |
| 679 | + node_id_list = self.__reshuffle_nodes(node_id_list=node_id_list, |
| 680 | + node_id_to_reservations=node_id_to_reservations, |
| 681 | + term=term) |
637 | 682 |
|
638 | 683 | if len(node_id_list) == 0 and sliver.site not in self.combined_broker_model.get_sites():
|
639 | 684 | error_msg = f'Unknown site {sliver.site} requested for {reservation}'
|
@@ -1685,6 +1730,52 @@ def get_algorithm_type(self, site: str) -> BrokerAllocationAlgorithm:
|
1685 | 1730 | return BrokerAllocationAlgorithm.FirstFit
|
1686 | 1731 | return BrokerAllocationAlgorithm.FirstFit
|
1687 | 1732 |
|
| 1733 | + def get_core_capacity_threshold(self) -> Tuple[bool, int]: |
| 1734 | + if self.properties is not None: |
| 1735 | + core_capacity_threshold = self.properties.get(Constants.CORE_CAPACITY_THRESHOLD, None) |
| 1736 | + if core_capacity_threshold and core_capacity_threshold.get('enabled'): |
| 1737 | + core_usage_threshold_percent = core_capacity_threshold.get('core_usage_threshold_percent', 75) |
| 1738 | + return True, core_usage_threshold_percent |
| 1739 | + return False, 0 |
| 1740 | + |
| 1741 | + def get_node_capacities(self, node_id: str, node_id_to_reservations: dict, |
| 1742 | + term: Term) -> Tuple[NodeSliver, Capacities, Capacities]: |
| 1743 | + """ |
| 1744 | + Get Node capacities - total as well as allocated capacities |
| 1745 | + @param node_id: Node Id |
| 1746 | + @param node_id_to_reservations: Reservations assigned as part of this bid |
| 1747 | + @param term: Term |
| 1748 | + @return: Tuple containing node, total and allocated capacity |
| 1749 | + """ |
| 1750 | + try: |
| 1751 | + graph_node = self.get_network_node_from_graph(node_id=node_id) |
| 1752 | + existing_reservations = self.get_existing_reservations(node_id=node_id, |
| 1753 | + node_id_to_reservations=node_id_to_reservations, |
| 1754 | + start=term.get_start_time(), |
| 1755 | + end=term.get_end_time()) |
| 1756 | + |
| 1757 | + delegation_id, delegated_capacity = NetworkNodeInventory.get_delegations( |
| 1758 | + lab_cap_delegations=graph_node.get_capacity_delegations()) |
| 1759 | + |
| 1760 | + allocated_capacity = Capacities() |
| 1761 | + |
| 1762 | + if existing_reservations: |
| 1763 | + for reservation in existing_reservations: |
| 1764 | + # For Active or Ticketed or Ticketing reservations; reduce the counts from available |
| 1765 | + resource_sliver = None |
| 1766 | + if reservation.is_ticketing() and reservation.get_approved_resources() is not None: |
| 1767 | + resource_sliver = reservation.get_approved_resources().get_sliver() |
| 1768 | + |
| 1769 | + if (reservation.is_active() or reservation.is_ticketed()) and \ |
| 1770 | + reservation.get_resources() is not None: |
| 1771 | + resource_sliver = reservation.get_resources().get_sliver() |
| 1772 | + |
| 1773 | + if resource_sliver is not None and isinstance(resource_sliver, NodeSliver): |
| 1774 | + allocated_capacity += resource_sliver.get_capacity_allocations() |
| 1775 | + |
| 1776 | + return graph_node, delegated_capacity, allocated_capacity |
| 1777 | + except Exception as e: |
| 1778 | + self.logger.error(f"Failed to determine node capacities: {node_id}, error: {e}") |
1688 | 1779 |
|
1689 | 1780 | if __name__ == '__main__':
|
1690 | 1781 | policy = BrokerSimplerUnitsPolicy()
|
0 commit comments