Code Monkey home page Code Monkey logo

app-netutil's Introduction

Network Utility Library for Application Running on Kubernetes

Table of Contents

Network Utility

Network Utility (app-netutil) is a library that provides API methods for applications running in a container to query network information associated with pod. Network Utility is written in golang and can be built into an application binary. It also has a C language binding allowing it to be built into C applications.

To add virtio based interfaces into a DPDK based application in a container, the DPDK application needs a unix socket file, which is shared with the host through a VolumeMount, and a set of configuration data about how the socketfile should be used. Currently, the Userspace CNI uses annotations or configuration files to share the data between host and container. SR-IOV needs to get the PCI Addresses of the VFs share with the DPDK application. Currently it is using Environmental Variables to do this. Once the above data is in the container, this library has been written to abstract out where to look and how to process all data passed in.

Note: Userspace CNI is not officially supported in OpenShift.

Additional Information:

APIs

There are three API methods to collect system and network information:

  • GetCPUInfo()
    • This function determines which CPUs are available to the container and returns the list to the caller.
  • GetHugepages()
    • This function determines the amount of hugepage memory available to each container and returns the values to the caller.
  • GetInterfaces()
    • This function determines the set of interfaces in the container and returns the list, along with the interface type and type specific data.

There is a GO and C version of each of these functions.

GO APIs

GO Sample APP

There is a GO sample app that provides an example of how to include the app-netutil as a library in a GO program and how to use the existing APIs:

C APIs

C Sample APP

There is a C sample app that provides an example of how to include the app-netutil as a library in a C program and how to use the existing APIs:

The gotcha with the C APIs is that data must be allocated on the C side and passed into the GO library. So the C APIs take data buffers as input. Then the GO library populates the input structures with the collect data. It is then up to the C side to free any allocated data.

GO has a special handling for strings where it still allocates the memory for the string on the C side, but it is hidden in the C.CString() library. So strings passed from the GO library back to the calling C code must also free strings, even though there were not explicitly malloc'd. The sample C code shows examples.

DPDK Sample Image

The initial problem app-netutil is trying to solve is to collect initial configuration data for a DPDK application running in a container. The DPDK Library is written in C, so there is a sample Docker Image that leverages the C APIs of app-netutil to collect the initial configuration data an then use it to start DPDK. See:

Custom Settings

app-netutil is a set of library functions that are intended to be pulled into a container workload to collect system and network information. In order to tailor the implementation to match the workload's needs, there are a few settings that can be configured.

For C, there is a single C API method to configure app-netutil configuration fields:

  • SetConfig()
    • This function takes an input structure and applies the configured fields. Since this C API is only writing (passing data in), no data is allocated on the GO side. Therefore no special freeing of memory is required other than the C side needs to free any memory it specifically allocates.

Since the base code is written in GO, the GO code can call the configuration APIs directly, so there is not one single GO configuration API.

Logging

app-netutil supports four levels of logging:

  • "error"
  • "warning"
  • "info"
  • "debug"

The default level is "warning". Other settings that can be configure are whether to write to stderr or not (default is true), and whether or not to write logs to a file (default is not write to a file).

GO Example:

   import "github.com/openshift/app-netutil/pkg/logging"

   logging.SetLogLevel("info")
   logging.SetLogStderr(true)
   logging.SetLogFile("/tmp/appnetutil.log")

C Example:

   #include "libnetutil_api.h"

   struct AppNetutilConfig config;

   // Update Log Settings
   memset(&config, 0, sizeof(config));
   config.log.level = LOG_LEVEL_INFO;
   config.log.stderr = true;
   config.log.filename = "/tmp/appnetutil.log";
   err = SetConfig(&config);

Downward API

The Kubernetes Downward API is leveraged to pass pod annotations and specific Downward API fields to a pod. This data is written to files in a directory defined by a VolumeMount in the podSpec. By default, app-netutil uses "/etc/podnetinfo", which is the directory Network Resources Injector (or SR-IOV DP Admission Controller in OpenShift) uses when Downward API fields are injected into a pod. app-netutil allows the base directory it should use to look for these files to be configured.

GO Example:

   import netlib "github.com/openshift/app-netutil/lib/v1alpha"

   netlib.SetDownwardAPIMountPath("/etc/podinfo")

C Example:

   #include "libnetutil_api.h"

   struct AppNetutilConfig config;

   // Update Downward API Settings
   memset(&config, 0, sizeof(config));
   config.downwardAPI.baseDir = "/etc/podinfo";
   err = SetConfig(&config);

Quick Start

This section provides examples of building the sample applications that use the Network Utility Library. This is just quick start guide, more details can be found in the links associated with each section.

Build GO APP

  1. Compile executable:
$ make go_sample

This builds application binary called go_app under bin/ dir.

  1. Run:
$ PCIDEVICE_INTEL_COM_SRIOV=0000:01:02.5,0000:01:0a.4 ./bin/go_app
|CPU      |: 0-63
|HugePage |: MyContainerName=sriov-example
| 0       |: ContainerName=sriov-example  Request 1G=0 2M=0 Ukn=1024  Limit 1G=0 2M=0 Ukn=1024
|Iface    |:
| 0       |: IfName=eth0  DeviceType=host  Network=  Default=true
|         |:   MAC=06:eb:7f:3a:48:85  IPs=[10.244.0.25]  DNS={Nameservers:[] Domain: Search:[] Options:[]}
| 1       |: IfName=net1  DeviceType=sr-iov  Network=default/sriov-net-a  Default=false
|         |:   MAC=  IPs=[]  DNS={Nameservers:[] Domain: Search:[] Options:[]}
|         |:   pci  1.0.0  PCI=0000:01:02.5  PF=  Vhostnet=  RdmaDevice=
| 2       |: IfName=net2  DeviceType=sr-iov  Network=default/sriov-net-b  Default=false
|         |:   MAC=  IPs=[]  DNS={Nameservers:[] Domain: Search:[] Options:[]}
|         |:   pci  1.0.0  PCI=0000:01:0a.4  PF=  Vhostnet=  RdmaDevice=

<CTRL>-C
  1. Clean up:
$ make clean

This cleans up built binary and softlinks.

For more details, see:

Build C APP

  1. Compile executable:
$ make c_sample

This builds application binary called c_sample under bin/ directory. The bin/ directory also contains the C header file libnetutil_api.h and shared library libnetutil_api.so needed to build the C APP.

  1. Run:
$ PCIDEVICE_INTEL_COM_SRIOV=0000:01:02.5,0000:01:0a.4 \
  LD_LIBRARY_PATH=$PWD/bin:$LD_LIBRARY_PATH \
  ./bin/c_sample
Starting sample C application.
Call NetUtil GetCPUInfo():
  cpuRsp.CPUSet = 0-63
Call NetUtil GetHugepages():
  MyContainerName=sriov-example
  Hugepages[0]:
    ContainerName=sriov-example  Request: 1G=0 2M=0 Ukn=1024  Limit: 1G=0 2M=0 Ukn=1024
Call NetUtil GetInterfaces():
  Interface[0]:
    DeviceType=host  Interface="eth0"
    MAC="06:eb:7f:3a:48:85"  IP="10.244.0.25"
  Interface[1]:
    DeviceType=SR-IOV  Name="default/sriov-net-a"  Interface="net1"
    Type=PCI  PCIAddress=0000:01:02.5
  Interface[2]:
    DeviceType=SR-IOV  Name="default/sriov-net-b"  Interface="net2"
    Type=PCI  PCIAddress=0000:01:0a.4
  1. Clean up:
$ make clean

This cleans up built binary and softlinks.

For more details, see:

Create testpod Image

The testpod image is a CentOS base image built the app-netutil library. It simply creates a container that runs the go_app sample applicatation described above.

  1. Build application container image:
$ make testpod
  1. Create application pod:
$ kubectl create -f samples/testpod/pod.yaml
  1. Check for pod logs:
$ kubectl logs testpod
I1202 18:47:09.067139       1 app_sample.go:16] starting sample application
I1202 18:47:09.069655       1 app_sample.go:20] CALL netlib.GetCPUInfo:
I1202 18:47:09.070542       1 resource.go:27] getting cpuset from path: /proc/1/root/sys/fs/cgroup/cpuset/cpuset.cpus
| CPU     |: 0-63
I1202 18:47:09.071110       1 app_sample.go:26] netlib.GetCPUInfo Response:
I1202 18:47:09.071126       1 app_sample.go:30] CALL netlib.GetHugepages:
I1202 18:47:09.071149       1 hugepages.go:22] GetHugepages: Open /etc/podnetinfo/hugepages_request
I1202 18:47:09.071349       1 hugepages.go:25] Error getting /etc/podnetinfo/hugepages_request info: open /etc/podnetinfo/hugepages_request: no such file or directory
I1202 18:47:09.071363       1 hugepages.go:35] GetHugepages: Open /etc/podnetinfo/hugepages_limit
I1202 18:47:09.071390       1 hugepages.go:38] Error getting /etc/podnetinfo/hugepages_limit info: open /etc/podnetinfo/hugepages_limit: no such file or directory
I1202 18:47:09.071404       1 app_sample.go:33] Error calling netlib.GetHugepages: open /etc/podnetinfo/hugepages_request: no such file or directory
I1202 18:47:09.071418       1 app_sample.go:40] CALL netlib.GetInterfaces:
I1202 18:47:09.071424       1 network.go:39] GetInterfaces: ENTER
I1202 18:47:09.071433       1 network.go:45] GetInterfaces: Open /etc/podnetinfo/annotations
I1202 18:47:09.071821       1 network.go:68]   s-k8s.v1.cni.cncf.io/network-status="[{\n    \"name\": \"\",\n    \"interface\": \"eth0\",\n    \"ips\": [\n        \"10.244.0.57\"\n    ],\n    \"mac\": \"82:33:bb:68:3a:3c\",\n    \"default\": true,\n    \"dns\": {}\n}]"
I1202 18:47:09.071832       1 network.go:72]   PartsLen-2
I1202 18:47:09.071843       1 network.go:74]   parts[0]-k8s.v1.cni.cncf.io/network-status
I1202 18:47:09.072035       1 network.go:68]   s-k8s.v1.cni.cncf.io/networks-status="[{\n    \"name\": \"\",\n    \"interface\": \"eth0\",\n    \"ips\": [\n        \"10.244.0.57\"\n    ],\n    \"mac\": \"82:33:bb:68:3a:3c\",\n    \"default\": true,\n    \"dns\": {}\n}]"
I1202 18:47:09.072047       1 network.go:72]   PartsLen-2
I1202 18:47:09.072054       1 network.go:74]   parts[0]-k8s.v1.cni.cncf.io/networks-status
I1202 18:47:09.072067       1 network.go:68]   s-kubernetes.io/config.seen="2020-12-02T13:46:08.010504651-05:00"
I1202 18:47:09.072075       1 network.go:72]   PartsLen-2
I1202 18:47:09.072082       1 network.go:74]   parts[0]-kubernetes.io/config.seen
I1202 18:47:09.072090       1 network.go:68]   s-kubernetes.io/config.source="api"
I1202 18:47:09.072097       1 network.go:72]   PartsLen-2
I1202 18:47:09.072105       1 network.go:74]   parts[0]-kubernetes.io/config.source
I1202 18:47:09.072111       1 networkstatus.go:42] PRINT EACH NetworkStatus - len=1
I1202 18:47:09.072118       1 networkstatus.go:46]   status:
I1202 18:47:09.072126       1 networkstatus.go:47] { eth0 [10.244.0.57] 82:33:bb:68:3a:3c true {[]  [] []} <nil>}
I1202 18:47:09.072162       1 userspace.go:49] PRINT EACH Userspace MappedDir
I1202 18:47:09.072166       1 userspace.go:50]   usrspMappedDir:
I1202 18:47:09.072171       1 userspace.go:51] 
I1202 18:47:09.072176       1 userspace.go:53] PRINT EACH Userspace ConfigData
I1202 18:47:09.072183       1 network.go:112] PROCESS ENV:
I1202 18:47:09.072190       1 resource.go:38] getting environment variables from path: /proc/1/environ
I1202 18:47:09.072232       1 network.go:165] eth0 is the "default" interface, mark as "host"
I1202 18:47:09.072246       1 network.go:221] RESPONSE:
I1202 18:47:09.072258       1 network.go:223] &{host { eth0 [10.244.0.57] 82:33:bb:68:3a:3c true {[]  [] []} <nil>}}
I1202 18:47:09.072280       1 app_sample.go:46] netlib.GetInterfaces Response:
| 0       |: IfName=eth0  DeviceType=host  Network=  Default=true
|         |:   MAC=82:33:bb:68:3a:3c  IPs=[10.244.0.57]  DNS={Nameservers:[] Domain: Search:[] Options:[]}
...

NOTE: If the hugepage Downward API is not included in the Pod Spec, which is the case for samples/testpod/pod.yaml, then the hugepage annotation files will not exist and hugepage data will not be retrieved. The hugepage Downward API requires Kubernetes 1.20 or greater and the feature gate to be enable.

  1. Delete application pod:
$ kubectl delete -f deployments/pod.yaml

For more details, see:

Create dpdk-app-centos Image

The dpdk-app-centos image is a CentOS base image built with DPDK and includes the app-netutil library. The setup to run the image is more complicated and depends on if you are using vhost interfaces from something like a Userspace CNI or SR-IOV VFs from SR-IOV CNI. Below is the quick command to build the image, but it is recommended that additional README files are consulted for detailed setup instructions.

  1. Build application container image:
$ make dpdk_app

For more details, see:

app-netutil's People

Contributors

amorenoz avatar billy99 avatar dougbtv avatar openshift-merge-robot avatar tfherbert avatar thrasher-redhat avatar zshi-redhat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

app-netutil's Issues

duplicate mount path issue

When create an app-netutil pod, the /etc/podnetinfo path don't automatically removed after execute oc delete pod podname, then try to create a new pod using oc create -f pod-app-netutil.yaml
oc -n default create -f pod-app-netutil.yaml, it threw below error

The Pod "dpdk-app-netutil" is invalid: spec.containers[0].volumeMounts[2].mountPath: Invalid value: "/etc/podnetinfo": must be unique

cat pod-app-netutil.yaml
kind: Pod
metadata:
name: dpdk-app-netutil
annotations:
k8s.v1.cni.cncf.io/networks: 'sriov-intel, sriov-intel, sriov-intel, sriov-intel'
spec:
# nodeSelector:
# kubernetes.io/hostname: worker001-740xd
containers:

  • name: app-netutil
    image: quay.io/openshift-psap-qe/dpdk-app-centos
    imagePullPolicy: IfNotPresent
    securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    seccompProfile:
    type: RuntimeDefault
    allowPrivilegeEscalation: false
    capabilities:
    drop:
    - ALL
    volumeMounts:
    • name: pod-netinfo
      mountPath: /etc/podnetinfo
      resources:
      requests:
      openshift.io/intelsriov: 4
      cpu: 12
      memory: 100Mi
      hugepages-1Gi: "2Gi"
      limits:
      openshift.io/intelsriov: 4
      cpu: 12
      memory: 100Mi
      hugepages-1Gi: "2Gi"
      volumes:
    • name: pod-netinfo
      downwardAPI:
      items:
      - path: "annotations"
      fieldRef:
      fieldPath: metadata.annotations

userspace-cni sockets path is not correct

Hi,

I'm currently testing kubevirt v0.58.0 patch from Juniper allowing to create vhostuser interfaces with userspace-cni.

I had this virt-launcher pod error:

"virError(Code=1, Domain=10, Message='internal error: process exited while connecting to monitor: 2023-01-26T09:17:29.655269Z qemu-kvm: -chardev socket,id=charua-vhost-user-net-1,path=/var/lib/cni/usrcni4ce780d6e1ba-net1,server=on: Failed to bind socket to /var/lib/cni/usrcni4ce780d6e1ba-net1: Permission denied')" }

It seems the socket Path (/var/lib/cni/usrcni4ce780d6e1ba-net1) is missing a / between mappedDir and SocketFile:

Path: usrspData.mappedDir + configData.Config.VhostConf.Socketfile,

For information, even if default mappedDir in Juniper patched Kubevirt is defined with an ending /, it's used to create the VolumeMount with filepath.Join that strips the ending /, and this VolumeMount path is then added by userspace-cni to pod annotation.

Regards,

Ben.

Passing command line arguments to C programs fails

A C program linked to libnetutil_api.so, if given command line arguments, fails with the following message:


 ./bin/c_sample -whatever
Starting sample C application.
Call NetUtil GetCPUInfo():
flag provided but not defined: -whatever
Usage of ./bin/c_sample:
  -alsologtostderr
        log to standard error as well as files
  -log_backtrace_at value
        when logging hits line file:N, emit a stack trace
  -log_dir string
        If non-empty, write log files in this directory
  -logtostderr
        log to standard error instead of files
  -stderrthreshold value
        logs at or above this threshold go to stderr
  -v value
        log level for V logs
  -vmodule value
        comma-separated list of pattern=N settings for file-filtered logging

Extra SR-IOV interface detected per vdpa interface

When detecting vdpa interfaces, app-netutil detects an extra SR-IOV interface per vdpa interface.

EAL: failed to parse device " 0000:65:00.5"
EAL: Unable to parse device ' 0000:65:00.5'
EAL: Error - exiting with code: 1
ENTER dpdk-app:
 argc=1
 l2fwd
  cpuRsp.CPUSet = 0-31
  Couldn't get Hugepage info, defaulting to 1024, err code: 1
  Interface[0]:
    DeviceType=SR-IOV  Interface="eth0"
    MAC="5e:87:8e:19:2d:e6"  IP="10.244.0.17"
    Type=PCI  PCIAddress=0000:65:00.5
  Interface[1]:
    DeviceType=vDPA  Name="default/vdpa-mlx-vhost-net-2000"  Interface="net1"
    Type=vDPA  ParentDevice=vdpa3  Driver=vhost  Path=/dev/vhost-vdpa-3  PCIAddress=0000:65:00.5
  Interface[2]:
    DeviceType=vDPA  Name="default/vdpa-mlx-vhost-net-1000"  Interface="net2"
  Cause: Invalid EAL parameters
    Type=vDPA  ParentDevice=vdpa2  Driver=vhost  Path=/dev/vhost-vdpa-2  PCIAddress=0000:65:00.4
  Interface[3]:
    DeviceType=SR-IOV
    Type=PCI  PCIAddress=0000:65:00.4
 myArgc=15

Change logging from glog to something else

Issue #34 uncovered the calling flag.parse() in the library consumed the argc and argv of the sample application, so PR #35 removed the flag.parse() call from c_api/netutil_c_api.go. Now, each log when APIs are called from the C API functions have the error: ERROR: logging before flag.Parse: ... For example:

ERROR: logging before flag.Parse: I0413 15:52:31.386675  965211 network.go:40] GetInterfaces: Version=  Git Commit=7a1305d
ERROR: logging before flag.Parse: I0413 15:52:31.386696  965211 network.go:48] GetInterfaces: "annotations" file: /etc/podnetinfo/annotations does not exist.
ERROR: logging before flag.Parse: I0413 15:52:31.386710  965211 network.go:117] PROCESS ENV:
ERROR: logging before flag.Parse: I0413 15:52:31.386723  965211 resource.go:40] getting environment variables from path: /proc/965211/environ
ERROR: logging before flag.Parse: I0413 15:52:31.386769  965211 network.go:126]   k:PCIDEVICE_INTEL_COM_SRIOV v:0000:01:02.5,0000:01:0a.4
ERROR: logging before flag.Parse: I0413 15:52:31.386783  965211 network.go:153]      Adding ID:0000:01:02.5
ERROR: logging before flag.Parse: I0413 15:52:31.386793  965211 network.go:153]      Adding ID:0000:01:0a.4
ERROR: logging before flag.Parse: I0413 15:52:31.386811  965211 network.go:228] Adding 0000:01:02.5 as new interface because no other matches, type "sr-iov"
ERROR: logging before flag.Parse: I0413 15:52:31.386825  965211 network.go:228] Adding 0000:01:0a.4 as new interface because no other matches, type "sr-iov"
ERROR: logging before flag.Parse: I0413 15:52:31.386837  965211 network.go:233] RESPONSE:
ERROR: logging before flag.Parse: I0413 15:52:31.386848  965211 network.go:235] &{sr-iov {  []  false {[]  [] []} 0xc0003b2b00}}
:

So swap out the glog calls for something that doesn't require flag.parse(), or switch to fmt.Printf().

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.