Skip to content
40 changes: 40 additions & 0 deletions cns/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@

package cns

import "encoding/json"

// Container Network Service remote API Contract
const (
SetEnvironmentPath = "/network/environment"
CreateNetworkPath = "/network/create"
DeleteNetworkPath = "/network/delete"
CreateHnsNetworkPath = "/network/hns/create"
DeleteHnsNetworkPath = "/network/hns/delete"
ReserveIPAddressPath = "/network/ip/reserve"
ReleaseIPAddressPath = "/network/ip/release"
GetHostLocalIPPath = "/network/ip/hostlocal"
Expand Down Expand Up @@ -44,6 +48,42 @@ type DeleteNetworkRequest struct {
NetworkName string
}

// CreateHnsNetworkRequest describes request to create the HNS network.
type CreateHnsNetworkRequest struct {
NetworkName string
NetworkType string
NetworkAdapterName string `json:",omitempty"`
SourceMac string `json:",omitempty"`
Policies []json.RawMessage `json:",omitempty"`
MacPools []MacPool `json:",omitempty"`
Subnets []SubnetInfo
DNSSuffix string `json:",omitempty"`
DNSServerList string `json:",omitempty"`
DNSServerCompartment uint32 `json:",omitempty"`
ManagementIP string `json:",omitempty"`
AutomaticDNS bool `json:",omitempty"`
}

// SubnetInfo is assoicated with HNS network and represents a list
// of subnets available to the network
type SubnetInfo struct {
AddressPrefix string `json:",omitempty"`
GatewayAddress string `json:",omitempty"`
Policies []json.RawMessage `json:",omitempty"`
}

// MacPool is assoicated with HNS network and represents a list
// of macaddresses available to the network
type MacPool struct {
StartMacAddress string `json:",omitempty"`
EndMacAddress string `json:",omitempty"`
}

// DeleteHnsNetworkRequest describes request to delete the HNS network.
type DeleteHnsNetworkRequest struct {
NetworkName string
}

// ReserveIPAddressRequest describes request to reserve an IP Address
type ReserveIPAddressRequest struct {
ReservationID string
Expand Down
117 changes: 115 additions & 2 deletions cns/restserver/restserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ func (service *HTTPRestService) Start(config *common.ServiceConfig) error {
listener.AddHandler(cns.GetNetworkContainerByOrchestratorContext, service.getNetworkContainerByOrchestratorContext)
listener.AddHandler(cns.AttachContainerToNetwork, service.attachNetworkContainerToNetwork)
listener.AddHandler(cns.DetachContainerFromNetwork, service.detachNetworkContainerFromNetwork)
listener.AddHandler(cns.CreateHnsNetworkPath, service.createHnsNetwork)
listener.AddHandler(cns.DeleteHnsNetworkPath, service.deleteHnsNetwork)

// handlers for v0.2
listener.AddHandler(cns.V2Prefix+cns.SetEnvironmentPath, service.setEnvironment)
Expand All @@ -172,6 +174,8 @@ func (service *HTTPRestService) Start(config *common.ServiceConfig) error {
listener.AddHandler(cns.V2Prefix+cns.GetNetworkContainerByOrchestratorContext, service.getNetworkContainerByOrchestratorContext)
listener.AddHandler(cns.V2Prefix+cns.AttachContainerToNetwork, service.attachNetworkContainerToNetwork)
listener.AddHandler(cns.V2Prefix+cns.DetachContainerFromNetwork, service.detachNetworkContainerFromNetwork)
listener.AddHandler(cns.V2Prefix+cns.CreateHnsNetworkPath, service.createHnsNetwork)
listener.AddHandler(cns.V2Prefix+cns.DeleteHnsNetworkPath, service.deleteHnsNetwork)

log.Printf("[Azure CNS] Listening.")
return nil
Expand Down Expand Up @@ -248,7 +252,7 @@ func (service *HTTPRestService) createNetwork(w http.ResponseWriter, r *http.Req
case "Underlay":
switch service.state.Location {
case "Azure":
log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName)
log.Printf("[Azure CNS] Creating network with name %v.", req.NetworkName)

err = rt.GetRoutingTable()
if err != nil {
Expand Down Expand Up @@ -344,7 +348,7 @@ func (service *HTTPRestService) deleteNetwork(w http.ResponseWriter, r *http.Req

// Network does exist
if err == nil {
log.Printf("[Azure CNS] Goign to delete network with name %v.", req.NetworkName)
log.Printf("[Azure CNS] Deleting network with name %v.", req.NetworkName)
err := dc.DeleteNetwork(req.NetworkName)
if err != nil {
returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error())
Expand Down Expand Up @@ -379,6 +383,115 @@ func (service *HTTPRestService) deleteNetwork(w http.ResponseWriter, r *http.Req
log.Response(service.Name, resp, resp.ReturnCode, ReturnCodeToString(resp.ReturnCode), err)
}

// Handles CreateHnsNetwork requests.
func (service *HTTPRestService) createHnsNetwork(w http.ResponseWriter, r *http.Request) {
log.Printf("[Azure CNS] createHnsNetwork")

var err error
returnCode := 0
returnMessage := ""

var req cns.CreateHnsNetworkRequest
err = service.Listener.Decode(w, r, &req)
log.Request(service.Name, &req, err)

if err != nil {
returnMessage = fmt.Sprintf("[Azure CNS] Error. Unable to decode input request.")
returnCode = InvalidParameter
} else {
switch r.Method {
case "POST":
if err := platform.CreateHnsNetwork(req); err == nil {
// Save the newly created HnsNetwork name. CNS deleteHnsNetwork API
// will only allow deleting these networks.
networkInfo := &networkInfo{
NetworkName: req.NetworkName,
}
service.lock.Lock()
service.state.Networks[req.NetworkName] = networkInfo
service.lock.Unlock()
returnMessage = fmt.Sprintf("[Azure CNS] Successfully created HNS network: %s", req.NetworkName)
} else {
returnMessage = fmt.Sprintf("[Azure CNS] CreateHnsNetwork failed with error %v", err.Error())
returnCode = UnexpectedError
}
default:
returnMessage = "[Azure CNS] Error. CreateHnsNetwork did not receive a POST."
returnCode = InvalidParameter
}
}

resp := &cns.Response{
ReturnCode: returnCode,
Message: returnMessage,
}

err = service.Listener.Encode(w, &resp)

if returnCode == 0 {
service.lock.Lock()
service.saveState()
service.lock.Unlock()
}

log.Response(service.Name, resp, resp.ReturnCode, ReturnCodeToString(resp.ReturnCode), err)
}

// Handles deleteHnsNetwork requests.
func (service *HTTPRestService) deleteHnsNetwork(w http.ResponseWriter, r *http.Request) {
log.Printf("[Azure CNS] deleteHnsNetwork")

var err error
var req cns.DeleteHnsNetworkRequest
returnCode := 0
returnMessage := ""

err = service.Listener.Decode(w, r, &req)
log.Request(service.Name, &req, err)

if err != nil {
returnMessage = fmt.Sprintf("[Azure CNS] Error. Unable to decode input request.")
returnCode = InvalidParameter
} else {
switch r.Method {
case "POST":
service.lock.Lock()
networkInfo, ok := service.state.Networks[req.NetworkName]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will encapsulate this in a function, so that people don't forget to acquire lock.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it as is. Don't understand the comment. I don't think moving the networkName validity check warrants a new func if that's what you are asking for.

if ok && networkInfo.NetworkName == req.NetworkName {
if err = platform.DeleteHnsNetwork(req.NetworkName); err == nil {
returnMessage = fmt.Sprintf("[Azure CNS] Successfully deleted HNS network: %s", req.NetworkName)
} else {
returnMessage = fmt.Sprintf("[Azure CNS] DeleteHnsNetwork failed with error %v", err.Error())
returnCode = UnexpectedError
}
} else {
returnMessage = fmt.Sprintf("[Azure CNS] Network %s not found", req.NetworkName)
returnCode = InvalidParameter
}
service.lock.Unlock()
default:
returnMessage = "[Azure CNS] Error. DeleteHnsNetwork did not receive a POST."
returnCode = InvalidParameter
}
}

resp := &cns.Response{
ReturnCode: returnCode,
Message: returnMessage,
}

err = service.Listener.Encode(w, &resp)

if returnCode == 0 {
service.lock.Lock()
delete(service.state.Networks, req.NetworkName)
service.saveState()
service.lock.Unlock()
}

log.Response(service.Name, resp, resp.ReturnCode, ReturnCodeToString(resp.ReturnCode), err)
}

// Handles ip reservation requests.
func (service *HTTPRestService) reserveIPAddress(w http.ResponseWriter, r *http.Request) {
log.Printf("[Azure CNS] reserveIPAddress")
Expand Down
28 changes: 28 additions & 0 deletions cns/service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"

"github.com/Azure/azure-container-networking/cnm/ipam"
Expand Down Expand Up @@ -136,6 +137,13 @@ var args = acn.ArgumentList{
Type: "string",
DefaultValue: platform.K8SNetConfigPath + string(os.PathSeparator) + defaultCNINetworkConfigFileName,
},
{
Name: acn.OptCreateDefaultExtNetworkType,
Shorthand: acn.OptCreateDefaultExtNetworkTypeAlias,
Description: "Create default external network for windows platform with the specified type (l2bridge or l2tunnel)",
Type: "string",
DefaultValue: "",
},
{
Name: acn.OptTelemetry,
Shorthand: acn.OptTelemetryAlias,
Expand Down Expand Up @@ -169,6 +177,7 @@ func main() {
ipamQueryInterval, _ := acn.GetArg(acn.OptIpamQueryInterval).(int)
stopcnm = acn.GetArg(acn.OptStopAzureVnet).(bool)
vers := acn.GetArg(acn.OptVersion).(bool)
createDefaultExtNetworkType := acn.GetArg(acn.OptCreateDefaultExtNetworkType).(string)
telemetryEnabled := acn.GetArg(acn.OptTelemetry).(bool)

if vers {
Expand Down Expand Up @@ -230,6 +239,17 @@ func main() {
httpRestService.SetOption(acn.OptCnsURL, cnsURL)
httpRestService.SetOption(acn.OptNetPluginPath, cniPath)
httpRestService.SetOption(acn.OptNetPluginConfigFile, cniConfigFile)
httpRestService.SetOption(acn.OptCreateDefaultExtNetworkType, createDefaultExtNetworkType)

// Create default ext network if commandline option is set
if len(strings.TrimSpace(createDefaultExtNetworkType)) > 0 {
if err := platform.CreateDefaultExtNetwork(createDefaultExtNetworkType); err == nil {
log.Printf("[Azure CNS] Successfully created default ext network")
} else {
log.Printf("[Azure CNS] Failed to create default ext network due to error: %v", err)
return
}
}

// Start CNS.
if httpRestService != nil {
Expand Down Expand Up @@ -308,6 +328,14 @@ func main() {
log.Printf("CNS Received unhandled error %v, shutting down.", err)
}

if len(strings.TrimSpace(createDefaultExtNetworkType)) > 0 {
if err := platform.DeleteDefaultExtNetwork(); err == nil {
log.Printf("[Azure CNS] Successfully deleted default ext network")
} else {
log.Printf("[Azure CNS] Failed to delete default ext network due to error: %v", err)
}
}

// Cleanup.
if httpRestService != nil {
httpRestService.Stop()
Expand Down
4 changes: 4 additions & 0 deletions common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const (
OptTelemetryConfigDir = "telemetry-config-file"
OptTelemetryConfigDirAlias = "d"

// Create ext Hns network
OptCreateDefaultExtNetworkType = "create-defaultextnetwork-type"
OptCreateDefaultExtNetworkTypeAlias = "defaultextnetworktype"

// Disable Telemetry
OptTelemetry = "telemetry"
OptTelemetryAlias = "dt"
Expand Down
26 changes: 26 additions & 0 deletions platform/os_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os/exec"
"time"

"github.com/Azure/azure-container-networking/cns"
"github.com/Azure/azure-container-networking/log"
)

Expand Down Expand Up @@ -111,3 +112,28 @@ func KillProcessByName(processName string) error {
func SetSdnRemoteArpMacAddress() error {
return nil
}

// CreateDefaultExtNetwork creates the default ext network (if it doesn't exist already)
// to create external switch on windows platform.
// This is windows platform specific.
func CreateDefaultExtNetwork(networkType string) error {
return fmt.Errorf("CreateDefaultExtNetwork shouldn't be called for the platform: %s", GetOSInfo)
}

// DeleteDefaultExtNetwork deletes the default HNS network.
// This is windows platform specific.
func DeleteDefaultExtNetwork() error {
return fmt.Errorf("DeleteDefaultExtNetwork shouldn't be called for the platform: %s", GetOSInfo)
}

// CreateHnsNetwork creates the HNS network with the provided configuration
// This is windows platform specific.
func CreateHnsNetwork(nwConfig cns.CreateHnsNetworkRequest) error {
return fmt.Errorf("CreateHnsNetwork shouldn't be called for the platform: %s", GetOSInfo)
}

// DeleteHnsNetwork deletes the HNS network with the provided name.
// This is windows platform specific.
func DeleteHnsNetwork(networkName string) error {
return fmt.Errorf("DeleteHnsNetwork shouldn't be called for the platform: %s", GetOSInfo)
}
Loading