Projects
openEuler:22.03:LTS:LoongArch
syscontainer-tools
_service:tar_scm_kernel_repo:0006-support-ipv6....
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm_kernel_repo:0006-support-ipv6.patch of Package syscontainer-tools
From 9acfe3e5c2889c511d28447f1660911c4ce17cc5 Mon Sep 17 00:00:00 2001 From: vegbir <yangjiaqi16@huawei.com> Date: Thu, 10 Aug 2023 02:44:07 +0000 Subject: [PATCH] support ipv6 Signed-off-by: vegbir <yangjiaqi16@huawei.com> --- Makefile | 2 +- config/config_network.go | 3 +- libnetwork/drivers/common/driver.go | 11 + libnetwork/drivers/common/driver_test.go | 68 +++ libnetwork/drivers/driver.go | 30 ++ libnetwork/drivers/driver_test.go | 147 +++++++ libnetwork/drivers/eth/driver.go | 59 ++- libnetwork/drivers/eth/driver_test.go | 397 +++++++++++++++++ libnetwork/interfaces.go | 37 +- libnetwork/interfaces_test.go | 255 +++++++++++ libnetwork/route.go | 12 - libnetwork/route_test.go | 143 +++++++ network.go | 14 +- types/network.go | 49 ++- types/network_test.go | 514 +++++++++++++++++++++++ 15 files changed, 1699 insertions(+), 42 deletions(-) create mode 100644 libnetwork/drivers/common/driver_test.go create mode 100644 libnetwork/drivers/driver_test.go create mode 100644 libnetwork/drivers/eth/driver_test.go create mode 100644 libnetwork/interfaces_test.go create mode 100644 libnetwork/route_test.go create mode 100644 types/network_test.go diff --git a/Makefile b/Makefile index 556382c..ede386b 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ syscontainer-hooks: $(SOURCES) | $(DEPS_LINK) @echo "Done!" localtest: - ${ENV} go test -mod=vendor -tags ${TAGS} -ldflags ${GO_LDFLAGS} -v ./... + go test -mod=vendor -v ./... clean: rm -rf build diff --git a/config/config_network.go b/config/config_network.go index 4e0da46..1bb9c7f 100644 --- a/config/config_network.go +++ b/config/config_network.go @@ -15,8 +15,9 @@ package config import ( "fmt" - "isula.org/syscontainer-tools/types" "path/filepath" + + "isula.org/syscontainer-tools/types" ) var ( diff --git a/libnetwork/drivers/common/driver.go b/libnetwork/drivers/common/driver.go index c2dadd7..a2158a1 100644 --- a/libnetwork/drivers/common/driver.go +++ b/libnetwork/drivers/common/driver.go @@ -27,6 +27,7 @@ type Driver struct { hostName string mac *net.HardwareAddr ip *net.IPNet + ip6 *net.IPNet bridge string bridgeDriver api.BridgeDriver mtu int @@ -73,6 +74,16 @@ func (d *Driver) GetIP() *net.IPNet { return d.ip } +// SetIP6 will set the network interface ip6 +func (d *Driver) SetIP6(addr *net.IPNet) { + d.ip6 = addr +} + +// GetIP6 will get the network interface ip6 +func (d *Driver) GetIP6() *net.IPNet { + return d.ip6 +} + // SetMac will set the network interface mac func (d *Driver) SetMac(mac *net.HardwareAddr) { d.mac = mac diff --git a/libnetwork/drivers/common/driver_test.go b/libnetwork/drivers/common/driver_test.go new file mode 100644 index 0000000..7247232 --- /dev/null +++ b/libnetwork/drivers/common/driver_test.go @@ -0,0 +1,68 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved. +// syscontainer-tools licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Author: Jiaqi Yang +// Date: 2023-05-03 +// Description: This file is used for test common.driver package + +// package common is common network driver implementation +package common + +import ( + "net" + "testing" +) + +// TestDriver_GetAndSet tests setter & getter methods +func TestDriver_GetAndSet(t *testing.T) { + type fields struct { + nsPath string + ctrName string + hostName string + mac *net.HardwareAddr + ip *net.IPNet + ip6 *net.IPNet + bridge string + mtu int + qlen int + } + tests := []struct { + name string + fields fields + }{ + { + name: "TC1-sety & get sucessfully", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &Driver{} + d.SetIP6(tt.fields.ip6) + d.SetIP(tt.fields.ip) + d.SetBridge(tt.fields.bridge) + d.SetCtrNicName(tt.fields.ctrName) + d.SetHostNicName(tt.fields.hostName) + d.SetMac(tt.fields.mac) + d.SetMtu(tt.fields.mtu) + d.SetQlen(tt.fields.qlen) + d.SetNsPath(tt.fields.nsPath) + + d.GetBridge() + d.GetCtrNicName() + d.GetHostNicName() + d.GetIP() + d.GetIP6() + d.GetMac() + d.GetMtu() + d.GetQlen() + d.GetNsPath() + d.GetBridgeDriver() + }) + } +} diff --git a/libnetwork/drivers/driver.go b/libnetwork/drivers/driver.go index a87831d..86cac7a 100644 --- a/libnetwork/drivers/driver.go +++ b/libnetwork/drivers/driver.go @@ -111,16 +111,46 @@ func NicOptionHostNicName(name string) DriverOptions { func NicOptionIP(ip string) DriverOptions { return func(d *common.Driver) error { ip = strings.TrimSpace(ip) + if len(ip) == 0 { + return nil + } + ipnet, err := netlink.ParseIPNet(ip) if err != nil { return err } + if ipnet.IP.To4() == nil { + // fail to get ip4 + return fmt.Errorf("ip only accepts CIDR data in ipv4 format, not %v", ipnet.String()) + } d.SetIP(ipnet) return nil } } +// NicOptionIP6 handles network interface ip6 option +func NicOptionIP6(ip6 string) DriverOptions { + return func(d *common.Driver) error { + ip6 = strings.TrimSpace(ip6) + if len(ip6) == 0 { + return nil + } + + ipnet6, err := netlink.ParseIPNet(ip6) + if err != nil { + return err + } + if ipnet6.IP.To4() != nil { + // can get ip4 + return fmt.Errorf("ip6 only accepts CIDR data in ipv6 format, not %v", ipnet6.String()) + } + + d.SetIP6(ipnet6) + return nil + } +} + // NicOptionMac handles network interface mac option func NicOptionMac(mac string) DriverOptions { return func(d *common.Driver) error { diff --git a/libnetwork/drivers/driver_test.go b/libnetwork/drivers/driver_test.go new file mode 100644 index 0000000..5640130 --- /dev/null +++ b/libnetwork/drivers/driver_test.go @@ -0,0 +1,147 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2018-2023. All rights reserved. +// syscontainer-tools is licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Description: network interface driver +// Author: Jiaqi Yang +// Create: 2023-04-04 + +package drivers + +import ( + "fmt" + "testing" + + "isula.org/syscontainer-tools/types" +) + +// TestNew tests New +func TestNew(t *testing.T) { + const ( + ctrNicName = "eth0" + hostNicName = "eth0" + nsPath = "/proc/1/ns/net" + ip = types.CIDRIpExample1 + ip6 = types.CIDRIp6Example1 + mac = "aa:bb:cc:dd:ee:aa" + mtu = 1500 + qlen = 1000 + bridge = "" + ) + type args struct { + driverType string + options []DriverOptions + } + tests := []struct { + name string + args args + want Driver + wantErr bool + }{ + { + name: "TC1-new eth driver success", + args: args{ + driverType: "eth", + options: []DriverOptions{ + NicOptionCtrNicName(ctrNicName), + NicOptionHostNicName(hostNicName), + NicOptionNsPath(nsPath), + NicOptionIP(ip), + NicOptionIP6(ip6), + NicOptionMac(mac), + NicOptionMtu(mtu), + NicOptionQlen(qlen), + NicOptionBridge(bridge), + }, + }, + wantErr: false, + }, + { + name: "TC2-ip6 is not CIDR address", + args: args{ + driverType: "eth", + options: []DriverOptions{ + NicOptionCtrNicName(ctrNicName), + NicOptionHostNicName(hostNicName), + NicOptionNsPath(nsPath), + NicOptionIP6(mac), + }, + }, + wantErr: true, + }, + { + name: "TC3-ip6 is empty", + args: args{ + driverType: "eth", + options: []DriverOptions{ + NicOptionCtrNicName(ctrNicName), + NicOptionHostNicName(hostNicName), + NicOptionNsPath(nsPath), + NicOptionIP6(bridge), + }, + }, + wantErr: false, + }, + { + name: "TC4-ip is not CIDR address", + args: args{ + driverType: "eth", + options: []DriverOptions{ + NicOptionCtrNicName(ctrNicName), + NicOptionHostNicName(hostNicName), + NicOptionNsPath(nsPath), + NicOptionIP(mac), + }, + }, + wantErr: true, + }, + { + name: "TC5-ip is empty", + args: args{ + driverType: "eth", + options: []DriverOptions{ + NicOptionCtrNicName(ctrNicName), + NicOptionHostNicName(hostNicName), + NicOptionNsPath(nsPath), + NicOptionIP(bridge), + }, + }, + wantErr: false, + }, + { + name: "TC6.1-set ip6 to ip", + args: args{ + driverType: "eth", + options: []DriverOptions{ + NicOptionIP(ip6), + }, + }, + wantErr: true, + }, + { + name: "TC6.2-set ip to ip6", + args: args{ + driverType: "eth", + options: []DriverOptions{ + NicOptionIP6(ip), + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := New(tt.args.driverType, tt.args.options...) + fmt.Printf("%v\n", err) + if (err != nil) != tt.wantErr { + t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/libnetwork/drivers/eth/driver.go b/libnetwork/drivers/eth/driver.go index cd70ddf..901ec9c 100644 --- a/libnetwork/drivers/eth/driver.go +++ b/libnetwork/drivers/eth/driver.go @@ -15,6 +15,7 @@ package eth import ( "fmt" + "net" "os" "strings" @@ -178,6 +179,44 @@ func (d *ethDriver) DeleteIf() (rErr error) { return err } +func (d *ethDriver) setNicIP(nic netlink.Link, ipType int) error { + if nic == nil { + return fmt.Errorf("nic is nil") + } + var ( + ipNet *net.IPNet + typ string + ) + switch ipType { + case netlink.FAMILY_V4: + ipNet = d.GetIP() + typ = "ip" + case netlink.FAMILY_V6: + ipNet = d.GetIP6() + typ = "ip6" + default: + return fmt.Errorf("unsupported IP type") + } + + // delete original ip/ip6 address + oldAddr, err := netlink.AddrList(nic, ipType) + if err != nil { + logrus.Infof("Fail to get origin %v addr: %v", typ, err) + } + if len(oldAddr) > 0 { + // we only have an IP set for the interface + if err := netlink.AddrDel(nic, &oldAddr[0]); err != nil { + return fmt.Errorf("failed to delete old %v address %v: %v", typ, oldAddr[0].IP, err) + } + } + // set new ipv4/ipv6 address + ipAddr := &netlink.Addr{IPNet: ipNet, Label: ""} + if err := netlink.AddrAdd(nic, ipAddr); err != nil { + return fmt.Errorf("failed to configure %v address %v: %v", typ, ipAddr, err) + } + return nil +} + func (d *ethDriver) setNicConfigure(nic netlink.Link) (rErr error) { // set MAC if d.GetMac() != nil { @@ -197,18 +236,18 @@ func (d *ethDriver) setNicConfigure(nic netlink.Link) (rErr error) { return fmt.Errorf("failed to set qlen(%d) for nic(%s)", d.GetQlen(), d.GetCtrNicName()) } - // set ipv4 address (TODO: ipv6 support?) - oldAddr, _ := netlink.AddrList(nic, netlink.FAMILY_V4) - if oldAddr != nil { - // we only have on IP set for the interface - if err := netlink.AddrDel(nic, &oldAddr[0]); err != nil { - return fmt.Errorf("failed to delete old ip address: %v", err) + // set ipv4 + if d.GetIP() != nil { + if err := d.setNicIP(nic, netlink.FAMILY_V4); err != nil { + return err } } - // set ipv4 address (TODO: ipv6 support?) - ipAddr := &netlink.Addr{IPNet: d.GetIP(), Label: ""} - if err := netlink.AddrAdd(nic, ipAddr); err != nil { - return fmt.Errorf("failed to configure ip address: %v", err) + + // set ipv6 + if d.GetIP6() != nil { + if err := d.setNicIP(nic, netlink.FAMILY_V6); err != nil { + return err + } } return nil diff --git a/libnetwork/drivers/eth/driver_test.go b/libnetwork/drivers/eth/driver_test.go new file mode 100644 index 0000000..9596a9a --- /dev/null +++ b/libnetwork/drivers/eth/driver_test.go @@ -0,0 +1,397 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved. +// syscontainer-tools is licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Description: ethetic network driver +// Author: Jiaqi Yang +// Create: 2023-05-03 + +package eth + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "testing" + + "github.com/vishvananda/netlink" + + "isula.org/syscontainer-tools/libnetwork/drivers/common" + "isula.org/syscontainer-tools/types" +) + +func getPhysicalNics() (map[string]struct{}, error) { + const ( + allNicDir = "/sys/class/net" + virtualNicDir = "/sys/devices/virtual/net" + ) + getFileName := func(dirName string) (map[string]struct{}, error) { + fis, err := ioutil.ReadDir(dirName) + if err != nil { + return nil, err + } + var fileNames = make(map[string]struct{}) + for _, fi := range fis { + fileNames[fi.Name()] = struct{}{} + } + return fileNames, nil + } + allNics, err := getFileName(allNicDir) + if err != nil { + return nil, err + } + virtualNics, err := getFileName(virtualNicDir) + if err != nil { + return nil, err + } + var res = make(map[string]struct{}) + for name := range allNics { + if _, ok := virtualNics[name]; !ok { + res[name] = struct{}{} + } + } + return res, nil +} + +func getUnusedNic() (string, error) { + nicNames, err := getPhysicalNics() + if err != nil { + return "", err + } + inters, err := net.Interfaces() + if err != nil { + return "", err + } + for _, i := range inters { + if _, ok := nicNames[i.Name]; !ok { + continue + } + addrs, err := i.Addrs() + if err != nil { + fmt.Printf("%v can not get its addr: %v\n", i.Name, err) + continue + } + if len(addrs) > 0 { + continue + } + return i.Name, nil + } + return "", fmt.Errorf("can not find any unused card") +} + +func getIPAddr(l netlink.Link, t int) (string, error) { + var typ string = "ip4" + if t == netlink.FAMILY_V6 { + typ = "ip6" + } + addrs, err := netlink.AddrList(l, t) + if err != nil { + return "", fmt.Errorf("fail to get %v: %v", typ, err) + } + if len(addrs) > 0 { + if typ == "ip6" { + return addrs[0].IP.To16().String(), nil + } + return addrs[0].IP.To4().String(), nil + } + return "", fmt.Errorf("empty %v", typ) +} + +func getIp6(l netlink.Link) (string, error) { + return getIPAddr(l, netlink.FAMILY_V6) +} + +func getIp(l netlink.Link) (string, error) { + return getIPAddr(l, netlink.FAMILY_V4) +} + +func removeAllAddrOfNic(nicName string) { + rmNic := func(ip string, nic netlink.Link) { + ipNet, err := netlink.ParseIPNet(ip) + if err != nil { + fmt.Printf("fail to create ip: %v\n", err) + return + } + ipaddr := &netlink.Addr{IPNet: ipNet} + err = netlink.AddrDel(nic, ipaddr) + if err != nil { + fmt.Printf("fail to del ip: %v\n", err) + } + } + + nic, err := netlink.LinkByName(nicName) + if err != nil { + fmt.Printf("should find nic: %v", err) + return + } + ip, _ := getIp(nic) + if ip != "" { + rmNic(ip+"/16", nic) + } + ip6, _ := getIp6(nic) + if ip6 != "" { + rmNic(ip6+"/64", nic) + } +} + +func hasPerm() error { + filePath := "/proc/1/ns/net" + + // is existed + fileInfo, err := os.Stat(filePath) + if err != nil { + return err + } + + // get file info + fileMode := fileInfo.Mode() + + // Check if the current user has permission to access the file + if fileMode.Perm()&(1<<(uint(7))) == 0 { + return fmt.Errorf("no permission to access the file") + } + return nil +} + +// Test_ethDriver_setNicConfigure tests setNicConfigure of ethDriver +func Test_ethDriver_setNicConfigure(t *testing.T) { + const ( + expIP = types.CIDRIpExample1 + expIP6 = types.CIDRIp6Example1 + expMTU = 1500 + expQlen = 1000 + invalidMtu = -1 + invalidMtu2 = 65536 + invalidIP = "xxx" + ) + + var ( + invalidBytes = []byte("ABCDEFG") + invalidMac = net.HardwareAddr(invalidBytes) + expIPNet, _ = netlink.ParseIPNet(expIP) + expIP6Net, _ = netlink.ParseIPNet(expIP6) + invalidIPNet = net.IPNet{IP: invalidBytes} + ) + if hasPerm() != nil { + return + } + + nicName, err := getUnusedNic() + if err != nil { + fmt.Printf("skip this tests: %v\n", err) + return + } + fmt.Printf("use nics: %v\n", nicName) + + type fields struct { + ip *net.IPNet + ip6 *net.IPNet + mtu int + qlen int + mac *net.HardwareAddr + } + tests := []struct { + name string + fields fields + wantErr bool + post func(t *testing.T, nic netlink.Link) + }{ + { + name: "TC1-success", + fields: fields{ + ip: expIPNet, + ip6: expIP6Net, + mtu: expMTU, + qlen: expQlen, + }, + post: func(t *testing.T, nic netlink.Link) { + ip, err := getIp(nic) + if err != nil { + t.Errorf("fail to get ip: %v", err) + } + ip6, err := getIp6(nic) + if err != nil { + t.Errorf("fail to get ip6: %v", err) + } + if ip+"/24" != expIP && ip6+"/64" != expIP6 { + t.Errorf("not same") + } + }, + wantErr: false, + }, + { + name: "TC2-invalid mac addr", + fields: fields{ + mac: &invalidMac, + }, + wantErr: true, + }, + { + name: "TC3-invalid MTU", + fields: fields{ + mtu: invalidMtu, + }, + wantErr: true, + }, + { + name: "TC3.1-invalid MTU", + fields: fields{ + mtu: invalidMtu2, + }, + wantErr: true, + }, + { + name: "TC4-invalid IP", + fields: fields{ + mtu: expMTU, + qlen: expQlen, + ip: &invalidIPNet, + }, + wantErr: true, + }, + { + name: "TC5-invalid IP6", + fields: fields{ + mtu: expMTU, + qlen: expQlen, + ip6: &invalidIPNet, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := ðDriver{ + Driver: &common.Driver{}, + } + if tt.fields.ip != nil { + d.SetIP(tt.fields.ip) + fmt.Printf("ip %v\n", tt.fields.ip) + } + if tt.fields.ip6 != nil { + d.SetIP6(tt.fields.ip6) + fmt.Printf("ip6 %v\n", tt.fields.ip6) + } + + d.SetMtu(tt.fields.mtu) + d.SetQlen(tt.fields.qlen) + d.SetMac(tt.fields.mac) + + nic, err := netlink.LinkByName(nicName) + if err != nil { + t.Errorf("should find nic: %v", err) + return + } + defer removeAllAddrOfNic(nicName) + + if err := d.setNicConfigure(nic); (err != nil) != tt.wantErr { + t.Errorf("ethDriver.setNicConfigure() error = %v, wantErr %v", err, tt.wantErr) + } + if tt.post != nil { + tt.post(t, nic) + } + }) + } +} + +// Test_ethDriver_setNicIP tests setNicIP +func Test_ethDriver_setNicIP(t *testing.T) { + const ( + originIP = types.CIDRIpExample1 + curIP = types.CIDRIpExample2 + invalidTyp = 111111 + ) + var ( + originIPNet, _ = netlink.ParseIPNet(originIP) + curIPNet, _ = netlink.ParseIPNet(curIP) + ) + + if hasPerm() != nil { + return + } + + type fields struct { + ip *net.IPNet + } + type args struct { + nic netlink.Link + ipType int + } + + nicName, err := getUnusedNic() + if err != nil { + fmt.Printf("skip this tests: %v\n", err) + return + } + fmt.Printf("use nics: %v\n", nicName) + nic, err := netlink.LinkByName(nicName) + if err != nil { + t.Errorf("should find nic: %v", err) + return + } + defer removeAllAddrOfNic(nicName) + + tests := []struct { + name string + args args + fields fields + pre func(t *testing.T) + wantErr bool + }{ + { + name: "TC1-invalid IPType", + args: args{ + ipType: invalidTyp, + nic: nic, + }, + wantErr: true, + }, + { + name: "TC2-invalid nic", + args: args{ + ipType: netlink.FAMILY_V4, + }, + wantErr: true, + }, + { + name: "TC3-delete origin ip", + fields: fields{ + ip: curIPNet, + }, + args: args{ + ipType: netlink.FAMILY_V4, + nic: nic, + }, + pre: func(t *testing.T) { + ipAddr := &netlink.Addr{IPNet: originIPNet, Label: ""} + if err := netlink.AddrAdd(nic, ipAddr); err != nil { + t.Errorf("failed to configure ip4 address %v: %v", ipAddr, err) + } + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := ðDriver{ + Driver: &common.Driver{}, + } + if tt.fields.ip != nil { + d.SetIP(tt.fields.ip) + } + if tt.pre != nil { + tt.pre(t) + } + if err := d.setNicIP(tt.args.nic, tt.args.ipType); (err != nil) != tt.wantErr { + t.Errorf("ethDriver.setNicIP() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/libnetwork/interfaces.go b/libnetwork/interfaces.go index 46a51aa..121243a 100644 --- a/libnetwork/interfaces.go +++ b/libnetwork/interfaces.go @@ -71,6 +71,7 @@ func AddNicToContainer(nsPath string, config *types.InterfaceConf) (rErr error) drivers.NicOptionHostNicName(config.HostNicName), drivers.NicOptionNsPath(nsPath), drivers.NicOptionIP(config.IP), + drivers.NicOptionIP6(config.IP6), drivers.NicOptionMac(config.Mac), drivers.NicOptionMtu(config.Mtu), drivers.NicOptionQlen(config.Qlen), @@ -94,6 +95,7 @@ func UpdateNicInContainer(nsPath string, config *types.InterfaceConf) (rErr erro drivers.NicOptionHostNicName(config.HostNicName), drivers.NicOptionNsPath(nsPath), drivers.NicOptionIP(config.IP), + drivers.NicOptionIP6(config.IP6), drivers.NicOptionMac(config.Mac), drivers.NicOptionMtu(config.Mtu), drivers.NicOptionQlen(config.Qlen), @@ -151,6 +153,7 @@ func DelNicFromContainer(nsPath string, config *types.InterfaceConf) error { drivers.NicOptionHostNicName(config.HostNicName), drivers.NicOptionNsPath(nsPath), drivers.NicOptionIP(config.IP), + drivers.NicOptionIP6(config.IP6), drivers.NicOptionMac(config.Mac), drivers.NicOptionMtu(config.Mtu), drivers.NicOptionBridge(config.Bridge)) @@ -176,21 +179,29 @@ func UpdateNic(ctr *container.Container, config *types.InterfaceConf, updateConf var tmpConfig = new(types.InterfaceConf) tmpConfig.CtrNicName = config.CtrNicName - var newConfig *types.InterfaceConf - if newConfig = hConfig.FindInterfaceByName(tmpConfig); newConfig == nil { + var oldConfig *types.InterfaceConf + if oldConfig = hConfig.FindInterfaceByName(tmpConfig); oldConfig == nil { return fmt.Errorf("Network interface %s,%s with type %s not exist in container %s", config.HostNicName, config.CtrNicName, config.Type, ctr.Name()) } if config.IP == "" { - tmpConfig.IP = newConfig.IP + tmpConfig.IP = oldConfig.IP } else { tmpConfig.IP = config.IP msg := fmt.Sprintf("Update IP address for network interface (%s,%v) done", config.CtrNicName, config.IP) fmt.Fprintln(os.Stdout, msg) logrus.Info(msg) } + if config.IP6 == "" { + tmpConfig.IP6 = oldConfig.IP6 + } else { + tmpConfig.IP6 = config.IP6 + msg := fmt.Sprintf("Update IP6 address for network interface (%s,%v) done", config.CtrNicName, config.IP6) + fmt.Fprintln(os.Stdout, msg) + logrus.Info(msg) + } if config.Mac == "" { - tmpConfig.Mac = newConfig.Mac + tmpConfig.Mac = oldConfig.Mac } else { tmpConfig.Mac = config.Mac msg := fmt.Sprintf("Update MAC address for network interface (%s,%v) done", config.CtrNicName, config.Mac) @@ -199,7 +210,7 @@ func UpdateNic(ctr *container.Container, config *types.InterfaceConf, updateConf } if config.Bridge == "" { - tmpConfig.Bridge = newConfig.Bridge + tmpConfig.Bridge = oldConfig.Bridge } else { tmpConfig.Bridge = config.Bridge msg := fmt.Sprintf("Update Bridge for network interface (%s,%v) done", config.CtrNicName, config.Bridge) @@ -208,7 +219,7 @@ func UpdateNic(ctr *container.Container, config *types.InterfaceConf, updateConf } if config.Mtu == 0 { - tmpConfig.Mtu = newConfig.Mtu + tmpConfig.Mtu = oldConfig.Mtu } else { tmpConfig.Mtu = config.Mtu msg := fmt.Sprintf("Update Mtu for network interface (%s,%v) done", config.CtrNicName, config.Mtu) @@ -218,26 +229,28 @@ func UpdateNic(ctr *container.Container, config *types.InterfaceConf, updateConf } // we use qlen < 0 to check if the user has set parameter qlen or not if config.Qlen < 0 { - tmpConfig.Qlen = newConfig.Qlen + tmpConfig.Qlen = oldConfig.Qlen } else { tmpConfig.Qlen = config.Qlen msg := fmt.Sprintf("Update Qlen for network interface (%s,%v)", config.CtrNicName, config.Qlen) fmt.Fprintln(os.Stdout, msg) logrus.Info(msg) } - tmpConfig.Type = newConfig.Type - tmpConfig.HostNicName = newConfig.HostNicName + tmpConfig.Type = oldConfig.Type + tmpConfig.HostNicName = oldConfig.HostNicName if hConfig.IsSameInterface(tmpConfig) { logrus.Infof("Network interface in container (%s, %s): Identical setting, nothing to change", config.CtrNicName, ctr.Name()) return nil } - if err := hConfig.UpdateNetworkInterface(newConfig, false); err != nil { + + if err := hConfig.UpdateNetworkInterface(oldConfig, false); err != nil { return err } + if err := hConfig.IsConflictInterface(tmpConfig); err != nil { - if err := hConfig.UpdateNetworkInterface(newConfig, true); err != nil { + if err := hConfig.UpdateNetworkInterface(oldConfig, true); err != nil { return err } return err @@ -245,7 +258,7 @@ func UpdateNic(ctr *container.Container, config *types.InterfaceConf, updateConf if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() { if err := UpdateNicInContainer(ctr.NetNsPath(), tmpConfig); err != nil { - if err := hConfig.UpdateNetworkInterface(newConfig, true); err != nil { + if err := hConfig.UpdateNetworkInterface(oldConfig, true); err != nil { return err } return err diff --git a/libnetwork/interfaces_test.go b/libnetwork/interfaces_test.go new file mode 100644 index 0000000..88b8e68 --- /dev/null +++ b/libnetwork/interfaces_test.go @@ -0,0 +1,255 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2018-2023. All rights reserved. +// syscontainer-tools is licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Description: network interface operation +// Author: Jiaqi Yang +// Create: 2023-05-03 + +// package libnetwork is network library +package libnetwork + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + + "isula.org/syscontainer-tools/container" + "isula.org/syscontainer-tools/types" +) + +const ( + ctrNicName = "ctr" + hostNicName = "host" + ip6 = types.CIDRIp6Example1 + mtu = 1500 + procOneNsPath = "/proc/1/ns/net" + nonExistBridge = "test" +) + +// TestAddNicToContainer tests AddNicToContainer +func TestAddNicToContainer(t *testing.T) { + type args struct { + nsPath string + config *types.InterfaceConf + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "TC1-fail to add nic", + args: args{ + nsPath: procOneNsPath, + config: &types.InterfaceConf{ + CtrNicName: ctrNicName, + HostNicName: hostNicName, + IP6: ip6, + Mtu: mtu, + Bridge: nonExistBridge, + }, + }, + wantErr: true, + }, + { + name: "TC2-invalid IP", + args: args{ + nsPath: procOneNsPath, + config: &types.InterfaceConf{ + CtrNicName: ctrNicName, + HostNicName: hostNicName, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := AddNicToContainer(tt.args.nsPath, tt.args.config); (err != nil) != tt.wantErr { + t.Errorf("AddNicToContainer() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// TestUpdateNicInContainer test UpdateNicInContainer +func TestUpdateNicInContainer(t *testing.T) { + type args struct { + nsPath string + config *types.InterfaceConf + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "TC1-fail to update nic", + args: args{ + nsPath: procOneNsPath, + config: &types.InterfaceConf{ + CtrNicName: ctrNicName, + HostNicName: hostNicName, + IP6: ip6, + Mtu: mtu, + Bridge: nonExistBridge, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := UpdateNicInContainer(tt.args.nsPath, tt.args.config); (err != nil) != tt.wantErr { + t.Errorf("UpdateNicInContainer() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// TestDelNicFromContainer tests DelNicFromContainer +func TestDelNicFromContainer(t *testing.T) { + type args struct { + nsPath string + config *types.InterfaceConf + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "TC1-delete non-existed nic", + args: args{ + nsPath: procOneNsPath, + config: &types.InterfaceConf{ + CtrNicName: ctrNicName, + HostNicName: hostNicName, + IP6: ip6, + Mtu: mtu, + Bridge: nonExistBridge, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := DelNicFromContainer(tt.args.nsPath, tt.args.config); (err != nil) != tt.wantErr { + t.Errorf("DelNicFromContainer() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +const ctrName = "ctrName" + +func createContainer() error { + const ( + imageName = "rnd-dockerhub.huawei.com/official/ubuntu" + ctrCmd = "bash" + ) + // cmdValue := "isula run -tid -name " + ctrName + " " + imageName + " " + cmd := exec.Command("isula", "run", "-tid", "--name", "ctrName", imageName, ctrCmd) + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %v", string(out), err) + } + return nil +} + +func removeContainer() { + cmd := exec.Command("bash", "-c", "isula rm -f `isula ps -aq`") + out, err := cmd.CombinedOutput() + if err != nil { + fmt.Printf("%s: %v\n", string(out), err) + } +} + +// TestUpdateNic tests UpdateNic +func TestUpdateNic(t *testing.T) { + type args struct { + ctr *container.Container + config *types.InterfaceConf + updateConfigOnly bool + data string + } + if err := createContainer(); err != nil { + fmt.Printf("skip this usecase: %v\n", err) + return + } + ctr, err := container.New(ctrName) + if err != nil { + return + } + defer removeContainer() + + const ( + defaultConfigFile = "device_hook.json" + isuladPath = "/var/lib/isulad/engines/lcr" + filePerm = 0750 + invalidQlen = -1 + ip6 = "abc" + ) + var ( + data1 = "{\"networkInterfaces\":[{\"Ip\":\"\",\"Ip6\":\"\",\"Mac\":\"\",\"Mtu\":1,\"Qlen\":1," + + "\"Type\":\"eth\",\"Bridge\":\"\",\"HostNicName\":\"host\",\"CtrNicName\":\"ctr\"}]}" + ) + ctrID := ctr.ContainerID() + + if err := os.WriteFile(filepath.Join(isuladPath, ctrID, defaultConfigFile), []byte(data1), filePerm); err != nil { + fmt.Printf("write fail: %v\n", err) + return + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "TC1-same config", + args: args{ + ctr: ctr, + config: &types.InterfaceConf{ + CtrNicName: ctrNicName, + HostNicName: hostNicName, + Qlen: invalidQlen, + }, + data: data1, + }, + wantErr: false, + }, + { + name: "TC2-ip6 changed", + args: args{ + ctr: ctr, + config: &types.InterfaceConf{ + CtrNicName: ctrNicName, + HostNicName: hostNicName, + IP6: ip6, + }, + data: data1, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := os.WriteFile(filepath.Join(isuladPath, ctrID, defaultConfigFile), []byte(tt.args.data), filePerm); err != nil { + fmt.Printf("write fail: %v\n", err) + return + } + if err := UpdateNic(tt.args.ctr, tt.args.config, tt.args.updateConfigOnly); (err != nil) != tt.wantErr { + t.Errorf("UpdateNic() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/libnetwork/route.go b/libnetwork/route.go index 0be5a76..2b72c3e 100644 --- a/libnetwork/route.go +++ b/libnetwork/route.go @@ -92,16 +92,10 @@ func AddRouteToContainer(nsPath string, route *types.Route) error { if len(src) != 0 { rule.Src = net.ParseIP(src) - if err != nil { - return fmt.Errorf("failed to parse src ip") - } } if len(gw) != 0 { rule.Gw = net.ParseIP(gw) - if err != nil { - return fmt.Errorf("failed to parse gw ip") - } } return nsutils.NsInvoke(nsPath, @@ -195,16 +189,10 @@ func DelRouteFromContainer(nsPath string, route *types.Route) error { if len(src) != 0 { rule.Src = net.ParseIP(src) - if err != nil { - return fmt.Errorf("failed to parse src ip") - } } if len(gw) != 0 { rule.Gw = net.ParseIP(gw) - if err != nil { - return fmt.Errorf("failed to parse gw ip") - } } return nsutils.NsInvoke(nsPath, diff --git a/libnetwork/route_test.go b/libnetwork/route_test.go new file mode 100644 index 0000000..2f8ab38 --- /dev/null +++ b/libnetwork/route_test.go @@ -0,0 +1,143 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved. +// syscontainer-tools is licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Description: network routes operation +// Author: zhangwei +// Create: 2018-01-18 + +// package libnetwork is network library +package libnetwork + +import ( + "fmt" + "os" + "testing" + + "isula.org/syscontainer-tools/types" +) + +func hasPerm() error { + filePath := "/proc/1/ns/net" + + // is existed + fileInfo, err := os.Stat(filePath) + if err != nil { + return err + } + + // get file info + fileMode := fileInfo.Mode() + + // Check if the current user has permission to access the file + if fileMode.Perm()&(1<<(uint(7))) == 0 { + return fmt.Errorf("no permission to access the file") + } + return nil +} + +// TestAddRouteToContainer tests AddRouteToContainer and DelRouteToContainer +func TestAddDelRouteToContainer(t *testing.T) { + const ( + normalIP = types.IPExample1 + cidrIP = normalIP + "/16" + dev = "netDev" + ) + + if hasPerm() != nil { + return + } + + type args struct { + nsPath string + route *types.Route + } + tests := []struct { + name string + args args + addWantErr bool + delWantErr bool + }{ + { + name: "TC1.1-fail to parse invalaid src", + args: args{ + nsPath: procOneNsPath, + route: &types.Route{ + Src: cidrIP, + }, + }, + addWantErr: true, + delWantErr: true, + }, + { + name: "TC1.2-fail to parse invalaid gw", + args: args{ + nsPath: procOneNsPath, + route: &types.Route{ + Gw: cidrIP, + }, + }, + addWantErr: true, + delWantErr: true, + }, + { + name: "TC1.3-fail to parse invalid dest", + args: args{ + nsPath: procOneNsPath, + route: &types.Route{ + Dest: normalIP, + Dev: dev, + }, + }, + addWantErr: true, + delWantErr: true, + }, + { + name: "TC1.4-lack of dev, gw & src", + args: args{ + nsPath: procOneNsPath, + route: &types.Route{}, + }, + addWantErr: true, + delWantErr: true, + }, + { + name: "TC1.5-non existed dev", + args: args{ + nsPath: procOneNsPath, + route: &types.Route{ + Dev: dev, + }, + }, + addWantErr: true, + delWantErr: true, + }, + { + name: "TC2-default dest", + args: args{ + nsPath: procOneNsPath, + route: &types.Route{ + Dest: "default", + Gw: normalIP, + }, + }, + addWantErr: false, + delWantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := AddRouteToContainer(tt.args.nsPath, tt.args.route); (err != nil) != tt.addWantErr { + t.Errorf("AddRouteToContainer() error = %v, wantErr %v", err, tt.addWantErr) + } + if err := DelRouteFromContainer(tt.args.nsPath, tt.args.route); (err != nil) != tt.delWantErr { + t.Errorf("DelRouteFromContainer() error = %v, wantErr %v", err, tt.addWantErr) + } + }) + } +} diff --git a/network.go b/network.go index e8eaa68..6e32dc4 100644 --- a/network.go +++ b/network.go @@ -47,7 +47,11 @@ and configure it as you wanted, then attach to specified bridge. }, cli.StringFlag{ Name: "ip", - Usage: "set ip address. E.g. 172.17.28.2/24", + Usage: "set ip address. E.g. " + types.CIDRIpExample1, + }, + cli.StringFlag{ + Name: "ip6", + Usage: "set ipv6 address. E.g. " + types.CIDRIp6Example1, }, cli.StringFlag{ Name: "mac", @@ -101,6 +105,7 @@ and configure it as you wanted, then attach to specified bridge. nicConf := &types.InterfaceConf{ IP: context.String("ip"), + IP6: context.String("ip6"), Mac: context.String("mac"), Mtu: context.Int("mtu"), Type: context.String("type"), @@ -207,7 +212,11 @@ var updateNicCommand = cli.Command{ }, cli.StringFlag{ Name: "ip", - Usage: "set ip address. E.g. 172.17.28.2/24", + Usage: "set ip address. E.g. " + types.CIDRIpExample1, + }, + cli.StringFlag{ + Name: "ip6", + Usage: "set ipv6 address. E.g. " + types.CIDRIp6Example1, }, cli.StringFlag{ Name: "mac", @@ -264,6 +273,7 @@ var updateNicCommand = cli.Command{ nicConf := &types.InterfaceConf{ IP: context.String("ip"), + IP6: context.String("ip6"), Mac: context.String("mac"), Mtu: context.Int("mtu"), Bridge: context.String("bridge"), diff --git a/types/network.go b/types/network.go index 74231f9..524e1d5 100644 --- a/types/network.go +++ b/types/network.go @@ -21,6 +21,26 @@ import ( "github.com/vishvananda/netlink" ) +const ( + // example1 of ip address + IPExample1 = "172.17.28.2" + // example2 of ip address + IPExample2 = "172.17.28.3" + // example1 of cidr format ip address (with mask) + CIDRIpExample1 = IPExample1 + "/24" + // example2 of cidr format ip address (with mask) + CIDRIpExample2 = IPExample2 + "/16" + + // example1 of ip6 address + IP6Example1 = "2001:0db8:0:f101::1" + // example2 of ip6 address + IP6Example2 = "fe80::2aee:d4ef:fe:b890" + // example1 of cidr format ip6 address (with mask) + CIDRIp6Example1 = IP6Example1 + "/64" + // example2 of cidr format ip6 address (with mask) + CIDRIp6Example2 = IP6Example2 + "/64" +) + // NamespacePath namespace paths type NamespacePath struct { Pid string `json:"pid,omitempty"` @@ -34,6 +54,7 @@ type NamespacePath struct { // InterfaceConf is the network interface config type InterfaceConf struct { IP string `json:"Ip"` + IP6 string `json:"Ip6"` Mac string `json:"Mac"` Mtu int `json:"Mtu"` Qlen int `json:"Qlen"` @@ -72,9 +93,12 @@ func IsConflictNic(nic1, nic2 *InterfaceConf) error { if nic1.Mac != "" && (nic1.Mac == nic2.Mac) { return fmt.Errorf("interface mac conflict: %s", nic1.Mac) } - if nic1.IP == nic2.IP { + if nic1.IP != "" && nic1.IP == nic2.IP { return fmt.Errorf("interface ip conflict: %s", nic1.IP) } + if nic1.IP6 != "" && nic1.IP6 == nic2.IP6 { + return fmt.Errorf("interface ip6 conflict: %s", nic1.IP6) + } return nil } @@ -83,6 +107,9 @@ func IsSameNic(obj, src *InterfaceConf) bool { if obj.IP != src.IP && obj.IP != "" { return false } + if obj.IP6 != src.IP6 && obj.IP6 != "" { + return false + } if obj.Mac != src.Mac && obj.Mac != "" { return false } @@ -134,10 +161,24 @@ func IsSameRoute(obj, src *Route) bool { // ValidNetworkConfig validate network config func ValidNetworkConfig(conf *InterfaceConf) error { - // check IP here conf.IP = strings.TrimSpace(conf.IP) - if _, err := netlink.ParseIPNet(conf.IP); err != nil { - return err + conf.IP6 = strings.TrimSpace(conf.IP6) + if len(conf.IP) == 0 && len(conf.IP6) == 0 { + return fmt.Errorf("either ip or ipv6 must be specified") + } + + // check IP here + if len(conf.IP) != 0 { + if _, err := netlink.ParseIPNet(conf.IP); err != nil { + return err + } + } + + // check IP6 here + if len(conf.IP6) != 0 { + if _, err := netlink.ParseIPNet(conf.IP6); err != nil { + return err + } } // Check mac here diff --git a/types/network_test.go b/types/network_test.go new file mode 100644 index 0000000..fecf8b6 --- /dev/null +++ b/types/network_test.go @@ -0,0 +1,514 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2018-2023. All rights reserved. +// syscontainer-tools is licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Description: network interface type +// Author: Jiaqi Yang +// Create: 2023-05-03 + +// package types defines type used by libnetwork +package types + +import ( + "testing" +) + +// TestIsConflictNic tests IsConflictNic +func TestIsConflictNic(t *testing.T) { + const ( + testCtrNicName1 = "ctr1" + testCtrNicName2 = "ctr2" + testHostNicName1 = "host1" + testHostNicName2 = "host2" + testMac1 = "aa:bb:cc:dd:ee:ff" + testIP41 = CIDRIpExample1 + testIP42 = CIDRIpExample2 + testIP61 = CIDRIp6Example1 + testIP62 = CIDRIp6Example2 + testMTU = 1500 + ) + type args struct { + nic1 *InterfaceConf + nic2 *InterfaceConf + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "TC1-same ctrName(ctrName can not be empty)", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName1, + }, + }, + wantErr: true, + }, + { + name: "TC2-same hostName(hostName can not be empty)", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName1, + }, + }, + wantErr: true, + }, + { + name: "TC3.1-same mac", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + Mac: testMac1, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + Mac: testMac1, + }, + }, + wantErr: true, + }, + { + name: "TC3.2-diffrent mac address", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + Mac: testMac1, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + }, + }, + wantErr: false, + }, + { + name: "TC3.3-both not have a mac address", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + }, + }, + wantErr: false, + }, + { + name: "TC4.1-smae IP4", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + IP: testIP41, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + IP: testIP41, + }, + }, + wantErr: true, + }, + { + name: "TC4.2-different IP4 & not set IP6", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + IP: testIP41, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + IP: testIP42, + }, + }, + wantErr: false, + }, + { + name: "TC4.3-both not have a IP4 address", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + }, + }, + wantErr: false, + }, + { + name: "TC5.1-smae IP6", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + IP6: testIP61, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + IP6: testIP61, + }, + }, + wantErr: true, + }, + { + name: "TC5.2-different IP6", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + IP6: testIP61, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + IP6: testIP62, + }, + }, + wantErr: false, + }, + { + name: "TC5.3-both not have a IP6 address", + args: args{ + nic1: &InterfaceConf{ + CtrNicName: testCtrNicName1, + HostNicName: testHostNicName1, + }, + nic2: &InterfaceConf{ + CtrNicName: testCtrNicName2, + HostNicName: testHostNicName2, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := IsConflictNic(tt.args.nic1, tt.args.nic2); (err != nil) != tt.wantErr { + t.Errorf("IsConflictNic() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// TestIsSameNic tests IsSameNic +func TestIsSameNic(t *testing.T) { + const ( + testCtrNicName1 = "ctr1" + testCtrNicName2 = "ctr2" + testHostNicName1 = "host1" + testHostNicName2 = "host2" + testMac1 = "aa:bb:cc:dd:ee:ff" + testMac2 = "ff:ee:dd:cc:bb:aa" + testIP41 = CIDRIpExample1 + testIP42 = CIDRIpExample2 + testIP61 = CIDRIp6Example1 + testIP62 = CIDRIp6Example2 + testMTU1 = 1500 + testMTU2 = 1200 + testQlen1 = 500 + testQlen2 = 1000 + testBridge1 = "test1" + testBridge2 = "test2" + testType1 = "eth" + testType2 = "veth" + ) + type args struct { + obj *InterfaceConf + src *InterfaceConf + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "TC1-all data is empty", + args: args{ + obj: &InterfaceConf{}, + src: &InterfaceConf{}, + }, + want: true, + }, + { + name: "TC2-different IP", + args: args{ + obj: &InterfaceConf{ + IP: testIP41, + }, + src: &InterfaceConf{ + IP: testIP42, + }, + }, + want: false, + }, + { + name: "TC3-different IP6", + args: args{ + obj: &InterfaceConf{ + IP6: testIP61, + }, + src: &InterfaceConf{ + IP6: testIP62, + }, + }, + want: false, + }, + { + name: "TC4-different Mac", + args: args{ + obj: &InterfaceConf{ + Mac: testMac1, + }, + src: &InterfaceConf{ + Mac: testMac2, + }, + }, + want: false, + }, + { + name: "TC5-different Mtu", + args: args{ + obj: &InterfaceConf{ + Mtu: testMTU1, + }, + src: &InterfaceConf{ + Mtu: testMTU2, + }, + }, + want: false, + }, + { + name: "TC6-different Qlen", + args: args{ + obj: &InterfaceConf{ + Qlen: testQlen1, + }, + src: &InterfaceConf{ + Qlen: testQlen2, + }, + }, + want: false, + }, + { + name: "TC7-different Type", + args: args{ + obj: &InterfaceConf{ + Type: testType1, + }, + src: &InterfaceConf{ + Type: testType2, + }, + }, + want: false, + }, + { + name: "TC8-different Bridge", + args: args{ + obj: &InterfaceConf{ + Bridge: testBridge1, + }, + src: &InterfaceConf{ + Bridge: testBridge2, + }, + }, + want: false, + }, + { + name: "TC9-different host Name", + args: args{ + obj: &InterfaceConf{ + HostNicName: testHostNicName1, + }, + src: &InterfaceConf{ + HostNicName: testHostNicName2, + }, + }, + want: false, + }, + { + name: "TC10-different Ctr Name", + args: args{ + obj: &InterfaceConf{ + CtrNicName: testCtrNicName1, + }, + src: &InterfaceConf{ + CtrNicName: testCtrNicName2, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsSameNic(tt.args.obj, tt.args.src); got != tt.want { + t.Errorf("IsSameNic() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestValidNetworkConfig tests ValidNetworkConfig +func TestValidNetworkConfig(t *testing.T) { + const ( + testCtrNicName = "ctr1" + testHostNicName = "host1" + testMac1 = "aa:bb:cc:dd:ee:ff" + invalidMac = "ABCDEFG" + testIP41 = CIDRIpExample1 + testIP42 = CIDRIpExample2 + testIP61 = CIDRIp6Example1 + testIP62 = CIDRIp6Example2 + testIP4 = CIDRIpExample1 + invalidIP4 = IPExample1 + testIP6 = CIDRIp6Example1 + invalidIP6 = IP6Example1 + testBridge = "test1" + ethType = "eth" + vethType = "veth" + invalidType = "fake" + ) + type args struct { + conf *InterfaceConf + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "TC1-No IP & IP6", + args: args{ + conf: &InterfaceConf{}, + }, + wantErr: true, + }, + { + name: "TC2-invalid ip", + args: args{ + conf: &InterfaceConf{ + IP: invalidIP4, + }, + }, + wantErr: true, + }, + { + name: "TC3-invalid ip6", + args: args{ + conf: &InterfaceConf{ + IP6: invalidIP6, + }, + }, + wantErr: true, + }, + { + name: "TC4-invalid Mac", + args: args{ + conf: &InterfaceConf{ + IP: testIP4, + Mac: invalidMac, + }, + }, + wantErr: true, + }, + { + name: "TC5-No such eth nic", + args: args{ + conf: &InterfaceConf{ + IP: testIP4, + IP6: testIP6, + HostNicName: testHostNicName, + }, + }, + wantErr: true, + }, + { + name: "TC6-veth:No host nic name is not existed", + args: args{ + conf: &InterfaceConf{ + IP: testIP4, + IP6: testIP6, + HostNicName: testHostNicName, + Type: vethType, + }, + }, + wantErr: true, + }, + { + name: "TC7-eth:empty host nic name", + args: args{ + conf: &InterfaceConf{ + IP: testIP4, + IP6: testIP6, + Type: ethType, + }, + }, + wantErr: true, + }, + { + name: "TC7.1-eth:empty bridge", + args: args{ + conf: &InterfaceConf{ + IP: testIP4, + IP6: testIP6, + HostNicName: testHostNicName, + Type: ethType, + }, + }, + wantErr: true, + }, + { + name: "TC7.2-eth can not find eth", + args: args{ + conf: &InterfaceConf{ + IP: testIP4, + IP6: testIP6, + HostNicName: testHostNicName, + Bridge: testBridge, + Type: ethType, + }, + }, + wantErr: true, + }, + { + name: "TC8-invalid type", + args: args{ + conf: &InterfaceConf{ + IP: testIP4, + Type: invalidType, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidNetworkConfig(tt.args.conf); (err != nil) != tt.wantErr { + t.Errorf("ValidNetworkConfig() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} -- 2.41.0
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.