1package hns
2
3import (
4	"encoding/json"
5	"net"
6	"strings"
7
8	"github.com/sirupsen/logrus"
9)
10
11// HNSEndpoint represents a network endpoint in HNS
12type HNSEndpoint struct {
13	Id                 string            `json:"ID,omitempty"`
14	Name               string            `json:",omitempty"`
15	VirtualNetwork     string            `json:",omitempty"`
16	VirtualNetworkName string            `json:",omitempty"`
17	Policies           []json.RawMessage `json:",omitempty"`
18	MacAddress         string            `json:",omitempty"`
19	IPAddress          net.IP            `json:",omitempty"`
20	IPv6Address        net.IP            `json:",omitempty"`
21	DNSSuffix          string            `json:",omitempty"`
22	DNSServerList      string            `json:",omitempty"`
23	GatewayAddress     string            `json:",omitempty"`
24	GatewayAddressV6   string            `json:",omitempty"`
25	EnableInternalDNS  bool              `json:",omitempty"`
26	DisableICC         bool              `json:",omitempty"`
27	PrefixLength       uint8             `json:",omitempty"`
28	IPv6PrefixLength   uint8             `json:",omitempty"`
29	IsRemoteEndpoint   bool              `json:",omitempty"`
30	EnableLowMetric    bool              `json:",omitempty"`
31	Namespace          *Namespace        `json:",omitempty"`
32	EncapOverhead      uint16            `json:",omitempty"`
33	SharedContainers   []string          `json:",omitempty"`
34}
35
36//SystemType represents the type of the system on which actions are done
37type SystemType string
38
39// SystemType const
40const (
41	ContainerType      SystemType = "Container"
42	VirtualMachineType SystemType = "VirtualMachine"
43	HostType           SystemType = "Host"
44)
45
46// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system
47// Supported resource types are Network and Request Types are Add/Remove
48type EndpointAttachDetachRequest struct {
49	ContainerID    string     `json:"ContainerId,omitempty"`
50	SystemType     SystemType `json:"SystemType"`
51	CompartmentID  uint16     `json:"CompartmentId,omitempty"`
52	VirtualNICName string     `json:"VirtualNicName,omitempty"`
53}
54
55// EndpointResquestResponse is object to get the endpoint request response
56type EndpointResquestResponse struct {
57	Success bool
58	Error   string
59}
60
61// EndpointStats is the object that has stats for a given endpoint
62type EndpointStats struct {
63	BytesReceived          uint64 `json:"BytesReceived"`
64	BytesSent              uint64 `json:"BytesSent"`
65	DroppedPacketsIncoming uint64 `json:"DroppedPacketsIncoming"`
66	DroppedPacketsOutgoing uint64 `json:"DroppedPacketsOutgoing"`
67	EndpointID             string `json:"EndpointId"`
68	InstanceID             string `json:"InstanceId"`
69	PacketsReceived        uint64 `json:"PacketsReceived"`
70	PacketsSent            uint64 `json:"PacketsSent"`
71}
72
73// HNSEndpointRequest makes a HNS call to modify/query a network endpoint
74func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) {
75	endpoint := &HNSEndpoint{}
76	err := hnsCall(method, "/endpoints/"+path, request, &endpoint)
77	if err != nil {
78		return nil, err
79	}
80
81	return endpoint, nil
82}
83
84// HNSListEndpointRequest makes a HNS call to query the list of available endpoints
85func HNSListEndpointRequest() ([]HNSEndpoint, error) {
86	var endpoint []HNSEndpoint
87	err := hnsCall("GET", "/endpoints/", "", &endpoint)
88	if err != nil {
89		return nil, err
90	}
91
92	return endpoint, nil
93}
94
95// hnsEndpointStatsRequest makes a HNS call to query the stats for a given endpoint ID
96func hnsEndpointStatsRequest(id string) (*EndpointStats, error) {
97	var stats EndpointStats
98	err := hnsCall("GET", "/endpointstats/"+id, "", &stats)
99	if err != nil {
100		return nil, err
101	}
102
103	return &stats, nil
104}
105
106// GetHNSEndpointByID get the Endpoint by ID
107func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) {
108	return HNSEndpointRequest("GET", endpointID, "")
109}
110
111// GetHNSEndpointStats get the stats for a n Endpoint by ID
112func GetHNSEndpointStats(endpointID string) (*EndpointStats, error) {
113	return hnsEndpointStatsRequest(endpointID)
114}
115
116// GetHNSEndpointByName gets the endpoint filtered by Name
117func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
118	hnsResponse, err := HNSListEndpointRequest()
119	if err != nil {
120		return nil, err
121	}
122	for _, hnsEndpoint := range hnsResponse {
123		if hnsEndpoint.Name == endpointName {
124			return &hnsEndpoint, nil
125		}
126	}
127	return nil, EndpointNotFoundError{EndpointName: endpointName}
128}
129
130type endpointAttachInfo struct {
131	SharedContainers json.RawMessage `json:",omitempty"`
132}
133
134func (endpoint *HNSEndpoint) IsAttached(vID string) (bool, error) {
135	attachInfo := endpointAttachInfo{}
136	err := hnsCall("GET", "/endpoints/"+endpoint.Id, "", &attachInfo)
137
138	// Return false allows us to just return the err
139	if err != nil {
140		return false, err
141	}
142
143	if strings.Contains(strings.ToLower(string(attachInfo.SharedContainers)), strings.ToLower(vID)) {
144		return true, nil
145	}
146
147	return false, nil
148
149}
150
151// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods
152func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) {
153	operation := "Create"
154	title := "hcsshim::HNSEndpoint::" + operation
155	logrus.Debugf(title+" id=%s", endpoint.Id)
156
157	jsonString, err := json.Marshal(endpoint)
158	if err != nil {
159		return nil, err
160	}
161	return HNSEndpointRequest("POST", "", string(jsonString))
162}
163
164// Delete Endpoint by sending EndpointRequest to HNS
165func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) {
166	operation := "Delete"
167	title := "hcsshim::HNSEndpoint::" + operation
168	logrus.Debugf(title+" id=%s", endpoint.Id)
169
170	return HNSEndpointRequest("DELETE", endpoint.Id, "")
171}
172
173// Update Endpoint
174func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) {
175	operation := "Update"
176	title := "hcsshim::HNSEndpoint::" + operation
177	logrus.Debugf(title+" id=%s", endpoint.Id)
178	jsonString, err := json.Marshal(endpoint)
179	if err != nil {
180		return nil, err
181	}
182	err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint)
183
184	return endpoint, err
185}
186
187// ApplyACLPolicy applies a set of ACL Policies on the Endpoint
188func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error {
189	operation := "ApplyACLPolicy"
190	title := "hcsshim::HNSEndpoint::" + operation
191	logrus.Debugf(title+" id=%s", endpoint.Id)
192
193	for _, policy := range policies {
194		if policy == nil {
195			continue
196		}
197		jsonString, err := json.Marshal(policy)
198		if err != nil {
199			return err
200		}
201		endpoint.Policies = append(endpoint.Policies, jsonString)
202	}
203
204	_, err := endpoint.Update()
205	return err
206}
207
208// ApplyProxyPolicy applies a set of Proxy Policies on the Endpoint
209func (endpoint *HNSEndpoint) ApplyProxyPolicy(policies ...*ProxyPolicy) error {
210	operation := "ApplyProxyPolicy"
211	title := "hcsshim::HNSEndpoint::" + operation
212	logrus.Debugf(title+" id=%s", endpoint.Id)
213
214	for _, policy := range policies {
215		if policy == nil {
216			continue
217		}
218		jsonString, err := json.Marshal(policy)
219		if err != nil {
220			return err
221		}
222		endpoint.Policies = append(endpoint.Policies, jsonString)
223	}
224
225	_, err := endpoint.Update()
226	return err
227}
228
229// ContainerAttach attaches an endpoint to container
230func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error {
231	operation := "ContainerAttach"
232	title := "hcsshim::HNSEndpoint::" + operation
233	logrus.Debugf(title+" id=%s", endpoint.Id)
234
235	requestMessage := &EndpointAttachDetachRequest{
236		ContainerID:   containerID,
237		CompartmentID: compartmentID,
238		SystemType:    ContainerType,
239	}
240	response := &EndpointResquestResponse{}
241	jsonString, err := json.Marshal(requestMessage)
242	if err != nil {
243		return err
244	}
245	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
246}
247
248// ContainerDetach detaches an endpoint from container
249func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error {
250	operation := "ContainerDetach"
251	title := "hcsshim::HNSEndpoint::" + operation
252	logrus.Debugf(title+" id=%s", endpoint.Id)
253
254	requestMessage := &EndpointAttachDetachRequest{
255		ContainerID: containerID,
256		SystemType:  ContainerType,
257	}
258	response := &EndpointResquestResponse{}
259
260	jsonString, err := json.Marshal(requestMessage)
261	if err != nil {
262		return err
263	}
264	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
265}
266
267// HostAttach attaches a nic on the host
268func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error {
269	operation := "HostAttach"
270	title := "hcsshim::HNSEndpoint::" + operation
271	logrus.Debugf(title+" id=%s", endpoint.Id)
272	requestMessage := &EndpointAttachDetachRequest{
273		CompartmentID: compartmentID,
274		SystemType:    HostType,
275	}
276	response := &EndpointResquestResponse{}
277
278	jsonString, err := json.Marshal(requestMessage)
279	if err != nil {
280		return err
281	}
282	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
283
284}
285
286// HostDetach detaches a nic on the host
287func (endpoint *HNSEndpoint) HostDetach() error {
288	operation := "HostDetach"
289	title := "hcsshim::HNSEndpoint::" + operation
290	logrus.Debugf(title+" id=%s", endpoint.Id)
291	requestMessage := &EndpointAttachDetachRequest{
292		SystemType: HostType,
293	}
294	response := &EndpointResquestResponse{}
295
296	jsonString, err := json.Marshal(requestMessage)
297	if err != nil {
298		return err
299	}
300	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
301}
302
303// VirtualMachineNICAttach attaches a endpoint to a virtual machine
304func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error {
305	operation := "VirtualMachineNicAttach"
306	title := "hcsshim::HNSEndpoint::" + operation
307	logrus.Debugf(title+" id=%s", endpoint.Id)
308	requestMessage := &EndpointAttachDetachRequest{
309		VirtualNICName: virtualMachineNICName,
310		SystemType:     VirtualMachineType,
311	}
312	response := &EndpointResquestResponse{}
313
314	jsonString, err := json.Marshal(requestMessage)
315	if err != nil {
316		return err
317	}
318	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
319}
320
321// VirtualMachineNICDetach detaches a endpoint  from a virtual machine
322func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error {
323	operation := "VirtualMachineNicDetach"
324	title := "hcsshim::HNSEndpoint::" + operation
325	logrus.Debugf(title+" id=%s", endpoint.Id)
326
327	requestMessage := &EndpointAttachDetachRequest{
328		SystemType: VirtualMachineType,
329	}
330	response := &EndpointResquestResponse{}
331
332	jsonString, err := json.Marshal(requestMessage)
333	if err != nil {
334		return err
335	}
336	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
337}
338