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