276 lines
8.8 KiB
Go
Raw Normal View History

2019-09-30 10:53:51 -04:00
// 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
2019-09-30 10:53:51 -04:00
// 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.
2019-09-30 10:53:51 -04:00
// Description: network interface operation
// Author: zhangwei
// Create: 2018-01-18
package libnetwork
import (
"fmt"
"os"
"strings"
"github.com/sirupsen/logrus"
hconfig "isula.org/syscontainer-tools/config"
"isula.org/syscontainer-tools/container"
"isula.org/syscontainer-tools/libnetwork/drivers"
"isula.org/syscontainer-tools/types"
2019-09-30 10:53:51 -04:00
)
// AddNic will add a network interface to container, it will update the config for container
func AddNic(ctr *container.Container, config *types.InterfaceConf, updateConfigOnly bool) error {
if err := ctr.Lock(); err != nil {
return err
}
defer ctr.Unlock()
// create config file handler.
hConfig, err := hconfig.NewContainerConfig(ctr)
if err != nil {
return err
}
defer hConfig.Flush()
if err := hConfig.CheckNicNum(); err != nil {
return err
}
if err := hConfig.IsConflictInterface(config); err != nil {
return err
}
if err := hConfig.UpdateNetworkInterface(config, true); err != nil {
return err
}
// don't insert net interface when:
// 1. update-config-only flag is set
// 2. container isn't running(pid==0)
if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() {
if err := AddNicToContainer(ctr.NetNsPath(), config); err != nil {
// roll back
hConfig.UpdateNetworkInterface(config, false)
return err
}
}
fmt.Fprintf(os.Stdout, "Add network interface (%s) to container (%s,%s) done\n", config.HostNicName, ctr.Name(), config.CtrNicName)
logrus.Infof("Add network interface (%s) to container (%s,%s) done", config.HostNicName, ctr.Name(), config.CtrNicName)
return nil
}
// AddNicToContainer will add a network interface to container only.
// It will be called by network-hook
func AddNicToContainer(nsPath string, config *types.InterfaceConf) (rErr error) {
driver, err := drivers.New(config.Type,
drivers.NicOptionCtrNicName(config.CtrNicName),
drivers.NicOptionHostNicName(config.HostNicName),
drivers.NicOptionNsPath(nsPath),
drivers.NicOptionIP(config.IP),
drivers.NicOptionMac(config.Mac),
drivers.NicOptionMtu(config.Mtu),
drivers.NicOptionQlen(config.Qlen),
drivers.NicOptionBridge(config.Bridge))
if err != nil {
return err
}
if err := driver.CreateIf(); err != nil {
return fmt.Errorf("failed to create interface: %v", err)
}
// do not need to DeleteIf here, if CreateIf failed, there are no ifs.
// JoinAndConfigure is doing cleanup within itself
return driver.JoinAndConfigure()
}
// UpdateNicInContainer will update an existing network interface in container.
func UpdateNicInContainer(nsPath string, config *types.InterfaceConf) (rErr error) {
driver, err := drivers.New(config.Type,
drivers.NicOptionCtrNicName(config.CtrNicName),
drivers.NicOptionHostNicName(config.HostNicName),
drivers.NicOptionNsPath(nsPath),
drivers.NicOptionIP(config.IP),
drivers.NicOptionMac(config.Mac),
drivers.NicOptionMtu(config.Mtu),
drivers.NicOptionQlen(config.Qlen),
drivers.NicOptionBridge(config.Bridge))
if err != nil {
return err
}
// Configure is doing cleanup within itself
return driver.Configure()
}
// DelNic will remove a network interface from container and update the config
func DelNic(ctr *container.Container, config *types.InterfaceConf) error {
if err := ctr.Lock(); err != nil {
return err
}
defer ctr.Unlock()
// create config file handler.
hConfig, err := hconfig.NewContainerConfig(ctr)
if err != nil {
return err
}
defer hConfig.Flush()
var newConfig *types.InterfaceConf
if newConfig = hConfig.FindInterfaceByName(config); newConfig == 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 err := hConfig.UpdateNetworkInterface(newConfig, false); err != nil {
return err
}
// only work for running container
if ctr.Pid() > 0 && ctr.CheckPidExist() {
if err := DelNicFromContainer(ctr.NetNsPath(), newConfig); err != nil {
if !strings.Contains(err.Error(), "failed to get host link by name") {
// roll back
hConfig.UpdateNetworkInterface(newConfig, true)
return err
}
logrus.Errorf("Remove network interface error: %s", err)
}
}
fmt.Fprintf(os.Stdout, "Remove network interface (%s) from container (%s,%s) done\n", newConfig.HostNicName, ctr.Name(), newConfig.CtrNicName)
logrus.Infof("Remove network interface (%s) from container (%s,%s) done", newConfig.HostNicName, ctr.Name(), newConfig.CtrNicName)
return nil
}
// DelNicFromContainer will remove a network interface from container only
func DelNicFromContainer(nsPath string, config *types.InterfaceConf) error {
driver, err := drivers.New(config.Type,
drivers.NicOptionCtrNicName(config.CtrNicName),
drivers.NicOptionHostNicName(config.HostNicName),
drivers.NicOptionNsPath(nsPath),
drivers.NicOptionIP(config.IP),
drivers.NicOptionMac(config.Mac),
drivers.NicOptionMtu(config.Mtu),
drivers.NicOptionBridge(config.Bridge))
if err != nil {
return err
}
return driver.DeleteIf()
}
// UpdateNic will reconfigure network interface for a container
func UpdateNic(ctr *container.Container, config *types.InterfaceConf, updateConfigOnly bool) error {
if err := ctr.Lock(); err != nil {
return err
}
defer ctr.Unlock()
hConfig, err := hconfig.NewContainerConfig(ctr)
if err != nil {
return err
}
var tmpConfig = new(types.InterfaceConf)
tmpConfig.CtrNicName = config.CtrNicName
var newConfig *types.InterfaceConf
if newConfig = hConfig.FindInterfaceByName(tmpConfig); newConfig == 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
} 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.Mac == "" {
tmpConfig.Mac = newConfig.Mac
} else {
tmpConfig.Mac = config.Mac
msg := fmt.Sprintf("Update MAC address for network interface (%s,%v) done", config.CtrNicName, config.Mac)
fmt.Fprintln(os.Stdout, msg)
logrus.Info(msg)
}
if config.Bridge == "" {
tmpConfig.Bridge = newConfig.Bridge
} else {
tmpConfig.Bridge = config.Bridge
msg := fmt.Sprintf("Update Bridge for network interface (%s,%v) done", config.CtrNicName, config.Bridge)
fmt.Fprintln(os.Stdout, msg)
logrus.Info(msg)
}
if config.Mtu == 0 {
tmpConfig.Mtu = newConfig.Mtu
} else {
tmpConfig.Mtu = config.Mtu
msg := fmt.Sprintf("Update Mtu for network interface (%s,%v) done", config.CtrNicName, config.Mtu)
fmt.Fprintln(os.Stdout, msg)
logrus.Info(msg)
}
// we use qlen < 0 to check if the user has set parameter qlen or not
if config.Qlen < 0 {
tmpConfig.Qlen = newConfig.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
if hConfig.IsSameInterface(tmpConfig) {
logrus.Infof("Network interface in container %s: Identical setting, nothing to change", config.CtrNicName, ctr.Name())
return nil
}
if err := hConfig.UpdateNetworkInterface(newConfig, false); err != nil {
return err
}
if err := hConfig.IsConflictInterface(tmpConfig); err != nil {
if err := hConfig.UpdateNetworkInterface(newConfig, true); err != nil {
return err
}
return err
}
if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() {
if err := UpdateNicInContainer(ctr.NetNsPath(), tmpConfig); err != nil {
if err := hConfig.UpdateNetworkInterface(newConfig, true); err != nil {
return err
}
return err
}
}
// update the config file.
if err := hConfig.UpdateNetworkInterface(tmpConfig, true); err != nil {
return err
}
hConfig.Flush()
logrus.Infof("Network interface %s in container %s update successfully", config.CtrNicName, ctr.Name())
return nil
}
// ListNic will list all network interfaces in a container
func ListNic(ctr *container.Container, filter *types.InterfaceConf) ([]*types.InterfaceConf, error) {
if err := ctr.Lock(); err != nil {
return nil, err
}
defer ctr.Unlock()
hConfig, err := hconfig.NewContainerConfig(ctr)
if err != nil {
return nil, err
}
return hConfig.GetNics(filter), nil
}