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