Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

378 support for p4 switches #379

Merged
merged 3 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fabric_cf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = "1.6.2"
__version__ = "1.7.0b1"
__VERSION__ = __version__
17 changes: 12 additions & 5 deletions fabric_cf/actor/core/apis/abc_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,18 @@ def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, pr
def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None,
bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]:
"""
Retrieves the components.

@return list of components

@throws Exception in case of error
Returns components matching the search criteria
@param node_id: Worker Node ID to which components belong
@param states: list of states used to find reservations
@param rsv_type: type of reservations
@param component: component name
@param bdf: Component's PCI address
@param start: start time
@param end: end time

NOTE# For P4 switches; node_id=node+renc-p4-sw component=ip+192.168.11.8 bdf=p1

@return Dictionary with component name as the key and value as list of associated PCI addresses in use.
"""

@abstractmethod
Expand Down
41 changes: 23 additions & 18 deletions fabric_cf/actor/core/kernel/reservation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,24 +515,29 @@ def prepare_ticket(self, extend: bool = False):

if parent_res is not None and (parent_res.is_ticketed() or parent_res.is_active()):
node_sliver = parent_res.get_resources().get_sliver()
component = node_sliver.attached_components_info.get_device(name=value1)
graph_id, bqm_component_id = component.get_node_map()
graph_id, node_id = node_sliver.get_node_map()
ifs.set_node_map(node_map=(node_id, bqm_component_id))

# For shared NICs grab the MAC & VLAN from corresponding Interface Sliver
# maintained in the Parent Reservation Sliver
if component.get_type() == ComponentType.SharedNIC:
parent_res_ifs_sliver = FimHelper.get_site_interface_sliver(component=component,
local_name=ifs.get_labels().local_name)
parent_labs = parent_res_ifs_sliver.get_label_allocations()

if component.get_model() == Constants.OPENSTACK_VNIC_MODEL:
ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, bdf=parent_labs.bdf,
instance_parent=f"{parent_res.get_reservation_id()}-{node_sliver.get_name()}")
else:
ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, vlan=parent_labs.vlan,
bdf=parent_labs.bdf)
# P4 Switch
if node_sliver.get_type() == NodeType.Switch:
graph_id, node_id = node_sliver.get_node_map()
ifs.set_node_map(node_map=(str(NodeType.Switch), node_id))
else:
component = node_sliver.attached_components_info.get_device(name=value1)
graph_id, bqm_component_id = component.get_node_map()
graph_id, node_id = node_sliver.get_node_map()
ifs.set_node_map(node_map=(node_id, bqm_component_id))

# For shared NICs grab the MAC & VLAN from corresponding Interface Sliver
# maintained in the Parent Reservation Sliver
if component.get_type() == ComponentType.SharedNIC:
parent_res_ifs_sliver = FimHelper.get_site_interface_sliver(component=component,
local_name=ifs.get_labels().local_name)
parent_labs = parent_res_ifs_sliver.get_label_allocations()

if component.get_model() == Constants.OPENSTACK_VNIC_MODEL:
ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, bdf=parent_labs.bdf,
instance_parent=f"{parent_res.get_reservation_id()}-{node_sliver.get_name()}")
else:
ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, vlan=parent_labs.vlan,
bdf=parent_labs.bdf)

self.logger.trace(f"Updated Network Res# {self.get_reservation_id()} {sliver}")

Expand Down
7 changes: 4 additions & 3 deletions fabric_cf/actor/core/manage/actor_management_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,10 +459,11 @@ def get_reservations(self, *, caller: AuthToken, states: List[int] = None,
if res_list is not None:
result.reservations = []
for r in res_list:
slice_id = r.get_slice_id()
slice_obj = self.get_slice_by_guid(guid=slice_id)
r_slice_id = r.get_slice_id()
slice_obj = self.get_slice_by_guid(guid=r_slice_id)
r.restore(actor=self.actor, slice_obj=slice_obj)
rr = Converter.fill_reservation(reservation=r, full=True)
full = True if slice_id or rid else False
rr = Converter.fill_reservation(reservation=r, full=full)
result.reservations.append(rr)
except ReservationNotFoundException as e:
self.logger.error("getReservations: {}".format(e))
Expand Down
46 changes: 29 additions & 17 deletions fabric_cf/actor/core/policy/broker_simpler_units_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,8 @@ def __candidate_nodes(self, *, sliver: NodeSliver) -> List[str]:

node_props = {ABCPropertyGraphConstants.PROP_SITE: sliver.site,
ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Server)}
if sliver.get_type() == NodeType.Switch:
node_props[ABCPropertyGraphConstants.PROP_TYPE] = str(NodeType.Switch)

storage_components = []
# remove storage components before the check
Expand All @@ -508,6 +510,16 @@ def __candidate_nodes(self, *, sliver: NodeSliver) -> List[str]:
label=ABCPropertyGraphConstants.CLASS_NetworkNode,
props=node_props,
comps=sliver.attached_components_info)

# Skip nodes without any delegations which would be data-switch in this case
if sliver.get_type() == NodeType.Switch:
exclude = []
for n in result:
if "p4" not in n:
exclude.append(n)
for e in exclude:
result.remove(e)

# re-add storage components
if len(storage_components) > 0:
for c in storage_components:
Expand Down Expand Up @@ -652,7 +664,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver:
error_msg = None
owner_ns = None
owner_ns_id = None
bqm_component = None
bqm_node = None
is_vnic = False
owner_mpls_ns = None
owner_switch = None
Expand All @@ -664,29 +676,31 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver:
node_map_id = self.combined_broker_model_graph_id

# Fetch Network Node Id and BQM Component Id
node_id, bqm_component_id = ifs.get_node_map()
node_id, bqm_node_id = ifs.get_node_map()

# Skipping the already allocated interface on a modify
#if node_id == self.combined_broker_model_graph_id:
if self.combined_broker_model_graph_id in node_id:
continue

if node_id == str(NodeType.Facility):
bqm_component = self.get_facility_sliver(node_name=bqm_component_id)
bqm_node = self.get_facility_sliver(node_name=bqm_node_id)
# Peered Interfaces are handled at the end
elif node_id == str(Constants.PEERED):
peered_ns_interfaces.append(ifs)
continue
elif node_id == str(NodeType.Switch):
bqm_node = self.get_network_node_from_graph(node_id=bqm_node_id)
node_map_id = f"{node_map_id}:{bqm_node.get_name()}:{bqm_node_id}:{ifs.get_labels().local_name}"
else:
# For VM interfaces
bqm_component = self.get_component_sliver(node_id=bqm_component_id)
node_map_id = f"{node_map_id}:{node_id}:{bqm_component_id}:{ifs.get_labels().bdf}"
bqm_node = self.get_component_sliver(node_id=bqm_node_id)
node_map_id = f"{node_map_id}:{node_id}:{bqm_node_id}:{ifs.get_labels().bdf}"

if bqm_component is None:
if bqm_node is None:
raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES)

# Get BQM Connection Point in Site Delegation (c)
site_cp = FimHelper.get_site_interface_sliver(component=bqm_component,
site_cp = FimHelper.get_site_interface_sliver(component=bqm_node,
local_name=ifs.get_labels().local_name,
region=ifs.get_labels().region,
device_name=ifs.get_labels().device_name)
Expand All @@ -712,13 +726,13 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver:
owner_ns_id = owner_ns_id.replace('ipv6ext-ns', 'ipv6-ns')

bqm_cp = net_cp
if bqm_component.get_type() == NodeType.Facility or \
if bqm_node.get_type() == NodeType.Facility or \
(sliver.get_type() == ServiceType.L2Bridge and
bqm_component.get_model() == Constants.OPENSTACK_VNIC_MODEL):
bqm_node.get_model() == Constants.OPENSTACK_VNIC_MODEL):
bqm_cp = site_cp

if bqm_component.get_type() == ComponentType.SharedNIC:
if bqm_component.get_model() == Constants.OPENSTACK_VNIC_MODEL:
if bqm_node.get_type() == ComponentType.SharedNIC:
if bqm_node.get_model() == Constants.OPENSTACK_VNIC_MODEL:
is_vnic = True

# VLAN is already set by the Orchestrator using the information from the Node Sliver Parent Reservation
Expand Down Expand Up @@ -754,15 +768,15 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver:
# Set the NSO device-name
ifs_labels = Labels.update(ifs_labels, device_name=device_name)
adm_ids = owner_switch.get_structural_info().adm_graph_ids
site_adm_ids = bqm_component.get_structural_info().adm_graph_ids
site_adm_ids = bqm_node.get_structural_info().adm_graph_ids

self.logger.debug(f"Owner Network Service: {owner_ns}")
self.logger.debug(f"Owner Switch: {owner_switch}")
if owner_switch.network_service_info is not None:
self.logger.debug(f"Owner Switch NS: {owner_switch.network_service_info.network_services.values()}")

net_adm_ids = site_adm_ids
if bqm_component.get_type() != NodeType.Facility and not is_vnic:
if bqm_node.get_type() != NodeType.Facility and not is_vnic:
net_adm_ids = [x for x in adm_ids if not x in site_adm_ids or site_adm_ids.remove(x)]
# For sites like EDC which share switch with other sites like NCSA,
# the net_adm_ids also includes delegation id from the other side,
Expand Down Expand Up @@ -807,7 +821,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver:

# Allocate VLAN for the Network Service
if is_vnic:
site_adm_ids = bqm_component.get_structural_info().adm_graph_ids
site_adm_ids = bqm_node.get_structural_info().adm_graph_ids
delegation_id = site_adm_ids[0]
inv.allocate_vnic(rid=rid, requested_ns=sliver, owner_ns=owner_ns,
existing_reservations=existing_reservations)
Expand Down Expand Up @@ -903,8 +917,6 @@ def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryF
# intended link (and possibly interfaces connected to it)

res_sliver = rset.get_sliver()
delegation_id = None
sliver = None

if isinstance(res_sliver, NodeSliver):
delegation_id, sliver, error_msg = self.__allocate_nodes(reservation=reservation, inv=inv,
Expand Down
1 change: 0 additions & 1 deletion fabric_cf/actor/core/policy/network_node_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,6 @@ def assign(self, *, reservation: ABCAuthorityReservation, delegation_name: str,
properties=reservation.get_slice().get_config_properties())
gained = UnitSet(plugin=self.authority.get_plugin(), units={unit.reservation_id: unit})
else:
# FIX ME: handle modify
self.logger.info(f"Extend Lease for now, no modify supported res# {reservation}")
current_sliver = current.get_sliver()
diff = current_sliver.diff(other_sliver=requested)
Expand Down
73 changes: 68 additions & 5 deletions fabric_cf/actor/core/policy/network_node_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from fim.slivers.delegations import Delegations
from fim.slivers.instance_catalog import InstanceCatalog
from fim.slivers.interface_info import InterfaceSliver
from fim.slivers.network_node import NodeSliver
from fim.slivers.network_node import NodeSliver, NodeType
from fim.slivers.network_service import NSLayer

from fabric_cf.actor.core.apis.abc_reservation_mixin import ABCReservationMixin
Expand Down Expand Up @@ -476,6 +476,60 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent

return requested_components

def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_id: str, graph_node: NodeSliver,
existing_reservations: List[ABCReservationMixin], existing_components: Dict[str, List[str]],
is_create: bool = False) -> Tuple[str, BaseSliver]:
"""
Allocate an extending or ticketing reservation for a P4 switch

:param rid: reservation id of the reservation to be allocated
:param requested_sliver: requested sliver
:param graph_id: BQM graph id
:param graph_node: BQM graph node identified to serve the reservation
:param existing_components: Existing Components
:param existing_reservations: Existing Reservations served by the same BQM node
:param is_create: Indicates if this is create or modify

:return: Tuple of Delegation Id and the Requested Sliver annotated with BQM Node Id and other properties
:raises: BrokerException in case the request cannot be satisfied
"""
delegation_id = None

if not is_create:
# In case of modify, directly get delegation_id
if len(graph_node.get_capacity_delegations().get_delegation_ids()) > 0:
delegation_id = next(iter(graph_node.get_capacity_delegations().get_delegation_ids()))

# Nothing to do, just return
return delegation_id, requested_sliver

# Handle allocation to account for leaked Network Services
for n in existing_components.keys():
if n in graph_node.node_id:
raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES,
msg=f"Node of type: {graph_node.get_type()} not available on site: "
f"{graph_node.get_site()}, already in use by another reservation")

# For create, we need to allocate the P4
requested_capacities = requested_sliver.get_capacities()

# Check if Capacities can be satisfied
delegation_id = self.__check_capacities(rid=rid,
requested_capacities=requested_capacities,
delegated_capacities=graph_node.get_capacity_delegations(),
existing_reservations=existing_reservations)
requested_sliver.capacity_allocations = Capacities()
requested_sliver.capacity_allocations = Capacities.update(lab=requested_capacities)
requested_sliver.label_allocations = Labels(local_name=graph_node.get_name())

requested_sliver.set_node_map(node_map=(graph_id, graph_node.node_id))
requested_sliver.management_ip = graph_node.management_ip

self.logger.info(f"Reservation# {rid} is being served by delegation# {delegation_id} "
f"node# [{graph_id}/{graph_node.node_id}]")

return delegation_id, requested_sliver

def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, graph_node: BaseSliver,
existing_reservations: List[ABCReservationMixin], existing_components: Dict[str, List[str]],
is_create: bool = False) -> Tuple[str, BaseSliver]:
Expand All @@ -492,17 +546,26 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap
:raises: BrokerException in case the request cannot be satisfied
"""
if graph_node.get_capacity_delegations() is None or rid is None:
raise BrokerException(error_code=Constants.INVALID_ARGUMENT,
raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT,
msg=f"capacity_delegations is missing or reservation is None")

if not isinstance(requested_sliver, NodeSliver):
raise BrokerException(error_code=Constants.INVALID_ARGUMENT,
raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT,
msg=f"resource type: {requested_sliver.get_type()}")

if not isinstance(graph_node, NodeSliver):
raise BrokerException(error_code=Constants.INVALID_ARGUMENT,
raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT,
msg=f"resource type: {graph_node.get_type()}")

if requested_sliver.get_type() not in [NodeType.VM, NodeType.Switch]:
raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT,
msg=f"Unsupported resource type: {graph_node.get_type()}")

if requested_sliver.get_type() == NodeType.Switch:
return self.__allocate_p4_switch(rid=rid, requested_sliver=requested_sliver, graph_id=graph_id,
graph_node=graph_node, existing_reservations=existing_reservations,
existing_components=existing_components, is_create=is_create)

delegation_id = None
requested_capacities = None
# For create, we need to allocate the VM
Expand Down Expand Up @@ -547,4 +610,4 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap
return delegation_id, requested_sliver

def free(self, *, count: int, request: dict = None, resource: dict = None) -> dict:
return
pass
14 changes: 14 additions & 0 deletions fabric_cf/actor/db/psql_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,20 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p

def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None,
bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]:
"""
Returns components matching the search criteria
@param node_id: Worker Node ID to which components belong
@param states: list of states used to find reservations
@param rsv_type: type of reservations
@param component: component name
@param bdf: Component's PCI address
@param start: start time
@param end: end time

NOTE# For P4 switches; node_id=node+renc-p4-sw component=ip+192.168.11.8 bdf=p1

@return Dictionary with component name as the key and value as list of associated PCI addresses in use.
"""
result = {}
session = self.get_session()
try:
Expand Down
Loading