Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
SOURCEFILES = \
$(wildcard cni/*.go) \
$(wildcard cni/ipam/*.go) \
$(wildcard cni/network/*.go) \
$(wildcard cni/plugin/*.go) \
$(wildcard cnm/*.go) \
$(wildcard cnm/ipam/*.go) \
$(wildcard cnm/network/*.go) \
$(wildcard cnm/plugin/*.go) \
# Source files common to all targets.
COREFILES = \
$(wildcard common/*.go) \
$(wildcard ebtables/*.go) \
$(wildcard ipam/*.go) \
Expand All @@ -15,6 +8,22 @@ SOURCEFILES = \
$(wildcard network/*.go) \
$(wildcard store/*.go)

# Source files for building CNM plugin.
CNMFILES = \
$(wildcard cnm/*.go) \
$(wildcard cnm/ipam/*.go) \
$(wildcard cnm/network/*.go) \
$(wildcard cnm/plugin/*.go) \
$(COREFILES)

# Source files for building CNI plugin.
CNIFILES = \
$(wildcard cni/*.go) \
$(wildcard cni/ipam/*.go) \
$(wildcard cni/network/*.go) \
$(wildcard cni/plugin/*.go) \
$(COREFILES)

CNMDIR = cnm/plugin

CNIDIR = cni/plugin
Expand All @@ -35,11 +44,11 @@ clean:
rm -rf $(OUTPUTDIR)

# Build the Azure CNM plugin.
$(OUTPUTDIR)/azure-cnm-plugin: $(SOURCEFILES)
$(OUTPUTDIR)/azure-cnm-plugin: $(CNMFILES)
go build -v -o $(OUTPUTDIR)/azure-cnm-plugin -ldflags "-X main.version=$(VERSION) -s -w" $(CNMDIR)/*.go

# Build the Azure CNI plugin.
$(OUTPUTDIR)/azure-cni-plugin: $(SOURCEFILES)
$(OUTPUTDIR)/azure-cni-plugin: $(CNIFILES)
go build -v -o $(OUTPUTDIR)/azure-cni-plugin -ldflags "-X main.version=$(VERSION) -s -w" $(CNIDIR)/*.go

install:
Expand Down
60 changes: 55 additions & 5 deletions cni/cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,70 @@ package cni

import (
"encoding/json"

cniSkel "github.com/containernetworking/cni/pkg/skel"
cniTypes "github.com/containernetworking/cni/pkg/types"
)

const (
Internal = "internal"

CmdAdd = "ADD"
CmdDel = "DEL"
)

// CNI contract.
type CniPlugin interface {
Add(args *cniSkel.CmdArgs) error
Delete(args *cniSkel.CmdArgs) error
}

// CallPlugin calls the given CNI plugin through the internal interface.
func CallPlugin(plugin CniPlugin, cmd string, args *cniSkel.CmdArgs, nwCfg *NetworkConfig) (*cniTypes.Result, error) {
var err error

savedType := nwCfg.Ipam.Type
nwCfg.Ipam.Type = Internal
args.StdinData = nwCfg.Serialize()

// Call the plugin's internal interface.
if cmd == CmdAdd {
err = plugin.Add(args)
} else {
err = plugin.Delete(args)
}

nwCfg.Ipam.Type = savedType

if err != nil {
return nil, err
}

// Read back the result.
var result cniTypes.Result
err = json.Unmarshal(args.StdinData, &result)
if err != nil {
return nil, err
}

return &result, nil
}

// NetworkConfig represents the Azure CNI plugin's network configuration.
type NetworkConfig struct {
CniVersion string `json:"cniVersion"`
Name string `json:"name"`
Type string `json:"type"`
Bridge string `json:"bridge,omitempty"`
IfName string `json:"ifName,omitempty"`
LogLevel string `json:"logLevel,omitempty"`
LogTarget string `json:"logTarget,omitempty"`
Ipam struct {
Type string `json:"type"`
AddrSpace string `json:"addressSpace,omitempty"`
Subnet string `json:"subnet,omitempty"`
Address string `json:"ipAddress,omitempty"`
Type string `json:"type"`
Environment string `json:"environment,omitempty"`
AddrSpace string `json:"addressSpace,omitempty"`
Subnet string `json:"subnet,omitempty"`
Address string `json:"ipAddress,omitempty"`
QueryInterval string `json:"queryInterval,omitempty"`
}
}

Expand Down
127 changes: 94 additions & 33 deletions cni/ipam/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package ipam

import (
"encoding/json"
"net"

"github.com/Azure/azure-container-networking/cni"
Expand All @@ -23,6 +24,15 @@ const (
defaultAddressSpaceId = "LocalDefaultAddressSpace"
)

var (
// Azure VNET pre-allocated host IDs.
ipv4DefaultGatewayHostId = net.ParseIP("::1")
ipv4DnsPrimaryHostId = net.ParseIP("::2")
ipv4DnsSecondaryHostId = net.ParseIP("::3")

ipv4DefaultRouteDstPrefix = net.IPNet{net.IPv4zero, net.IPv4Mask(0, 0, 0, 0)}
)

// IpamPlugin represents a CNI IPAM plugin.
type ipamPlugin struct {
*common.Plugin
Expand All @@ -43,31 +53,34 @@ func NewPlugin(config *common.PluginConfig) (*ipamPlugin, error) {
return nil, err
}

config.IpamApi = am

return &ipamPlugin{
// Create IPAM plugin.
ipamPlg := &ipamPlugin{
Plugin: plugin,
am: am,
}, nil
}

config.IpamApi = ipamPlg

return ipamPlg, nil
}

// Starts the plugin.
func (plugin *ipamPlugin) Start(config *common.PluginConfig) error {
// Initialize base plugin.
err := plugin.Initialize(config)
if err != nil {
log.Printf("[ipam] Failed to initialize base plugin, err:%v.", err)
log.Printf("[cni-ipam] Failed to initialize base plugin, err:%v.", err)
return err
}

// Initialize address manager.
err = plugin.am.Initialize(config, plugin.Options)
if err != nil {
log.Printf("[ipam] Failed to initialize address manager, err:%v.", err)
log.Printf("[cni-ipam] Failed to initialize address manager, err:%v.", err)
return err
}

log.Printf("[ipam] Plugin started.")
log.Printf("[cni-ipam] Plugin started.")

return nil
}
Expand All @@ -76,7 +89,7 @@ func (plugin *ipamPlugin) Start(config *common.PluginConfig) error {
func (plugin *ipamPlugin) Stop() {
plugin.am.Uninitialize()
plugin.Uninitialize()
log.Printf("[ipam] Plugin stopped.")
log.Printf("[cni-ipam] Plugin stopped.")
}

//
Expand All @@ -86,77 +99,125 @@ func (plugin *ipamPlugin) Stop() {

// Add handles CNI add commands.
func (plugin *ipamPlugin) Add(args *cniSkel.CmdArgs) error {
log.Printf("[ipam] Processing ADD command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.",
log.Printf("[cni-ipam] Processing ADD command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.",
args.ContainerID, args.Netns, args.IfName, args.Args, args.Path)

// Parse network configuration from stdin.
nwCfg, err := cni.ParseNetworkConfig(args.StdinData)
if err != nil {
log.Printf("[ipam] Failed to parse network configuration: %v.", err)
log.Printf("[cni-ipam] Failed to parse network configuration: %v.", err)
return nil
}

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

// Assume default address space if not specified.
if nwCfg.Ipam.AddrSpace == "" {
nwCfg.Ipam.AddrSpace = defaultAddressSpaceId
}

var poolId string
var subnet string
var ipv4Address *net.IPNet
var result *cniTypes.Result
var apInfo *ipam.AddressPoolInfo

// Check if an address pool is specified.
if nwCfg.Ipam.Subnet == "" {
// Allocate an address pool.
poolId, subnet, err := plugin.am.RequestPool(nwCfg.Ipam.AddrSpace, "", "", nil, false)
poolId, subnet, err = plugin.am.RequestPool(nwCfg.Ipam.AddrSpace, "", "", nil, false)
if err != nil {
log.Printf("[ipam] Failed to allocate pool, err:%v.", err)
log.Printf("[cni-ipam] Failed to allocate pool, err:%v.", err)
return nil
}

nwCfg.Ipam.Subnet = subnet
log.Printf("[ipam] Allocated address poolId %v with subnet %v.", poolId, subnet)
log.Printf("[cni-ipam] Allocated address poolId %v with subnet %v.", poolId, subnet)
}

// Allocate an address for the endpoint.
address, err := plugin.am.RequestAddress(nwCfg.Ipam.AddrSpace, nwCfg.Ipam.Subnet, nwCfg.Ipam.Address, nil)
if err != nil {
log.Printf("[ipam] Failed to allocate address, err:%v.", err)
return nil
log.Printf("[cni-ipam] Failed to allocate address, err:%v.", err)
goto Rollback
}

log.Printf("[ipam] Allocated address %v.", address)
log.Printf("[cni-ipam] Allocated address %v.", address)

// Output the result.
ip, cidr, err := net.ParseCIDR(address)
cidr.IP = ip
// Parse IP address.
ipv4Address, err = ipam.ConvertAddressToIPNet(address)
if err != nil {
log.Printf("[ipam] Failed to parse address, err:%v.", err)
return nil
goto Rollback
}

// Query pool information for gateways and DNS servers.
apInfo, err = plugin.am.GetPoolInfo(nwCfg.Ipam.AddrSpace, nwCfg.Ipam.Subnet)
if err != nil {
goto Rollback
}

result := &cniTypes.Result{
IP4: &cniTypes.IPConfig{IP: *cidr},
// Populate IP configuration.
result = &cniTypes.Result{
IP4: &cniTypes.IPConfig{
IP: *ipv4Address,
Gateway: apInfo.Gateway,
Routes: []cniTypes.Route{
cniTypes.Route{
Dst: ipv4DefaultRouteDstPrefix,
GW: apInfo.Gateway,
},
},
},
}

result.Print()
// Populate DNS servers.
for _, ip := range apInfo.DnsServers {
result.DNS.Nameservers = append(result.DNS.Nameservers, ip.String())
}

// Output the result.
if nwCfg.Ipam.Type == cni.Internal {
// Called via the internal interface. Pass output back in args.
args.StdinData, _ = json.Marshal(result)
} else {
// Called via the executable interface. Print output to stdout.
result.Print()
}

log.Printf("[cni-ipam] ADD succeeded with output %+v.", result)

return nil

Rollback:
// Roll back allocations made during this call.
log.Printf("[cni-ipam] ADD failed, err:%v.", err)

log.Printf("[ipam] ADD succeeded with output %+v.", result)
if address != "" {
log.Printf("[cni-ipam] Releasing address %v.", address)
err = plugin.am.ReleaseAddress(nwCfg.Ipam.AddrSpace, nwCfg.Ipam.Subnet, address)
}

if poolId != "" {
log.Printf("[cni-ipam] Releasing pool %v.", poolId)
err = plugin.am.ReleasePool(nwCfg.Ipam.AddrSpace, poolId)
}

return err
}

// Delete handles CNI delete commands.
func (plugin *ipamPlugin) Delete(args *cniSkel.CmdArgs) error {
log.Printf("[ipam] Processing DEL command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.",
log.Printf("[cni-ipam] Processing DEL command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.",
args.ContainerID, args.Netns, args.IfName, args.Args, args.Path)

// Parse network configuration from stdin.
nwCfg, err := cni.ParseNetworkConfig(args.StdinData)
if err != nil {
log.Printf("[ipam] Failed to parse network configuration: %v.", err)
log.Printf("[cni-ipam] Failed to parse network configuration: %v.", err)
return nil
}

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

// Assume default address space if not specified.
if nwCfg.Ipam.AddrSpace == "" {
Expand All @@ -168,19 +229,19 @@ func (plugin *ipamPlugin) Delete(args *cniSkel.CmdArgs) error {
// Release the address.
err := plugin.am.ReleaseAddress(nwCfg.Ipam.AddrSpace, nwCfg.Ipam.Subnet, nwCfg.Ipam.Address)
if err != nil {
log.Printf("[cni] Failed to release address, err:%v.", err)
log.Printf("[cni-ipam] Failed to release address, err:%v.", err)
return nil
}
} else {
// Release the pool.
err := plugin.am.ReleasePool(nwCfg.Ipam.AddrSpace, nwCfg.Ipam.Subnet)
if err != nil {
log.Printf("[cni] Failed to release pool, err:%v.", err)
log.Printf("[cni-ipam] Failed to release pool, err:%v.", err)
return nil
}
}

log.Printf("[ipam] DEL succeeded.")
log.Printf("[cni-ipam] DEL succeeded.")

return err
return nil
}
Loading