Merge pull request #321 from mars1024/modify/bandwidth
bandwidth: get bandwidth interface in host ns through container interface
This commit is contained in:
@ -621,6 +621,257 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("Getting the host interface which plugin should work on from veth peer of container interface", func() {
|
||||||
|
It("Should work with multiple host veth interfaces", func() {
|
||||||
|
conf := `{
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"name": "cni-plugin-bandwidth-test",
|
||||||
|
"type": "bandwidth",
|
||||||
|
"ingressRate": 8,
|
||||||
|
"ingressBurst": 8,
|
||||||
|
"egressRate": 16,
|
||||||
|
"egressBurst": 8,
|
||||||
|
"prevResult": {
|
||||||
|
"interfaces": [
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": "%s"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ips": [
|
||||||
|
{
|
||||||
|
"version": "4",
|
||||||
|
"address": "%s/24",
|
||||||
|
"gateway": "10.0.0.1",
|
||||||
|
"interface": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"routes": []
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// create veth peer in host ns
|
||||||
|
vethName, peerName := "host-veth-peer1", "host-veth-peer2"
|
||||||
|
createVethInOneNs(hostNs.Path(), vethName, peerName)
|
||||||
|
|
||||||
|
conf = fmt.Sprintf(conf, vethName, peerName, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
||||||
|
args := &skel.CmdArgs{
|
||||||
|
ContainerID: "dummy",
|
||||||
|
Netns: containerNs.Path(),
|
||||||
|
IfName: containerIfname,
|
||||||
|
StdinData: []byte(conf),
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||||
|
result, err := current.GetResult(r)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(result.Interfaces).To(HaveLen(5))
|
||||||
|
Expect(result.Interfaces[4].Name).To(Equal(ifbDeviceName))
|
||||||
|
Expect(result.Interfaces[4].Sandbox).To(Equal(""))
|
||||||
|
|
||||||
|
ifbLink, err := netlink.LinkByName(ifbDeviceName)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU))
|
||||||
|
|
||||||
|
qdiscs, err := netlink.QdiscList(ifbLink)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(qdiscs).To(HaveLen(1))
|
||||||
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
||||||
|
|
||||||
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{}))
|
||||||
|
Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(2)))
|
||||||
|
Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1)))
|
||||||
|
|
||||||
|
hostVethLink, err := netlink.LinkByName(hostIfname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0))
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(qdiscFilters).To(HaveLen(1))
|
||||||
|
Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
|
||||||
|
Expect(hostNs.Do(func(n ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
ifbLink, err := netlink.LinkByName(hostIfname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
qdiscs, err := netlink.QdiscList(ifbLink)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(qdiscs).To(HaveLen(2))
|
||||||
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
||||||
|
|
||||||
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{}))
|
||||||
|
Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1)))
|
||||||
|
Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1)))
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Should fail when container interface has no veth peer", func() {
|
||||||
|
conf := `{
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"name": "cni-plugin-bandwidth-test",
|
||||||
|
"type": "bandwidth",
|
||||||
|
"ingressRate": 8,
|
||||||
|
"ingressBurst": 8,
|
||||||
|
"egressRate": 16,
|
||||||
|
"egressBurst": 8,
|
||||||
|
"prevResult": {
|
||||||
|
"interfaces": [
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": "%s"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ips": [
|
||||||
|
{
|
||||||
|
"version": "4",
|
||||||
|
"address": "%s/24",
|
||||||
|
"gateway": "10.0.0.1",
|
||||||
|
"interface": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"routes": []
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// create a macvlan device to be container interface
|
||||||
|
macvlanContainerIfname := "container-macv"
|
||||||
|
createMacvlan(containerNs.Path(), containerIfname, macvlanContainerIfname)
|
||||||
|
|
||||||
|
conf = fmt.Sprintf(conf, hostIfname, macvlanContainerIfname, containerNs.Path(), containerIP.String())
|
||||||
|
args := &skel.CmdArgs{
|
||||||
|
ContainerID: "dummy",
|
||||||
|
Netns: containerNs.Path(),
|
||||||
|
IfName: macvlanContainerIfname,
|
||||||
|
StdinData: []byte(conf),
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Should fail when preResult has no interfaces", func() {
|
||||||
|
conf := `{
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"name": "cni-plugin-bandwidth-test",
|
||||||
|
"type": "bandwidth",
|
||||||
|
"ingressRate": 8,
|
||||||
|
"ingressBurst": 8,
|
||||||
|
"egressRate": 16,
|
||||||
|
"egressBurst": 8,
|
||||||
|
"prevResult": {
|
||||||
|
"interfaces": [],
|
||||||
|
"ips": [],
|
||||||
|
"routes": []
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
args := &skel.CmdArgs{
|
||||||
|
ContainerID: "dummy",
|
||||||
|
Netns: containerNs.Path(),
|
||||||
|
IfName: "eth0",
|
||||||
|
StdinData: []byte(conf),
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Should fail when veth peer of container interface does not match any of host interfaces in preResult", func() {
|
||||||
|
conf := `{
|
||||||
|
"cniVersion": "0.4.0",
|
||||||
|
"name": "cni-plugin-bandwidth-test",
|
||||||
|
"type": "bandwidth",
|
||||||
|
"ingressRate": 8,
|
||||||
|
"ingressBurst": 8,
|
||||||
|
"egressRate": 16,
|
||||||
|
"egressBurst": 8,
|
||||||
|
"prevResult": {
|
||||||
|
"interfaces": [
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": "%s"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ips": [
|
||||||
|
{
|
||||||
|
"version": "4",
|
||||||
|
"address": "%s/24",
|
||||||
|
"gateway": "10.0.0.1",
|
||||||
|
"interface": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"routes": []
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// fake a non-exist host interface name
|
||||||
|
fakeHostIfname := fmt.Sprintf("%s-fake", hostIfname)
|
||||||
|
|
||||||
|
conf = fmt.Sprintf(conf, fakeHostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
||||||
|
args := &skel.CmdArgs{
|
||||||
|
ContainerID: "dummy",
|
||||||
|
Netns: containerNs.Path(),
|
||||||
|
IfName: containerIfname,
|
||||||
|
StdinData: []byte(conf),
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Context("when chaining bandwidth plugin with PTP using 0.3.0 config", func() {
|
Context("when chaining bandwidth plugin with PTP using 0.3.0 config", func() {
|
||||||
var ptpConf string
|
var ptpConf string
|
||||||
var rateInBits int
|
var rateInBits int
|
||||||
|
@ -199,3 +199,61 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
|
|||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createVethInOneNs(namespace, vethName, peerName string) {
|
||||||
|
vethDeviceRequest := &netlink.Veth{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
|
Name: vethName,
|
||||||
|
Flags: net.FlagUp,
|
||||||
|
},
|
||||||
|
PeerName: peerName,
|
||||||
|
}
|
||||||
|
|
||||||
|
netNS, err := ns.GetNS(namespace)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
err = netNS.Do(func(_ ns.NetNS) error {
|
||||||
|
if err := netlink.LinkAdd(vethDeviceRequest); err != nil {
|
||||||
|
return fmt.Errorf("failed to create veth pair: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := netlink.LinkByName(peerName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find newly-created veth device %q: %v", peerName, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMacvlan(namespace, master, macvlanName string) {
|
||||||
|
netNS, err := ns.GetNS(namespace)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
err = netNS.Do(func(_ ns.NetNS) error {
|
||||||
|
m, err := netlink.LinkByName(master)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to lookup master %q: %v", master, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
macvlanDeviceRequest := &netlink.Macvlan{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
|
MTU: m.Attrs().MTU,
|
||||||
|
Name: macvlanName,
|
||||||
|
ParentIndex: m.Attrs().Index,
|
||||||
|
},
|
||||||
|
Mode: netlink.MACVLAN_MODE_BRIDGE,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = netlink.LinkAdd(macvlanDeviceRequest); err != nil {
|
||||||
|
return fmt.Errorf("failed to create macvlan device: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = netlink.LinkByName(macvlanName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find newly-created macvlan device %q: %v", macvlanName, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
@ -17,7 +17,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
@ -28,6 +27,7 @@ import (
|
|||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -130,21 +130,35 @@ func getMTU(deviceName string) (int, error) {
|
|||||||
return link.Attrs().MTU, nil
|
return link.Attrs().MTU, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHostInterface(interfaces []*current.Interface) (*current.Interface, error) {
|
// get the veth peer of container interface in host namespace
|
||||||
|
func getHostInterface(interfaces []*current.Interface, containerIfName string, netns ns.NetNS) (*current.Interface, error) {
|
||||||
if len(interfaces) == 0 {
|
if len(interfaces) == 0 {
|
||||||
return nil, errors.New("no interfaces provided")
|
return nil, fmt.Errorf("no interfaces provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get veth peer index of container interface
|
||||||
|
var peerIndex int
|
||||||
var err error
|
var err error
|
||||||
|
_ = netns.Do(func(_ ns.NetNS) error {
|
||||||
|
_, peerIndex, err = ip.GetVethPeerIfindex(containerIfName)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if peerIndex <= 0 {
|
||||||
|
return nil, fmt.Errorf("container interface %s has no veth peer: %v", containerIfName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find host interface by index
|
||||||
|
link, err := netlink.LinkByIndex(peerIndex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("veth peer with index %d is not in host ns", peerIndex)
|
||||||
|
}
|
||||||
for _, iface := range interfaces {
|
for _, iface := range interfaces {
|
||||||
if iface.Sandbox == "" { // host interface
|
if iface.Sandbox == "" && iface.Name == link.Attrs().Name {
|
||||||
_, _, err = ip.GetVethPeerIfindex(iface.Name)
|
return iface, nil
|
||||||
if err == nil {
|
|
||||||
return iface, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, errors.New(fmt.Sprintf("no host interface found. last error: %s", err))
|
|
||||||
|
return nil, fmt.Errorf("no veth peer of container interface found in host ns")
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdAdd(args *skel.CmdArgs) error {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
@ -166,7 +180,14 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not convert result to current version: %v", err)
|
return fmt.Errorf("could not convert result to current version: %v", err)
|
||||||
}
|
}
|
||||||
hostInterface, err := getHostInterface(result.Interfaces)
|
|
||||||
|
netns, err := ns.GetNS(args.Netns)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||||
|
}
|
||||||
|
defer netns.Close()
|
||||||
|
|
||||||
|
hostInterface, err := getHostInterface(result.Interfaces, args.IfName, netns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -266,7 +287,13 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
return fmt.Errorf("could not convert result to current version: %v", err)
|
return fmt.Errorf("could not convert result to current version: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hostInterface, err := getHostInterface(result.Interfaces)
|
netns, err := ns.GetNS(args.Netns)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||||
|
}
|
||||||
|
defer netns.Close()
|
||||||
|
|
||||||
|
hostInterface, err := getHostInterface(result.Interfaces, args.IfName, netns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user