go.ligato.io/vpp-agent/v3@v3.5.0/tests/robot/tools/vpp_api_executor.py (about)

     1  #!/usr/bin/env python3
     2  
     3  # Copyright (c) 2019 Cisco and/or its affiliates.
     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  import os
    17  import sys
    18  import json
    19  import fnmatch
    20  import argparse
    21  import binascii
    22  import ipaddress
    23  
    24  from vpp_papi import VPP
    25  
    26  vpp_json_dir_core = "/usr/share/vpp/api/core"
    27  vpp_json_dir_plugins = "/usr/share/vpp/api/plugins"
    28  
    29  
    30  def _convert_reply(api_r):
    31      """Process API reply / a part of API reply for smooth converting to
    32      JSON string.
    33  
    34      It is used only with 'request' and 'dump' methods.
    35  
    36      Apply binascii.hexlify() method for string values.
    37  
    38      TODO: Implement complex solution to process of replies.
    39  
    40      :param api_r: API reply.
    41      :type api_r: Vpp_serializer reply object (named tuple)
    42      :returns: Processed API reply / a part of API reply.
    43      :rtype: dict
    44      """
    45      unwanted_fields = ['count', 'index', 'context']
    46  
    47      def process_value(val):
    48          """Process value.
    49  
    50          :param val: Value to be processed.
    51          :type val: object
    52          :returns: Processed value.
    53          :rtype: dict or str or int
    54          """
    55  
    56          # with dict or list just recursively iterate through all elements
    57          if isinstance(val, dict):
    58              for val_k, val_v in val.items():
    59                  val[str(val_k)] = process_value(val_v)
    60              return val
    61          elif isinstance(val, list):
    62              for idx, val_l in enumerate(val):
    63                  val[idx] = process_value(val_l)
    64              return val
    65          # no processing for int
    66          elif hasattr(val, '__int__'):
    67              return int(val)
    68          elif isinstance(val, bytes):
    69              # if exactly 16 bytes it's probably an IP address
    70              if len(val) == 16:
    71                  try:
    72                      # without context we don't know if it's IPv4 or IPv6, return both forms
    73                      ipv4 = ipaddress.IPv4Address(val[:4])
    74                      ipv6 = ipaddress.IPv6Address(val)
    75                      return {"ipv4": str(ipv4), "ipv6": str(ipv6)}
    76                  except ipaddress.AddressValueError:
    77                      # maybe it's not an IP address after all
    78                      pass
    79              elif len(val) in (6, 8):
    80                  # Probably a padded MAC address(8) or "Dmac, Smac, etc."??(6)
    81                  return val.hex()
    82  
    83              # strip null byte padding from some fields, such as tag or name
    84              while val.endswith(b"\x00"):
    85                  val = val[:-1]
    86              return str(val, "ascii")
    87          elif hasattr(val, '__str__'):
    88  
    89              if "(" in repr(val):
    90                  # it's another vpp-internal object
    91                  item_dict = dict()
    92                  for item in dir(val):
    93                      if not item.startswith("_") and item not in unwanted_fields:
    94                          item_dict[item] = process_value(getattr(val, item))
    95                  return item_dict
    96              else:
    97                  # just a simple string
    98                  return str(val)
    99          # Next handles parameters not supporting preferred integer or string
   100          # representation to get it logged
   101          elif hasattr(val, '__repr__'):
   102              return repr(val)
   103          else:
   104              return val
   105  
   106      reply_dict = dict()
   107      reply_key = repr(api_r).split('(')[0]
   108      reply_value = dict()
   109      for item in dir(api_r):
   110          if not item.startswith('_') and item not in unwanted_fields:
   111              reply_value[item] = process_value(getattr(api_r, item))
   112      reply_dict[reply_key] = reply_value
   113      return reply_dict
   114  
   115  
   116  class VppApi(object):
   117      def __init__(self):
   118          self.vpp = None
   119  
   120          jsonfiles = []
   121          for root, dirnames, filenames in os.walk(vpp_json_dir_core):
   122              for filename in fnmatch.filter(filenames, '*.api.json'):
   123                  jsonfiles.append(os.path.join(vpp_json_dir_core, filename))
   124          for root, dirnames, filenames in os.walk(vpp_json_dir_plugins):
   125              for filename in fnmatch.filter(filenames, '*.api.json'):
   126                  jsonfiles.append(os.path.join(vpp_json_dir_plugins, filename))
   127  
   128          self.vpp = VPP(jsonfiles)
   129  
   130      def connect(self):
   131          resp = self.vpp.connect("ligato-test-api")
   132          if resp != 0:
   133              raise RuntimeError("VPP papi connection failed.")
   134  
   135      def list_capabilities(self):
   136          print(dir(self.vpp.api))
   137  
   138      def disconnect(self):
   139          resp = self.vpp.disconnect()
   140          if resp != 0:
   141              print("Warning: VPP papi disconnect failed.")
   142  
   143      def show_version(self):
   144          print(self.vpp.api.show_version())
   145  
   146      def process_json_request(self, args):
   147          """Process the request/reply and dump classes of VPP API methods.
   148  
   149          :param args: Command line arguments passed to VPP PAPI Provider.
   150          :type args: ArgumentParser
   151          :returns: JSON formatted string.
   152          :rtype: str
   153          :raises RuntimeError: If PAPI command error occurs.
   154          """
   155  
   156          vpp = self.vpp
   157  
   158          reply = list()
   159  
   160          def process_value(val):
   161              """Process value.
   162  
   163              :param val: Value to be processed.
   164              :type val: object
   165              :returns: Processed value.
   166              :rtype: dict or str or int
   167              """
   168              if isinstance(val, dict):
   169                  for val_k, val_v in val.items():
   170                      val[str(val_k)] = process_value(val_v)
   171                  return val
   172              elif isinstance(val, list):
   173                  for idx, val_l in enumerate(val):
   174                      val[idx] = process_value(val_l)
   175                  return val
   176              elif isinstance(val, str):
   177                  return binascii.unhexlify(val)
   178              elif isinstance(val, int):
   179                  return val
   180              else:
   181                  return str(val)
   182  
   183          self.connect()
   184          json_data = json.loads(args.data)
   185  
   186          for data in json_data:
   187              api_name = data['api_name']
   188              api_args_unicode = data['api_args']
   189              api_reply = dict(api_name=api_name)
   190              api_args = dict()
   191              for a_k, a_v in api_args_unicode.items():
   192                  api_args[str(a_k)] = process_value(a_v)
   193              try:
   194                  papi_fn = getattr(vpp.api, api_name)
   195                  rep = papi_fn(**api_args)
   196  
   197                  if isinstance(rep, list):
   198                      converted_reply = list()
   199                      for r in rep:
   200                          converted_reply.append(_convert_reply(r))
   201                  else:
   202                      converted_reply = _convert_reply(rep)
   203  
   204                  api_reply['api_reply'] = converted_reply
   205                  reply.append(api_reply)
   206              except (AttributeError, ValueError) as err:
   207                  vpp.disconnect()
   208                  raise RuntimeError('PAPI command {api}({args}) input error:\n{err}'.
   209                                     format(api=api_name,
   210                                            args=api_args,
   211                                            err=repr(err)))
   212              except Exception as err:
   213                  vpp.disconnect()
   214                  raise RuntimeError('PAPI command {api}({args}) error:\n{exc}'.
   215                                     format(api=api_name,
   216                                            args=api_args,
   217                                            exc=repr(err)))
   218          self.disconnect()
   219  
   220          return json.dumps(reply)
   221  
   222  
   223  def main():
   224      """Main function for the Python API provider.
   225      """
   226  
   227      # The functions which process different types of VPP Python API methods.
   228  
   229      parser = argparse.ArgumentParser(
   230          formatter_class=argparse.RawDescriptionHelpFormatter,
   231          description=__doc__)
   232      parser.add_argument("-d", "--data",
   233                          required=True,
   234                          help="Data is a JSON string (list) containing API name(s)"
   235                               "and its/their input argument(s).")
   236  
   237      args = parser.parse_args()
   238  
   239      vpp = VppApi()
   240      return VppApi.process_json_request(vpp, args)
   241  
   242  
   243  if __name__ == '__main__':
   244      sys.stdout.write(main())
   245      sys.stdout.flush()
   246      sys.exit(0)