1package hcn 2 3import ( 4 "encoding/json" 5 "errors" 6 7 "github.com/Microsoft/go-winio/pkg/guid" 8 "github.com/Microsoft/hcsshim/internal/interop" 9 "github.com/sirupsen/logrus" 10) 11 12// IpConfig is assoicated with an endpoint 13type IpConfig struct { 14 IpAddress string `json:",omitempty"` 15 PrefixLength uint8 `json:",omitempty"` 16} 17 18// EndpointFlags are special settings on an endpoint. 19type EndpointFlags uint32 20 21var ( 22 // EndpointFlagsNone is the default. 23 EndpointFlagsNone EndpointFlags 24 // EndpointFlagsRemoteEndpoint means that an endpoint is on another host. 25 EndpointFlagsRemoteEndpoint EndpointFlags = 1 26) 27 28// HostComputeEndpoint represents a network endpoint 29type HostComputeEndpoint struct { 30 Id string `json:"ID,omitempty"` 31 Name string `json:",omitempty"` 32 HostComputeNetwork string `json:",omitempty"` // GUID 33 HostComputeNamespace string `json:",omitempty"` // GUID 34 Policies []EndpointPolicy `json:",omitempty"` 35 IpConfigurations []IpConfig `json:",omitempty"` 36 Dns Dns `json:",omitempty"` 37 Routes []Route `json:",omitempty"` 38 MacAddress string `json:",omitempty"` 39 Flags EndpointFlags `json:",omitempty"` 40 SchemaVersion SchemaVersion `json:",omitempty"` 41} 42 43// EndpointResourceType are the two different Endpoint settings resources. 44type EndpointResourceType string 45 46var ( 47 // EndpointResourceTypePolicy is for Endpoint Policies. Ex: ACL, NAT 48 EndpointResourceTypePolicy EndpointResourceType = "Policy" 49 // EndpointResourceTypePort is for Endpoint Port settings. 50 EndpointResourceTypePort EndpointResourceType = "Port" 51) 52 53// ModifyEndpointSettingRequest is the structure used to send request to modify an endpoint. 54// Used to update policy/port on an endpoint. 55type ModifyEndpointSettingRequest struct { 56 ResourceType EndpointResourceType `json:",omitempty"` // Policy, Port 57 RequestType RequestType `json:",omitempty"` // Add, Remove, Update, Refresh 58 Settings json.RawMessage `json:",omitempty"` 59} 60 61type PolicyEndpointRequest struct { 62 Policies []EndpointPolicy `json:",omitempty"` 63} 64 65func getEndpoint(endpointGuid guid.GUID, query string) (*HostComputeEndpoint, error) { 66 // Open endpoint. 67 var ( 68 endpointHandle hcnEndpoint 69 resultBuffer *uint16 70 propertiesBuffer *uint16 71 ) 72 hr := hcnOpenEndpoint(&endpointGuid, &endpointHandle, &resultBuffer) 73 if err := checkForErrors("hcnOpenEndpoint", hr, resultBuffer); err != nil { 74 return nil, err 75 } 76 // Query endpoint. 77 hr = hcnQueryEndpointProperties(endpointHandle, query, &propertiesBuffer, &resultBuffer) 78 if err := checkForErrors("hcnQueryEndpointProperties", hr, resultBuffer); err != nil { 79 return nil, err 80 } 81 properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer) 82 // Close endpoint. 83 hr = hcnCloseEndpoint(endpointHandle) 84 if err := checkForErrors("hcnCloseEndpoint", hr, nil); err != nil { 85 return nil, err 86 } 87 // Convert output to HostComputeEndpoint 88 var outputEndpoint HostComputeEndpoint 89 if err := json.Unmarshal([]byte(properties), &outputEndpoint); err != nil { 90 return nil, err 91 } 92 return &outputEndpoint, nil 93} 94 95func enumerateEndpoints(query string) ([]HostComputeEndpoint, error) { 96 // Enumerate all Endpoint Guids 97 var ( 98 resultBuffer *uint16 99 endpointBuffer *uint16 100 ) 101 hr := hcnEnumerateEndpoints(query, &endpointBuffer, &resultBuffer) 102 if err := checkForErrors("hcnEnumerateEndpoints", hr, resultBuffer); err != nil { 103 return nil, err 104 } 105 106 endpoints := interop.ConvertAndFreeCoTaskMemString(endpointBuffer) 107 var endpointIds []guid.GUID 108 err := json.Unmarshal([]byte(endpoints), &endpointIds) 109 if err != nil { 110 return nil, err 111 } 112 113 var outputEndpoints []HostComputeEndpoint 114 for _, endpointGuid := range endpointIds { 115 endpoint, err := getEndpoint(endpointGuid, query) 116 if err != nil { 117 return nil, err 118 } 119 outputEndpoints = append(outputEndpoints, *endpoint) 120 } 121 return outputEndpoints, nil 122} 123 124func createEndpoint(networkId string, endpointSettings string) (*HostComputeEndpoint, error) { 125 networkGuid, err := guid.FromString(networkId) 126 if err != nil { 127 return nil, errInvalidNetworkID 128 } 129 // Open network. 130 var networkHandle hcnNetwork 131 var resultBuffer *uint16 132 hr := hcnOpenNetwork(&networkGuid, &networkHandle, &resultBuffer) 133 if err := checkForErrors("hcnOpenNetwork", hr, resultBuffer); err != nil { 134 return nil, err 135 } 136 // Create endpoint. 137 endpointId := guid.GUID{} 138 var endpointHandle hcnEndpoint 139 hr = hcnCreateEndpoint(networkHandle, &endpointId, endpointSettings, &endpointHandle, &resultBuffer) 140 if err := checkForErrors("hcnCreateEndpoint", hr, resultBuffer); err != nil { 141 return nil, err 142 } 143 // Query endpoint. 144 hcnQuery := defaultQuery() 145 query, err := json.Marshal(hcnQuery) 146 if err != nil { 147 return nil, err 148 } 149 var propertiesBuffer *uint16 150 hr = hcnQueryEndpointProperties(endpointHandle, string(query), &propertiesBuffer, &resultBuffer) 151 if err := checkForErrors("hcnQueryEndpointProperties", hr, resultBuffer); err != nil { 152 return nil, err 153 } 154 properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer) 155 // Close endpoint. 156 hr = hcnCloseEndpoint(endpointHandle) 157 if err := checkForErrors("hcnCloseEndpoint", hr, nil); err != nil { 158 return nil, err 159 } 160 // Close network. 161 hr = hcnCloseNetwork(networkHandle) 162 if err := checkForErrors("hcnCloseNetwork", hr, nil); err != nil { 163 return nil, err 164 } 165 // Convert output to HostComputeEndpoint 166 var outputEndpoint HostComputeEndpoint 167 if err := json.Unmarshal([]byte(properties), &outputEndpoint); err != nil { 168 return nil, err 169 } 170 return &outputEndpoint, nil 171} 172 173func modifyEndpoint(endpointId string, settings string) (*HostComputeEndpoint, error) { 174 endpointGuid, err := guid.FromString(endpointId) 175 if err != nil { 176 return nil, errInvalidEndpointID 177 } 178 // Open endpoint 179 var ( 180 endpointHandle hcnEndpoint 181 resultBuffer *uint16 182 propertiesBuffer *uint16 183 ) 184 hr := hcnOpenEndpoint(&endpointGuid, &endpointHandle, &resultBuffer) 185 if err := checkForErrors("hcnOpenEndpoint", hr, resultBuffer); err != nil { 186 return nil, err 187 } 188 // Modify endpoint 189 hr = hcnModifyEndpoint(endpointHandle, settings, &resultBuffer) 190 if err := checkForErrors("hcnModifyEndpoint", hr, resultBuffer); err != nil { 191 return nil, err 192 } 193 // Query endpoint. 194 hcnQuery := defaultQuery() 195 query, err := json.Marshal(hcnQuery) 196 if err != nil { 197 return nil, err 198 } 199 hr = hcnQueryEndpointProperties(endpointHandle, string(query), &propertiesBuffer, &resultBuffer) 200 if err := checkForErrors("hcnQueryEndpointProperties", hr, resultBuffer); err != nil { 201 return nil, err 202 } 203 properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer) 204 // Close endpoint. 205 hr = hcnCloseEndpoint(endpointHandle) 206 if err := checkForErrors("hcnCloseEndpoint", hr, nil); err != nil { 207 return nil, err 208 } 209 // Convert output to HostComputeEndpoint 210 var outputEndpoint HostComputeEndpoint 211 if err := json.Unmarshal([]byte(properties), &outputEndpoint); err != nil { 212 return nil, err 213 } 214 return &outputEndpoint, nil 215} 216 217func deleteEndpoint(endpointId string) error { 218 endpointGuid, err := guid.FromString(endpointId) 219 if err != nil { 220 return errInvalidEndpointID 221 } 222 var resultBuffer *uint16 223 hr := hcnDeleteEndpoint(&endpointGuid, &resultBuffer) 224 if err := checkForErrors("hcnDeleteEndpoint", hr, resultBuffer); err != nil { 225 return err 226 } 227 return nil 228} 229 230// ListEndpoints makes a call to list all available endpoints. 231func ListEndpoints() ([]HostComputeEndpoint, error) { 232 hcnQuery := defaultQuery() 233 endpoints, err := ListEndpointsQuery(hcnQuery) 234 if err != nil { 235 return nil, err 236 } 237 return endpoints, nil 238} 239 240// ListEndpointsQuery makes a call to query the list of available endpoints. 241func ListEndpointsQuery(query HostComputeQuery) ([]HostComputeEndpoint, error) { 242 queryJson, err := json.Marshal(query) 243 if err != nil { 244 return nil, err 245 } 246 247 endpoints, err := enumerateEndpoints(string(queryJson)) 248 if err != nil { 249 return nil, err 250 } 251 return endpoints, nil 252} 253 254// ListEndpointsOfNetwork queries the list of endpoints on a network. 255func ListEndpointsOfNetwork(networkId string) ([]HostComputeEndpoint, error) { 256 hcnQuery := defaultQuery() 257 // TODO: Once query can convert schema, change to {HostComputeNetwork:networkId} 258 mapA := map[string]string{"VirtualNetwork": networkId} 259 filter, err := json.Marshal(mapA) 260 if err != nil { 261 return nil, err 262 } 263 hcnQuery.Filter = string(filter) 264 265 return ListEndpointsQuery(hcnQuery) 266} 267 268// GetEndpointByID returns an endpoint specified by Id 269func GetEndpointByID(endpointId string) (*HostComputeEndpoint, error) { 270 hcnQuery := defaultQuery() 271 mapA := map[string]string{"ID": endpointId} 272 filter, err := json.Marshal(mapA) 273 if err != nil { 274 return nil, err 275 } 276 hcnQuery.Filter = string(filter) 277 278 endpoints, err := ListEndpointsQuery(hcnQuery) 279 if err != nil { 280 return nil, err 281 } 282 if len(endpoints) == 0 { 283 return nil, EndpointNotFoundError{EndpointID: endpointId} 284 } 285 return &endpoints[0], err 286} 287 288// GetEndpointByName returns an endpoint specified by Name 289func GetEndpointByName(endpointName string) (*HostComputeEndpoint, error) { 290 hcnQuery := defaultQuery() 291 mapA := map[string]string{"Name": endpointName} 292 filter, err := json.Marshal(mapA) 293 if err != nil { 294 return nil, err 295 } 296 hcnQuery.Filter = string(filter) 297 298 endpoints, err := ListEndpointsQuery(hcnQuery) 299 if err != nil { 300 return nil, err 301 } 302 if len(endpoints) == 0 { 303 return nil, EndpointNotFoundError{EndpointName: endpointName} 304 } 305 return &endpoints[0], err 306} 307 308// Create Endpoint. 309func (endpoint *HostComputeEndpoint) Create() (*HostComputeEndpoint, error) { 310 logrus.Debugf("hcn::HostComputeEndpoint::Create id=%s", endpoint.Id) 311 312 if endpoint.HostComputeNamespace != "" { 313 return nil, errors.New("endpoint create error, endpoint json HostComputeNamespace is read only and should not be set") 314 } 315 316 jsonString, err := json.Marshal(endpoint) 317 if err != nil { 318 return nil, err 319 } 320 321 logrus.Debugf("hcn::HostComputeEndpoint::Create JSON: %s", jsonString) 322 endpoint, hcnErr := createEndpoint(endpoint.HostComputeNetwork, string(jsonString)) 323 if hcnErr != nil { 324 return nil, hcnErr 325 } 326 return endpoint, nil 327} 328 329// Delete Endpoint. 330func (endpoint *HostComputeEndpoint) Delete() error { 331 logrus.Debugf("hcn::HostComputeEndpoint::Delete id=%s", endpoint.Id) 332 333 if err := deleteEndpoint(endpoint.Id); err != nil { 334 return err 335 } 336 return nil 337} 338 339// ModifyEndpointSettings updates the Port/Policy of an Endpoint. 340func ModifyEndpointSettings(endpointId string, request *ModifyEndpointSettingRequest) error { 341 logrus.Debugf("hcn::HostComputeEndpoint::ModifyEndpointSettings id=%s", endpointId) 342 343 endpointSettingsRequest, err := json.Marshal(request) 344 if err != nil { 345 return err 346 } 347 348 _, err = modifyEndpoint(endpointId, string(endpointSettingsRequest)) 349 if err != nil { 350 return err 351 } 352 return nil 353} 354 355// ApplyPolicy applies a Policy (ex: ACL) on the Endpoint. 356func (endpoint *HostComputeEndpoint) ApplyPolicy(requestType RequestType, endpointPolicy PolicyEndpointRequest) error { 357 logrus.Debugf("hcn::HostComputeEndpoint::ApplyPolicy id=%s", endpoint.Id) 358 359 settingsJson, err := json.Marshal(endpointPolicy) 360 if err != nil { 361 return err 362 } 363 requestMessage := &ModifyEndpointSettingRequest{ 364 ResourceType: EndpointResourceTypePolicy, 365 RequestType: requestType, 366 Settings: settingsJson, 367 } 368 369 return ModifyEndpointSettings(endpoint.Id, requestMessage) 370} 371 372// NamespaceAttach modifies a Namespace to add an endpoint. 373func (endpoint *HostComputeEndpoint) NamespaceAttach(namespaceId string) error { 374 return AddNamespaceEndpoint(namespaceId, endpoint.Id) 375} 376 377// NamespaceDetach modifies a Namespace to remove an endpoint. 378func (endpoint *HostComputeEndpoint) NamespaceDetach(namespaceId string) error { 379 return RemoveNamespaceEndpoint(namespaceId, endpoint.Id) 380} 381