Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support 'mac' to specify MAC address for interface #148

Merged
merged 9 commits into from
Nov 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,47 @@ NAME READY STATUS RESTARTS AGE
multus-multi-net-poc 1/1 Running 0 30s
```

#### Pod Annotation Parameters

JSON formated network annotation in Pod can have several parameters as following:

- namespace: Kubernetes namespace that the target network attach definition is defined in.
- mac: MAC address (e.g "c2:11:22:33:44:66") for target network interface
- interfaceRequest: interface name for target network interface

Note: If you add `mac`, please add 'tuning' plugin into target network attach definition as CNI plugin chaining as following.

```
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan with tuning
spec:
config: '{
"cniVersion": "0.3.0",
"name": "chains",
"plugins": [ {
"type": "macvlan",
"master": "eth0",
"mode": "bridge",
"ipam": {
"type": "host-local",
"subnet": "192.168.1.0/24",
"rangeStart": "192.168.1.200",
"rangeEnd": "192.168.1.216",
"routes": [
{ "dst": "0.0.0.0/0" }
],
"gateway": "192.168.1.1"
}
},
{
"type":"tuning"
}]

}'
```

### Verifying Pod network interfaces

1. Run `ifconfig` command in Pod:
Expand Down
8 changes: 4 additions & 4 deletions k8sclient/k8sclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func getKubernetesDelegate(client KubeClient, net *types.NetworkSelectionElement
return nil, resourceMap, err
}

delegate, err := types.LoadDelegateNetConf(configBytes, net.InterfaceRequest, deviceID)
delegate, err := types.LoadDelegateNetConf(configBytes, net, deviceID)
if err != nil {
return nil, resourceMap, err
}
Expand Down Expand Up @@ -536,7 +536,7 @@ func getDefaultNetDelegateCRD(client KubeClient, net string, confdir string) (*t
return nil, err
}

delegate, err := types.LoadDelegateNetConf(configBytes, "", "")
delegate, err := types.LoadDelegateNetConf(configBytes, nil, "")
if err != nil {
return nil, err
}
Expand All @@ -556,7 +556,7 @@ func getNetDelegate(client KubeClient, netname string, confdir string) (*types.D
var configBytes []byte
configBytes, err = getCNIConfigFromFile(netname, confdir)
if err == nil {
delegate, err := types.LoadDelegateNetConf(configBytes, "", "")
delegate, err := types.LoadDelegateNetConf(configBytes, nil, "")
if err != nil {
return nil, err
}
Expand All @@ -572,7 +572,7 @@ func getNetDelegate(client KubeClient, netname string, confdir string) (*types.D
var configBytes []byte
configBytes, err = getCNIConfigFromFile("", netname)
if err == nil {
delegate, err := types.LoadDelegateNetConf(configBytes, "", "")
delegate, err := types.LoadDelegateNetConf(configBytes, nil, "")
if err != nil {
return nil, err
}
Expand Down
14 changes: 14 additions & 0 deletions multus/multus.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"time"
Expand Down Expand Up @@ -170,6 +171,19 @@ func delegateAdd(exec invoke.Exec, ifName string, delegate *types.DelegateNetCon
return nil, logging.Errorf("cannot set %q ifname to %q: %v", delegate.Conf.Type, ifName, err)
}

if delegate.MacRequest != "" {
// validate Mac address
_, err := net.ParseMAC(delegate.MacRequest)
if err != nil {
return nil, logging.Errorf("failed to parse mac address %q", delegate.MacRequest)
}

if os.Setenv("CNI_ARGS", fmt.Sprintf("IgnoreUnknown=true;MAC=%s", delegate.MacRequest)) != nil {
return nil, logging.Errorf("cannot set %q mac to %q: %v", delegate.Conf.Type, delegate.MacRequest, err)
}
logging.Debugf("Set MAC address %q to %q", delegate.MacRequest, ifName)
}

if delegate.ConfListPlugin != false {
result, err := conflistAdd(rt, delegate.Bytes, binDir, exec)
if err != nil {
Expand Down
105 changes: 93 additions & 12 deletions multus/multus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ func TestMultus(t *testing.T) {
}

type fakePlugin struct {
expectedEnv []string
expectedConf string
expectedIfname string
result cnitypes.Result
err error
expectedEnv []string
expectedConf string
expectedIfname string
expectedMacAddr string
result cnitypes.Result
err error
}

type fakeExec struct {
Expand All @@ -59,7 +60,7 @@ type fakeExec struct {
plugins []*fakePlugin
}

func (f *fakeExec) addPlugin(expectedEnv []string, expectedIfname, expectedConf string, result *types020.Result, err error) {
func (f *fakeExec) addPlugin(expectedEnv []string, expectedIfname, expectedMacAddr, expectedConf string, result *types020.Result, err error) {
f.plugins = append(f.plugins, &fakePlugin{
expectedEnv: expectedEnv,
expectedConf: expectedConf,
Expand Down Expand Up @@ -123,6 +124,9 @@ func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []str
if plugin.expectedIfname != "" {
Expect(os.Getenv("CNI_IFNAME")).To(Equal(plugin.expectedIfname))
}
if plugin.expectedMacAddr != "" {
Expect(os.Getenv("MAC")).To(Equal(plugin.expectedMacAddr))
}
if len(plugin.expectedEnv) > 0 {
matchArray(gatherCNIEnv(), plugin.expectedEnv)
}
Expand Down Expand Up @@ -203,7 +207,7 @@ var _ = Describe("multus operations", func() {
"cniVersion": "0.2.0",
"type": "weave-net"
}`
fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil)
fExec.addPlugin(nil, "eth0", "", expectedConf1, expectedResult1, nil)

expectedResult2 := &types020.Result{
CNIVersion: "0.2.0",
Expand All @@ -216,7 +220,7 @@ var _ = Describe("multus operations", func() {
"cniVersion": "0.2.0",
"type": "other-plugin"
}`
fExec.addPlugin(nil, "net1", expectedConf2, expectedResult2, nil)
fExec.addPlugin(nil, "net1", "", expectedConf2, expectedResult2, nil)

os.Setenv("CNI_COMMAND", "ADD")
os.Setenv("CNI_IFNAME", "eth0")
Expand All @@ -241,6 +245,83 @@ var _ = Describe("multus operations", func() {

})

It("executes delegates with interface name and MAC addr", func() {
podNet := `[{"name":"net1",
"interfaceRequest": "test1"},
{"name":"net2",
"mac": "c2:11:22:33:44:66"}
]`
fakePod := testhelpers.NewFakePod("testpod", podNet)
net1 := `{
"name": "net1",
"type": "mynet",
"cniVersion": "0.2.0"
}`
net2 := `{
"name": "net2",
"type": "mynet2",
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
ContainerID: "123456789",
Netns: testNS.Path(),
IfName: "eth0",
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
StdinData: []byte(`{
"name": "node-cni-network",
"type": "multus",
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
"delegates": [{
"name": "weave1",
"cniVersion": "0.2.0",
"type": "weave-net"
}]
}`),
}

fExec := &fakeExec{}
expectedResult1 := &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
IP: *testhelpers.EnsureCIDR("1.1.1.2/24"),
},
}
expectedConf1 := `{
"name": "weave1",
"cniVersion": "0.2.0",
"type": "weave-net"
}`
fExec.addPlugin(nil, "eth0", "", expectedConf1, expectedResult1, nil)
fExec.addPlugin(nil, "test1", "", net1, &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
IP: *testhelpers.EnsureCIDR("1.1.1.3/24"),
},
}, nil)
fExec.addPlugin(nil, "net2", "c2:11:22:33:44:66", net2, &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
IP: *testhelpers.EnsureCIDR("1.1.1.4/24"),
},
}, nil)

fKubeClient := testhelpers.NewFakeKubeClient()
fKubeClient.AddPod(fakePod)
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net1", net1)
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net2", net2)

os.Setenv("CNI_COMMAND", "ADD")
os.Setenv("CNI_IFNAME", "eth0")
result, err := cmdAdd(args, fExec, fKubeClient)
Expect(err).NotTo(HaveOccurred())
Expect(fExec.addIndex).To(Equal(len(fExec.plugins)))
Expect(fKubeClient.PodCount).To(Equal(2))
Expect(fKubeClient.NetCount).To(Equal(2))
r := result.(*types020.Result)
// plugin 1 is the masterplugin
Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue())
})

It("executes delegates and kubernetes networks", func() {
fakePod := testhelpers.NewFakePod("testpod", "net1,net2")
net1 := `{
Expand Down Expand Up @@ -287,14 +368,14 @@ var _ = Describe("multus operations", func() {
"cniVersion": "0.2.0",
"type": "weave-net"
}`
fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil)
fExec.addPlugin(nil, "net1", net1, &types020.Result{
fExec.addPlugin(nil, "eth0", "", expectedConf1, expectedResult1, nil)
fExec.addPlugin(nil, "net1", "", net1, &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
IP: *testhelpers.EnsureCIDR("1.1.1.3/24"),
},
}, nil)
fExec.addPlugin(nil, "net2", net2, &types020.Result{
fExec.addPlugin(nil, "net2", "", net2, &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
IP: *testhelpers.EnsureCIDR("1.1.1.4/24"),
Expand Down Expand Up @@ -358,7 +439,7 @@ var _ = Describe("multus operations", func() {
]
}
}`
fExec.addPlugin(nil, "eth0", expectedConf1, nil, nil)
fExec.addPlugin(nil, "eth0", "", expectedConf1, nil, nil)
os.Setenv("CNI_COMMAND", "ADD")
os.Setenv("CNI_IFNAME", "eth0")
_, err := cmdAdd(args, fExec, nil)
Expand Down
23 changes: 15 additions & 8 deletions types/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,20 @@ func LoadDelegateNetConfList(bytes []byte, delegateConf *DelegateNetConf) error
}

// Convert raw CNI JSON into a DelegateNetConf structure
func LoadDelegateNetConf(bytes []byte, ifnameRequest, deviceID string) (*DelegateNetConf, error) {
func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID string) (*DelegateNetConf, error) {
var err error
logging.Debugf("LoadDelegateNetConf: %s, %v, %s", string(bytes), net, deviceID)

// If deviceID is present, inject this into delegate config
if deviceID != "" {
if updatedBytes, err := delegateAddDeviceID(bytes, deviceID); err != nil {
var updatedBytes []byte
if updatedBytes, err = delegateAddDeviceID(bytes, deviceID); err != nil {
return nil, logging.Errorf("error in LoadDelegateNetConf - delegateAddDeviceID unable to update delegate config: %v", err)
} else {
bytes = updatedBytes
}
bytes = updatedBytes
}

delegateConf := &DelegateNetConf{}
logging.Debugf("LoadDelegateNetConf: %s, %s", string(bytes), ifnameRequest)
if err := json.Unmarshal(bytes, &delegateConf.Conf); err != nil {
return nil, logging.Errorf("error in LoadDelegateNetConf - unmarshalling delegate config: %v", err)
}
Expand All @@ -74,8 +76,13 @@ func LoadDelegateNetConf(bytes []byte, ifnameRequest, deviceID string) (*Delegat
}
}

if ifnameRequest != "" {
delegateConf.IfnameRequest = ifnameRequest
if net != nil {
if net.InterfaceRequest != "" {
delegateConf.IfnameRequest = net.InterfaceRequest
}
if net.MacRequest != "" {
delegateConf.MacRequest = net.MacRequest
}
}

delegateConf.Bytes = bytes
Expand Down Expand Up @@ -216,7 +223,7 @@ func LoadNetConf(bytes []byte) (*NetConf, error) {
if err != nil {
return nil, logging.Errorf("error marshalling delegate %d config: %v", idx, err)
}
delegateConf, err := LoadDelegateNetConf(bytes, "", "")
delegateConf, err := LoadDelegateNetConf(bytes, nil, "")
if err != nil {
return nil, logging.Errorf("failed to load delegate %d config: %v", idx, err)
}
Expand Down
5 changes: 3 additions & 2 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type DelegateNetConf struct {
Conf types.NetConf
ConfList types.NetConfList
IfnameRequest string `json:"ifnameRequest,omitempty"`
MacRequest string `json:"macRequest,omitempty"`
// MasterPlugin is only used internal housekeeping
MasterPlugin bool `json:"-"`
// Conflist plugin is only used internal housekeeping
Expand Down Expand Up @@ -118,10 +119,10 @@ type NetworkSelectionElement struct {
Namespace string `json:"namespace,omitempty"`
// IPRequest contains an optional requested IP address for this network
// attachment
IPRequest string `json:"ipRequest,omitempty"`
IPRequest string `json:"ips,omitempty"`
// MacRequest contains an optional requested MAC address for this
// network attachment
MacRequest string `json:"macRequest,omitempty"`
MacRequest string `json:"mac,omitempty"`
// InterfaceRequest contains an optional requested name for the
// network interface this attachment will create in the container
InterfaceRequest string `json:"interfaceRequest,omitempty"`
Expand Down