intel userspace cni 源码分析
基于 截至2022.06.27 最新的 intel userspace cni 源码
KubeVirt + DPDK,涉及到一个intel的网络插件intel userspace cni,在搞KubeVirt + DPDK前先梳理下intel userspace cni的源码。
# intel userspace cni 大致目录结构,暂时不涉及vpp,忽略vpp的代码├── cniovs
│ ├── cniovs.go - ovs网络插件
│ ├── localdb.go - ovs本地存储
│ ├── ovsctrl.go - 各种ovs-vsctl命令
├── cnivpp - vpp相关,本文不涉及vpp-dpdk,只涉及ovs-dpdk
├── logging - 日志
├── pkg
│ ├── annotations
│ │ ├── annotations.go - pod annotation操作
│ ├── configdata
│ │ ├── configdata.go - 配置数据结构
│ ├── k8sclient
│ │ ├── k8sclient.go - k8s 客户端(调用client-go)
│ └── types
│ └── types.go - userspace cni自定义的类型
└── userspace
└── userspace.go - main文件入口,cni的add/get/del方法的实现language-bash复制代码
cat cniovs/localdb.go
package cniovsimport (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/containernetworking/cni/pkg/skel"
"github.com/intel/userspace-cni-network-plugin/pkg/annotations"
"github.com/intel/userspace-cni-network-plugin/pkg/configdata"
"github.com/intel/userspace-cni-network-plugin/pkg/types")// This structure is a union of all the OVS data (for all types of// interfaces) that need to be preserved for later use.type OvsSavedData struct {
Vhostname string `json:"vhostname"` // Vhost Port name
VhostMac string `json:"vhostmac"` // Vhost port MAC address
IfMac string `json:"ifmac"` // Interface Mac address}// SaveConfig() - Some data needs to be saved for cmdDel().// This function squirrels the data away to be retrieved later.func SaveConfig(conf *types.NetConf, args *skel.CmdArgs, data *OvsSavedData) error {
// Current implementation is to write data to a file with the name:
// /var/run/ovs/cni/data/local-<ContainerId:12>-<IfName>.json
fileName := fmt.Sprintf("local-%s-%s.json", args.ContainerID[:], args.IfName)
if dataBytes, err := json.Marshal(data); err == nil {
localDir := annotations.DefaultLocalCNIDir if _, err := os.Stat(localDir); err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(localDir, ); err != nil {
return err }
} else {
return err }
}
path := filepath.Join(localDir, fileName)
return ioutil.WriteFile(path, dataBytes, )
} else {
return fmt.Errorf("ERROR: serializing delegate OVS saved data: %v", err)
}}func LoadConfig(conf *types.NetConf, args *skel.CmdArgs, data *OvsSavedData) error {
fileName := fmt.Sprintf("local-%s-%s.json", args.ContainerID[:], args.IfName)
localDir := annotations.DefaultLocalCNIDir
path := filepath.Join(localDir, fileName)
if _, err := os.Stat(path); err == nil {
if dataBytes, err := ioutil.ReadFile(path); err == nil {
if err = json.Unmarshal(dataBytes, data); err != nil {
return fmt.Errorf("ERROR: Failed to parse OVS saved data: %v", err)
}
} else {
return fmt.Errorf("ERROR: Failed to read OVS saved data: %v", err)
}
} else {
path = ""
}
// Delete file (and directory if empty)
configdata.FileCleanup(localDir, path)
return nil}language-go复制代码
以上代码: OvsSavedData是存储OVS data的数据结构,后面需要json序列号和反序列化,标记json key, SaveConfig()方法将OvsSavedData保存至文件/var/run/ovs/cni/data/local-ContainerId:12-.json LoadConfig()和SaveConfig()反过来,将文件内容读到OvsSavedData 存储用文件的形式保存OvsSavedData为了给 cmdDel() 方法用
cniovs/ovsctrl.go (代码太长,可在github查看,这里为了减少篇幅仅注释下方法)
每个方法对应一条openvswitch命令。
func createVhostPort // COMMAND: ovs-vsctl add-port <bridge_name> <sock_name> -- set Interface <sock_name> type=<dpdkvhostuser|dpdkvhostuserclient>func deleteVhostPort // COMMAND: ovs-vsctl del-port <bridge_name> <sock_name>func createBridge // COMMAND: ovs-vsctl add-br <bridge_name> -- set bridge <bridge_name> datapath_type=netdevfunc configL2Bridge // COMMAND: ovs-ofctl add-flow <bridge_name> actions=NORMALfunc deleteBridge // COMMAND: ovs-vsctl del-br <bridge_name>func getVhostPortMac // COMMAND: ovs-vsctl --bare --columns=mac find port name=<sock_name>func findBridge // COMMAND: ovs-vsctl --bare --columns=name find bridge name=<bridge_name>func doesBridgeContainInterfaces // COMMAND: ovs-vsctl list-ports <bridge_name>language-go复制代码
cniovs/cniovs.go (代码太长,可在github查看,这里为了减少篇幅仅注释下方法)
func AddOnHost // step1:根据conf.HostConf.BridgeConf.BridgeName创建ovs bridge,若未配置用默认br0代替。step2:创建bridge interface仅支持conf.HostConf.IfType == "vhostuser"一种类型。step3:Save Config - Save Create Data for Deletefunc DelFromHost // step1:用cniovs/localdb.go 从本地保存的json文件中 Load Config 删除bridge interface,检查brdge,若没有interface则删除bridgefunc AddOnContainer // 调用下面的SaveRemoteConfig方法Write configuration data(下面的ConfigurationData struct) that will be consumed by container。func DelFromContainer // 调用configdata.FileCleanup方法清理文件夹(文件夹下0个文件则清理文件夹)和文件func addLocalDeviceVhost // 在 AddOnHost 方法中被调用,用于创建vhostuser socket以及相关操作func delLocalDeviceVhost // 在 DelFromHost 方法中被调用,删除ovs bridge vhostuser port,umount 相关文件夹,删除vhostuser socket 以及相关文件func generateRandomMacAddress // 生成随机mac地址func getShortSharedDir createSharedDir setSharedDirGroup // 这三个方法参考 https://www.backendcloud.cn/2022/06/24/userspace-cni-for-kubevirt/language-go复制代码
type ConfigurationData struct {
ContainerId string `json:"containerId"` // From args.ContainerId, used locally. Used in several place, namely in the socket filenames.
IfName string `json:"ifName"` // From args.IfName, used locally. Used in several place, namely in the socket filenames.
Name string `json:"name"` // From NetConf.Name
Config UserSpaceConf `json:"config"` // From NetConf.ContainerConf
IPResult current.Result `json:"ipResult"` // Network Status also has IP, but wrong format}language-go复制代码
pgk文件夹主要三个go文件,annotations.go,configdata.go,k8sclient.go都是k8s client更新pod annotation相关的,联合起来解释 (代码太长,可在github查看,这里为了减少篇幅仅注释下方法)
func saveRemoteConfig // 分2种情况,有k8sclient,k8sclient.WritePodAnnotation写入集群的PodAnnotation。若没有k8sclient,用文件保存信息 func getK8sArgs // 将cni main方法的命令参数转成go结构体变量k8sArgs中去func getK8sClient // 生成k8sclient。分2种情况,传参传入了kubernetes.Interface,直接返会该client,另一种情况没有传入client,则根据传参kubeconfig或者环境变量生成k8sclient。func GetPod // 用k8s client-go包和上面生成k8sclinet和pod ns&podname信息,get podfunc WritePodAnnotation // 更新pod annotationfunc setPodAnnotationConfigData // 将types.ConfigurationData更新的pod中去language-go复制代码
userspace/userspace.go 最后是包含main方法的go代码文件,入口main方法实现了k8s cni定义的add,get,del方法
func main() {
skel.PluginMain(
func(args *skel.CmdArgs) error {
return cmdAdd(args, nil, nil)
},
func(args *skel.CmdArgs) error {
return cmdGet(args, nil, nil)
},
func(args *skel.CmdArgs) error {
return cmdDel(args, nil, nil)
},
cniSpecVersion.All,
"CNI plugin that manages DPDK based interfaces")}language-go复制代码
实际上 intel userspace cni的get方法是空方法,就实现了add和del
add主要有5步:
- 通过cni命令的args获取网络namespace
- get host和pod的共享目录
- 增加host相关的网络资源
- 通过cni命令的args获取ip信息,通过reset参数传递给AddOnContainer方法
- 增加pod相关的网络资源
del主要有5步:
- get host和pod的共享目录
- 删除host相关的网络资源
- 清理pod相关的网络资源
- 删除ipam相关配置
- 删除网络namespac
相关文章
- 五分钟快速搭建一个实时人脸口罩检测系统(OpenCV+PaddleHub 含源码)
- [Delta][SQL] Delta开源付费功能,最全分析ZOrder的源码实现流程
- 【说站】智力考验看成语猜古诗句微信小程序源码
- Python 源码混淆与加密
- react源码分析:组件的创建和更新
- k8s first commit 源码分析之 API Server
- React-Hooks源码解读
- React源码中的计算流程和优先级
- 基于SpringBoot和Vue开发的功能强大的图书馆系统(附源码)
- selenium源码通读·10 |webdriver/common/proxy.py-Proxy类分析
- selenium源码通读·13 |webdriver/support分析
- 联邦学习开源框架FATE-Flow 源码分析
- Spring Cloud 微服务实战——nacos 服务注册中心搭建(附源码)
- Java对象创建源码分析
- 禾匠商城小程序微擎版V3_3.1.53完整源码包+小程序前端
- 【Android 异步操作】Timer 定时器 ( Timer 与 TimerTask 基本使用 | Timer 定时器常用用法 | Timer 源码分析 )
- 【Android 安全】DEX 加密 ( Application 替换 | Android 应用启动原理 | LoadedApk 源码分析 )
- 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
- 【Linux 内核 内存管理】物理内存组织结构 ③ ( 内存管理系统三级结构 | 内存节点描述 | 内存节点 pglist_data 结构体 | pglist_data 结构体源码 )
- 【Linux 内核 内存管理】物理分配页 ⑥ ( get_page_from_freelist 快速路径调用函数源码分析 | 检查内存区域水线 | 判定节点回收 | 判定回收距离 | 回收分配页 )
- Spark源码分析之RDD的生成及stage的切分详解大数据
- 探索QT在Linux中的源码与应用(qtlinux源码)
- 分析Redis源码深入了解缓存存储引擎(解剖redis源码书籍)
- jQuery源码分析笔记(4)Ready函数
- 模拟电子签章盖章效果的jQuery插件源码