Skip to content

Commit

Permalink
Implement multitenancy for windows (#259)
Browse files Browse the repository at this point in the history
* Implement multitenancy for windows
  • Loading branch information
ashvindeodhar authored and Yongli Chen committed Oct 31, 2018
1 parent 6e6260a commit bfb3eaa
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 57 deletions.
98 changes: 69 additions & 29 deletions cni/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,31 @@ func GetEndpointID(args *cniSkel.CmdArgs) string {
return infraEpId
}

// getPodInfo returns POD info by parsing the CNI args.
func (plugin *netPlugin) getPodInfo(args string) (string, string, error) {
podCfg, err := cni.ParseCniArgs(args)
if err != nil {
log.Printf("Error while parsing CNI Args %v", err)
return "", "", err
}

k8sNamespace := string(podCfg.K8S_POD_NAMESPACE)
if len(k8sNamespace) == 0 {
errMsg := "Pod Namespace not specified in CNI Args"
log.Printf(errMsg)
return "", "", plugin.Errorf(errMsg)
}

k8sPodName := string(podCfg.K8S_POD_NAME)
if len(k8sPodName) == 0 {
errMsg := "Pod Name not specified in CNI Args"
log.Printf(errMsg)
return "", "", plugin.Errorf(errMsg)
}

return k8sPodName, k8sNamespace, nil
}

//
// CNI implementation
// https://github.com/containernetworking/cni/blob/master/SPEC.md
Expand Down Expand Up @@ -192,26 +217,11 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
}()

// Parse Pod arguments.
podCfg, err := cni.ParseCniArgs(args.Args)
k8sPodName, k8sNamespace, err := plugin.getPodInfo(args.Args)
if err != nil {
log.Printf("Error while parsing CNI Args %v", err)
return err
}

k8sNamespace := string(podCfg.K8S_POD_NAMESPACE)
if len(k8sNamespace) == 0 {
errMsg := "Pod Namespace not specified in CNI Args"
log.Printf(errMsg)
return plugin.Errorf(errMsg)
}

k8sPodName := string(podCfg.K8S_POD_NAME)
if len(k8sPodName) == 0 {
errMsg := "Pod Name not specified in CNI Args"
log.Printf(errMsg)
return plugin.Errorf(errMsg)
}

k8sContainerID := args.ContainerID
if len(k8sContainerID) == 0 {
errMsg := "Container ID not specified in CNI Args"
Expand All @@ -234,10 +244,6 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
}
}

// Initialize values from network config.
networkId := nwCfg.Name
endpointId := GetEndpointID(args)

result, cnsNetworkConfig, subnetPrefix, azIpamResult, err = GetMultiTenancyCNIResult(enableInfraVnet, nwCfg, plugin, k8sPodName, k8sNamespace, args.IfName)
if err != nil {
log.Printf("GetMultiTenancyCNIResult failed with error %v", err)
Expand All @@ -252,6 +258,15 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {

log.Printf("Result from multitenancy %+v", result)

// Initialize values from network config.
networkId, err := getNetworkName(k8sPodName, k8sNamespace, args.IfName, nwCfg)
if err != nil {
log.Printf("[cni-net] Failed to extract network name from network config. error: %v", err)
return err
}

endpointId := GetEndpointID(args)

policies := cni.GetPoliciesFromNwCfg(nwCfg.AdditionalArgs)

// Check whether the network already exists.
Expand All @@ -265,13 +280,15 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
*/
epInfo, _ := plugin.nm.GetEndpointInfo(networkId, endpointId)
if epInfo != nil {
result, err = handleConsecutiveAdd(args.ContainerID, endpointId, nwInfo, nwCfg)
if err != nil {
log.Printf("handleConsecutiveAdd failed with error %v", err)
return err
resultConsAdd, errConsAdd := handleConsecutiveAdd(args.ContainerID, endpointId, nwInfo, nwCfg)
if errConsAdd != nil {
log.Printf("handleConsecutiveAdd failed with error %v", errConsAdd)
result = resultConsAdd
return errConsAdd
}

if result != nil {
if resultConsAdd != nil {
result = resultConsAdd
return nil
}
}
Expand Down Expand Up @@ -335,11 +352,14 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
}

log.Printf("[cni-net] nwDNSInfo: %v", nwDNSInfo)
// Update subnet prefix for multi-tenant scenario
updateSubnetPrefix(cnsNetworkConfig, &subnetPrefix)

// Create the network.
nwInfo := network.NetworkInfo{
Id: networkId,
Mode: nwCfg.Mode,
Id: networkId,
Mode: nwCfg.Mode,
MasterIfName: masterIfName,
Subnets: []network.SubnetInfo{
network.SubnetInfo{
Family: platform.AfINET,
Expand Down Expand Up @@ -492,8 +512,18 @@ func (plugin *netPlugin) Get(args *cniSkel.CmdArgs) error {

log.Printf("[cni-net] Read network configuration %+v.", nwCfg)

// Parse Pod arguments.
k8sPodName, k8sNamespace, err := plugin.getPodInfo(args.Args)
if err != nil {
return err
}

// Initialize values from network config.
networkId := nwCfg.Name
networkId, err := getNetworkName(k8sPodName, k8sNamespace, args.IfName, nwCfg)
if err != nil {
log.Printf("[cni-net] Failed to extract network name from network config. error: %v", err)
}

endpointId := GetEndpointID(args)

// Query the network.
Expand Down Expand Up @@ -552,8 +582,18 @@ func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error {

log.Printf("[cni-net] Read network configuration %+v.", nwCfg)

// Parse Pod arguments.
k8sPodName, k8sNamespace, err := plugin.getPodInfo(args.Args)
if err != nil {
return err
}

// Initialize values from network config.
networkId := nwCfg.Name
networkId, err := getNetworkName(k8sPodName, k8sNamespace, args.IfName, nwCfg)
if err != nil {
log.Printf("[cni-net] Failed to extract network name from network config. error: %v", err)
}

endpointId := GetEndpointID(args)

// Query the network.
Expand Down
7 changes: 7 additions & 0 deletions cni/network/network_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,10 @@ func getEndpointDNSSettings(nwCfg *cni.NetworkConfig, result *cniTypesCurr.Resul
func getPoliciesFromRuntimeCfg(nwCfg *cni.NetworkConfig) []policy.Policy {
return nil
}

func updateSubnetPrefix(cnsNetworkConfig *cns.GetNetworkContainerResponse, subnetPrefix *net.IPNet) {
}

func getNetworkName(podName, podNs, ifName string, nwCfg *cni.NetworkConfig) (string, error) {
return nwCfg.Name, nil
}
46 changes: 46 additions & 0 deletions cni/network/network_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net"
"strconv"
"strings"

"github.com/Azure/azure-container-networking/cni"
Expand Down Expand Up @@ -70,14 +71,59 @@ func addInfraRoutes(azIpamResult *cniTypesCurr.Result, result *cniTypesCurr.Resu
}

func setNetworkOptions(cnsNwConfig *cns.GetNetworkContainerResponse, nwInfo *network.NetworkInfo) {
if cnsNwConfig != nil && cnsNwConfig.MultiTenancyInfo.ID != 0 {
log.Printf("Setting Network Options")
vlanMap := make(map[string]interface{})
vlanMap[network.VlanIDKey] = strconv.Itoa(cnsNwConfig.MultiTenancyInfo.ID)
nwInfo.Options[dockerNetworkOption] = vlanMap
}
}

func setEndpointOptions(cnsNwConfig *cns.GetNetworkContainerResponse, epInfo *network.EndpointInfo, vethName string) {
if cnsNwConfig != nil && cnsNwConfig.MultiTenancyInfo.ID != 0 {
log.Printf("Setting Endpoint Options")
var cnetAddressMap []string
for _, ipSubnet := range cnsNwConfig.CnetAddressSpace {
cnetAddressMap = append(cnetAddressMap, ipSubnet.IPAddress+"/"+strconv.Itoa(int(ipSubnet.PrefixLength)))
}
epInfo.Data[network.CnetAddressSpace] = cnetAddressMap
}
}

func addSnatInterface(nwCfg *cni.NetworkConfig, result *cniTypesCurr.Result) {
}

func updateSubnetPrefix(cnsNwConfig *cns.GetNetworkContainerResponse, subnetPrefix *net.IPNet) {
if cnsNwConfig != nil && cnsNwConfig.MultiTenancyInfo.ID != 0 {
ipconfig := cnsNwConfig.IPConfiguration
ipAddr := net.ParseIP(ipconfig.IPSubnet.IPAddress)

if ipAddr.To4() != nil {
*subnetPrefix = net.IPNet{IP: ipAddr, Mask: net.CIDRMask(int(ipconfig.IPSubnet.PrefixLength), 32)}
} else {
*subnetPrefix = net.IPNet{IP: ipAddr, Mask: net.CIDRMask(int(ipconfig.IPSubnet.PrefixLength), 128)}
}

subnetPrefix.IP = subnetPrefix.IP.Mask(subnetPrefix.Mask)
log.Printf("Updated subnetPrefix: %s", subnetPrefix.String())
}
}

func getNetworkName(podName, podNs, ifName string, nwCfg *cni.NetworkConfig) (string, error) {
if nwCfg.MultiTenancy {
_, cnsNetworkConfig, _, err := getContainerNetworkConfiguration(nwCfg, "", podName, podNs, ifName)
if err != nil {
log.Printf("GetContainerNetworkConfiguration failed for podname %v namespace %v with error %v", podName, podNs, err)
return "", err
}

networkName := fmt.Sprintf("%s-vlanid%v", nwCfg.Name, cnsNetworkConfig.MultiTenancyInfo.ID)
return networkName, nil
}

return nwCfg.Name, nil
}

func setupInfraVnetRoutingForMultitenancy(
nwCfg *cni.NetworkConfig,
azIpamResult *cniTypesCurr.Result,
Expand Down
54 changes: 46 additions & 8 deletions network/endpoint_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ func ConstructEndpointID(containerID string, netNsPath string, ifName string) (s

// newEndpointImpl creates a new endpoint in the network.
func (nw *network) newEndpointImpl(epInfo *EndpointInfo) (*endpoint, error) {
var vlanid int

if epInfo.Data != nil {
if _, ok := epInfo.Data[VlanIDKey]; ok {
vlanid = epInfo.Data[VlanIDKey].(int)
}
}

// Get Infrastructure containerID. Handle ADD calls for workload container.
var err error
infraEpName, _ := ConstructEndpointID(epInfo.ContainerID, epInfo.NetNsPath, epInfo.IfName)
Expand All @@ -53,7 +61,35 @@ func (nw *network) newEndpointImpl(epInfo *EndpointInfo) (*endpoint, error) {
VirtualNetwork: nw.HnsId,
DNSSuffix: epInfo.DNS.Suffix,
DNSServerList: strings.Join(epInfo.DNS.Servers, ","),
Policies: policy.SerializePolicies(policy.EndpointPolicy, epInfo.Policies),
}

// Set outbound NAT policy
outBoundNatPolicy := hcsshim.OutboundNatPolicy{}
outBoundNatPolicy.Policy.Type = hcsshim.OutboundNat

exceptionList, err := policy.GetOutBoundNatExceptionList(epInfo.Policies)
if err != nil {
log.Printf("[net] Failed to parse outbound NAT policy %v", err)
return nil, err
}

if exceptionList != nil {
for _, ipAddress := range exceptionList {
outBoundNatPolicy.Exceptions = append(outBoundNatPolicy.Exceptions, ipAddress)
}
}

if epInfo.Data[CnetAddressSpace] != nil {
if cnetAddressSpace := epInfo.Data[CnetAddressSpace].([]string); cnetAddressSpace != nil {
for _, ipAddress := range cnetAddressSpace {
outBoundNatPolicy.Exceptions = append(outBoundNatPolicy.Exceptions, ipAddress)
}
}
}

if outBoundNatPolicy.Exceptions != nil {
serializedOutboundNatPolicy, _ := json.Marshal(outBoundNatPolicy)
hnsEndpoint.Policies = append(hnsEndpoint.Policies, serializedOutboundNatPolicy)
}

// HNS currently supports only one IP address per endpoint.
Expand Down Expand Up @@ -96,13 +132,15 @@ func (nw *network) newEndpointImpl(epInfo *EndpointInfo) (*endpoint, error) {

// Create the endpoint object.
ep := &endpoint{
Id: infraEpName,
HnsId: hnsResponse.Id,
SandboxKey: epInfo.ContainerID,
IfName: epInfo.IfName,
IPAddresses: epInfo.IPAddresses,
Gateways: []net.IP{net.ParseIP(hnsResponse.GatewayAddress)},
DNS: epInfo.DNS,
Id: infraEpName,
HnsId: hnsResponse.Id,
SandboxKey: epInfo.ContainerID,
IfName: epInfo.IfName,
IPAddresses: epInfo.IPAddresses,
Gateways: []net.IP{net.ParseIP(hnsResponse.GatewayAddress)},
DNS: epInfo.DNS,
VlanID: vlanid,
EnableSnatOnHost: epInfo.EnableSnatOnHost,
}

for _, route := range epInfo.Routes {
Expand Down
5 changes: 3 additions & 2 deletions network/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import (

const (
// Network store key.
storeKey = "Network"
VlanIDKey = "VlanID"
storeKey = "Network"
VlanIDKey = "VlanID"
genericData = "com.docker.network.generic"
)

type NetworkClient interface {
Expand Down
22 changes: 20 additions & 2 deletions network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package network

import (
"net"
"strings"

"github.com/Azure/azure-container-networking/log"
"github.com/Azure/azure-container-networking/network/policy"
Expand Down Expand Up @@ -46,6 +47,7 @@ type network struct {

// NetworkInfo contains read-only information about a container network.
type NetworkInfo struct {
MasterIfName string
Id string
Mode string
Subnets []SubnetInfo
Expand Down Expand Up @@ -121,6 +123,16 @@ func (nm *networkManager) findExternalInterfaceBySubnet(subnet string) *external
return nil
}

// FindExternalInterfaceByName finds an external interface by name.
func (nm *networkManager) findExternalInterfaceByName(ifName string) *externalInterface {
extIf, exists := nm.ExternalInterfaces[ifName]
if exists && extIf != nil {
return extIf
}

return nil
}

// NewNetwork creates a new container network.
func (nm *networkManager) newNetwork(nwInfo *NetworkInfo) (*network, error) {
var nw *network
Expand All @@ -138,8 +150,14 @@ func (nm *networkManager) newNetwork(nwInfo *NetworkInfo) (*network, error) {
nwInfo.Mode = opModeDefault
}

// Find the external interface for this subnet.
extIf := nm.findExternalInterfaceBySubnet(nwInfo.Subnets[0].Prefix.String())
// If the master interface name is provided, find the external interface by name
// else use subnet to to find the interface
var extIf *externalInterface
if len(strings.TrimSpace(nwInfo.MasterIfName)) > 0 {
extIf = nm.findExternalInterfaceByName(nwInfo.MasterIfName)
} else {
extIf = nm.findExternalInterfaceBySubnet(nwInfo.Subnets[0].Prefix.String())
}
if extIf == nil {
err = errSubnetNotFound
return nil, err
Expand Down
2 changes: 0 additions & 2 deletions network/network_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ const (
// Virtual MAC address used by Azure VNET.
virtualMacAddress = "12:34:56:78:9a:bc"

genericData = "com.docker.network.generic"

SnatBridgeIPKey = "snatBridgeIP"

LocalIPKey = "localIP"
Expand Down
Loading

0 comments on commit bfb3eaa

Please sign in to comment.