network/manager.go (650 lines of code) (raw):
// Copyright 2017 Microsoft. All rights reserved.
// MIT License
package network
import (
"context"
"net"
"sync"
"time"
"github.com/Azure/azure-container-networking/cns"
cnsclient "github.com/Azure/azure-container-networking/cns/client"
"github.com/Azure/azure-container-networking/cns/restserver"
"github.com/Azure/azure-container-networking/cns/types"
"github.com/Azure/azure-container-networking/common"
"github.com/Azure/azure-container-networking/log"
"github.com/Azure/azure-container-networking/netio"
"github.com/Azure/azure-container-networking/netlink"
"github.com/Azure/azure-container-networking/platform"
"github.com/Azure/azure-container-networking/store"
"github.com/pkg/errors"
"go.uber.org/zap"
)
const (
// Network store key.
storeKey = "Network"
VlanIDKey = "VlanID"
AzureCNS = "azure-cns"
SNATIPKey = "NCPrimaryIPKey"
RoutesKey = "RoutesKey"
IPTablesKey = "IPTablesKey"
genericData = "com.docker.network.generic"
ipv6AddressMask = 128
cnsBaseURL = "" // fallback to default http://localhost:10090
cnsReqTimeout = 15 * time.Second
StateLessCNIIsNotSet = "StateLess CNI mode is not enabled"
InfraInterfaceName = "eth0"
ContainerIDLength = 8
EndpointIfIndex = 0 // Azure CNI supports only one interface
DefaultNetworkID = "azure"
// TODO: Remove dummy GUID and come up with more permanent solution
dummyGUID = "12345678-1234-1234-1234-123456789012" // guid to trigger hnsv2 in windows
)
var Ipv4DefaultRouteDstPrefix = net.IPNet{
IP: net.IPv4zero,
Mask: net.IPv4Mask(0, 0, 0, 0),
}
var Ipv6DefaultRouteDstPrefix = net.IPNet{
IP: net.IPv6zero,
// This mask corresponds to a /0 subnet for IPv6
Mask: net.CIDRMask(0, ipv6AddressMask),
}
type NetworkClient interface {
CreateBridge() error
DeleteBridge() error
AddL2Rules(extIf *externalInterface) error
DeleteL2Rules(extIf *externalInterface)
SetBridgeMasterToHostInterface() error
SetHairpinOnHostInterface(bool) error
}
type EndpointClient interface {
AddEndpoints(epInfo *EndpointInfo) error
AddEndpointRules(epInfo *EndpointInfo) error
DeleteEndpointRules(ep *endpoint)
MoveEndpointsToContainerNS(epInfo *EndpointInfo, nsID uintptr) error
SetupContainerInterfaces(epInfo *EndpointInfo) error
ConfigureContainerInterfacesAndRoutes(epInfo *EndpointInfo) error
DeleteEndpoints(ep *endpoint) error
}
// NetworkManager manages the set of container networking resources.
type networkManager struct {
statelessCniMode bool
CnsClient *cnsclient.Client
Version string
TimeStamp time.Time
ExternalInterfaces map[string]*externalInterface
store store.KeyValueStore
netlink netlink.NetlinkInterface
netio netio.NetIOInterface
plClient platform.ExecClient
nsClient NamespaceClientInterface
iptablesClient ipTablesClient
dhcpClient dhcpClient
sync.Mutex
}
// NetworkManager API.
type NetworkManager interface {
Initialize(config *common.PluginConfig, isRehydrationRequired bool) error
Uninitialize()
AddExternalInterface(ifName, subnet, nicType string) error
CreateNetwork(nwInfo *EndpointInfo) error
DeleteNetwork(networkID string) error
GetNetworkInfo(networkID string) (EndpointInfo, error)
// FindNetworkIDFromNetNs returns the network name that contains an endpoint created for this netNS, errNetworkNotFound if no network is found
FindNetworkIDFromNetNs(netNs string) (string, error)
GetNumEndpointsByContainerID(containerID string) int
CreateEndpoint(client apipaClient, networkID string, epInfo *EndpointInfo) error
EndpointCreate(client apipaClient, epInfos []*EndpointInfo) error // TODO: change name
DeleteEndpoint(networkID string, endpointID string, epInfo *EndpointInfo) error
GetEndpointInfo(networkID string, endpointID string) (*EndpointInfo, error)
GetAllEndpoints(networkID string) (map[string]*EndpointInfo, error)
GetEndpointInfoBasedOnPODDetails(networkID string, podName string, podNameSpace string, doExactMatchForPodName bool) (*EndpointInfo, error)
AttachEndpoint(networkID string, endpointID string, sandboxKey string) (*endpoint, error)
DetachEndpoint(networkID string, endpointID string) error
UpdateEndpoint(networkID string, existingEpInfo *EndpointInfo, targetEpInfo *EndpointInfo) error
GetNumberOfEndpoints(ifName string, networkID string) int
GetEndpointID(containerID, ifName string) string
IsStatelessCNIMode() bool
SaveState(eps []*endpoint) error
DeleteState(epInfos []*EndpointInfo) error
GetEndpointInfosFromContainerID(containerID string) []*EndpointInfo
GetEndpointState(networkID, containerID string) ([]*EndpointInfo, error)
}
// Creates a new network manager.
func NewNetworkManager(nl netlink.NetlinkInterface, plc platform.ExecClient, netioCli netio.NetIOInterface, nsc NamespaceClientInterface,
iptc ipTablesClient, dhcpc dhcpClient,
) (NetworkManager, error) {
nm := &networkManager{
ExternalInterfaces: make(map[string]*externalInterface),
netlink: nl,
plClient: plc,
netio: netioCli,
nsClient: nsc,
iptablesClient: iptc,
dhcpClient: dhcpc,
}
return nm, nil
}
// Initialize configures network manager.
func (nm *networkManager) Initialize(config *common.PluginConfig, isRehydrationRequired bool) error {
nm.Version = config.Version
nm.store = config.Store
if config.Stateless {
if err := nm.SetStatelessCNIMode(); err != nil {
return errors.Wrapf(err, "Failed to initialize stateles CNI")
}
return nil
}
// Restore persisted state.
err := nm.restore(isRehydrationRequired)
return err
}
// Uninitialize cleans up network manager.
func (nm *networkManager) Uninitialize() {
}
// SetStatelessCNIMode enable the statelessCNI falg and inititlizes a CNSClient
func (nm *networkManager) SetStatelessCNIMode() error {
nm.statelessCniMode = true
// Create CNS client
client, err := cnsclient.New(cnsBaseURL, cnsReqTimeout)
if err != nil {
return errors.Wrapf(err, "failed to initialize CNS client")
}
nm.CnsClient = client
return nil
}
// IsStatelessCNIMode checks if the Stateless CNI mode has been enabled or not
func (nm *networkManager) IsStatelessCNIMode() bool {
return nm.statelessCniMode
}
// Restore reads network manager state from persistent store.
func (nm *networkManager) restore(isRehydrationRequired bool) error {
// Skip if a store is not provided.
if nm.store == nil {
logger.Info("network store is nil")
return nil
}
rebooted := false
// After a reboot, all address resources are implicitly released.
// Ignore the persisted state if it is older than the last reboot time.
// Read any persisted state.
err := nm.store.Read(storeKey, nm)
if err != nil {
if err == store.ErrKeyNotFound {
logger.Info("network store key not found")
// Considered successful.
return nil
} else if err == store.ErrStoreEmpty {
logger.Info("network store empty")
return nil
} else {
logger.Error("Failed to restore state", zap.Error(err))
return err
}
}
if isRehydrationRequired {
modTime, err := nm.store.GetModificationTime()
if err == nil {
rebootTime, err := nm.plClient.GetLastRebootTime()
logger.Info("reboot time, store mod time", zap.Any("rebootTime", rebootTime), zap.Any("modTime", modTime))
if err == nil && rebootTime.After(modTime) {
logger.Info("Detected Reboot")
rebooted = true
if clearNwConfig, err := nm.plClient.ClearNetworkConfiguration(); clearNwConfig {
if err != nil {
logger.Error("Failed to clear network configuration", zap.Error(err))
return err
}
// Delete the networks left behind after reboot
for _, extIf := range nm.ExternalInterfaces {
for _, nw := range extIf.Networks {
logger.Info("Deleting the network on reboot", zap.String("id", nw.Id))
_ = nm.deleteNetwork(nw.Id)
}
}
// Clear networkManager contents
nm.TimeStamp = time.Time{}
for extIfName := range nm.ExternalInterfaces {
delete(nm.ExternalInterfaces, extIfName)
}
return nil
}
}
}
}
// Populate pointers.
for _, extIf := range nm.ExternalInterfaces {
for _, nw := range extIf.Networks {
nw.extIf = extIf
}
}
// if rebooted recreate the network that existed before reboot.
if rebooted {
logger.Info("Rehydrating network state from persistent store")
for _, extIf := range nm.ExternalInterfaces {
for _, nw := range extIf.Networks {
nwInfo, err := nm.GetNetworkInfo(nw.Id)
if err != nil {
logger.Error("Failed to fetch network info for network extif err. This should not happen",
zap.Any("nw", nw), zap.Any("extIf", extIf), zap.Error(err))
return err
}
extIf.BridgeName = ""
_, err = nm.newNetworkImpl(&nwInfo, extIf)
if err != nil {
logger.Error("Restoring network failed for nwInfo extif. This should not happen",
zap.Any("nwInfo", nwInfo), zap.Any("extIf", extIf), zap.Error(err))
return err
}
}
}
}
logger.Info("Restored state")
return nil
}
// Save writes network manager state to persistent store.
func (nm *networkManager) save() error {
// CNI is not maintaining the state in Steless Mode.
if nm.IsStatelessCNIMode() {
return nil
}
// Skip if a store is not provided.
if nm.store == nil {
return nil
}
// Update time stamp.
nm.TimeStamp = time.Now()
err := nm.store.Write(storeKey, nm)
if err == nil {
logger.Info("Save succeeded")
} else {
logger.Error("Save failed", zap.Error(err))
}
return err
}
//
// NetworkManager API
//
// Provides atomic stateful wrappers around core networking functionality.
//
// AddExternalInterface adds a host interface to the list of available external interfaces.
func (nm *networkManager) AddExternalInterface(ifName, subnet, nicType string) error {
nm.Lock()
defer nm.Unlock()
err := nm.newExternalInterface(ifName, subnet, nicType)
if err != nil {
return err
}
return nil
}
// CreateNetwork creates a new container network.
func (nm *networkManager) CreateNetwork(epInfo *EndpointInfo) error {
nm.Lock()
defer nm.Unlock()
_, err := nm.newNetwork(epInfo)
if err != nil {
return err
}
return nil
}
// DeleteNetwork deletes an existing container network.
func (nm *networkManager) DeleteNetwork(networkID string) error {
nm.Lock()
defer nm.Unlock()
err := nm.deleteNetwork(networkID)
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}
// GetNetworkInfo returns information about the given network.
func (nm *networkManager) GetNetworkInfo(networkID string) (EndpointInfo, error) {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkID)
if err != nil {
return EndpointInfo{}, err
}
nwInfo := EndpointInfo{
NetworkID: networkID,
Subnets: nw.Subnets,
Mode: nw.Mode,
EnableSnatOnHost: nw.EnableSnatOnHost,
Options: make(map[string]interface{}),
}
getNetworkInfoImpl(&nwInfo, nw)
if nw.extIf != nil {
nwInfo.BridgeName = nw.extIf.BridgeName
}
return nwInfo, nil
}
func (nm *networkManager) createEndpoint(cli apipaClient, networkID string, epInfo *EndpointInfo) (*endpoint, error) {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkID)
if err != nil {
return nil, err
}
if nw.VlanId != 0 {
if epInfo.Data[VlanIDKey] == nil {
logger.Info("overriding endpoint vlanid with network vlanid")
epInfo.Data[VlanIDKey] = nw.VlanId
}
}
ep, err := nw.newEndpoint(cli, nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, nm.dhcpClient, epInfo)
if err != nil {
return nil, err
}
// any error after this point should also clean up the endpoint we created above
defer func() {
if err != nil {
logger.Error("Create endpoint failure", zap.Error(err))
logger.Info("Cleanup resources")
delErr := nw.deleteEndpoint(nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, nm.dhcpClient, ep.Id)
if delErr != nil {
logger.Error("Deleting endpoint after create endpoint failure failed with", zap.Error(delErr))
}
}
}()
return ep, nil
}
// CreateEndpoint creates a new container endpoint (this is for compatibility-- add flow should no longer use this).
func (nm *networkManager) CreateEndpoint(cli apipaClient, networkID string, epInfo *EndpointInfo) error {
_, err := nm.createEndpoint(cli, networkID, epInfo)
return err
}
// UpdateEndpointState will make a call to CNS updatEndpointState API in the stateless CNI mode
// It will add HNSEndpointID or HostVeth name to the endpoint state
func (nm *networkManager) UpdateEndpointState(eps []*endpoint) error {
if len(eps) == 0 {
return nil
}
ifnameToIPInfoMap := generateCNSIPInfoMap(eps) // key : interface name, value : IPInfo
for _, ipinfo := range ifnameToIPInfoMap {
logger.Info("Update endpoint state", zap.String("hnsEndpointID", ipinfo.HnsEndpointID), zap.String("hnsNetworkID", ipinfo.HnsNetworkID),
zap.String("hostVethName", ipinfo.HostVethName), zap.String("macAddress", ipinfo.MacAddress), zap.String("nicType", string(ipinfo.NICType)))
}
// we assume all endpoints have the same container id
cnsEndpointID := eps[0].ContainerID
if err := validateUpdateEndpointState(cnsEndpointID, ifnameToIPInfoMap); err != nil {
return errors.Wrap(err, "failed to validate update endpoint state that will be sent to cns")
}
response, err := nm.CnsClient.UpdateEndpoint(context.TODO(), cnsEndpointID, ifnameToIPInfoMap)
if err != nil {
return errors.Wrapf(err, "Update endpoint API returend with error")
}
logger.Info("Update endpoint API returend ", zap.String("podname: ", response.ReturnCode.String()))
return nil
}
func validateUpdateEndpointState(endpointID string, ifNameToIPInfoMap map[string]*restserver.IPInfo) error {
if endpointID == "" {
return errors.New("endpoint id empty while validating update endpoint state")
}
for ifName := range ifNameToIPInfoMap {
if ifName == "" {
return errors.New("an interface name is empty while validating update endpoint state")
}
}
return nil
}
// GetEndpointState will make a call to CNS GetEndpointState API in the stateless CNI mode to fetch the endpointInfo
// TODO unit tests need to be added, WorkItem: 26606939
// In stateless cni, container id is the endpoint id, so you can pass in either
func (nm *networkManager) GetEndpointState(networkID, containerID string) ([]*EndpointInfo, error) {
endpointResponse, err := nm.CnsClient.GetEndpoint(context.TODO(), containerID)
if err != nil {
if endpointResponse.Response.ReturnCode == types.NotFound {
return nil, ErrEndpointStateNotFound
}
if endpointResponse.Response.ReturnCode == types.ConnectionError {
return nil, ErrConnectionFailure
}
return nil, ErrGetEndpointStateFailure
}
epInfos := cnsEndpointInfotoCNIEpInfos(endpointResponse.EndpointInfo, containerID)
for i := 0; i < len(epInfos); i++ {
if epInfos[i].NICType == cns.InfraNIC {
if epInfos[i].IsEndpointStateIncomplete() { // assume false for swift v2 for now
if networkID == "" {
networkID = DefaultNetworkID
}
epInfos[i], err = epInfos[i].GetEndpointInfoByIPImpl(epInfos[i].IPAddresses, networkID)
if err != nil {
logger.Info("Endpoint State is incomlete for endpoint: ", zap.Error(err), zap.String("endpointID", epInfos[i].EndpointID))
}
}
}
}
return epInfos, nil
}
// DeleteEndpoint deletes an existing container endpoint.
func (nm *networkManager) DeleteEndpoint(networkID, endpointID string, epInfo *EndpointInfo) error {
nm.Lock()
defer nm.Unlock()
if nm.IsStatelessCNIMode() {
// Calls deleteEndpointImpl directly, skipping the get network check; does not call cns
return nm.DeleteEndpointState(networkID, epInfo)
}
nw, err := nm.getNetwork(networkID)
if err != nil {
return err
}
err = nw.deleteEndpoint(nm.netlink, nm.plClient, nm.netio, nm.nsClient, nm.iptablesClient, nm.dhcpClient, endpointID)
if err != nil {
return err
}
return nil
}
func (nm *networkManager) DeleteEndpointState(networkID string, epInfo *EndpointInfo) error {
// we want to always use hnsv2 in stateless
// hnsv2 is only enabled if NetNs has a valid guid and the hnsv2 api is supported
// by passing in a dummy guid, we satisfy the first condition
nw := &network{
Id: networkID, // currently unused in stateless cni
HnsId: epInfo.HNSNetworkID,
Mode: opModeTransparentVlan,
SnatBridgeIP: "",
NetNs: dummyGUID, // to trigger hns v2, windows
extIf: &externalInterface{
Name: InfraInterfaceName,
MacAddress: nil,
},
}
ep := &endpoint{
Id: epInfo.EndpointID,
HnsId: epInfo.HNSEndpointID,
HNSNetworkID: epInfo.HNSNetworkID, // unused (we use nw.HnsId for deleting the network)
HostIfName: epInfo.HostIfName,
LocalIP: "",
VlanID: 0,
AllowInboundFromHostToNC: false, // stateless currently does not support apipa
AllowInboundFromNCToHost: false,
EnableSnatOnHost: false,
EnableMultitenancy: false,
NetworkContainerID: epInfo.NetworkContainerID, // we don't use this as long as AllowInboundFromHostToNC and AllowInboundFromNCToHost are false
NetNs: dummyGUID, // to trigger hnsv2, windows
NICType: epInfo.NICType,
IfName: epInfo.IfName, // TODO: For stateless cni linux populate IfName here to use in deletion in secondary endpoint client
}
logger.Info("Deleting endpoint with", zap.String("Endpoint Info: ", epInfo.PrettyString()), zap.String("HNISID : ", ep.HnsId))
err := nw.deleteEndpointImpl(netlink.NewNetlink(), platform.NewExecClient(logger), nil, nil, nil, nil, nil, ep)
if err != nil {
return err
}
err = nm.deleteNetworkImpl(nw, ep.NICType)
// no need to clean up state in stateless
if err != nil {
return errors.Wrap(err, "Failed to delete HNS Network")
}
return nil
}
// GetEndpointInfo returns information about the given endpoint.
func (nm *networkManager) GetEndpointInfo(networkID, endpointID string) (*EndpointInfo, error) {
nm.Lock()
defer nm.Unlock()
if nm.IsStatelessCNIMode() {
logger.Info("calling cns getEndpoint API")
epInfos, err := nm.GetEndpointState(networkID, endpointID)
if err != nil {
return nil, err
}
for _, epInfo := range epInfos {
if epInfo.NICType == cns.InfraNIC {
return epInfo, nil
}
}
return nil, err
}
nw, err := nm.getNetwork(networkID)
if err != nil {
return nil, err
}
ep, err := nw.getEndpoint(endpointID)
if err != nil {
return nil, err
}
return ep.getInfo(), nil
}
func (nm *networkManager) GetAllEndpoints(networkId string) (map[string]*EndpointInfo, error) {
nm.Lock()
defer nm.Unlock()
eps := make(map[string]*EndpointInfo)
// Special case when CNS invokes CNI, but there is no state, but return gracefully
if len(nm.ExternalInterfaces) == 0 {
logger.Info("Network manager has no external interfaces, is the state file populated?")
return eps, store.ErrStoreEmpty
}
nw, err := nm.getNetwork(networkId)
if err != nil {
return nil, err
}
for epid, ep := range nw.Endpoints {
eps[epid] = ep.getInfo()
}
return eps, nil
}
// GetEndpointInfoBasedOnPODDetails returns information about the given endpoint.
// It returns an error if a single pod has multiple endpoints.
func (nm *networkManager) GetEndpointInfoBasedOnPODDetails(networkID string, podName string, podNameSpace string, doExactMatchForPodName bool) (*EndpointInfo, error) {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkID)
if err != nil {
return nil, err
}
ep, err := nw.getEndpointByPOD(podName, podNameSpace, doExactMatchForPodName)
if err != nil {
return nil, err
}
return ep.getInfo(), nil
}
// AttachEndpoint attaches an endpoint to a sandbox.
func (nm *networkManager) AttachEndpoint(networkId string, endpointId string, sandboxKey string) (*endpoint, error) {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkId)
if err != nil {
return nil, err
}
ep, err := nw.getEndpoint(endpointId)
if err != nil {
return nil, err
}
err = ep.attach(sandboxKey)
if err != nil {
return nil, err
}
err = nm.save()
if err != nil {
return nil, err
}
return ep, nil
}
// DetachEndpoint detaches an endpoint from its sandbox.
func (nm *networkManager) DetachEndpoint(networkId string, endpointId string) error {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkId)
if err != nil {
return err
}
ep, err := nw.getEndpoint(endpointId)
if err != nil {
return err
}
err = ep.detach()
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}
// UpdateEndpoint updates an existing container endpoint.
func (nm *networkManager) UpdateEndpoint(networkID string, existingEpInfo *EndpointInfo, targetEpInfo *EndpointInfo) error {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkID)
if err != nil {
return err
}
err = nm.updateEndpoint(nw, existingEpInfo, targetEpInfo)
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}
func (nm *networkManager) GetNumberOfEndpoints(ifName string, networkId string) int {
if ifName == "" {
for key := range nm.ExternalInterfaces {
ifName = key
break
}
}
if nm.ExternalInterfaces != nil {
extIf := nm.ExternalInterfaces[ifName]
if extIf != nil && extIf.Networks != nil {
nw := extIf.Networks[networkId]
if nw != nil && nw.Endpoints != nil {
return len(nw.Endpoints)
}
}
}
return 0
}
// GetEndpointID returns a unique endpoint ID based on the CNI mode.
func (nm *networkManager) GetEndpointID(containerID, ifName string) string {
if nm.IsStatelessCNIMode() {
return containerID
}
if len(containerID) > ContainerIDLength {
containerID = containerID[:ContainerIDLength]
} else {
log.Printf("Container ID is not greater than 8 ID: %v", containerID)
return ""
}
return containerID + "-" + ifName
}
// saves the map of network ids to endpoints to the state file
func (nm *networkManager) SaveState(eps []*endpoint) error {
nm.Lock()
defer nm.Unlock()
logger.Info("Saving state")
// If we fail half way, we'll propagate an error up which should clean everything up
if nm.IsStatelessCNIMode() {
err := nm.UpdateEndpointState(eps)
return err
}
// once endpoints and networks are in-memory, save once
return nm.save()
}
func (nm *networkManager) DeleteState(_ []*EndpointInfo) error {
nm.Lock()
defer nm.Unlock()
logger.Info("Deleting state")
// We do not use DeleteEndpointState for stateless cni because we already call it in DeleteEndpoint
// This function is only for saving to stateless cni or the cni statefile
// For stateless cni, plugin.ipamInvoker.Delete takes care of removing the state in the main Delete function
if nm.IsStatelessCNIMode() {
return nil
}
// once endpoints and networks are deleted in-memory, save once
return nm.save()
}
// called to convert a cns restserver EndpointInfo into a network EndpointInfo
func cnsEndpointInfotoCNIEpInfos(endpointInfo restserver.EndpointInfo, endpointID string) []*EndpointInfo {
ret := []*EndpointInfo{}
for ifName, ipInfo := range endpointInfo.IfnameToIPMap {
epInfo := &EndpointInfo{
EndpointID: endpointID, // endpoint id is always the same, but we shouldn't use it in the stateless path
IfIndex: EndpointIfIndex, // Azure CNI supports only one interface
ContainerID: endpointID,
PODName: endpointInfo.PodName,
PODNameSpace: endpointInfo.PodNamespace,
NetworkContainerID: endpointID,
}
// If we create an endpoint state with stateful cni and then swap to a stateless cni binary, ifname would not be populated
// triggered in migration to stateless only, assuming no incomplete state for delegated
if ifName == "" {
ifName = InfraInterfaceName
ipInfo.NICType = cns.InfraNIC
}
// filling out the InfraNIC from the state
epInfo.IPAddresses = ipInfo.IPv4
epInfo.IPAddresses = append(epInfo.IPAddresses, ipInfo.IPv6...)
epInfo.IfName = ifName // epInfo.IfName is set to the value of ep.IfName when the endpoint was added
// sidenote: ifname doesn't seem to be used in linux (or even windows) deletion
epInfo.HostIfName = ipInfo.HostVethName
epInfo.HNSEndpointID = ipInfo.HnsEndpointID
epInfo.NICType = ipInfo.NICType
epInfo.HNSNetworkID = ipInfo.HnsNetworkID
epInfo.MacAddress = net.HardwareAddr(ipInfo.MacAddress)
ret = append(ret, epInfo)
}
return ret
}
// gets all endpoint infos associated with a container id and populates the network id field
// nictype may be empty in which case it is likely of type "infra"
func (nm *networkManager) GetEndpointInfosFromContainerID(containerID string) []*EndpointInfo {
ret := []*EndpointInfo{}
for _, extIf := range nm.ExternalInterfaces {
for networkID, nw := range extIf.Networks {
for _, ep := range nw.Endpoints {
if ep.ContainerID == containerID {
val := ep.getInfo()
val.NetworkID = networkID // endpoint doesn't contain the network id
ret = append(ret, val)
}
}
}
}
return ret
}
func generateCNSIPInfoMap(eps []*endpoint) map[string]*restserver.IPInfo {
ifNametoIPInfoMap := make(map[string]*restserver.IPInfo) // key : interface name, value : IPInfo
for _, ep := range eps {
ifNametoIPInfoMap[ep.IfName] = &restserver.IPInfo{ // in windows, the nicname is args ifname, in linux, it's ethX
NICType: ep.NICType,
HnsEndpointID: ep.HnsId,
HnsNetworkID: ep.HNSNetworkID,
HostVethName: ep.HostIfName,
MacAddress: ep.MacAddress.String(),
}
}
return ifNametoIPInfoMap
}