cnitool: Add or remove network interfaces from a network namespace cnitool add <net> <netns> cnitool del <net> <netns>
cnitool的使用方式如下:其中<net>是配置文件所在目錄,一般為/etc/cni/net.d/*.conf文件,<netns>為network namespace的目錄文件,一般為/var/run/netns/NS-ID
1、cni/libcni/api.go
// AddNetwork executes the plugin with ADD command
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
(1)、首先調用pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path),該函數用於在c.Path中尋找對應插件的可執行文件,然后返回路徑
(2)、調用 return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt)),net.Bytes是配置文件的序列化二進制碼,其中c.args函數主要的作用是填充並返回一個*invoke.Args類型:
return &invoke.Args {
Command: action,
ContainerID: rt.ContainerID,
NetNS: rt.NetNS,
PluginArgs: rt.Args,
IfName: rt.IfName,
Path: strings.Join(c.Path, string(os.PathListSeparator)),
}
NetworkConfig的數據結構如下所示,用於表示容器要加入的網絡:
// libcni/api.go
type NetworkConfig struct {
Network *types.NetConf
Bytes []byte // 在初始化CNI的時候,會將配置文件寫入Bytes
}
// pkg/types/types.go
// NetConf describes a network
type NetConf struct {
CNIVersion string
Name string
IPAM string {
Type string
}
DNS DNS
}
Runtime的數據結構如下所示,用於表示加入網絡的容器的信息:
type RuntimeConf struct {
ContainerID string
NetNS string
IfName string
Args [][2]string
}
CNIConfig的數據結構如下所示:CNIconfig包含的是bridge,dhcp等插件的可執行文件的目錄的路徑集合
type CNIConfig struct {
Path []string
}
2、cni/pkg/invoke/exec.go
func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error)
該函數只是簡單地返回 return defaultPluginExec.WithResult(pluginPath, netconf, args)
其中defaultPluginExec是一個*PluginExec的類型變量,賦值過程如下所示:
var defaultPluginExec = &PluginExec{
RawExec: &RawExec{Stderr: os.Stderr}, --->RawExec又是在raw_exec.go中定義的一個結構類型,其中只有一個Stderr的io.Writer類型
VersionDecoder: &version.PluginDecoder{},
}
// 其中PluginExec的定義如下所示:
type PluginExec struct {
RawExec interface {
ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error)
}
VersionDecoder interface {
Decode(jsonBytes []byte) (version.PluginInfo, error)
}
}
3、cni/pkg/invoke/exec.go
func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error)
(1)、調用stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv()),args.AsEnv()將args里的內容轉變為環境變量返回,例如CNI_COMMAND=ADD等等
// Plugin must return result in same version as specified in netconf
(2)、調用versionDecoder := &version.ConfigDecoder{}和confVersion, err := versionDecoder.Decode(netconf)從netconf中解析處CNI的版本
(3)、最后調用return version.NewResult(confVersion, stdoutBytes)返回相應版本的Result
4、cni/pkg/invoke/args.go
func (args *Args) AsEnv() []string
1、調用env := os.Environ()獲取已有的環境變量
2、若args.PluginArgsStr為"",則將args.PluginArgs拼接為“A=B;C=D”的形式
3、調用env = append(env,
"CNI_COMMAND=" + args.Command,
"CNI_CONTAINERID=" + args.ContainerID,
"CNI_NETNS=" + args.NetNS,
"CNI_ARGS=" + pluginArgsStr,
"CNI_IFNAME=" + args.IfName,
"CNI_PATH="+args.Path)
),將參數都作為環境變量傳遞給plugin
RawExec的結構如下所示:
type RawExec struct {
Stderr io.Writer
}
5、cni/pkg/invoke/raw_exec.go
func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error)
(1)、首先獲得stdout := bytes.Buffer{}作為輸出緩沖器
(2)、創建執行命令並調用c.Run()運行
c := exec.Cmd {
Env: environ,
Path: pluginPath,
Args: []string{pluginPath},
Stdin: bytes.NewBuffer(stdinData),
Stdout: stdout,
Stderr: e.Stderr,
}
(3)、最后返回stdout.Bytes(),插件直接將結果輸出到stdout
