Merge "[SDFAB-502] Add P4Runtime CLI-based driver and UP4 library"
diff --git a/TestON/drivers/common/cli/onosclusterdriver.py b/TestON/drivers/common/cli/onosclusterdriver.py
index eebc5ad..8b25d22 100755
--- a/TestON/drivers/common/cli/onosclusterdriver.py
+++ b/TestON/drivers/common/cli/onosclusterdriver.py
@@ -54,7 +54,7 @@
name. This method should return the (computed) attribute value
or raise an AttributeError exception.
- We will look into each of the node's component handles to try to find the attreibute, looking at REST first
+ We will look into each of the node's component handles to try to find the attribute, looking at REST first
"""
if hasattr( self.REST, name ):
main.log.debug( "%s: Using Rest driver's attribute for '%s'" % ( self.name, name ) )
@@ -65,10 +65,13 @@
if hasattr( self.Bench, name ):
main.log.debug( "%s: Using Bench driver's attribute for '%s'" % ( self.name, name ) )
return getattr( self.Bench, name )
+ if hasattr( self.p4rtUp4, name ):
+ main.log.debug( "%s: Using UP4 driver's attribute for '%s'" % ( self.name, name ) )
+ return getattr( self.p4rtUp4, name )
raise AttributeError( "Could not find the attribute %s in %r or it's component handles" % ( name, self ) )
def __init__( self, name, ipAddress, CLI=None, REST=None, Bench=None, pos=None,
- userName=None, server=None, k8s=None, dockerPrompt=None ):
+ userName=None, server=None, k8s=None, p4rtUp4=None, dockerPrompt=None ):
# TODO: validate these arguments
self.name = str( name )
self.ipAddress = ipAddress
@@ -81,6 +84,7 @@
self.user_name = userName
self.server = server
self.k8s = k8s
+ self.p4rtUp4 = p4rtUp4
self.dockerPrompt = dockerPrompt
class OnosClusterDriver( CLI ):
@@ -101,6 +105,7 @@
self.nodeUser = None
self.nodePass = None
self.nodes = []
+ self.up4Port = None
super( OnosClusterDriver, self ).__init__()
def connect( self, **connectargs ):
@@ -145,6 +150,9 @@
self.maxNodes = self.options[ key ]
elif key == "kubeConfig":
self.kubeConfig = self.options[ key ]
+ elif key == "up4_port":
+ # Defining up4_port triggers the creation of the P4RuntimeCliDriver component
+ self.up4Port = self.options[ key ]
self.home = self.checkOptions( self.home, "~/onos" )
self.karafUser = self.checkOptions( self.karafUser, self.user_name )
@@ -160,6 +168,7 @@
self.dockerPrompt = self.checkOptions( self.dockerPrompt, "~/onos#" )
self.maxNodes = int( self.checkOptions( self.maxNodes, 100 ) )
self.kubeConfig = self.checkOptions( self.kubeConfig, None )
+ self.up4Port = self.checkOptions(self.up4Port, None)
self.name = self.options[ 'name' ]
@@ -243,14 +252,19 @@
# Setup port-forwarding and save the local port
guiPort = 8181
cliPort = 8101
+ fwdPorts = [ guiPort, cliPort ]
+ if self.up4Port:
+ fwdPorts.append( int( self.up4Port ) )
portsList = ""
- for port in [ guiPort, cliPort ]:
+ for port in fwdPorts:
localPort = port + index + 1
portsList += "%s:%s " % ( localPort, port )
if port == cliPort:
node.CLI.karafPort = localPort
elif port == guiPort:
node.REST.port = localPort
+ elif self.up4Port and port == int( self.up4Port ):
+ node.p4rtUp4.p4rtPort = localPort
main.log.info( "Setting up port forward for pod %s: [ %s ]" % ( self.podNames[ index ], portsList ) )
pf = kubectl.kubectlPortForward( self.podNames[ index ],
portsList,
@@ -466,6 +480,51 @@
main.log.error( name + " component already exists!" )
main.cleanAndExit()
+ def setP4rtCLIOptions( self, name, ipAddress ):
+ """
+ Parse the cluster options to create an UP4 component with the given name
+
+ Arguments:
+ name - The name of the P4RuntimeCLI component
+ ipAddress - The ip address of the ONOS instance
+ """
+ main.componentDictionary[name] = main.componentDictionary[self.name].copy()
+ main.componentDictionary[name]['type'] = "P4RuntimeCliDriver"
+ main.componentDictionary[name]['host'] = ipAddress
+ port = main.componentDictionary[name]['COMPONENTS'].get( "p4rt_port", "9559" )
+ main.componentDictionary[name]['p4rt_port'] = self.checkOptions( port, "9559" )
+ main.componentDictionary[name]['connect_order'] = str( int( main.componentDictionary[name]['connect_order'] ) + 1 )
+
+ def createP4rtCLIComponent( self, name, ipAddress ):
+ """
+ Creates a new P4Runtime CLI component. This will be connected to the node
+ ONOS is running on.
+
+ Arguments:
+ name - The string of the name of this component. The new component
+ will be assigned to main.<name> .
+ In addition, main.<name>.name = str( name )
+ ipAddress - The ip address of the server
+ """
+ try:
+ # look to see if this component already exists
+ getattr( main, name )
+ except AttributeError:
+ # namespace is clear, creating component
+ self.setP4rtCLIOptions( name, ipAddress )
+ return main.componentInit( name )
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanAndExit()
+ else:
+ # namespace is not clear!
+ main.log.error( name + " component already exists!" )
+ main.cleanAndExit()
+
def createComponents( self, prefix='', createServer=True ):
"""
Creates a CLI and REST component for each nodes in the cluster
@@ -476,6 +535,7 @@
benchPrefix = prefix + "bench"
serverPrefix = prefix + "server"
k8sPrefix = prefix + "k8s"
+ up4Prefix = prefix + "up4cl"
for i in xrange( 1, self.maxNodes + 1 ):
cliName = cliPrefix + str( i )
restName = restPrefix + str( i )
@@ -483,6 +543,8 @@
serverName = serverPrefix + str( i )
if self.kubeConfig:
k8sName = k8sPrefix + str( i )
+ if self.up4Port:
+ up4Name = up4Prefix + str( i )
# Unfortunately this means we need to have a cell set beofre running TestON,
# Even if it is just the entire possible cluster size
@@ -493,9 +555,10 @@
bench = self.createBenchComponent( benchName )
server = self.createServerComponent( serverName, ip ) if createServer else None
k8s = self.createServerComponent( k8sName, ip ) if self.kubeConfig else None
+ p4rtUp4 = self.createP4rtCLIComponent( up4Name, ip ) if self.up4Port else None
if self.kubeConfig:
k8s.kubeConfig = self.kubeConfig
k8s.podName = None
self.nodes.append( Controller( prefix + str( i ), ip, cli, rest, bench, i - 1,
self.user_name, server=server, k8s=k8s,
- dockerPrompt=self.dockerPrompt ) )
+ p4rtUp4=p4rtUp4, dockerPrompt=self.dockerPrompt ) )
diff --git a/TestON/drivers/common/cli/p4runtimeclidriver.py b/TestON/drivers/common/cli/p4runtimeclidriver.py
new file mode 100644
index 0000000..6728cba
--- /dev/null
+++ b/TestON/drivers/common/cli/p4runtimeclidriver.py
@@ -0,0 +1,361 @@
+"""
+Copyright 2021 Open Networking Foundation (ONF)
+
+Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
+the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
+or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
+
+"""
+
+import pexpect
+import os
+from drivers.common.clidriver import CLI
+
+
+class P4RuntimeCliDriver(CLI):
+ """
+ Implements a P4Runtime Client CLI-based driver to control devices that uses
+ the P4Runtime Protocol, using the P4Runtime shell CLI.
+
+ This driver requires that P4Runtime CLI is configured to not generated colored
+ text. To do so, add the following lines to a file in
+ ~/.ipython/profile_default/ipython_config.py:
+ c.InteractiveShell.color_info = False
+ c.InteractiveShell.colors = 'NoColor'
+ c.TerminalInteractiveShell.color_info = False
+ c.TerminalInteractiveShell.colors = 'NoColor'
+ """
+
+ def __init__(self):
+ """
+ Initialize client
+ """
+ super(P4RuntimeCliDriver, self).__init__()
+ self.name = None
+ self.handle = None
+ self.p4rtAddress = "localhost"
+ self.p4rtPort = "9559" # Default P4RT server port
+ self.prompt = "\$"
+ self.p4rtShPrompt = ">>>"
+ self.p4rtDeviceId = "1"
+ self.p4rtElectionId = "0,100" # (high,low)
+ self.p4rtConfig = None # Can be used to pass a path to the P4Info and pipeline config
+
+ def connect(self, **connectargs):
+ """
+ Creates the ssh handle for the P4Runtime CLI
+ The ip_address would come from the topo file using the host tag, the
+ value can be an environment variable as well as a "localhost" to get
+ the ip address needed to ssh to the "bench"
+ """
+ try:
+ for key in connectargs:
+ vars(self)[key] = connectargs[key]
+ self.name = self.options.get("name", "")
+ self.p4rtAddress = self.options.get("p4rt_address", "localhost")
+ self.p4rtPort = self.options.get("p4rt_port", "9559")
+ self.p4rtShPrompt = self.options.get("p4rt_sh_prompt", ">>>")
+ self.p4rtDeviceId = self.options.get("p4rt_device_id", "1")
+ self.p4rtElectionId = self.options.get("p4rt_election_id", "0,100")
+ self.p4rtConfig = self.options.get("p4rt_config", None)
+ try:
+ if os.getenv(str(self.ip_address)) is not None:
+ self.ip_address = os.getenv(str(self.ip_address))
+ else:
+ main.log.info(self.name + ": ip set to " + self.ip_address)
+ except KeyError:
+ main.log.info(self.name + ": Invalid host name," +
+ "defaulting to 'localhost' instead")
+ self.ip_address = 'localhost'
+ except Exception as inst:
+ main.log.error("Uncaught exception: " + str(inst))
+
+ self.handle = super(P4RuntimeCliDriver, self).connect(
+ user_name=self.user_name,
+ ip_address=self.ip_address,
+ port=None,
+ pwd=self.pwd)
+ if self.handle:
+ main.log.info("Connection successful to the host " +
+ self.user_name +
+ "@" +
+ self.ip_address)
+ self.handle.sendline("")
+ self.handle.expect(self.prompt)
+ return main.TRUE
+ else:
+ main.log.error("Connection failed to " +
+ self.user_name +
+ "@" +
+ self.ip_address)
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error(self.name + ": EOF exception found")
+ main.log.error(self.name + ": " + self.handle.before)
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception(self.name + ": Uncaught exception!")
+ main.cleanAndExit()
+
+ def startP4RtClient(self, pushConfig=False):
+ """
+ Start the P4Runtime shell CLI client
+
+ :param pushConfig: True if you want to push the pipeline config, False otherwise
+ requires the p4rt_config configuration parameter to be set
+ :return:
+ """
+ try:
+ main.log.debug(self.name + ": Starting P4Runtime Shell CLI")
+ grpcAddr = "%s:%s" % (self.p4rtAddress, self.p4rtPort)
+ startP4RtShLine = "python3 -m p4runtime_sh --grpc-addr " + grpcAddr + \
+ " --device-id " + self.p4rtDeviceId + \
+ " --election-id " + self.p4rtElectionId
+ if pushConfig:
+ if self.p4rtConfig:
+ startP4RtShLine += " --config " + self.p4rtConfig
+ else:
+ main.log.error(
+ "You should provide a P4 Runtime config to push!")
+ main.cleanAndExit()
+ response = self.__clearSendAndExpect(startP4RtShLine)
+ self.preDisconnect = self.stopP4RtClient
+ except pexpect.TIMEOUT:
+ main.log.exception(self.name + ": Command timed out")
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.exception(self.name + ": connection closed.")
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception(self.name + ": Uncaught exception!")
+ main.cleanAndExit()
+
+ def stopP4RtClient(self):
+ """
+ Exit the P4Runtime shell CLI
+ """
+ try:
+ main.log.debug(self.name + ": Stopping P4Runtime Shell CLI")
+ self.handle.sendline("exit")
+ self.handle.expect(self.prompt)
+ return main.TRUE
+ except pexpect.TIMEOUT:
+ main.log.exception(self.name + ": Command timed out")
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.exception(self.name + ": connection closed.")
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception(self.name + ": Uncaught exception!")
+ main.cleanAndExit()
+
+ def pushTableEntry(self, tableEntry=None, debug=True):
+ """
+ Push a table entry with either the given table entry or use the saved
+ table entry in the variable 'te'.
+
+ Example of a valid tableEntry string:
+ te = table_entry["FabricIngress.forwarding.routing_v4"](action="set_next_id_routing_v4"); te.action["next_id"] = "10"; te.match["ipv4_dst"] = "10.0.0.0" # nopep8
+
+ :param tableEntry: the string table entry, if None it uses the table
+ entry saved in the 'te' variable
+ :param debug: True to enable debug logging, False otherwise
+ :return: main.TRUE or main.FALSE on error
+ """
+ try:
+ main.log.debug(self.name + ": Pushing Table Entry")
+ if debug:
+ self.handle.sendline("te")
+ self.handle.expect(self.p4rtShPrompt)
+ pushCmd = ""
+ if tableEntry:
+ pushCmd = tableEntry + ";"
+ pushCmd += "te.insert()"
+ response = self.__clearSendAndExpect(pushCmd)
+ if "Traceback" in response or "Error" in response:
+ # TODO: other possibile errors?
+ # NameError...
+ main.log.error(
+ self.name + ": Error in pushing table entry: " + response)
+ return main.FALSE
+ return main.TRUE
+ except pexpect.TIMEOUT:
+ main.log.exception(self.name + ": Command timed out")
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.exception(self.name + ": connection closed.")
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception(self.name + ": Uncaught exception!")
+ main.cleanAndExit()
+
+ def deleteTableEntry(self, tableEntry=None, debug=True):
+ """
+ Deletes a table entry with either the given table entry or use the saved
+ table entry in the variable 'te'.
+
+ Example of a valid tableEntry string:
+ te = table_entry["FabricIngress.forwarding.routing_v4"](action="set_next_id_routing_v4"); te.action["next_id"] = "10"; te.match["ipv4_dst"] = "10.0.0.0" # nopep8
+
+ :param tableEntry: the string table entry, if None it uses the table
+ entry saved in the 'te' variable
+ :param debug: True to enable debug logging, False otherwise
+ :return: main.TRUE or main.FALSE on error
+ """
+ try:
+ main.log.debug(self.name + ": Deleting Table Entry")
+ if debug:
+ self.__clearSendAndExpect("te")
+ pushCmd = ""
+ if tableEntry:
+ pushCmd = tableEntry + ";"
+ pushCmd += "te.delete()"
+ response = self.__clearSendAndExpect(pushCmd)
+ main.log.debug(
+ self.name + ": Delete table entry response: {}".format(
+ response))
+ if "Traceback" in response or "Error" in response:
+ # TODO: other possibile errors?
+ # NameError...
+ main.log.error(
+ self.name + ": Error in deleting table entry: " + response)
+ return main.FALSE
+ return main.TRUE
+ except pexpect.TIMEOUT:
+ main.log.exception(self.name + ": Command timed out")
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.exception(self.name + ": connection closed.")
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception(self.name + ": Uncaught exception!")
+ main.cleanAndExit()
+
+ def buildP4RtTableEntry(self, tableName, actionName, actionParams={},
+ matchFields={}):
+ """
+ Build a Table Entry
+ :param tableName: The name of table
+ :param actionName: The name of the action
+ :param actionParams: A dictionary containing name and values for the action parameters
+ :param matchFields: A dictionary containing name and values for the match fields
+ :return: main.TRUE or main.FALSE on error
+ """
+ # TODO: improve error checking when creating the table entry, add
+ # params, and match fields.
+ try:
+ main.log.debug(self.name + ": Building P4RT Table Entry")
+ cmd = 'te = table_entry["%s"](action="%s"); ' % (
+ tableName, actionName)
+
+ # Action Parameters
+ for name, value in actionParams.items():
+ cmd += 'te.action["%s"]="%s";' % (name, str(value))
+
+ # Match Fields
+ for name, value in matchFields.items():
+ cmd += 'te.match["%s"]="%s";' % (name, str(value))
+
+ response = self.__clearSendAndExpect(cmd)
+ if "Unknown action" in response:
+ main.log.error("Unknown action: " + response)
+ return main.FALSE
+ if "AttributeError" in response:
+ main.log.error("Wrong action: " + response)
+ return main.FALSE
+ if "Invalid value" in response:
+ main.log.error("Invalid action value: " + response)
+ return main.FALSE
+ if "Action parameter value must be a string" in response:
+ main.log.error(
+ "Action parameter value must be a string: " + response)
+ return main.FALSE
+ if "table" in response and "does not exist" in response:
+ main.log.error("Unknown table: " + response)
+ return main.FALSE
+ if "not a valid match field name" in response:
+ main.log.error("Invalid match field name: " + response)
+ return main.FALSE
+ if "is not a valid" in response:
+ main.log.error("Invalid match field: " + response)
+ return main.FALSE
+ if "Traceback" in response:
+ main.log.error("Error in creating the table entry: " + response)
+ return main.FALSE
+ return main.TRUE
+ except pexpect.TIMEOUT:
+ main.log.exception(self.name + ": Command timed out")
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.exception(self.name + ": connection closed.")
+ main.cleanAndExit()
+ except Exception:
+ main.log.exception(self.name + ": Uncaught exception!")
+ main.cleanAndExit()
+
+ def disconnect(self):
+ """
+ Called at the end of the test to stop the p4rt CLI component and
+ disconnect the handle.
+ """
+ response = main.TRUE
+ try:
+ if self.handle:
+ self.handle.sendline("")
+ i = self.handle.expect([self.p4rtShPrompt, pexpect.TIMEOUT],
+ timeout=2)
+ if i != 1:
+ # If the p4rtShell is still connected make sure to
+ # disconnect it before
+ self.stopP4RtClient()
+ i = self.handle.expect([self.prompt, pexpect.TIMEOUT],
+ timeout=2)
+ if i == 1:
+ main.log.warn(
+ self.name + ": timeout when waiting for response")
+ main.log.warn(
+ self.name + ": response: " + str(self.handle.before))
+ self.handle.sendline("exit")
+ i = self.handle.expect(["closed", pexpect.TIMEOUT], timeout=2)
+ if i == 1:
+ main.log.warn(
+ self.name + ": timeout when waiting for response")
+ main.log.warn(
+ self.name + ": response: " + str(self.handle.before))
+ return main.TRUE
+ except TypeError:
+ main.log.exception(self.name + ": Object not as expected")
+ response = main.FALSE
+ except pexpect.EOF:
+ main.log.error(self.name + ": EOF exception found")
+ main.log.error(self.name + ": " + self.handle.before)
+ except ValueError:
+ main.log.exception("Exception in disconnect of " + self.name)
+ response = main.TRUE
+ except Exception:
+ main.log.exception(self.name + ": Connection failed to the host")
+ response = main.FALSE
+ return response
+
+ def __clearSendAndExpect(self, cmd):
+ self.clearBuffer()
+ self.handle.sendline(cmd)
+ self.handle.expect(self.p4rtShPrompt)
+ return self.handle.before
+
+ def clearBuffer(self, debug=False):
+ """
+ Keep reading from buffer until it's empty
+ """
+ i = 0
+ response = ''
+ while True:
+ try:
+ i += 1
+ # clear buffer
+ if debug:
+ main.log.warn("%s expect loop iteration" % i)
+ self.handle.expect(self.p4rtShPrompt, timeout=5)
+ response += self.cleanOutput(self.handle.before, debug)
+ except pexpect.TIMEOUT:
+ return response
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/up4libcli.py b/TestON/tests/USECASE/SegmentRouting/dependencies/up4libcli.py
new file mode 100644
index 0000000..73dd39b
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/up4libcli.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+
+"""
+Copyright 2021 Open Networking Foundation (ONF)
+
+Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
+the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
+or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
+
+"""
+
+FALSE = '0'
+TRUE = '1'
+DIR_UPLINK = '1'
+DIR_DOWNLINK = '2'
+IFACE_ACCESS = '1'
+IFACE_CORE = '2'
+TUNNEL_SPORT = '2152'
+TUNNEL_TYPE_GPDU = '3'
+
+
+class Up4LibCli():
+ """
+ Helper library to attach and detach UEs via UP4 P4Runtime APIs.
+ """
+
+ @staticmethod
+ def attachUe(p4rtCli, s1u_address, enb_address, pfcp_session_id, ue_address,
+ teid=None, up_id=None, down_id=None,
+ teid_up=None, teid_down=None,
+ pdr_id_up=None, far_id_up=None, ctr_id_up=None,
+ pdr_id_down=None, far_id_down=None, ctr_id_down=None,
+ qfi=None, five_g=False):
+ Up4LibCli.__programUp4Rules(p4rtCli, s1u_address, enb_address,
+ pfcp_session_id,
+ ue_address,
+ teid, up_id, down_id,
+ teid_up, teid_down,
+ pdr_id_up, far_id_up, ctr_id_up,
+ pdr_id_down, far_id_down, ctr_id_down,
+ qfi, five_g, action="program")
+
+ @staticmethod
+ def detachUe(p4rtCli, s1u_address, enb_address, pfcp_session_id, ue_address,
+ teid=None, up_id=None, down_id=None,
+ teid_up=None, teid_down=None,
+ pdr_id_up=None, far_id_up=None, ctr_id_up=None,
+ pdr_id_down=None, far_id_down=None, ctr_id_down=None,
+ qfi=None, five_g=False):
+ Up4LibCli.__programUp4Rules(p4rtCli, s1u_address, enb_address,
+ pfcp_session_id,
+ ue_address,
+ teid, up_id, down_id,
+ teid_up, teid_down,
+ pdr_id_up, far_id_up, ctr_id_up,
+ pdr_id_down, far_id_down, ctr_id_down,
+ qfi, five_g, action="clear")
+
+ @staticmethod
+ def __programUp4Rules(p4rtCli, s1u_address, enb_address, pfcp_session_id,
+ ue_address,
+ teid=None, up_id=None, down_id=None,
+ teid_up=None, teid_down=None,
+ pdr_id_up=None, far_id_up=None, ctr_id_up=None,
+ pdr_id_down=None, far_id_down=None, ctr_id_down=None,
+ qfi=None, five_g=False, action="program"):
+ if up_id is not None:
+ pdr_id_up = up_id
+ far_id_up = up_id
+ ctr_id_up = up_id
+ if down_id is not None:
+ pdr_id_down = down_id
+ far_id_down = down_id
+ ctr_id_down = down_id
+ if teid is not None:
+ teid_up = teid
+ teid_down = teid
+
+ entries = []
+
+ # ========================#
+ # PDR Entries
+ # ========================#
+
+ # Uplink
+ tableName = 'PreQosPipe.pdrs'
+ actionName = ''
+ matchFields = {}
+ actionParams = {}
+ if qfi is None:
+ actionName = 'PreQosPipe.set_pdr_attributes'
+ else:
+ actionName = 'PreQosPipe.set_pdr_attributes_qos'
+ if five_g:
+ # TODO: currently QFI_MATCH is unsupported in TNA
+ matchFields['has_qfi'] = TRUE
+ matchFields["qfi"] = str(qfi)
+ actionParams['needs_qfi_push'] = FALSE
+ actionParams['qfi'] = str(qfi)
+ # Match fields
+ matchFields['src_iface'] = IFACE_ACCESS
+ matchFields['ue_addr'] = str(ue_address)
+ matchFields['teid'] = str(teid_up)
+ matchFields['tunnel_ipv4_dst'] = str(s1u_address)
+ # Action params
+ actionParams['id'] = str(pdr_id_up)
+ actionParams['fseid'] = str(pfcp_session_id)
+ actionParams['ctr_id'] = str(ctr_id_up)
+ actionParams['far_id'] = str(far_id_up)
+ actionParams['needs_gtpu_decap'] = TRUE
+ if not Up4LibCli.__add_entry(p4rtCli, tableName, actionName, matchFields,
+ actionParams, entries, action):
+ return False
+
+ # Downlink
+ tableName = 'PreQosPipe.pdrs'
+ actionName = ''
+ matchFields = {}
+ actionParams = {}
+ if qfi is None:
+ actionName = 'PreQosPipe.set_pdr_attributes'
+ else:
+ actionName = 'PreQosPipe.set_pdr_attributes_qos'
+ # TODO: currently QFI_PUSH is unsupported in TNA
+ actionParams['needs_qfi_push'] = TRUE if five_g else FALSE
+ actionParams['qfi'] = str(qfi)
+ # Match fields
+ matchFields['src_iface'] = IFACE_CORE
+ matchFields['ue_addr'] = str(ue_address)
+ # Action params
+ actionParams['id'] = str(pdr_id_down)
+ actionParams['fseid'] = str(pfcp_session_id)
+ actionParams['ctr_id'] = str(ctr_id_down)
+ actionParams['far_id'] = str(far_id_down)
+ actionParams['needs_gtpu_decap'] = FALSE
+ if not Up4LibCli.__add_entry(p4rtCli, tableName, actionName, matchFields,
+ actionParams, entries, action):
+ return False
+
+ # ========================#
+ # FAR Entries
+ # ========================#
+
+ # Uplink
+ tableName = 'PreQosPipe.load_far_attributes'
+ actionName = 'PreQosPipe.load_normal_far_attributes'
+ matchFields = {}
+ actionParams = {}
+
+ # Match fields
+ matchFields['far_id'] = str(far_id_up)
+ matchFields['session_id'] = str(pfcp_session_id)
+ # Action params
+ actionParams['needs_dropping'] = FALSE
+ actionParams['notify_cp'] = FALSE
+ if not Up4LibCli.__add_entry(p4rtCli, tableName, actionName, matchFields,
+ actionParams, entries, action):
+ return False
+
+ # Downlink
+ tableName = 'PreQosPipe.load_far_attributes'
+ actionName = 'PreQosPipe.load_tunnel_far_attributes'
+ matchFields = {}
+ actionParams = {}
+
+ # Match fields
+ matchFields['far_id'] = str(far_id_down)
+ matchFields['session_id'] = str(pfcp_session_id)
+ # Action params
+ actionParams['needs_dropping'] = FALSE
+ actionParams['notify_cp'] = FALSE
+ actionParams['needs_buffering'] = FALSE
+ actionParams['tunnel_type'] = TUNNEL_TYPE_GPDU
+ actionParams['src_addr'] = str(s1u_address)
+ actionParams['dst_addr'] = str(enb_address)
+ actionParams['teid'] = str(teid_down)
+ actionParams['sport'] = TUNNEL_SPORT
+ if not Up4LibCli.__add_entry(p4rtCli, tableName, actionName, matchFields,
+ actionParams, entries, action):
+ return False
+
+ if action == "program":
+ main.log.info("All entries added successfully.")
+ elif action == "clear":
+ Up4LibCli.__clear_entries(p4rtCli, entries)
+
+ @staticmethod
+ def __add_entry(p4rtCli, tableName, actionName, matchFields, actionParams,
+ entries, action):
+ if action == "program":
+ p4rtCli.buildP4RtTableEntry(tableName=tableName,
+ actionName=actionName,
+ actionParams=actionParams,
+ matchFields=matchFields)
+ if p4rtCli.pushTableEntry(debug=True) == main.TRUE:
+ main.log.info("*** Entry added.")
+ else:
+ main.log.error("Error during table insertion")
+ Up4LibCli.__clear_entries(p4rtCli, entries)
+ return False
+ entries.append({"tableName": tableName, "actionName": actionName,
+ "matchFields": matchFields,
+ "actionParams": actionParams})
+ return True
+
+ @staticmethod
+ def __clear_entries(p4rtCli, entries):
+ for i, entry in enumerate(entries):
+ p4rtCli.buildP4RtTableEntry(**entry)
+ if p4rtCli.deleteTableEntry(debug=True) == main.TRUE:
+ main.log.info("*** Entry %d of %d deleted." % (i + 1, len(entries)))
+ else:
+ main.log.error("Error during table delete")