Skip to content
p4_manager.py 199 KiB
Newer Older
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
P4Runtime manager.
"""

import enum
import os
import queue
import time
import logging
from collections import Counter, OrderedDict
from threading import Thread
from tabulate import tabulate
from p4.v1 import p4runtime_pb2
from p4.config.v1 import p4info_pb2

try:
    from .p4_client import P4RuntimeClient, P4RuntimeException,\
        P4RuntimeWriteException, WriteOperation, parse_p4runtime_error
    from .p4_context import P4RuntimeEntity, P4Type, Context
    from .p4_global_options import make_canonical_if_option_set
    from .p4_common import encode,\
        parse_resource_string_from_json, parse_resource_integer_from_json,\
        parse_resource_bytes_from_json, parse_match_operations_from_json,\
        parse_action_parameters_from_json, parse_integer_list_from_json
    from .p4_exception import UserError, InvalidP4InfoError
except ImportError:
    from p4_client import P4RuntimeClient, P4RuntimeException,\
        P4RuntimeWriteException, WriteOperation, parse_p4runtime_error
    from p4_context import P4RuntimeEntity, P4Type, Context
    from p4_global_options import make_canonical_if_option_set
    from p4_common import encode,\
        parse_resource_string_from_json, parse_resource_integer_from_json,\
        parse_resource_bytes_from_json, parse_match_operations_from_json,\
        parse_action_parameters_from_json, parse_integer_list_from_json
    from p4_exception import UserError, InvalidP4InfoError

# Logger instance
LOGGER = logging.getLogger(__name__)

# Global P4Runtime context
CONTEXT = Context()

# Global P4Runtime client
CLIENT = None

# Constant P4 entities
KEY_TABLE = "table"
KEY_ACTION = "action"
KEY_ACTION_PROFILE = "action_profile"
KEY_COUNTER = "counter"
KEY_DIR_COUNTER = "direct_counter"
KEY_METER = "meter"
KEY_DIR_METER = "direct_meter"
KEY_CTL_PKT_METADATA = "controller_packet_metadata"


def get_context():
    """
    Return P4 context.

    :return: context object
    """
    return CONTEXT


def get_client():
    """
    Return P4 client.

    :return: P4Runtime client object
    """
    return CLIENT


def get_api_version():
    """
    Get the supported P4Runtime API version.

    :return: API version
    """
    return CLIENT.api_version()


def get_table_type(table):
    """
    Assess the type of P4 table based upon the matching scheme.

    :param table: P4 table
    :return: P4 table type
    """
    for m_f in table.match_fields:
        if m_f.match_type == p4info_pb2.MatchField.EXACT:
            return p4info_pb2.MatchField.EXACT
        if m_f.match_type == p4info_pb2.MatchField.LPM:
            return p4info_pb2.MatchField.LPM
        if m_f.match_type == p4info_pb2.MatchField.TERNARY:
            return p4info_pb2.MatchField.TERNARY
        if m_f.match_type == p4info_pb2.MatchField.RANGE:
            return p4info_pb2.MatchField.RANGE
        if m_f.match_type == p4info_pb2.MatchField.OPTIONAL:
            return p4info_pb2.MatchField.OPTIONAL
    return None


def match_type_to_str(match_type):
    """
    Convert table match type to string.

    :param match_type: table match type object
    :return: table match type string
    """
    if match_type == p4info_pb2.MatchField.EXACT:
        return "Exact"
    if match_type == p4info_pb2.MatchField.LPM:
        return "LPM"
    if match_type == p4info_pb2.MatchField.TERNARY:
        return "Ternary"
    if match_type == p4info_pb2.MatchField.RANGE:
        return "Range"
    if match_type == p4info_pb2.MatchField.OPTIONAL:
        return "Optional"
    return None


def insert_table_entry_exact(
        table_name, match_map, action_name, action_params, metadata,
        cnt_pkt=-1, cnt_byte=-1):
    """
    Insert an entry into an exact match table.

    :param table_name: P4 table name
    :param match_map: Map of match operations
    :param action_name: Action name
    :param action_params: Map of action parameters
    :param metadata: table metadata
    :param cnt_pkt: packet count
    :param cnt_byte: byte count
    :return: inserted entry
    """
    assert match_map, "Table entry without match operations is not accepted"
    assert action_name, "Table entry without action is not accepted"

    table_entry = TableEntry(table_name)(action=action_name)

    for match_k, match_v in match_map.items():
        table_entry.match[match_k] = match_v

    for action_k, action_v in action_params.items():
        table_entry.action[action_k] = action_v

    if metadata:
        table_entry.metadata = metadata

    if cnt_pkt > 0:
        table_entry.counter_data.packet_count = cnt_pkt

    if cnt_byte > 0:
        table_entry.counter_data.byte_count = cnt_byte

    ex_msg = ""
    try:
        table_entry.insert()
        LOGGER.info("Inserted exact table entry: %s", table_entry)
    except P4RuntimeWriteException as ex:
        ex_msg = str(ex)
    except P4RuntimeException as ex:
        raise P4RuntimeException from ex

    # Table entry exists, needs to be modified
    if "ALREADY_EXISTS" in ex_msg:
        table_entry.modify()
        LOGGER.info("Updated exact table entry: %s", table_entry)

    return table_entry


def insert_table_entry_ternary(
        table_name, match_map, action_name, action_params, metadata,
        priority, cnt_pkt=-1, cnt_byte=-1):
    """
    Insert an entry into a ternary match table.

    :param table_name: P4 table name
    :param match_map: Map of match operations
    :param action_name: Action name
    :param action_params: Map of action parameters
Loading
Loading full blame…