2019-01-12 04:58:27 +00:00
|
|
|
package hcn
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2019-09-30 23:25:17 +00:00
|
|
|
"errors"
|
2019-01-12 04:58:27 +00:00
|
|
|
|
2019-09-30 23:25:17 +00:00
|
|
|
"github.com/Microsoft/go-winio/pkg/guid"
|
2019-01-12 04:58:27 +00:00
|
|
|
"github.com/Microsoft/hcsshim/internal/interop"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Route is assoicated with a subnet.
|
|
|
|
type Route struct {
|
|
|
|
NextHop string `json:",omitempty"`
|
|
|
|
DestinationPrefix string `json:",omitempty"`
|
|
|
|
Metric uint16 `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subnet is assoicated with a Ipam.
|
|
|
|
type Subnet struct {
|
|
|
|
IpAddressPrefix string `json:",omitempty"`
|
|
|
|
Policies []json.RawMessage `json:",omitempty"`
|
|
|
|
Routes []Route `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ipam (Internet Protocol Addres Management) is assoicated with a network
|
|
|
|
// and represents the address space(s) of a network.
|
|
|
|
type Ipam struct {
|
|
|
|
Type string `json:",omitempty"` // Ex: Static, DHCP
|
|
|
|
Subnets []Subnet `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// MacRange is associated with MacPool and respresents the start and end addresses.
|
|
|
|
type MacRange struct {
|
|
|
|
StartMacAddress string `json:",omitempty"`
|
|
|
|
EndMacAddress string `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// MacPool is assoicated with a network and represents pool of MacRanges.
|
|
|
|
type MacPool struct {
|
|
|
|
Ranges []MacRange `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dns (Domain Name System is associated with a network.
|
|
|
|
type Dns struct {
|
2019-07-10 00:29:38 +00:00
|
|
|
Domain string `json:",omitempty"`
|
2019-01-12 04:58:27 +00:00
|
|
|
Search []string `json:",omitempty"`
|
|
|
|
ServerList []string `json:",omitempty"`
|
|
|
|
Options []string `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// NetworkType are various networks.
|
|
|
|
type NetworkType string
|
|
|
|
|
|
|
|
// NetworkType const
|
|
|
|
const (
|
|
|
|
NAT NetworkType = "NAT"
|
|
|
|
Transparent NetworkType = "Transparent"
|
|
|
|
L2Bridge NetworkType = "L2Bridge"
|
|
|
|
L2Tunnel NetworkType = "L2Tunnel"
|
|
|
|
ICS NetworkType = "ICS"
|
|
|
|
Private NetworkType = "Private"
|
|
|
|
Overlay NetworkType = "Overlay"
|
|
|
|
)
|
|
|
|
|
2019-07-10 00:29:38 +00:00
|
|
|
// NetworkFlags are various network flags.
|
|
|
|
type NetworkFlags uint32
|
|
|
|
|
|
|
|
// NetworkFlags const
|
|
|
|
const (
|
|
|
|
None NetworkFlags = 0
|
|
|
|
EnableNonPersistent NetworkFlags = 8
|
|
|
|
)
|
|
|
|
|
2019-01-12 04:58:27 +00:00
|
|
|
// HostComputeNetwork represents a network
|
|
|
|
type HostComputeNetwork struct {
|
|
|
|
Id string `json:"ID,omitempty"`
|
|
|
|
Name string `json:",omitempty"`
|
|
|
|
Type NetworkType `json:",omitempty"`
|
|
|
|
Policies []NetworkPolicy `json:",omitempty"`
|
|
|
|
MacPool MacPool `json:",omitempty"`
|
|
|
|
Dns Dns `json:",omitempty"`
|
|
|
|
Ipams []Ipam `json:",omitempty"`
|
2019-07-10 00:29:38 +00:00
|
|
|
Flags NetworkFlags `json:",omitempty"` // 0: None
|
2019-01-12 04:58:27 +00:00
|
|
|
SchemaVersion SchemaVersion `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// NetworkResourceType are the 3 different Network settings resources.
|
|
|
|
type NetworkResourceType string
|
|
|
|
|
|
|
|
var (
|
|
|
|
// NetworkResourceTypePolicy is for Network's policies. Ex: RemoteSubnet
|
|
|
|
NetworkResourceTypePolicy NetworkResourceType = "Policy"
|
|
|
|
// NetworkResourceTypeDNS is for Network's DNS settings.
|
|
|
|
NetworkResourceTypeDNS NetworkResourceType = "DNS"
|
|
|
|
// NetworkResourceTypeExtension is for Network's extension settings.
|
|
|
|
NetworkResourceTypeExtension NetworkResourceType = "Extension"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ModifyNetworkSettingRequest is the structure used to send request to modify an network.
|
|
|
|
// Used to update DNS/extension/policy on an network.
|
|
|
|
type ModifyNetworkSettingRequest struct {
|
|
|
|
ResourceType NetworkResourceType `json:",omitempty"` // Policy, DNS, Extension
|
|
|
|
RequestType RequestType `json:",omitempty"` // Add, Remove, Update, Refresh
|
|
|
|
Settings json.RawMessage `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type PolicyNetworkRequest struct {
|
|
|
|
Policies []NetworkPolicy `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func getNetwork(networkGuid guid.GUID, query string) (*HostComputeNetwork, error) {
|
|
|
|
// Open network.
|
|
|
|
var (
|
|
|
|
networkHandle hcnNetwork
|
|
|
|
resultBuffer *uint16
|
|
|
|
propertiesBuffer *uint16
|
|
|
|
)
|
|
|
|
hr := hcnOpenNetwork(&networkGuid, &networkHandle, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnOpenNetwork", hr, resultBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Query network.
|
|
|
|
hr = hcnQueryNetworkProperties(networkHandle, query, &propertiesBuffer, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnQueryNetworkProperties", hr, resultBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
|
|
|
|
// Close network.
|
|
|
|
hr = hcnCloseNetwork(networkHandle)
|
|
|
|
if err := checkForErrors("hcnCloseNetwork", hr, nil); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Convert output to HostComputeNetwork
|
|
|
|
var outputNetwork HostComputeNetwork
|
2019-09-30 23:25:17 +00:00
|
|
|
|
|
|
|
// If HNS sets the network type to NAT (i.e. '0' in HNS.Schema.Network.NetworkMode),
|
|
|
|
// the value will be omitted from the JSON blob. We therefore need to initialize NAT here before
|
|
|
|
// unmarshaling the JSON blob.
|
|
|
|
outputNetwork.Type = NAT
|
|
|
|
|
2019-01-12 04:58:27 +00:00
|
|
|
if err := json.Unmarshal([]byte(properties), &outputNetwork); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &outputNetwork, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func enumerateNetworks(query string) ([]HostComputeNetwork, error) {
|
|
|
|
// Enumerate all Network Guids
|
|
|
|
var (
|
|
|
|
resultBuffer *uint16
|
|
|
|
networkBuffer *uint16
|
|
|
|
)
|
|
|
|
hr := hcnEnumerateNetworks(query, &networkBuffer, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnEnumerateNetworks", hr, resultBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
networks := interop.ConvertAndFreeCoTaskMemString(networkBuffer)
|
|
|
|
var networkIds []guid.GUID
|
|
|
|
if err := json.Unmarshal([]byte(networks), &networkIds); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var outputNetworks []HostComputeNetwork
|
|
|
|
for _, networkGuid := range networkIds {
|
|
|
|
network, err := getNetwork(networkGuid, query)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
outputNetworks = append(outputNetworks, *network)
|
|
|
|
}
|
|
|
|
return outputNetworks, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func createNetwork(settings string) (*HostComputeNetwork, error) {
|
|
|
|
// Create new network.
|
|
|
|
var (
|
|
|
|
networkHandle hcnNetwork
|
|
|
|
resultBuffer *uint16
|
|
|
|
propertiesBuffer *uint16
|
|
|
|
)
|
|
|
|
networkGuid := guid.GUID{}
|
|
|
|
hr := hcnCreateNetwork(&networkGuid, settings, &networkHandle, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnCreateNetwork", hr, resultBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Query network.
|
|
|
|
hcnQuery := defaultQuery()
|
|
|
|
query, err := json.Marshal(hcnQuery)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hr = hcnQueryNetworkProperties(networkHandle, string(query), &propertiesBuffer, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnQueryNetworkProperties", hr, resultBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
|
|
|
|
// Close network.
|
|
|
|
hr = hcnCloseNetwork(networkHandle)
|
|
|
|
if err := checkForErrors("hcnCloseNetwork", hr, nil); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Convert output to HostComputeNetwork
|
|
|
|
var outputNetwork HostComputeNetwork
|
2019-09-30 23:25:17 +00:00
|
|
|
|
|
|
|
// If HNS sets the network type to NAT (i.e. '0' in HNS.Schema.Network.NetworkMode),
|
|
|
|
// the value will be omitted from the JSON blob. We therefore need to initialize NAT here before
|
|
|
|
// unmarshaling the JSON blob.
|
|
|
|
outputNetwork.Type = NAT
|
|
|
|
|
2019-01-12 04:58:27 +00:00
|
|
|
if err := json.Unmarshal([]byte(properties), &outputNetwork); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &outputNetwork, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func modifyNetwork(networkId string, settings string) (*HostComputeNetwork, error) {
|
2019-09-30 23:25:17 +00:00
|
|
|
networkGuid, err := guid.FromString(networkId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errInvalidNetworkID
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
// Open Network
|
|
|
|
var (
|
|
|
|
networkHandle hcnNetwork
|
|
|
|
resultBuffer *uint16
|
|
|
|
propertiesBuffer *uint16
|
|
|
|
)
|
|
|
|
hr := hcnOpenNetwork(&networkGuid, &networkHandle, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnOpenNetwork", hr, resultBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Modify Network
|
|
|
|
hr = hcnModifyNetwork(networkHandle, settings, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnModifyNetwork", hr, resultBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Query network.
|
|
|
|
hcnQuery := defaultQuery()
|
|
|
|
query, err := json.Marshal(hcnQuery)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hr = hcnQueryNetworkProperties(networkHandle, string(query), &propertiesBuffer, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnQueryNetworkProperties", hr, resultBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
|
|
|
|
// Close network.
|
|
|
|
hr = hcnCloseNetwork(networkHandle)
|
|
|
|
if err := checkForErrors("hcnCloseNetwork", hr, nil); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Convert output to HostComputeNetwork
|
|
|
|
var outputNetwork HostComputeNetwork
|
2019-09-30 23:25:17 +00:00
|
|
|
|
|
|
|
// If HNS sets the network type to NAT (i.e. '0' in HNS.Schema.Network.NetworkMode),
|
|
|
|
// the value will be omitted from the JSON blob. We therefore need to initialize NAT here before
|
|
|
|
// unmarshaling the JSON blob.
|
|
|
|
outputNetwork.Type = NAT
|
|
|
|
|
2019-01-12 04:58:27 +00:00
|
|
|
if err := json.Unmarshal([]byte(properties), &outputNetwork); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &outputNetwork, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteNetwork(networkId string) error {
|
2019-09-30 23:25:17 +00:00
|
|
|
networkGuid, err := guid.FromString(networkId)
|
|
|
|
if err != nil {
|
|
|
|
return errInvalidNetworkID
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
var resultBuffer *uint16
|
|
|
|
hr := hcnDeleteNetwork(&networkGuid, &resultBuffer)
|
|
|
|
if err := checkForErrors("hcnDeleteNetwork", hr, resultBuffer); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListNetworks makes a call to list all available networks.
|
|
|
|
func ListNetworks() ([]HostComputeNetwork, error) {
|
|
|
|
hcnQuery := defaultQuery()
|
|
|
|
networks, err := ListNetworksQuery(hcnQuery)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return networks, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListNetworksQuery makes a call to query the list of available networks.
|
|
|
|
func ListNetworksQuery(query HostComputeQuery) ([]HostComputeNetwork, error) {
|
|
|
|
queryJson, err := json.Marshal(query)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
networks, err := enumerateNetworks(string(queryJson))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return networks, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetNetworkByID returns the network specified by Id.
|
|
|
|
func GetNetworkByID(networkID string) (*HostComputeNetwork, error) {
|
|
|
|
hcnQuery := defaultQuery()
|
|
|
|
mapA := map[string]string{"ID": networkID}
|
|
|
|
filter, err := json.Marshal(mapA)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hcnQuery.Filter = string(filter)
|
|
|
|
|
|
|
|
networks, err := ListNetworksQuery(hcnQuery)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(networks) == 0 {
|
|
|
|
return nil, NetworkNotFoundError{NetworkID: networkID}
|
|
|
|
}
|
|
|
|
return &networks[0], err
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetNetworkByName returns the network specified by Name.
|
|
|
|
func GetNetworkByName(networkName string) (*HostComputeNetwork, error) {
|
|
|
|
hcnQuery := defaultQuery()
|
|
|
|
mapA := map[string]string{"Name": networkName}
|
|
|
|
filter, err := json.Marshal(mapA)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hcnQuery.Filter = string(filter)
|
|
|
|
|
|
|
|
networks, err := ListNetworksQuery(hcnQuery)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(networks) == 0 {
|
|
|
|
return nil, NetworkNotFoundError{NetworkName: networkName}
|
|
|
|
}
|
|
|
|
return &networks[0], err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create Network.
|
|
|
|
func (network *HostComputeNetwork) Create() (*HostComputeNetwork, error) {
|
|
|
|
logrus.Debugf("hcn::HostComputeNetwork::Create id=%s", network.Id)
|
2019-09-30 23:25:17 +00:00
|
|
|
for _, ipam := range network.Ipams {
|
|
|
|
for _, subnet := range ipam.Subnets {
|
|
|
|
if subnet.IpAddressPrefix != "" {
|
|
|
|
hasDefault := false
|
|
|
|
for _, route := range subnet.Routes {
|
|
|
|
if route.NextHop == "" {
|
|
|
|
return nil, errors.New("network create error, subnet has address prefix but no gateway specified")
|
|
|
|
}
|
|
|
|
if route.DestinationPrefix == "0.0.0.0/0" || route.DestinationPrefix == "::/0" {
|
|
|
|
hasDefault = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !hasDefault {
|
|
|
|
return nil, errors.New("network create error, no default gateway")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
jsonString, err := json.Marshal(network)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
logrus.Debugf("hcn::HostComputeNetwork::Create JSON: %s", jsonString)
|
|
|
|
network, hcnErr := createNetwork(string(jsonString))
|
|
|
|
if hcnErr != nil {
|
|
|
|
return nil, hcnErr
|
|
|
|
}
|
|
|
|
return network, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete Network.
|
2019-07-10 00:29:38 +00:00
|
|
|
func (network *HostComputeNetwork) Delete() error {
|
2019-01-12 04:58:27 +00:00
|
|
|
logrus.Debugf("hcn::HostComputeNetwork::Delete id=%s", network.Id)
|
|
|
|
|
|
|
|
if err := deleteNetwork(network.Id); err != nil {
|
2019-07-10 00:29:38 +00:00
|
|
|
return err
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
2019-07-10 00:29:38 +00:00
|
|
|
return nil
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ModifyNetworkSettings updates the Policy for a network.
|
|
|
|
func (network *HostComputeNetwork) ModifyNetworkSettings(request *ModifyNetworkSettingRequest) error {
|
|
|
|
logrus.Debugf("hcn::HostComputeNetwork::ModifyNetworkSettings id=%s", network.Id)
|
|
|
|
|
|
|
|
networkSettingsRequest, err := json.Marshal(request)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = modifyNetwork(network.Id, string(networkSettingsRequest))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddPolicy applies a Policy (ex: RemoteSubnet) on the Network.
|
|
|
|
func (network *HostComputeNetwork) AddPolicy(networkPolicy PolicyNetworkRequest) error {
|
|
|
|
logrus.Debugf("hcn::HostComputeNetwork::AddPolicy id=%s", network.Id)
|
|
|
|
|
|
|
|
settingsJson, err := json.Marshal(networkPolicy)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
requestMessage := &ModifyNetworkSettingRequest{
|
|
|
|
ResourceType: NetworkResourceTypePolicy,
|
|
|
|
RequestType: RequestTypeAdd,
|
|
|
|
Settings: settingsJson,
|
|
|
|
}
|
|
|
|
|
|
|
|
return network.ModifyNetworkSettings(requestMessage)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemovePolicy removes a Policy (ex: RemoteSubnet) from the Network.
|
|
|
|
func (network *HostComputeNetwork) RemovePolicy(networkPolicy PolicyNetworkRequest) error {
|
|
|
|
logrus.Debugf("hcn::HostComputeNetwork::RemovePolicy id=%s", network.Id)
|
|
|
|
|
|
|
|
settingsJson, err := json.Marshal(networkPolicy)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
requestMessage := &ModifyNetworkSettingRequest{
|
|
|
|
ResourceType: NetworkResourceTypePolicy,
|
|
|
|
RequestType: RequestTypeRemove,
|
|
|
|
Settings: settingsJson,
|
|
|
|
}
|
|
|
|
|
|
|
|
return network.ModifyNetworkSettings(requestMessage)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateEndpoint creates an endpoint on the Network.
|
|
|
|
func (network *HostComputeNetwork) CreateEndpoint(endpoint *HostComputeEndpoint) (*HostComputeEndpoint, error) {
|
|
|
|
isRemote := endpoint.Flags&EndpointFlagsRemoteEndpoint != 0
|
|
|
|
logrus.Debugf("hcn::HostComputeNetwork::CreatEndpoint, networkId=%s remote=%t", network.Id, isRemote)
|
|
|
|
|
|
|
|
endpoint.HostComputeNetwork = network.Id
|
|
|
|
endpointSettings, err := json.Marshal(endpoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
newEndpoint, err := createEndpoint(network.Id, string(endpointSettings))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newEndpoint, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateRemoteEndpoint creates a remote endpoint on the Network.
|
|
|
|
func (network *HostComputeNetwork) CreateRemoteEndpoint(endpoint *HostComputeEndpoint) (*HostComputeEndpoint, error) {
|
|
|
|
endpoint.Flags = EndpointFlagsRemoteEndpoint | endpoint.Flags
|
|
|
|
return network.CreateEndpoint(endpoint)
|
|
|
|
}
|