Skip to content

Commit 84fb35b

Browse files
Add Host NC communication support in Windows with HnsV2 (#424)
This PR adds support for host NC bidirectional communication with windows HnsV2. This is supported in multitenant scenario only. AllowHostToNCCommunication and AllowNCToHostCommunication flags are used to enable Host to NC and NC to host communication respectively.
1 parent edd2ae7 commit 84fb35b

18 files changed

+1075
-79
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ CNSFILES = \
4141
$(wildcard cns/dockerclient/*.go) \
4242
$(wildcard cns/imdsclient/*.go) \
4343
$(wildcard cns/ipamclient/*.go) \
44+
$(wildcard cns/hnsclient/*.go) \
4445
$(wildcard cns/restserver/*.go) \
4546
$(wildcard cns/routes/*.go) \
4647
$(wildcard cns/service/*.go) \

cni/network/multitenancy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ func getContainerNetworkConfigurationInternal(
6464
namespace string,
6565
podName string,
6666
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
67-
cnsClient, err := cnsclient.NewCnsClient(address)
67+
cnsClient, err := cnsclient.GetCnsClient()
6868
if err != nil {
69-
log.Printf("Initializing CNS client error %v", err)
69+
log.Printf("Failed to get CNS client. Error: %v", err)
7070
return nil, nil, net.IPNet{}, err
7171
}
7272

cni/network/network.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
242242
return err
243243
}
244244

245+
if nwCfg.MultiTenancy {
246+
// Initialize CNSClient
247+
cnsclient.InitCnsClient(nwCfg.CNSUrl)
248+
}
249+
245250
k8sContainerID := args.ContainerID
246251
if len(k8sContainerID) == 0 {
247252
errMsg := "Container ID not specified in CNI Args"
@@ -552,6 +557,11 @@ func (plugin *netPlugin) Get(args *cniSkel.CmdArgs) error {
552557
return err
553558
}
554559

560+
if nwCfg.MultiTenancy {
561+
// Initialize CNSClient
562+
cnsclient.InitCnsClient(nwCfg.CNSUrl)
563+
}
564+
555565
// Initialize values from network config.
556566
if networkId, err = getNetworkName(k8sPodName, k8sNamespace, args.IfName, nwCfg); err != nil {
557567
log.Printf("[cni-net] Failed to extract network name from network config. error: %v", err)
@@ -627,6 +637,11 @@ func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error {
627637
log.Printf("[cni-net] Failed to get POD info due to error: %v", err)
628638
}
629639

640+
if nwCfg.MultiTenancy {
641+
// Initialize CNSClient
642+
cnsclient.InitCnsClient(nwCfg.CNSUrl)
643+
}
644+
630645
// Initialize values from network config.
631646
if networkId, err = getNetworkName(k8sPodName, k8sNamespace, args.IfName, nwCfg); err != nil {
632647
log.Printf("[cni-net] Failed to extract network name from network config. error: %v", err)
@@ -772,7 +787,7 @@ func (plugin *netPlugin) Update(args *cniSkel.CmdArgs) error {
772787

773788
// now query CNS to get the target routes that should be there in the networknamespace (as a result of update)
774789
log.Printf("Going to collect target routes for [name=%v, namespace=%v] from CNS.", k8sPodName, k8sNamespace)
775-
if cnsClient, err = cnsclient.NewCnsClient(nwCfg.CNSUrl); err != nil {
790+
if cnsClient, err = cnsclient.InitCnsClient(nwCfg.CNSUrl); err != nil {
776791
log.Printf("Initializing CNS client error in CNI Update%v", err)
777792
log.Printf(err.Error())
778793
return plugin.Errorf(err.Error())

cni/network/network_linux.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func setEndpointOptions(cnsNwConfig *cns.GetNetworkContainerResponse, epInfo *ne
5858
epInfo.Data[network.SnatBridgeIPKey] = cnsNwConfig.LocalIPConfiguration.GatewayIPAddress + "/" + strconv.Itoa(int(cnsNwConfig.LocalIPConfiguration.IPSubnet.PrefixLength))
5959
epInfo.AllowInboundFromHostToNC = cnsNwConfig.AllowHostToNCCommunication
6060
epInfo.AllowInboundFromNCToHost = cnsNwConfig.AllowNCToHostCommunication
61+
epInfo.NetworkContainerID = cnsNwConfig.NetworkContainerID
6162
}
6263

6364
epInfo.Data[network.OptVethName] = vethName

cni/network/network_windows.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ func setEndpointOptions(cnsNwConfig *cns.GetNetworkContainerResponse, epInfo *ne
9393
cnetAddressMap = append(cnetAddressMap, ipSubnet.IPAddress+"/"+strconv.Itoa(int(ipSubnet.PrefixLength)))
9494
}
9595
epInfo.Data[network.CnetAddressSpace] = cnetAddressMap
96+
epInfo.AllowInboundFromHostToNC = cnsNwConfig.AllowHostToNCCommunication
97+
epInfo.AllowInboundFromNCToHost = cnsNwConfig.AllowNCToHostCommunication
98+
epInfo.NetworkContainerID = cnsNwConfig.NetworkContainerID
9699
}
97100
}
98101

cns/NetworkContainerContract.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ type GetNetworkContainerRequest struct {
131131

132132
// GetNetworkContainerResponse describes the response to retrieve a specifc network container.
133133
type GetNetworkContainerResponse struct {
134+
NetworkContainerID string
134135
IPConfiguration IPConfiguration
135136
Routes []Route
136137
CnetAddressSpace []IPSubnet

cns/api.go

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,22 @@ import "encoding/json"
77

88
// Container Network Service remote API Contract
99
const (
10-
SetEnvironmentPath = "/network/environment"
11-
CreateNetworkPath = "/network/create"
12-
DeleteNetworkPath = "/network/delete"
13-
CreateHnsNetworkPath = "/network/hns/create"
14-
DeleteHnsNetworkPath = "/network/hns/delete"
15-
ReserveIPAddressPath = "/network/ip/reserve"
16-
ReleaseIPAddressPath = "/network/ip/release"
17-
GetHostLocalIPPath = "/network/ip/hostlocal"
18-
GetIPAddressUtilizationPath = "/network/ip/utilization"
19-
GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy"
20-
GetHealthReportPath = "/network/health"
21-
NumberOfCPUCoresPath = "/hostcpucores"
22-
V1Prefix = "/v0.1"
23-
V2Prefix = "/v0.2"
10+
SetEnvironmentPath = "/network/environment"
11+
CreateNetworkPath = "/network/create"
12+
DeleteNetworkPath = "/network/delete"
13+
CreateHnsNetworkPath = "/network/hns/create"
14+
DeleteHnsNetworkPath = "/network/hns/delete"
15+
ReserveIPAddressPath = "/network/ip/reserve"
16+
ReleaseIPAddressPath = "/network/ip/release"
17+
GetHostLocalIPPath = "/network/ip/hostlocal"
18+
GetIPAddressUtilizationPath = "/network/ip/utilization"
19+
GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy"
20+
GetHealthReportPath = "/network/health"
21+
NumberOfCPUCoresPath = "/hostcpucores"
22+
CreateHostNCApipaEndpointPath = "/network/createhostncapipaendpoint"
23+
DeleteHostNCApipaEndpointPath = "/network/deletehostncapipaendpoint"
24+
V1Prefix = "/v0.1"
25+
V2Prefix = "/v0.2"
2426
)
2527

2628
// SetEnvironmentRequest describes the Request to set the environment in CNS.
@@ -153,3 +155,27 @@ type OptionMap map[string]interface{}
153155
type errorResponse struct {
154156
Err string
155157
}
158+
159+
// CreateHostNCApipaEndpointRequest describes request for create apipa endpoint
160+
// for host container connectivity for the given network container
161+
type CreateHostNCApipaEndpointRequest struct {
162+
NetworkContainerID string
163+
}
164+
165+
// CreateHostNCApipaEndpointResponse describes response for create apipa endpoint request
166+
// for host container connectivity.
167+
type CreateHostNCApipaEndpointResponse struct {
168+
Response Response
169+
EndpointID string
170+
}
171+
172+
// DeleteHostNCApipaEndpointRequest describes request for deleting apipa endpoint created
173+
// for host NC connectivity.
174+
type DeleteHostNCApipaEndpointRequest struct {
175+
NetworkContainerID string
176+
}
177+
178+
// DeleteHostNCApipaEndpointResponse describes response for delete host NC apipa endpoint request.
179+
type DeleteHostNCApipaEndpointResponse struct {
180+
Response Response
181+
}

cns/cnsclient/cnsclient.go

Lines changed: 128 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,34 @@ const (
1919
defaultCnsURL = "http://localhost:10090"
2020
)
2121

22-
// NewCnsClient create a new cns client.
23-
func NewCnsClient(url string) (*CNSClient, error) {
24-
if url == "" {
25-
url = defaultCnsURL
22+
var (
23+
cnsClient *CNSClient
24+
)
25+
26+
// InitCnsClient initializes new cns client and returns the object
27+
func InitCnsClient(url string) (*CNSClient, error) {
28+
if cnsClient == nil {
29+
if url == "" {
30+
url = defaultCnsURL
31+
}
32+
33+
cnsClient = &CNSClient{
34+
connectionURL: url,
35+
}
2636
}
2737

28-
return &CNSClient{
29-
connectionURL: url,
30-
}, nil
38+
return cnsClient, nil
39+
}
40+
41+
// GetCnsClient returns the cns client object
42+
func GetCnsClient() (*CNSClient, error) {
43+
var err error
44+
45+
if cnsClient == nil {
46+
err = fmt.Errorf("[Azure CNSClient] CNS Client not initialized")
47+
}
48+
49+
return cnsClient, err
3150
}
3251

3352
// GetNetworkConfiguration Request to get network config.
@@ -77,3 +96,105 @@ func (cnsClient *CNSClient) GetNetworkConfiguration(orchestratorContext []byte)
7796

7897
return &resp, nil
7998
}
99+
100+
// CreateHostNCApipaEndpoint creates an endpoint in APIPA network for host container connectivity.
101+
func (cnsClient *CNSClient) CreateHostNCApipaEndpoint(
102+
networkContainerID string) (string, error) {
103+
var (
104+
err error
105+
body bytes.Buffer
106+
)
107+
108+
httpc := &http.Client{}
109+
url := cnsClient.connectionURL + cns.CreateHostNCApipaEndpointPath
110+
log.Printf("CreateHostNCApipaEndpoint url: %v for NC: %s", url, networkContainerID)
111+
112+
payload := &cns.CreateHostNCApipaEndpointRequest{
113+
NetworkContainerID: networkContainerID,
114+
}
115+
116+
if err = json.NewEncoder(&body).Encode(payload); err != nil {
117+
log.Errorf("encoding json failed with %v", err)
118+
return "", err
119+
}
120+
121+
res, err := httpc.Post(url, "application/json", &body)
122+
if err != nil {
123+
log.Errorf("[Azure CNSClient] HTTP Post returned error %v", err.Error())
124+
return "", err
125+
}
126+
127+
defer res.Body.Close()
128+
129+
if res.StatusCode != http.StatusOK {
130+
errMsg := fmt.Sprintf("[Azure CNSClient] CreateHostNCApipaEndpoint: Invalid http status code: %v",
131+
res.StatusCode)
132+
log.Errorf(errMsg)
133+
return "", fmt.Errorf(errMsg)
134+
}
135+
136+
var resp cns.CreateHostNCApipaEndpointResponse
137+
138+
if err = json.NewDecoder(res.Body).Decode(&resp); err != nil {
139+
log.Errorf("[Azure CNSClient] Error parsing CreateHostNCApipaEndpoint response resp: %v err: %v",
140+
res.Body, err.Error())
141+
return "", err
142+
}
143+
144+
if resp.Response.ReturnCode != 0 {
145+
log.Errorf("[Azure CNSClient] CreateHostNCApipaEndpoint received error response :%v", resp.Response.Message)
146+
return "", fmt.Errorf(resp.Response.Message)
147+
}
148+
149+
return resp.EndpointID, nil
150+
}
151+
152+
// DeleteHostNCApipaEndpoint deletes the endpoint in APIPA network created for host container connectivity.
153+
func (cnsClient *CNSClient) DeleteHostNCApipaEndpoint(networkContainerID string) error {
154+
var body bytes.Buffer
155+
156+
httpc := &http.Client{}
157+
url := cnsClient.connectionURL + cns.DeleteHostNCApipaEndpointPath
158+
log.Printf("DeleteHostNCApipaEndpoint url: %v for NC: %s", url, networkContainerID)
159+
160+
payload := &cns.DeleteHostNCApipaEndpointRequest{
161+
NetworkContainerID: networkContainerID,
162+
}
163+
164+
err := json.NewEncoder(&body).Encode(payload)
165+
if err != nil {
166+
log.Errorf("encoding json failed with %v", err)
167+
return err
168+
}
169+
170+
res, err := httpc.Post(url, "application/json", &body)
171+
if err != nil {
172+
log.Errorf("[Azure CNSClient] HTTP Post returned error %v", err.Error())
173+
return err
174+
}
175+
176+
defer res.Body.Close()
177+
178+
if res.StatusCode != http.StatusOK {
179+
errMsg := fmt.Sprintf("[Azure CNSClient] DeleteHostNCApipaEndpoint: Invalid http status code: %v",
180+
res.StatusCode)
181+
log.Errorf(errMsg)
182+
return fmt.Errorf(errMsg)
183+
}
184+
185+
var resp cns.DeleteHostNCApipaEndpointResponse
186+
187+
err = json.NewDecoder(res.Body).Decode(&resp)
188+
if err != nil {
189+
log.Errorf("[Azure CNSClient] Error parsing DeleteHostNCApipaEndpoint response resp: %v err: %v",
190+
res.Body, err.Error())
191+
return err
192+
}
193+
194+
if resp.Response.ReturnCode != 0 {
195+
log.Errorf("[Azure CNSClient] DeleteHostNCApipaEndpoint received error response :%v", resp.Response.Message)
196+
return fmt.Errorf(resp.Response.Message)
197+
}
198+
199+
return nil
200+
}

cns/hnsclient/hnsclient_linux.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,22 @@ func CreateHnsNetwork(nwConfig cns.CreateHnsNetworkRequest) error {
3030
func DeleteHnsNetwork(networkName string) error {
3131
return fmt.Errorf("DeleteHnsNetwork shouldn't be called for linux platform")
3232
}
33+
34+
// CreateHostNCApipaEndpoint creates the endpoint in the apipa network
35+
// for host container connectivity
36+
// This is windows platform specific.
37+
func CreateHostNCApipaEndpoint(
38+
networkContainerID string,
39+
localIPConfiguration cns.IPConfiguration,
40+
allowNCToHostCommunication bool,
41+
allowHostToNCCommunication bool) (string, error) {
42+
return "", nil
43+
}
44+
45+
// DeleteHostNCApipaEndpoint deletes the endpoint in the apipa network
46+
// created for host container connectivity
47+
// This is windows platform specific.
48+
func DeleteHostNCApipaEndpoint(
49+
networkContainerID string) error {
50+
return nil
51+
}

0 commit comments

Comments
 (0)