2017-02-12 13:03:39 +03:00
|
|
|
// Copyright 2017 Microsoft. All rights reserved.
|
|
|
|
// MIT License
|
2016-06-03 06:51:18 +03:00
|
|
|
|
|
|
|
package ipam
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/xml"
|
2019-11-26 04:07:50 +03:00
|
|
|
"fmt"
|
2016-06-03 06:51:18 +03:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2016-06-09 14:03:43 +03:00
|
|
|
"strings"
|
2016-06-03 06:51:18 +03:00
|
|
|
"time"
|
2016-12-06 22:19:35 +03:00
|
|
|
|
|
|
|
"github.com/Azure/azure-container-networking/common"
|
2017-08-17 23:49:36 +03:00
|
|
|
"github.com/Azure/azure-container-networking/log"
|
2016-06-03 06:51:18 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Host URL to query.
|
2019-02-27 03:03:29 +03:00
|
|
|
azureQueryUrl = "http://168.63.129.16/machine/plugins?comp=nmagent&type=getinterfaceinfov1"
|
2016-12-06 22:19:35 +03:00
|
|
|
// Minimum time interval between consecutive queries.
|
|
|
|
azureQueryInterval = 10 * time.Second
|
2019-11-26 04:07:50 +03:00
|
|
|
// http connection timeout
|
|
|
|
httpConnectionTimeout = 10
|
|
|
|
// http response header timeout
|
|
|
|
responseHeaderTimeout = 10
|
2016-06-03 06:51:18 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// Microsoft Azure IPAM configuration source.
|
|
|
|
type azureSource struct {
|
|
|
|
name string
|
2016-09-16 06:27:36 +03:00
|
|
|
sink addressConfigSink
|
2016-12-06 22:19:35 +03:00
|
|
|
queryUrl string
|
|
|
|
queryInterval time.Duration
|
2016-06-03 06:51:18 +03:00
|
|
|
lastRefresh time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates the Azure source.
|
2016-12-06 22:19:35 +03:00
|
|
|
func newAzureSource(options map[string]interface{}) (*azureSource, error) {
|
|
|
|
queryUrl, _ := options[common.OptIpamQueryUrl].(string)
|
|
|
|
if queryUrl == "" {
|
|
|
|
queryUrl = azureQueryUrl
|
|
|
|
}
|
|
|
|
|
|
|
|
i, _ := options[common.OptIpamQueryInterval].(int)
|
|
|
|
queryInterval := time.Duration(i) * time.Second
|
|
|
|
if queryInterval == 0 {
|
|
|
|
queryInterval = azureQueryInterval
|
|
|
|
}
|
|
|
|
|
2016-06-03 06:51:18 +03:00
|
|
|
return &azureSource{
|
|
|
|
name: "Azure",
|
2016-12-06 22:19:35 +03:00
|
|
|
queryUrl: queryUrl,
|
|
|
|
queryInterval: queryInterval,
|
2016-06-03 06:51:18 +03:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Starts the Azure source.
|
2016-09-16 06:27:36 +03:00
|
|
|
func (s *azureSource) start(sink addressConfigSink) error {
|
|
|
|
s.sink = sink
|
2016-06-03 06:51:18 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stops the Azure source.
|
|
|
|
func (s *azureSource) stop() {
|
2016-09-16 06:27:36 +03:00
|
|
|
s.sink = nil
|
2016-06-03 06:51:18 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refreshes configuration.
|
|
|
|
func (s *azureSource) refresh() error {
|
|
|
|
|
2016-12-06 22:19:35 +03:00
|
|
|
// Refresh only if enough time has passed since the last query.
|
|
|
|
if time.Since(s.lastRefresh) < s.queryInterval {
|
2016-06-03 06:51:18 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.lastRefresh = time.Now()
|
|
|
|
|
|
|
|
// Query the list of local interfaces.
|
|
|
|
interfaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Configure the local default address space.
|
2017-01-25 03:20:34 +03:00
|
|
|
local, err := s.sink.newAddressSpace(LocalDefaultAddressSpaceId, LocalScope)
|
2016-06-03 06:51:18 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-11-26 04:07:50 +03:00
|
|
|
httpClient := common.InitHttpClient(httpConnectionTimeout, responseHeaderTimeout)
|
|
|
|
if httpClient == nil {
|
|
|
|
log.Errorf("[ipam] Failed intializing http client")
|
|
|
|
return fmt.Errorf("Error intializing http client")
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[ipam] Wireserver call %v to retrieve IP List", s.queryUrl)
|
2016-06-03 06:51:18 +03:00
|
|
|
// Fetch configuration.
|
2019-11-26 04:07:50 +03:00
|
|
|
resp, err := httpClient.Get(s.queryUrl)
|
2016-06-03 06:51:18 +03:00
|
|
|
if err != nil {
|
2019-11-26 04:07:50 +03:00
|
|
|
log.Printf("[ipam] wireserver call failed with: %v", err)
|
2016-06-03 06:51:18 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
2019-11-26 04:07:50 +03:00
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
log.Errorf("[ipam] http return error code for wireserver call %+v", resp)
|
|
|
|
return fmt.Errorf("wireserver http error %+v", resp)
|
|
|
|
}
|
|
|
|
|
2016-06-03 06:51:18 +03:00
|
|
|
// Decode XML document.
|
2017-12-11 23:12:16 +03:00
|
|
|
var doc common.XmlDocument
|
2016-06-03 06:51:18 +03:00
|
|
|
decoder := xml.NewDecoder(resp.Body)
|
|
|
|
err = decoder.Decode(&doc)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// For each interface...
|
|
|
|
for _, i := range doc.Interface {
|
|
|
|
ifName := ""
|
2016-06-09 14:03:43 +03:00
|
|
|
priority := 0
|
2016-06-14 08:38:35 +03:00
|
|
|
i.MacAddress = strings.ToLower(i.MacAddress)
|
2016-06-09 14:03:43 +03:00
|
|
|
|
|
|
|
// Find the interface with the matching MacAddress.
|
2016-06-03 06:51:18 +03:00
|
|
|
for _, iface := range interfaces {
|
2016-06-09 14:03:43 +03:00
|
|
|
macAddr := strings.Replace(iface.HardwareAddr.String(), ":", "", -1)
|
2016-06-14 08:38:35 +03:00
|
|
|
macAddr = strings.ToLower(macAddr)
|
2016-12-06 22:19:35 +03:00
|
|
|
if macAddr == i.MacAddress || i.MacAddress == "*" {
|
2016-06-03 06:51:18 +03:00
|
|
|
ifName = iface.Name
|
2016-06-09 14:03:43 +03:00
|
|
|
|
|
|
|
// Prioritize secondary interfaces.
|
|
|
|
if !i.IsPrimary {
|
|
|
|
priority = 1
|
|
|
|
}
|
2016-06-03 06:51:18 +03:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip if interface is not found.
|
|
|
|
if ifName == "" {
|
2017-08-17 23:49:36 +03:00
|
|
|
log.Printf("[ipam] Failed to find interface with MAC address:%v.", i.MacAddress)
|
2016-06-03 06:51:18 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// For each subnet on the interface...
|
|
|
|
for _, s := range i.IPSubnet {
|
|
|
|
_, subnet, err := net.ParseCIDR(s.Prefix)
|
|
|
|
if err != nil {
|
2017-08-17 23:49:36 +03:00
|
|
|
log.Printf("[ipam] Failed to parse subnet:%v err:%v.", s.Prefix, err)
|
|
|
|
continue
|
2016-06-03 06:51:18 +03:00
|
|
|
}
|
|
|
|
|
2016-06-09 14:03:43 +03:00
|
|
|
ap, err := local.newAddressPool(ifName, priority, subnet)
|
2017-08-17 23:49:36 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("[ipam] Failed to create pool:%v ifName:%v err:%v.", subnet, ifName, err)
|
|
|
|
continue
|
2016-06-03 06:51:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// For each address in the subnet...
|
|
|
|
for _, a := range s.IPAddress {
|
2016-06-09 14:03:43 +03:00
|
|
|
// Primary addresses are reserved for the host.
|
|
|
|
if a.IsPrimary {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-06-03 06:51:18 +03:00
|
|
|
address := net.ParseIP(a.Address)
|
|
|
|
|
|
|
|
_, err = ap.newAddressRecord(&address)
|
|
|
|
if err != nil {
|
2017-08-17 23:49:36 +03:00
|
|
|
log.Printf("[ipam] Failed to create address:%v err:%v.", address, err)
|
|
|
|
continue
|
2016-06-03 06:51:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the local address space as active.
|
|
|
|
s.sink.setAddressSpace(local)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|