cloudFPGA (cF) API  1.0
The documentation of the source code of cloudFPGA (cF)
tc_utils.py
Go to the documentation of this file.
1 #/*
2 # * Copyright 2016 -- 2021 IBM Corporation
3 # *
4 # * Licensed under the Apache License, Version 2.0 (the "License");
5 # * you may not use this file except in compliance with the License.
6 # * You may obtain a copy of the License at
7 # *
8 # * http://www.apache.org/licenses/LICENSE-2.0
9 # *
10 # * Unless required by applicable law or agreed to in writing, software
11 # * distributed under the License is distributed on an "AS IS" BASIS,
12 # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # * See the License for the specific language governing permissions and
14 # * limitations under the License.
15 # */
16 
17 # *****************************************************************************
18 # * @file : tc_utils.py
19 # * @brief : A python module with some helper functions for the UDP and
20 # * TCP test-cases.
21 # * System: : cloudFPGA
22 # * Component : cFp_BringUp/ROLE
23 # * Language : Python 3
24 # *
25 # *****************************************************************************
26 
27 # ### REQUIRED PYTHON PACKAGES ################################################
28 import ipaddress
29 import os
30 import random
31 import requests
32 import string
33 
34 # Return codes for the function calls
35 ACM_OK = 0 # ACM = Accelerator Module (alias for FM = Fpga Module)
36 ACM_KO = 1
37 
38 MTU = 1500 # ETHERNET - Maximum Transfer Unit
39 MTU_ZYC2 = 1450 # ETHERNET - MTU in ZYC2 = 1500-20-8-8-14
40 IP4_HDR_LEN = 20 # IPv4 Header Length
41 TCP_HDR_LEN = 20 # TCP Header Length
42 ZYC2_MSS = (MTU_ZYC2 - 92) & ~0x7 # ZYC2 Maximum Segment Size (1352)
43 UDP_HDR_LEN = 8 # UDP Header Length
44 UDP_MDS = (MTU_ZYC2 - IP4_HDR_LEN - UDP_HDR_LEN) & ~0x7 # Maximum Datagram Size (modulo 8)
45 
46 # -------------------------------------------------------------------
47 # -- DEFAULT LISTENING PORTS
48 # -- By default, the following port numbers will be used by the
49 # -- UdpShellInterface (unless user specifies new ones via TBD).
50 # -- Default listen ports:
51 # -- --> 5001 : Traffic received on this port is [TODO-TBD].
52 # -- It is used to emulate IPERF V2.
53 # -- --> 5201 : Traffic received on this port is [TODO-TBD].
54 # -- It is used to emulate IPREF V3.
55 # -- --> 8800 : Traffic received on this port is systematically
56 # -- dumped. It is used to test the Rx part of UOE.
57 # -- --> 8801 : A message received on this port triggers the
58 # -- transmission of 'nr' bytes from the FPGA to the host.
59 # -- It is used to test the Tx part of UOE.
60 # -- --> 8803 : Traffic received on this port is looped backed and
61 # -- echoed to the sender.
62 # -------------------------------------------------------------------
63 RECV_MODE_LSN_PORT = 8800 # 0x2260
64 XMIT_MODE_LSN_PORT = 8801 # 0x2261
65 BIDIR_MODE_LSN_PORT = 8802 # 0x2262
66 ECHO_MODE_LSN_PORT = 8803 # 0x2263
67 IPERF_LSN_PORT = 5001 # 0x1389
68 IPREF3_LSN_PORT = 5201 # 0x1451
69 
70 def num_to_char(num):
71  """ Function to map a number to a character."""
72  switcher = {
73  0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7',
74  8: '8', 9: '9', 10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'
75  }
76  return switcher.get(num, ' ') # ' ' is default
77 
78 
79 def str_static_gen(size):
80  """Returns an encoded static string of length 'size'."""
81  msg = '\n'
82  msg += '__________Hello_World__________'
83  while (len(msg)) < (size):
84  msg += num_to_char(len(msg) % 16)
85  msg = (msg[:size]) if len(msg) > size else msg
86  return msg.encode('ascii', 'replace')
87 
88 
89 def str_rand_gen(size):
90  """Returns an encoded random string of length 'size'."""
91  msg = '\n'
92  msg += "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(size-1))
93  return msg.encode('ascii', 'replace')
94 
95 def getFpgaIpv4(args):
96  """Retrieve the IPv4 address of the FPGA module.
97  :param args The options passed as arguments to the script.
98  :return The IPv4 address as an 'ipaddress.IPv4Address'."""
99  ipFpgaStr = args.fpga_ipv4
100  while True:
101  if args.fpga_ipv4 == '':
102  # Take an IP address from the console
103  print("Enter the IPv4 address of the FPGA module to connect to (e.g. 10.12.200.21)")
104  ipFpgaStr = input()
105  try:
106  ipFpga = ipaddress.ip_address(ipFpgaStr)
107  except ValueError:
108  print('[ERROR] Unrecognized IPv4 address.')
109  else:
110  return ipFpga
111 
112 
113 def getFpgaPort(args):
114  """Retrieve the UDP listen port of the FPGA.
115  :param args The options passed as arguments to the script.
116  :return The UDP port number as an integer."""
117  portFpga = args.fpga_port
118  if portFpga != 8803:
119  print("[ERROR] The current version of the cFp_BringUp role always listens on port #8803.\n")
120  exit(1)
121  return portFpga
122 
123 
125  """Retrieve the IPv4 address of the cloudFPGA Resource Manager.
126  :param args The options passed as arguments to the script.
127  :return The IP address as an 'ipaddress.IPv4Address'."""
128  ipResMngrStr = args.mngr_ipv4
129  while True:
130  if args.mngr_ipv4 == '':
131  # Take an IP address from the console
132  print("Enter the IPv4 address of the cloudFPGA Resource Manager (e.g. 10.12.0.132)")
133  ipResMngrStr = input()
134  try:
135  ipResMngr = ipaddress.ip_address(ipResMngrStr)
136  except ValueError:
137  print('[ERROR] Unrecognized IPv4 address.')
138  else:
139  return ipResMngr
140 
141 
143  """Retrieve the TCP port of the cloudFPGA Resource Manager.
144  :param args The options passed as arguments to the script.
145  :return The TCP port number as an integer."""
146  portMngr = args.mngr_port
147  if portMngr != 8080:
148  print("[ERROR] The current version of the cloudFPGA Resource manager always listens on port #8080.\n")
149  exit(1)
150  return portMngr
151 
152 
153 def getInstanceId(args):
154  """Retrieve the instance Id that was assigned by the cloudFPGA Resource Manager.
155  :param args The options passed as arguments to the script.
156  :return The instance Id as an integer."""
157  instId = args.inst_id
158  while True:
159  if not 1 <= args.inst_id: # Take an instance Id from the console
160  print("Enter the instance Id that was assigned by the cloudFPGA Resource Manager (e.g. 42)")
161  instIdStr = input()
162  try:
163  instId = int(instIdStr)
164  except ValueError:
165  print("ERROR: Bad format for the instance Id.")
166  print("\tEnter a new instance Id > 0.\n")
167  else:
168  break
169  else:
170  break;
171  return instId
172 
173 
174 def restartApp(instId, ipResMngr, portResMngr, user_name, user_passwd):
175  """Trigger the role of an FPGA to restart (i.e. perform a SW reset of the role)
176  :param instId: The instance Id to restart.
177  :param ipResMngr: The IPv4 address of the cF resource manager.
178  :param portResMngr: The TCP port number of the cF resource manager.
179  :param user_name: The user name as used to log in ZYC2.
180  :param user_passwd: The ZYC2 password attached to the user name.
181  :return: Nothing
182  """
183  print("\nNow: Requesting the application of FPGA instance #%d to restart." % instId)
184  try:
185  # We must build a request that is formatted as follows:
186  # http://10.12.0.132:8080/instances/13/app_restart?username=fab&password=secret
187  reqUrl = "http://" + str(ipResMngr) + ":" + str(portResMngr) + "/instances/" \
188  + str(instId) + "/app_restart?username=" + user_name \
189  + "&password=" + user_passwd
190  # DEBUG print("Generated request URL = ", reqUrl)
191  r1 = requests.patch(reqUrl)
192  print(r1.content.decode('ascii'))
193  except Exception as e:
194  print("ERROR: Failed to reset the FPGA role")
195  print(str(e))
196  exit(1)
197 
198 
199 def pingFpga(ipFpga):
200  """Ping an FPGA.
201  :param ipFpga: The IPv4 address of the FPGA.
202  :return: Nothing
203  """
204  print("Now: Trying to \'ping\' the FPGA: ")
205  # Send 2 PINGs and wait 2 seconds max for each of them (for a total max of 4s)
206  rc = os.system("ping -c 2 -W 2 " + str(ipFpga))
207  if rc != 0:
208  print("[ERROR] FPGA does not reply to \'ping\'!")
209  exit(1)
210 
211 def display_throughput(byteCount, elapseTime):
212  """Display the throughput in human readable form.
213  :param byteCount: The number of bytes transferred.
214  :param elapseTime: The duration of the transfer.
215  :return: Nothing
216  """
217  if byteCount < 1000000:
218  print("[INFO] Transferred a total of %d bytes." % byteCount)
219  elif byteCount < 1000000000:
220  megaBytes = (byteCount * 1.0) / (1024 * 1024 * 1.0)
221  print("[INFO] Transferred a total of %.1f MB." % megaBytes)
222  else:
223  gigaBytes = (byteCount * 1.0) / (1024 * 1024 * 1024 * 1.0)
224  print("[INFO] Transferred a total of %.1f GB." % gigaBytes)
225  throughput = (byteCount * 8 * 1.0) / (elapseTime.total_seconds() * 1024 * 1024)
226 
227  strMsg = ""
228  if throughput < 1000:
229  strMsg = "#### DONE with throughput = %.1f Mb/s ####" % throughput
230  else:
231  throughput = throughput / 1000
232  strMsg = "#### DONE with throughput = %.1f Gb/s ####" % throughput
233 
234  strHashLine = ""
235  for i in range(0, len(strMsg)):
236  strHashLine += "#"
237 
238  print(strHashLine)
239  print(strMsg)
240  print(strHashLine)
241  print()
242 
243 
def restartApp(instId, ipResMngr, portResMngr, user_name, user_passwd)
Definition: tc_utils.py:174
def getFpgaIpv4(args)
Definition: tc_utils.py:95
def getInstanceId(args)
Definition: tc_utils.py:153
def getResourceManagerIpv4(args)
Definition: tc_utils.py:124
def str_rand_gen(size)
Definition: tc_utils.py:89
def num_to_char(num)
Definition: tc_utils.py:70
def getFpgaPort(args)
Definition: tc_utils.py:113
def str_static_gen(size)
Definition: tc_utils.py:79
def pingFpga(ipFpga)
Definition: tc_utils.py:199
def display_throughput(byteCount, elapseTime)
Definition: tc_utils.py:211
def getResourceManagerPort(args)
Definition: tc_utils.py:142
string input
Definition: test.py:9