Skip to content

Commit

Permalink
Merge pull request #379 from fabric-testbed/378-support-for-p4-switches
Browse files Browse the repository at this point in the history
378 support for p4 switches
  • Loading branch information
kthare10 authored May 13, 2024
2 parents 428ffe5 + 3b7438a commit 7e4627c
Show file tree
Hide file tree
Showing 21 changed files with 239 additions and 77 deletions.
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

0 comments on commit 7e4627c

Please sign in to comment.