update go vendor
This commit is contained in:
parent
38fe79948b
commit
f6854f5528
1
go.mod
1
go.mod
|
@ -90,7 +90,6 @@ require (
|
|||
github.com/zclconf/go-cty v1.8.1
|
||||
github.com/zclconf/go-cty-yaml v1.0.1
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2
|
||||
golang.org/x/mod v0.3.0
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ovf
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
/*
|
||||
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_VirtualSystemSettingData.xsd
|
||||
*/
|
||||
|
||||
type CIMVirtualSystemSettingData struct {
|
||||
ElementName string `xml:"ElementName"`
|
||||
InstanceID string `xml:"InstanceID"`
|
||||
|
||||
AutomaticRecoveryAction *uint8 `xml:"AutomaticRecoveryAction"`
|
||||
AutomaticShutdownAction *uint8 `xml:"AutomaticShutdownAction"`
|
||||
AutomaticStartupAction *uint8 `xml:"AutomaticStartupAction"`
|
||||
AutomaticStartupActionDelay *string `xml:"AutomaticStartupActionDelay>Interval"`
|
||||
AutomaticStartupActionSequenceNumber *uint16 `xml:"AutomaticStartupActionSequenceNumber"`
|
||||
Caption *string `xml:"Caption"`
|
||||
ConfigurationDataRoot *string `xml:"ConfigurationDataRoot"`
|
||||
ConfigurationFile *string `xml:"ConfigurationFile"`
|
||||
ConfigurationID *string `xml:"ConfigurationID"`
|
||||
CreationTime *string `xml:"CreationTime"`
|
||||
Description *string `xml:"Description"`
|
||||
LogDataRoot *string `xml:"LogDataRoot"`
|
||||
Notes []string `xml:"Notes"`
|
||||
RecoveryFile *string `xml:"RecoveryFile"`
|
||||
SnapshotDataRoot *string `xml:"SnapshotDataRoot"`
|
||||
SuspendDataRoot *string `xml:"SuspendDataRoot"`
|
||||
SwapFileDataRoot *string `xml:"SwapFileDataRoot"`
|
||||
VirtualSystemIdentifier *string `xml:"VirtualSystemIdentifier"`
|
||||
VirtualSystemType *string `xml:"VirtualSystemType"`
|
||||
}
|
||||
|
||||
/*
|
||||
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_ResourceAllocationSettingData.xsd
|
||||
*/
|
||||
|
||||
type CIMResourceAllocationSettingData struct {
|
||||
ElementName string `xml:"ElementName"`
|
||||
InstanceID string `xml:"InstanceID"`
|
||||
|
||||
ResourceType *uint16 `xml:"ResourceType"`
|
||||
OtherResourceType *string `xml:"OtherResourceType"`
|
||||
ResourceSubType *string `xml:"ResourceSubType"`
|
||||
|
||||
AddressOnParent *string `xml:"AddressOnParent"`
|
||||
Address *string `xml:"Address"`
|
||||
AllocationUnits *string `xml:"AllocationUnits"`
|
||||
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
|
||||
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
|
||||
Caption *string `xml:"Caption"`
|
||||
Connection []string `xml:"Connection"`
|
||||
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
|
||||
Description *string `xml:"Description"`
|
||||
HostResource []string `xml:"HostResource"`
|
||||
Limit *uint64 `xml:"Limit"`
|
||||
MappingBehavior *uint `xml:"MappingBehavior"`
|
||||
Parent *string `xml:"Parent"`
|
||||
PoolID *string `xml:"PoolID"`
|
||||
Reservation *uint64 `xml:"Reservation"`
|
||||
VirtualQuantity *uint `xml:"VirtualQuantity"`
|
||||
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
|
||||
Weight *uint `xml:"Weight"`
|
||||
}
|
||||
|
||||
/*
|
||||
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_StorageAllocationSettingData.xsd
|
||||
*/
|
||||
type CIMStorageAllocationSettingData struct {
|
||||
ElementName string `xml:"ElementName"`
|
||||
InstanceID string `xml:"InstanceID"`
|
||||
|
||||
ResourceType *uint16 `xml:"ResourceType"`
|
||||
OtherResourceType *string `xml:"OtherResourceType"`
|
||||
ResourceSubType *string `xml:"ResourceSubType"`
|
||||
|
||||
Access *uint16 `xml:"Access"`
|
||||
Address *string `xml:"Address"`
|
||||
AddressOnParent *string `xml:"AddressOnParent"`
|
||||
AllocationUnits *string `xml:"AllocationUnits"`
|
||||
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
|
||||
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
|
||||
Caption *string `xml:"Caption"`
|
||||
ChangeableType *uint16 `xml:"ChangeableType"`
|
||||
ComponentSetting []types.AnyType `xml:"ComponentSetting"`
|
||||
ConfigurationName *string `xml:"ConfigurationName"`
|
||||
Connection []string `xml:"Connection"`
|
||||
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
|
||||
Description *string `xml:"Description"`
|
||||
Generation *uint64 `xml:"Generation"`
|
||||
HostExtentName *string `xml:"HostExtentName"`
|
||||
HostExtentNameFormat *uint16 `xml:"HostExtentNameFormat"`
|
||||
HostExtentNameNamespace *uint16 `xml:"HostExtentNameNamespace"`
|
||||
HostExtentStartingAddress *uint64 `xml:"HostExtentStartingAddress"`
|
||||
HostResource []string `xml:"HostResource"`
|
||||
HostResourceBlockSize *uint64 `xml:"HostResourceBlockSize"`
|
||||
Limit *uint64 `xml:"Limit"`
|
||||
MappingBehavior *uint `xml:"MappingBehavior"`
|
||||
OtherHostExtentNameFormat *string `xml:"OtherHostExtentNameFormat"`
|
||||
OtherHostExtentNameNamespace *string `xml:"OtherHostExtentNameNamespace"`
|
||||
Parent *string `xml:"Parent"`
|
||||
PoolID *string `xml:"PoolID"`
|
||||
Reservation *uint64 `xml:"Reservation"`
|
||||
SoID *string `xml:"SoID"`
|
||||
SoOrgID *string `xml:"SoOrgID"`
|
||||
VirtualQuantity *uint `xml:"VirtualQuantity"`
|
||||
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
|
||||
VirtualResourceBlockSize *uint64 `xml:"VirtualResourceBlockSize"`
|
||||
Weight *uint `xml:"Weight"`
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package ovf provides functionality to unmarshal and inspect the structure
|
||||
of an OVF file. It is not a complete implementation of the specification and
|
||||
is intended to be used to import virtual infrastructure into vSphere.
|
||||
|
||||
For a complete specification of the OVF standard, refer to:
|
||||
https://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.0.pdf
|
||||
*/
|
||||
package ovf
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ovf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/xml"
|
||||
)
|
||||
|
||||
const (
|
||||
ovfEnvHeader = `<Environment
|
||||
xmlns="http://schemas.dmtf.org/ovf/environment/1"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
|
||||
xmlns:ve="http://www.vmware.com/schema/ovfenv"
|
||||
oe:id=""
|
||||
ve:esxId="%s">`
|
||||
ovfEnvPlatformSection = `<PlatformSection>
|
||||
<Kind>%s</Kind>
|
||||
<Version>%s</Version>
|
||||
<Vendor>%s</Vendor>
|
||||
<Locale>%s</Locale>
|
||||
</PlatformSection>`
|
||||
ovfEnvPropertyHeader = `<PropertySection>`
|
||||
ovfEnvPropertyEntry = `<Property oe:key="%s" oe:value="%s"/>`
|
||||
ovfEnvPropertyFooter = `</PropertySection>`
|
||||
ovfEnvFooter = `</Environment>`
|
||||
)
|
||||
|
||||
type Env struct {
|
||||
XMLName xml.Name `xml:"http://schemas.dmtf.org/ovf/environment/1 Environment"`
|
||||
ID string `xml:"id,attr"`
|
||||
EsxID string `xml:"http://www.vmware.com/schema/ovfenv esxId,attr"`
|
||||
|
||||
Platform *PlatformSection `xml:"PlatformSection"`
|
||||
Property *PropertySection `xml:"PropertySection"`
|
||||
}
|
||||
|
||||
type PlatformSection struct {
|
||||
Kind string `xml:"Kind"`
|
||||
Version string `xml:"Version"`
|
||||
Vendor string `xml:"Vendor"`
|
||||
Locale string `xml:"Locale"`
|
||||
}
|
||||
|
||||
type PropertySection struct {
|
||||
Properties []EnvProperty `xml:"Property"`
|
||||
}
|
||||
|
||||
type EnvProperty struct {
|
||||
Key string `xml:"key,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
|
||||
// Marshal marshals Env to xml by using xml.Marshal.
|
||||
func (e Env) Marshal() (string, error) {
|
||||
x, err := xml.Marshal(e)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s", xml.Header, x), nil
|
||||
}
|
||||
|
||||
// MarshalManual manually marshals Env to xml suitable for a vApp guest.
|
||||
// It exists to overcome the lack of expressiveness in Go's XML namespaces.
|
||||
func (e Env) MarshalManual() string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
buffer.WriteString(xml.Header)
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvHeader, e.EsxID))
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvPlatformSection, e.Platform.Kind, e.Platform.Version, e.Platform.Vendor, e.Platform.Locale))
|
||||
|
||||
buffer.WriteString(fmt.Sprint(ovfEnvPropertyHeader))
|
||||
for _, p := range e.Property.Properties {
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvPropertyEntry, p.Key, p.Value))
|
||||
}
|
||||
buffer.WriteString(fmt.Sprint(ovfEnvPropertyFooter))
|
||||
|
||||
buffer.WriteString(fmt.Sprint(ovfEnvFooter))
|
||||
|
||||
return buffer.String()
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ovf
|
||||
|
||||
type Envelope struct {
|
||||
References []File `xml:"References>File"`
|
||||
|
||||
// Package level meta-data
|
||||
Annotation *AnnotationSection `xml:"AnnotationSection"`
|
||||
Product *ProductSection `xml:"ProductSection"`
|
||||
Network *NetworkSection `xml:"NetworkSection"`
|
||||
Disk *DiskSection `xml:"DiskSection"`
|
||||
OperatingSystem *OperatingSystemSection `xml:"OperatingSystemSection"`
|
||||
Eula *EulaSection `xml:"EulaSection"`
|
||||
VirtualHardware *VirtualHardwareSection `xml:"VirtualHardwareSection"`
|
||||
ResourceAllocation *ResourceAllocationSection `xml:"ResourceAllocationSection"`
|
||||
DeploymentOption *DeploymentOptionSection `xml:"DeploymentOptionSection"`
|
||||
|
||||
// Content: A VirtualSystem or a VirtualSystemCollection
|
||||
VirtualSystem *VirtualSystem `xml:"VirtualSystem"`
|
||||
}
|
||||
|
||||
type VirtualSystem struct {
|
||||
Content
|
||||
|
||||
Annotation []AnnotationSection `xml:"AnnotationSection"`
|
||||
Product []ProductSection `xml:"ProductSection"`
|
||||
OperatingSystem []OperatingSystemSection `xml:"OperatingSystemSection"`
|
||||
Eula []EulaSection `xml:"EulaSection"`
|
||||
VirtualHardware []VirtualHardwareSection `xml:"VirtualHardwareSection"`
|
||||
}
|
||||
|
||||
type File struct {
|
||||
ID string `xml:"id,attr"`
|
||||
Href string `xml:"href,attr"`
|
||||
Size uint `xml:"size,attr"`
|
||||
Compression *string `xml:"compression,attr"`
|
||||
ChunkSize *int `xml:"chunkSize,attr"`
|
||||
}
|
||||
|
||||
type Content struct {
|
||||
ID string `xml:"id,attr"`
|
||||
Info string `xml:"Info"`
|
||||
Name *string `xml:"Name"`
|
||||
}
|
||||
|
||||
type Section struct {
|
||||
Required *bool `xml:"required,attr"`
|
||||
Info string `xml:"Info"`
|
||||
}
|
||||
|
||||
type AnnotationSection struct {
|
||||
Section
|
||||
|
||||
Annotation string `xml:"Annotation"`
|
||||
}
|
||||
|
||||
type ProductSection struct {
|
||||
Section
|
||||
|
||||
Class *string `xml:"class,attr"`
|
||||
Instance *string `xml:"instance,attr"`
|
||||
|
||||
Product string `xml:"Product"`
|
||||
Vendor string `xml:"Vendor"`
|
||||
Version string `xml:"Version"`
|
||||
FullVersion string `xml:"FullVersion"`
|
||||
ProductURL string `xml:"ProductUrl"`
|
||||
VendorURL string `xml:"VendorUrl"`
|
||||
AppURL string `xml:"AppUrl"`
|
||||
Property []Property `xml:"Property"`
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
Key string `xml:"key,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Qualifiers *string `xml:"qualifiers,attr"`
|
||||
UserConfigurable *bool `xml:"userConfigurable,attr"`
|
||||
Default *string `xml:"value,attr"`
|
||||
Password *bool `xml:"password,attr"`
|
||||
|
||||
Label *string `xml:"Label"`
|
||||
Description *string `xml:"Description"`
|
||||
|
||||
Values []PropertyConfigurationValue `xml:"Value"`
|
||||
}
|
||||
|
||||
type PropertyConfigurationValue struct {
|
||||
Value string `xml:"value,attr"`
|
||||
Configuration *string `xml:"configuration,attr"`
|
||||
}
|
||||
|
||||
type NetworkSection struct {
|
||||
Section
|
||||
|
||||
Networks []Network `xml:"Network"`
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
Name string `xml:"name,attr"`
|
||||
|
||||
Description string `xml:"Description"`
|
||||
}
|
||||
|
||||
type DiskSection struct {
|
||||
Section
|
||||
|
||||
Disks []VirtualDiskDesc `xml:"Disk"`
|
||||
}
|
||||
|
||||
type VirtualDiskDesc struct {
|
||||
DiskID string `xml:"diskId,attr"`
|
||||
FileRef *string `xml:"fileRef,attr"`
|
||||
Capacity string `xml:"capacity,attr"`
|
||||
CapacityAllocationUnits *string `xml:"capacityAllocationUnits,attr"`
|
||||
Format *string `xml:"format,attr"`
|
||||
PopulatedSize *int `xml:"populatedSize,attr"`
|
||||
ParentRef *string `xml:"parentRef,attr"`
|
||||
}
|
||||
|
||||
type OperatingSystemSection struct {
|
||||
Section
|
||||
|
||||
ID int16 `xml:"id,attr"`
|
||||
Version *string `xml:"version,attr"`
|
||||
OSType *string `xml:"osType,attr"`
|
||||
|
||||
Description *string `xml:"Description"`
|
||||
}
|
||||
|
||||
type EulaSection struct {
|
||||
Section
|
||||
|
||||
License string `xml:"License"`
|
||||
}
|
||||
|
||||
type VirtualHardwareSection struct {
|
||||
Section
|
||||
|
||||
ID *string `xml:"id,attr"`
|
||||
Transport *string `xml:"transport,attr"`
|
||||
|
||||
System *VirtualSystemSettingData `xml:"System"`
|
||||
Item []ResourceAllocationSettingData `xml:"Item"`
|
||||
StorageItem []StorageAllocationSettingData `xml:"StorageItem"`
|
||||
}
|
||||
|
||||
type VirtualSystemSettingData struct {
|
||||
CIMVirtualSystemSettingData
|
||||
}
|
||||
|
||||
type ResourceAllocationSettingData struct {
|
||||
CIMResourceAllocationSettingData
|
||||
|
||||
Required *bool `xml:"required,attr"`
|
||||
Configuration *string `xml:"configuration,attr"`
|
||||
Bound *string `xml:"bound,attr"`
|
||||
}
|
||||
|
||||
type StorageAllocationSettingData struct {
|
||||
CIMStorageAllocationSettingData
|
||||
|
||||
Required *bool `xml:"required,attr"`
|
||||
Configuration *string `xml:"configuration,attr"`
|
||||
Bound *string `xml:"bound,attr"`
|
||||
}
|
||||
|
||||
type ResourceAllocationSection struct {
|
||||
Section
|
||||
|
||||
Item []ResourceAllocationSettingData `xml:"Item"`
|
||||
}
|
||||
|
||||
type DeploymentOptionSection struct {
|
||||
Section
|
||||
|
||||
Configuration []DeploymentOptionConfiguration `xml:"Configuration"`
|
||||
}
|
||||
|
||||
type DeploymentOptionConfiguration struct {
|
||||
ID string `xml:"id,attr"`
|
||||
Default *bool `xml:"default,attr"`
|
||||
|
||||
Label string `xml:"Label"`
|
||||
Description string `xml:"Description"`
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ovf
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
types.ManagedObjectReference
|
||||
|
||||
c *vim25.Client
|
||||
}
|
||||
|
||||
func NewManager(c *vim25.Client) *Manager {
|
||||
return &Manager{*c.ServiceContent.OvfManager, c}
|
||||
}
|
||||
|
||||
// CreateDescriptor wraps methods.CreateDescriptor
|
||||
func (m *Manager) CreateDescriptor(ctx context.Context, obj mo.Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
|
||||
req := types.CreateDescriptor{
|
||||
This: m.Reference(),
|
||||
Obj: obj.Reference(),
|
||||
Cdp: cdp,
|
||||
}
|
||||
|
||||
res, err := methods.CreateDescriptor(ctx, m.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res.Returnval, nil
|
||||
}
|
||||
|
||||
// CreateImportSpec wraps methods.CreateImportSpec
|
||||
func (m *Manager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool mo.Reference, datastore mo.Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) {
|
||||
req := types.CreateImportSpec{
|
||||
This: m.Reference(),
|
||||
OvfDescriptor: ovfDescriptor,
|
||||
ResourcePool: resourcePool.Reference(),
|
||||
Datastore: datastore.Reference(),
|
||||
Cisp: cisp,
|
||||
}
|
||||
|
||||
res, err := methods.CreateImportSpec(ctx, m.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res.Returnval, nil
|
||||
}
|
||||
|
||||
// ParseDescriptor wraps methods.ParseDescriptor
|
||||
func (m *Manager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) {
|
||||
req := types.ParseDescriptor{
|
||||
This: m.Reference(),
|
||||
OvfDescriptor: ovfDescriptor,
|
||||
Pdp: pdp,
|
||||
}
|
||||
|
||||
res, err := methods.ParseDescriptor(ctx, m.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res.Returnval, nil
|
||||
}
|
||||
|
||||
// ValidateHost wraps methods.ValidateHost
|
||||
func (m *Manager) ValidateHost(ctx context.Context, ovfDescriptor string, host mo.Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) {
|
||||
req := types.ValidateHost{
|
||||
This: m.Reference(),
|
||||
OvfDescriptor: ovfDescriptor,
|
||||
Host: host.Reference(),
|
||||
Vhp: vhp,
|
||||
}
|
||||
|
||||
res, err := methods.ValidateHost(ctx, m.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res.Returnval, nil
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ovf
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/xml"
|
||||
)
|
||||
|
||||
func Unmarshal(r io.Reader) (*Envelope, error) {
|
||||
var e Envelope
|
||||
|
||||
dec := xml.NewDecoder(r)
|
||||
err := dec.Decode(&e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &e, nil
|
||||
}
|
|
@ -1,280 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type AuthorizationManager struct {
|
||||
mo.AuthorizationManager
|
||||
|
||||
permissions map[types.ManagedObjectReference][]types.Permission
|
||||
privileges map[string]struct{}
|
||||
system []string
|
||||
nextID int32
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) init(r *Registry) {
|
||||
if len(m.RoleList) == 0 {
|
||||
m.RoleList = make([]types.AuthorizationRole, len(esx.RoleList))
|
||||
copy(m.RoleList, esx.RoleList)
|
||||
}
|
||||
|
||||
m.permissions = make(map[types.ManagedObjectReference][]types.Permission)
|
||||
|
||||
l := object.AuthorizationRoleList(m.RoleList)
|
||||
m.system = l.ByName("ReadOnly").Privilege
|
||||
admin := l.ByName("Admin")
|
||||
m.privileges = make(map[string]struct{}, len(admin.Privilege))
|
||||
|
||||
for _, id := range admin.Privilege {
|
||||
m.privileges[id] = struct{}{}
|
||||
}
|
||||
|
||||
root := r.content().RootFolder
|
||||
|
||||
for _, u := range DefaultUserGroup {
|
||||
m.permissions[root] = append(m.permissions[root], types.Permission{
|
||||
Entity: &root,
|
||||
Principal: u.Principal,
|
||||
Group: u.Group,
|
||||
RoleId: admin.RoleId,
|
||||
Propagate: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) RetrieveEntityPermissions(req *types.RetrieveEntityPermissions) soap.HasFault {
|
||||
e := Map.Get(req.Entity).(mo.Entity)
|
||||
|
||||
p := m.permissions[e.Reference()]
|
||||
|
||||
if req.Inherited {
|
||||
for {
|
||||
parent := e.Entity().Parent
|
||||
if parent == nil {
|
||||
break
|
||||
}
|
||||
|
||||
e = Map.Get(parent.Reference()).(mo.Entity)
|
||||
|
||||
p = append(p, m.permissions[e.Reference()]...)
|
||||
}
|
||||
}
|
||||
|
||||
return &methods.RetrieveEntityPermissionsBody{
|
||||
Res: &types.RetrieveEntityPermissionsResponse{
|
||||
Returnval: p,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) RetrieveAllPermissions(req *types.RetrieveAllPermissions) soap.HasFault {
|
||||
var p []types.Permission
|
||||
|
||||
for _, v := range m.permissions {
|
||||
p = append(p, v...)
|
||||
}
|
||||
|
||||
return &methods.RetrieveAllPermissionsBody{
|
||||
Res: &types.RetrieveAllPermissionsResponse{
|
||||
Returnval: p,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) RemoveEntityPermission(req *types.RemoveEntityPermission) soap.HasFault {
|
||||
var p []types.Permission
|
||||
|
||||
for _, v := range m.permissions[req.Entity] {
|
||||
if v.Group == req.IsGroup && v.Principal == req.User {
|
||||
continue
|
||||
}
|
||||
p = append(p, v)
|
||||
}
|
||||
|
||||
m.permissions[req.Entity] = p
|
||||
|
||||
return &methods.RemoveEntityPermissionBody{
|
||||
Res: &types.RemoveEntityPermissionResponse{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) SetEntityPermissions(req *types.SetEntityPermissions) soap.HasFault {
|
||||
m.permissions[req.Entity] = req.Permission
|
||||
|
||||
return &methods.SetEntityPermissionsBody{
|
||||
Res: &types.SetEntityPermissionsResponse{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) RetrieveRolePermissions(req *types.RetrieveRolePermissions) soap.HasFault {
|
||||
var p []types.Permission
|
||||
|
||||
for _, set := range m.permissions {
|
||||
for _, v := range set {
|
||||
if v.RoleId == req.RoleId {
|
||||
p = append(p, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &methods.RetrieveRolePermissionsBody{
|
||||
Res: &types.RetrieveRolePermissionsResponse{
|
||||
Returnval: p,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) HasPrivilegeOnEntities(req *types.HasPrivilegeOnEntities) soap.HasFault {
|
||||
var p []types.EntityPrivilege
|
||||
|
||||
for _, e := range req.Entity {
|
||||
priv := types.EntityPrivilege{Entity: e}
|
||||
|
||||
for _, id := range req.PrivId {
|
||||
priv.PrivAvailability = append(priv.PrivAvailability, types.PrivilegeAvailability{
|
||||
PrivId: id,
|
||||
IsGranted: true,
|
||||
})
|
||||
}
|
||||
|
||||
p = append(p, priv)
|
||||
}
|
||||
|
||||
return &methods.HasPrivilegeOnEntitiesBody{
|
||||
Res: &types.HasPrivilegeOnEntitiesResponse{
|
||||
Returnval: p,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) AddAuthorizationRole(req *types.AddAuthorizationRole) soap.HasFault {
|
||||
body := &methods.AddAuthorizationRoleBody{}
|
||||
|
||||
for _, role := range m.RoleList {
|
||||
if role.Name == req.Name {
|
||||
body.Fault_ = Fault("", &types.AlreadyExists{})
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
ids, err := m.privIDs(req.PrivIds)
|
||||
if err != nil {
|
||||
body.Fault_ = err
|
||||
return body
|
||||
}
|
||||
|
||||
m.RoleList = append(m.RoleList, types.AuthorizationRole{
|
||||
Info: &types.Description{
|
||||
Label: req.Name,
|
||||
Summary: req.Name,
|
||||
},
|
||||
RoleId: m.nextID,
|
||||
Privilege: ids,
|
||||
Name: req.Name,
|
||||
System: false,
|
||||
})
|
||||
|
||||
m.nextID++
|
||||
|
||||
body.Res = &types.AddAuthorizationRoleResponse{}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) UpdateAuthorizationRole(req *types.UpdateAuthorizationRole) soap.HasFault {
|
||||
body := &methods.UpdateAuthorizationRoleBody{}
|
||||
|
||||
for _, role := range m.RoleList {
|
||||
if role.Name == req.NewName && role.RoleId != req.RoleId {
|
||||
body.Fault_ = Fault("", &types.AlreadyExists{})
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
for i, role := range m.RoleList {
|
||||
if role.RoleId == req.RoleId {
|
||||
if len(req.PrivIds) != 0 {
|
||||
ids, err := m.privIDs(req.PrivIds)
|
||||
if err != nil {
|
||||
body.Fault_ = err
|
||||
return body
|
||||
}
|
||||
m.RoleList[i].Privilege = ids
|
||||
}
|
||||
|
||||
m.RoleList[i].Name = req.NewName
|
||||
|
||||
body.Res = &types.UpdateAuthorizationRoleResponse{}
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
body.Fault_ = Fault("", &types.NotFound{})
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) RemoveAuthorizationRole(req *types.RemoveAuthorizationRole) soap.HasFault {
|
||||
body := &methods.RemoveAuthorizationRoleBody{}
|
||||
|
||||
for i, role := range m.RoleList {
|
||||
if role.RoleId == req.RoleId {
|
||||
m.RoleList = append(m.RoleList[:i], m.RoleList[i+1:]...)
|
||||
|
||||
body.Res = &types.RemoveAuthorizationRoleResponse{}
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
body.Fault_ = Fault("", &types.NotFound{})
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *AuthorizationManager) privIDs(ids []string) ([]string, *soap.Fault) {
|
||||
system := make(map[string]struct{}, len(m.system))
|
||||
|
||||
for _, id := range ids {
|
||||
if _, ok := m.privileges[id]; !ok {
|
||||
return nil, Fault("", &types.InvalidArgument{InvalidProperty: "privIds"})
|
||||
}
|
||||
|
||||
if strings.HasPrefix(id, "System.") {
|
||||
system[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range m.system {
|
||||
if _, ok := system[id]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
ids = append(ids, id)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
|
@ -1,382 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type ClusterComputeResource struct {
|
||||
mo.ClusterComputeResource
|
||||
|
||||
ruleKey int32
|
||||
}
|
||||
|
||||
func (c *ClusterComputeResource) RenameTask(req *types.Rename_Task) soap.HasFault {
|
||||
return RenameTask(c, req)
|
||||
}
|
||||
|
||||
type addHost struct {
|
||||
*ClusterComputeResource
|
||||
|
||||
req *types.AddHost_Task
|
||||
}
|
||||
|
||||
func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
spec := add.req.Spec
|
||||
|
||||
if spec.HostName == "" {
|
||||
return nil, &types.NoHost{}
|
||||
}
|
||||
|
||||
host := NewHostSystem(esx.HostSystem)
|
||||
host.configure(spec, add.req.AsConnected)
|
||||
|
||||
cr := add.ClusterComputeResource
|
||||
Map.PutEntity(cr, Map.NewEntity(host))
|
||||
host.Summary.Host = &host.Self
|
||||
|
||||
cr.Host = append(cr.Host, host.Reference())
|
||||
addComputeResource(cr.Summary.GetComputeResourceSummary(), host)
|
||||
host.Network = cr.Network[:1] // VM Network
|
||||
|
||||
return host.Reference(), nil
|
||||
}
|
||||
|
||||
func (c *ClusterComputeResource) AddHostTask(add *types.AddHost_Task) soap.HasFault {
|
||||
return &methods.AddHost_TaskBody{
|
||||
Res: &types.AddHost_TaskResponse{
|
||||
Returnval: NewTask(&addHost{c, add}).Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClusterComputeResource) updateRules(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
|
||||
for _, spec := range cspec.RulesSpec {
|
||||
var i int
|
||||
exists := false
|
||||
|
||||
match := func(info types.BaseClusterRuleInfo) bool {
|
||||
return info.GetClusterRuleInfo().Name == spec.Info.GetClusterRuleInfo().Name
|
||||
}
|
||||
|
||||
if spec.Operation == types.ArrayUpdateOperationRemove {
|
||||
match = func(rule types.BaseClusterRuleInfo) bool {
|
||||
return rule.GetClusterRuleInfo().Key == spec.ArrayUpdateSpec.RemoveKey.(int32)
|
||||
}
|
||||
}
|
||||
|
||||
for i = range cfg.Rule {
|
||||
if match(cfg.Rule[i].GetClusterRuleInfo()) {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch spec.Operation {
|
||||
case types.ArrayUpdateOperationAdd:
|
||||
if exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
info := spec.Info.GetClusterRuleInfo()
|
||||
info.Key = atomic.AddInt32(&c.ruleKey, 1)
|
||||
info.RuleUuid = uuid.New().String()
|
||||
cfg.Rule = append(cfg.Rule, spec.Info)
|
||||
case types.ArrayUpdateOperationEdit:
|
||||
if !exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.Rule[i] = spec.Info
|
||||
case types.ArrayUpdateOperationRemove:
|
||||
if !exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.Rule = append(cfg.Rule[:i], cfg.Rule[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClusterComputeResource) updateGroups(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
|
||||
for _, spec := range cspec.GroupSpec {
|
||||
var i int
|
||||
exists := false
|
||||
|
||||
match := func(info types.BaseClusterGroupInfo) bool {
|
||||
return info.GetClusterGroupInfo().Name == spec.Info.GetClusterGroupInfo().Name
|
||||
}
|
||||
|
||||
if spec.Operation == types.ArrayUpdateOperationRemove {
|
||||
match = func(info types.BaseClusterGroupInfo) bool {
|
||||
return info.GetClusterGroupInfo().Name == spec.ArrayUpdateSpec.RemoveKey.(string)
|
||||
}
|
||||
}
|
||||
|
||||
for i = range cfg.Group {
|
||||
if match(cfg.Group[i].GetClusterGroupInfo()) {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch spec.Operation {
|
||||
case types.ArrayUpdateOperationAdd:
|
||||
if exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.Group = append(cfg.Group, spec.Info)
|
||||
case types.ArrayUpdateOperationEdit:
|
||||
if !exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.Group[i] = spec.Info
|
||||
case types.ArrayUpdateOperationRemove:
|
||||
if !exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.Group = append(cfg.Group[:i], cfg.Group[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClusterComputeResource) updateOverridesDAS(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
|
||||
for _, spec := range cspec.DasVmConfigSpec {
|
||||
var i int
|
||||
var key types.ManagedObjectReference
|
||||
exists := false
|
||||
|
||||
if spec.Operation == types.ArrayUpdateOperationRemove {
|
||||
key = spec.RemoveKey.(types.ManagedObjectReference)
|
||||
} else {
|
||||
key = spec.Info.Key
|
||||
}
|
||||
|
||||
for i = range cfg.DasVmConfig {
|
||||
if cfg.DasVmConfig[i].Key == key {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch spec.Operation {
|
||||
case types.ArrayUpdateOperationAdd:
|
||||
if exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.DasVmConfig = append(cfg.DasVmConfig, *spec.Info)
|
||||
case types.ArrayUpdateOperationEdit:
|
||||
if !exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
src := spec.Info.DasSettings
|
||||
if src == nil {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
dst := cfg.DasVmConfig[i].DasSettings
|
||||
if src.RestartPriority != "" {
|
||||
dst.RestartPriority = src.RestartPriority
|
||||
}
|
||||
if src.RestartPriorityTimeout != 0 {
|
||||
dst.RestartPriorityTimeout = src.RestartPriorityTimeout
|
||||
}
|
||||
case types.ArrayUpdateOperationRemove:
|
||||
if !exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.DasVmConfig = append(cfg.DasVmConfig[:i], cfg.DasVmConfig[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClusterComputeResource) updateOverridesDRS(cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
|
||||
for _, spec := range cspec.DrsVmConfigSpec {
|
||||
var i int
|
||||
var key types.ManagedObjectReference
|
||||
exists := false
|
||||
|
||||
if spec.Operation == types.ArrayUpdateOperationRemove {
|
||||
key = spec.RemoveKey.(types.ManagedObjectReference)
|
||||
} else {
|
||||
key = spec.Info.Key
|
||||
}
|
||||
|
||||
for i = range cfg.DrsVmConfig {
|
||||
if cfg.DrsVmConfig[i].Key == key {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch spec.Operation {
|
||||
case types.ArrayUpdateOperationAdd:
|
||||
if exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.DrsVmConfig = append(cfg.DrsVmConfig, *spec.Info)
|
||||
case types.ArrayUpdateOperationEdit:
|
||||
if !exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
if spec.Info.Enabled != nil {
|
||||
cfg.DrsVmConfig[i].Enabled = spec.Info.Enabled
|
||||
}
|
||||
if spec.Info.Behavior != "" {
|
||||
cfg.DrsVmConfig[i].Behavior = spec.Info.Behavior
|
||||
}
|
||||
case types.ArrayUpdateOperationRemove:
|
||||
if !exists {
|
||||
return new(types.InvalidArgument)
|
||||
}
|
||||
cfg.DrsVmConfig = append(cfg.DrsVmConfig[:i], cfg.DrsVmConfig[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClusterComputeResource) ReconfigureComputeResourceTask(req *types.ReconfigureComputeResource_Task) soap.HasFault {
|
||||
task := CreateTask(c, "reconfigureCluster", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
spec, ok := req.Spec.(*types.ClusterConfigSpecEx)
|
||||
if !ok {
|
||||
return nil, new(types.InvalidArgument)
|
||||
}
|
||||
|
||||
updates := []func(*types.ClusterConfigInfoEx, *types.ClusterConfigSpecEx) types.BaseMethodFault{
|
||||
c.updateRules,
|
||||
c.updateGroups,
|
||||
c.updateOverridesDAS,
|
||||
c.updateOverridesDRS,
|
||||
}
|
||||
|
||||
for _, update := range updates {
|
||||
if err := update(c.ConfigurationEx.(*types.ClusterConfigInfoEx), spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.ReconfigureComputeResource_TaskBody{
|
||||
Res: &types.ReconfigureComputeResource_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClusterComputeResource) PlaceVm(ctx *Context, req *types.PlaceVm) soap.HasFault {
|
||||
body := new(methods.PlaceVmBody)
|
||||
|
||||
if len(c.Host) == 0 {
|
||||
body.Fault_ = Fault("", new(types.InvalidState))
|
||||
return body
|
||||
}
|
||||
|
||||
res := types.ClusterRecommendation{
|
||||
Key: "1",
|
||||
Type: "V1",
|
||||
Time: time.Now(),
|
||||
Rating: 1,
|
||||
Reason: string(types.RecommendationReasonCodeXvmotionPlacement),
|
||||
ReasonText: string(types.RecommendationReasonCodeXvmotionPlacement),
|
||||
Target: &c.Self,
|
||||
}
|
||||
|
||||
hosts := req.PlacementSpec.Hosts
|
||||
if len(hosts) == 0 {
|
||||
hosts = c.Host
|
||||
}
|
||||
|
||||
datastores := req.PlacementSpec.Datastores
|
||||
if len(datastores) == 0 {
|
||||
datastores = c.Datastore
|
||||
}
|
||||
|
||||
spec := &types.VirtualMachineRelocateSpec{
|
||||
Datastore: &datastores[rand.Intn(len(c.Datastore))],
|
||||
Host: &hosts[rand.Intn(len(c.Host))],
|
||||
Pool: c.ResourcePool,
|
||||
}
|
||||
|
||||
switch types.PlacementSpecPlacementType(req.PlacementSpec.PlacementType) {
|
||||
case types.PlacementSpecPlacementTypeClone, types.PlacementSpecPlacementTypeCreate:
|
||||
res.Action = append(res.Action, &types.PlacementAction{
|
||||
Vm: req.PlacementSpec.Vm,
|
||||
TargetHost: spec.Host,
|
||||
RelocateSpec: spec,
|
||||
})
|
||||
default:
|
||||
log.Printf("unsupported placement type: %s", req.PlacementSpec.PlacementType)
|
||||
body.Fault_ = Fault("", new(types.NotSupported))
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.PlaceVmResponse{
|
||||
Returnval: types.PlacementResult{
|
||||
Recommendations: []types.ClusterRecommendation{res},
|
||||
},
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func CreateClusterComputeResource(ctx *Context, f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) {
|
||||
if e := Map.FindByName(name, f.ChildEntity); e != nil {
|
||||
return nil, &types.DuplicateName{
|
||||
Name: e.Entity().Name,
|
||||
Object: e.Reference(),
|
||||
}
|
||||
}
|
||||
|
||||
cluster := &ClusterComputeResource{}
|
||||
cluster.EnvironmentBrowser = newEnvironmentBrowser()
|
||||
cluster.Name = name
|
||||
cluster.Network = Map.getEntityDatacenter(f).defaultNetwork()
|
||||
cluster.Summary = &types.ClusterComputeResourceSummary{
|
||||
UsageSummary: new(types.ClusterUsageSummary),
|
||||
}
|
||||
|
||||
config := &types.ClusterConfigInfoEx{}
|
||||
cluster.ConfigurationEx = config
|
||||
|
||||
config.VmSwapPlacement = string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory)
|
||||
config.DrsConfig.Enabled = types.NewBool(true)
|
||||
|
||||
pool := NewResourcePool()
|
||||
Map.PutEntity(cluster, Map.NewEntity(pool))
|
||||
cluster.ResourcePool = &pool.Self
|
||||
|
||||
folderPutChild(ctx, &f.Folder, cluster)
|
||||
pool.Owner = cluster.Self
|
||||
|
||||
return cluster, nil
|
||||
}
|
|
@ -1,408 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var (
|
||||
shell = "/bin/sh"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if sh, err := exec.LookPath("bash"); err != nil {
|
||||
shell = sh
|
||||
}
|
||||
}
|
||||
|
||||
// container provides methods to manage a container within a simulator VM lifecycle.
|
||||
type container struct {
|
||||
id string
|
||||
name string
|
||||
}
|
||||
|
||||
type networkSettings struct {
|
||||
Gateway string
|
||||
IPAddress string
|
||||
IPPrefixLen int
|
||||
MacAddress string
|
||||
}
|
||||
|
||||
// inspect applies container network settings to vm.Guest properties.
|
||||
func (c *container) inspect(vm *VirtualMachine) error {
|
||||
if c.id == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var objects []struct {
|
||||
NetworkSettings struct {
|
||||
networkSettings
|
||||
Networks map[string]networkSettings
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", "inspect", c.id)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = json.NewDecoder(bytes.NewReader(out)).Decode(&objects); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vm.Config.Annotation = strings.Join(cmd.Args, " ")
|
||||
vm.logPrintf("%s: %s", vm.Config.Annotation, string(out))
|
||||
|
||||
for _, o := range objects {
|
||||
s := o.NetworkSettings.networkSettings
|
||||
|
||||
for _, n := range o.NetworkSettings.Networks {
|
||||
s = n
|
||||
break
|
||||
}
|
||||
|
||||
if s.IPAddress == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
vm.Guest.IpAddress = s.IPAddress
|
||||
vm.Summary.Guest.IpAddress = s.IPAddress
|
||||
|
||||
if len(vm.Guest.Net) != 0 {
|
||||
net := &vm.Guest.Net[0]
|
||||
net.IpAddress = []string{s.IPAddress}
|
||||
net.MacAddress = s.MacAddress
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *container) prepareGuestOperation(vm *VirtualMachine, auth types.BaseGuestAuthentication) types.BaseMethodFault {
|
||||
if c.id == "" {
|
||||
return new(types.GuestOperationsUnavailable)
|
||||
}
|
||||
if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
|
||||
return &types.InvalidPowerState{
|
||||
RequestedState: types.VirtualMachinePowerStatePoweredOn,
|
||||
ExistingState: vm.Runtime.PowerState,
|
||||
}
|
||||
}
|
||||
switch creds := auth.(type) {
|
||||
case *types.NamePasswordAuthentication:
|
||||
if creds.Username == "" || creds.Password == "" {
|
||||
return new(types.InvalidGuestLogin)
|
||||
}
|
||||
default:
|
||||
return new(types.InvalidGuestLogin)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createDMI writes BIOS UUID DMI files to a container volume
|
||||
func (c *container) createDMI(vm *VirtualMachine, name string) error {
|
||||
cmd := exec.Command("docker", "run", "--rm", "-i", "-v", name+":"+"/"+name, "busybox", "tar", "-C", "/"+name, "-xf", "-")
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tw := tar.NewWriter(stdin)
|
||||
|
||||
dmi := []struct {
|
||||
name string
|
||||
val func(uuid.UUID) string
|
||||
}{
|
||||
{"product_uuid", productUUID},
|
||||
{"product_serial", productSerial},
|
||||
}
|
||||
|
||||
for _, file := range dmi {
|
||||
val := file.val(vm.uid)
|
||||
_ = tw.WriteHeader(&tar.Header{
|
||||
Name: file.name,
|
||||
Size: int64(len(val) + 1),
|
||||
Mode: 0444,
|
||||
ModTime: time.Now(),
|
||||
})
|
||||
_, _ = fmt.Fprintln(tw, val)
|
||||
}
|
||||
|
||||
_ = tw.Close()
|
||||
_ = stdin.Close()
|
||||
|
||||
return cmd.Wait()
|
||||
}
|
||||
|
||||
// start runs the container if specified by the RUN.container extraConfig property.
|
||||
func (c *container) start(vm *VirtualMachine) {
|
||||
if c.id != "" {
|
||||
start := "start"
|
||||
if vm.Runtime.PowerState == types.VirtualMachinePowerStateSuspended {
|
||||
start = "unpause"
|
||||
}
|
||||
cmd := exec.Command("docker", start, c.id)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var args []string
|
||||
var env []string
|
||||
|
||||
for _, opt := range vm.Config.ExtraConfig {
|
||||
val := opt.GetOptionValue()
|
||||
if val.Key == "RUN.container" {
|
||||
run := val.Value.(string)
|
||||
err := json.Unmarshal([]byte(run), &args)
|
||||
if err != nil {
|
||||
args = []string{run}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(val.Key, "guestinfo.") {
|
||||
key := strings.Replace(strings.ToUpper(val.Key), ".", "_", -1)
|
||||
env = append(env, "--env", fmt.Sprintf("VMX_%s=%s", key, val.Value.(string)))
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
return
|
||||
}
|
||||
if len(env) != 0 {
|
||||
// Configure env as the data access method for cloud-init-vmware-guestinfo
|
||||
env = append(env, "--env", "VMX_GUESTINFO=true")
|
||||
}
|
||||
|
||||
c.name = fmt.Sprintf("vcsim-%s-%s", vm.Name, vm.uid)
|
||||
run := append([]string{"docker", "run", "-d", "--name", c.name}, env...)
|
||||
|
||||
if err := c.createDMI(vm, c.name); err != nil {
|
||||
log.Printf("%s: %s", vm.Name, err)
|
||||
return
|
||||
}
|
||||
run = append(run, "-v", fmt.Sprintf("%s:%s:ro", c.name, "/sys/class/dmi/id"))
|
||||
|
||||
args = append(run, args...)
|
||||
cmd := exec.Command(shell, "-c", strings.Join(args, " "))
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.id = strings.TrimSpace(string(out))
|
||||
vm.logPrintf("%s %s: %s", cmd.Path, cmd.Args, c.id)
|
||||
|
||||
if err = c.inspect(vm); err != nil {
|
||||
log.Printf("%s inspect %s: %s", vm.Name, c.id, err)
|
||||
}
|
||||
}
|
||||
|
||||
// stop the container (if any) for the given vm.
|
||||
func (c *container) stop(vm *VirtualMachine) {
|
||||
if c.id == "" {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", "stop", c.id)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
|
||||
}
|
||||
}
|
||||
|
||||
// pause the container (if any) for the given vm.
|
||||
func (c *container) pause(vm *VirtualMachine) {
|
||||
if c.id == "" {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", "pause", c.id)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
|
||||
}
|
||||
}
|
||||
|
||||
// remove the container (if any) for the given vm.
|
||||
func (c *container) remove(vm *VirtualMachine) {
|
||||
if c.id == "" {
|
||||
return
|
||||
}
|
||||
|
||||
args := [][]string{
|
||||
[]string{"rm", "-v", "-f", c.id},
|
||||
[]string{"volume", "rm", "-f", c.name},
|
||||
}
|
||||
|
||||
for i := range args {
|
||||
cmd := exec.Command("docker", args[i]...)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
|
||||
}
|
||||
}
|
||||
|
||||
c.id = ""
|
||||
}
|
||||
|
||||
func guestUpload(file string, r *http.Request) error {
|
||||
cmd := exec.Command("docker", "cp", "-", path.Dir(file))
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tw := tar.NewWriter(stdin)
|
||||
_ = tw.WriteHeader(&tar.Header{
|
||||
Name: path.Base(file),
|
||||
Size: r.ContentLength,
|
||||
Mode: 0444,
|
||||
ModTime: time.Now(),
|
||||
})
|
||||
|
||||
_, _ = io.Copy(tw, r.Body)
|
||||
|
||||
_ = tw.Close()
|
||||
_ = stdin.Close()
|
||||
_ = r.Body.Close()
|
||||
|
||||
return cmd.Wait()
|
||||
}
|
||||
|
||||
func guestDownload(file string, w http.ResponseWriter) error {
|
||||
cmd := exec.Command("docker", "cp", file, "-")
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tr := tar.NewReader(stdout)
|
||||
header, err := tr.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(header.Size, 10))
|
||||
_, _ = io.Copy(w, tr)
|
||||
|
||||
_ = stdout.Close()
|
||||
|
||||
return cmd.Wait()
|
||||
}
|
||||
|
||||
const guestPrefix = "/guestFile/"
|
||||
|
||||
// ServeGuest handles container guest file upload/download
|
||||
func ServeGuest(w http.ResponseWriter, r *http.Request) {
|
||||
// Real vCenter form: /guestFile?id=139&token=...
|
||||
// vcsim form: /guestFile/tmp/foo/bar?id=ebc8837b8cb6&token=...
|
||||
|
||||
id := r.URL.Query().Get("id")
|
||||
file := id + ":" + strings.TrimPrefix(r.URL.Path, guestPrefix[:len(guestPrefix)-1])
|
||||
var err error
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodPut:
|
||||
err = guestUpload(file, r)
|
||||
case http.MethodGet:
|
||||
err = guestDownload(file, w)
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%s %s: %s", r.Method, r.URL, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// productSerial returns the uuid in /sys/class/dmi/id/product_serial format
|
||||
func productSerial(id uuid.UUID) string {
|
||||
var dst [len(id)*2 + len(id) - 1]byte
|
||||
|
||||
j := 0
|
||||
for i := 0; i < len(id); i++ {
|
||||
hex.Encode(dst[j:j+2], id[i:i+1])
|
||||
j += 3
|
||||
if j < len(dst) {
|
||||
s := j - 1
|
||||
if s == len(dst)/2 {
|
||||
dst[s] = '-'
|
||||
} else {
|
||||
dst[s] = ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("VMware-%s", string(dst[:]))
|
||||
}
|
||||
|
||||
// productUUID returns the uuid in /sys/class/dmi/id/product_uuid format
|
||||
func productUUID(id uuid.UUID) string {
|
||||
var dst [36]byte
|
||||
|
||||
hex.Encode(dst[0:2], id[3:4])
|
||||
hex.Encode(dst[2:4], id[2:3])
|
||||
hex.Encode(dst[4:6], id[1:2])
|
||||
hex.Encode(dst[6:8], id[0:1])
|
||||
dst[8] = '-'
|
||||
hex.Encode(dst[9:11], id[5:6])
|
||||
hex.Encode(dst[11:13], id[4:5])
|
||||
dst[13] = '-'
|
||||
hex.Encode(dst[14:16], id[7:8])
|
||||
hex.Encode(dst[16:18], id[6:7])
|
||||
dst[18] = '-'
|
||||
hex.Encode(dst[19:23], id[8:10])
|
||||
dst[23] = '-'
|
||||
hex.Encode(dst[24:], id[10:])
|
||||
|
||||
return strings.ToUpper(string(dst[:]))
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type CustomFieldsManager struct {
|
||||
mo.CustomFieldsManager
|
||||
|
||||
nextKey int32
|
||||
}
|
||||
|
||||
// Iterates through all entities of passed field type;
|
||||
// Removes found field from their custom field properties.
|
||||
func entitiesFieldRemove(field types.CustomFieldDef) {
|
||||
entities := Map.All(field.ManagedObjectType)
|
||||
for _, e := range entities {
|
||||
entity := e.Entity()
|
||||
Map.WithLock(entity, func() {
|
||||
aFields := entity.AvailableField
|
||||
for i, aField := range aFields {
|
||||
if aField.Key == field.Key {
|
||||
entity.AvailableField = append(aFields[:i], aFields[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
values := e.Entity().Value
|
||||
for i, value := range values {
|
||||
if value.(*types.CustomFieldStringValue).Key == field.Key {
|
||||
entity.Value = append(values[:i], values[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cValues := e.Entity().CustomValue
|
||||
for i, cValue := range cValues {
|
||||
if cValue.(*types.CustomFieldStringValue).Key == field.Key {
|
||||
entity.CustomValue = append(cValues[:i], cValues[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Iterates through all entities of passed field type;
|
||||
// Renames found field in entity's AvailableField property.
|
||||
func entitiesFieldRename(field types.CustomFieldDef) {
|
||||
entities := Map.All(field.ManagedObjectType)
|
||||
for _, e := range entities {
|
||||
entity := e.Entity()
|
||||
Map.WithLock(entity, func() {
|
||||
aFields := entity.AvailableField
|
||||
for i, aField := range aFields {
|
||||
if aField.Key == field.Key {
|
||||
aFields[i].Name = field.Name
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CustomFieldsManager) findByNameType(name, moType string) (int, *types.CustomFieldDef) {
|
||||
for i, field := range c.Field {
|
||||
if (field.ManagedObjectType == "" || field.ManagedObjectType == moType || moType == "") &&
|
||||
field.Name == name {
|
||||
return i, &c.Field[i]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (c *CustomFieldsManager) findByKey(key int32) (int, *types.CustomFieldDef) {
|
||||
for i, field := range c.Field {
|
||||
if field.Key == key {
|
||||
return i, &c.Field[i]
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (c *CustomFieldsManager) AddCustomFieldDef(req *types.AddCustomFieldDef) soap.HasFault {
|
||||
body := &methods.AddCustomFieldDefBody{}
|
||||
|
||||
_, field := c.findByNameType(req.Name, req.MoType)
|
||||
if field != nil {
|
||||
body.Fault_ = Fault("", &types.DuplicateName{
|
||||
Name: req.Name,
|
||||
Object: c.Reference(),
|
||||
})
|
||||
return body
|
||||
}
|
||||
|
||||
def := types.CustomFieldDef{
|
||||
Key: c.nextKey,
|
||||
Name: req.Name,
|
||||
ManagedObjectType: req.MoType,
|
||||
Type: req.MoType,
|
||||
FieldDefPrivileges: req.FieldDefPolicy,
|
||||
FieldInstancePrivileges: req.FieldPolicy,
|
||||
}
|
||||
|
||||
entities := Map.All(req.MoType)
|
||||
for _, e := range entities {
|
||||
entity := e.Entity()
|
||||
Map.WithLock(entity, func() {
|
||||
entity.AvailableField = append(entity.AvailableField, def)
|
||||
})
|
||||
}
|
||||
|
||||
c.Field = append(c.Field, def)
|
||||
c.nextKey++
|
||||
|
||||
body.Res = &types.AddCustomFieldDefResponse{
|
||||
Returnval: def,
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func (c *CustomFieldsManager) RemoveCustomFieldDef(req *types.RemoveCustomFieldDef) soap.HasFault {
|
||||
body := &methods.RemoveCustomFieldDefBody{}
|
||||
|
||||
i, field := c.findByKey(req.Key)
|
||||
if field == nil {
|
||||
body.Fault_ = Fault("", &types.NotFound{})
|
||||
return body
|
||||
}
|
||||
|
||||
entitiesFieldRemove(*field)
|
||||
|
||||
c.Field = append(c.Field[:i], c.Field[i+1:]...)
|
||||
|
||||
body.Res = &types.RemoveCustomFieldDefResponse{}
|
||||
return body
|
||||
}
|
||||
|
||||
func (c *CustomFieldsManager) RenameCustomFieldDef(req *types.RenameCustomFieldDef) soap.HasFault {
|
||||
body := &methods.RenameCustomFieldDefBody{}
|
||||
|
||||
_, field := c.findByKey(req.Key)
|
||||
if field == nil {
|
||||
body.Fault_ = Fault("", &types.NotFound{})
|
||||
return body
|
||||
}
|
||||
|
||||
field.Name = req.Name
|
||||
|
||||
entitiesFieldRename(*field)
|
||||
|
||||
body.Res = &types.RenameCustomFieldDefResponse{}
|
||||
return body
|
||||
}
|
||||
|
||||
func (c *CustomFieldsManager) SetField(ctx *Context, req *types.SetField) soap.HasFault {
|
||||
body := &methods.SetFieldBody{}
|
||||
|
||||
_, field := c.findByKey(req.Key)
|
||||
if field == nil {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "key"})
|
||||
return body
|
||||
}
|
||||
|
||||
newValue := &types.CustomFieldStringValue{
|
||||
CustomFieldValue: types.CustomFieldValue{Key: req.Key},
|
||||
Value: req.Value,
|
||||
}
|
||||
|
||||
entity := Map.Get(req.Entity).(mo.Entity).Entity()
|
||||
ctx.WithLock(entity, func() {
|
||||
entity.CustomValue = append(entity.CustomValue, newValue)
|
||||
entity.Value = append(entity.Value, newValue)
|
||||
})
|
||||
|
||||
body.Res = &types.SetFieldResponse{}
|
||||
return body
|
||||
}
|
|
@ -1,349 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2019 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var DefaultCustomizationSpec = []types.CustomizationSpecItem{
|
||||
types.CustomizationSpecItem{
|
||||
Info: types.CustomizationSpecInfo{
|
||||
Name: "vcsim-linux",
|
||||
Description: "",
|
||||
Type: "Linux",
|
||||
ChangeVersion: "1569965707",
|
||||
LastUpdateTime: types.NewTime(time.Now()),
|
||||
},
|
||||
Spec: types.CustomizationSpec{
|
||||
Options: &types.CustomizationLinuxOptions{},
|
||||
Identity: &types.CustomizationLinuxPrep{
|
||||
CustomizationIdentitySettings: types.CustomizationIdentitySettings{},
|
||||
HostName: &types.CustomizationVirtualMachineName{},
|
||||
Domain: "eng.vmware.com",
|
||||
TimeZone: "Pacific/Apia",
|
||||
HwClockUTC: types.NewBool(true),
|
||||
},
|
||||
GlobalIPSettings: types.CustomizationGlobalIPSettings{
|
||||
DnsSuffixList: nil,
|
||||
DnsServerList: []string{"127.0.1.1"},
|
||||
},
|
||||
NicSettingMap: []types.CustomizationAdapterMapping{
|
||||
{
|
||||
MacAddress: "",
|
||||
Adapter: types.CustomizationIPSettings{
|
||||
Ip: &types.CustomizationDhcpIpGenerator{},
|
||||
SubnetMask: "",
|
||||
Gateway: nil,
|
||||
IpV6Spec: (*types.CustomizationIPSettingsIpV6AddressSpec)(nil),
|
||||
DnsServerList: nil,
|
||||
DnsDomain: "",
|
||||
PrimaryWINS: "",
|
||||
SecondaryWINS: "",
|
||||
NetBIOS: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
EncryptionKey: nil,
|
||||
},
|
||||
},
|
||||
types.CustomizationSpecItem{
|
||||
Info: types.CustomizationSpecInfo{
|
||||
Name: "vcsim-linux-static",
|
||||
Description: "",
|
||||
Type: "Linux",
|
||||
ChangeVersion: "1569969598",
|
||||
LastUpdateTime: types.NewTime(time.Now()),
|
||||
},
|
||||
Spec: types.CustomizationSpec{
|
||||
Options: &types.CustomizationLinuxOptions{},
|
||||
Identity: &types.CustomizationLinuxPrep{
|
||||
CustomizationIdentitySettings: types.CustomizationIdentitySettings{},
|
||||
HostName: &types.CustomizationPrefixName{
|
||||
CustomizationName: types.CustomizationName{},
|
||||
Base: "vcsim",
|
||||
},
|
||||
Domain: "eng.vmware.com",
|
||||
TimeZone: "Africa/Cairo",
|
||||
HwClockUTC: types.NewBool(true),
|
||||
},
|
||||
GlobalIPSettings: types.CustomizationGlobalIPSettings{
|
||||
DnsSuffixList: nil,
|
||||
DnsServerList: []string{"127.0.1.1"},
|
||||
},
|
||||
NicSettingMap: []types.CustomizationAdapterMapping{
|
||||
{
|
||||
MacAddress: "",
|
||||
Adapter: types.CustomizationIPSettings{
|
||||
Ip: &types.CustomizationUnknownIpGenerator{},
|
||||
SubnetMask: "255.255.255.0",
|
||||
Gateway: []string{"10.0.0.1"},
|
||||
IpV6Spec: (*types.CustomizationIPSettingsIpV6AddressSpec)(nil),
|
||||
DnsServerList: nil,
|
||||
DnsDomain: "",
|
||||
PrimaryWINS: "",
|
||||
SecondaryWINS: "",
|
||||
NetBIOS: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
EncryptionKey: nil,
|
||||
},
|
||||
},
|
||||
types.CustomizationSpecItem{
|
||||
Info: types.CustomizationSpecInfo{
|
||||
Name: "vcsim-windows-static",
|
||||
Description: "",
|
||||
Type: "Windows",
|
||||
ChangeVersion: "1569978029",
|
||||
LastUpdateTime: types.NewTime(time.Now()),
|
||||
},
|
||||
Spec: types.CustomizationSpec{
|
||||
Options: &types.CustomizationWinOptions{
|
||||
CustomizationOptions: types.CustomizationOptions{},
|
||||
ChangeSID: true,
|
||||
DeleteAccounts: false,
|
||||
Reboot: "",
|
||||
},
|
||||
Identity: &types.CustomizationSysprep{
|
||||
CustomizationIdentitySettings: types.CustomizationIdentitySettings{},
|
||||
GuiUnattended: types.CustomizationGuiUnattended{
|
||||
Password: (*types.CustomizationPassword)(nil),
|
||||
TimeZone: 2,
|
||||
AutoLogon: false,
|
||||
AutoLogonCount: 1,
|
||||
},
|
||||
UserData: types.CustomizationUserData{
|
||||
FullName: "vcsim",
|
||||
OrgName: "VMware",
|
||||
ComputerName: &types.CustomizationVirtualMachineName{},
|
||||
ProductId: "",
|
||||
},
|
||||
GuiRunOnce: (*types.CustomizationGuiRunOnce)(nil),
|
||||
Identification: types.CustomizationIdentification{
|
||||
JoinWorkgroup: "WORKGROUP",
|
||||
JoinDomain: "",
|
||||
DomainAdmin: "",
|
||||
DomainAdminPassword: (*types.CustomizationPassword)(nil),
|
||||
},
|
||||
LicenseFilePrintData: &types.CustomizationLicenseFilePrintData{
|
||||
AutoMode: "perServer",
|
||||
AutoUsers: 5,
|
||||
},
|
||||
},
|
||||
GlobalIPSettings: types.CustomizationGlobalIPSettings{},
|
||||
NicSettingMap: []types.CustomizationAdapterMapping{
|
||||
{
|
||||
MacAddress: "",
|
||||
Adapter: types.CustomizationIPSettings{
|
||||
Ip: &types.CustomizationUnknownIpGenerator{},
|
||||
SubnetMask: "255.255.255.0",
|
||||
Gateway: []string{"10.0.0.1"},
|
||||
IpV6Spec: (*types.CustomizationIPSettingsIpV6AddressSpec)(nil),
|
||||
DnsServerList: nil,
|
||||
DnsDomain: "",
|
||||
PrimaryWINS: "",
|
||||
SecondaryWINS: "",
|
||||
NetBIOS: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
EncryptionKey: []uint8{0x30},
|
||||
},
|
||||
},
|
||||
types.CustomizationSpecItem{
|
||||
Info: types.CustomizationSpecInfo{
|
||||
Name: "vcsim-windows-domain",
|
||||
Description: "",
|
||||
Type: "Windows",
|
||||
ChangeVersion: "1569970234",
|
||||
LastUpdateTime: types.NewTime(time.Now()),
|
||||
},
|
||||
Spec: types.CustomizationSpec{
|
||||
Options: &types.CustomizationWinOptions{
|
||||
CustomizationOptions: types.CustomizationOptions{},
|
||||
ChangeSID: true,
|
||||
DeleteAccounts: false,
|
||||
Reboot: "",
|
||||
},
|
||||
Identity: &types.CustomizationSysprep{
|
||||
CustomizationIdentitySettings: types.CustomizationIdentitySettings{},
|
||||
GuiUnattended: types.CustomizationGuiUnattended{
|
||||
Password: &types.CustomizationPassword{
|
||||
Value: "3Gs...==",
|
||||
PlainText: false,
|
||||
},
|
||||
TimeZone: 15,
|
||||
AutoLogon: false,
|
||||
AutoLogonCount: 1,
|
||||
},
|
||||
UserData: types.CustomizationUserData{
|
||||
FullName: "dougm",
|
||||
OrgName: "VMware",
|
||||
ComputerName: &types.CustomizationVirtualMachineName{},
|
||||
ProductId: "",
|
||||
},
|
||||
GuiRunOnce: (*types.CustomizationGuiRunOnce)(nil),
|
||||
Identification: types.CustomizationIdentification{
|
||||
JoinWorkgroup: "",
|
||||
JoinDomain: "DOMAIN",
|
||||
DomainAdmin: "vcsim",
|
||||
DomainAdminPassword: &types.CustomizationPassword{
|
||||
Value: "H3g...==",
|
||||
PlainText: false,
|
||||
},
|
||||
},
|
||||
LicenseFilePrintData: &types.CustomizationLicenseFilePrintData{
|
||||
AutoMode: "perServer",
|
||||
AutoUsers: 5,
|
||||
},
|
||||
},
|
||||
GlobalIPSettings: types.CustomizationGlobalIPSettings{},
|
||||
NicSettingMap: []types.CustomizationAdapterMapping{
|
||||
{
|
||||
MacAddress: "",
|
||||
Adapter: types.CustomizationIPSettings{
|
||||
Ip: &types.CustomizationUnknownIpGenerator{},
|
||||
SubnetMask: "255.255.255.0",
|
||||
Gateway: []string{"10.0.0.1"},
|
||||
IpV6Spec: (*types.CustomizationIPSettingsIpV6AddressSpec)(nil),
|
||||
DnsServerList: nil,
|
||||
DnsDomain: "",
|
||||
PrimaryWINS: "",
|
||||
SecondaryWINS: "",
|
||||
NetBIOS: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
EncryptionKey: []uint8{0x30},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type CustomizationSpecManager struct {
|
||||
mo.CustomizationSpecManager
|
||||
|
||||
items []types.CustomizationSpecItem
|
||||
}
|
||||
|
||||
func (m *CustomizationSpecManager) init(r *Registry) {
|
||||
m.items = DefaultCustomizationSpec
|
||||
}
|
||||
|
||||
var customizeNameCounter uint64
|
||||
|
||||
func customizeName(vm *VirtualMachine, base types.BaseCustomizationName) string {
|
||||
n := atomic.AddUint64(&customizeNameCounter, 1)
|
||||
|
||||
switch name := base.(type) {
|
||||
case *types.CustomizationPrefixName:
|
||||
return fmt.Sprintf("%s-%d", name.Base, n)
|
||||
case *types.CustomizationCustomName:
|
||||
return fmt.Sprintf("%s-%d", name.Argument, n)
|
||||
case *types.CustomizationFixedName:
|
||||
return name.Name
|
||||
case *types.CustomizationUnknownName:
|
||||
return ""
|
||||
case *types.CustomizationVirtualMachineName:
|
||||
return fmt.Sprintf("%s-%d", vm.Name, n)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CustomizationSpecManager) DoesCustomizationSpecExist(ctx *Context, req *types.DoesCustomizationSpecExist) soap.HasFault {
|
||||
exists := false
|
||||
|
||||
for _, item := range m.items {
|
||||
if item.Info.Name == req.Name {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &methods.DoesCustomizationSpecExistBody{
|
||||
Res: &types.DoesCustomizationSpecExistResponse{
|
||||
Returnval: exists,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CustomizationSpecManager) GetCustomizationSpec(ctx *Context, req *types.GetCustomizationSpec) soap.HasFault {
|
||||
body := new(methods.GetCustomizationSpecBody)
|
||||
|
||||
for _, item := range m.items {
|
||||
if item.Info.Name == req.Name {
|
||||
body.Res = &types.GetCustomizationSpecResponse{
|
||||
Returnval: item,
|
||||
}
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
body.Fault_ = Fault("", new(types.NotFound))
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *CustomizationSpecManager) CreateCustomizationSpec(ctx *Context, req *types.CreateCustomizationSpec) soap.HasFault {
|
||||
body := new(methods.CreateCustomizationSpecBody)
|
||||
|
||||
for _, item := range m.items {
|
||||
if item.Info.Name == req.Item.Info.Name {
|
||||
body.Fault_ = Fault("", &types.AlreadyExists{Name: req.Item.Info.Name})
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
m.items = append(m.items, req.Item)
|
||||
body.Res = new(types.CreateCustomizationSpecResponse)
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *CustomizationSpecManager) OverwriteCustomizationSpec(ctx *Context, req *types.OverwriteCustomizationSpec) soap.HasFault {
|
||||
body := new(methods.OverwriteCustomizationSpecBody)
|
||||
|
||||
for i, item := range m.items {
|
||||
if item.Info.Name == req.Item.Info.Name {
|
||||
m.items[i] = req.Item
|
||||
body.Res = new(types.OverwriteCustomizationSpecResponse)
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
body.Fault_ = Fault("", new(types.NotFound))
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *CustomizationSpecManager) Get() mo.Reference {
|
||||
clone := *m
|
||||
|
||||
for i := range clone.items {
|
||||
clone.Info = append(clone.Info, clone.items[i].Info)
|
||||
}
|
||||
|
||||
return &clone
|
||||
}
|
|
@ -1,198 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type Datacenter struct {
|
||||
mo.Datacenter
|
||||
|
||||
isESX bool
|
||||
}
|
||||
|
||||
// NewDatacenter creates a Datacenter and its child folders.
|
||||
func NewDatacenter(ctx *Context, f *mo.Folder) *Datacenter {
|
||||
dc := &Datacenter{
|
||||
isESX: f.Self == esx.RootFolder.Self,
|
||||
}
|
||||
|
||||
if dc.isESX {
|
||||
dc.Datacenter = esx.Datacenter
|
||||
}
|
||||
|
||||
folderPutChild(ctx, f, dc)
|
||||
|
||||
dc.createFolders(ctx)
|
||||
|
||||
return dc
|
||||
}
|
||||
|
||||
func (dc *Datacenter) RenameTask(r *types.Rename_Task) soap.HasFault {
|
||||
return RenameTask(dc, r)
|
||||
}
|
||||
|
||||
// Create Datacenter Folders.
|
||||
// Every Datacenter has 4 inventory Folders: Vm, Host, Datastore and Network.
|
||||
// The ESX folder child types are limited to 1 type.
|
||||
// The VC folders have additional child types, including nested folders.
|
||||
func (dc *Datacenter) createFolders(ctx *Context) {
|
||||
folders := []struct {
|
||||
ref *types.ManagedObjectReference
|
||||
name string
|
||||
types []string
|
||||
}{
|
||||
{&dc.VmFolder, "vm", []string{"VirtualMachine", "VirtualApp", "Folder"}},
|
||||
{&dc.HostFolder, "host", []string{"ComputeResource", "Folder"}},
|
||||
{&dc.DatastoreFolder, "datastore", []string{"Datastore", "StoragePod", "Folder"}},
|
||||
{&dc.NetworkFolder, "network", []string{"Network", "DistributedVirtualSwitch", "Folder"}},
|
||||
}
|
||||
|
||||
for _, f := range folders {
|
||||
folder := &Folder{}
|
||||
folder.Name = f.name
|
||||
|
||||
if dc.isESX {
|
||||
folder.ChildType = f.types[:1]
|
||||
folder.Self = *f.ref
|
||||
Map.PutEntity(dc, folder)
|
||||
} else {
|
||||
folder.ChildType = f.types
|
||||
e := Map.PutEntity(dc, folder)
|
||||
|
||||
// propagate the generated morefs to Datacenter
|
||||
ref := e.Reference()
|
||||
f.ref.Type = ref.Type
|
||||
f.ref.Value = ref.Value
|
||||
}
|
||||
}
|
||||
|
||||
net := Map.Get(dc.NetworkFolder).(*Folder)
|
||||
|
||||
for _, ref := range esx.Datacenter.Network {
|
||||
// Add VM Network by default to each Datacenter
|
||||
network := &mo.Network{}
|
||||
network.Self = ref
|
||||
network.Name = strings.Split(ref.Value, "-")[1]
|
||||
network.Entity().Name = network.Name
|
||||
if !dc.isESX {
|
||||
network.Self.Value = "" // we want a different moid per-DC
|
||||
}
|
||||
|
||||
folderPutChild(ctx, &net.Folder, network)
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *Datacenter) defaultNetwork() []types.ManagedObjectReference {
|
||||
return dc.Network[:1] // VM Network
|
||||
}
|
||||
|
||||
// folder returns the Datacenter folder that can contain the given object type
|
||||
func (dc *Datacenter) folder(obj mo.Entity) *mo.Folder {
|
||||
folders := []types.ManagedObjectReference{
|
||||
dc.VmFolder,
|
||||
dc.HostFolder,
|
||||
dc.DatastoreFolder,
|
||||
dc.NetworkFolder,
|
||||
}
|
||||
otype := getManagedObject(obj).Type()
|
||||
rtype := obj.Reference().Type
|
||||
|
||||
for i := range folders {
|
||||
folder, _ := asFolderMO(Map.Get(folders[i]))
|
||||
for _, kind := range folder.ChildType {
|
||||
if rtype == kind {
|
||||
return folder
|
||||
}
|
||||
if f, ok := otype.FieldByName(kind); ok && f.Anonymous {
|
||||
return folder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Panicf("failed to find folder for type=%s", rtype)
|
||||
return nil
|
||||
}
|
||||
|
||||
func datacenterEventArgument(obj mo.Entity) *types.DatacenterEventArgument {
|
||||
dc, ok := obj.(*Datacenter)
|
||||
if !ok {
|
||||
dc = Map.getEntityDatacenter(obj)
|
||||
}
|
||||
return &types.DatacenterEventArgument{
|
||||
Datacenter: dc.Self,
|
||||
EntityEventArgument: types.EntityEventArgument{Name: dc.Name},
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *Datacenter) PowerOnMultiVMTask(ctx *Context, req *types.PowerOnMultiVM_Task) soap.HasFault {
|
||||
task := CreateTask(dc, "powerOnMultiVM", func(_ *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
if dc.isESX {
|
||||
return nil, new(types.NotImplemented)
|
||||
}
|
||||
|
||||
for _, ref := range req.Vm {
|
||||
vm := Map.Get(ref).(*VirtualMachine)
|
||||
Map.WithLock(vm, func() {
|
||||
vm.PowerOnVMTask(ctx, &types.PowerOnVM_Task{})
|
||||
})
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.PowerOnMultiVM_TaskBody{
|
||||
Res: &types.PowerOnMultiVM_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Datacenter) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
|
||||
task := CreateTask(d, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
folders := []types.ManagedObjectReference{
|
||||
d.VmFolder,
|
||||
d.HostFolder,
|
||||
}
|
||||
|
||||
for _, ref := range folders {
|
||||
f, _ := asFolderMO(Map.Get(ref))
|
||||
if len(f.ChildEntity) != 0 {
|
||||
return nil, &types.ResourceInUse{}
|
||||
}
|
||||
}
|
||||
|
||||
p, _ := asFolderMO(Map.Get(*d.Parent))
|
||||
folderRemoveChild(ctx, p, d.Self)
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.Destroy_TaskBody{
|
||||
Res: &types.Destroy_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type Datastore struct {
|
||||
mo.Datastore
|
||||
}
|
||||
|
||||
func parseDatastorePath(dsPath string) (*object.DatastorePath, types.BaseMethodFault) {
|
||||
var p object.DatastorePath
|
||||
|
||||
if p.FromString(dsPath) {
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
return nil, &types.InvalidDatastorePath{DatastorePath: dsPath}
|
||||
}
|
||||
|
||||
func (ds *Datastore) RefreshDatastore(*types.RefreshDatastore) soap.HasFault {
|
||||
r := &methods.RefreshDatastoreBody{}
|
||||
|
||||
err := ds.stat()
|
||||
if err != nil {
|
||||
r.Fault_ = Fault(err.Error(), &types.HostConfigFault{})
|
||||
return r
|
||||
}
|
||||
|
||||
info := ds.Info.GetDatastoreInfo()
|
||||
|
||||
now := time.Now()
|
||||
|
||||
info.Timestamp = &now
|
||||
info.MaxMemoryFileSize = info.FreeSpace
|
||||
info.MaxFileSize = info.FreeSpace
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (ds *Datastore) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
|
||||
task := CreateTask(ds, "destroy", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
if len(ds.Vm) != 0 {
|
||||
return nil, &types.ResourceInUse{
|
||||
Type: ds.Self.Type,
|
||||
Name: ds.Name,
|
||||
}
|
||||
}
|
||||
|
||||
for _, mount := range ds.Host {
|
||||
host := Map.Get(mount.Key).(*HostSystem)
|
||||
Map.RemoveReference(host, &host.Datastore, ds.Self)
|
||||
parent := hostParent(&host.HostSystem)
|
||||
Map.RemoveReference(parent, &parent.Datastore, ds.Self)
|
||||
}
|
||||
|
||||
p, _ := asFolderMO(Map.Get(*ds.Parent))
|
||||
folderRemoveChild(ctx, p, ds.Self)
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.Destroy_TaskBody{
|
||||
Res: &types.Destroy_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package simulator is a mock framework for the vSphere API.
|
||||
|
||||
See also: https://github.com/vmware/govmomi/blob/master/vcsim/README.md
|
||||
*/
|
||||
package simulator
|
|
@ -1,281 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type DistributedVirtualSwitch struct {
|
||||
mo.DistributedVirtualSwitch
|
||||
}
|
||||
|
||||
func (s *DistributedVirtualSwitch) AddDVPortgroupTask(ctx *Context, c *types.AddDVPortgroup_Task) soap.HasFault {
|
||||
task := CreateTask(s, "addDVPortgroup", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
f := Map.getEntityParent(s, "Folder").(*Folder)
|
||||
|
||||
portgroups := s.Portgroup
|
||||
portgroupNames := s.Summary.PortgroupName
|
||||
|
||||
for _, spec := range c.Spec {
|
||||
pg := &DistributedVirtualPortgroup{}
|
||||
pg.Name = spec.Name
|
||||
pg.Entity().Name = pg.Name
|
||||
|
||||
// Standard AddDVPortgroupTask() doesn't allow duplicate names, but NSX 3.0 does create some DVPGs with the same name.
|
||||
// Allow duplicate names using this prefix so we can reproduce and test this condition.
|
||||
if !strings.HasPrefix(pg.Name, "NSX-") {
|
||||
if obj := Map.FindByName(pg.Name, f.ChildEntity); obj != nil {
|
||||
return nil, &types.DuplicateName{
|
||||
Name: pg.Name,
|
||||
Object: obj.Reference(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
folderPutChild(ctx, &f.Folder, pg)
|
||||
|
||||
pg.Key = pg.Self.Value
|
||||
pg.Config = types.DVPortgroupConfigInfo{
|
||||
Key: pg.Key,
|
||||
Name: pg.Name,
|
||||
NumPorts: spec.NumPorts,
|
||||
DistributedVirtualSwitch: &s.Self,
|
||||
DefaultPortConfig: spec.DefaultPortConfig,
|
||||
Description: spec.Description,
|
||||
Type: spec.Type,
|
||||
Policy: spec.Policy,
|
||||
PortNameFormat: spec.PortNameFormat,
|
||||
Scope: spec.Scope,
|
||||
VendorSpecificConfig: spec.VendorSpecificConfig,
|
||||
ConfigVersion: spec.ConfigVersion,
|
||||
AutoExpand: spec.AutoExpand,
|
||||
VmVnicNetworkResourcePoolKey: spec.VmVnicNetworkResourcePoolKey,
|
||||
LogicalSwitchUuid: spec.LogicalSwitchUuid,
|
||||
BackingType: spec.BackingType,
|
||||
}
|
||||
|
||||
if pg.Config.LogicalSwitchUuid != "" {
|
||||
if pg.Config.BackingType == "" {
|
||||
pg.Config.BackingType = "nsx"
|
||||
}
|
||||
}
|
||||
|
||||
if pg.Config.DefaultPortConfig == nil {
|
||||
pg.Config.DefaultPortConfig = &types.VMwareDVSPortSetting{
|
||||
Vlan: new(types.VmwareDistributedVirtualSwitchVlanIdSpec),
|
||||
UplinkTeamingPolicy: &types.VmwareUplinkPortTeamingPolicy{
|
||||
Policy: &types.StringPolicy{
|
||||
Value: "loadbalance_srcid",
|
||||
},
|
||||
ReversePolicy: &types.BoolPolicy{
|
||||
Value: types.NewBool(true),
|
||||
},
|
||||
NotifySwitches: &types.BoolPolicy{
|
||||
Value: types.NewBool(true),
|
||||
},
|
||||
RollingOrder: &types.BoolPolicy{
|
||||
Value: types.NewBool(true),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if pg.Config.Policy == nil {
|
||||
pg.Config.Policy = &types.VMwareDVSPortgroupPolicy{
|
||||
DVPortgroupPolicy: types.DVPortgroupPolicy{
|
||||
BlockOverrideAllowed: true,
|
||||
ShapingOverrideAllowed: false,
|
||||
VendorConfigOverrideAllowed: false,
|
||||
LivePortMovingAllowed: false,
|
||||
PortConfigResetAtDisconnect: true,
|
||||
NetworkResourcePoolOverrideAllowed: types.NewBool(false),
|
||||
TrafficFilterOverrideAllowed: types.NewBool(false),
|
||||
},
|
||||
VlanOverrideAllowed: false,
|
||||
UplinkTeamingOverrideAllowed: false,
|
||||
SecurityPolicyOverrideAllowed: false,
|
||||
IpfixOverrideAllowed: types.NewBool(false),
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < int(spec.NumPorts); i++ {
|
||||
pg.PortKeys = append(pg.PortKeys, strconv.Itoa(i))
|
||||
}
|
||||
|
||||
portgroups = append(portgroups, pg.Self)
|
||||
portgroupNames = append(portgroupNames, pg.Name)
|
||||
|
||||
for _, h := range s.Summary.HostMember {
|
||||
pg.Host = append(pg.Host, h)
|
||||
|
||||
host := Map.Get(h).(*HostSystem)
|
||||
Map.AppendReference(host, &host.Network, pg.Reference())
|
||||
|
||||
parent := Map.Get(*host.HostSystem.Parent)
|
||||
computeNetworks := append(hostParent(&host.HostSystem).Network, pg.Reference())
|
||||
Map.Update(parent, []types.PropertyChange{
|
||||
{Name: "network", Val: computeNetworks},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Map.Update(s, []types.PropertyChange{
|
||||
{Name: "portgroup", Val: portgroups},
|
||||
{Name: "summary.portgroupName", Val: portgroupNames},
|
||||
})
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.AddDVPortgroup_TaskBody{
|
||||
Res: &types.AddDVPortgroup_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_Task) soap.HasFault {
|
||||
task := CreateTask(s, "reconfigureDvs", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
spec := req.Spec.GetDVSConfigSpec()
|
||||
|
||||
members := s.Summary.HostMember
|
||||
|
||||
for _, member := range spec.Host {
|
||||
h := Map.Get(member.Host)
|
||||
if h == nil {
|
||||
return nil, &types.ManagedObjectNotFound{Obj: member.Host}
|
||||
}
|
||||
|
||||
host := h.(*HostSystem)
|
||||
|
||||
switch types.ConfigSpecOperation(member.Operation) {
|
||||
case types.ConfigSpecOperationAdd:
|
||||
if FindReference(s.Summary.HostMember, member.Host) != nil {
|
||||
return nil, &types.AlreadyExists{Name: host.Name}
|
||||
}
|
||||
|
||||
hostNetworks := append(host.Network, s.Portgroup...)
|
||||
Map.Update(host, []types.PropertyChange{
|
||||
{Name: "network", Val: hostNetworks},
|
||||
})
|
||||
members = append(members, member.Host)
|
||||
parent := Map.Get(*host.HostSystem.Parent)
|
||||
|
||||
var pgs []types.ManagedObjectReference
|
||||
for _, ref := range s.Portgroup {
|
||||
pg := Map.Get(ref).(*DistributedVirtualPortgroup)
|
||||
pgs = append(pgs, ref)
|
||||
|
||||
pgHosts := append(pg.Host, member.Host)
|
||||
Map.Update(pg, []types.PropertyChange{
|
||||
{Name: "host", Val: pgHosts},
|
||||
})
|
||||
|
||||
cr := hostParent(&host.HostSystem)
|
||||
if FindReference(cr.Network, ref) == nil {
|
||||
computeNetworks := append(cr.Network, ref)
|
||||
Map.Update(parent, []types.PropertyChange{
|
||||
{Name: "network", Val: computeNetworks},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
case types.ConfigSpecOperationRemove:
|
||||
for _, ref := range host.Vm {
|
||||
vm := Map.Get(ref).(*VirtualMachine)
|
||||
if pg := FindReference(vm.Network, s.Portgroup...); pg != nil {
|
||||
return nil, &types.ResourceInUse{
|
||||
Type: pg.Type,
|
||||
Name: pg.Value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RemoveReference(&members, member.Host)
|
||||
case types.ConfigSpecOperationEdit:
|
||||
return nil, &types.NotSupported{}
|
||||
}
|
||||
}
|
||||
|
||||
Map.Update(s, []types.PropertyChange{
|
||||
{Name: "summary.hostMember", Val: members},
|
||||
})
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.ReconfigureDvs_TaskBody{
|
||||
Res: &types.ReconfigureDvs_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DistributedVirtualSwitch) FetchDVPorts(req *types.FetchDVPorts) soap.HasFault {
|
||||
body := &methods.FetchDVPortsBody{}
|
||||
body.Res = &types.FetchDVPortsResponse{
|
||||
Returnval: s.dvPortgroups(req.Criteria),
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *DistributedVirtualSwitch) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
|
||||
task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
f := Map.getEntityParent(s, "Folder").(*Folder)
|
||||
folderRemoveChild(ctx, &f.Folder, s.Reference())
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.Destroy_TaskBody{
|
||||
Res: &types.Destroy_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DistributedVirtualSwitch) dvPortgroups(_ *types.DistributedVirtualSwitchPortCriteria) []types.DistributedVirtualPort {
|
||||
// TODO(agui): Filter is not implemented yet
|
||||
var res []types.DistributedVirtualPort
|
||||
for _, ref := range s.Portgroup {
|
||||
pg := Map.Get(ref).(*DistributedVirtualPortgroup)
|
||||
res = append(res, types.DistributedVirtualPort{
|
||||
DvsUuid: s.Uuid,
|
||||
Key: pg.Key,
|
||||
Config: types.DVPortConfigInfo{
|
||||
Setting: pg.Config.DefaultPortConfig,
|
||||
},
|
||||
})
|
||||
|
||||
for _, key := range pg.PortKeys {
|
||||
res = append(res, types.DistributedVirtualPort{
|
||||
DvsUuid: s.Uuid,
|
||||
Key: key,
|
||||
Config: types.DVPortConfigInfo{
|
||||
Setting: pg.Config.DefaultPortConfig,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
func RenameTask(e mo.Entity, r *types.Rename_Task) soap.HasFault {
|
||||
task := CreateTask(e, "rename", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
obj := Map.Get(r.This).(mo.Entity).Entity()
|
||||
|
||||
if parent, ok := asFolderMO(Map.Get(*obj.Parent)); ok {
|
||||
if Map.FindByName(r.NewName, parent.ChildEntity) != nil {
|
||||
return nil, &types.InvalidArgument{InvalidProperty: "name"}
|
||||
}
|
||||
}
|
||||
|
||||
Map.Update(e, []types.PropertyChange{{Name: "name", Val: r.NewName}})
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.Rename_TaskBody{
|
||||
Res: &types.Rename_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2019 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type EnvironmentBrowser struct {
|
||||
mo.EnvironmentBrowser
|
||||
}
|
||||
|
||||
func newEnvironmentBrowser() *types.ManagedObjectReference {
|
||||
env := new(EnvironmentBrowser)
|
||||
Map.Put(env)
|
||||
return &env.Self
|
||||
}
|
||||
|
||||
func (b *EnvironmentBrowser) hosts(ctx *Context) []types.ManagedObjectReference {
|
||||
ctx.Map.m.Lock()
|
||||
defer ctx.Map.m.Unlock()
|
||||
for _, obj := range ctx.Map.objects {
|
||||
switch e := obj.(type) {
|
||||
case *mo.ComputeResource:
|
||||
if b.Self == *e.EnvironmentBrowser {
|
||||
return e.Host
|
||||
}
|
||||
case *ClusterComputeResource:
|
||||
if b.Self == *e.EnvironmentBrowser {
|
||||
return e.Host
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *EnvironmentBrowser) QueryConfigOption(req *types.QueryConfigOption) soap.HasFault {
|
||||
body := new(methods.QueryConfigOptionBody)
|
||||
|
||||
opt := &types.VirtualMachineConfigOption{
|
||||
Version: esx.HardwareVersion,
|
||||
DefaultDevice: esx.VirtualDevice,
|
||||
}
|
||||
|
||||
body.Res = &types.QueryConfigOptionResponse{
|
||||
Returnval: opt,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func guestFamily(id string) string {
|
||||
// TODO: We could capture the entire GuestOsDescriptor list from EnvironmentBrowser,
|
||||
// but it is a ton of data.. this should be good enough for now.
|
||||
switch {
|
||||
case strings.HasPrefix(id, "win"):
|
||||
return string(types.VirtualMachineGuestOsFamilyWindowsGuest)
|
||||
case strings.HasPrefix(id, "darwin"):
|
||||
return string(types.VirtualMachineGuestOsFamilyDarwinGuestFamily)
|
||||
default:
|
||||
return string(types.VirtualMachineGuestOsFamilyLinuxGuest)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *EnvironmentBrowser) QueryConfigOptionEx(req *types.QueryConfigOptionEx) soap.HasFault {
|
||||
body := new(methods.QueryConfigOptionExBody)
|
||||
|
||||
opt := &types.VirtualMachineConfigOption{
|
||||
Version: esx.HardwareVersion,
|
||||
DefaultDevice: esx.VirtualDevice,
|
||||
}
|
||||
|
||||
// From the SDK QueryConfigOptionEx doc:
|
||||
// "If guestId is nonempty, the guestOSDescriptor array of the config option is filtered to match against the guest IDs in the spec.
|
||||
// If there is no match, the whole list is returned."
|
||||
for _, id := range req.Spec.GuestId {
|
||||
for _, gid := range GuestID {
|
||||
if string(gid) == id {
|
||||
opt.GuestOSDescriptor = []types.GuestOsDescriptor{{
|
||||
Id: id,
|
||||
Family: guestFamily(id),
|
||||
}}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(opt.GuestOSDescriptor) == 0 {
|
||||
for i := range GuestID {
|
||||
id := string(GuestID[i])
|
||||
opt.GuestOSDescriptor = append(opt.GuestOSDescriptor, types.GuestOsDescriptor{
|
||||
Id: id,
|
||||
Family: guestFamily(id),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
body.Res = &types.QueryConfigOptionExResponse{
|
||||
Returnval: opt,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (b *EnvironmentBrowser) QueryConfigOptionDescriptor(ctx *Context, req *types.QueryConfigOptionDescriptor) soap.HasFault {
|
||||
body := &methods.QueryConfigOptionDescriptorBody{
|
||||
Res: new(types.QueryConfigOptionDescriptorResponse),
|
||||
}
|
||||
|
||||
body.Res.Returnval = []types.VirtualMachineConfigOptionDescriptor{{
|
||||
Key: esx.HardwareVersion,
|
||||
Description: esx.HardwareVersion,
|
||||
Host: b.hosts(ctx),
|
||||
CreateSupported: types.NewBool(true),
|
||||
DefaultConfigOption: types.NewBool(false),
|
||||
RunSupported: types.NewBool(true),
|
||||
UpgradeSupported: types.NewBool(true),
|
||||
}}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (b *EnvironmentBrowser) QueryConfigTarget(ctx *Context, req *types.QueryConfigTarget) soap.HasFault {
|
||||
body := &methods.QueryConfigTargetBody{
|
||||
Res: &types.QueryConfigTargetResponse{
|
||||
Returnval: &types.ConfigTarget{
|
||||
SmcPresent: types.NewBool(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
target := body.Res.Returnval
|
||||
|
||||
var hosts []types.ManagedObjectReference
|
||||
if req.Host == nil {
|
||||
hosts = b.hosts(ctx)
|
||||
} else {
|
||||
hosts = append(hosts, *req.Host)
|
||||
}
|
||||
|
||||
seen := make(map[types.ManagedObjectReference]bool)
|
||||
|
||||
for i := range hosts {
|
||||
host := ctx.Map.Get(hosts[i]).(*HostSystem)
|
||||
target.NumCpus += int32(host.Summary.Hardware.NumCpuPkgs)
|
||||
target.NumCpuCores += int32(host.Summary.Hardware.NumCpuCores)
|
||||
target.NumNumaNodes++
|
||||
|
||||
for _, ref := range host.Datastore {
|
||||
if seen[ref] {
|
||||
continue
|
||||
}
|
||||
seen[ref] = true
|
||||
|
||||
ds := ctx.Map.Get(ref).(*Datastore)
|
||||
target.Datastore = append(target.Datastore, types.VirtualMachineDatastoreInfo{
|
||||
VirtualMachineTargetInfo: types.VirtualMachineTargetInfo{
|
||||
Name: ds.Name,
|
||||
},
|
||||
Datastore: ds.Summary,
|
||||
Capability: ds.Capability,
|
||||
Mode: string(types.HostMountModeReadWrite),
|
||||
VStorageSupport: string(types.FileSystemMountInfoVStorageSupportStatusVStorageUnsupported),
|
||||
})
|
||||
}
|
||||
|
||||
for _, ref := range host.Network {
|
||||
if seen[ref] {
|
||||
continue
|
||||
}
|
||||
seen[ref] = true
|
||||
|
||||
switch n := ctx.Map.Get(ref).(type) {
|
||||
case *mo.Network:
|
||||
target.Network = append(target.Network, types.VirtualMachineNetworkInfo{
|
||||
VirtualMachineTargetInfo: types.VirtualMachineTargetInfo{
|
||||
Name: n.Name,
|
||||
},
|
||||
Network: n.Summary.GetNetworkSummary(),
|
||||
})
|
||||
case *DistributedVirtualPortgroup:
|
||||
dvs := ctx.Map.Get(*n.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
|
||||
target.DistributedVirtualPortgroup = append(target.DistributedVirtualPortgroup, types.DistributedVirtualPortgroupInfo{
|
||||
SwitchName: dvs.Name,
|
||||
SwitchUuid: dvs.Uuid,
|
||||
PortgroupName: n.Name,
|
||||
PortgroupKey: n.Key,
|
||||
PortgroupType: n.Config.Type,
|
||||
UplinkPortgroup: false,
|
||||
Portgroup: n.Self,
|
||||
NetworkReservationSupported: types.NewBool(false),
|
||||
})
|
||||
case *DistributedVirtualSwitch:
|
||||
target.DistributedVirtualSwitch = append(target.DistributedVirtualSwitch, types.DistributedVirtualSwitchInfo{
|
||||
SwitchName: n.Name,
|
||||
SwitchUuid: n.Uuid,
|
||||
DistributedVirtualSwitch: n.Self,
|
||||
NetworkReservationSupported: types.NewBool(false),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// Datacenter is the default template for Datacenter properties.
|
||||
// Capture method:
|
||||
// govc datacenter.info -dump
|
||||
var Datacenter = mo.Datacenter{
|
||||
ManagedEntity: mo.ManagedEntity{
|
||||
ExtensibleManagedObject: mo.ExtensibleManagedObject{
|
||||
Self: types.ManagedObjectReference{Type: "Datacenter", Value: "ha-datacenter"},
|
||||
Value: nil,
|
||||
AvailableField: nil,
|
||||
},
|
||||
Parent: (*types.ManagedObjectReference)(nil),
|
||||
CustomValue: nil,
|
||||
OverallStatus: "",
|
||||
ConfigStatus: "",
|
||||
ConfigIssue: nil,
|
||||
EffectiveRole: nil,
|
||||
Permission: nil,
|
||||
Name: "ha-datacenter",
|
||||
DisabledMethod: nil,
|
||||
RecentTask: nil,
|
||||
DeclaredAlarmState: nil,
|
||||
TriggeredAlarmState: nil,
|
||||
AlarmActionsEnabled: (*bool)(nil),
|
||||
Tag: nil,
|
||||
},
|
||||
VmFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-vm"},
|
||||
HostFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-host"},
|
||||
DatastoreFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-datastore"},
|
||||
NetworkFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-network"},
|
||||
Datastore: []types.ManagedObjectReference{
|
||||
{Type: "Datastore", Value: "57089c25-85e3ccd4-17b6-000c29d0beb3"},
|
||||
},
|
||||
Network: []types.ManagedObjectReference{
|
||||
{Type: "Network", Value: "HaNetwork-VM Network"},
|
||||
},
|
||||
Configuration: types.DatacenterConfigInfo{},
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package esx contains SOAP responses from an ESX server, captured using `govc ... -dump`.
|
||||
*/
|
||||
package esx
|
|
@ -1,248 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import "github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
// EventInfo is the default template for the EventManager description.eventInfo property.
|
||||
// Capture method:
|
||||
// govc object.collect -s -dump EventManager:ha-eventmgr description.eventInfo
|
||||
// The captured list has been manually pruned and FullFormat fields changed to use Go's template variable syntax.
|
||||
var EventInfo = []types.EventDescriptionEventDetail{
|
||||
{
|
||||
Key: "UserLoginSessionEvent",
|
||||
Description: "User login",
|
||||
Category: "info",
|
||||
FullFormat: "User {{.UserName}}@{{.IpAddress}} logged in as {{.UserAgent}}",
|
||||
},
|
||||
{
|
||||
Key: "UserLogoutSessionEvent",
|
||||
Description: "User logout",
|
||||
Category: "info",
|
||||
FullFormat: "User {{.UserName}}@{{.IpAddress}} logged out (login time: {{.LoginTime}}, number of API invocations: {{.CallCount}}, user agent: {{.UserAgent}})",
|
||||
},
|
||||
{
|
||||
Key: "DatacenterCreatedEvent",
|
||||
Description: "Datacenter created",
|
||||
Category: "info",
|
||||
FullFormat: "Created datacenter {{.Datacenter.Name}} in folder {{.Parent.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "DatastoreFileMovedEvent",
|
||||
Description: "File or directory moved to datastore",
|
||||
Category: "info",
|
||||
FullFormat: "Move of file or directory {{.SourceFile}} from {{.SourceDatastore.Name}} to {{.Datastore.Name}} as {{.TargetFile}}",
|
||||
},
|
||||
{
|
||||
Key: "DatastoreFileCopiedEvent",
|
||||
Description: "File or directory copied to datastore",
|
||||
Category: "info",
|
||||
FullFormat: "Copy of file or directory {{.SourceFile}} from {{.SourceDatastore.Name}} to {{.Datastore.Name}} as {{.TargetFile}}",
|
||||
},
|
||||
{
|
||||
Key: "DatastoreFileDeletedEvent",
|
||||
Description: "File or directory deleted",
|
||||
Category: "info",
|
||||
FullFormat: "Deletion of file or directory {{.TargetFile}} from {{.Datastore.Name}} was initiated",
|
||||
},
|
||||
{
|
||||
Key: "EnteringMaintenanceModeEvent",
|
||||
Description: "Entering maintenance mode",
|
||||
Category: "info",
|
||||
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has started to enter maintenance mode",
|
||||
},
|
||||
{
|
||||
Key: "EnteredMaintenanceModeEvent",
|
||||
Description: "Entered maintenance mode",
|
||||
Category: "info",
|
||||
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has entered maintenance mode",
|
||||
},
|
||||
{
|
||||
Key: "ExitMaintenanceModeEvent",
|
||||
Description: "Exit maintenance mode",
|
||||
Category: "info",
|
||||
FullFormat: "Host {{.Host.Name}} in {{.Datacenter.Name}} has exited maintenance mode",
|
||||
},
|
||||
{
|
||||
Key: "HostRemovedEvent",
|
||||
Description: "Host removed",
|
||||
FullFormat: "Removed host {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
Category: "info",
|
||||
},
|
||||
{
|
||||
Key: "VmSuspendedEvent",
|
||||
Description: "VM suspended",
|
||||
Category: "info",
|
||||
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is suspended",
|
||||
},
|
||||
{
|
||||
Key: "VmMigratedEvent",
|
||||
Description: "VM migrated",
|
||||
Category: "info",
|
||||
FullFormat: "Migration of virtual machine {{.Vm.Name}} from {{.SourceHost.Name}, {{.SourceDatastore.Name}} to {{.Host.Name}, {{.Ds.Name}} completed",
|
||||
},
|
||||
{
|
||||
Key: "VmBeingMigratedEvent",
|
||||
Description: "VM migrating",
|
||||
Category: "info",
|
||||
FullFormat: "Relocating {{.Vm.Name}} from {{.Host.Name}, {{.Ds.Name}} in {{.Datacenter.Name}} to {{.DestHost.Name}, {{.DestDatastore.Name}} in {{.DestDatacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmMacAssignedEvent",
|
||||
Description: "VM MAC assigned",
|
||||
Category: "info",
|
||||
FullFormat: "New MAC address ({{.Mac}}) assigned to adapter {{.Adapter}} for {{.Vm.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmRegisteredEvent",
|
||||
Description: "VM registered",
|
||||
Category: "info",
|
||||
FullFormat: "Registered {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmReconfiguredEvent",
|
||||
Description: "VM reconfigured",
|
||||
Category: "info",
|
||||
FullFormat: "Reconfigured {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmGuestRebootEvent",
|
||||
Description: "Guest reboot",
|
||||
Category: "info",
|
||||
FullFormat: "Guest OS reboot for {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmBeingClonedEvent",
|
||||
Description: "VM being cloned",
|
||||
Category: "info",
|
||||
FullFormat: "Cloning {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} to {{.DestName}} on host {{.DestHost.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmClonedEvent",
|
||||
Description: "VM cloned",
|
||||
Category: "info",
|
||||
FullFormat: "Clone of {{.SourceVm.Name}} completed",
|
||||
},
|
||||
{
|
||||
Key: "VmBeingDeployedEvent",
|
||||
Description: "Deploying VM",
|
||||
Category: "info",
|
||||
FullFormat: "Deploying {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} from template {{.SrcTemplate.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmDeployedEvent",
|
||||
Description: "VM deployed",
|
||||
Category: "info",
|
||||
FullFormat: "Template {{.SrcTemplate.Name}} deployed on host {{.Host.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmInstanceUuidAssignedEvent",
|
||||
Description: "Assign a new instance UUID",
|
||||
Category: "info",
|
||||
FullFormat: "Assign a new instance UUID ({{.InstanceUuid}}) to {{.Vm.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmPoweredOnEvent",
|
||||
Description: "VM powered on",
|
||||
Category: "info",
|
||||
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is powered on",
|
||||
},
|
||||
{
|
||||
Key: "VmStartingEvent",
|
||||
Description: "VM starting",
|
||||
Category: "info",
|
||||
FullFormat: "{{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} is starting",
|
||||
},
|
||||
{
|
||||
Key: "VmStoppingEvent",
|
||||
Description: "VM stopping",
|
||||
Category: "info",
|
||||
FullFormat: "{{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}} is stopping",
|
||||
},
|
||||
{
|
||||
Key: "VmSuspendingEvent",
|
||||
Description: "VM being suspended",
|
||||
Category: "info",
|
||||
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is being suspended",
|
||||
},
|
||||
{
|
||||
Key: "VmResumingEvent",
|
||||
Description: "VM resuming",
|
||||
Category: "info",
|
||||
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is resumed",
|
||||
},
|
||||
{
|
||||
Key: "VmBeingCreatedEvent",
|
||||
Description: "Creating VM",
|
||||
Category: "info",
|
||||
FullFormat: "Creating {{.Vm.Name}} on host {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmCreatedEvent",
|
||||
Description: "VM created",
|
||||
Category: "info",
|
||||
FullFormat: "Created virtual machine {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmRemovedEvent",
|
||||
Description: "VM removed",
|
||||
Category: "info",
|
||||
FullFormat: "Removed {{.Vm.Name}} on {{.Host.Name}} from {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmResettingEvent",
|
||||
Description: "VM resetting",
|
||||
Category: "info",
|
||||
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is reset",
|
||||
},
|
||||
{
|
||||
Key: "VmGuestShutdownEvent",
|
||||
Description: "Guest OS shut down",
|
||||
Category: "info",
|
||||
FullFormat: "Guest OS shut down for {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmUuidAssignedEvent",
|
||||
Description: "VM UUID assigned",
|
||||
Category: "info",
|
||||
FullFormat: "Assigned new BIOS UUID ({{.Uuid}}) to {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "VmPoweredOffEvent",
|
||||
Description: "VM powered off",
|
||||
Category: "info",
|
||||
FullFormat: "{{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}} is powered off",
|
||||
},
|
||||
{
|
||||
Key: "VmRelocatedEvent",
|
||||
Description: "VM relocated",
|
||||
Category: "info",
|
||||
FullFormat: "Completed the relocation of the virtual machine",
|
||||
},
|
||||
{
|
||||
Key: "DrsVmMigratedEvent",
|
||||
Description: "DRS VM migrated",
|
||||
Category: "info",
|
||||
FullFormat: "DRS migrated {{.Vm.Name}} from {{.SourceHost.Name}} to {{.Host.Name}} in cluster {{.ComputeResource.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
{
|
||||
Key: "DrsVmPoweredOnEvent",
|
||||
Description: "DRS VM powered on",
|
||||
Category: "info",
|
||||
FullFormat: "DRS powered On {{.Vm.Name}} on {{.Host.Name}} in {{.Datacenter.Name}}",
|
||||
},
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,864 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// HostHardwareInfo is the default template for the HostSystem hardware property.
|
||||
// Capture method:
|
||||
// govc object.collect -s -dump HostSystem:ha-host hardware
|
||||
var HostHardwareInfo = &types.HostHardwareInfo{
|
||||
SystemInfo: types.HostSystemInfo{
|
||||
Vendor: "VMware, Inc.",
|
||||
Model: "VMware Virtual Platform",
|
||||
Uuid: "e88d4d56-9f1e-3ea1-71fa-13a8e1a7fd70",
|
||||
OtherIdentifyingInfo: []types.HostSystemIdentificationInfo{
|
||||
{
|
||||
IdentifierValue: " No Asset Tag",
|
||||
IdentifierType: &types.ElementDescription{
|
||||
Description: types.Description{
|
||||
Label: "Asset Tag",
|
||||
Summary: "Asset tag of the system",
|
||||
},
|
||||
Key: "AssetTag",
|
||||
},
|
||||
},
|
||||
{
|
||||
IdentifierValue: "[MS_VM_CERT/SHA1/27d66596a61c48dd3dc7216fd715126e33f59ae7]",
|
||||
IdentifierType: &types.ElementDescription{
|
||||
Description: types.Description{
|
||||
Label: "OEM specific string",
|
||||
Summary: "OEM specific string",
|
||||
},
|
||||
Key: "OemSpecificString",
|
||||
},
|
||||
},
|
||||
{
|
||||
IdentifierValue: "Welcome to the Virtual Machine",
|
||||
IdentifierType: &types.ElementDescription{
|
||||
Description: types.Description{
|
||||
Label: "OEM specific string",
|
||||
Summary: "OEM specific string",
|
||||
},
|
||||
Key: "OemSpecificString",
|
||||
},
|
||||
},
|
||||
{
|
||||
IdentifierValue: "VMware-56 4d 8d e8 1e 9f a1 3e-71 fa 13 a8 e1 a7 fd 70",
|
||||
IdentifierType: &types.ElementDescription{
|
||||
Description: types.Description{
|
||||
Label: "Service tag",
|
||||
Summary: "Service tag of the system",
|
||||
},
|
||||
Key: "ServiceTag",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
CpuPowerManagementInfo: &types.HostCpuPowerManagementInfo{
|
||||
CurrentPolicy: "Balanced",
|
||||
HardwareSupport: "",
|
||||
},
|
||||
CpuInfo: types.HostCpuInfo{
|
||||
NumCpuPackages: 2,
|
||||
NumCpuCores: 2,
|
||||
NumCpuThreads: 2,
|
||||
Hz: 3591345000,
|
||||
},
|
||||
CpuPkg: []types.HostCpuPackage{
|
||||
{
|
||||
Index: 0,
|
||||
Vendor: "intel",
|
||||
Hz: 3591345000,
|
||||
BusHz: 115849838,
|
||||
Description: "Intel(R) Xeon(R) CPU E5-1620 0 @ 3.60GHz",
|
||||
ThreadId: []int16{0},
|
||||
CpuFeature: []types.HostCpuIdInfo{
|
||||
{
|
||||
Level: 0,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0000:0000:0000:1101",
|
||||
Ebx: "0111:0101:0110:1110:0110:0101:0100:0111",
|
||||
Ecx: "0110:1100:0110:0101:0111:0100:0110:1110",
|
||||
Edx: "0100:1001:0110:0101:0110:1110:0110:1001",
|
||||
},
|
||||
{
|
||||
Level: 1,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0010:0000:0110:1101:0111",
|
||||
Ebx: "0000:0000:0000:0001:0000:1000:0000:0000",
|
||||
Ecx: "1001:0111:1011:1010:0010:0010:0010:1011",
|
||||
Edx: "0000:1111:1010:1011:1111:1011:1111:1111",
|
||||
},
|
||||
{
|
||||
Level: -2147483648,
|
||||
Vendor: "",
|
||||
Eax: "1000:0000:0000:0000:0000:0000:0000:1000",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
},
|
||||
{
|
||||
Level: -2147483647,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0001",
|
||||
Edx: "0010:1000:0001:0000:0000:1000:0000:0000",
|
||||
},
|
||||
{
|
||||
Level: -2147483640,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0011:0000:0010:1010",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Vendor: "intel",
|
||||
Hz: 3591345000,
|
||||
BusHz: 115849838,
|
||||
Description: "Intel(R) Xeon(R) CPU E5-1620 0 @ 3.60GHz",
|
||||
ThreadId: []int16{1},
|
||||
CpuFeature: []types.HostCpuIdInfo{
|
||||
{
|
||||
Level: 0,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0000:0000:0000:1101",
|
||||
Ebx: "0111:0101:0110:1110:0110:0101:0100:0111",
|
||||
Ecx: "0110:1100:0110:0101:0111:0100:0110:1110",
|
||||
Edx: "0100:1001:0110:0101:0110:1110:0110:1001",
|
||||
},
|
||||
{
|
||||
Level: 1,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0010:0000:0110:1101:0111",
|
||||
Ebx: "0000:0010:0000:0001:0000:1000:0000:0000",
|
||||
Ecx: "1001:0111:1011:1010:0010:0010:0010:1011",
|
||||
Edx: "0000:1111:1010:1011:1111:1011:1111:1111",
|
||||
},
|
||||
{
|
||||
Level: -2147483648,
|
||||
Vendor: "",
|
||||
Eax: "1000:0000:0000:0000:0000:0000:0000:1000",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
},
|
||||
{
|
||||
Level: -2147483647,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0001",
|
||||
Edx: "0010:1000:0001:0000:0000:1000:0000:0000",
|
||||
},
|
||||
{
|
||||
Level: -2147483640,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0011:0000:0010:1010",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MemorySize: 4294430720,
|
||||
NumaInfo: &types.HostNumaInfo{
|
||||
Type: "NUMA",
|
||||
NumNodes: 1,
|
||||
NumaNode: []types.HostNumaNode{
|
||||
{
|
||||
TypeId: 0x0,
|
||||
CpuID: []int16{1, 0},
|
||||
MemoryRangeBegin: 4294967296,
|
||||
MemoryRangeLength: 1073741824,
|
||||
},
|
||||
},
|
||||
},
|
||||
SmcPresent: types.NewBool(false),
|
||||
PciDevice: []types.HostPciDevice{
|
||||
{
|
||||
Id: "0000:00:00.0",
|
||||
ClassId: 1536,
|
||||
Bus: 0x0,
|
||||
Slot: 0x0,
|
||||
Function: 0x0,
|
||||
VendorId: -32634,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "Intel Corporation",
|
||||
DeviceId: 29072,
|
||||
SubDeviceId: 6518,
|
||||
ParentBridge: "",
|
||||
DeviceName: "Virtual Machine Chipset",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:01.0",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x1,
|
||||
Function: 0x0,
|
||||
VendorId: -32634,
|
||||
SubVendorId: 0,
|
||||
VendorName: "Intel Corporation",
|
||||
DeviceId: 29073,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "440BX/ZX/DX - 82443BX/ZX/DX AGP bridge",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:07.0",
|
||||
ClassId: 1537,
|
||||
Bus: 0x0,
|
||||
Slot: 0x7,
|
||||
Function: 0x0,
|
||||
VendorId: -32634,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "Intel Corporation",
|
||||
DeviceId: 28944,
|
||||
SubDeviceId: 6518,
|
||||
ParentBridge: "",
|
||||
DeviceName: "Virtual Machine Chipset",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:07.1",
|
||||
ClassId: 257,
|
||||
Bus: 0x0,
|
||||
Slot: 0x7,
|
||||
Function: 0x1,
|
||||
VendorId: -32634,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "Intel Corporation",
|
||||
DeviceId: 28945,
|
||||
SubDeviceId: 6518,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PIIX4 for 430TX/440BX/MX IDE Controller",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:07.3",
|
||||
ClassId: 1664,
|
||||
Bus: 0x0,
|
||||
Slot: 0x7,
|
||||
Function: 0x3,
|
||||
VendorId: -32634,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "Intel Corporation",
|
||||
DeviceId: 28947,
|
||||
SubDeviceId: 6518,
|
||||
ParentBridge: "",
|
||||
DeviceName: "Virtual Machine Chipset",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:07.7",
|
||||
ClassId: 2176,
|
||||
Bus: 0x0,
|
||||
Slot: 0x7,
|
||||
Function: 0x7,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1856,
|
||||
SubDeviceId: 1856,
|
||||
ParentBridge: "",
|
||||
DeviceName: "Virtual Machine Communication Interface",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:0f.0",
|
||||
ClassId: 768,
|
||||
Bus: 0x0,
|
||||
Slot: 0xf,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1029,
|
||||
SubDeviceId: 1029,
|
||||
ParentBridge: "",
|
||||
DeviceName: "SVGA II Adapter",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:11.0",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x11,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1936,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI bridge",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:15.0",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x15,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:15.1",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x15,
|
||||
Function: 0x1,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:15.2",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x15,
|
||||
Function: 0x2,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:15.3",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x15,
|
||||
Function: 0x3,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:15.4",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x15,
|
||||
Function: 0x4,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:15.5",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x15,
|
||||
Function: 0x5,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:15.6",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x15,
|
||||
Function: 0x6,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:15.7",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x15,
|
||||
Function: 0x7,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:16.0",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x16,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:16.1",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x16,
|
||||
Function: 0x1,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:16.2",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x16,
|
||||
Function: 0x2,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:16.3",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x16,
|
||||
Function: 0x3,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:16.4",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x16,
|
||||
Function: 0x4,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:16.5",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x16,
|
||||
Function: 0x5,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:16.6",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x16,
|
||||
Function: 0x6,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:16.7",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x16,
|
||||
Function: 0x7,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:17.0",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x17,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:17.1",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x17,
|
||||
Function: 0x1,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:17.2",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x17,
|
||||
Function: 0x2,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:17.3",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x17,
|
||||
Function: 0x3,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:17.4",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x17,
|
||||
Function: 0x4,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:17.5",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x17,
|
||||
Function: 0x5,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:17.6",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x17,
|
||||
Function: 0x6,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:17.7",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x17,
|
||||
Function: 0x7,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:18.0",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x18,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:18.1",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x18,
|
||||
Function: 0x1,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:18.2",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x18,
|
||||
Function: 0x2,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:18.3",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x18,
|
||||
Function: 0x3,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:18.4",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x18,
|
||||
Function: 0x4,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:18.5",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x18,
|
||||
Function: 0x5,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:18.6",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x18,
|
||||
Function: 0x6,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:00:18.7",
|
||||
ClassId: 1540,
|
||||
Bus: 0x0,
|
||||
Slot: 0x18,
|
||||
Function: 0x7,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 0,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1952,
|
||||
SubDeviceId: 0,
|
||||
ParentBridge: "",
|
||||
DeviceName: "PCI Express Root Port",
|
||||
},
|
||||
{
|
||||
Id: "0000:03:00.0",
|
||||
ClassId: 263,
|
||||
Bus: 0x3,
|
||||
Slot: 0x0,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "VMware",
|
||||
DeviceId: 1984,
|
||||
SubDeviceId: 1984,
|
||||
ParentBridge: "0000:00:15.0",
|
||||
DeviceName: "PVSCSI SCSI Controller",
|
||||
},
|
||||
{
|
||||
Id: "0000:0b:00.0",
|
||||
ClassId: 512,
|
||||
Bus: 0xb,
|
||||
Slot: 0x0,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "VMware Inc.",
|
||||
DeviceId: 1968,
|
||||
SubDeviceId: 1968,
|
||||
ParentBridge: "0000:00:16.0",
|
||||
DeviceName: "vmxnet3 Virtual Ethernet Controller",
|
||||
},
|
||||
{
|
||||
Id: "0000:13:00.0",
|
||||
ClassId: 512,
|
||||
Bus: 0x13,
|
||||
Slot: 0x0,
|
||||
Function: 0x0,
|
||||
VendorId: 5549,
|
||||
SubVendorId: 5549,
|
||||
VendorName: "VMware Inc.",
|
||||
DeviceId: 1968,
|
||||
SubDeviceId: 1968,
|
||||
ParentBridge: "0000:00:17.0",
|
||||
DeviceName: "vmxnet3 Virtual Ethernet Controller",
|
||||
},
|
||||
},
|
||||
CpuFeature: []types.HostCpuIdInfo{
|
||||
{
|
||||
Level: 0,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0000:0000:0000:1101",
|
||||
Ebx: "0111:0101:0110:1110:0110:0101:0100:0111",
|
||||
Ecx: "0110:1100:0110:0101:0111:0100:0110:1110",
|
||||
Edx: "0100:1001:0110:0101:0110:1110:0110:1001",
|
||||
},
|
||||
{
|
||||
Level: 1,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0010:0000:0110:1101:0111",
|
||||
Ebx: "0000:0000:0000:0001:0000:1000:0000:0000",
|
||||
Ecx: "1001:0111:1011:1010:0010:0010:0010:1011",
|
||||
Edx: "0000:1111:1010:1011:1111:1011:1111:1111",
|
||||
},
|
||||
{
|
||||
Level: -2147483648,
|
||||
Vendor: "",
|
||||
Eax: "1000:0000:0000:0000:0000:0000:0000:1000",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
},
|
||||
{
|
||||
Level: -2147483647,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0001",
|
||||
Edx: "0010:1000:0001:0000:0000:1000:0000:0000",
|
||||
},
|
||||
{
|
||||
Level: -2147483640,
|
||||
Vendor: "",
|
||||
Eax: "0000:0000:0000:0000:0011:0000:0010:1010",
|
||||
Ebx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Ecx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
Edx: "0000:0000:0000:0000:0000:0000:0000:0000",
|
||||
},
|
||||
},
|
||||
BiosInfo: &types.HostBIOSInfo{
|
||||
BiosVersion: "6.00",
|
||||
ReleaseDate: nil,
|
||||
Vendor: "",
|
||||
MajorRelease: 0,
|
||||
MinorRelease: 0,
|
||||
FirmwareMajorRelease: 0,
|
||||
FirmwareMinorRelease: 0,
|
||||
},
|
||||
ReliableMemoryInfo: &types.HostReliableMemoryInfo{},
|
||||
}
|
||||
|
||||
func init() {
|
||||
date, _ := time.Parse("2006-01-02", "2015-07-02")
|
||||
|
||||
HostHardwareInfo.BiosInfo.ReleaseDate = &date
|
||||
}
|
|
@ -1,346 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import "github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
// HostStorageDeviceInfo is the default template for the HostSystem config.storageDevice property.
|
||||
// Capture method:
|
||||
// govc object.collect -s -dump HostSystem:ha-host config.storageDevice
|
||||
var HostStorageDeviceInfo = types.HostStorageDeviceInfo{
|
||||
HostBusAdapter: []types.BaseHostHostBusAdapter{
|
||||
&types.HostParallelScsiHba{
|
||||
HostHostBusAdapter: types.HostHostBusAdapter{
|
||||
Key: "key-vim.host.ParallelScsiHba-vmhba0",
|
||||
Device: "vmhba0",
|
||||
Bus: 3,
|
||||
Status: "unknown",
|
||||
Model: "PVSCSI SCSI Controller",
|
||||
Driver: "pvscsi",
|
||||
Pci: "0000:03:00.0",
|
||||
},
|
||||
},
|
||||
&types.HostBlockHba{
|
||||
HostHostBusAdapter: types.HostHostBusAdapter{
|
||||
Key: "key-vim.host.BlockHba-vmhba1",
|
||||
Device: "vmhba1",
|
||||
Bus: 0,
|
||||
Status: "unknown",
|
||||
Model: "PIIX4 for 430TX/440BX/MX IDE Controller",
|
||||
Driver: "vmkata",
|
||||
Pci: "0000:00:07.1",
|
||||
},
|
||||
},
|
||||
&types.HostBlockHba{
|
||||
HostHostBusAdapter: types.HostHostBusAdapter{
|
||||
Key: "key-vim.host.BlockHba-vmhba64",
|
||||
Device: "vmhba64",
|
||||
Bus: 0,
|
||||
Status: "unknown",
|
||||
Model: "PIIX4 for 430TX/440BX/MX IDE Controller",
|
||||
Driver: "vmkata",
|
||||
Pci: "0000:00:07.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
ScsiLun: []types.BaseScsiLun{
|
||||
&types.ScsiLun{
|
||||
HostDevice: types.HostDevice{
|
||||
DeviceName: "/vmfs/devices/cdrom/mpx.vmhba1:C0:T0:L0",
|
||||
DeviceType: "cdrom",
|
||||
},
|
||||
Key: "key-vim.host.ScsiLun-0005000000766d686261313a303a30",
|
||||
Uuid: "0005000000766d686261313a303a30",
|
||||
Descriptor: []types.ScsiLunDescriptor{
|
||||
{
|
||||
Quality: "lowQuality",
|
||||
Id: "mpx.vmhba1:C0:T0:L0",
|
||||
},
|
||||
{
|
||||
Quality: "lowQuality",
|
||||
Id: "vml.0005000000766d686261313a303a30",
|
||||
},
|
||||
{
|
||||
Quality: "lowQuality",
|
||||
Id: "0005000000766d686261313a303a30",
|
||||
},
|
||||
},
|
||||
CanonicalName: "mpx.vmhba1:C0:T0:L0",
|
||||
DisplayName: "Local NECVMWar CD-ROM (mpx.vmhba1:C0:T0:L0)",
|
||||
LunType: "cdrom",
|
||||
Vendor: "NECVMWar",
|
||||
Model: "VMware IDE CDR00",
|
||||
Revision: "1.00",
|
||||
ScsiLevel: 5,
|
||||
SerialNumber: "unavailable",
|
||||
DurableName: (*types.ScsiLunDurableName)(nil),
|
||||
AlternateName: []types.ScsiLunDurableName{
|
||||
{
|
||||
Namespace: "GENERIC_VPD",
|
||||
NamespaceId: 0x5,
|
||||
Data: []uint8{0x2d, 0x37, 0x39},
|
||||
},
|
||||
{
|
||||
Namespace: "GENERIC_VPD",
|
||||
NamespaceId: 0x5,
|
||||
Data: []uint8{0x30},
|
||||
},
|
||||
},
|
||||
StandardInquiry: []uint8{0x30},
|
||||
QueueDepth: 0,
|
||||
OperationalState: []string{"ok"},
|
||||
Capabilities: &types.ScsiLunCapabilities{},
|
||||
VStorageSupport: "vStorageUnsupported",
|
||||
ProtocolEndpoint: types.NewBool(false),
|
||||
},
|
||||
&types.HostScsiDisk{
|
||||
ScsiLun: types.ScsiLun{
|
||||
HostDevice: types.HostDevice{
|
||||
DeviceName: "/vmfs/devices/disks/mpx.vmhba0:C0:T0:L0",
|
||||
DeviceType: "disk",
|
||||
},
|
||||
Key: "key-vim.host.ScsiDisk-0000000000766d686261303a303a30",
|
||||
Uuid: "0000000000766d686261303a303a30",
|
||||
Descriptor: []types.ScsiLunDescriptor{
|
||||
{
|
||||
Quality: "lowQuality",
|
||||
Id: "mpx.vmhba0:C0:T0:L0",
|
||||
},
|
||||
{
|
||||
Quality: "lowQuality",
|
||||
Id: "vml.0000000000766d686261303a303a30",
|
||||
},
|
||||
{
|
||||
Quality: "lowQuality",
|
||||
Id: "0000000000766d686261303a303a30",
|
||||
},
|
||||
},
|
||||
CanonicalName: "mpx.vmhba0:C0:T0:L0",
|
||||
DisplayName: "Local VMware, Disk (mpx.vmhba0:C0:T0:L0)",
|
||||
LunType: "disk",
|
||||
Vendor: "VMware, ",
|
||||
Model: "VMware Virtual S",
|
||||
Revision: "1.0 ",
|
||||
ScsiLevel: 2,
|
||||
SerialNumber: "unavailable",
|
||||
DurableName: (*types.ScsiLunDurableName)(nil),
|
||||
AlternateName: []types.ScsiLunDurableName{
|
||||
{
|
||||
Namespace: "GENERIC_VPD",
|
||||
NamespaceId: 0x5,
|
||||
Data: []uint8{0x2d, 0x37, 0x39},
|
||||
},
|
||||
{
|
||||
Namespace: "GENERIC_VPD",
|
||||
NamespaceId: 0x5,
|
||||
Data: []uint8{0x30},
|
||||
},
|
||||
},
|
||||
StandardInquiry: []uint8{0x30},
|
||||
QueueDepth: 1024,
|
||||
OperationalState: []string{"ok"},
|
||||
Capabilities: &types.ScsiLunCapabilities{},
|
||||
VStorageSupport: "vStorageUnsupported",
|
||||
ProtocolEndpoint: types.NewBool(false),
|
||||
},
|
||||
Capacity: types.HostDiskDimensionsLba{
|
||||
BlockSize: 512,
|
||||
Block: 67108864,
|
||||
},
|
||||
DevicePath: "/vmfs/devices/disks/mpx.vmhba0:C0:T0:L0",
|
||||
Ssd: types.NewBool(true),
|
||||
LocalDisk: types.NewBool(true),
|
||||
PhysicalLocation: nil,
|
||||
EmulatedDIXDIFEnabled: types.NewBool(false),
|
||||
VsanDiskInfo: (*types.VsanHostVsanDiskInfo)(nil),
|
||||
ScsiDiskType: "native512",
|
||||
},
|
||||
},
|
||||
ScsiTopology: &types.HostScsiTopology{
|
||||
Adapter: []types.HostScsiTopologyInterface{
|
||||
{
|
||||
Key: "key-vim.host.ScsiTopology.Interface-vmhba0",
|
||||
Adapter: "key-vim.host.ParallelScsiHba-vmhba0",
|
||||
Target: []types.HostScsiTopologyTarget{
|
||||
{
|
||||
Key: "key-vim.host.ScsiTopology.Target-vmhba0:0:0",
|
||||
Target: 0,
|
||||
Lun: []types.HostScsiTopologyLun{
|
||||
{
|
||||
Key: "key-vim.host.ScsiTopology.Lun-0000000000766d686261303a303a30",
|
||||
Lun: 0,
|
||||
ScsiLun: "key-vim.host.ScsiDisk-0000000000766d686261303a303a30",
|
||||
},
|
||||
},
|
||||
Transport: &types.HostParallelScsiTargetTransport{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "key-vim.host.ScsiTopology.Interface-vmhba1",
|
||||
Adapter: "key-vim.host.BlockHba-vmhba1",
|
||||
Target: []types.HostScsiTopologyTarget{
|
||||
{
|
||||
Key: "key-vim.host.ScsiTopology.Target-vmhba1:0:0",
|
||||
Target: 0,
|
||||
Lun: []types.HostScsiTopologyLun{
|
||||
{
|
||||
Key: "key-vim.host.ScsiTopology.Lun-0005000000766d686261313a303a30",
|
||||
Lun: 0,
|
||||
ScsiLun: "key-vim.host.ScsiLun-0005000000766d686261313a303a30",
|
||||
},
|
||||
},
|
||||
Transport: &types.HostBlockAdapterTargetTransport{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "key-vim.host.ScsiTopology.Interface-vmhba64",
|
||||
Adapter: "key-vim.host.BlockHba-vmhba64",
|
||||
Target: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
MultipathInfo: &types.HostMultipathInfo{
|
||||
Lun: []types.HostMultipathInfoLogicalUnit{
|
||||
{
|
||||
Key: "key-vim.host.MultipathInfo.LogicalUnit-0005000000766d686261313a303a30",
|
||||
Id: "0005000000766d686261313a303a30",
|
||||
Lun: "key-vim.host.ScsiLun-0005000000766d686261313a303a30",
|
||||
Path: []types.HostMultipathInfoPath{
|
||||
{
|
||||
Key: "key-vim.host.MultipathInfo.Path-vmhba1:C0:T0:L0",
|
||||
Name: "vmhba1:C0:T0:L0",
|
||||
PathState: "active",
|
||||
State: "active",
|
||||
IsWorkingPath: types.NewBool(true),
|
||||
Adapter: "key-vim.host.BlockHba-vmhba1",
|
||||
Lun: "key-vim.host.MultipathInfo.LogicalUnit-0005000000766d686261313a303a30",
|
||||
Transport: &types.HostBlockAdapterTargetTransport{},
|
||||
},
|
||||
},
|
||||
Policy: &types.HostMultipathInfoFixedLogicalUnitPolicy{
|
||||
HostMultipathInfoLogicalUnitPolicy: types.HostMultipathInfoLogicalUnitPolicy{
|
||||
Policy: "VMW_PSP_FIXED",
|
||||
},
|
||||
Prefer: "vmhba1:C0:T0:L0",
|
||||
},
|
||||
StorageArrayTypePolicy: &types.HostMultipathInfoLogicalUnitStorageArrayTypePolicy{
|
||||
Policy: "VMW_SATP_LOCAL",
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "key-vim.host.MultipathInfo.LogicalUnit-0000000000766d686261303a303a30",
|
||||
Id: "0000000000766d686261303a303a30",
|
||||
Lun: "key-vim.host.ScsiDisk-0000000000766d686261303a303a30",
|
||||
Path: []types.HostMultipathInfoPath{
|
||||
{
|
||||
Key: "key-vim.host.MultipathInfo.Path-vmhba0:C0:T0:L0",
|
||||
Name: "vmhba0:C0:T0:L0",
|
||||
PathState: "active",
|
||||
State: "active",
|
||||
IsWorkingPath: types.NewBool(true),
|
||||
Adapter: "key-vim.host.ParallelScsiHba-vmhba0",
|
||||
Lun: "key-vim.host.MultipathInfo.LogicalUnit-0000000000766d686261303a303a30",
|
||||
Transport: &types.HostParallelScsiTargetTransport{},
|
||||
},
|
||||
},
|
||||
Policy: &types.HostMultipathInfoFixedLogicalUnitPolicy{
|
||||
HostMultipathInfoLogicalUnitPolicy: types.HostMultipathInfoLogicalUnitPolicy{
|
||||
Policy: "VMW_PSP_FIXED",
|
||||
},
|
||||
Prefer: "vmhba0:C0:T0:L0",
|
||||
},
|
||||
StorageArrayTypePolicy: &types.HostMultipathInfoLogicalUnitStorageArrayTypePolicy{
|
||||
Policy: "VMW_SATP_LOCAL",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PlugStoreTopology: &types.HostPlugStoreTopology{
|
||||
Adapter: []types.HostPlugStoreTopologyAdapter{
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Adapter-vmhba0",
|
||||
Adapter: "key-vim.host.ParallelScsiHba-vmhba0",
|
||||
Path: []string{"key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T0:L0"},
|
||||
},
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Adapter-vmhba1",
|
||||
Adapter: "key-vim.host.BlockHba-vmhba1",
|
||||
Path: []string{"key-vim.host.PlugStoreTopology.Path-vmhba1:C0:T0:L0"},
|
||||
},
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Adapter-vmhba64",
|
||||
Adapter: "key-vim.host.BlockHba-vmhba64",
|
||||
Path: nil,
|
||||
},
|
||||
},
|
||||
Path: []types.HostPlugStoreTopologyPath{
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T0:L0",
|
||||
Name: "vmhba0:C0:T0:L0",
|
||||
ChannelNumber: 0,
|
||||
TargetNumber: 0,
|
||||
LunNumber: 0,
|
||||
Adapter: "key-vim.host.PlugStoreTopology.Adapter-vmhba0",
|
||||
Target: "key-vim.host.PlugStoreTopology.Target-pscsi.0:0",
|
||||
Device: "key-vim.host.PlugStoreTopology.Device-0000000000766d686261303a303a30",
|
||||
},
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Path-vmhba1:C0:T0:L0",
|
||||
Name: "vmhba1:C0:T0:L0",
|
||||
ChannelNumber: 0,
|
||||
TargetNumber: 0,
|
||||
LunNumber: 0,
|
||||
Adapter: "key-vim.host.PlugStoreTopology.Adapter-vmhba1",
|
||||
Target: "key-vim.host.PlugStoreTopology.Target-ide.0:0",
|
||||
Device: "key-vim.host.PlugStoreTopology.Device-0005000000766d686261313a303a30",
|
||||
},
|
||||
},
|
||||
Target: []types.HostPlugStoreTopologyTarget{
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Target-pscsi.0:0",
|
||||
Transport: &types.HostParallelScsiTargetTransport{},
|
||||
},
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Target-ide.0:0",
|
||||
Transport: &types.HostBlockAdapterTargetTransport{},
|
||||
},
|
||||
},
|
||||
Device: []types.HostPlugStoreTopologyDevice{
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Device-0005000000766d686261313a303a30",
|
||||
Lun: "key-vim.host.ScsiLun-0005000000766d686261313a303a30",
|
||||
Path: []string{"key-vim.host.PlugStoreTopology.Path-vmhba1:C0:T0:L0"},
|
||||
},
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Device-0000000000766d686261303a303a30",
|
||||
Lun: "key-vim.host.ScsiDisk-0000000000766d686261303a303a30",
|
||||
Path: []string{"key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T0:L0"},
|
||||
},
|
||||
},
|
||||
Plugin: []types.HostPlugStoreTopologyPlugin{
|
||||
{
|
||||
Key: "key-vim.host.PlugStoreTopology.Plugin-NMP",
|
||||
Name: "NMP",
|
||||
Device: []string{"key-vim.host.PlugStoreTopology.Device-0005000000766d686261313a303a30", "key-vim.host.PlugStoreTopology.Device-0000000000766d686261303a303a30"},
|
||||
ClaimedPath: []string{"key-vim.host.PlugStoreTopology.Path-vmhba0:C0:T0:L0", "key-vim.host.PlugStoreTopology.Path-vmhba1:C0:T0:L0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
SoftwareInternetScsiEnabled: false,
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// ResourcePool is the default template for ResourcePool properties.
|
||||
// Capture method:
|
||||
// govc pool.info "*" -dump
|
||||
var ResourcePool = mo.ResourcePool{
|
||||
ManagedEntity: mo.ManagedEntity{
|
||||
ExtensibleManagedObject: mo.ExtensibleManagedObject{
|
||||
Self: types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"},
|
||||
Value: nil,
|
||||
AvailableField: nil,
|
||||
},
|
||||
Parent: &types.ManagedObjectReference{Type: "ComputeResource", Value: "ha-compute-res"},
|
||||
CustomValue: nil,
|
||||
OverallStatus: "green",
|
||||
ConfigStatus: "green",
|
||||
ConfigIssue: nil,
|
||||
EffectiveRole: []int32{-1},
|
||||
Permission: nil,
|
||||
Name: "Resources",
|
||||
DisabledMethod: []string{"CreateVApp", "CreateChildVM_Task"},
|
||||
RecentTask: nil,
|
||||
DeclaredAlarmState: nil,
|
||||
TriggeredAlarmState: nil,
|
||||
AlarmActionsEnabled: (*bool)(nil),
|
||||
Tag: nil,
|
||||
},
|
||||
Summary: &types.ResourcePoolSummary{
|
||||
DynamicData: types.DynamicData{},
|
||||
Name: "Resources",
|
||||
Config: types.ResourceConfigSpec{
|
||||
DynamicData: types.DynamicData{},
|
||||
Entity: &types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"},
|
||||
ChangeVersion: "",
|
||||
LastModified: (*time.Time)(nil),
|
||||
CpuAllocation: types.ResourceAllocationInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Reservation: types.NewInt64(4121),
|
||||
ExpandableReservation: types.NewBool(false),
|
||||
Limit: types.NewInt64(4121),
|
||||
Shares: &types.SharesInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Shares: 9000,
|
||||
Level: "custom",
|
||||
},
|
||||
OverheadLimit: nil,
|
||||
},
|
||||
MemoryAllocation: types.ResourceAllocationInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Reservation: types.NewInt64(961),
|
||||
ExpandableReservation: types.NewBool(false),
|
||||
Limit: types.NewInt64(961),
|
||||
Shares: &types.SharesInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Shares: 9000,
|
||||
Level: "custom",
|
||||
},
|
||||
OverheadLimit: nil,
|
||||
},
|
||||
},
|
||||
Runtime: types.ResourcePoolRuntimeInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Memory: types.ResourcePoolResourceUsage{
|
||||
DynamicData: types.DynamicData{},
|
||||
ReservationUsed: 0,
|
||||
ReservationUsedForVm: 0,
|
||||
UnreservedForPool: 1007681536,
|
||||
UnreservedForVm: 1007681536,
|
||||
OverallUsage: 0,
|
||||
MaxUsage: 1007681536,
|
||||
},
|
||||
Cpu: types.ResourcePoolResourceUsage{
|
||||
DynamicData: types.DynamicData{},
|
||||
ReservationUsed: 0,
|
||||
ReservationUsedForVm: 0,
|
||||
UnreservedForPool: 4121,
|
||||
UnreservedForVm: 4121,
|
||||
OverallUsage: 0,
|
||||
MaxUsage: 4121,
|
||||
},
|
||||
OverallStatus: "green",
|
||||
},
|
||||
QuickStats: (*types.ResourcePoolQuickStats)(nil),
|
||||
ConfiguredMemoryMB: 0,
|
||||
},
|
||||
Runtime: types.ResourcePoolRuntimeInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Memory: types.ResourcePoolResourceUsage{
|
||||
DynamicData: types.DynamicData{},
|
||||
ReservationUsed: 0,
|
||||
ReservationUsedForVm: 0,
|
||||
UnreservedForPool: 1007681536,
|
||||
UnreservedForVm: 1007681536,
|
||||
OverallUsage: 0,
|
||||
MaxUsage: 1007681536,
|
||||
},
|
||||
Cpu: types.ResourcePoolResourceUsage{
|
||||
DynamicData: types.DynamicData{},
|
||||
ReservationUsed: 0,
|
||||
ReservationUsedForVm: 0,
|
||||
UnreservedForPool: 4121,
|
||||
UnreservedForVm: 4121,
|
||||
OverallUsage: 0,
|
||||
MaxUsage: 4121,
|
||||
},
|
||||
OverallStatus: "green",
|
||||
},
|
||||
Owner: types.ManagedObjectReference{Type: "ComputeResource", Value: "ha-compute-res"},
|
||||
ResourcePool: nil,
|
||||
Vm: nil,
|
||||
Config: types.ResourceConfigSpec{
|
||||
DynamicData: types.DynamicData{},
|
||||
Entity: &types.ManagedObjectReference{Type: "ResourcePool", Value: "ha-root-pool"},
|
||||
ChangeVersion: "",
|
||||
LastModified: (*time.Time)(nil),
|
||||
CpuAllocation: types.ResourceAllocationInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Reservation: types.NewInt64(4121),
|
||||
ExpandableReservation: types.NewBool(false),
|
||||
Limit: types.NewInt64(4121),
|
||||
Shares: &types.SharesInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Shares: 9000,
|
||||
Level: "custom",
|
||||
},
|
||||
OverheadLimit: nil,
|
||||
},
|
||||
MemoryAllocation: types.ResourceAllocationInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Reservation: types.NewInt64(961),
|
||||
ExpandableReservation: types.NewBool(false),
|
||||
Limit: types.NewInt64(961),
|
||||
Shares: &types.SharesInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
Shares: 9000,
|
||||
Level: "custom",
|
||||
},
|
||||
OverheadLimit: nil,
|
||||
},
|
||||
},
|
||||
ChildConfiguration: nil,
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// RootFolder is the default template for the ServiceContent rootFolder property.
|
||||
// Capture method:
|
||||
// govc folder.info -dump /
|
||||
var RootFolder = mo.Folder{
|
||||
ManagedEntity: mo.ManagedEntity{
|
||||
ExtensibleManagedObject: mo.ExtensibleManagedObject{
|
||||
Self: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
|
||||
Value: nil,
|
||||
AvailableField: nil,
|
||||
},
|
||||
Parent: (*types.ManagedObjectReference)(nil),
|
||||
CustomValue: nil,
|
||||
OverallStatus: "green",
|
||||
ConfigStatus: "green",
|
||||
ConfigIssue: nil,
|
||||
EffectiveRole: []int32{-1},
|
||||
Permission: []types.Permission{
|
||||
{
|
||||
DynamicData: types.DynamicData{},
|
||||
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
|
||||
Principal: "vpxuser",
|
||||
Group: false,
|
||||
RoleId: -1,
|
||||
Propagate: true,
|
||||
},
|
||||
{
|
||||
DynamicData: types.DynamicData{},
|
||||
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
|
||||
Principal: "dcui",
|
||||
Group: false,
|
||||
RoleId: -1,
|
||||
Propagate: true,
|
||||
},
|
||||
{
|
||||
DynamicData: types.DynamicData{},
|
||||
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
|
||||
Principal: "root",
|
||||
Group: false,
|
||||
RoleId: -1,
|
||||
Propagate: true,
|
||||
},
|
||||
},
|
||||
Name: "ha-folder-root",
|
||||
DisabledMethod: nil,
|
||||
RecentTask: nil,
|
||||
DeclaredAlarmState: nil,
|
||||
TriggeredAlarmState: nil,
|
||||
AlarmActionsEnabled: (*bool)(nil),
|
||||
Tag: nil,
|
||||
},
|
||||
ChildType: []string{"Datacenter"},
|
||||
ChildEntity: nil,
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import "github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
// ServiceContent is the default template for the ServiceInstance content property.
|
||||
// Capture method:
|
||||
// govc object.collect -s -dump - content
|
||||
var ServiceContent = types.ServiceContent{
|
||||
RootFolder: types.ManagedObjectReference{Type: "Folder", Value: "ha-folder-root"},
|
||||
PropertyCollector: types.ManagedObjectReference{Type: "PropertyCollector", Value: "ha-property-collector"},
|
||||
ViewManager: &types.ManagedObjectReference{Type: "ViewManager", Value: "ViewManager"},
|
||||
About: types.AboutInfo{
|
||||
Name: "VMware ESXi",
|
||||
FullName: "VMware ESXi 6.5.0 build-5969303",
|
||||
Vendor: "VMware, Inc.",
|
||||
Version: "6.5.0",
|
||||
Build: "5969303",
|
||||
LocaleVersion: "INTL",
|
||||
LocaleBuild: "000",
|
||||
OsType: "vmnix-x86",
|
||||
ProductLineId: "embeddedEsx",
|
||||
ApiType: "HostAgent",
|
||||
ApiVersion: "6.5",
|
||||
InstanceUuid: "",
|
||||
LicenseProductName: "VMware ESX Server",
|
||||
LicenseProductVersion: "6.0",
|
||||
},
|
||||
Setting: &types.ManagedObjectReference{Type: "OptionManager", Value: "HostAgentSettings"},
|
||||
UserDirectory: &types.ManagedObjectReference{Type: "UserDirectory", Value: "ha-user-directory"},
|
||||
SessionManager: &types.ManagedObjectReference{Type: "SessionManager", Value: "ha-sessionmgr"},
|
||||
AuthorizationManager: &types.ManagedObjectReference{Type: "AuthorizationManager", Value: "ha-authmgr"},
|
||||
ServiceManager: &types.ManagedObjectReference{Type: "ServiceManager", Value: "ha-servicemanager"},
|
||||
PerfManager: &types.ManagedObjectReference{Type: "PerformanceManager", Value: "ha-perfmgr"},
|
||||
ScheduledTaskManager: (*types.ManagedObjectReference)(nil),
|
||||
AlarmManager: (*types.ManagedObjectReference)(nil),
|
||||
EventManager: &types.ManagedObjectReference{Type: "EventManager", Value: "ha-eventmgr"},
|
||||
TaskManager: &types.ManagedObjectReference{Type: "TaskManager", Value: "ha-taskmgr"},
|
||||
ExtensionManager: (*types.ManagedObjectReference)(nil),
|
||||
CustomizationSpecManager: (*types.ManagedObjectReference)(nil),
|
||||
CustomFieldsManager: (*types.ManagedObjectReference)(nil),
|
||||
AccountManager: &types.ManagedObjectReference{Type: "HostLocalAccountManager", Value: "ha-localacctmgr"},
|
||||
DiagnosticManager: &types.ManagedObjectReference{Type: "DiagnosticManager", Value: "ha-diagnosticmgr"},
|
||||
LicenseManager: &types.ManagedObjectReference{Type: "LicenseManager", Value: "ha-license-manager"},
|
||||
SearchIndex: &types.ManagedObjectReference{Type: "SearchIndex", Value: "ha-searchindex"},
|
||||
FileManager: &types.ManagedObjectReference{Type: "FileManager", Value: "ha-nfc-file-manager"},
|
||||
DatastoreNamespaceManager: &types.ManagedObjectReference{Type: "DatastoreNamespaceManager", Value: "ha-datastore-namespace-manager"},
|
||||
VirtualDiskManager: &types.ManagedObjectReference{Type: "VirtualDiskManager", Value: "ha-vdiskmanager"},
|
||||
VirtualizationManager: (*types.ManagedObjectReference)(nil),
|
||||
SnmpSystem: (*types.ManagedObjectReference)(nil),
|
||||
VmProvisioningChecker: (*types.ManagedObjectReference)(nil),
|
||||
VmCompatibilityChecker: (*types.ManagedObjectReference)(nil),
|
||||
OvfManager: &types.ManagedObjectReference{Type: "OvfManager", Value: "ha-ovf-manager"},
|
||||
IpPoolManager: (*types.ManagedObjectReference)(nil),
|
||||
DvSwitchManager: &types.ManagedObjectReference{Type: "DistributedVirtualSwitchManager", Value: "ha-dvsmanager"},
|
||||
HostProfileManager: (*types.ManagedObjectReference)(nil),
|
||||
ClusterProfileManager: (*types.ManagedObjectReference)(nil),
|
||||
ComplianceManager: (*types.ManagedObjectReference)(nil),
|
||||
LocalizationManager: &types.ManagedObjectReference{Type: "LocalizationManager", Value: "ha-l10n-manager"},
|
||||
StorageResourceManager: &types.ManagedObjectReference{Type: "StorageResourceManager", Value: "ha-storage-resource-manager"},
|
||||
GuestOperationsManager: &types.ManagedObjectReference{Type: "GuestOperationsManager", Value: "ha-guest-operations-manager"},
|
||||
OverheadMemoryManager: (*types.ManagedObjectReference)(nil),
|
||||
CertificateManager: (*types.ManagedObjectReference)(nil),
|
||||
IoFilterManager: (*types.ManagedObjectReference)(nil),
|
||||
VStorageObjectManager: &types.ManagedObjectReference{Type: "HostVStorageObjectManager", Value: "ha-vstorage-object-manager"},
|
||||
HostSpecManager: (*types.ManagedObjectReference)(nil),
|
||||
CryptoManager: &types.ManagedObjectReference{Type: "CryptoManager", Value: "ha-crypto-manager"},
|
||||
HealthUpdateManager: (*types.ManagedObjectReference)(nil),
|
||||
FailoverClusterConfigurator: (*types.ManagedObjectReference)(nil),
|
||||
FailoverClusterManager: (*types.ManagedObjectReference)(nil),
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import "github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
// HardwareVersion is the default VirtualMachine.Config.Version
|
||||
var HardwareVersion = "vmx-13"
|
||||
|
||||
// Setting is captured from ESX's HostSystem.configManager.advancedOption
|
||||
// Capture method:
|
||||
// govc object.collect -s -dump $(govc object.collect -s HostSystem:ha-host configManager.advancedOption) setting
|
||||
var Setting = []types.BaseOptionValue{
|
||||
// This list is currently pruned to include a single option for testing
|
||||
&types.OptionValue{
|
||||
Key: "Config.HostAgent.log.level",
|
||||
Value: "info",
|
||||
},
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,242 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package esx
|
||||
|
||||
import "github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
// VirtualDevice is the default set of VirtualDevice types created for a VirtualMachine
|
||||
// Capture method:
|
||||
// govc vm.create foo
|
||||
// govc object.collect -s -dump vm/foo config.hardware.device
|
||||
var VirtualDevice = []types.BaseVirtualDevice{
|
||||
&types.VirtualIDEController{
|
||||
VirtualController: types.VirtualController{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 200,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "IDE 0",
|
||||
Summary: "IDE 0",
|
||||
},
|
||||
Backing: nil,
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 0,
|
||||
UnitNumber: (*int32)(nil),
|
||||
},
|
||||
BusNumber: 0,
|
||||
Device: nil,
|
||||
},
|
||||
},
|
||||
&types.VirtualIDEController{
|
||||
VirtualController: types.VirtualController{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 201,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "IDE 1",
|
||||
Summary: "IDE 1",
|
||||
},
|
||||
Backing: nil,
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 0,
|
||||
UnitNumber: (*int32)(nil),
|
||||
},
|
||||
BusNumber: 1,
|
||||
Device: nil,
|
||||
},
|
||||
},
|
||||
&types.VirtualPS2Controller{
|
||||
VirtualController: types.VirtualController{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 300,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "PS2 controller 0",
|
||||
Summary: "PS2 controller 0",
|
||||
},
|
||||
Backing: nil,
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 0,
|
||||
UnitNumber: (*int32)(nil),
|
||||
},
|
||||
BusNumber: 0,
|
||||
Device: []int32{600, 700},
|
||||
},
|
||||
},
|
||||
&types.VirtualPCIController{
|
||||
VirtualController: types.VirtualController{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 100,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "PCI controller 0",
|
||||
Summary: "PCI controller 0",
|
||||
},
|
||||
Backing: nil,
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 0,
|
||||
UnitNumber: (*int32)(nil),
|
||||
},
|
||||
BusNumber: 0,
|
||||
Device: []int32{500, 12000},
|
||||
},
|
||||
},
|
||||
&types.VirtualSIOController{
|
||||
VirtualController: types.VirtualController{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 400,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "SIO controller 0",
|
||||
Summary: "SIO controller 0",
|
||||
},
|
||||
Backing: nil,
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 0,
|
||||
UnitNumber: (*int32)(nil),
|
||||
},
|
||||
BusNumber: 0,
|
||||
Device: nil,
|
||||
},
|
||||
},
|
||||
&types.VirtualKeyboard{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 600,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "Keyboard ",
|
||||
Summary: "Keyboard",
|
||||
},
|
||||
Backing: nil,
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 300,
|
||||
UnitNumber: types.NewInt32(0),
|
||||
},
|
||||
},
|
||||
&types.VirtualPointingDevice{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 700,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "Pointing device",
|
||||
Summary: "Pointing device; Device",
|
||||
},
|
||||
Backing: &types.VirtualPointingDeviceDeviceBackingInfo{
|
||||
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
|
||||
VirtualDeviceBackingInfo: types.VirtualDeviceBackingInfo{},
|
||||
DeviceName: "",
|
||||
UseAutoDetect: types.NewBool(false),
|
||||
},
|
||||
HostPointingDevice: "autodetect",
|
||||
},
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 300,
|
||||
UnitNumber: types.NewInt32(1),
|
||||
},
|
||||
},
|
||||
&types.VirtualMachineVideoCard{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 500,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "Video card ",
|
||||
Summary: "Video card",
|
||||
},
|
||||
Backing: nil,
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 100,
|
||||
UnitNumber: types.NewInt32(0),
|
||||
},
|
||||
VideoRamSizeInKB: 4096,
|
||||
NumDisplays: 1,
|
||||
UseAutoDetect: types.NewBool(false),
|
||||
Enable3DSupport: types.NewBool(false),
|
||||
Use3dRenderer: "automatic",
|
||||
GraphicsMemorySizeInKB: 262144,
|
||||
},
|
||||
&types.VirtualMachineVMCIDevice{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 12000,
|
||||
DeviceInfo: &types.Description{
|
||||
DynamicData: types.DynamicData{},
|
||||
Label: "VMCI device",
|
||||
Summary: "Device on the virtual machine PCI bus that provides support for the virtual machine communication interface",
|
||||
},
|
||||
Backing: nil,
|
||||
Connectable: (*types.VirtualDeviceConnectInfo)(nil),
|
||||
SlotInfo: nil,
|
||||
ControllerKey: 100,
|
||||
UnitNumber: types.NewInt32(17),
|
||||
},
|
||||
Id: -1,
|
||||
AllowUnrestrictedCommunication: types.NewBool(false),
|
||||
FilterEnable: types.NewBool(true),
|
||||
FilterInfo: (*types.VirtualMachineVMCIDeviceFilterInfo)(nil),
|
||||
},
|
||||
}
|
||||
|
||||
// EthernetCard template for types.VirtualEthernetCard
|
||||
var EthernetCard = types.VirtualE1000{
|
||||
VirtualEthernetCard: types.VirtualEthernetCard{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
DynamicData: types.DynamicData{},
|
||||
Key: 4000,
|
||||
Backing: &types.VirtualEthernetCardNetworkBackingInfo{
|
||||
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
|
||||
VirtualDeviceBackingInfo: types.VirtualDeviceBackingInfo{},
|
||||
DeviceName: "VM Network",
|
||||
UseAutoDetect: types.NewBool(false),
|
||||
},
|
||||
Network: (*types.ManagedObjectReference)(nil),
|
||||
InPassthroughMode: types.NewBool(false),
|
||||
},
|
||||
Connectable: &types.VirtualDeviceConnectInfo{
|
||||
DynamicData: types.DynamicData{},
|
||||
StartConnected: true,
|
||||
AllowGuestControl: true,
|
||||
Connected: false,
|
||||
Status: "untried",
|
||||
},
|
||||
SlotInfo: &types.VirtualDevicePciBusSlotInfo{
|
||||
VirtualDeviceBusSlotInfo: types.VirtualDeviceBusSlotInfo{},
|
||||
PciSlotNumber: 32,
|
||||
},
|
||||
ControllerKey: 100,
|
||||
UnitNumber: types.NewInt32(7),
|
||||
},
|
||||
AddressType: "generated",
|
||||
MacAddress: "",
|
||||
WakeOnLanEnabled: types.NewBool(true),
|
||||
},
|
||||
}
|
|
@ -1,494 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/ring"
|
||||
"log"
|
||||
"reflect"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var (
|
||||
maxPageSize = 1000
|
||||
logEvents = false
|
||||
)
|
||||
|
||||
type EventManager struct {
|
||||
mo.EventManager
|
||||
|
||||
root types.ManagedObjectReference
|
||||
page *ring.Ring
|
||||
key int32
|
||||
collectors map[types.ManagedObjectReference]*EventHistoryCollector
|
||||
templates map[string]*template.Template
|
||||
}
|
||||
|
||||
func (m *EventManager) init(r *Registry) {
|
||||
if len(m.Description.EventInfo) == 0 {
|
||||
m.Description.EventInfo = esx.EventInfo
|
||||
}
|
||||
if m.MaxCollector == 0 {
|
||||
m.MaxCollector = 1000
|
||||
}
|
||||
m.root = r.content().RootFolder
|
||||
m.page = ring.New(maxPageSize)
|
||||
m.collectors = make(map[types.ManagedObjectReference]*EventHistoryCollector)
|
||||
m.templates = make(map[string]*template.Template)
|
||||
}
|
||||
|
||||
func (m *EventManager) createCollector(ctx *Context, req *types.CreateCollectorForEvents) (*EventHistoryCollector, *soap.Fault) {
|
||||
size, err := validatePageSize(req.Filter.MaxCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(m.collectors) >= int(m.MaxCollector) {
|
||||
return nil, Fault("Too many event collectors to create", new(types.InvalidState))
|
||||
}
|
||||
|
||||
collector := &EventHistoryCollector{
|
||||
m: m,
|
||||
page: ring.New(size),
|
||||
}
|
||||
collector.Filter = req.Filter
|
||||
collector.fillPage(size)
|
||||
|
||||
return collector, nil
|
||||
}
|
||||
|
||||
func (m *EventManager) CreateCollectorForEvents(ctx *Context, req *types.CreateCollectorForEvents) soap.HasFault {
|
||||
body := new(methods.CreateCollectorForEventsBody)
|
||||
collector, err := m.createCollector(ctx, req)
|
||||
if err != nil {
|
||||
body.Fault_ = err
|
||||
return body
|
||||
}
|
||||
|
||||
ref := ctx.Session.Put(collector).Reference()
|
||||
m.collectors[ref] = collector
|
||||
|
||||
body.Res = &types.CreateCollectorForEventsResponse{
|
||||
Returnval: ref,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *EventManager) QueryEvents(ctx *Context, req *types.QueryEvents) soap.HasFault {
|
||||
if Map.IsESX() {
|
||||
return &methods.QueryEventsBody{
|
||||
Fault_: Fault("", new(types.NotImplemented)),
|
||||
}
|
||||
}
|
||||
|
||||
body := new(methods.QueryEventsBody)
|
||||
collector, err := m.createCollector(ctx, &types.CreateCollectorForEvents{Filter: req.Filter})
|
||||
if err != nil {
|
||||
body.Fault_ = err
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.QueryEventsResponse{
|
||||
Returnval: collector.GetLatestPage(),
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
// formatMessage applies the EventDescriptionEventDetail.FullFormat template to the given event's FullFormattedMessage field.
|
||||
func (m *EventManager) formatMessage(event types.BaseEvent) {
|
||||
id := reflect.ValueOf(event).Elem().Type().Name()
|
||||
e := event.GetEvent()
|
||||
|
||||
t, ok := m.templates[id]
|
||||
if !ok {
|
||||
for _, info := range m.Description.EventInfo {
|
||||
if info.Key == id {
|
||||
t = template.Must(template.New(id).Parse(info.FullFormat))
|
||||
m.templates[id] = t
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if t != nil {
|
||||
var buf bytes.Buffer
|
||||
if err := t.Execute(&buf, event); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
e.FullFormattedMessage = buf.String()
|
||||
}
|
||||
|
||||
if logEvents {
|
||||
log.Printf("[%s] %s", id, e.FullFormattedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *EventManager) PostEvent(ctx *Context, req *types.PostEvent) soap.HasFault {
|
||||
m.key++
|
||||
event := req.EventToPost.GetEvent()
|
||||
event.Key = m.key
|
||||
event.ChainId = event.Key
|
||||
event.CreatedTime = time.Now()
|
||||
event.UserName = ctx.Session.UserName
|
||||
|
||||
m.page = m.page.Prev()
|
||||
m.page.Value = req.EventToPost
|
||||
m.formatMessage(req.EventToPost)
|
||||
|
||||
for _, c := range m.collectors {
|
||||
ctx.WithLock(c, func() {
|
||||
if c.eventMatches(req.EventToPost) {
|
||||
c.page = c.page.Prev()
|
||||
c.page.Value = req.EventToPost
|
||||
Map.Update(c, []types.PropertyChange{{Name: "latestPage", Val: c.GetLatestPage()}})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return &methods.PostEventBody{
|
||||
Res: new(types.PostEventResponse),
|
||||
}
|
||||
}
|
||||
|
||||
type EventHistoryCollector struct {
|
||||
mo.EventHistoryCollector
|
||||
|
||||
m *EventManager
|
||||
page *ring.Ring
|
||||
pos int
|
||||
}
|
||||
|
||||
// doEntityEventArgument calls f for each entity argument in the event.
|
||||
// If f returns true, the iteration stops.
|
||||
func doEntityEventArgument(event types.BaseEvent, f func(types.ManagedObjectReference, *types.EntityEventArgument) bool) bool {
|
||||
e := event.GetEvent()
|
||||
|
||||
if arg := e.Vm; arg != nil {
|
||||
if f(arg.Vm, &arg.EntityEventArgument) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if arg := e.Host; arg != nil {
|
||||
if f(arg.Host, &arg.EntityEventArgument) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if arg := e.ComputeResource; arg != nil {
|
||||
if f(arg.ComputeResource, &arg.EntityEventArgument) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if arg := e.Ds; arg != nil {
|
||||
if f(arg.Datastore, &arg.EntityEventArgument) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if arg := e.Net; arg != nil {
|
||||
if f(arg.Network, &arg.EntityEventArgument) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if arg := e.Dvs; arg != nil {
|
||||
if f(arg.Dvs, &arg.EntityEventArgument) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if arg := e.Datacenter; arg != nil {
|
||||
if f(arg.Datacenter, &arg.EntityEventArgument) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// eventFilterSelf returns true if self is one of the entity arguments in the event.
|
||||
func eventFilterSelf(event types.BaseEvent, self types.ManagedObjectReference) bool {
|
||||
return doEntityEventArgument(event, func(ref types.ManagedObjectReference, _ *types.EntityEventArgument) bool {
|
||||
return self == ref
|
||||
})
|
||||
}
|
||||
|
||||
// eventFilterChildren returns true if a child of self is one of the entity arguments in the event.
|
||||
func eventFilterChildren(event types.BaseEvent, self types.ManagedObjectReference) bool {
|
||||
return doEntityEventArgument(event, func(ref types.ManagedObjectReference, _ *types.EntityEventArgument) bool {
|
||||
seen := false
|
||||
|
||||
var match func(types.ManagedObjectReference)
|
||||
|
||||
match = func(child types.ManagedObjectReference) {
|
||||
if child == self {
|
||||
seen = true
|
||||
return
|
||||
}
|
||||
|
||||
walk(child, match)
|
||||
}
|
||||
|
||||
walk(ref, match)
|
||||
|
||||
return seen
|
||||
})
|
||||
}
|
||||
|
||||
// entityMatches returns true if the spec Entity filter matches the event.
|
||||
func (c *EventHistoryCollector) entityMatches(event types.BaseEvent, spec *types.EventFilterSpec) bool {
|
||||
e := spec.Entity
|
||||
if e == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
isRootFolder := c.m.root == e.Entity
|
||||
|
||||
switch e.Recursion {
|
||||
case types.EventFilterSpecRecursionOptionSelf:
|
||||
return isRootFolder || eventFilterSelf(event, e.Entity)
|
||||
case types.EventFilterSpecRecursionOptionChildren:
|
||||
return eventFilterChildren(event, e.Entity)
|
||||
case types.EventFilterSpecRecursionOptionAll:
|
||||
if isRootFolder || eventFilterSelf(event, e.Entity) {
|
||||
return true
|
||||
}
|
||||
return eventFilterChildren(event, e.Entity)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// typeMatches returns true if one of the spec EventTypeId types matches the event.
|
||||
func (c *EventHistoryCollector) typeMatches(event types.BaseEvent, spec *types.EventFilterSpec) bool {
|
||||
if len(spec.EventTypeId) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
matches := func(name string) bool {
|
||||
for _, id := range spec.EventTypeId {
|
||||
if id == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
kind := reflect.ValueOf(event).Elem().Type()
|
||||
|
||||
if matches(kind.Name()) {
|
||||
return true // concrete type
|
||||
}
|
||||
|
||||
field, ok := kind.FieldByNameFunc(matches)
|
||||
if ok {
|
||||
return field.Anonymous // base type (embedded field)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// eventMatches returns true one of the filters matches the event.
|
||||
func (c *EventHistoryCollector) eventMatches(event types.BaseEvent) bool {
|
||||
spec := c.Filter.(types.EventFilterSpec)
|
||||
|
||||
if !c.typeMatches(event, &spec) {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: spec.Time, spec.UserName, etc
|
||||
|
||||
return c.entityMatches(event, &spec)
|
||||
}
|
||||
|
||||
// filePage copies the manager's latest events into the collector's page with Filter applied.
|
||||
func (c *EventHistoryCollector) fillPage(size int) {
|
||||
c.pos = 0
|
||||
l := c.page.Len()
|
||||
delta := size - l
|
||||
|
||||
if delta < 0 {
|
||||
// Shrink ring size
|
||||
c.page = c.page.Unlink(-delta)
|
||||
return
|
||||
}
|
||||
|
||||
matches := 0
|
||||
mpage := c.m.page
|
||||
page := c.page
|
||||
|
||||
if delta != 0 {
|
||||
// Grow ring size
|
||||
c.page = c.page.Link(ring.New(delta))
|
||||
}
|
||||
|
||||
for i := 0; i < maxPageSize; i++ {
|
||||
event, ok := mpage.Value.(types.BaseEvent)
|
||||
mpage = mpage.Prev()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if c.eventMatches(event) {
|
||||
page.Value = event
|
||||
page = page.Prev()
|
||||
matches++
|
||||
if matches == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validatePageSize(count int32) (int, *soap.Fault) {
|
||||
size := int(count)
|
||||
|
||||
if size == 0 {
|
||||
size = 10 // defaultPageSize
|
||||
} else if size < 0 || size > maxPageSize {
|
||||
return -1, Fault("", &types.InvalidArgument{InvalidProperty: "maxCount"})
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (c *EventHistoryCollector) SetCollectorPageSize(ctx *Context, req *types.SetCollectorPageSize) soap.HasFault {
|
||||
body := new(methods.SetCollectorPageSizeBody)
|
||||
size, err := validatePageSize(req.MaxCount)
|
||||
if err != nil {
|
||||
body.Fault_ = err
|
||||
return body
|
||||
}
|
||||
|
||||
ctx.WithLock(c.m, func() {
|
||||
c.fillPage(size)
|
||||
})
|
||||
|
||||
body.Res = new(types.SetCollectorPageSizeResponse)
|
||||
return body
|
||||
}
|
||||
|
||||
func (c *EventHistoryCollector) ResetCollector(ctx *Context, req *types.ResetCollector) soap.HasFault {
|
||||
c.pos = len(c.GetLatestPage())
|
||||
|
||||
return &methods.ResetCollectorBody{
|
||||
Res: new(types.ResetCollectorResponse),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *EventHistoryCollector) RewindCollector(ctx *Context, req *types.RewindCollector) soap.HasFault {
|
||||
c.pos = 0
|
||||
return &methods.RewindCollectorBody{
|
||||
Res: new(types.RewindCollectorResponse),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *EventHistoryCollector) ReadNextEvents(ctx *Context, req *types.ReadNextEvents) soap.HasFault {
|
||||
body := &methods.ReadNextEventsBody{}
|
||||
if req.MaxCount <= 0 {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "maxCount"})
|
||||
return body
|
||||
}
|
||||
body.Res = new(types.ReadNextEventsResponse)
|
||||
|
||||
events := c.GetLatestPage()
|
||||
nevents := len(events)
|
||||
if c.pos == nevents {
|
||||
return body // already read to EOF
|
||||
}
|
||||
|
||||
start := c.pos
|
||||
end := start + int(req.MaxCount)
|
||||
c.pos += int(req.MaxCount)
|
||||
if end > nevents {
|
||||
end = nevents
|
||||
c.pos = nevents
|
||||
}
|
||||
|
||||
body.Res.Returnval = events[start:end]
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (c *EventHistoryCollector) ReadPreviousEvents(ctx *Context, req *types.ReadPreviousEvents) soap.HasFault {
|
||||
body := &methods.ReadPreviousEventsBody{}
|
||||
if req.MaxCount <= 0 {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "maxCount"})
|
||||
return body
|
||||
}
|
||||
body.Res = new(types.ReadPreviousEventsResponse)
|
||||
|
||||
events := c.GetLatestPage()
|
||||
if c.pos == 0 {
|
||||
return body // already read to EOF
|
||||
}
|
||||
|
||||
start := c.pos - int(req.MaxCount)
|
||||
end := c.pos
|
||||
c.pos -= int(req.MaxCount)
|
||||
if start < 0 {
|
||||
start = 0
|
||||
c.pos = 0
|
||||
}
|
||||
|
||||
body.Res.Returnval = events[start:end]
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (c *EventHistoryCollector) DestroyCollector(ctx *Context, req *types.DestroyCollector) soap.HasFault {
|
||||
ctx.Session.Remove(req.This)
|
||||
|
||||
ctx.WithLock(c.m, func() {
|
||||
delete(c.m.collectors, req.This)
|
||||
})
|
||||
|
||||
return &methods.DestroyCollectorBody{
|
||||
Res: new(types.DestroyCollectorResponse),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *EventHistoryCollector) GetLatestPage() []types.BaseEvent {
|
||||
var latestPage []types.BaseEvent
|
||||
|
||||
c.page.Do(func(val interface{}) {
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
latestPage = append(latestPage, val.(types.BaseEvent))
|
||||
})
|
||||
|
||||
return latestPage
|
||||
}
|
||||
|
||||
func (c *EventHistoryCollector) Get() mo.Reference {
|
||||
clone := *c
|
||||
|
||||
clone.LatestPage = clone.GetLatestPage()
|
||||
|
||||
return &clone
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type FileManager struct {
|
||||
mo.FileManager
|
||||
}
|
||||
|
||||
func (f *FileManager) findDatastore(ref mo.Reference, name string) (*Datastore, types.BaseMethodFault) {
|
||||
var refs []types.ManagedObjectReference
|
||||
|
||||
if d, ok := asFolderMO(ref); ok {
|
||||
refs = d.ChildEntity
|
||||
}
|
||||
if p, ok := ref.(*StoragePod); ok {
|
||||
refs = p.ChildEntity
|
||||
}
|
||||
|
||||
for _, ref := range refs {
|
||||
obj := Map.Get(ref)
|
||||
|
||||
if ds, ok := obj.(*Datastore); ok && ds.Name == name {
|
||||
return ds, nil
|
||||
}
|
||||
if p, ok := obj.(*StoragePod); ok {
|
||||
ds, _ := f.findDatastore(p, name)
|
||||
if ds != nil {
|
||||
return ds, nil
|
||||
}
|
||||
}
|
||||
if d, ok := asFolderMO(obj); ok {
|
||||
ds, _ := f.findDatastore(d, name)
|
||||
if ds != nil {
|
||||
return ds, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, &types.InvalidDatastore{Name: name}
|
||||
}
|
||||
|
||||
func (f *FileManager) resolve(dc *types.ManagedObjectReference, name string) (string, types.BaseMethodFault) {
|
||||
p, fault := parseDatastorePath(name)
|
||||
if fault != nil {
|
||||
return "", fault
|
||||
}
|
||||
|
||||
if dc == nil {
|
||||
if Map.IsESX() {
|
||||
dc = &esx.Datacenter.Self
|
||||
} else {
|
||||
return "", &types.InvalidArgument{InvalidProperty: "dc"}
|
||||
}
|
||||
}
|
||||
|
||||
folder := Map.Get(*dc).(*Datacenter).DatastoreFolder
|
||||
|
||||
ds, fault := f.findDatastore(Map.Get(folder), p.Datastore)
|
||||
if fault != nil {
|
||||
return "", fault
|
||||
}
|
||||
|
||||
dir := ds.Info.GetDatastoreInfo().Url
|
||||
|
||||
return path.Join(dir, p.Path), nil
|
||||
}
|
||||
|
||||
func (f *FileManager) fault(name string, err error, fault types.BaseFileFault) types.BaseMethodFault {
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
fault = new(types.FileNotFound)
|
||||
case os.IsExist(err):
|
||||
fault = new(types.FileAlreadyExists)
|
||||
}
|
||||
|
||||
fault.GetFileFault().File = name
|
||||
|
||||
return fault.(types.BaseMethodFault)
|
||||
}
|
||||
|
||||
func (f *FileManager) deleteDatastoreFile(req *types.DeleteDatastoreFile_Task) types.BaseMethodFault {
|
||||
file, fault := f.resolve(req.Datacenter, req.Name)
|
||||
if fault != nil {
|
||||
return fault
|
||||
}
|
||||
|
||||
_, err := os.Stat(file)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return f.fault(file, err, new(types.CannotDeleteFile))
|
||||
}
|
||||
}
|
||||
|
||||
err = os.RemoveAll(file)
|
||||
if err != nil {
|
||||
return f.fault(file, err, new(types.CannotDeleteFile))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileManager) DeleteDatastoreFileTask(req *types.DeleteDatastoreFile_Task) soap.HasFault {
|
||||
task := CreateTask(f, "deleteDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
return nil, f.deleteDatastoreFile(req)
|
||||
})
|
||||
|
||||
return &methods.DeleteDatastoreFile_TaskBody{
|
||||
Res: &types.DeleteDatastoreFile_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FileManager) MakeDirectory(req *types.MakeDirectory) soap.HasFault {
|
||||
body := &methods.MakeDirectoryBody{}
|
||||
|
||||
name, fault := f.resolve(req.Datacenter, req.Name)
|
||||
if fault != nil {
|
||||
body.Fault_ = Fault("", fault)
|
||||
return body
|
||||
}
|
||||
|
||||
mkdir := os.Mkdir
|
||||
|
||||
if isTrue(req.CreateParentDirectories) {
|
||||
mkdir = os.MkdirAll
|
||||
}
|
||||
|
||||
err := mkdir(name, 0700)
|
||||
if err != nil {
|
||||
fault = f.fault(req.Name, err, new(types.CannotCreateFile))
|
||||
body.Fault_ = Fault(err.Error(), fault)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = new(types.MakeDirectoryResponse)
|
||||
return body
|
||||
}
|
||||
|
||||
func (f *FileManager) moveDatastoreFile(req *types.MoveDatastoreFile_Task) types.BaseMethodFault {
|
||||
src, fault := f.resolve(req.SourceDatacenter, req.SourceName)
|
||||
if fault != nil {
|
||||
return fault
|
||||
}
|
||||
|
||||
dst, fault := f.resolve(req.DestinationDatacenter, req.DestinationName)
|
||||
if fault != nil {
|
||||
return fault
|
||||
}
|
||||
|
||||
if !isTrue(req.Force) {
|
||||
_, err := os.Stat(dst)
|
||||
if err == nil {
|
||||
return f.fault(dst, nil, new(types.FileAlreadyExists))
|
||||
}
|
||||
}
|
||||
|
||||
err := os.Rename(src, dst)
|
||||
if err != nil {
|
||||
return f.fault(src, err, new(types.CannotAccessFile))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileManager) MoveDatastoreFileTask(req *types.MoveDatastoreFile_Task) soap.HasFault {
|
||||
task := CreateTask(f, "moveDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
return nil, f.moveDatastoreFile(req)
|
||||
})
|
||||
|
||||
return &methods.MoveDatastoreFile_TaskBody{
|
||||
Res: &types.MoveDatastoreFile_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FileManager) copyDatastoreFile(req *types.CopyDatastoreFile_Task) types.BaseMethodFault {
|
||||
src, fault := f.resolve(req.SourceDatacenter, req.SourceName)
|
||||
if fault != nil {
|
||||
return fault
|
||||
}
|
||||
|
||||
dst, fault := f.resolve(req.DestinationDatacenter, req.DestinationName)
|
||||
if fault != nil {
|
||||
return fault
|
||||
}
|
||||
|
||||
if !isTrue(req.Force) {
|
||||
_, err := os.Stat(dst)
|
||||
if err == nil {
|
||||
return f.fault(dst, nil, new(types.FileAlreadyExists))
|
||||
}
|
||||
}
|
||||
|
||||
r, err := os.Open(filepath.Clean(src))
|
||||
if err != nil {
|
||||
return f.fault(dst, err, new(types.CannotAccessFile))
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
w, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return f.fault(dst, err, new(types.CannotCreateFile))
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
if _, err = io.Copy(w, r); err != nil {
|
||||
return f.fault(dst, err, new(types.CannotCreateFile))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileManager) CopyDatastoreFileTask(req *types.CopyDatastoreFile_Task) soap.HasFault {
|
||||
task := CreateTask(f, "copyDatastoreFile", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
return nil, f.copyDatastoreFile(req)
|
||||
})
|
||||
|
||||
return &methods.CopyDatastoreFile_TaskBody{
|
||||
Res: &types.CopyDatastoreFile_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,690 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type Folder struct {
|
||||
mo.Folder
|
||||
}
|
||||
|
||||
func asFolderMO(obj mo.Reference) (*mo.Folder, bool) {
|
||||
if obj == nil {
|
||||
return nil, false
|
||||
}
|
||||
f, ok := getManagedObject(obj).Addr().Interface().(*mo.Folder)
|
||||
return f, ok
|
||||
}
|
||||
|
||||
func folderEventArgument(f *mo.Folder) types.FolderEventArgument {
|
||||
return types.FolderEventArgument{
|
||||
Folder: f.Self,
|
||||
EntityEventArgument: types.EntityEventArgument{Name: f.Name},
|
||||
}
|
||||
}
|
||||
|
||||
// update references when objects are added/removed from a Folder
|
||||
func folderUpdate(f *mo.Folder, o mo.Reference, u func(mo.Reference, *[]types.ManagedObjectReference, types.ManagedObjectReference)) {
|
||||
ref := o.Reference()
|
||||
|
||||
if f.Parent == nil {
|
||||
return // this is the root folder
|
||||
}
|
||||
|
||||
switch ref.Type {
|
||||
case "Datacenter", "Folder":
|
||||
return // nothing to update
|
||||
}
|
||||
|
||||
dc := Map.getEntityDatacenter(f)
|
||||
|
||||
switch ref.Type {
|
||||
case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup":
|
||||
u(dc, &dc.Network, ref)
|
||||
case "Datastore":
|
||||
u(dc, &dc.Datastore, ref)
|
||||
}
|
||||
}
|
||||
|
||||
func networkSummary(n *mo.Network) types.BaseNetworkSummary {
|
||||
if n.Summary != nil {
|
||||
return n.Summary
|
||||
}
|
||||
return &types.NetworkSummary{
|
||||
Network: &n.Self,
|
||||
Name: n.Name,
|
||||
Accessible: true,
|
||||
}
|
||||
}
|
||||
|
||||
func folderPutChild(ctx *Context, f *mo.Folder, o mo.Entity) {
|
||||
Map.PutEntity(f, o)
|
||||
|
||||
ctx.WithLock(f, func() {
|
||||
f.ChildEntity = append(f.ChildEntity, o.Reference())
|
||||
folderUpdate(f, o, Map.AddReference)
|
||||
|
||||
ctx.WithLock(o, func() {
|
||||
switch e := o.(type) {
|
||||
case *mo.Network:
|
||||
e.Summary = networkSummary(e)
|
||||
case *mo.OpaqueNetwork:
|
||||
e.Summary = networkSummary(&e.Network)
|
||||
case *DistributedVirtualPortgroup:
|
||||
e.Summary = networkSummary(&e.Network)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func folderRemoveChild(ctx *Context, f *mo.Folder, o mo.Reference) {
|
||||
Map.Remove(o.Reference())
|
||||
|
||||
ctx.WithLock(f, func() {
|
||||
RemoveReference(&f.ChildEntity, o.Reference())
|
||||
|
||||
folderUpdate(f, o, Map.RemoveReference)
|
||||
})
|
||||
}
|
||||
|
||||
func folderHasChildType(f *mo.Folder, kind string) bool {
|
||||
for _, t := range f.ChildType {
|
||||
if t == kind {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *Folder) typeNotSupported() *soap.Fault {
|
||||
return Fault(fmt.Sprintf("%s supports types: %#v", f.Self, f.ChildType), &types.NotSupported{})
|
||||
}
|
||||
|
||||
// AddOpaqueNetwork adds an OpaqueNetwork type to the inventory, with default backing to that of an nsx.LogicalSwitch.
|
||||
// The vSphere API does not have a method to add this directly, so it must either be called directly or via Model.OpaqueNetwork setting.
|
||||
func (f *Folder) AddOpaqueNetwork(summary types.OpaqueNetworkSummary) error {
|
||||
if !folderHasChildType(&f.Folder, "Network") {
|
||||
return errors.New("not a network folder")
|
||||
}
|
||||
|
||||
if summary.OpaqueNetworkId == "" {
|
||||
summary.OpaqueNetworkId = uuid.New().String()
|
||||
}
|
||||
if summary.OpaqueNetworkType == "" {
|
||||
summary.OpaqueNetworkType = "nsx.LogicalSwitch"
|
||||
}
|
||||
if summary.Name == "" {
|
||||
summary.Name = summary.OpaqueNetworkType + "-" + summary.OpaqueNetworkId
|
||||
}
|
||||
|
||||
net := new(mo.OpaqueNetwork)
|
||||
if summary.Network == nil {
|
||||
summary.Network = &net.Self
|
||||
} else {
|
||||
net.Self = *summary.Network
|
||||
}
|
||||
summary.Accessible = true
|
||||
net.Network.Name = summary.Name
|
||||
net.Summary = &summary
|
||||
|
||||
folderPutChild(internalContext, &f.Folder, net)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type addStandaloneHost struct {
|
||||
*Folder
|
||||
ctx *Context
|
||||
req *types.AddStandaloneHost_Task
|
||||
}
|
||||
|
||||
func (add *addStandaloneHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
host, err := CreateStandaloneHost(add.ctx, add.Folder, add.req.Spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if add.req.AddConnected {
|
||||
host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
|
||||
}
|
||||
|
||||
return host.Reference(), nil
|
||||
}
|
||||
|
||||
func (f *Folder) AddStandaloneHostTask(ctx *Context, a *types.AddStandaloneHost_Task) soap.HasFault {
|
||||
r := &methods.AddStandaloneHost_TaskBody{}
|
||||
|
||||
if folderHasChildType(&f.Folder, "ComputeResource") && folderHasChildType(&f.Folder, "Folder") {
|
||||
r.Res = &types.AddStandaloneHost_TaskResponse{
|
||||
Returnval: NewTask(&addStandaloneHost{f, ctx, a}).Run(),
|
||||
}
|
||||
} else {
|
||||
r.Fault_ = f.typeNotSupported()
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *Folder) CreateFolder(ctx *Context, c *types.CreateFolder) soap.HasFault {
|
||||
r := &methods.CreateFolderBody{}
|
||||
|
||||
if folderHasChildType(&f.Folder, "Folder") {
|
||||
if obj := Map.FindByName(c.Name, f.ChildEntity); obj != nil {
|
||||
r.Fault_ = Fault("", &types.DuplicateName{
|
||||
Name: c.Name,
|
||||
Object: f.Self,
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
folder := &Folder{}
|
||||
|
||||
folder.Name = c.Name
|
||||
folder.ChildType = f.ChildType
|
||||
|
||||
folderPutChild(ctx, &f.Folder, folder)
|
||||
|
||||
r.Res = &types.CreateFolderResponse{
|
||||
Returnval: folder.Self,
|
||||
}
|
||||
} else {
|
||||
r.Fault_ = f.typeNotSupported()
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// StoragePod aka "Datastore Cluster"
|
||||
type StoragePod struct {
|
||||
mo.StoragePod
|
||||
}
|
||||
|
||||
func (f *Folder) CreateStoragePod(ctx *Context, c *types.CreateStoragePod) soap.HasFault {
|
||||
r := &methods.CreateStoragePodBody{}
|
||||
|
||||
if folderHasChildType(&f.Folder, "StoragePod") {
|
||||
if obj := Map.FindByName(c.Name, f.ChildEntity); obj != nil {
|
||||
r.Fault_ = Fault("", &types.DuplicateName{
|
||||
Name: c.Name,
|
||||
Object: f.Self,
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
pod := &StoragePod{}
|
||||
|
||||
pod.Name = c.Name
|
||||
pod.ChildType = []string{"Datastore"}
|
||||
pod.Summary = new(types.StoragePodSummary)
|
||||
pod.PodStorageDrsEntry = new(types.PodStorageDrsEntry)
|
||||
pod.PodStorageDrsEntry.StorageDrsConfig.PodConfig.Enabled = true
|
||||
|
||||
folderPutChild(ctx, &f.Folder, pod)
|
||||
|
||||
r.Res = &types.CreateStoragePodResponse{
|
||||
Returnval: pod.Self,
|
||||
}
|
||||
} else {
|
||||
r.Fault_ = f.typeNotSupported()
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *StoragePod) MoveIntoFolderTask(ctx *Context, c *types.MoveIntoFolder_Task) soap.HasFault {
|
||||
f := &Folder{Folder: p.Folder}
|
||||
res := f.MoveIntoFolderTask(ctx, c)
|
||||
p.ChildEntity = append(p.ChildEntity, f.ChildEntity...)
|
||||
return res
|
||||
}
|
||||
|
||||
func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.HasFault {
|
||||
r := &methods.CreateDatacenterBody{}
|
||||
|
||||
if folderHasChildType(&f.Folder, "Datacenter") && folderHasChildType(&f.Folder, "Folder") {
|
||||
dc := NewDatacenter(ctx, &f.Folder)
|
||||
|
||||
dc.Name = c.Name
|
||||
|
||||
r.Res = &types.CreateDatacenterResponse{
|
||||
Returnval: dc.Self,
|
||||
}
|
||||
|
||||
ctx.postEvent(&types.DatacenterCreatedEvent{
|
||||
DatacenterEvent: types.DatacenterEvent{
|
||||
Event: types.Event{
|
||||
Datacenter: datacenterEventArgument(dc),
|
||||
},
|
||||
},
|
||||
Parent: folderEventArgument(&f.Folder),
|
||||
})
|
||||
} else {
|
||||
r.Fault_ = f.typeNotSupported()
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *Folder) CreateClusterEx(ctx *Context, c *types.CreateClusterEx) soap.HasFault {
|
||||
r := &methods.CreateClusterExBody{}
|
||||
|
||||
if folderHasChildType(&f.Folder, "ComputeResource") && folderHasChildType(&f.Folder, "Folder") {
|
||||
cluster, err := CreateClusterComputeResource(ctx, f, c.Name, c.Spec)
|
||||
if err != nil {
|
||||
r.Fault_ = Fault("", err)
|
||||
return r
|
||||
}
|
||||
|
||||
r.Res = &types.CreateClusterExResponse{
|
||||
Returnval: cluster.Self,
|
||||
}
|
||||
} else {
|
||||
r.Fault_ = f.typeNotSupported()
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type createVM struct {
|
||||
*Folder
|
||||
|
||||
ctx *Context
|
||||
req *types.CreateVM_Task
|
||||
|
||||
register bool
|
||||
}
|
||||
|
||||
// hostsWithDatastore returns hosts that have access to the given datastore path
|
||||
func hostsWithDatastore(hosts []types.ManagedObjectReference, path string) []types.ManagedObjectReference {
|
||||
attached := hosts[:0]
|
||||
var p object.DatastorePath
|
||||
p.FromString(path)
|
||||
|
||||
for _, host := range hosts {
|
||||
h := Map.Get(host).(*HostSystem)
|
||||
if Map.FindByName(p.Datastore, h.Datastore) != nil {
|
||||
attached = append(attached, host)
|
||||
}
|
||||
}
|
||||
|
||||
return attached
|
||||
}
|
||||
|
||||
func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
vm, err := NewVirtualMachine(c.ctx, c.Folder.Self, &c.req.Config)
|
||||
if err != nil {
|
||||
folderRemoveChild(c.ctx, &c.Folder.Folder, vm)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vm.ResourcePool = &c.req.Pool
|
||||
|
||||
if c.req.Host == nil {
|
||||
pool := Map.Get(c.req.Pool).(mo.Entity)
|
||||
cr := Map.getEntityComputeResource(pool)
|
||||
|
||||
Map.WithLock(cr, func() {
|
||||
var hosts []types.ManagedObjectReference
|
||||
switch cr := cr.(type) {
|
||||
case *mo.ComputeResource:
|
||||
hosts = cr.Host
|
||||
case *ClusterComputeResource:
|
||||
hosts = cr.Host
|
||||
}
|
||||
|
||||
hosts = hostsWithDatastore(hosts, c.req.Config.Files.VmPathName)
|
||||
host := hosts[rand.Intn(len(hosts))]
|
||||
vm.Runtime.Host = &host
|
||||
})
|
||||
} else {
|
||||
vm.Runtime.Host = c.req.Host
|
||||
}
|
||||
|
||||
vm.Guest = &types.GuestInfo{
|
||||
ToolsStatus: types.VirtualMachineToolsStatusToolsNotInstalled,
|
||||
ToolsVersion: "0",
|
||||
ToolsRunningStatus: string(types.VirtualMachineToolsRunningStatusGuestToolsNotRunning),
|
||||
}
|
||||
|
||||
vm.Summary.Guest = &types.VirtualMachineGuestSummary{
|
||||
ToolsStatus: vm.Guest.ToolsStatus,
|
||||
}
|
||||
vm.Summary.Config.VmPathName = vm.Config.Files.VmPathName
|
||||
vm.Summary.Runtime.Host = vm.Runtime.Host
|
||||
|
||||
err = vm.create(&c.req.Config, c.register)
|
||||
if err != nil {
|
||||
folderRemoveChild(c.ctx, &c.Folder.Folder, vm)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
|
||||
Map.AppendReference(host, &host.Vm, vm.Self)
|
||||
vm.EnvironmentBrowser = *hostParent(&host.HostSystem).EnvironmentBrowser
|
||||
|
||||
for i := range vm.Datastore {
|
||||
ds := Map.Get(vm.Datastore[i]).(*Datastore)
|
||||
Map.AppendReference(ds, &ds.Vm, vm.Self)
|
||||
}
|
||||
|
||||
pool := Map.Get(*vm.ResourcePool)
|
||||
// This can be an internal call from VirtualApp.CreateChildVMTask, where pool is already locked.
|
||||
c.ctx.WithLock(pool, func() {
|
||||
if rp, ok := asResourcePoolMO(pool); ok {
|
||||
rp.Vm = append(rp.Vm, vm.Self)
|
||||
}
|
||||
if vapp, ok := pool.(*VirtualApp); ok {
|
||||
vapp.Vm = append(vapp.Vm, vm.Self)
|
||||
}
|
||||
})
|
||||
|
||||
event := vm.event()
|
||||
c.ctx.postEvent(
|
||||
&types.VmBeingCreatedEvent{
|
||||
VmEvent: event,
|
||||
ConfigSpec: &c.req.Config,
|
||||
},
|
||||
&types.VmInstanceUuidAssignedEvent{
|
||||
VmEvent: event,
|
||||
InstanceUuid: vm.Config.InstanceUuid,
|
||||
},
|
||||
&types.VmUuidAssignedEvent{
|
||||
VmEvent: event,
|
||||
Uuid: vm.Config.Uuid,
|
||||
},
|
||||
&types.VmCreatedEvent{
|
||||
VmEvent: event,
|
||||
},
|
||||
)
|
||||
|
||||
vm.RefreshStorageInfo(c.ctx, nil)
|
||||
|
||||
return vm.Reference(), nil
|
||||
}
|
||||
|
||||
func (f *Folder) CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault {
|
||||
return &methods.CreateVM_TaskBody{
|
||||
Res: &types.CreateVM_TaskResponse{
|
||||
Returnval: NewTask(&createVM{f, ctx, c, false}).Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type registerVM struct {
|
||||
*Folder
|
||||
|
||||
ctx *Context
|
||||
req *types.RegisterVM_Task
|
||||
}
|
||||
|
||||
func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
host := c.req.Host
|
||||
pool := c.req.Pool
|
||||
|
||||
if c.req.AsTemplate {
|
||||
if host == nil {
|
||||
return nil, &types.InvalidArgument{InvalidProperty: "host"}
|
||||
} else if pool != nil {
|
||||
return nil, &types.InvalidArgument{InvalidProperty: "pool"}
|
||||
}
|
||||
|
||||
pool = hostParent(&Map.Get(*host).(*HostSystem).HostSystem).ResourcePool
|
||||
} else {
|
||||
if pool == nil {
|
||||
return nil, &types.InvalidArgument{InvalidProperty: "pool"}
|
||||
}
|
||||
}
|
||||
|
||||
if c.req.Path == "" {
|
||||
return nil, &types.InvalidArgument{InvalidProperty: "path"}
|
||||
}
|
||||
|
||||
s := Map.SearchIndex()
|
||||
r := s.FindByDatastorePath(&types.FindByDatastorePath{
|
||||
This: s.Reference(),
|
||||
Path: c.req.Path,
|
||||
Datacenter: Map.getEntityDatacenter(c.Folder).Reference(),
|
||||
})
|
||||
|
||||
if ref := r.(*methods.FindByDatastorePathBody).Res.Returnval; ref != nil {
|
||||
return nil, &types.AlreadyExists{Name: ref.Value}
|
||||
}
|
||||
|
||||
if c.req.Name == "" {
|
||||
p, err := parseDatastorePath(c.req.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.req.Name = path.Dir(p.Path)
|
||||
}
|
||||
|
||||
create := NewTask(&createVM{
|
||||
Folder: c.Folder,
|
||||
register: true,
|
||||
ctx: c.ctx,
|
||||
req: &types.CreateVM_Task{
|
||||
This: c.Folder.Reference(),
|
||||
Config: types.VirtualMachineConfigSpec{
|
||||
Name: c.req.Name,
|
||||
Files: &types.VirtualMachineFileInfo{
|
||||
VmPathName: c.req.Path,
|
||||
},
|
||||
},
|
||||
Pool: *pool,
|
||||
Host: host,
|
||||
},
|
||||
})
|
||||
|
||||
create.Run()
|
||||
|
||||
if create.Info.Error != nil {
|
||||
return nil, create.Info.Error.Fault
|
||||
}
|
||||
|
||||
return create.Info.Result, nil
|
||||
}
|
||||
|
||||
func (f *Folder) RegisterVMTask(ctx *Context, c *types.RegisterVM_Task) soap.HasFault {
|
||||
return &methods.RegisterVM_TaskBody{
|
||||
Res: &types.RegisterVM_TaskResponse{
|
||||
Returnval: NewTask(®isterVM{f, ctx, c}).Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Folder) MoveIntoFolderTask(ctx *Context, c *types.MoveIntoFolder_Task) soap.HasFault {
|
||||
task := CreateTask(f, "moveIntoFolder", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
for _, ref := range c.List {
|
||||
obj := Map.Get(ref).(mo.Entity)
|
||||
|
||||
parent, ok := Map.Get(*(obj.Entity()).Parent).(*Folder)
|
||||
|
||||
if !ok || !folderHasChildType(&f.Folder, ref.Type) {
|
||||
return nil, &types.NotSupported{}
|
||||
}
|
||||
|
||||
folderRemoveChild(ctx, &parent.Folder, ref)
|
||||
folderPutChild(ctx, &f.Folder, obj)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.MoveIntoFolder_TaskBody{
|
||||
Res: &types.MoveIntoFolder_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Folder) CreateDVSTask(ctx *Context, req *types.CreateDVS_Task) soap.HasFault {
|
||||
task := CreateTask(f, "createDVS", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
spec := req.Spec.ConfigSpec.GetDVSConfigSpec()
|
||||
dvs := &DistributedVirtualSwitch{}
|
||||
dvs.Name = spec.Name
|
||||
dvs.Entity().Name = dvs.Name
|
||||
|
||||
if Map.FindByName(dvs.Name, f.ChildEntity) != nil {
|
||||
return nil, &types.InvalidArgument{InvalidProperty: "name"}
|
||||
}
|
||||
|
||||
dvs.Uuid = newUUID(dvs.Name)
|
||||
|
||||
folderPutChild(ctx, &f.Folder, dvs)
|
||||
|
||||
dvs.Summary = types.DVSSummary{
|
||||
Name: dvs.Name,
|
||||
Uuid: dvs.Uuid,
|
||||
NumPorts: spec.NumStandalonePorts,
|
||||
ProductInfo: req.Spec.ProductInfo,
|
||||
Description: spec.Description,
|
||||
}
|
||||
|
||||
configInfo := &types.VMwareDVSConfigInfo{
|
||||
DVSConfigInfo: types.DVSConfigInfo{
|
||||
Uuid: dvs.Uuid,
|
||||
Name: spec.Name,
|
||||
ConfigVersion: spec.ConfigVersion,
|
||||
NumStandalonePorts: spec.NumStandalonePorts,
|
||||
MaxPorts: spec.MaxPorts,
|
||||
UplinkPortPolicy: spec.UplinkPortPolicy,
|
||||
UplinkPortgroup: spec.UplinkPortgroup,
|
||||
DefaultPortConfig: spec.DefaultPortConfig,
|
||||
ExtensionKey: spec.ExtensionKey,
|
||||
Description: spec.Description,
|
||||
Policy: spec.Policy,
|
||||
VendorSpecificConfig: spec.VendorSpecificConfig,
|
||||
SwitchIpAddress: spec.SwitchIpAddress,
|
||||
DefaultProxySwitchMaxNumPorts: spec.DefaultProxySwitchMaxNumPorts,
|
||||
InfrastructureTrafficResourceConfig: spec.InfrastructureTrafficResourceConfig,
|
||||
NetworkResourceControlVersion: spec.NetworkResourceControlVersion,
|
||||
},
|
||||
}
|
||||
|
||||
if spec.Contact != nil {
|
||||
configInfo.Contact = *spec.Contact
|
||||
}
|
||||
|
||||
dvs.Config = configInfo
|
||||
|
||||
if dvs.Summary.ProductInfo == nil {
|
||||
product := Map.content().About
|
||||
dvs.Summary.ProductInfo = &types.DistributedVirtualSwitchProductSpec{
|
||||
Name: "DVS",
|
||||
Vendor: product.Vendor,
|
||||
Version: product.Version,
|
||||
Build: product.Build,
|
||||
ForwardingClass: "etherswitch",
|
||||
}
|
||||
}
|
||||
|
||||
dvs.AddDVPortgroupTask(ctx, &types.AddDVPortgroup_Task{
|
||||
Spec: []types.DVPortgroupConfigSpec{{
|
||||
Name: dvs.Name + "-DVUplinks" + strings.TrimPrefix(dvs.Self.Value, "dvs"),
|
||||
DefaultPortConfig: &types.VMwareDVSPortSetting{
|
||||
Vlan: &types.VmwareDistributedVirtualSwitchTrunkVlanSpec{
|
||||
VlanId: []types.NumericRange{{Start: 0, End: 4094}},
|
||||
},
|
||||
UplinkTeamingPolicy: &types.VmwareUplinkPortTeamingPolicy{
|
||||
Policy: &types.StringPolicy{
|
||||
Value: "loadbalance_srcid",
|
||||
},
|
||||
ReversePolicy: &types.BoolPolicy{
|
||||
Value: types.NewBool(true),
|
||||
},
|
||||
NotifySwitches: &types.BoolPolicy{
|
||||
Value: types.NewBool(true),
|
||||
},
|
||||
RollingOrder: &types.BoolPolicy{
|
||||
Value: types.NewBool(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
})
|
||||
|
||||
return dvs.Reference(), nil
|
||||
})
|
||||
|
||||
return &methods.CreateDVS_TaskBody{
|
||||
Res: &types.CreateDVS_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault {
|
||||
return RenameTask(f, r)
|
||||
}
|
||||
|
||||
func (f *Folder) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
|
||||
type destroyer interface {
|
||||
mo.Reference
|
||||
DestroyTask(*types.Destroy_Task) soap.HasFault
|
||||
}
|
||||
|
||||
task := CreateTask(f, "destroy", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
// Attempt to destroy all children
|
||||
for _, c := range f.ChildEntity {
|
||||
obj, ok := Map.Get(c).(destroyer)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var fault types.BaseMethodFault
|
||||
Map.WithLock(obj, func() {
|
||||
id := obj.DestroyTask(&types.Destroy_Task{
|
||||
This: c,
|
||||
}).(*methods.Destroy_TaskBody).Res.Returnval
|
||||
|
||||
t := Map.Get(id).(*Task)
|
||||
if t.Info.Error != nil {
|
||||
fault = t.Info.Error.Fault // For example, can't destroy a powered on VM
|
||||
}
|
||||
})
|
||||
if fault != nil {
|
||||
return nil, fault
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the folder itself
|
||||
folderRemoveChild(ctx, &Map.Get(*f.Parent).(*Folder).Folder, f.Self)
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.Destroy_TaskBody{
|
||||
Res: &types.Destroy_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import "github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
// GuestID is the list of valid types.VirtualMachineGuestOsIdentifier
|
||||
var GuestID = []types.VirtualMachineGuestOsIdentifier{
|
||||
types.VirtualMachineGuestOsIdentifierDosGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWin31Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWin95Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWin98Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWinMeGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNTGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWin2000ProGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWin2000ServGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWin2000AdvServGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinXPHomeGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinXPProGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinXPPro64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNetWebGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNetStandardGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNetEnterpriseGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNetDatacenterGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNetBusinessGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNetStandard64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNetEnterprise64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWinLonghornGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinLonghorn64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWinNetDatacenter64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWinVistaGuest,
|
||||
types.VirtualMachineGuestOsIdentifierWinVista64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows7Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows7_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows7Server64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows8Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows8_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows8Server64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows9Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows9_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindows9Server64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierWindowsHyperVGuest,
|
||||
types.VirtualMachineGuestOsIdentifierFreebsdGuest,
|
||||
types.VirtualMachineGuestOsIdentifierFreebsd64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRedhatGuest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel2Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel3Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel3_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel4Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel4_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel5Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel5_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel6Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel6_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel7Guest,
|
||||
types.VirtualMachineGuestOsIdentifierRhel7_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierCentosGuest,
|
||||
types.VirtualMachineGuestOsIdentifierCentos64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierCentos6Guest,
|
||||
types.VirtualMachineGuestOsIdentifierCentos6_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierCentos7Guest,
|
||||
types.VirtualMachineGuestOsIdentifierCentos7_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOracleLinuxGuest,
|
||||
types.VirtualMachineGuestOsIdentifierOracleLinux64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOracleLinux6Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOracleLinux6_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOracleLinux7Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOracleLinux7_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSuseGuest,
|
||||
types.VirtualMachineGuestOsIdentifierSuse64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSlesGuest,
|
||||
types.VirtualMachineGuestOsIdentifierSles64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSles10Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSles10_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSles11Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSles11_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSles12Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSles12_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierNld9Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOesGuest,
|
||||
types.VirtualMachineGuestOsIdentifierSjdsGuest,
|
||||
types.VirtualMachineGuestOsIdentifierMandrakeGuest,
|
||||
types.VirtualMachineGuestOsIdentifierMandrivaGuest,
|
||||
types.VirtualMachineGuestOsIdentifierMandriva64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierTurboLinuxGuest,
|
||||
types.VirtualMachineGuestOsIdentifierTurboLinux64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierUbuntuGuest,
|
||||
types.VirtualMachineGuestOsIdentifierUbuntu64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian4Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian4_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian5Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian5_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian6Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian6_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian7Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian7_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian8Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian8_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian9Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian9_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian10Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDebian10_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierAsianux3Guest,
|
||||
types.VirtualMachineGuestOsIdentifierAsianux3_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierAsianux4Guest,
|
||||
types.VirtualMachineGuestOsIdentifierAsianux4_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierAsianux5_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierAsianux7_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOpensuseGuest,
|
||||
types.VirtualMachineGuestOsIdentifierOpensuse64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierFedoraGuest,
|
||||
types.VirtualMachineGuestOsIdentifierFedora64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierCoreos64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierVmwarePhoton64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOther24xLinuxGuest,
|
||||
types.VirtualMachineGuestOsIdentifierOther26xLinuxGuest,
|
||||
types.VirtualMachineGuestOsIdentifierOtherLinuxGuest,
|
||||
types.VirtualMachineGuestOsIdentifierOther3xLinuxGuest,
|
||||
types.VirtualMachineGuestOsIdentifierGenericLinuxGuest,
|
||||
types.VirtualMachineGuestOsIdentifierOther24xLinux64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOther26xLinux64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOther3xLinux64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOtherLinux64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSolaris6Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSolaris7Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSolaris8Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSolaris9Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSolaris10Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSolaris10_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierSolaris11_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOs2Guest,
|
||||
types.VirtualMachineGuestOsIdentifierEComStationGuest,
|
||||
types.VirtualMachineGuestOsIdentifierEComStation2Guest,
|
||||
types.VirtualMachineGuestOsIdentifierNetware4Guest,
|
||||
types.VirtualMachineGuestOsIdentifierNetware5Guest,
|
||||
types.VirtualMachineGuestOsIdentifierNetware6Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOpenServer5Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOpenServer6Guest,
|
||||
types.VirtualMachineGuestOsIdentifierUnixWare7Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwinGuest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin10Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin10_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin11Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin11_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin12_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin13_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin14_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin15_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierDarwin16_64Guest,
|
||||
types.VirtualMachineGuestOsIdentifierVmkernelGuest,
|
||||
types.VirtualMachineGuestOsIdentifierVmkernel5Guest,
|
||||
types.VirtualMachineGuestOsIdentifierVmkernel6Guest,
|
||||
types.VirtualMachineGuestOsIdentifierVmkernel65Guest,
|
||||
types.VirtualMachineGuestOsIdentifierOtherGuest,
|
||||
types.VirtualMachineGuestOsIdentifierOtherGuest64,
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
pushd "$(dirname "$0")" >/dev/null
|
||||
|
||||
{
|
||||
cat <<EOF
|
||||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
// GuestID is the list of valid types.VirtualMachineGuestOsIdentifier
|
||||
var GuestID = []types.VirtualMachineGuestOsIdentifier{
|
||||
EOF
|
||||
|
||||
ids=($(grep 'VirtualMachineGuestOsIdentifier(' ../vim25/types/enum.go | grep = | awk '{print $1}'))
|
||||
printf "types.%s,\n" "${ids[@]}"
|
||||
|
||||
echo "}"
|
||||
} > guest_id.go
|
||||
|
||||
goimports -w guest_id.go
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2020 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type GuestOperationsManager struct {
|
||||
mo.GuestOperationsManager
|
||||
}
|
||||
|
||||
func (m *GuestOperationsManager) init(r *Registry) {
|
||||
fm := new(GuestFileManager)
|
||||
if m.FileManager == nil {
|
||||
m.FileManager = &types.ManagedObjectReference{
|
||||
Type: "GuestFileManager",
|
||||
Value: "guestOperationsFileManager",
|
||||
}
|
||||
}
|
||||
fm.Self = *m.FileManager
|
||||
r.Put(fm)
|
||||
|
||||
pm := new(GuestProcessManager)
|
||||
if m.ProcessManager == nil {
|
||||
m.ProcessManager = &types.ManagedObjectReference{
|
||||
Type: "GuestProcessManager",
|
||||
Value: "guestOperationsProcessManager",
|
||||
}
|
||||
}
|
||||
pm.Self = *m.ProcessManager
|
||||
r.Put(pm)
|
||||
}
|
||||
|
||||
type GuestFileManager struct {
|
||||
mo.GuestFileManager
|
||||
}
|
||||
|
||||
func guestURL(ctx *Context, vm *VirtualMachine, path string) string {
|
||||
return (&url.URL{
|
||||
Scheme: ctx.svc.Listen.Scheme,
|
||||
Host: "*", // See guest.FileManager.TransferURL
|
||||
Path: guestPrefix + strings.TrimPrefix(path, "/"),
|
||||
RawQuery: url.Values{
|
||||
"id": []string{vm.run.id},
|
||||
"token": []string{ctx.Session.Key},
|
||||
}.Encode(),
|
||||
}).String()
|
||||
}
|
||||
|
||||
func (m *GuestFileManager) InitiateFileTransferToGuest(ctx *Context, req *types.InitiateFileTransferToGuest) soap.HasFault {
|
||||
body := new(methods.InitiateFileTransferToGuestBody)
|
||||
|
||||
vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
|
||||
err := vm.run.prepareGuestOperation(vm, req.Auth)
|
||||
if err != nil {
|
||||
body.Fault_ = Fault("", err)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.InitiateFileTransferToGuestResponse{
|
||||
Returnval: guestURL(ctx, vm, req.GuestFilePath),
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *GuestFileManager) InitiateFileTransferFromGuest(ctx *Context, req *types.InitiateFileTransferFromGuest) soap.HasFault {
|
||||
body := new(methods.InitiateFileTransferFromGuestBody)
|
||||
|
||||
vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
|
||||
err := vm.run.prepareGuestOperation(vm, req.Auth)
|
||||
if err != nil {
|
||||
body.Fault_ = Fault("", err)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.InitiateFileTransferFromGuestResponse{
|
||||
Returnval: types.FileTransferInformation{
|
||||
Attributes: nil, // TODO
|
||||
Size: 0, // TODO
|
||||
Url: guestURL(ctx, vm, req.GuestFilePath),
|
||||
},
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
type GuestProcessManager struct {
|
||||
mo.GuestProcessManager
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type HostDatastoreBrowser struct {
|
||||
mo.HostDatastoreBrowser
|
||||
}
|
||||
|
||||
type searchDatastore struct {
|
||||
*HostDatastoreBrowser
|
||||
|
||||
DatastorePath string
|
||||
SearchSpec *types.HostDatastoreBrowserSearchSpec
|
||||
|
||||
res []types.HostDatastoreBrowserSearchResults
|
||||
|
||||
recurse bool
|
||||
}
|
||||
|
||||
func (s *searchDatastore) addFile(file os.FileInfo, res *types.HostDatastoreBrowserSearchResults) {
|
||||
details := s.SearchSpec.Details
|
||||
if details == nil {
|
||||
details = new(types.FileQueryFlags)
|
||||
}
|
||||
|
||||
name := file.Name()
|
||||
|
||||
info := types.FileInfo{
|
||||
Path: name,
|
||||
}
|
||||
|
||||
var finfo types.BaseFileInfo = &info
|
||||
|
||||
if details.FileSize {
|
||||
info.FileSize = file.Size()
|
||||
}
|
||||
|
||||
if details.Modification {
|
||||
mtime := file.ModTime()
|
||||
info.Modification = &mtime
|
||||
}
|
||||
|
||||
if isTrue(details.FileOwner) {
|
||||
// Assume for now this process created all files in the datastore
|
||||
user := os.Getenv("USER")
|
||||
|
||||
info.Owner = user
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
finfo = &types.FolderFileInfo{FileInfo: info}
|
||||
} else if details.FileType {
|
||||
switch path.Ext(name) {
|
||||
case ".img":
|
||||
finfo = &types.FloppyImageFileInfo{FileInfo: info}
|
||||
case ".iso":
|
||||
finfo = &types.IsoImageFileInfo{FileInfo: info}
|
||||
case ".log":
|
||||
finfo = &types.VmLogFileInfo{FileInfo: info}
|
||||
case ".nvram":
|
||||
finfo = &types.VmNvramFileInfo{FileInfo: info}
|
||||
case ".vmdk":
|
||||
// TODO: lookup device to set other fields
|
||||
finfo = &types.VmDiskFileInfo{FileInfo: info}
|
||||
case ".vmx":
|
||||
finfo = &types.VmConfigFileInfo{FileInfo: info}
|
||||
}
|
||||
}
|
||||
|
||||
res.File = append(res.File, finfo)
|
||||
}
|
||||
|
||||
func (s *searchDatastore) queryMatch(file os.FileInfo) bool {
|
||||
if len(s.SearchSpec.Query) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
name := file.Name()
|
||||
ext := path.Ext(name)
|
||||
|
||||
for _, q := range s.SearchSpec.Query {
|
||||
switch q.(type) {
|
||||
case *types.FileQuery:
|
||||
return true
|
||||
case *types.FolderFileQuery:
|
||||
if file.IsDir() {
|
||||
return true
|
||||
}
|
||||
case *types.FloppyImageFileQuery:
|
||||
if ext == ".img" {
|
||||
return true
|
||||
}
|
||||
case *types.IsoImageFileQuery:
|
||||
if ext == ".iso" {
|
||||
return true
|
||||
}
|
||||
case *types.VmConfigFileQuery:
|
||||
if ext == ".vmx" {
|
||||
// TODO: check Filter and Details fields
|
||||
return true
|
||||
}
|
||||
case *types.VmDiskFileQuery:
|
||||
if ext == ".vmdk" {
|
||||
// TODO: check Filter and Details fields
|
||||
return !strings.HasSuffix(name, "-flat.vmdk")
|
||||
}
|
||||
case *types.VmLogFileQuery:
|
||||
if ext == ".log" {
|
||||
return strings.HasPrefix(name, "vmware")
|
||||
}
|
||||
case *types.VmNvramFileQuery:
|
||||
if ext == ".nvram" {
|
||||
return true
|
||||
}
|
||||
case *types.VmSnapshotFileQuery:
|
||||
if ext == ".vmsn" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *searchDatastore) search(ds *types.ManagedObjectReference, folder string, dir string) error {
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Printf("search %s: %s", dir, err)
|
||||
return err
|
||||
}
|
||||
|
||||
res := types.HostDatastoreBrowserSearchResults{
|
||||
Datastore: ds,
|
||||
FolderPath: folder,
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
|
||||
if s.queryMatch(file) {
|
||||
for _, m := range s.SearchSpec.MatchPattern {
|
||||
if ok, _ := path.Match(m, name); ok {
|
||||
s.addFile(file, &res)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.recurse && file.IsDir() {
|
||||
_ = s.search(ds, path.Join(folder, name), path.Join(dir, name))
|
||||
}
|
||||
}
|
||||
|
||||
s.res = append(s.res, res)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *searchDatastore) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
p, fault := parseDatastorePath(s.DatastorePath)
|
||||
if fault != nil {
|
||||
return nil, fault
|
||||
}
|
||||
|
||||
ref := Map.FindByName(p.Datastore, s.Datastore)
|
||||
if ref == nil {
|
||||
return nil, &types.InvalidDatastore{Name: p.Datastore}
|
||||
}
|
||||
|
||||
ds := ref.(*Datastore)
|
||||
task.Info.Entity = &ds.Self // TODO: CreateTask() should require mo.Entity, rather than mo.Reference
|
||||
task.Info.EntityName = ds.Name
|
||||
|
||||
dir := path.Join(ds.Info.GetDatastoreInfo().Url, p.Path)
|
||||
|
||||
err := s.search(&ds.Self, s.DatastorePath, dir)
|
||||
if err != nil {
|
||||
ff := types.FileFault{
|
||||
File: p.Path,
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
return nil, &types.FileNotFound{FileFault: ff}
|
||||
}
|
||||
|
||||
return nil, &types.InvalidArgument{InvalidProperty: p.Path}
|
||||
}
|
||||
|
||||
if s.recurse {
|
||||
return types.ArrayOfHostDatastoreBrowserSearchResults{
|
||||
HostDatastoreBrowserSearchResults: s.res,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return s.res[0], nil
|
||||
}
|
||||
|
||||
func (b *HostDatastoreBrowser) SearchDatastoreTask(s *types.SearchDatastore_Task) soap.HasFault {
|
||||
task := NewTask(&searchDatastore{
|
||||
HostDatastoreBrowser: b,
|
||||
DatastorePath: s.DatastorePath,
|
||||
SearchSpec: s.SearchSpec,
|
||||
})
|
||||
|
||||
return &methods.SearchDatastore_TaskBody{
|
||||
Res: &types.SearchDatastore_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *HostDatastoreBrowser) SearchDatastoreSubFoldersTask(s *types.SearchDatastoreSubFolders_Task) soap.HasFault {
|
||||
task := NewTask(&searchDatastore{
|
||||
HostDatastoreBrowser: b,
|
||||
DatastorePath: s.DatastorePath,
|
||||
SearchSpec: s.SearchSpec,
|
||||
recurse: true,
|
||||
})
|
||||
|
||||
return &methods.SearchDatastoreSubFolders_TaskBody{
|
||||
Res: &types.SearchDatastoreSubFolders_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type HostDatastoreSystem struct {
|
||||
mo.HostDatastoreSystem
|
||||
|
||||
Host *mo.HostSystem
|
||||
}
|
||||
|
||||
func (dss *HostDatastoreSystem) add(ctx *Context, ds *Datastore) *soap.Fault {
|
||||
info := ds.Info.GetDatastoreInfo()
|
||||
|
||||
info.Name = ds.Name
|
||||
|
||||
if e := Map.FindByName(ds.Name, dss.Datastore); e != nil {
|
||||
return Fault(e.Reference().Value, &types.DuplicateName{
|
||||
Name: ds.Name,
|
||||
Object: e.Reference(),
|
||||
})
|
||||
}
|
||||
|
||||
fi, err := os.Stat(info.Url)
|
||||
if err == nil && !fi.IsDir() {
|
||||
err = os.ErrInvalid
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
return Fault(err.Error(), &types.NotFound{})
|
||||
default:
|
||||
return Fault(err.Error(), &types.HostConfigFault{})
|
||||
}
|
||||
}
|
||||
|
||||
folder := Map.getEntityFolder(dss.Host, "datastore")
|
||||
ds.Self.Type = typeName(ds)
|
||||
// Datastore is the only type where create methods do not include the parent (Folder in this case),
|
||||
// but we need the moref to be unique per DC/datastoreFolder, but not per-HostSystem.
|
||||
ds.Self.Value += "@" + folder.Self.Value
|
||||
// TODO: name should be made unique in the case of Local ds type
|
||||
|
||||
ds.Summary.Datastore = &ds.Self
|
||||
ds.Summary.Name = ds.Name
|
||||
ds.Summary.Url = info.Url
|
||||
ds.Capability = types.DatastoreCapability{
|
||||
DirectoryHierarchySupported: true,
|
||||
RawDiskMappingsSupported: false,
|
||||
PerFileThinProvisioningSupported: true,
|
||||
StorageIORMSupported: types.NewBool(true),
|
||||
NativeSnapshotSupported: types.NewBool(false),
|
||||
TopLevelDirectoryCreateSupported: types.NewBool(true),
|
||||
SeSparseSupported: types.NewBool(true),
|
||||
}
|
||||
|
||||
dss.Datastore = append(dss.Datastore, ds.Self)
|
||||
dss.Host.Datastore = dss.Datastore
|
||||
parent := hostParent(dss.Host)
|
||||
Map.AddReference(parent, &parent.Datastore, ds.Self)
|
||||
|
||||
browser := &HostDatastoreBrowser{}
|
||||
browser.Datastore = dss.Datastore
|
||||
ds.Browser = Map.Put(browser).Reference()
|
||||
|
||||
folderPutChild(ctx, folder, ds)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dss *HostDatastoreSystem) CreateLocalDatastore(ctx *Context, c *types.CreateLocalDatastore) soap.HasFault {
|
||||
r := &methods.CreateLocalDatastoreBody{}
|
||||
|
||||
ds := &Datastore{}
|
||||
ds.Name = c.Name
|
||||
ds.Self.Value = c.Path
|
||||
|
||||
ds.Info = &types.LocalDatastoreInfo{
|
||||
DatastoreInfo: types.DatastoreInfo{
|
||||
Name: c.Name,
|
||||
Url: c.Path,
|
||||
},
|
||||
Path: c.Path,
|
||||
}
|
||||
|
||||
ds.Summary.Type = string(types.HostFileSystemVolumeFileSystemTypeOTHER)
|
||||
ds.Summary.MaintenanceMode = string(types.DatastoreSummaryMaintenanceModeStateNormal)
|
||||
ds.Summary.Accessible = true
|
||||
|
||||
if err := dss.add(ctx, ds); err != nil {
|
||||
r.Fault_ = err
|
||||
return r
|
||||
}
|
||||
|
||||
ds.Host = append(ds.Host, types.DatastoreHostMount{
|
||||
Key: dss.Host.Reference(),
|
||||
MountInfo: types.HostMountInfo{
|
||||
AccessMode: string(types.HostMountModeReadWrite),
|
||||
Mounted: types.NewBool(true),
|
||||
Accessible: types.NewBool(true),
|
||||
},
|
||||
})
|
||||
|
||||
_ = ds.RefreshDatastore(&types.RefreshDatastore{This: ds.Self})
|
||||
|
||||
r.Res = &types.CreateLocalDatastoreResponse{
|
||||
Returnval: ds.Self,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (dss *HostDatastoreSystem) CreateNasDatastore(ctx *Context, c *types.CreateNasDatastore) soap.HasFault {
|
||||
r := &methods.CreateNasDatastoreBody{}
|
||||
|
||||
ds := &Datastore{}
|
||||
ds.Name = path.Base(c.Spec.LocalPath)
|
||||
ds.Self.Value = c.Spec.RemoteHost + ":" + c.Spec.RemotePath
|
||||
|
||||
ds.Info = &types.NasDatastoreInfo{
|
||||
DatastoreInfo: types.DatastoreInfo{
|
||||
Url: c.Spec.LocalPath,
|
||||
},
|
||||
Nas: &types.HostNasVolume{
|
||||
HostFileSystemVolume: types.HostFileSystemVolume{
|
||||
Name: c.Spec.LocalPath,
|
||||
Type: c.Spec.Type,
|
||||
},
|
||||
RemoteHost: c.Spec.RemoteHost,
|
||||
RemotePath: c.Spec.RemotePath,
|
||||
},
|
||||
}
|
||||
|
||||
ds.Summary.Type = c.Spec.Type
|
||||
ds.Summary.MaintenanceMode = string(types.DatastoreSummaryMaintenanceModeStateNormal)
|
||||
ds.Summary.Accessible = true
|
||||
|
||||
if err := dss.add(ctx, ds); err != nil {
|
||||
r.Fault_ = err
|
||||
return r
|
||||
}
|
||||
|
||||
_ = ds.RefreshDatastore(&types.RefreshDatastore{This: ds.Self})
|
||||
|
||||
r.Res = &types.CreateNasDatastoreResponse{
|
||||
Returnval: ds.Self,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type HostFirewallSystem struct {
|
||||
mo.HostFirewallSystem
|
||||
}
|
||||
|
||||
func NewHostFirewallSystem(_ *mo.HostSystem) *HostFirewallSystem {
|
||||
info := esx.HostFirewallInfo
|
||||
|
||||
return &HostFirewallSystem{
|
||||
HostFirewallSystem: mo.HostFirewallSystem{
|
||||
FirewallInfo: &info,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func DisableRuleset(info *types.HostFirewallInfo, id string) bool {
|
||||
for i := range info.Ruleset {
|
||||
if info.Ruleset[i].Key == id {
|
||||
info.Ruleset[i].Enabled = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *HostFirewallSystem) DisableRuleset(req *types.DisableRuleset) soap.HasFault {
|
||||
body := &methods.DisableRulesetBody{}
|
||||
|
||||
if DisableRuleset(s.HostFirewallSystem.FirewallInfo, req.Id) {
|
||||
body.Res = new(types.DisableRulesetResponse)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Fault_ = Fault("", &types.NotFound{})
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func EnableRuleset(info *types.HostFirewallInfo, id string) bool {
|
||||
for i := range info.Ruleset {
|
||||
if info.Ruleset[i].Key == id {
|
||||
info.Ruleset[i].Enabled = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *HostFirewallSystem) EnableRuleset(req *types.EnableRuleset) soap.HasFault {
|
||||
body := &methods.EnableRulesetBody{}
|
||||
|
||||
if EnableRuleset(s.HostFirewallSystem.FirewallInfo, req.Id) {
|
||||
body.Res = new(types.EnableRulesetResponse)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Fault_ = Fault("", &types.NotFound{})
|
||||
|
||||
return body
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// As of vSphere API 5.1, local groups operations are deprecated, so it's not supported here.
|
||||
|
||||
type HostLocalAccountManager struct {
|
||||
mo.HostLocalAccountManager
|
||||
}
|
||||
|
||||
func (h *HostLocalAccountManager) CreateUser(req *types.CreateUser) soap.HasFault {
|
||||
spec := req.User.GetHostAccountSpec()
|
||||
userDirectory := Map.UserDirectory()
|
||||
|
||||
found := userDirectory.search(true, false, compareFunc(spec.Id, true))
|
||||
if len(found) > 0 {
|
||||
return &methods.CreateUserBody{
|
||||
Fault_: Fault("", &types.AlreadyExists{}),
|
||||
}
|
||||
}
|
||||
|
||||
userDirectory.addUser(spec.Id)
|
||||
|
||||
return &methods.CreateUserBody{
|
||||
Res: &types.CreateUserResponse{},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostLocalAccountManager) RemoveUser(req *types.RemoveUser) soap.HasFault {
|
||||
userDirectory := Map.UserDirectory()
|
||||
|
||||
found := userDirectory.search(true, false, compareFunc(req.UserName, true))
|
||||
|
||||
if len(found) == 0 {
|
||||
return &methods.RemoveUserBody{
|
||||
Fault_: Fault("", &types.UserNotFound{}),
|
||||
}
|
||||
}
|
||||
|
||||
userDirectory.removeUser(req.UserName)
|
||||
|
||||
return &methods.RemoveUserBody{
|
||||
Res: &types.RemoveUserResponse{},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostLocalAccountManager) UpdateUser(req *types.UpdateUser) soap.HasFault {
|
||||
return &methods.CreateUserBody{
|
||||
Res: &types.CreateUserResponse{},
|
||||
}
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type HostNetworkSystem struct {
|
||||
mo.HostNetworkSystem
|
||||
|
||||
Host *mo.HostSystem
|
||||
}
|
||||
|
||||
func NewHostNetworkSystem(host *mo.HostSystem) *HostNetworkSystem {
|
||||
return &HostNetworkSystem{
|
||||
Host: host,
|
||||
HostNetworkSystem: mo.HostNetworkSystem{
|
||||
NetworkInfo: &types.HostNetworkInfo{
|
||||
Vswitch: []types.HostVirtualSwitch{
|
||||
{
|
||||
Name: "vSwitch0",
|
||||
Portgroup: []string{"VM Network"},
|
||||
},
|
||||
},
|
||||
Portgroup: host.Config.Network.Portgroup,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HostNetworkSystem) init(r *Registry) {
|
||||
for _, obj := range r.objects {
|
||||
if h, ok := obj.(*HostSystem); ok {
|
||||
if h.ConfigManager.NetworkSystem.Value == s.Self.Value {
|
||||
s.Host = &h.HostSystem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HostNetworkSystem) folder() *Folder {
|
||||
f := Map.getEntityDatacenter(s.Host).NetworkFolder
|
||||
return Map.Get(f).(*Folder)
|
||||
}
|
||||
|
||||
func (s *HostNetworkSystem) AddVirtualSwitch(c *types.AddVirtualSwitch) soap.HasFault {
|
||||
r := &methods.AddVirtualSwitchBody{}
|
||||
|
||||
for _, vswitch := range s.NetworkInfo.Vswitch {
|
||||
if vswitch.Name == c.VswitchName {
|
||||
r.Fault_ = Fault("", &types.AlreadyExists{Name: c.VswitchName})
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
s.NetworkInfo.Vswitch = append(s.NetworkInfo.Vswitch, types.HostVirtualSwitch{
|
||||
Name: c.VswitchName,
|
||||
})
|
||||
|
||||
r.Res = &types.AddVirtualSwitchResponse{}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *HostNetworkSystem) RemoveVirtualSwitch(c *types.RemoveVirtualSwitch) soap.HasFault {
|
||||
r := &methods.RemoveVirtualSwitchBody{}
|
||||
|
||||
vs := s.NetworkInfo.Vswitch
|
||||
|
||||
for i, v := range vs {
|
||||
if v.Name == c.VswitchName {
|
||||
s.NetworkInfo.Vswitch = append(vs[:i], vs[i+1:]...)
|
||||
r.Res = &types.RemoveVirtualSwitchResponse{}
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
r.Fault_ = Fault("", &types.NotFound{})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *HostNetworkSystem) AddPortGroup(ctx *Context, c *types.AddPortGroup) soap.HasFault {
|
||||
var vswitch *types.HostVirtualSwitch
|
||||
|
||||
r := &methods.AddPortGroupBody{}
|
||||
|
||||
if c.Portgrp.Name == "" {
|
||||
r.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "name"})
|
||||
return r
|
||||
}
|
||||
|
||||
for i := range s.NetworkInfo.Vswitch {
|
||||
if s.NetworkInfo.Vswitch[i].Name == c.Portgrp.VswitchName {
|
||||
vswitch = &s.NetworkInfo.Vswitch[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if vswitch == nil {
|
||||
r.Fault_ = Fault("", &types.NotFound{})
|
||||
return r
|
||||
}
|
||||
|
||||
network := &mo.Network{}
|
||||
network.Name = c.Portgrp.Name
|
||||
network.Entity().Name = network.Name
|
||||
|
||||
folder := s.folder()
|
||||
|
||||
if obj := Map.FindByName(c.Portgrp.Name, folder.ChildEntity); obj != nil {
|
||||
r.Fault_ = Fault("", &types.DuplicateName{
|
||||
Name: c.Portgrp.Name,
|
||||
Object: obj.Reference(),
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
folderPutChild(ctx, &folder.Folder, network)
|
||||
|
||||
vswitch.Portgroup = append(vswitch.Portgroup, c.Portgrp.Name)
|
||||
|
||||
s.NetworkInfo.Portgroup = append(s.NetworkInfo.Portgroup, types.HostPortGroup{
|
||||
Key: "key-vim.host.PortGroup-" + c.Portgrp.Name,
|
||||
Port: nil,
|
||||
Spec: c.Portgrp,
|
||||
})
|
||||
|
||||
r.Res = &types.AddPortGroupResponse{}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *HostNetworkSystem) RemovePortGroup(ctx *Context, c *types.RemovePortGroup) soap.HasFault {
|
||||
var vswitch *types.HostVirtualSwitch
|
||||
|
||||
r := &methods.RemovePortGroupBody{}
|
||||
|
||||
for i, v := range s.NetworkInfo.Vswitch {
|
||||
for j, pg := range v.Portgroup {
|
||||
if pg == c.PgName {
|
||||
vswitch = &s.NetworkInfo.Vswitch[i]
|
||||
vswitch.Portgroup = append(vswitch.Portgroup[:j], vswitch.Portgroup[j+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if vswitch == nil {
|
||||
r.Fault_ = Fault("", &types.NotFound{})
|
||||
return r
|
||||
}
|
||||
|
||||
folder := s.folder()
|
||||
e := Map.FindByName(c.PgName, folder.ChildEntity)
|
||||
folderRemoveChild(ctx, &folder.Folder, e.Reference())
|
||||
|
||||
for i, pg := range s.NetworkInfo.Portgroup {
|
||||
if pg.Spec.Name == c.PgName {
|
||||
var portgroup = s.NetworkInfo.Portgroup
|
||||
s.NetworkInfo.Portgroup = append(portgroup[:i], portgroup[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
r.Res = &types.RemovePortGroupResponse{}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *HostNetworkSystem) UpdateNetworkConfig(req *types.UpdateNetworkConfig) soap.HasFault {
|
||||
s.NetworkConfig = &req.Config
|
||||
|
||||
return &methods.UpdateNetworkConfigBody{
|
||||
Res: &types.UpdateNetworkConfigResponse{
|
||||
Returnval: types.HostNetworkConfigResult{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HostNetworkSystem) QueryNetworkHint(req *types.QueryNetworkHint) soap.HasFault {
|
||||
var info []types.PhysicalNicHintInfo
|
||||
|
||||
for _, nic := range s.Host.Config.Network.Pnic {
|
||||
info = append(info, types.PhysicalNicHintInfo{
|
||||
Device: nic.Device,
|
||||
})
|
||||
}
|
||||
|
||||
return &methods.QueryNetworkHintBody{
|
||||
Res: &types.QueryNetworkHintResponse{
|
||||
Returnval: info,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2020 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type HostStorageSystem struct {
|
||||
mo.HostStorageSystem
|
||||
|
||||
Host *mo.HostSystem
|
||||
HBA []types.BaseHostHostBusAdapter
|
||||
}
|
||||
|
||||
func NewHostStorageSystem(h *mo.HostSystem) *HostStorageSystem {
|
||||
s := &HostStorageSystem{Host: h}
|
||||
|
||||
s.StorageDeviceInfo = &esx.HostStorageDeviceInfo
|
||||
|
||||
s.HBA = fibreChannelHBA
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// RescanAllHba swaps HostStorageSystem.HBA and StorageDeviceInfo.HostBusAdapter.
|
||||
// This allows testing HBA with and without Fibre Channel data.
|
||||
func (s *HostStorageSystem) RescanAllHba(ctx *Context, _ *types.RescanAllHba) soap.HasFault {
|
||||
hba := s.StorageDeviceInfo.HostBusAdapter
|
||||
s.StorageDeviceInfo.HostBusAdapter = s.HBA
|
||||
s.HBA = hba
|
||||
|
||||
ctx.WithLock(s.Host, func() {
|
||||
s.Host.Config.StorageDevice.HostBusAdapter = s.StorageDeviceInfo.HostBusAdapter
|
||||
})
|
||||
|
||||
return &methods.RescanAllHbaBody{
|
||||
Res: new(types.RescanAllHbaResponse),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HostStorageSystem) RescanVmfs(*Context, *types.RescanVmfs) soap.HasFault {
|
||||
return &methods.RescanVmfsBody{Res: new(types.RescanVmfsResponse)}
|
||||
}
|
||||
|
||||
func (s *HostStorageSystem) RefreshStorageSystem(*Context, *types.RefreshStorageSystem) soap.HasFault {
|
||||
return &methods.RefreshStorageSystemBody{Res: new(types.RefreshStorageSystemResponse)}
|
||||
}
|
||||
|
||||
// HBA with FibreChannel data, see RescanAllHba()
|
||||
var fibreChannelHBA = []types.BaseHostHostBusAdapter{
|
||||
&types.HostBlockHba{
|
||||
HostHostBusAdapter: types.HostHostBusAdapter{
|
||||
Key: "key-vim.host.BlockHba-vmhba0",
|
||||
Device: "vmhba0",
|
||||
Bus: 0,
|
||||
Status: "unknown",
|
||||
Model: "Lewisburg SATA AHCI Controller",
|
||||
Driver: "vmw_ahci",
|
||||
Pci: "0000:00:11.5",
|
||||
},
|
||||
},
|
||||
&types.HostBlockHba{
|
||||
HostHostBusAdapter: types.HostHostBusAdapter{
|
||||
Key: "key-vim.host.BlockHba-vmhba1",
|
||||
Device: "vmhba1",
|
||||
Bus: 0,
|
||||
Status: "unknown",
|
||||
Model: "Lewisburg SATA AHCI Controller",
|
||||
Driver: "vmw_ahci",
|
||||
Pci: "0000:00:17.0",
|
||||
},
|
||||
},
|
||||
&types.HostFibreChannelHba{
|
||||
HostHostBusAdapter: types.HostHostBusAdapter{
|
||||
Key: "key-vim.host.FibreChannelHba-vmhba2",
|
||||
Device: "vmhba2",
|
||||
Bus: 59,
|
||||
Status: "online",
|
||||
Model: "Emulex LightPulse LPe32000 PCIe Fibre Channel Adapter",
|
||||
Driver: "lpfc",
|
||||
Pci: "0000:3b:00.0",
|
||||
},
|
||||
PortWorldWideName: 1152922127287604726,
|
||||
NodeWorldWideName: 2305843631894451702,
|
||||
PortType: "unknown",
|
||||
Speed: 16,
|
||||
},
|
||||
&types.HostFibreChannelHba{
|
||||
HostHostBusAdapter: types.HostHostBusAdapter{
|
||||
Key: "key-vim.host.FibreChannelHba-vmhba3",
|
||||
Device: "vmhba3",
|
||||
Bus: 95,
|
||||
Status: "online",
|
||||
Model: "Emulex LightPulse LPe32000 PCIe Fibre Channel Adapter",
|
||||
Driver: "lpfc",
|
||||
Pci: "0000:5f:00.0",
|
||||
},
|
||||
PortWorldWideName: 1152922127287604554,
|
||||
NodeWorldWideName: 2305843631894451530,
|
||||
PortType: "unknown",
|
||||
Speed: 16,
|
||||
},
|
||||
&types.HostSerialAttachedHba{
|
||||
HostHostBusAdapter: types.HostHostBusAdapter{
|
||||
Key: "key-vim.host.SerialAttachedHba-vmhba4",
|
||||
Device: "vmhba4",
|
||||
Bus: 24,
|
||||
Status: "unknown",
|
||||
Model: "PERC H330 Adapter",
|
||||
Driver: "lsi_mr3",
|
||||
Pci: "0000:18:00.0",
|
||||
},
|
||||
NodeWorldWideName: "5d0946606e78ac00",
|
||||
},
|
||||
}
|
|
@ -1,269 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var (
|
||||
hostPortUnique = os.Getenv("VCSIM_HOST_PORT_UNIQUE") == "true"
|
||||
)
|
||||
|
||||
type HostSystem struct {
|
||||
mo.HostSystem
|
||||
}
|
||||
|
||||
func asHostSystemMO(obj mo.Reference) (*mo.HostSystem, bool) {
|
||||
h, ok := getManagedObject(obj).Addr().Interface().(*mo.HostSystem)
|
||||
return h, ok
|
||||
}
|
||||
|
||||
func NewHostSystem(host mo.HostSystem) *HostSystem {
|
||||
if hostPortUnique { // configure unique port for each host
|
||||
port := &esx.HostSystem.Summary.Config.Port
|
||||
*port++
|
||||
host.Summary.Config.Port = *port
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
hs := &HostSystem{
|
||||
HostSystem: host,
|
||||
}
|
||||
|
||||
hs.Name = hs.Summary.Config.Name
|
||||
hs.Summary.Runtime = &hs.Runtime
|
||||
hs.Summary.Runtime.BootTime = &now
|
||||
|
||||
hardware := *host.Summary.Hardware
|
||||
hs.Summary.Hardware = &hardware
|
||||
|
||||
info := *esx.HostHardwareInfo
|
||||
hs.Hardware = &info
|
||||
|
||||
cfg := new(types.HostConfigInfo)
|
||||
deepCopy(hs.Config, cfg)
|
||||
hs.Config = cfg
|
||||
|
||||
config := []struct {
|
||||
ref **types.ManagedObjectReference
|
||||
obj mo.Reference
|
||||
}{
|
||||
{&hs.ConfigManager.DatastoreSystem, &HostDatastoreSystem{Host: &hs.HostSystem}},
|
||||
{&hs.ConfigManager.NetworkSystem, NewHostNetworkSystem(&hs.HostSystem)},
|
||||
{&hs.ConfigManager.AdvancedOption, NewOptionManager(nil, esx.Setting)},
|
||||
{&hs.ConfigManager.FirewallSystem, NewHostFirewallSystem(&hs.HostSystem)},
|
||||
{&hs.ConfigManager.StorageSystem, NewHostStorageSystem(&hs.HostSystem)},
|
||||
}
|
||||
|
||||
for _, c := range config {
|
||||
ref := Map.Put(c.obj).Reference()
|
||||
|
||||
*c.ref = &ref
|
||||
}
|
||||
|
||||
return hs
|
||||
}
|
||||
|
||||
func (h *HostSystem) configure(spec types.HostConnectSpec, connected bool) {
|
||||
h.Runtime.ConnectionState = types.HostSystemConnectionStateDisconnected
|
||||
if connected {
|
||||
h.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
|
||||
}
|
||||
if net.ParseIP(spec.HostName) != nil {
|
||||
h.Config.Network.Vnic[0].Spec.Ip.IpAddress = spec.HostName
|
||||
}
|
||||
|
||||
h.Summary.Config.Name = spec.HostName
|
||||
h.Name = h.Summary.Config.Name
|
||||
id := newUUID(h.Name)
|
||||
h.Summary.Hardware.Uuid = id
|
||||
h.Hardware.SystemInfo.Uuid = id
|
||||
}
|
||||
|
||||
func (h *HostSystem) event() types.HostEvent {
|
||||
return types.HostEvent{
|
||||
Event: types.Event{
|
||||
Datacenter: datacenterEventArgument(h),
|
||||
ComputeResource: h.eventArgumentParent(),
|
||||
Host: h.eventArgument(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostSystem) eventArgument() *types.HostEventArgument {
|
||||
return &types.HostEventArgument{
|
||||
Host: h.Self,
|
||||
EntityEventArgument: types.EntityEventArgument{Name: h.Name},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostSystem) eventArgumentParent() *types.ComputeResourceEventArgument {
|
||||
parent := hostParent(&h.HostSystem)
|
||||
|
||||
return &types.ComputeResourceEventArgument{
|
||||
ComputeResource: parent.Self,
|
||||
EntityEventArgument: types.EntityEventArgument{Name: parent.Name},
|
||||
}
|
||||
}
|
||||
|
||||
func hostParent(host *mo.HostSystem) *mo.ComputeResource {
|
||||
switch parent := Map.Get(*host.Parent).(type) {
|
||||
case *mo.ComputeResource:
|
||||
return parent
|
||||
case *ClusterComputeResource:
|
||||
return &parent.ComputeResource
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func addComputeResource(s *types.ComputeResourceSummary, h *HostSystem) {
|
||||
s.TotalCpu += h.Summary.Hardware.CpuMhz
|
||||
s.TotalMemory += h.Summary.Hardware.MemorySize
|
||||
s.NumCpuCores += h.Summary.Hardware.NumCpuCores
|
||||
s.NumCpuThreads += h.Summary.Hardware.NumCpuThreads
|
||||
s.EffectiveCpu += h.Summary.Hardware.CpuMhz
|
||||
s.EffectiveMemory += h.Summary.Hardware.MemorySize
|
||||
s.NumHosts++
|
||||
s.NumEffectiveHosts++
|
||||
s.OverallStatus = types.ManagedEntityStatusGreen
|
||||
}
|
||||
|
||||
// CreateDefaultESX creates a standalone ESX
|
||||
// Adds objects of type: Datacenter, Network, ComputeResource, ResourcePool and HostSystem
|
||||
func CreateDefaultESX(ctx *Context, f *Folder) {
|
||||
dc := NewDatacenter(ctx, &f.Folder)
|
||||
|
||||
host := NewHostSystem(esx.HostSystem)
|
||||
|
||||
summary := new(types.ComputeResourceSummary)
|
||||
addComputeResource(summary, host)
|
||||
|
||||
cr := &mo.ComputeResource{
|
||||
Summary: summary,
|
||||
Network: esx.Datacenter.Network,
|
||||
}
|
||||
cr.EnvironmentBrowser = newEnvironmentBrowser()
|
||||
cr.Self = *host.Parent
|
||||
cr.Name = host.Name
|
||||
cr.Host = append(cr.Host, host.Reference())
|
||||
host.Network = cr.Network
|
||||
Map.PutEntity(cr, host)
|
||||
|
||||
pool := NewResourcePool()
|
||||
cr.ResourcePool = &pool.Self
|
||||
Map.PutEntity(cr, pool)
|
||||
pool.Owner = cr.Self
|
||||
|
||||
folderPutChild(ctx, &Map.Get(dc.HostFolder).(*Folder).Folder, cr)
|
||||
}
|
||||
|
||||
// CreateStandaloneHost uses esx.HostSystem as a template, applying the given spec
|
||||
// and creating the ComputeResource parent and ResourcePool sibling.
|
||||
func CreateStandaloneHost(ctx *Context, f *Folder, spec types.HostConnectSpec) (*HostSystem, types.BaseMethodFault) {
|
||||
if spec.HostName == "" {
|
||||
return nil, &types.NoHost{}
|
||||
}
|
||||
|
||||
pool := NewResourcePool()
|
||||
host := NewHostSystem(esx.HostSystem)
|
||||
host.configure(spec, false)
|
||||
|
||||
summary := new(types.ComputeResourceSummary)
|
||||
addComputeResource(summary, host)
|
||||
|
||||
cr := &mo.ComputeResource{
|
||||
ConfigurationEx: &types.ComputeResourceConfigInfo{
|
||||
VmSwapPlacement: string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory),
|
||||
},
|
||||
Summary: summary,
|
||||
EnvironmentBrowser: newEnvironmentBrowser(),
|
||||
}
|
||||
|
||||
Map.PutEntity(cr, Map.NewEntity(host))
|
||||
host.Summary.Host = &host.Self
|
||||
|
||||
Map.PutEntity(cr, Map.NewEntity(pool))
|
||||
|
||||
cr.Name = host.Name
|
||||
cr.Network = Map.getEntityDatacenter(f).defaultNetwork()
|
||||
cr.Host = append(cr.Host, host.Reference())
|
||||
cr.ResourcePool = &pool.Self
|
||||
|
||||
folderPutChild(ctx, &f.Folder, cr)
|
||||
pool.Owner = cr.Self
|
||||
host.Network = cr.Network
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
func (h *HostSystem) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
|
||||
task := CreateTask(h, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
if len(h.Vm) > 0 {
|
||||
return nil, &types.ResourceInUse{}
|
||||
}
|
||||
|
||||
ctx.postEvent(&types.HostRemovedEvent{HostEvent: h.event()})
|
||||
|
||||
f := Map.getEntityParent(h, "Folder").(*Folder)
|
||||
folderRemoveChild(ctx, &f.Folder, h.Reference())
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.Destroy_TaskBody{
|
||||
Res: &types.Destroy_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostSystem) EnterMaintenanceModeTask(spec *types.EnterMaintenanceMode_Task) soap.HasFault {
|
||||
task := CreateTask(h, "enterMaintenanceMode", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
h.Runtime.InMaintenanceMode = true
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.EnterMaintenanceMode_TaskBody{
|
||||
Res: &types.EnterMaintenanceMode_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostSystem) ExitMaintenanceModeTask(spec *types.ExitMaintenanceMode_Task) soap.HasFault {
|
||||
task := CreateTask(h, "exitMaintenanceMode", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
h.Runtime.InMaintenanceMode = false
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.ExitMaintenanceMode_TaskBody{
|
||||
Res: &types.ExitMaintenanceMode_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2019 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type HttpNfcLease struct {
|
||||
mo.HttpNfcLease
|
||||
files map[string]string
|
||||
}
|
||||
|
||||
var (
|
||||
nfcLease sync.Map // HTTP access to NFC leases are token based and do not require Session auth
|
||||
nfcPrefix = "/nfc/"
|
||||
)
|
||||
|
||||
// ServeNFC handles NFC file upload/download
|
||||
func ServeNFC(w http.ResponseWriter, r *http.Request) {
|
||||
p := strings.Split(r.URL.Path, "/")
|
||||
id, name := p[len(p)-2], p[len(p)-1]
|
||||
ref := types.ManagedObjectReference{Type: "HttpNfcLease", Value: id}
|
||||
l, ok := nfcLease.Load(ref)
|
||||
if !ok {
|
||||
log.Printf("invalid NFC lease: %s", id)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
lease := l.(*HttpNfcLease)
|
||||
file, ok := lease.files[name]
|
||||
if !ok {
|
||||
log.Printf("invalid NFC device id: %s", name)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
status := http.StatusOK
|
||||
var dst io.Writer
|
||||
var src io.ReadCloser
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodPut, http.MethodPost:
|
||||
dst = ioutil.Discard
|
||||
src = r.Body
|
||||
case http.MethodGet:
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
src = f
|
||||
default:
|
||||
status = http.StatusMethodNotAllowed
|
||||
}
|
||||
|
||||
n, err := io.Copy(dst, src)
|
||||
_ = src.Close()
|
||||
|
||||
msg := fmt.Sprintf("transferred %d bytes", n)
|
||||
if err != nil {
|
||||
status = http.StatusInternalServerError
|
||||
msg = err.Error()
|
||||
}
|
||||
log.Printf("nfc %s %s: %s", r.Method, file, msg)
|
||||
w.WriteHeader(status)
|
||||
}
|
||||
|
||||
func NewHttpNfcLease(ctx *Context, entity types.ManagedObjectReference) *HttpNfcLease {
|
||||
lease := &HttpNfcLease{
|
||||
HttpNfcLease: mo.HttpNfcLease{
|
||||
Info: &types.HttpNfcLeaseInfo{
|
||||
Entity: entity,
|
||||
LeaseTimeout: 30000,
|
||||
},
|
||||
State: types.HttpNfcLeaseStateReady,
|
||||
},
|
||||
files: make(map[string]string),
|
||||
}
|
||||
|
||||
ctx.Session.Put(lease)
|
||||
nfcLease.Store(lease.Reference(), lease)
|
||||
|
||||
return lease
|
||||
}
|
||||
|
||||
func (l *HttpNfcLease) HttpNfcLeaseComplete(ctx *Context, req *types.HttpNfcLeaseComplete) soap.HasFault {
|
||||
ctx.Session.Remove(req.This)
|
||||
nfcLease.Delete(req.This)
|
||||
|
||||
return &methods.HttpNfcLeaseCompleteBody{
|
||||
Res: new(types.HttpNfcLeaseCompleteResponse),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *HttpNfcLease) HttpNfcLeaseAbort(ctx *Context, req *types.HttpNfcLeaseAbort) soap.HasFault {
|
||||
ctx.Session.Remove(req.This)
|
||||
nfcLease.Delete(req.This)
|
||||
|
||||
return &methods.HttpNfcLeaseAbortBody{
|
||||
Res: new(types.HttpNfcLeaseAbortResponse),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *HttpNfcLease) HttpNfcLeaseProgress(ctx *Context, req *types.HttpNfcLeaseProgress) soap.HasFault {
|
||||
l.TransferProgress = req.Percent
|
||||
|
||||
return &methods.HttpNfcLeaseProgressBody{
|
||||
Res: new(types.HttpNfcLeaseProgressResponse),
|
||||
}
|
||||
}
|
|
@ -1,343 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Implementation of Server
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Server is an HTTP server listening on a system-chosen port on the
|
||||
// local loopback interface, for use in end-to-end HTTP tests.
|
||||
type Server struct {
|
||||
URL string // base URL of form http://ipaddr:port with no trailing slash
|
||||
Listener net.Listener
|
||||
|
||||
// TLS is the optional TLS configuration, populated with a new config
|
||||
// after TLS is started. If set on an unstarted server before StartTLS
|
||||
// is called, existing fields are copied into the new config.
|
||||
TLS *tls.Config
|
||||
|
||||
// Config may be changed after calling NewUnstartedServer and
|
||||
// before Start or StartTLS.
|
||||
Config *http.Server
|
||||
|
||||
// certificate is a parsed version of the TLS config certificate, if present.
|
||||
certificate *x509.Certificate
|
||||
|
||||
// wg counts the number of outstanding HTTP requests on this server.
|
||||
// Close blocks until all requests are finished.
|
||||
wg sync.WaitGroup
|
||||
|
||||
mu sync.Mutex // guards closed and conns
|
||||
closed bool
|
||||
conns map[net.Conn]http.ConnState // except terminal states
|
||||
|
||||
// client is configured for use with the server.
|
||||
// Its transport is automatically closed when Close is called.
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func newLocalListener(serve string) net.Listener {
|
||||
if serve != "" {
|
||||
l, err := net.Listen("tcp", serve)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("httptest: failed to listen on %v: %v", serve, err))
|
||||
}
|
||||
return l
|
||||
}
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
|
||||
panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// NewServer starts and returns a new Server.
|
||||
// The caller should call Close when finished, to shut it down.
|
||||
func NewServer(handler http.Handler) *Server {
|
||||
ts := NewUnstartedServer(handler, "")
|
||||
ts.Start()
|
||||
return ts
|
||||
}
|
||||
|
||||
// NewUnstartedServer returns a new Server but doesn't start it.
|
||||
//
|
||||
// After changing its configuration, the caller should call Start or
|
||||
// StartTLS.
|
||||
//
|
||||
// The caller should call Close when finished, to shut it down.
|
||||
// serve allows the server's listen address to be specified.
|
||||
func NewUnstartedServer(handler http.Handler, serve string) *Server {
|
||||
return &Server{
|
||||
Listener: newLocalListener(serve),
|
||||
Config: &http.Server{Handler: handler},
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts a server from NewUnstartedServer.
|
||||
func (s *Server) Start() {
|
||||
if s.URL != "" {
|
||||
panic("Server already started")
|
||||
}
|
||||
if s.client == nil {
|
||||
s.client = &http.Client{Transport: &http.Transport{}}
|
||||
}
|
||||
s.URL = "http://" + s.Listener.Addr().String()
|
||||
s.wrap()
|
||||
s.goServe()
|
||||
}
|
||||
|
||||
// StartTLS starts TLS on a server from NewUnstartedServer.
|
||||
func (s *Server) StartTLS() {
|
||||
if s.URL != "" {
|
||||
panic("Server already started")
|
||||
}
|
||||
if s.client == nil {
|
||||
s.client = &http.Client{Transport: &http.Transport{}}
|
||||
}
|
||||
cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("httptest: NewTLSServer: %v", err))
|
||||
}
|
||||
|
||||
existingConfig := s.TLS
|
||||
if existingConfig != nil {
|
||||
s.TLS = existingConfig.Clone()
|
||||
} else {
|
||||
s.TLS = new(tls.Config)
|
||||
}
|
||||
if s.TLS.NextProtos == nil {
|
||||
s.TLS.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
if len(s.TLS.Certificates) == 0 {
|
||||
s.TLS.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
s.certificate, err = x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("httptest: NewTLSServer: %v", err))
|
||||
}
|
||||
certpool := x509.NewCertPool()
|
||||
certpool.AddCert(s.certificate)
|
||||
s.client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certpool,
|
||||
},
|
||||
}
|
||||
s.Listener = tls.NewListener(s.Listener, s.TLS)
|
||||
s.URL = "https://" + s.Listener.Addr().String()
|
||||
s.wrap()
|
||||
s.goServe()
|
||||
}
|
||||
|
||||
// NewTLSServer starts and returns a new Server using TLS.
|
||||
// The caller should call Close when finished, to shut it down.
|
||||
func NewTLSServer(handler http.Handler) *Server {
|
||||
ts := NewUnstartedServer(handler, "")
|
||||
ts.StartTLS()
|
||||
return ts
|
||||
}
|
||||
|
||||
type closeIdleTransport interface {
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
// Close shuts down the server and blocks until all outstanding
|
||||
// requests on this server have completed.
|
||||
func (s *Server) Close() {
|
||||
s.mu.Lock()
|
||||
if !s.closed {
|
||||
s.closed = true
|
||||
s.Listener.Close()
|
||||
s.Config.SetKeepAlivesEnabled(false)
|
||||
for c, st := range s.conns {
|
||||
// Force-close any idle connections (those between
|
||||
// requests) and new connections (those which connected
|
||||
// but never sent a request). StateNew connections are
|
||||
// super rare and have only been seen (in
|
||||
// previously-flaky tests) in the case of
|
||||
// socket-late-binding races from the http Client
|
||||
// dialing this server and then getting an idle
|
||||
// connection before the dial completed. There is thus
|
||||
// a connected connection in StateNew with no
|
||||
// associated Request. We only close StateIdle and
|
||||
// StateNew because they're not doing anything. It's
|
||||
// possible StateNew is about to do something in a few
|
||||
// milliseconds, but a previous CL to check again in a
|
||||
// few milliseconds wasn't liked (early versions of
|
||||
// https://golang.org/cl/15151) so now we just
|
||||
// forcefully close StateNew. The docs for Server.Close say
|
||||
// we wait for "outstanding requests", so we don't close things
|
||||
// in StateActive.
|
||||
if st == http.StateIdle || st == http.StateNew {
|
||||
s.closeConn(c)
|
||||
}
|
||||
}
|
||||
// If this server doesn't shut down in 5 seconds, tell the user why.
|
||||
t := time.AfterFunc(5*time.Second, s.logCloseHangDebugInfo)
|
||||
defer t.Stop()
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
// Not part of httptest.Server's correctness, but assume most
|
||||
// users of httptest.Server will be using the standard
|
||||
// transport, so help them out and close any idle connections for them.
|
||||
if t, ok := http.DefaultTransport.(closeIdleTransport); ok {
|
||||
t.CloseIdleConnections()
|
||||
}
|
||||
|
||||
// Also close the client idle connections.
|
||||
if s.client != nil {
|
||||
if t, ok := s.client.Transport.(closeIdleTransport); ok {
|
||||
t.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
s.wg.Wait()
|
||||
}
|
||||
|
||||
func (s *Server) logCloseHangDebugInfo() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
var buf strings.Builder
|
||||
buf.WriteString("httptest.Server blocked in Close after 5 seconds, waiting for connections:\n")
|
||||
for c, st := range s.conns {
|
||||
fmt.Fprintf(&buf, " %T %p %v in state %v\n", c, c, c.RemoteAddr(), st)
|
||||
}
|
||||
log.Print(buf.String())
|
||||
}
|
||||
|
||||
// CloseClientConnections closes any open HTTP connections to the test Server.
|
||||
func (s *Server) CloseClientConnections() {
|
||||
s.mu.Lock()
|
||||
nconn := len(s.conns)
|
||||
ch := make(chan struct{}, nconn)
|
||||
for c := range s.conns {
|
||||
go s.closeConnChan(c, ch)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
// Wait for outstanding closes to finish.
|
||||
//
|
||||
// Out of paranoia for making a late change in Go 1.6, we
|
||||
// bound how long this can wait, since golang.org/issue/14291
|
||||
// isn't fully understood yet. At least this should only be used
|
||||
// in tests.
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
defer timer.Stop()
|
||||
for i := 0; i < nconn; i++ {
|
||||
select {
|
||||
case <-ch:
|
||||
case <-timer.C:
|
||||
// Too slow. Give up.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Certificate returns the certificate used by the server, or nil if
|
||||
// the server doesn't use TLS.
|
||||
func (s *Server) Certificate() *x509.Certificate {
|
||||
return s.certificate
|
||||
}
|
||||
|
||||
// Client returns an HTTP client configured for making requests to the server.
|
||||
// It is configured to trust the server's TLS test certificate and will
|
||||
// close its idle connections on Server.Close.
|
||||
func (s *Server) Client() *http.Client {
|
||||
return s.client
|
||||
}
|
||||
|
||||
func (s *Server) goServe() {
|
||||
s.wg.Add(1)
|
||||
go func() {
|
||||
defer s.wg.Done()
|
||||
s.Config.Serve(s.Listener)
|
||||
}()
|
||||
}
|
||||
|
||||
// wrap installs the connection state-tracking hook to know which
|
||||
// connections are idle.
|
||||
func (s *Server) wrap() {
|
||||
oldHook := s.Config.ConnState
|
||||
s.Config.ConnState = func(c net.Conn, cs http.ConnState) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
switch cs {
|
||||
case http.StateNew:
|
||||
s.wg.Add(1)
|
||||
if _, exists := s.conns[c]; exists {
|
||||
panic("invalid state transition")
|
||||
}
|
||||
if s.conns == nil {
|
||||
s.conns = make(map[net.Conn]http.ConnState)
|
||||
}
|
||||
s.conns[c] = cs
|
||||
if s.closed {
|
||||
// Probably just a socket-late-binding dial from
|
||||
// the default transport that lost the race (and
|
||||
// thus this connection is now idle and will
|
||||
// never be used).
|
||||
s.closeConn(c)
|
||||
}
|
||||
case http.StateActive:
|
||||
if oldState, ok := s.conns[c]; ok {
|
||||
if oldState != http.StateNew && oldState != http.StateIdle {
|
||||
panic("invalid state transition")
|
||||
}
|
||||
s.conns[c] = cs
|
||||
}
|
||||
case http.StateIdle:
|
||||
if oldState, ok := s.conns[c]; ok {
|
||||
if oldState != http.StateActive {
|
||||
panic("invalid state transition")
|
||||
}
|
||||
s.conns[c] = cs
|
||||
}
|
||||
if s.closed {
|
||||
s.closeConn(c)
|
||||
}
|
||||
case http.StateHijacked, http.StateClosed:
|
||||
s.forgetConn(c)
|
||||
}
|
||||
if oldHook != nil {
|
||||
oldHook(c, cs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// closeConn closes c.
|
||||
// s.mu must be held.
|
||||
func (s *Server) closeConn(c net.Conn) { s.closeConnChan(c, nil) }
|
||||
|
||||
// closeConnChan is like closeConn, but takes an optional channel to receive a value
|
||||
// when the goroutine closing c is done.
|
||||
func (s *Server) closeConnChan(c net.Conn, done chan<- struct{}) {
|
||||
c.Close()
|
||||
if done != nil {
|
||||
done <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// forgetConn removes c from the set of tracked conns and decrements it from the
|
||||
// waitgroup, unless it was previously removed.
|
||||
// s.mu must be held.
|
||||
func (s *Server) forgetConn(c net.Conn) {
|
||||
if _, ok := s.conns[c]; ok {
|
||||
delete(s.conns, c)
|
||||
s.wg.Done()
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
|
||||
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
|
||||
// generated from src/crypto/tls:
|
||||
// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||||
var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
||||
iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4
|
||||
iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul
|
||||
rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO
|
||||
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
|
||||
AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA
|
||||
AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9
|
||||
tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs
|
||||
h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM
|
||||
fblo6RBxUQ==
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
// LocalhostKey is the private key for localhostCert.
|
||||
var LocalhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9
|
||||
SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZB
|
||||
l2+XsDulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB
|
||||
AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
|
||||
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
|
||||
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
|
||||
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
|
||||
jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
|
||||
fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
|
||||
fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU
|
||||
y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtIX
|
||||
qyUBnu3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo
|
||||
f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA==
|
||||
-----END RSA PRIVATE KEY-----`)
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2019 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// Minimal set of internal types and methods:
|
||||
// - Fetch() - used by ovftool to collect various managed object properties
|
||||
// - RetrieveInternalContent() - used by ovftool to obtain a reference to NfcService (which it does not use by default)
|
||||
|
||||
func init() {
|
||||
types.Add("Fetch", reflect.TypeOf((*Fetch)(nil)).Elem())
|
||||
}
|
||||
|
||||
type Fetch struct {
|
||||
This types.ManagedObjectReference `xml:"_this"`
|
||||
Prop string `xml:"prop"`
|
||||
}
|
||||
|
||||
type FetchResponse struct {
|
||||
Returnval types.AnyType `xml:"returnval,omitempty,typeattr"`
|
||||
}
|
||||
|
||||
type FetchBody struct {
|
||||
Res *FetchResponse `xml:"FetchResponse,omitempty"`
|
||||
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
|
||||
}
|
||||
|
||||
func (b *FetchBody) Fault() *soap.Fault { return b.Fault_ }
|
||||
|
||||
func init() {
|
||||
types.Add("RetrieveInternalContent", reflect.TypeOf((*RetrieveInternalContent)(nil)).Elem())
|
||||
}
|
||||
|
||||
type RetrieveInternalContent struct {
|
||||
This types.ManagedObjectReference `xml:"_this"`
|
||||
}
|
||||
|
||||
type RetrieveInternalContentResponse struct {
|
||||
Returnval InternalServiceInstanceContent `xml:"returnval"`
|
||||
}
|
||||
|
||||
type RetrieveInternalContentBody struct {
|
||||
Res *RetrieveInternalContentResponse `xml:"RetrieveInternalContentResponse,omitempty"`
|
||||
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
|
||||
}
|
||||
|
||||
func (b *RetrieveInternalContentBody) Fault() *soap.Fault { return b.Fault_ }
|
||||
|
||||
type InternalServiceInstanceContent struct {
|
||||
types.DynamicData
|
||||
|
||||
NfcService types.ManagedObjectReference `xml:"nfcService"`
|
||||
}
|
|
@ -1,387 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var ipPool = MustNewIpPool(&types.IpPool{
|
||||
Id: 1,
|
||||
Name: "ip-pool",
|
||||
AvailableIpv4Addresses: 250,
|
||||
AvailableIpv6Addresses: 250,
|
||||
AllocatedIpv6Addresses: 0,
|
||||
AllocatedIpv4Addresses: 0,
|
||||
Ipv4Config: &types.IpPoolIpPoolConfigInfo{
|
||||
Netmask: "10.10.10.255",
|
||||
Gateway: "10.10.10.1",
|
||||
SubnetAddress: "10.10.10.0",
|
||||
Range: "10.10.10.2#250",
|
||||
},
|
||||
Ipv6Config: &types.IpPoolIpPoolConfigInfo{
|
||||
Netmask: "2001:4860:0:2001::ff",
|
||||
Gateway: "2001:4860:0:2001::1",
|
||||
SubnetAddress: "2001:4860:0:2001::0",
|
||||
Range: "2001:4860:0:2001::2#250",
|
||||
},
|
||||
})
|
||||
|
||||
// IpPoolManager implements a simple IP Pool manager in which all pools are shared
|
||||
// across different datacenters.
|
||||
type IpPoolManager struct {
|
||||
mo.IpPoolManager
|
||||
|
||||
pools map[int32]*IpPool
|
||||
nextPoolId int32
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) init(*Registry) {
|
||||
m.pools = map[int32]*IpPool{
|
||||
1: ipPool,
|
||||
}
|
||||
m.nextPoolId = 2
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) CreateIpPool(req *types.CreateIpPool) soap.HasFault {
|
||||
body := &methods.CreateIpPoolBody{}
|
||||
id := m.nextPoolId
|
||||
|
||||
var err error
|
||||
m.pools[id], err = NewIpPool(&req.Pool)
|
||||
if err != nil {
|
||||
body.Fault_ = Fault("", &types.RuntimeFault{})
|
||||
return body
|
||||
}
|
||||
|
||||
m.nextPoolId++
|
||||
|
||||
body.Res = &types.CreateIpPoolResponse{
|
||||
Returnval: id,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) DestroyIpPool(req *types.DestroyIpPool) soap.HasFault {
|
||||
delete(m.pools, req.Id)
|
||||
|
||||
return &methods.DestroyIpPoolBody{
|
||||
Res: &types.DestroyIpPoolResponse{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) QueryIpPools(req *types.QueryIpPools) soap.HasFault {
|
||||
pools := []types.IpPool{}
|
||||
|
||||
for i := int32(1); i < m.nextPoolId; i++ {
|
||||
if p, ok := m.pools[i]; ok {
|
||||
pools = append(pools, *p.config)
|
||||
}
|
||||
}
|
||||
|
||||
return &methods.QueryIpPoolsBody{
|
||||
Res: &types.QueryIpPoolsResponse{
|
||||
Returnval: pools,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) UpdateIpPool(req *types.UpdateIpPool) soap.HasFault {
|
||||
body := &methods.UpdateIpPoolBody{}
|
||||
|
||||
var pool *IpPool
|
||||
var err error
|
||||
var ok bool
|
||||
|
||||
if pool, ok = m.pools[req.Pool.Id]; !ok {
|
||||
body.Fault_ = Fault("", &types.NotFoundFault{})
|
||||
return body
|
||||
}
|
||||
|
||||
if pool.config.AllocatedIpv4Addresses+pool.config.AllocatedIpv6Addresses != 0 {
|
||||
body.Fault_ = Fault("update a pool has been used is not supported", &types.RuntimeFault{})
|
||||
return body
|
||||
}
|
||||
|
||||
m.pools[req.Pool.Id], err = NewIpPool(&req.Pool)
|
||||
if err != nil {
|
||||
body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.UpdateIpPoolResponse{}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) AllocateIpv4Address(req *types.AllocateIpv4Address) soap.HasFault {
|
||||
body := &methods.AllocateIpv4AddressBody{}
|
||||
|
||||
pool, ok := m.pools[req.PoolId]
|
||||
if !ok {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{})
|
||||
return body
|
||||
}
|
||||
|
||||
ip, err := pool.AllocateIPv4(req.AllocationId)
|
||||
if err != nil {
|
||||
body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.AllocateIpv4AddressResponse{
|
||||
Returnval: ip,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) AllocateIpv6Address(req *types.AllocateIpv6Address) soap.HasFault {
|
||||
body := &methods.AllocateIpv6AddressBody{}
|
||||
|
||||
pool, ok := m.pools[req.PoolId]
|
||||
if !ok {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{})
|
||||
return body
|
||||
}
|
||||
|
||||
ip, err := pool.AllocateIpv6(req.AllocationId)
|
||||
if err != nil {
|
||||
body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.AllocateIpv6AddressResponse{
|
||||
Returnval: ip,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) ReleaseIpAllocation(req *types.ReleaseIpAllocation) soap.HasFault {
|
||||
body := &methods.ReleaseIpAllocationBody{}
|
||||
|
||||
pool, ok := m.pools[req.PoolId]
|
||||
if !ok {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{})
|
||||
return body
|
||||
}
|
||||
|
||||
pool.ReleaseIpv4(req.AllocationId)
|
||||
pool.ReleaseIpv6(req.AllocationId)
|
||||
|
||||
body.Res = &types.ReleaseIpAllocationResponse{}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *IpPoolManager) QueryIPAllocations(req *types.QueryIPAllocations) soap.HasFault {
|
||||
body := &methods.QueryIPAllocationsBody{}
|
||||
|
||||
pool, ok := m.pools[req.PoolId]
|
||||
if !ok {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{})
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.QueryIPAllocationsResponse{}
|
||||
|
||||
ipv4, ok := pool.ipv4Allocation[req.ExtensionKey]
|
||||
if ok {
|
||||
body.Res.Returnval = append(body.Res.Returnval, types.IpPoolManagerIpAllocation{
|
||||
IpAddress: ipv4,
|
||||
AllocationId: req.ExtensionKey,
|
||||
})
|
||||
}
|
||||
|
||||
ipv6, ok := pool.ipv6Allocation[req.ExtensionKey]
|
||||
if ok {
|
||||
body.Res.Returnval = append(body.Res.Returnval, types.IpPoolManagerIpAllocation{
|
||||
IpAddress: ipv6,
|
||||
AllocationId: req.ExtensionKey,
|
||||
})
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
var (
|
||||
errNoIpAvailable = errors.New("no ip address available")
|
||||
errInvalidAllocation = errors.New("allocation id not recognized")
|
||||
)
|
||||
|
||||
type IpPool struct {
|
||||
config *types.IpPool
|
||||
ipv4Allocation map[string]string
|
||||
ipv6Allocation map[string]string
|
||||
ipv4Pool []string
|
||||
ipv6Pool []string
|
||||
}
|
||||
|
||||
func MustNewIpPool(config *types.IpPool) *IpPool {
|
||||
pool, err := NewIpPool(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
func NewIpPool(config *types.IpPool) (*IpPool, error) {
|
||||
pool := &IpPool{
|
||||
config: config,
|
||||
ipv4Allocation: make(map[string]string),
|
||||
ipv6Allocation: make(map[string]string),
|
||||
}
|
||||
|
||||
return pool, pool.init()
|
||||
}
|
||||
|
||||
func (p *IpPool) init() error {
|
||||
// IPv4 range
|
||||
if p.config.Ipv4Config != nil {
|
||||
ranges := strings.Split(p.config.Ipv4Config.Range, ",")
|
||||
for _, r := range ranges {
|
||||
sp := strings.Split(r, "#")
|
||||
if len(sp) != 2 {
|
||||
return fmt.Errorf("format of range should be ip#number; got %q", r)
|
||||
}
|
||||
|
||||
ip := net.ParseIP(strings.TrimSpace(sp[0])).To4()
|
||||
if ip == nil {
|
||||
return fmt.Errorf("bad ip format: %q", sp[0])
|
||||
}
|
||||
|
||||
length, err := strconv.Atoi(sp[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
p.ipv4Pool = append(p.ipv4Pool, net.IPv4(ip[0], ip[1], ip[2], ip[3]+byte(i)).String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6 range
|
||||
if p.config.Ipv6Config != nil {
|
||||
ranges := strings.Split(p.config.Ipv6Config.Range, ",")
|
||||
for _, r := range ranges {
|
||||
sp := strings.Split(r, "#")
|
||||
if len(sp) != 2 {
|
||||
return fmt.Errorf("format of range should be ip#number; got %q", r)
|
||||
}
|
||||
|
||||
ip := net.ParseIP(strings.TrimSpace(sp[0])).To16()
|
||||
if ip == nil {
|
||||
return fmt.Errorf("bad ip format: %q", sp[0])
|
||||
}
|
||||
|
||||
length, err := strconv.Atoi(sp[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
var ipv6 [16]byte
|
||||
copy(ipv6[:], ip)
|
||||
ipv6[15] += byte(i)
|
||||
p.ipv6Pool = append(p.ipv6Pool, net.IP(ipv6[:]).String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *IpPool) AllocateIPv4(allocation string) (string, error) {
|
||||
if ip, ok := p.ipv4Allocation[allocation]; ok {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
l := len(p.ipv4Pool)
|
||||
if l == 0 {
|
||||
return "", errNoIpAvailable
|
||||
}
|
||||
|
||||
ip := p.ipv4Pool[l-1]
|
||||
|
||||
p.config.AvailableIpv4Addresses--
|
||||
p.config.AllocatedIpv4Addresses++
|
||||
p.ipv4Pool = p.ipv4Pool[:l-1]
|
||||
p.ipv4Allocation[allocation] = ip
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func (p *IpPool) ReleaseIpv4(allocation string) error {
|
||||
ip, ok := p.ipv4Allocation[allocation]
|
||||
if !ok {
|
||||
return errInvalidAllocation
|
||||
}
|
||||
|
||||
delete(p.ipv4Allocation, allocation)
|
||||
p.config.AvailableIpv4Addresses++
|
||||
p.config.AllocatedIpv4Addresses--
|
||||
p.ipv4Pool = append(p.ipv4Pool, ip)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *IpPool) AllocateIpv6(allocation string) (string, error) {
|
||||
if ip, ok := p.ipv6Allocation[allocation]; ok {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
l := len(p.ipv6Pool)
|
||||
if l == 0 {
|
||||
return "", errNoIpAvailable
|
||||
}
|
||||
|
||||
ip := p.ipv6Pool[l-1]
|
||||
|
||||
p.config.AvailableIpv6Addresses--
|
||||
p.config.AllocatedIpv6Addresses++
|
||||
p.ipv6Pool = p.ipv6Pool[:l-1]
|
||||
p.ipv6Allocation[allocation] = ip
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func (p *IpPool) ReleaseIpv6(allocation string) error {
|
||||
ip, ok := p.ipv6Allocation[allocation]
|
||||
if !ok {
|
||||
return errInvalidAllocation
|
||||
}
|
||||
|
||||
delete(p.ipv6Allocation, allocation)
|
||||
p.config.AvailableIpv6Addresses++
|
||||
p.config.AllocatedIpv6Addresses--
|
||||
p.ipv6Pool = append(p.ipv6Pool, ip)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// EvalLicense is the default license
|
||||
var EvalLicense = types.LicenseManagerLicenseInfo{
|
||||
LicenseKey: "00000-00000-00000-00000-00000",
|
||||
EditionKey: "eval",
|
||||
Name: "Evaluation Mode",
|
||||
Properties: []types.KeyAnyValue{
|
||||
{
|
||||
Key: "feature",
|
||||
Value: types.KeyValue{
|
||||
Key: "serialuri:2",
|
||||
Value: "Remote virtual Serial Port Concentrator",
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "feature",
|
||||
Value: types.KeyValue{
|
||||
Key: "dvs",
|
||||
Value: "vSphere Distributed Switch",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type LicenseManager struct {
|
||||
mo.LicenseManager
|
||||
}
|
||||
|
||||
func (m *LicenseManager) init(r *Registry) {
|
||||
m.Licenses = []types.LicenseManagerLicenseInfo{EvalLicense}
|
||||
|
||||
if r.IsVPX() {
|
||||
am := Map.Put(&LicenseAssignmentManager{}).Reference()
|
||||
m.LicenseAssignmentManager = &am
|
||||
}
|
||||
}
|
||||
|
||||
func (m *LicenseManager) AddLicense(req *types.AddLicense) soap.HasFault {
|
||||
body := &methods.AddLicenseBody{
|
||||
Res: &types.AddLicenseResponse{},
|
||||
}
|
||||
|
||||
for _, license := range m.Licenses {
|
||||
if license.LicenseKey == req.LicenseKey {
|
||||
body.Res.Returnval = licenseInfo(license.LicenseKey, license.Labels)
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
m.Licenses = append(m.Licenses, types.LicenseManagerLicenseInfo{
|
||||
LicenseKey: req.LicenseKey,
|
||||
Labels: req.Labels,
|
||||
})
|
||||
|
||||
body.Res.Returnval = licenseInfo(req.LicenseKey, req.Labels)
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *LicenseManager) RemoveLicense(req *types.RemoveLicense) soap.HasFault {
|
||||
body := &methods.RemoveLicenseBody{
|
||||
Res: &types.RemoveLicenseResponse{},
|
||||
}
|
||||
|
||||
for i, license := range m.Licenses {
|
||||
if req.LicenseKey == license.LicenseKey {
|
||||
m.Licenses = append(m.Licenses[:i], m.Licenses[i+1:]...)
|
||||
return body
|
||||
}
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *LicenseManager) UpdateLicenseLabel(req *types.UpdateLicenseLabel) soap.HasFault {
|
||||
body := &methods.UpdateLicenseLabelBody{}
|
||||
|
||||
for i := range m.Licenses {
|
||||
license := &m.Licenses[i]
|
||||
|
||||
if req.LicenseKey != license.LicenseKey {
|
||||
continue
|
||||
}
|
||||
|
||||
body.Res = new(types.UpdateLicenseLabelResponse)
|
||||
|
||||
for j := range license.Labels {
|
||||
label := &license.Labels[j]
|
||||
|
||||
if label.Key == req.LabelKey {
|
||||
if req.LabelValue == "" {
|
||||
license.Labels = append(license.Labels[:i], license.Labels[i+1:]...)
|
||||
} else {
|
||||
label.Value = req.LabelValue
|
||||
}
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
license.Labels = append(license.Labels, types.KeyValue{
|
||||
Key: req.LabelKey,
|
||||
Value: req.LabelValue,
|
||||
})
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "licenseKey"})
|
||||
return body
|
||||
}
|
||||
|
||||
type LicenseAssignmentManager struct {
|
||||
mo.LicenseAssignmentManager
|
||||
}
|
||||
|
||||
func (m *LicenseAssignmentManager) QueryAssignedLicenses(req *types.QueryAssignedLicenses) soap.HasFault {
|
||||
body := &methods.QueryAssignedLicensesBody{
|
||||
Res: &types.QueryAssignedLicensesResponse{},
|
||||
}
|
||||
|
||||
// EntityId can be a HostSystem or the vCenter InstanceUuid
|
||||
if req.EntityId != "" {
|
||||
if req.EntityId != Map.content().About.InstanceUuid {
|
||||
id := types.ManagedObjectReference{
|
||||
Type: "HostSystem",
|
||||
Value: req.EntityId,
|
||||
}
|
||||
|
||||
if Map.Get(id) == nil {
|
||||
return body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.Res.Returnval = []types.LicenseAssignmentManagerLicenseAssignment{
|
||||
{
|
||||
EntityId: req.EntityId,
|
||||
AssignedLicense: EvalLicense,
|
||||
},
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func licenseInfo(key string, labels []types.KeyValue) types.LicenseManagerLicenseInfo {
|
||||
info := EvalLicense
|
||||
|
||||
info.LicenseKey = key
|
||||
info.Labels = labels
|
||||
|
||||
return info
|
||||
}
|
|
@ -1,824 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/vmware/govmomi"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/simulator/vpx"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"github.com/vmware/govmomi/vim25/xml"
|
||||
)
|
||||
|
||||
type DelayConfig struct {
|
||||
// Delay specifies the number of milliseconds to delay serving a SOAP call. 0 means no delay.
|
||||
// This can be used to simulate a poorly performing vCenter or network lag.
|
||||
Delay int
|
||||
|
||||
// Delay specifies the number of milliseconds to delay serving a specific method.
|
||||
// Each entry in the map represents the name of a method and its associated delay in milliseconds,
|
||||
// This can be used to simulate a poorly performing vCenter or network lag.
|
||||
MethodDelay map[string]int
|
||||
|
||||
// DelayJitter defines the delay jitter as a coefficient of variation (stddev/mean).
|
||||
// This can be used to simulate unpredictable delay. 0 means no jitter, i.e. all invocations get the same delay.
|
||||
DelayJitter float64
|
||||
}
|
||||
|
||||
// Model is used to populate a Model with an initial set of managed entities.
|
||||
// This is a simple helper for tests running against a simulator, to populate an inventory
|
||||
// with commonly used models.
|
||||
// The inventory names generated by a Model have a string prefix per-type and integer suffix per-instance.
|
||||
// The names are concatenated with their ancestor names and delimited by '_', making the generated names unique.
|
||||
type Model struct {
|
||||
Service *Service `json:"-"`
|
||||
|
||||
ServiceContent types.ServiceContent `json:"-"`
|
||||
RootFolder mo.Folder `json:"-"`
|
||||
|
||||
// Autostart will power on Model created VMs when true
|
||||
Autostart bool `json:"-"`
|
||||
|
||||
// Datacenter specifies the number of Datacenter entities to create
|
||||
// Name prefix: DC, vcsim flag: -dc
|
||||
Datacenter int
|
||||
|
||||
// Portgroup specifies the number of DistributedVirtualPortgroup entities to create per Datacenter
|
||||
// Name prefix: DVPG, vcsim flag: -pg
|
||||
Portgroup int
|
||||
|
||||
// PortgroupNSX specifies the number NSX backed DistributedVirtualPortgroup entities to create per Datacenter
|
||||
// Name prefix: NSXPG, vcsim flag: -nsx-pg
|
||||
PortgroupNSX int
|
||||
|
||||
// OpaqueNetwork specifies the number of OpaqueNetwork entities to create per Datacenter,
|
||||
// with Summary.OpaqueNetworkType set to nsx.LogicalSwitch and Summary.OpaqueNetworkId to a random uuid.
|
||||
// Name prefix: NSX, vcsim flag: -nsx
|
||||
OpaqueNetwork int
|
||||
|
||||
// Host specifies the number of standalone HostSystems entities to create per Datacenter
|
||||
// Name prefix: H, vcsim flag: -standalone-host
|
||||
Host int `json:",omitempty"`
|
||||
|
||||
// Cluster specifies the number of ClusterComputeResource entities to create per Datacenter
|
||||
// Name prefix: C, vcsim flag: -cluster
|
||||
Cluster int
|
||||
|
||||
// ClusterHost specifies the number of HostSystems entities to create within a Cluster
|
||||
// Name prefix: H, vcsim flag: -host
|
||||
ClusterHost int `json:",omitempty"`
|
||||
|
||||
// Pool specifies the number of ResourcePool entities to create per Cluster
|
||||
// Note that every cluster has a root ResourcePool named "Resources", as real vCenter does.
|
||||
// For example: /DC0/host/DC0_C0/Resources
|
||||
// The root ResourcePool is named "RP0" within other object names.
|
||||
// When Model.Pool is set to 1 or higher, this creates child ResourcePools under the root pool.
|
||||
// For example: /DC0/host/DC0_C0/Resources/DC0_C0_RP1
|
||||
// Name prefix: RP, vcsim flag: -pool
|
||||
Pool int
|
||||
|
||||
// Datastore specifies the number of Datastore entities to create
|
||||
// Each Datastore will have temporary local file storage and will be mounted
|
||||
// on every HostSystem created by the ModelConfig
|
||||
// Name prefix: LocalDS, vcsim flag: -ds
|
||||
Datastore int
|
||||
|
||||
// Machine specifies the number of VirtualMachine entities to create per ResourcePool
|
||||
// Name prefix: VM, vcsim flag: -vm
|
||||
Machine int
|
||||
|
||||
// Folder specifies the number of Datacenter to place within a Folder.
|
||||
// This includes a folder for the Datacenter itself and its host, vm, network and datastore folders.
|
||||
// All resources for the Datacenter are placed within these folders, rather than the top-level folders.
|
||||
// Name prefix: F, vcsim flag: -folder
|
||||
Folder int
|
||||
|
||||
// App specifies the number of VirtualApp to create per Cluster
|
||||
// Name prefix: APP, vcsim flag: -app
|
||||
App int
|
||||
|
||||
// Pod specifies the number of StoragePod to create per Cluster
|
||||
// Name prefix: POD, vcsim flag: -pod
|
||||
Pod int
|
||||
|
||||
// Delay configurations
|
||||
DelayConfig DelayConfig `json:"-"`
|
||||
|
||||
// total number of inventory objects, set by Count()
|
||||
total int
|
||||
|
||||
dirs []string
|
||||
}
|
||||
|
||||
// ESX is the default Model for a standalone ESX instance
|
||||
func ESX() *Model {
|
||||
return &Model{
|
||||
ServiceContent: esx.ServiceContent,
|
||||
RootFolder: esx.RootFolder,
|
||||
Autostart: true,
|
||||
Datastore: 1,
|
||||
Machine: 2,
|
||||
DelayConfig: DelayConfig{
|
||||
Delay: 0,
|
||||
DelayJitter: 0,
|
||||
MethodDelay: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// VPX is the default Model for a vCenter instance
|
||||
func VPX() *Model {
|
||||
return &Model{
|
||||
ServiceContent: vpx.ServiceContent,
|
||||
RootFolder: vpx.RootFolder,
|
||||
Autostart: true,
|
||||
Datacenter: 1,
|
||||
Portgroup: 1,
|
||||
Host: 1,
|
||||
Cluster: 1,
|
||||
ClusterHost: 3,
|
||||
Datastore: 1,
|
||||
Machine: 2,
|
||||
DelayConfig: DelayConfig{
|
||||
Delay: 0,
|
||||
DelayJitter: 0,
|
||||
MethodDelay: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Count returns a Model with total number of each existing type
|
||||
func (m *Model) Count() Model {
|
||||
count := Model{}
|
||||
|
||||
for ref, obj := range Map.objects {
|
||||
if _, ok := obj.(mo.Entity); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
count.total++
|
||||
|
||||
switch ref.Type {
|
||||
case "Datacenter":
|
||||
count.Datacenter++
|
||||
case "DistributedVirtualPortgroup":
|
||||
count.Portgroup++
|
||||
case "ClusterComputeResource":
|
||||
count.Cluster++
|
||||
case "Datastore":
|
||||
count.Datastore++
|
||||
case "HostSystem":
|
||||
count.Host++
|
||||
case "VirtualMachine":
|
||||
count.Machine++
|
||||
case "ResourcePool":
|
||||
count.Pool++
|
||||
case "VirtualApp":
|
||||
count.App++
|
||||
case "Folder":
|
||||
count.Folder++
|
||||
case "StoragePod":
|
||||
count.Pod++
|
||||
case "OpaqueNetwork":
|
||||
count.OpaqueNetwork++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func (*Model) fmtName(prefix string, num int) string {
|
||||
return fmt.Sprintf("%s%d", prefix, num)
|
||||
}
|
||||
|
||||
// kinds maps managed object types to their vcsim wrapper types
|
||||
var kinds = map[string]reflect.Type{
|
||||
"AuthorizationManager": reflect.TypeOf((*AuthorizationManager)(nil)).Elem(),
|
||||
"ClusterComputeResource": reflect.TypeOf((*ClusterComputeResource)(nil)).Elem(),
|
||||
"CustomFieldsManager": reflect.TypeOf((*CustomFieldsManager)(nil)).Elem(),
|
||||
"CustomizationSpecManager": reflect.TypeOf((*CustomizationSpecManager)(nil)).Elem(),
|
||||
"Datacenter": reflect.TypeOf((*Datacenter)(nil)).Elem(),
|
||||
"Datastore": reflect.TypeOf((*Datastore)(nil)).Elem(),
|
||||
"DistributedVirtualPortgroup": reflect.TypeOf((*DistributedVirtualPortgroup)(nil)).Elem(),
|
||||
"DistributedVirtualSwitch": reflect.TypeOf((*DistributedVirtualSwitch)(nil)).Elem(),
|
||||
"EnvironmentBrowser": reflect.TypeOf((*EnvironmentBrowser)(nil)).Elem(),
|
||||
"EventManager": reflect.TypeOf((*EventManager)(nil)).Elem(),
|
||||
"FileManager": reflect.TypeOf((*FileManager)(nil)).Elem(),
|
||||
"Folder": reflect.TypeOf((*Folder)(nil)).Elem(),
|
||||
"GuestOperationsManager": reflect.TypeOf((*GuestOperationsManager)(nil)).Elem(),
|
||||
"HostDatastoreBrowser": reflect.TypeOf((*HostDatastoreBrowser)(nil)).Elem(),
|
||||
"HostLocalAccountManager": reflect.TypeOf((*HostLocalAccountManager)(nil)).Elem(),
|
||||
"HostNetworkSystem": reflect.TypeOf((*HostNetworkSystem)(nil)).Elem(),
|
||||
"HostSystem": reflect.TypeOf((*HostSystem)(nil)).Elem(),
|
||||
"IpPoolManager": reflect.TypeOf((*IpPoolManager)(nil)).Elem(),
|
||||
"LicenseManager": reflect.TypeOf((*LicenseManager)(nil)).Elem(),
|
||||
"OptionManager": reflect.TypeOf((*OptionManager)(nil)).Elem(),
|
||||
"OvfManager": reflect.TypeOf((*OvfManager)(nil)).Elem(),
|
||||
"PerformanceManager": reflect.TypeOf((*PerformanceManager)(nil)).Elem(),
|
||||
"PropertyCollector": reflect.TypeOf((*PropertyCollector)(nil)).Elem(),
|
||||
"ResourcePool": reflect.TypeOf((*ResourcePool)(nil)).Elem(),
|
||||
"SearchIndex": reflect.TypeOf((*SearchIndex)(nil)).Elem(),
|
||||
"SessionManager": reflect.TypeOf((*SessionManager)(nil)).Elem(),
|
||||
"StoragePod": reflect.TypeOf((*StoragePod)(nil)).Elem(),
|
||||
"StorageResourceManager": reflect.TypeOf((*StorageResourceManager)(nil)).Elem(),
|
||||
"TaskManager": reflect.TypeOf((*TaskManager)(nil)).Elem(),
|
||||
"UserDirectory": reflect.TypeOf((*UserDirectory)(nil)).Elem(),
|
||||
"VcenterVStorageObjectManager": reflect.TypeOf((*VcenterVStorageObjectManager)(nil)).Elem(),
|
||||
"ViewManager": reflect.TypeOf((*ViewManager)(nil)).Elem(),
|
||||
"VirtualApp": reflect.TypeOf((*VirtualApp)(nil)).Elem(),
|
||||
"VirtualDiskManager": reflect.TypeOf((*VirtualDiskManager)(nil)).Elem(),
|
||||
"VirtualMachine": reflect.TypeOf((*VirtualMachine)(nil)).Elem(),
|
||||
"VmwareDistributedVirtualSwitch": reflect.TypeOf((*DistributedVirtualSwitch)(nil)).Elem(),
|
||||
}
|
||||
|
||||
func loadObject(content types.ObjectContent) (mo.Reference, error) {
|
||||
var obj mo.Reference
|
||||
id := content.Obj
|
||||
|
||||
kind, ok := kinds[id.Type]
|
||||
if ok {
|
||||
obj = reflect.New(kind).Interface().(mo.Reference)
|
||||
}
|
||||
|
||||
if obj == nil {
|
||||
// No vcsim wrapper for this type, e.g. IoFilterManager
|
||||
x, err := mo.ObjectContentToType(content, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj = x.(mo.Reference)
|
||||
} else {
|
||||
if len(content.PropSet) == 0 {
|
||||
// via NewServiceInstance()
|
||||
Map.setReference(obj, id)
|
||||
} else {
|
||||
// via Model.Load()
|
||||
dst := getManagedObject(obj).Addr().Interface().(mo.Reference)
|
||||
err := mo.LoadObjectContent([]types.ObjectContent{content}, dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if x, ok := obj.(interface{ init(*Registry) }); ok {
|
||||
x.init(Map)
|
||||
}
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// resolveReferences attempts to resolve any object references that were not included via Load()
|
||||
// example: Load's dir only contains a single OpaqueNetwork, we need to create a Datacenter and
|
||||
// place the OpaqueNetwork in the Datacenter's network folder.
|
||||
func (m *Model) resolveReferences(ctx *Context) error {
|
||||
dc, ok := Map.Any("Datacenter").(*Datacenter)
|
||||
if !ok {
|
||||
// Need to have at least 1 Datacenter
|
||||
root := Map.Get(Map.content().RootFolder).(*Folder)
|
||||
ref := root.CreateDatacenter(internalContext, &types.CreateDatacenter{
|
||||
This: root.Self,
|
||||
Name: "DC0",
|
||||
}).(*methods.CreateDatacenterBody).Res.Returnval
|
||||
dc = Map.Get(ref).(*Datacenter)
|
||||
}
|
||||
|
||||
for ref, val := range Map.objects {
|
||||
me, ok := val.(mo.Entity)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
e := me.Entity()
|
||||
if e.Parent == nil || ref.Type == "Folder" {
|
||||
continue
|
||||
}
|
||||
if Map.Get(*e.Parent) == nil {
|
||||
// object was loaded without its parent, attempt to foster with another parent
|
||||
switch e.Parent.Type {
|
||||
case "Folder":
|
||||
folder := dc.folder(me)
|
||||
e.Parent = &folder.Self
|
||||
log.Printf("%s adopted %s", e.Parent, ref)
|
||||
folderPutChild(ctx, folder, me)
|
||||
default:
|
||||
return fmt.Errorf("unable to foster %s with parent type=%s", ref, e.Parent.Type)
|
||||
}
|
||||
}
|
||||
// TODO: resolve any remaining orphan references via mo.References()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load Model from the given directory, as created by the 'govc object.save' command.
|
||||
func (m *Model) Load(dir string) error {
|
||||
ctx := internalContext
|
||||
var s *ServiceInstance
|
||||
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if filepath.Ext(path) != ".xml" {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
dec := xml.NewDecoder(f)
|
||||
dec.TypeFunc = types.TypeFunc()
|
||||
var content types.ObjectContent
|
||||
err = dec.Decode(&content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if content.Obj == vim25.ServiceInstance {
|
||||
s = new(ServiceInstance)
|
||||
s.Self = content.Obj
|
||||
Map = NewRegistry()
|
||||
Map.Put(s)
|
||||
return mo.LoadObjectContent([]types.ObjectContent{content}, &s.ServiceInstance)
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
s = NewServiceInstance(m.ServiceContent, m.RootFolder)
|
||||
}
|
||||
|
||||
obj, err := loadObject(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Map.Put(obj)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Service = New(s)
|
||||
|
||||
return m.resolveReferences(ctx)
|
||||
}
|
||||
|
||||
// Create populates the Model with the given ModelConfig
|
||||
func (m *Model) Create() error {
|
||||
ctx := internalContext
|
||||
m.Service = New(NewServiceInstance(m.ServiceContent, m.RootFolder))
|
||||
|
||||
client := m.Service.client
|
||||
root := object.NewRootFolder(client)
|
||||
|
||||
// After all hosts are created, this var is used to mount the host datastores.
|
||||
var hosts []*object.HostSystem
|
||||
hostMap := make(map[string][]*object.HostSystem)
|
||||
|
||||
// We need to defer VM creation until after the datastores are created.
|
||||
var vms []func() error
|
||||
// 1 DVS per DC, added to all hosts
|
||||
var dvs *object.DistributedVirtualSwitch
|
||||
// 1 NIC per VM, backed by a DVPG if Model.Portgroup > 0
|
||||
vmnet := esx.EthernetCard.Backing
|
||||
|
||||
// addHost adds a cluster host or a stanalone host.
|
||||
addHost := func(name string, f func(types.HostConnectSpec) (*object.Task, error)) (*object.HostSystem, error) {
|
||||
spec := types.HostConnectSpec{
|
||||
HostName: name,
|
||||
}
|
||||
|
||||
task, err := f(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := task.WaitForResult(context.Background(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host := object.NewHostSystem(client, info.Result.(types.ManagedObjectReference))
|
||||
hosts = append(hosts, host)
|
||||
|
||||
if dvs != nil {
|
||||
config := &types.DVSConfigSpec{
|
||||
Host: []types.DistributedVirtualSwitchHostMemberConfigSpec{{
|
||||
Operation: string(types.ConfigSpecOperationAdd),
|
||||
Host: host.Reference(),
|
||||
}},
|
||||
}
|
||||
|
||||
_, _ = dvs.Reconfigure(ctx, config)
|
||||
}
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
// addMachine returns a func to create a VM.
|
||||
addMachine := func(prefix string, host *object.HostSystem, pool *object.ResourcePool, folders *object.DatacenterFolders) {
|
||||
nic := esx.EthernetCard
|
||||
nic.Backing = vmnet
|
||||
ds := types.ManagedObjectReference{}
|
||||
|
||||
f := func() error {
|
||||
for i := 0; i < m.Machine; i++ {
|
||||
name := m.fmtName(prefix+"_VM", i)
|
||||
|
||||
config := types.VirtualMachineConfigSpec{
|
||||
Name: name,
|
||||
GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
|
||||
Files: &types.VirtualMachineFileInfo{
|
||||
VmPathName: "[LocalDS_0]",
|
||||
},
|
||||
}
|
||||
|
||||
if pool == nil {
|
||||
pool, _ = host.ResourcePool(ctx)
|
||||
}
|
||||
|
||||
var devices object.VirtualDeviceList
|
||||
|
||||
scsi, _ := devices.CreateSCSIController("pvscsi")
|
||||
ide, _ := devices.CreateIDEController()
|
||||
cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController))
|
||||
disk := devices.CreateDisk(scsi.(types.BaseVirtualController), ds,
|
||||
config.Files.VmPathName+" "+path.Join(name, "disk1.vmdk"))
|
||||
disk.CapacityInKB = 1024
|
||||
|
||||
devices = append(devices, scsi, cdrom, disk, &nic)
|
||||
|
||||
config.DeviceChange, _ = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
|
||||
|
||||
task, err := folders.VmFolder.CreateVM(ctx, config, pool, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := task.WaitForResult(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vm := object.NewVirtualMachine(client, info.Result.(types.ManagedObjectReference))
|
||||
|
||||
if m.Autostart {
|
||||
_, _ = vm.PowerOn(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
vms = append(vms, f)
|
||||
}
|
||||
|
||||
nfolder := 0
|
||||
|
||||
for ndc := 0; ndc < m.Datacenter; ndc++ {
|
||||
dcName := m.fmtName("DC", ndc)
|
||||
folder := root
|
||||
fName := m.fmtName("F", nfolder)
|
||||
|
||||
// If Datacenter > Folder, don't create folders for the first N DCs.
|
||||
if nfolder < m.Folder && ndc >= (m.Datacenter-m.Folder) {
|
||||
f, err := folder.CreateFolder(ctx, fName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
folder = f
|
||||
}
|
||||
|
||||
dc, err := folder.CreateDatacenter(ctx, dcName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
folders, err := dc.Folders(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.Pod > 0 {
|
||||
for pod := 0; pod < m.Pod; pod++ {
|
||||
_, _ = folders.DatastoreFolder.CreateStoragePod(ctx, m.fmtName(dcName+"_POD", pod))
|
||||
}
|
||||
}
|
||||
|
||||
if folder != root {
|
||||
// Create sub-folders and use them to create any resources that follow
|
||||
subs := []**object.Folder{&folders.DatastoreFolder, &folders.HostFolder, &folders.NetworkFolder, &folders.VmFolder}
|
||||
|
||||
for _, sub := range subs {
|
||||
f, err := (*sub).CreateFolder(ctx, fName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*sub = f
|
||||
}
|
||||
|
||||
nfolder++
|
||||
}
|
||||
|
||||
if m.Portgroup > 0 || m.PortgroupNSX > 0 {
|
||||
var spec types.DVSCreateSpec
|
||||
spec.ConfigSpec = &types.VMwareDVSConfigSpec{}
|
||||
spec.ConfigSpec.GetDVSConfigSpec().Name = m.fmtName("DVS", 0)
|
||||
|
||||
task, err := folders.NetworkFolder.CreateDVS(ctx, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := task.WaitForResult(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dvs = object.NewDistributedVirtualSwitch(client, info.Result.(types.ManagedObjectReference))
|
||||
}
|
||||
|
||||
for npg := 0; npg < m.Portgroup; npg++ {
|
||||
name := m.fmtName(dcName+"_DVPG", npg)
|
||||
|
||||
task, err := dvs.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{{Name: name}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = task.Wait(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use the 1st DVPG for the VMs eth0 backing
|
||||
if npg == 0 {
|
||||
// AddPortgroup_Task does not return the moid, so we look it up by name
|
||||
net := Map.Get(folders.NetworkFolder.Reference()).(*Folder)
|
||||
pg := Map.FindByName(name, net.ChildEntity)
|
||||
|
||||
vmnet, _ = object.NewDistributedVirtualPortgroup(client, pg.Reference()).EthernetCardBackingInfo(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
for npg := 0; npg < m.PortgroupNSX; npg++ {
|
||||
name := m.fmtName(dcName+"_NSXPG", npg)
|
||||
spec := types.DVPortgroupConfigSpec{
|
||||
Name: name,
|
||||
LogicalSwitchUuid: uuid.New().String(),
|
||||
}
|
||||
|
||||
task, err := dvs.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{spec})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = task.Wait(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Must use simulator methods directly for OpaqueNetwork
|
||||
networkFolder := Map.Get(folders.NetworkFolder.Reference()).(*Folder)
|
||||
|
||||
for i := 0; i < m.OpaqueNetwork; i++ {
|
||||
var summary types.OpaqueNetworkSummary
|
||||
summary.Name = m.fmtName(dcName+"_NSX", i)
|
||||
err := networkFolder.AddOpaqueNetwork(summary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for nhost := 0; nhost < m.Host; nhost++ {
|
||||
name := m.fmtName(dcName+"_H", nhost)
|
||||
|
||||
host, err := addHost(name, func(spec types.HostConnectSpec) (*object.Task, error) {
|
||||
return folders.HostFolder.AddStandaloneHost(ctx, spec, true, nil, nil)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addMachine(name, host, nil, folders)
|
||||
}
|
||||
|
||||
for ncluster := 0; ncluster < m.Cluster; ncluster++ {
|
||||
clusterName := m.fmtName(dcName+"_C", ncluster)
|
||||
|
||||
cluster, err := folders.HostFolder.CreateCluster(ctx, clusterName, types.ClusterConfigSpecEx{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for nhost := 0; nhost < m.ClusterHost; nhost++ {
|
||||
name := m.fmtName(clusterName+"_H", nhost)
|
||||
|
||||
_, err = addHost(name, func(spec types.HostConnectSpec) (*object.Task, error) {
|
||||
return cluster.AddHost(ctx, spec, true, nil, nil)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pool, err := cluster.ResourcePool(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prefix := clusterName + "_RP"
|
||||
|
||||
addMachine(prefix+"0", nil, pool, folders)
|
||||
|
||||
for npool := 1; npool <= m.Pool; npool++ {
|
||||
spec := types.DefaultResourceConfigSpec()
|
||||
|
||||
_, err = pool.Create(ctx, m.fmtName(prefix, npool), spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
prefix = clusterName + "_APP"
|
||||
|
||||
for napp := 0; napp < m.App; napp++ {
|
||||
rspec := types.DefaultResourceConfigSpec()
|
||||
vspec := NewVAppConfigSpec()
|
||||
name := m.fmtName(prefix, napp)
|
||||
|
||||
vapp, err := pool.CreateVApp(ctx, name, rspec, vspec, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addMachine(name, nil, vapp.ResourcePool, folders)
|
||||
}
|
||||
}
|
||||
|
||||
hostMap[dcName] = hosts
|
||||
hosts = nil
|
||||
}
|
||||
|
||||
if m.ServiceContent.RootFolder == esx.RootFolder.Reference() {
|
||||
// ESX model
|
||||
host := object.NewHostSystem(client, esx.HostSystem.Reference())
|
||||
|
||||
dc := object.NewDatacenter(client, esx.Datacenter.Reference())
|
||||
folders, err := dc.Folders(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostMap[dc.Reference().Value] = append(hosts, host)
|
||||
|
||||
addMachine(host.Reference().Value, host, nil, folders)
|
||||
}
|
||||
|
||||
for dc, dchosts := range hostMap {
|
||||
for i := 0; i < m.Datastore; i++ {
|
||||
err := m.createLocalDatastore(dc, m.fmtName("LocalDS_", i), dchosts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, createVM := range vms {
|
||||
err := createVM()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Turn on delay AFTER we're done building the service content
|
||||
m.Service.delay = &m.DelayConfig
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) createLocalDatastore(dc string, name string, hosts []*object.HostSystem) error {
|
||||
ctx := context.Background()
|
||||
dir, err := ioutil.TempDir("", fmt.Sprintf("govcsim-%s-%s-", dc, name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.dirs = append(m.dirs, dir)
|
||||
|
||||
for _, host := range hosts {
|
||||
dss, err := host.ConfigManager().DatastoreSystem(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = dss.CreateLocalDatastore(ctx, name, dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove cleans up items created by the Model, such as local datastore directories
|
||||
func (m *Model) Remove() {
|
||||
// Remove associated vm containers, if any
|
||||
for _, obj := range Map.objects {
|
||||
if vm, ok := obj.(*VirtualMachine); ok {
|
||||
vm.run.remove(vm)
|
||||
}
|
||||
}
|
||||
|
||||
for _, dir := range m.dirs {
|
||||
_ = os.RemoveAll(dir)
|
||||
}
|
||||
}
|
||||
|
||||
// Run calls f with a Client connected to a simulator server instance, which is stopped after f returns.
|
||||
func (m *Model) Run(f func(context.Context, *vim25.Client) error) error {
|
||||
ctx := context.Background()
|
||||
|
||||
defer m.Remove()
|
||||
|
||||
if m.Service == nil {
|
||||
err := m.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.Service.TLS = new(tls.Config)
|
||||
m.Service.RegisterEndpoints = true
|
||||
|
||||
s := m.Service.NewServer()
|
||||
defer s.Close()
|
||||
|
||||
c, err := govmomi.NewClient(ctx, s.URL, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer c.Logout(ctx)
|
||||
|
||||
return f(ctx, c.Client)
|
||||
}
|
||||
|
||||
// Run calls Model.Run for each model and will panic if f returns an error.
|
||||
// If no model is specified, the VPX Model is used by default.
|
||||
func Run(f func(context.Context, *vim25.Client) error, model ...*Model) {
|
||||
m := model
|
||||
if len(m) == 0 {
|
||||
m = []*Model{VPX()}
|
||||
}
|
||||
|
||||
for i := range m {
|
||||
err := m[i].Run(f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test calls Run and expects the caller propagate any errors, via testing.T for example.
|
||||
func Test(f func(context.Context, *vim25.Client), model ...*Model) {
|
||||
Run(func(ctx context.Context, c *vim25.Client) error {
|
||||
f(ctx, c)
|
||||
return nil
|
||||
}, model...)
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"github.com/vmware/govmomi/vim25/xml"
|
||||
)
|
||||
|
||||
func SetCustomValue(ctx *Context, req *types.SetCustomValue) soap.HasFault {
|
||||
body := &methods.SetCustomValueBody{}
|
||||
|
||||
cfm := Map.CustomFieldsManager()
|
||||
|
||||
_, field := cfm.findByNameType(req.Key, req.This.Type)
|
||||
if field == nil {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "key"})
|
||||
return body
|
||||
}
|
||||
|
||||
res := cfm.SetField(ctx, &types.SetField{
|
||||
This: cfm.Reference(),
|
||||
Entity: req.This,
|
||||
Key: field.Key,
|
||||
Value: req.Value,
|
||||
})
|
||||
|
||||
if res.Fault() != nil {
|
||||
body.Fault_ = res.Fault()
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.SetCustomValueResponse{}
|
||||
return body
|
||||
}
|
||||
|
||||
// newUUID returns a stable UUID string based on input s
|
||||
func newUUID(s string) string {
|
||||
return sha1UUID(s).String()
|
||||
}
|
||||
|
||||
// sha1UUID returns a stable UUID based on input s
|
||||
func sha1UUID(s string) uuid.UUID {
|
||||
return uuid.NewSHA1(uuid.NameSpaceOID, []byte(s))
|
||||
}
|
||||
|
||||
// deepCopy uses xml encode/decode to copy src to dst
|
||||
func deepCopy(src, dst interface{}) {
|
||||
b, err := xml.Marshal(src)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
dec := xml.NewDecoder(bytes.NewReader(b))
|
||||
dec.TypeFunc = types.TypeFunc()
|
||||
err = dec.Decode(dst)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/simulator/vpx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type OptionManager struct {
|
||||
mo.OptionManager
|
||||
}
|
||||
|
||||
func NewOptionManager(ref *types.ManagedObjectReference, setting []types.BaseOptionValue) object.Reference {
|
||||
s := &OptionManager{}
|
||||
if ref != nil {
|
||||
s.Self = *ref
|
||||
}
|
||||
s.Setting = setting
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *OptionManager) init(r *Registry) {
|
||||
if len(m.Setting) == 0 {
|
||||
if r.IsVPX() {
|
||||
m.Setting = vpx.Setting
|
||||
} else {
|
||||
m.Setting = esx.Setting
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *OptionManager) QueryOptions(req *types.QueryOptions) soap.HasFault {
|
||||
body := &methods.QueryOptionsBody{}
|
||||
res := &types.QueryOptionsResponse{}
|
||||
|
||||
for _, opt := range m.Setting {
|
||||
if strings.HasPrefix(opt.GetOptionValue().Key, req.Name) {
|
||||
res.Returnval = append(res.Returnval, opt)
|
||||
}
|
||||
}
|
||||
|
||||
if len(res.Returnval) == 0 {
|
||||
body.Fault_ = Fault("", &types.InvalidName{Name: req.Name})
|
||||
} else {
|
||||
body.Res = res
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *OptionManager) find(key string) *types.OptionValue {
|
||||
for _, opt := range m.Setting {
|
||||
setting := opt.GetOptionValue()
|
||||
if setting.Key == key {
|
||||
return setting
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *OptionManager) UpdateOptions(req *types.UpdateOptions) soap.HasFault {
|
||||
body := new(methods.UpdateOptionsBody)
|
||||
|
||||
for _, change := range req.ChangedValue {
|
||||
setting := change.GetOptionValue()
|
||||
|
||||
// We don't currently include the entire list of default settings for ESX and vCenter,
|
||||
// this prefix is currently used to test the failure path.
|
||||
// Real vCenter seems to only allow new options if Key has a "config." prefix.
|
||||
// TODO: consider behaving the same, which would require including 2 long lists of options in vpx.Setting and esx.Setting
|
||||
if strings.HasPrefix(setting.Key, "ENOENT.") {
|
||||
body.Fault_ = Fault("", &types.InvalidName{Name: setting.Key})
|
||||
return body
|
||||
}
|
||||
|
||||
opt := m.find(setting.Key)
|
||||
if opt != nil {
|
||||
// This is an existing option.
|
||||
opt.Value = setting.Value
|
||||
continue
|
||||
}
|
||||
|
||||
m.Setting = append(m.Setting, change)
|
||||
}
|
||||
|
||||
body.Res = new(types.UpdateOptionsResponse)
|
||||
return body
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
//+build !windows
|
||||
|
||||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import "syscall"
|
||||
|
||||
func (ds *Datastore) stat() error {
|
||||
info := ds.Info.GetDatastoreInfo()
|
||||
var stat syscall.Statfs_t
|
||||
|
||||
err := syscall.Statfs(info.Url, &stat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info.FreeSpace = int64(stat.Bfree * uint64(stat.Bsize))
|
||||
|
||||
ds.Summary.FreeSpace = info.FreeSpace
|
||||
ds.Summary.Capacity = int64(stat.Blocks * uint64(stat.Bsize))
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import "os"
|
||||
|
||||
func (ds *Datastore) stat() error {
|
||||
info := ds.Info.GetDatastoreInfo()
|
||||
|
||||
_, err := os.Stat(info.Url)
|
||||
return err
|
||||
}
|
|
@ -1,292 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2019 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/ovf"
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type OvfManager struct {
|
||||
mo.OvfManager
|
||||
}
|
||||
|
||||
func ovfDisk(e *ovf.Envelope, diskID string) *ovf.VirtualDiskDesc {
|
||||
for _, disk := range e.Disk.Disks {
|
||||
if strings.HasSuffix(diskID, disk.DiskID) {
|
||||
return &disk
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ovfNetwork(ctx *Context, req *types.CreateImportSpec, item ovf.ResourceAllocationSettingData) types.BaseVirtualDeviceBackingInfo {
|
||||
if len(item.Connection) == 0 {
|
||||
return nil
|
||||
}
|
||||
pool := ctx.Map.Get(req.ResourcePool).(mo.Entity)
|
||||
ref := ctx.Map.getEntityDatacenter(pool).defaultNetwork()[0] // Default to VM Network
|
||||
c := item.Connection[0]
|
||||
|
||||
for _, net := range req.Cisp.NetworkMapping {
|
||||
if net.Name == c {
|
||||
ref = net.Network
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch obj := ctx.Map.Get(ref).(type) {
|
||||
case *mo.Network:
|
||||
return &types.VirtualEthernetCardNetworkBackingInfo{
|
||||
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
|
||||
DeviceName: obj.Name,
|
||||
},
|
||||
}
|
||||
case *DistributedVirtualPortgroup:
|
||||
dvs := ctx.Map.Get(*obj.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
|
||||
return &types.VirtualEthernetCardDistributedVirtualPortBackingInfo{
|
||||
Port: types.DistributedVirtualSwitchPortConnection{
|
||||
PortgroupKey: obj.Key,
|
||||
SwitchUuid: dvs.Config.GetDVSConfigInfo().Uuid,
|
||||
},
|
||||
}
|
||||
default:
|
||||
log.Printf("ovf: unknown network type: %T", ref)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ovfDiskCapacity(disk *ovf.VirtualDiskDesc) int64 {
|
||||
b, _ := strconv.ParseUint(disk.Capacity, 10, 64)
|
||||
if disk.CapacityAllocationUnits == nil {
|
||||
return int64(b)
|
||||
}
|
||||
c := strings.Fields(*disk.CapacityAllocationUnits)
|
||||
if len(c) == 3 && c[0] == "byte" && c[1] == "*" { // "byte * 2^20"
|
||||
p := strings.Split(c[2], "^")
|
||||
x, _ := strconv.ParseUint(p[0], 10, 64)
|
||||
if len(p) == 2 {
|
||||
y, _ := strconv.ParseUint(p[1], 10, 64)
|
||||
b *= uint64(math.Pow(float64(x), float64(y)))
|
||||
} else {
|
||||
b *= x
|
||||
}
|
||||
}
|
||||
return int64(b / 1024)
|
||||
}
|
||||
|
||||
func (m *OvfManager) CreateImportSpec(ctx *Context, req *types.CreateImportSpec) soap.HasFault {
|
||||
body := new(methods.CreateImportSpecBody)
|
||||
|
||||
env, err := ovf.Unmarshal(strings.NewReader(req.OvfDescriptor))
|
||||
if err != nil {
|
||||
body.Fault_ = Fault(err.Error(), &types.InvalidArgument{InvalidProperty: "ovfDescriptor"})
|
||||
return body
|
||||
}
|
||||
|
||||
ds := ctx.Map.Get(req.Datastore).(*Datastore)
|
||||
path := object.DatastorePath{Datastore: ds.Name}
|
||||
spec := &types.VirtualMachineImportSpec{
|
||||
ConfigSpec: types.VirtualMachineConfigSpec{
|
||||
Name: req.Cisp.EntityName,
|
||||
Version: esx.HardwareVersion,
|
||||
GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
|
||||
Files: &types.VirtualMachineFileInfo{
|
||||
VmPathName: path.String(),
|
||||
},
|
||||
NumCPUs: 1,
|
||||
NumCoresPerSocket: 1,
|
||||
MemoryMB: 32,
|
||||
},
|
||||
ResPoolEntity: &req.ResourcePool,
|
||||
}
|
||||
|
||||
if req.Cisp.DeploymentOption == "" && env.DeploymentOption != nil {
|
||||
for _, c := range env.DeploymentOption.Configuration {
|
||||
if isTrue(c.Default) {
|
||||
req.Cisp.DeploymentOption = c.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if os := env.VirtualSystem.OperatingSystem; len(os) != 0 {
|
||||
if id := os[0].OSType; id != nil {
|
||||
spec.ConfigSpec.GuestId = *id
|
||||
}
|
||||
}
|
||||
|
||||
var device object.VirtualDeviceList
|
||||
result := types.OvfCreateImportSpecResult{
|
||||
ImportSpec: spec,
|
||||
}
|
||||
|
||||
hw := env.VirtualSystem.VirtualHardware[0]
|
||||
if vmx := hw.System.VirtualSystemType; vmx != nil {
|
||||
spec.ConfigSpec.Version = *vmx
|
||||
}
|
||||
|
||||
ndisk := 0
|
||||
ndev := 0
|
||||
resources := make(map[string]types.BaseVirtualDevice)
|
||||
|
||||
for _, item := range hw.Item {
|
||||
if req.Cisp.DeploymentOption != "" && item.Configuration != nil {
|
||||
if req.Cisp.DeploymentOption != *item.Configuration {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
kind := func() string {
|
||||
if item.ResourceSubType == nil {
|
||||
return "unknown"
|
||||
}
|
||||
return strings.ToLower(*item.ResourceSubType)
|
||||
}
|
||||
|
||||
unsupported := func(err error) {
|
||||
result.Error = append(result.Error, types.LocalizedMethodFault{
|
||||
Fault: &types.OvfUnsupportedType{
|
||||
Name: item.ElementName,
|
||||
InstanceId: item.InstanceID,
|
||||
DeviceType: int32(*item.ResourceType),
|
||||
},
|
||||
LocalizedMessage: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
upload := func(file ovf.File, c types.BaseVirtualDevice, n int) {
|
||||
result.FileItem = append(result.FileItem, types.OvfFileItem{
|
||||
DeviceId: fmt.Sprintf("/%s/%s:%d", req.Cisp.EntityName, device.Type(c), n),
|
||||
Path: file.Href,
|
||||
Size: int64(file.Size),
|
||||
CimType: int32(*item.ResourceType),
|
||||
})
|
||||
}
|
||||
|
||||
switch *item.ResourceType {
|
||||
case 1: // VMCI
|
||||
case 3: // Number of Virtual CPUs
|
||||
spec.ConfigSpec.NumCPUs = int32(*item.VirtualQuantity)
|
||||
case 4: // Memory Size
|
||||
spec.ConfigSpec.MemoryMB = int64(*item.VirtualQuantity)
|
||||
case 5: // IDE Controller
|
||||
d, _ := device.CreateIDEController()
|
||||
device = append(device, d)
|
||||
resources[item.InstanceID] = d
|
||||
case 6: // SCSI Controller
|
||||
d, err := device.CreateSCSIController(kind())
|
||||
if err == nil {
|
||||
device = append(device, d)
|
||||
resources[item.InstanceID] = d
|
||||
} else {
|
||||
unsupported(err)
|
||||
}
|
||||
case 10: // Virtual Network
|
||||
net := ovfNetwork(ctx, req, item)
|
||||
if net != nil {
|
||||
d, err := device.CreateEthernetCard(kind(), net)
|
||||
if err == nil {
|
||||
device = append(device, d)
|
||||
} else {
|
||||
unsupported(err)
|
||||
}
|
||||
}
|
||||
case 14: // Floppy Drive
|
||||
if device.PickController((*types.VirtualSIOController)(nil)) == nil {
|
||||
c := &types.VirtualSIOController{}
|
||||
c.Key = device.NewKey()
|
||||
device = append(device, c)
|
||||
}
|
||||
d, err := device.CreateFloppy()
|
||||
if err == nil {
|
||||
device = append(device, d)
|
||||
resources[item.InstanceID] = d
|
||||
} else {
|
||||
unsupported(err)
|
||||
}
|
||||
case 15: // CD/DVD
|
||||
c, ok := resources[*item.Parent]
|
||||
if !ok {
|
||||
continue // Parent is unsupported()
|
||||
}
|
||||
d, _ := device.CreateCdrom(c.(*types.VirtualIDEController))
|
||||
if len(item.HostResource) != 0 {
|
||||
for _, file := range env.References {
|
||||
if strings.HasSuffix(item.HostResource[0], file.ID) {
|
||||
path.Path = fmt.Sprintf("%s/_deviceImage%d.iso", req.Cisp.EntityName, ndev)
|
||||
device.InsertIso(d, path.String())
|
||||
upload(file, d, ndev)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
device = append(device, d)
|
||||
ndev++
|
||||
case 17: // Virtual Disk
|
||||
c, ok := resources[*item.Parent]
|
||||
if !ok {
|
||||
continue // Parent is unsupported()
|
||||
}
|
||||
path.Path = fmt.Sprintf("%s/disk-%d.vmdk", req.Cisp.EntityName, ndisk)
|
||||
d := device.CreateDisk(c.(types.BaseVirtualController), ds.Reference(), path.String())
|
||||
d.VirtualDevice.DeviceInfo = &types.Description{
|
||||
Label: item.ElementName,
|
||||
}
|
||||
disk := ovfDisk(env, item.HostResource[0])
|
||||
for _, file := range env.References {
|
||||
if file.ID == *disk.FileRef {
|
||||
upload(file, d, ndisk)
|
||||
break
|
||||
}
|
||||
}
|
||||
d.CapacityInKB = ovfDiskCapacity(disk)
|
||||
device = append(device, d)
|
||||
ndisk++
|
||||
case 23: // USB Controller
|
||||
case 24: // Video Card
|
||||
default:
|
||||
unsupported(fmt.Errorf("unsupported resource type: %d", *item.ResourceType))
|
||||
}
|
||||
}
|
||||
|
||||
spec.ConfigSpec.DeviceChange, _ = device.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
|
||||
|
||||
for _, p := range req.Cisp.PropertyMapping {
|
||||
spec.ConfigSpec.ExtraConfig = append(spec.ConfigSpec.ExtraConfig, &types.OptionValue{
|
||||
Key: p.Key,
|
||||
Value: p.Value,
|
||||
})
|
||||
}
|
||||
|
||||
body.Res = &types.CreateImportSpecResponse{
|
||||
Returnval: result,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/simulator/vpx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var realtimeProviderSummary = types.PerfProviderSummary{
|
||||
CurrentSupported: true,
|
||||
SummarySupported: true,
|
||||
RefreshRate: 20,
|
||||
}
|
||||
|
||||
var historicProviderSummary = types.PerfProviderSummary{
|
||||
CurrentSupported: false,
|
||||
SummarySupported: true,
|
||||
RefreshRate: -1,
|
||||
}
|
||||
|
||||
type PerformanceManager struct {
|
||||
mo.PerformanceManager
|
||||
vmMetrics []types.PerfMetricId
|
||||
hostMetrics []types.PerfMetricId
|
||||
rpMetrics []types.PerfMetricId
|
||||
clusterMetrics []types.PerfMetricId
|
||||
datastoreMetrics []types.PerfMetricId
|
||||
datacenterMetrics []types.PerfMetricId
|
||||
perfCounterIndex map[int32]types.PerfCounterInfo
|
||||
metricData map[string]map[int32][]int64
|
||||
}
|
||||
|
||||
func (m *PerformanceManager) init(r *Registry) {
|
||||
if r.IsESX() {
|
||||
m.PerfCounter = esx.PerfCounter
|
||||
m.hostMetrics = esx.HostMetrics
|
||||
m.vmMetrics = esx.VmMetrics
|
||||
m.rpMetrics = esx.ResourcePoolMetrics
|
||||
m.metricData = esx.MetricData
|
||||
} else {
|
||||
m.PerfCounter = vpx.PerfCounter
|
||||
m.hostMetrics = vpx.HostMetrics
|
||||
m.vmMetrics = vpx.VmMetrics
|
||||
m.rpMetrics = vpx.ResourcePoolMetrics
|
||||
m.clusterMetrics = vpx.ClusterMetrics
|
||||
m.datastoreMetrics = vpx.DatastoreMetrics
|
||||
m.datacenterMetrics = vpx.DatacenterMetrics
|
||||
m.metricData = vpx.MetricData
|
||||
}
|
||||
m.perfCounterIndex = make(map[int32]types.PerfCounterInfo, len(m.PerfCounter))
|
||||
for _, p := range m.PerfCounter {
|
||||
m.perfCounterIndex[p.Key] = p
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PerformanceManager) QueryPerfCounter(ctx *Context, req *types.QueryPerfCounter) soap.HasFault {
|
||||
body := new(methods.QueryPerfCounterBody)
|
||||
body.Req = req
|
||||
body.Res.Returnval = make([]types.PerfCounterInfo, len(req.CounterId))
|
||||
for i, id := range req.CounterId {
|
||||
if info, ok := p.perfCounterIndex[id]; !ok {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{
|
||||
InvalidProperty: "CounterId",
|
||||
})
|
||||
return body
|
||||
} else {
|
||||
body.Res.Returnval[i] = info
|
||||
}
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func (p *PerformanceManager) QueryPerfProviderSummary(ctx *Context, req *types.QueryPerfProviderSummary) soap.HasFault {
|
||||
body := new(methods.QueryPerfProviderSummaryBody)
|
||||
body.Req = req
|
||||
body.Res = new(types.QueryPerfProviderSummaryResponse)
|
||||
|
||||
// The entity must exist
|
||||
if Map.Get(req.Entity) == nil {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{
|
||||
InvalidProperty: "Entity",
|
||||
})
|
||||
return body
|
||||
}
|
||||
|
||||
switch req.Entity.Type {
|
||||
case "VirtualMachine", "HostSystem", "ResourcePool":
|
||||
body.Res.Returnval = realtimeProviderSummary
|
||||
default:
|
||||
body.Res.Returnval = historicProviderSummary
|
||||
}
|
||||
body.Res.Returnval.Entity = req.Entity
|
||||
return body
|
||||
}
|
||||
|
||||
func (p *PerformanceManager) buildAvailablePerfMetricsQueryResponse(ids []types.PerfMetricId, numCPU int, datastoreURL string) *types.QueryAvailablePerfMetricResponse {
|
||||
r := new(types.QueryAvailablePerfMetricResponse)
|
||||
r.Returnval = make([]types.PerfMetricId, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
switch id.Instance {
|
||||
case "$cpu":
|
||||
for i := 0; i < numCPU; i++ {
|
||||
r.Returnval = append(r.Returnval, types.PerfMetricId{CounterId: id.CounterId, Instance: strconv.Itoa(i)})
|
||||
}
|
||||
case "$physDisk":
|
||||
r.Returnval = append(r.Returnval, types.PerfMetricId{CounterId: id.CounterId, Instance: datastoreURL})
|
||||
case "$file":
|
||||
r.Returnval = append(r.Returnval, types.PerfMetricId{CounterId: id.CounterId, Instance: "DISKFILE"})
|
||||
r.Returnval = append(r.Returnval, types.PerfMetricId{CounterId: id.CounterId, Instance: "DELTAFILE"})
|
||||
r.Returnval = append(r.Returnval, types.PerfMetricId{CounterId: id.CounterId, Instance: "SWAPFILE"})
|
||||
r.Returnval = append(r.Returnval, types.PerfMetricId{CounterId: id.CounterId, Instance: "OTHERFILE"})
|
||||
default:
|
||||
r.Returnval = append(r.Returnval, types.PerfMetricId{CounterId: id.CounterId, Instance: id.Instance})
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *PerformanceManager) queryAvailablePerfMetric(entity types.ManagedObjectReference, interval int32) *types.QueryAvailablePerfMetricResponse {
|
||||
switch entity.Type {
|
||||
case "VirtualMachine":
|
||||
vm := Map.Get(entity).(*VirtualMachine)
|
||||
return p.buildAvailablePerfMetricsQueryResponse(p.vmMetrics, int(vm.Summary.Config.NumCpu), vm.Datastore[0].Value)
|
||||
case "HostSystem":
|
||||
host := Map.Get(entity).(*HostSystem)
|
||||
return p.buildAvailablePerfMetricsQueryResponse(p.hostMetrics, int(host.Hardware.CpuInfo.NumCpuThreads), host.Datastore[0].Value)
|
||||
case "ResourcePool":
|
||||
return p.buildAvailablePerfMetricsQueryResponse(p.rpMetrics, 0, "")
|
||||
case "ClusterComputeResource":
|
||||
if interval != 20 {
|
||||
return p.buildAvailablePerfMetricsQueryResponse(p.clusterMetrics, 0, "")
|
||||
}
|
||||
case "Datastore":
|
||||
if interval != 20 {
|
||||
return p.buildAvailablePerfMetricsQueryResponse(p.datastoreMetrics, 0, "")
|
||||
}
|
||||
case "Datacenter":
|
||||
if interval != 20 {
|
||||
return p.buildAvailablePerfMetricsQueryResponse(p.datacenterMetrics, 0, "")
|
||||
}
|
||||
}
|
||||
|
||||
// Don't know how to handle this. Return empty response.
|
||||
return new(types.QueryAvailablePerfMetricResponse)
|
||||
}
|
||||
|
||||
func (p *PerformanceManager) QueryAvailablePerfMetric(ctx *Context, req *types.QueryAvailablePerfMetric) soap.HasFault {
|
||||
body := new(methods.QueryAvailablePerfMetricBody)
|
||||
body.Req = req
|
||||
body.Res = p.queryAvailablePerfMetric(req.Entity, req.IntervalId)
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (p *PerformanceManager) QueryPerf(ctx *Context, req *types.QueryPerf) soap.HasFault {
|
||||
body := new(methods.QueryPerfBody)
|
||||
body.Req = req
|
||||
body.Res = new(types.QueryPerfResponse)
|
||||
body.Res.Returnval = make([]types.BasePerfEntityMetricBase, len(req.QuerySpec))
|
||||
|
||||
for i, qs := range req.QuerySpec {
|
||||
metrics := new(types.PerfEntityMetric)
|
||||
metrics.Entity = qs.Entity
|
||||
|
||||
// Get metric data for this entity type
|
||||
metricData, ok := p.metricData[qs.Entity.Type]
|
||||
if !ok {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{
|
||||
InvalidProperty: "Entity",
|
||||
})
|
||||
}
|
||||
var start, end time.Time
|
||||
if qs.StartTime == nil {
|
||||
start = time.Now().Add(time.Duration(-365*24) * time.Hour) // Assume we have data for a year
|
||||
} else {
|
||||
start = *qs.StartTime
|
||||
}
|
||||
if qs.EndTime == nil {
|
||||
end = time.Now()
|
||||
} else {
|
||||
end = *qs.EndTime
|
||||
}
|
||||
|
||||
// Generate metric series. Divide into n buckets of interval seconds
|
||||
interval := qs.IntervalId
|
||||
if interval == -1 || interval == 0 {
|
||||
interval = 20 // TODO: Determine from entity type
|
||||
}
|
||||
n := 1 + int32(end.Sub(start).Seconds())/interval
|
||||
if n > qs.MaxSample {
|
||||
n = qs.MaxSample
|
||||
}
|
||||
|
||||
// Loop through each interval "tick"
|
||||
metrics.SampleInfo = make([]types.PerfSampleInfo, n)
|
||||
metrics.Value = make([]types.BasePerfMetricSeries, len(qs.MetricId))
|
||||
for tick := int32(0); tick < n; tick++ {
|
||||
metrics.SampleInfo[tick] = types.PerfSampleInfo{Timestamp: end.Add(time.Duration(-interval*tick) * time.Second), Interval: interval}
|
||||
}
|
||||
|
||||
for j, mid := range qs.MetricId {
|
||||
// Create list of metrics for this tick
|
||||
series := &types.PerfMetricIntSeries{Value: make([]int64, n)}
|
||||
series.Id = mid
|
||||
points := metricData[mid.CounterId]
|
||||
offset := int64(start.Unix()) / int64(interval)
|
||||
|
||||
for tick := int32(0); tick < n; tick++ {
|
||||
var p int64
|
||||
|
||||
// Use sample data if we have it. Otherwise, just send 0.
|
||||
if len(points) > 0 {
|
||||
p = points[(offset+int64(tick))%int64(len(points))]
|
||||
scale := p / 5
|
||||
if scale > 0 {
|
||||
// Add some gaussian noise to make the data look more "real"
|
||||
p += int64(rand.NormFloat64() * float64(scale))
|
||||
if p < 0 {
|
||||
p = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p = 0
|
||||
}
|
||||
series.Value[tick] = p
|
||||
}
|
||||
metrics.Value[j] = series
|
||||
}
|
||||
body.Res.Returnval[i] = metrics
|
||||
}
|
||||
return body
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type DistributedVirtualPortgroup struct {
|
||||
mo.DistributedVirtualPortgroup
|
||||
}
|
||||
|
||||
func (s *DistributedVirtualPortgroup) ReconfigureDVPortgroupTask(req *types.ReconfigureDVPortgroup_Task) soap.HasFault {
|
||||
task := CreateTask(s, "reconfigureDvPortgroup", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
s.Config.DefaultPortConfig = req.Spec.DefaultPortConfig
|
||||
s.Config.NumPorts = req.Spec.NumPorts
|
||||
s.Config.AutoExpand = req.Spec.AutoExpand
|
||||
s.Config.Type = req.Spec.Type
|
||||
s.Config.Description = req.Spec.Description
|
||||
s.Config.DynamicData = req.Spec.DynamicData
|
||||
s.Config.Name = req.Spec.Name
|
||||
s.Config.Policy = req.Spec.Policy
|
||||
s.Config.PortNameFormat = req.Spec.PortNameFormat
|
||||
s.Config.VmVnicNetworkResourcePoolKey = req.Spec.VmVnicNetworkResourcePoolKey
|
||||
s.Config.LogicalSwitchUuid = req.Spec.LogicalSwitchUuid
|
||||
s.Config.BackingType = req.Spec.BackingType
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.ReconfigureDVPortgroup_TaskBody{
|
||||
Res: &types.ReconfigureDVPortgroup_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DistributedVirtualPortgroup) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
|
||||
task := CreateTask(s, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
vswitch := Map.Get(*s.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
|
||||
Map.RemoveReference(vswitch, &vswitch.Portgroup, s.Reference())
|
||||
Map.removeString(vswitch, &vswitch.Summary.PortgroupName, s.Name)
|
||||
|
||||
f := Map.getEntityParent(vswitch, "Folder").(*Folder)
|
||||
folderRemoveChild(ctx, &f.Folder, s.Reference())
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.Destroy_TaskBody{
|
||||
Res: &types.Destroy_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
|
||||
}
|
|
@ -1,841 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/simulator/internal"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type PropertyCollector struct {
|
||||
mo.PropertyCollector
|
||||
|
||||
nopLocker
|
||||
updates []types.ObjectUpdate
|
||||
mu sync.Mutex
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewPropertyCollector(ref types.ManagedObjectReference) object.Reference {
|
||||
s := &PropertyCollector{}
|
||||
s.Self = ref
|
||||
return s
|
||||
}
|
||||
|
||||
var errMissingField = errors.New("missing field")
|
||||
var errEmptyField = errors.New("empty field")
|
||||
|
||||
func getObject(ctx *Context, ref types.ManagedObjectReference) (reflect.Value, bool) {
|
||||
var obj mo.Reference
|
||||
if ctx.Session == nil {
|
||||
// Even without permissions to access an object or specific fields, RetrieveProperties
|
||||
// returns an ObjectContent response as long as the object exists. See retrieveResult.add()
|
||||
obj = Map.Get(ref)
|
||||
} else {
|
||||
obj = ctx.Session.Get(ref)
|
||||
}
|
||||
|
||||
if obj == nil {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
if ctx.Session == nil && ref.Type == "SessionManager" {
|
||||
// RetrieveProperties on SessionManager without a session always returns empty,
|
||||
// rather than MissingSet + Fault.NotAuthenticated for each field.
|
||||
obj = &mo.SessionManager{Self: ref}
|
||||
}
|
||||
|
||||
// For objects that use internal types that differ from that of the vim25/mo field types.
|
||||
// See EventHistoryCollector for example.
|
||||
type get interface {
|
||||
Get() mo.Reference
|
||||
}
|
||||
if o, ok := obj.(get); ok {
|
||||
obj = o.Get()
|
||||
}
|
||||
|
||||
return getManagedObject(obj), true
|
||||
}
|
||||
|
||||
func getManagedObject(obj mo.Reference) reflect.Value {
|
||||
rval := reflect.ValueOf(obj).Elem()
|
||||
rtype := rval.Type()
|
||||
|
||||
// PropertyCollector is for Managed Object types only (package mo).
|
||||
// If the registry object is not in the mo package, assume it is a wrapper
|
||||
// type where the first field is an embedded mo type.
|
||||
// We need to dig out the mo type for PropSet.All to work properly and
|
||||
// for the case where the type has a field of the same name, for example:
|
||||
// mo.ResourcePool.ResourcePool
|
||||
for {
|
||||
if path.Base(rtype.PkgPath()) == "mo" {
|
||||
break
|
||||
}
|
||||
if rtype.Kind() != reflect.Struct || rtype.NumField() == 0 {
|
||||
log.Panicf("%#v does not have an embedded mo type", obj.Reference())
|
||||
}
|
||||
rval = rval.Field(0)
|
||||
rtype = rval.Type()
|
||||
}
|
||||
|
||||
return rval
|
||||
}
|
||||
|
||||
// wrapValue converts slice types to the appropriate ArrayOf type used in property collector responses.
|
||||
func wrapValue(rval reflect.Value, rtype reflect.Type) interface{} {
|
||||
pval := rval.Interface()
|
||||
|
||||
if rval.Kind() == reflect.Slice {
|
||||
// Convert slice to types.ArrayOf*
|
||||
switch v := pval.(type) {
|
||||
case []string:
|
||||
pval = &types.ArrayOfString{
|
||||
String: v,
|
||||
}
|
||||
case []uint8:
|
||||
pval = &types.ArrayOfByte{
|
||||
Byte: v,
|
||||
}
|
||||
case []int16:
|
||||
pval = &types.ArrayOfShort{
|
||||
Short: v,
|
||||
}
|
||||
case []int32:
|
||||
pval = &types.ArrayOfInt{
|
||||
Int: v,
|
||||
}
|
||||
case []int64:
|
||||
pval = &types.ArrayOfLong{
|
||||
Long: v,
|
||||
}
|
||||
default:
|
||||
kind := rtype.Elem().Name()
|
||||
// Remove govmomi interface prefix name
|
||||
kind = strings.TrimPrefix(kind, "Base")
|
||||
akind, _ := defaultMapType("ArrayOf" + kind)
|
||||
a := reflect.New(akind)
|
||||
a.Elem().FieldByName(kind).Set(rval)
|
||||
pval = a.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return pval
|
||||
}
|
||||
|
||||
func fieldValueInterface(f reflect.StructField, rval reflect.Value) interface{} {
|
||||
if rval.Kind() == reflect.Ptr {
|
||||
rval = rval.Elem()
|
||||
}
|
||||
|
||||
return wrapValue(rval, f.Type)
|
||||
}
|
||||
|
||||
func fieldValue(rval reflect.Value, p string) (interface{}, error) {
|
||||
var value interface{}
|
||||
fields := strings.Split(p, ".")
|
||||
|
||||
for i, name := range fields {
|
||||
kind := rval.Type().Kind()
|
||||
|
||||
if kind == reflect.Interface {
|
||||
if rval.IsNil() {
|
||||
continue
|
||||
}
|
||||
rval = rval.Elem()
|
||||
kind = rval.Type().Kind()
|
||||
}
|
||||
|
||||
if kind == reflect.Ptr {
|
||||
if rval.IsNil() {
|
||||
continue
|
||||
}
|
||||
rval = rval.Elem()
|
||||
}
|
||||
|
||||
x := ucFirst(name)
|
||||
val := rval.FieldByName(x)
|
||||
if !val.IsValid() {
|
||||
return nil, errMissingField
|
||||
}
|
||||
|
||||
if isEmpty(val) {
|
||||
return nil, errEmptyField
|
||||
}
|
||||
|
||||
if i == len(fields)-1 {
|
||||
ftype, _ := rval.Type().FieldByName(x)
|
||||
value = fieldValueInterface(ftype, val)
|
||||
break
|
||||
}
|
||||
|
||||
rval = val
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func fieldRefs(f interface{}) []types.ManagedObjectReference {
|
||||
switch fv := f.(type) {
|
||||
case types.ManagedObjectReference:
|
||||
return []types.ManagedObjectReference{fv}
|
||||
case *types.ArrayOfManagedObjectReference:
|
||||
return fv.ManagedObjectReference
|
||||
case nil:
|
||||
// empty field
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isEmpty(rval reflect.Value) bool {
|
||||
switch rval.Kind() {
|
||||
case reflect.Ptr:
|
||||
return rval.IsNil()
|
||||
case reflect.String:
|
||||
return rval.Len() == 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isTrue(v *bool) bool {
|
||||
return v != nil && *v
|
||||
}
|
||||
|
||||
func isFalse(v *bool) bool {
|
||||
return v == nil || !*v
|
||||
}
|
||||
|
||||
func lcFirst(s string) string {
|
||||
return strings.ToLower(s[:1]) + s[1:]
|
||||
}
|
||||
|
||||
func ucFirst(s string) string {
|
||||
return strings.ToUpper(s[:1]) + s[1:]
|
||||
}
|
||||
|
||||
type retrieveResult struct {
|
||||
*types.RetrieveResult
|
||||
req *types.RetrievePropertiesEx
|
||||
collected map[types.ManagedObjectReference]bool
|
||||
specs map[string]*types.TraversalSpec
|
||||
}
|
||||
|
||||
func (rr *retrieveResult) add(ctx *Context, name string, val types.AnyType, content *types.ObjectContent) {
|
||||
if ctx.Session != nil {
|
||||
content.PropSet = append(content.PropSet, types.DynamicProperty{
|
||||
Name: name,
|
||||
Val: val,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
content.MissingSet = append(content.MissingSet, types.MissingProperty{
|
||||
Path: name,
|
||||
Fault: types.LocalizedMethodFault{Fault: &types.NotAuthenticated{
|
||||
NoPermission: types.NoPermission{
|
||||
Object: content.Obj,
|
||||
PrivilegeId: "System.Read",
|
||||
}},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (rr *retrieveResult) collectAll(ctx *Context, rval reflect.Value, rtype reflect.Type, content *types.ObjectContent) {
|
||||
for i := 0; i < rval.NumField(); i++ {
|
||||
val := rval.Field(i)
|
||||
|
||||
f := rtype.Field(i)
|
||||
|
||||
if isEmpty(val) || f.Name == "Self" {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Anonymous {
|
||||
// recurse into embedded field
|
||||
rr.collectAll(ctx, val, f.Type, content)
|
||||
continue
|
||||
}
|
||||
|
||||
rr.add(ctx, lcFirst(f.Name), fieldValueInterface(f, val), content)
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *retrieveResult) collectFields(ctx *Context, rval reflect.Value, fields []string, content *types.ObjectContent) {
|
||||
seen := make(map[string]bool)
|
||||
|
||||
for i := range content.PropSet {
|
||||
seen[content.PropSet[i].Name] = true // mark any already collected via embedded field
|
||||
}
|
||||
|
||||
for _, name := range fields {
|
||||
if seen[name] {
|
||||
// rvc 'ls' includes the "name" property twice, then fails with no error message or stack trace
|
||||
// in RbVmomi::VIM::ObjectContent.to_hash_uncached when it sees the 2nd "name" property.
|
||||
continue
|
||||
}
|
||||
seen[name] = true
|
||||
|
||||
val, err := fieldValue(rval, name)
|
||||
|
||||
switch err {
|
||||
case nil, errEmptyField:
|
||||
rr.add(ctx, name, val, content)
|
||||
case errMissingField:
|
||||
content.MissingSet = append(content.MissingSet, types.MissingProperty{
|
||||
Path: name,
|
||||
Fault: types.LocalizedMethodFault{Fault: &types.InvalidProperty{
|
||||
Name: name,
|
||||
}},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *retrieveResult) collect(ctx *Context, ref types.ManagedObjectReference) {
|
||||
if rr.collected[ref] {
|
||||
return
|
||||
}
|
||||
|
||||
content := types.ObjectContent{
|
||||
Obj: ref,
|
||||
}
|
||||
|
||||
rval, ok := getObject(ctx, ref)
|
||||
if !ok {
|
||||
// Possible if a test uses Map.Remove instead of Destroy_Task
|
||||
log.Printf("object %s no longer exists", ref)
|
||||
return
|
||||
}
|
||||
|
||||
rtype := rval.Type()
|
||||
match := false
|
||||
|
||||
for _, spec := range rr.req.SpecSet {
|
||||
for _, p := range spec.PropSet {
|
||||
if p.Type != ref.Type {
|
||||
// e.g. ManagedEntity, ComputeResource
|
||||
field, ok := rtype.FieldByName(p.Type)
|
||||
|
||||
if !(ok && field.Anonymous) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
match = true
|
||||
if isTrue(p.All) {
|
||||
rr.collectAll(ctx, rval, rtype, &content)
|
||||
continue
|
||||
}
|
||||
|
||||
rr.collectFields(ctx, rval, p.PathSet, &content)
|
||||
}
|
||||
}
|
||||
|
||||
if match {
|
||||
rr.Objects = append(rr.Objects, content)
|
||||
}
|
||||
|
||||
rr.collected[ref] = true
|
||||
}
|
||||
|
||||
func (rr *retrieveResult) selectSet(ctx *Context, obj reflect.Value, s []types.BaseSelectionSpec, refs *[]types.ManagedObjectReference) types.BaseMethodFault {
|
||||
for _, ss := range s {
|
||||
ts, ok := ss.(*types.TraversalSpec)
|
||||
if ok {
|
||||
if ts.Name != "" {
|
||||
rr.specs[ts.Name] = ts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ss := range s {
|
||||
ts, ok := ss.(*types.TraversalSpec)
|
||||
if !ok {
|
||||
ts = rr.specs[ss.GetSelectionSpec().Name]
|
||||
if ts == nil {
|
||||
return &types.InvalidArgument{InvalidProperty: "undefined TraversalSpec name"}
|
||||
}
|
||||
}
|
||||
|
||||
f, _ := fieldValue(obj, ts.Path)
|
||||
|
||||
for _, ref := range fieldRefs(f) {
|
||||
if isFalse(ts.Skip) {
|
||||
*refs = append(*refs, ref)
|
||||
}
|
||||
|
||||
rval, ok := getObject(ctx, ref)
|
||||
if ok {
|
||||
if err := rr.selectSet(ctx, rval, ts.SelectSet, refs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) collect(ctx *Context, r *types.RetrievePropertiesEx) (*types.RetrieveResult, types.BaseMethodFault) {
|
||||
var refs []types.ManagedObjectReference
|
||||
|
||||
rr := &retrieveResult{
|
||||
RetrieveResult: &types.RetrieveResult{},
|
||||
req: r,
|
||||
collected: make(map[types.ManagedObjectReference]bool),
|
||||
specs: make(map[string]*types.TraversalSpec),
|
||||
}
|
||||
|
||||
// Select object references
|
||||
for _, spec := range r.SpecSet {
|
||||
for _, o := range spec.ObjectSet {
|
||||
var rval reflect.Value
|
||||
ok := false
|
||||
ctx.WithLock(o.Obj, func() { rval, ok = getObject(ctx, o.Obj) })
|
||||
if !ok {
|
||||
if isFalse(spec.ReportMissingObjectsInResults) {
|
||||
return nil, &types.ManagedObjectNotFound{Obj: o.Obj}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if o.SelectSet == nil || isFalse(o.Skip) {
|
||||
refs = append(refs, o.Obj)
|
||||
}
|
||||
|
||||
if err := rr.selectSet(ctx, rval, o.SelectSet, &refs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ref := range refs {
|
||||
ctx.WithLock(ref, func() { rr.collect(ctx, ref) })
|
||||
}
|
||||
|
||||
return rr.RetrieveResult, nil
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) CreateFilter(ctx *Context, c *types.CreateFilter) soap.HasFault {
|
||||
body := &methods.CreateFilterBody{}
|
||||
|
||||
filter := &PropertyFilter{
|
||||
pc: pc,
|
||||
refs: make(map[types.ManagedObjectReference]struct{}),
|
||||
}
|
||||
filter.PartialUpdates = c.PartialUpdates
|
||||
filter.Spec = c.Spec
|
||||
|
||||
pc.Filter = append(pc.Filter, ctx.Session.Put(filter).Reference())
|
||||
|
||||
body.Res = &types.CreateFilterResponse{
|
||||
Returnval: filter.Self,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) CreatePropertyCollector(ctx *Context, c *types.CreatePropertyCollector) soap.HasFault {
|
||||
body := &methods.CreatePropertyCollectorBody{}
|
||||
|
||||
cpc := &PropertyCollector{}
|
||||
|
||||
body.Res = &types.CreatePropertyCollectorResponse{
|
||||
Returnval: ctx.Session.Put(cpc).Reference(),
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) DestroyPropertyCollector(ctx *Context, c *types.DestroyPropertyCollector) soap.HasFault {
|
||||
pc.CancelWaitForUpdates(&types.CancelWaitForUpdates{This: c.This})
|
||||
|
||||
body := &methods.DestroyPropertyCollectorBody{}
|
||||
|
||||
for _, ref := range pc.Filter {
|
||||
filter := ctx.Session.Get(ref).(*PropertyFilter)
|
||||
filter.DestroyPropertyFilter(ctx, &types.DestroyPropertyFilter{This: ref})
|
||||
}
|
||||
|
||||
ctx.Session.Remove(c.This)
|
||||
ctx.Map.Remove(c.This)
|
||||
|
||||
body.Res = &types.DestroyPropertyCollectorResponse{}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) RetrievePropertiesEx(ctx *Context, r *types.RetrievePropertiesEx) soap.HasFault {
|
||||
body := &methods.RetrievePropertiesExBody{}
|
||||
|
||||
res, fault := pc.collect(ctx, r)
|
||||
|
||||
if fault != nil {
|
||||
switch fault.(type) {
|
||||
case *types.ManagedObjectNotFound:
|
||||
body.Fault_ = Fault("The object has already been deleted or has not been completely created", fault)
|
||||
default:
|
||||
body.Fault_ = Fault("", fault)
|
||||
}
|
||||
} else {
|
||||
objects := res.Objects[:0]
|
||||
for _, o := range res.Objects {
|
||||
propSet := o.PropSet[:0]
|
||||
for _, p := range o.PropSet {
|
||||
if p.Val != nil {
|
||||
propSet = append(propSet, p)
|
||||
}
|
||||
}
|
||||
o.PropSet = propSet
|
||||
|
||||
objects = append(objects, o)
|
||||
}
|
||||
res.Objects = objects
|
||||
body.Res = &types.RetrievePropertiesExResponse{
|
||||
Returnval: res,
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
// RetrieveProperties is deprecated, but govmomi is still using it at the moment.
|
||||
func (pc *PropertyCollector) RetrieveProperties(ctx *Context, r *types.RetrieveProperties) soap.HasFault {
|
||||
body := &methods.RetrievePropertiesBody{}
|
||||
|
||||
res := pc.RetrievePropertiesEx(ctx, &types.RetrievePropertiesEx{
|
||||
This: r.This,
|
||||
SpecSet: r.SpecSet,
|
||||
})
|
||||
|
||||
if res.Fault() != nil {
|
||||
body.Fault_ = res.Fault()
|
||||
} else {
|
||||
body.Res = &types.RetrievePropertiesResponse{
|
||||
Returnval: res.(*methods.RetrievePropertiesExBody).Res.Returnval.Objects,
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) CancelWaitForUpdates(r *types.CancelWaitForUpdates) soap.HasFault {
|
||||
pc.mu.Lock()
|
||||
if pc.cancel != nil {
|
||||
pc.cancel()
|
||||
}
|
||||
pc.mu.Unlock()
|
||||
|
||||
return &methods.CancelWaitForUpdatesBody{Res: new(types.CancelWaitForUpdatesResponse)}
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) update(u types.ObjectUpdate) {
|
||||
pc.mu.Lock()
|
||||
pc.updates = append(pc.updates, u)
|
||||
pc.mu.Unlock()
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) PutObject(o mo.Reference) {
|
||||
pc.update(types.ObjectUpdate{
|
||||
Obj: o.Reference(),
|
||||
Kind: types.ObjectUpdateKindEnter,
|
||||
ChangeSet: nil,
|
||||
})
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) UpdateObject(o mo.Reference, changes []types.PropertyChange) {
|
||||
pc.update(types.ObjectUpdate{
|
||||
Obj: o.Reference(),
|
||||
Kind: types.ObjectUpdateKindModify,
|
||||
ChangeSet: changes,
|
||||
})
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) RemoveObject(ref types.ManagedObjectReference) {
|
||||
pc.update(types.ObjectUpdate{
|
||||
Obj: ref,
|
||||
Kind: types.ObjectUpdateKindLeave,
|
||||
ChangeSet: nil,
|
||||
})
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) apply(ctx *Context, update *types.UpdateSet) types.BaseMethodFault {
|
||||
for _, ref := range pc.Filter {
|
||||
filter := ctx.Session.Get(ref).(*PropertyFilter)
|
||||
|
||||
r := &types.RetrievePropertiesEx{}
|
||||
r.SpecSet = append(r.SpecSet, filter.Spec)
|
||||
|
||||
res, fault := pc.collect(ctx, r)
|
||||
if fault != nil {
|
||||
return fault
|
||||
}
|
||||
|
||||
fu := types.PropertyFilterUpdate{
|
||||
Filter: ref,
|
||||
}
|
||||
|
||||
for _, o := range res.Objects {
|
||||
if _, ok := filter.refs[o.Obj]; ok {
|
||||
continue
|
||||
}
|
||||
filter.refs[o.Obj] = struct{}{}
|
||||
ou := types.ObjectUpdate{
|
||||
Obj: o.Obj,
|
||||
Kind: types.ObjectUpdateKindEnter,
|
||||
}
|
||||
|
||||
for _, p := range o.PropSet {
|
||||
ou.ChangeSet = append(ou.ChangeSet, types.PropertyChange{
|
||||
Op: types.PropertyChangeOpAssign,
|
||||
Name: p.Name,
|
||||
Val: p.Val,
|
||||
})
|
||||
}
|
||||
|
||||
fu.ObjectSet = append(fu.ObjectSet, ou)
|
||||
}
|
||||
|
||||
if len(fu.ObjectSet) != 0 {
|
||||
update.FilterSet = append(update.FilterSet, fu)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpdatesEx) soap.HasFault {
|
||||
wait, cancel := context.WithCancel(context.Background())
|
||||
oneUpdate := false
|
||||
if r.Options != nil {
|
||||
if max := r.Options.MaxWaitSeconds; max != nil {
|
||||
// A value of 0 causes WaitForUpdatesEx to do one update calculation and return any results.
|
||||
oneUpdate = (*max == 0)
|
||||
if *max > 0 {
|
||||
wait, cancel = context.WithTimeout(context.Background(), time.Second*time.Duration(*max))
|
||||
}
|
||||
}
|
||||
}
|
||||
pc.mu.Lock()
|
||||
pc.cancel = cancel
|
||||
pc.mu.Unlock()
|
||||
|
||||
body := &methods.WaitForUpdatesExBody{}
|
||||
|
||||
set := &types.UpdateSet{
|
||||
Version: r.Version,
|
||||
}
|
||||
|
||||
body.Res = &types.WaitForUpdatesExResponse{
|
||||
Returnval: set,
|
||||
}
|
||||
|
||||
apply := func() bool {
|
||||
if fault := pc.apply(ctx, set); fault != nil {
|
||||
body.Fault_ = Fault("", fault)
|
||||
body.Res = nil
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if r.Version == "" {
|
||||
apply() // Collect current state
|
||||
set.Version = "-" // Next request with Version set will wait via loop below
|
||||
ctx.Map.AddHandler(pc) // Listen for create, update, delete of managed objects
|
||||
return body
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(250 * time.Millisecond) // allow for updates to accumulate
|
||||
defer ticker.Stop()
|
||||
// Start the wait loop, returning on one of:
|
||||
// - Client calls CancelWaitForUpdates
|
||||
// - MaxWaitSeconds was specified and has been exceeded
|
||||
// - We have updates to send to the client
|
||||
for {
|
||||
select {
|
||||
case <-wait.Done():
|
||||
body.Res.Returnval = nil
|
||||
switch wait.Err() {
|
||||
case context.Canceled:
|
||||
log.Printf("%s: WaitForUpdates canceled", pc.Self)
|
||||
body.Fault_ = Fault("", new(types.RequestCanceled)) // CancelWaitForUpdates was called
|
||||
body.Res = nil
|
||||
case context.DeadlineExceeded:
|
||||
log.Printf("%s: WaitForUpdates MaxWaitSeconds exceeded", pc.Self)
|
||||
}
|
||||
|
||||
return body
|
||||
case <-ticker.C:
|
||||
pc.mu.Lock()
|
||||
updates := pc.updates
|
||||
pc.updates = nil // clear updates collected by the managed object CRUD listeners
|
||||
pc.mu.Unlock()
|
||||
if len(updates) == 0 {
|
||||
if oneUpdate {
|
||||
body.Res.Returnval = nil
|
||||
return body
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("%s: applying %d updates to %d filters", pc.Self, len(updates), len(pc.Filter))
|
||||
|
||||
for _, f := range pc.Filter {
|
||||
filter := ctx.Session.Get(f).(*PropertyFilter)
|
||||
fu := types.PropertyFilterUpdate{Filter: f}
|
||||
|
||||
for _, update := range updates {
|
||||
switch update.Kind {
|
||||
case types.ObjectUpdateKindEnter: // Create
|
||||
if !apply() {
|
||||
return body
|
||||
}
|
||||
case types.ObjectUpdateKindModify: // Update
|
||||
log.Printf("%s has %d changes", update.Obj, len(update.ChangeSet))
|
||||
if !apply() { // An update may apply to collector traversal specs
|
||||
return body
|
||||
}
|
||||
if _, ok := filter.refs[update.Obj]; ok {
|
||||
// This object has already been applied by the filter,
|
||||
// now check if the property spec applies for this update.
|
||||
update = filter.apply(ctx, update)
|
||||
if len(update.ChangeSet) != 0 {
|
||||
fu.ObjectSet = append(fu.ObjectSet, update)
|
||||
}
|
||||
}
|
||||
case types.ObjectUpdateKindLeave: // Delete
|
||||
if _, ok := filter.refs[update.Obj]; !ok {
|
||||
continue
|
||||
}
|
||||
delete(filter.refs, update.Obj)
|
||||
fu.ObjectSet = append(fu.ObjectSet, update)
|
||||
}
|
||||
}
|
||||
|
||||
if len(fu.ObjectSet) != 0 {
|
||||
set.FilterSet = append(set.FilterSet, fu)
|
||||
}
|
||||
}
|
||||
if len(set.FilterSet) != 0 {
|
||||
return body
|
||||
}
|
||||
if oneUpdate {
|
||||
body.Res.Returnval = nil
|
||||
return body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WaitForUpdates is deprecated, but pyvmomi is still using it at the moment.
|
||||
func (pc *PropertyCollector) WaitForUpdates(ctx *Context, r *types.WaitForUpdates) soap.HasFault {
|
||||
body := &methods.WaitForUpdatesBody{}
|
||||
|
||||
res := pc.WaitForUpdatesEx(ctx, &types.WaitForUpdatesEx{
|
||||
This: r.This,
|
||||
Version: r.Version,
|
||||
})
|
||||
|
||||
if res.Fault() != nil {
|
||||
body.Fault_ = res.Fault()
|
||||
} else {
|
||||
body.Res = &types.WaitForUpdatesResponse{
|
||||
Returnval: *res.(*methods.WaitForUpdatesExBody).Res.Returnval,
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
// Fetch is not documented in the vSphere SDK, but ovftool depends on it.
|
||||
// A Fetch request is converted to a RetrievePropertiesEx method call by vcsim.
|
||||
func (pc *PropertyCollector) Fetch(ctx *Context, req *internal.Fetch) soap.HasFault {
|
||||
body := new(internal.FetchBody)
|
||||
|
||||
if req.This == vim25.ServiceInstance && req.Prop == "content" {
|
||||
content := ctx.Map.content()
|
||||
// ovftool uses API version for 6.0 and fails when these fields are non-nil; TODO
|
||||
content.VStorageObjectManager = nil
|
||||
content.HostProfileManager = nil
|
||||
content.HostSpecManager = nil
|
||||
content.CryptoManager = nil
|
||||
content.HostProfileManager = nil
|
||||
content.HealthUpdateManager = nil
|
||||
content.FailoverClusterConfigurator = nil
|
||||
content.FailoverClusterManager = nil
|
||||
body.Res = &internal.FetchResponse{
|
||||
Returnval: content,
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
if ctx.Map.Get(req.This) == nil {
|
||||
// The Fetch method supports use of super class types, this is a quick hack to support the cases used by ovftool
|
||||
switch req.This.Type {
|
||||
case "ManagedEntity":
|
||||
for o := range ctx.Map.objects {
|
||||
if o.Value == req.This.Value {
|
||||
req.This.Type = o.Type
|
||||
break
|
||||
}
|
||||
}
|
||||
case "ComputeResource":
|
||||
req.This.Type = "Cluster" + req.This.Type
|
||||
}
|
||||
}
|
||||
|
||||
res := pc.RetrievePropertiesEx(ctx, &types.RetrievePropertiesEx{
|
||||
SpecSet: []types.PropertyFilterSpec{{
|
||||
PropSet: []types.PropertySpec{{
|
||||
Type: req.This.Type,
|
||||
PathSet: []string{req.Prop},
|
||||
}},
|
||||
ObjectSet: []types.ObjectSpec{{
|
||||
Obj: req.This,
|
||||
}},
|
||||
}}})
|
||||
|
||||
if res.Fault() != nil {
|
||||
return res
|
||||
}
|
||||
|
||||
obj := res.(*methods.RetrievePropertiesExBody).Res.Returnval.Objects[0]
|
||||
if len(obj.PropSet) == 0 {
|
||||
fault := obj.MissingSet[0].Fault
|
||||
body.Fault_ = Fault(fault.LocalizedMessage, fault.Fault)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &internal.FetchResponse{
|
||||
Returnval: obj.PropSet[0].Val,
|
||||
}
|
||||
return body
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type PropertyFilter struct {
|
||||
mo.PropertyFilter
|
||||
|
||||
pc *PropertyCollector
|
||||
refs map[types.ManagedObjectReference]struct{}
|
||||
}
|
||||
|
||||
func (f *PropertyFilter) DestroyPropertyFilter(ctx *Context, c *types.DestroyPropertyFilter) soap.HasFault {
|
||||
body := &methods.DestroyPropertyFilterBody{}
|
||||
|
||||
RemoveReference(&f.pc.Filter, c.This)
|
||||
|
||||
ctx.Session.Remove(c.This)
|
||||
|
||||
body.Res = &types.DestroyPropertyFilterResponse{}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
// matches returns true if the change matches one of the filter Spec.PropSet
|
||||
func (f *PropertyFilter) matches(ctx *Context, ref types.ManagedObjectReference, change *types.PropertyChange) bool {
|
||||
var kind reflect.Type
|
||||
|
||||
for _, p := range f.Spec.PropSet {
|
||||
if p.Type != ref.Type {
|
||||
if kind == nil {
|
||||
kind = getManagedObject(ctx.Map.Get(ref)).Type()
|
||||
}
|
||||
// e.g. ManagedEntity, ComputeResource
|
||||
field, ok := kind.FieldByName(p.Type)
|
||||
if !(ok && field.Anonymous) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if isTrue(p.All) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, name := range p.PathSet {
|
||||
if name == change.Name {
|
||||
return true
|
||||
}
|
||||
|
||||
// strings.HasPrefix("runtime.powerState", "runtime") == parent field matches
|
||||
if strings.HasPrefix(change.Name, name) {
|
||||
if obj := ctx.Map.Get(ref); obj != nil { // object may have since been deleted
|
||||
change.Name = name
|
||||
change.Val, _ = fieldValue(reflect.ValueOf(obj), name)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// apply the PropertyFilter.Spec to the given ObjectUpdate
|
||||
func (f *PropertyFilter) apply(ctx *Context, change types.ObjectUpdate) types.ObjectUpdate {
|
||||
parents := make(map[string]bool)
|
||||
set := change.ChangeSet
|
||||
change.ChangeSet = nil
|
||||
|
||||
for i, p := range set {
|
||||
if f.matches(ctx, change.Obj, &p) {
|
||||
if p.Name != set[i].Name {
|
||||
// update matches a parent field from the spec.
|
||||
if parents[p.Name] {
|
||||
continue // only return 1 instance of the parent
|
||||
}
|
||||
parents[p.Name] = true
|
||||
}
|
||||
change.ChangeSet = append(change.ChangeSet, p)
|
||||
}
|
||||
}
|
||||
|
||||
return change
|
||||
}
|
|
@ -1,558 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// This is a map from a reference type name to a reference value name prefix.
|
||||
// It's a convention that VirtualCenter follows. The map is not complete, but
|
||||
// it should cover the most popular objects.
|
||||
var refValueMap = map[string]string{
|
||||
"DistributedVirtualPortgroup": "dvportgroup",
|
||||
"EnvironmentBrowser": "envbrowser",
|
||||
"HostSystem": "host",
|
||||
"ResourcePool": "resgroup",
|
||||
"VirtualMachine": "vm",
|
||||
"VirtualMachineSnapshot": "snapshot",
|
||||
"VmwareDistributedVirtualSwitch": "dvs",
|
||||
"DistributedVirtualSwitch": "dvs",
|
||||
}
|
||||
|
||||
// Map is the default Registry instance.
|
||||
var Map = NewRegistry()
|
||||
|
||||
// RegisterObject interface supports callbacks when objects are created, updated and deleted from the Registry
|
||||
type RegisterObject interface {
|
||||
mo.Reference
|
||||
PutObject(mo.Reference)
|
||||
UpdateObject(mo.Reference, []types.PropertyChange)
|
||||
RemoveObject(types.ManagedObjectReference)
|
||||
}
|
||||
|
||||
// Registry manages a map of mo.Reference objects
|
||||
type Registry struct {
|
||||
counter int64 // Keep first to ensure 64-bit alignment
|
||||
m sync.Mutex
|
||||
objects map[types.ManagedObjectReference]mo.Reference
|
||||
handlers map[types.ManagedObjectReference]RegisterObject
|
||||
locks map[types.ManagedObjectReference]sync.Locker
|
||||
|
||||
Namespace string
|
||||
Path string
|
||||
|
||||
tagManager tagManager
|
||||
}
|
||||
|
||||
// tagManager is an interface to simplify internal interaction with the vapi tag manager simulator.
|
||||
type tagManager interface {
|
||||
AttachedObjects(types.VslmTagEntry) ([]types.ManagedObjectReference, types.BaseMethodFault)
|
||||
AttachedTags(id types.ManagedObjectReference) ([]types.VslmTagEntry, types.BaseMethodFault)
|
||||
AttachTag(types.ManagedObjectReference, types.VslmTagEntry) types.BaseMethodFault
|
||||
DetachTag(types.ManagedObjectReference, types.VslmTagEntry) types.BaseMethodFault
|
||||
}
|
||||
|
||||
// NewRegistry creates a new instances of Registry
|
||||
func NewRegistry() *Registry {
|
||||
r := &Registry{
|
||||
objects: make(map[types.ManagedObjectReference]mo.Reference),
|
||||
handlers: make(map[types.ManagedObjectReference]RegisterObject),
|
||||
locks: make(map[types.ManagedObjectReference]sync.Locker),
|
||||
|
||||
Namespace: vim25.Namespace,
|
||||
Path: vim25.Path,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Registry) typeFunc(name string) (reflect.Type, bool) {
|
||||
if r.Namespace != "" && r.Namespace != vim25.Namespace {
|
||||
if kind, ok := defaultMapType(r.Namespace + ":" + name); ok {
|
||||
return kind, ok
|
||||
}
|
||||
}
|
||||
return defaultMapType(name)
|
||||
}
|
||||
|
||||
// typeName returns the type of the given object.
|
||||
func typeName(item mo.Reference) string {
|
||||
return reflect.TypeOf(item).Elem().Name()
|
||||
}
|
||||
|
||||
// valuePrefix returns the value name prefix of a given object
|
||||
func valuePrefix(typeName string) string {
|
||||
if v, ok := refValueMap[typeName]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return strings.ToLower(typeName)
|
||||
}
|
||||
|
||||
// newReference returns a new MOR, where Type defaults to type of the given item
|
||||
// and Value defaults to a unique id for the given type.
|
||||
func (r *Registry) newReference(item mo.Reference) types.ManagedObjectReference {
|
||||
ref := item.Reference()
|
||||
|
||||
if ref.Type == "" {
|
||||
ref.Type = typeName(item)
|
||||
}
|
||||
|
||||
if ref.Value == "" {
|
||||
n := atomic.AddInt64(&r.counter, 1)
|
||||
ref.Value = fmt.Sprintf("%s-%d", valuePrefix(ref.Type), n)
|
||||
}
|
||||
|
||||
return ref
|
||||
}
|
||||
|
||||
func (r *Registry) setReference(item mo.Reference, ref types.ManagedObjectReference) {
|
||||
// mo.Reference() returns a value, not a pointer so use reflect to set the Self field
|
||||
reflect.ValueOf(item).Elem().FieldByName("Self").Set(reflect.ValueOf(ref))
|
||||
}
|
||||
|
||||
// AddHandler adds a RegisterObject handler to the Registry.
|
||||
func (r *Registry) AddHandler(h RegisterObject) {
|
||||
r.m.Lock()
|
||||
r.handlers[h.Reference()] = h
|
||||
r.m.Unlock()
|
||||
}
|
||||
|
||||
// NewEntity sets Entity().Self with a new, unique Value.
|
||||
// Useful for creating object instances from templates.
|
||||
func (r *Registry) NewEntity(item mo.Entity) mo.Entity {
|
||||
e := item.Entity()
|
||||
e.Self.Value = ""
|
||||
e.Self = r.newReference(item)
|
||||
return item
|
||||
}
|
||||
|
||||
// PutEntity sets item.Parent to that of parent.Self before adding item to the Registry.
|
||||
func (r *Registry) PutEntity(parent mo.Entity, item mo.Entity) mo.Entity {
|
||||
e := item.Entity()
|
||||
|
||||
if parent != nil {
|
||||
e.Parent = &parent.Entity().Self
|
||||
}
|
||||
|
||||
r.Put(item)
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
// Get returns the object for the given reference.
|
||||
func (r *Registry) Get(ref types.ManagedObjectReference) mo.Reference {
|
||||
r.m.Lock()
|
||||
defer r.m.Unlock()
|
||||
|
||||
return r.objects[ref]
|
||||
}
|
||||
|
||||
// Any returns the first instance of entity type specified by kind.
|
||||
func (r *Registry) Any(kind string) mo.Entity {
|
||||
r.m.Lock()
|
||||
defer r.m.Unlock()
|
||||
|
||||
for ref, val := range r.objects {
|
||||
if ref.Type == kind {
|
||||
return val.(mo.Entity)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// All returns all entities of type specified by kind.
|
||||
// If kind is empty - all entities will be returned.
|
||||
func (r *Registry) All(kind string) []mo.Entity {
|
||||
r.m.Lock()
|
||||
defer r.m.Unlock()
|
||||
|
||||
var entities []mo.Entity
|
||||
for ref, val := range r.objects {
|
||||
if kind == "" || ref.Type == kind {
|
||||
if e, ok := val.(mo.Entity); ok {
|
||||
entities = append(entities, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entities
|
||||
}
|
||||
|
||||
// applyHandlers calls the given func for each r.handlers
|
||||
func (r *Registry) applyHandlers(f func(o RegisterObject)) {
|
||||
r.m.Lock()
|
||||
handlers := make([]RegisterObject, 0, len(r.handlers))
|
||||
for _, handler := range r.handlers {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
||||
r.m.Unlock()
|
||||
|
||||
for i := range handlers {
|
||||
f(handlers[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Put adds a new object to Registry, generating a ManagedObjectReference if not already set.
|
||||
func (r *Registry) Put(item mo.Reference) mo.Reference {
|
||||
r.m.Lock()
|
||||
|
||||
ref := item.Reference()
|
||||
if ref.Type == "" || ref.Value == "" {
|
||||
ref = r.newReference(item)
|
||||
r.setReference(item, ref)
|
||||
}
|
||||
|
||||
if me, ok := item.(mo.Entity); ok {
|
||||
me.Entity().ConfigStatus = types.ManagedEntityStatusGreen
|
||||
me.Entity().OverallStatus = types.ManagedEntityStatusGreen
|
||||
me.Entity().EffectiveRole = []int32{-1} // Admin
|
||||
}
|
||||
|
||||
r.objects[ref] = item
|
||||
|
||||
r.m.Unlock()
|
||||
|
||||
r.applyHandlers(func(o RegisterObject) {
|
||||
o.PutObject(item)
|
||||
})
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
// Remove removes an object from the Registry.
|
||||
func (r *Registry) Remove(item types.ManagedObjectReference) {
|
||||
r.applyHandlers(func(o RegisterObject) {
|
||||
o.RemoveObject(item)
|
||||
})
|
||||
|
||||
r.m.Lock()
|
||||
delete(r.objects, item)
|
||||
delete(r.handlers, item)
|
||||
delete(r.locks, item)
|
||||
r.m.Unlock()
|
||||
}
|
||||
|
||||
// Update dispatches object property changes to RegisterObject handlers,
|
||||
// such as any PropertyCollector instances with in-progress WaitForUpdates calls.
|
||||
// The changes are also applied to the given object via mo.ApplyPropertyChange,
|
||||
// so there is no need to set object fields directly.
|
||||
func (r *Registry) Update(obj mo.Reference, changes []types.PropertyChange) {
|
||||
for i := range changes {
|
||||
if changes[i].Op == "" {
|
||||
changes[i].Op = types.PropertyChangeOpAssign
|
||||
}
|
||||
if changes[i].Val != nil {
|
||||
rval := reflect.ValueOf(changes[i].Val)
|
||||
changes[i].Val = wrapValue(rval, rval.Type())
|
||||
}
|
||||
}
|
||||
|
||||
val := getManagedObject(obj).Addr().Interface().(mo.Reference)
|
||||
|
||||
mo.ApplyPropertyChange(val, changes)
|
||||
|
||||
r.applyHandlers(func(o RegisterObject) {
|
||||
o.UpdateObject(val, changes)
|
||||
})
|
||||
}
|
||||
|
||||
// getEntityParent traverses up the inventory and returns the first object of type kind.
|
||||
// If no object of type kind is found, the method will panic when it reaches the
|
||||
// inventory root Folder where the Parent field is nil.
|
||||
func (r *Registry) getEntityParent(item mo.Entity, kind string) mo.Entity {
|
||||
var ok bool
|
||||
for {
|
||||
parent := item.Entity().Parent
|
||||
|
||||
item, ok = r.Get(*parent).(mo.Entity)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if item.Reference().Type == kind {
|
||||
return item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getEntityDatacenter returns the Datacenter containing the given item
|
||||
func (r *Registry) getEntityDatacenter(item mo.Entity) *Datacenter {
|
||||
dc, ok := r.getEntityParent(item, "Datacenter").(*Datacenter)
|
||||
if ok {
|
||||
return dc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) getEntityFolder(item mo.Entity, kind string) *mo.Folder {
|
||||
dc := Map.getEntityDatacenter(item)
|
||||
|
||||
var ref types.ManagedObjectReference
|
||||
|
||||
switch kind {
|
||||
case "datastore":
|
||||
ref = dc.DatastoreFolder
|
||||
}
|
||||
|
||||
folder, _ := asFolderMO(r.Get(ref))
|
||||
|
||||
// If Model was created with Folder option, use that Folder; else use top-level folder
|
||||
for _, child := range folder.ChildEntity {
|
||||
if child.Type == "Folder" {
|
||||
folder, _ = asFolderMO(Map.Get(child))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return folder
|
||||
}
|
||||
|
||||
// getEntityComputeResource returns the ComputeResource parent for the given item.
|
||||
// A ResourcePool for example may have N Parents of type ResourcePool, but the top
|
||||
// most Parent pool is always a ComputeResource child.
|
||||
func (r *Registry) getEntityComputeResource(item mo.Entity) mo.Entity {
|
||||
for {
|
||||
parent := item.Entity().Parent
|
||||
|
||||
item = r.Get(*parent).(mo.Entity)
|
||||
|
||||
switch item.Reference().Type {
|
||||
case "ComputeResource":
|
||||
return item
|
||||
case "ClusterComputeResource":
|
||||
return item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FindByName returns the first mo.Entity of the given refs whose Name field is equal to the given name.
|
||||
// If there is no match, nil is returned.
|
||||
// This method is useful for cases where objects are required to have a unique name, such as Datastore with
|
||||
// a HostStorageSystem or HostSystem within a ClusterComputeResource.
|
||||
func (r *Registry) FindByName(name string, refs []types.ManagedObjectReference) mo.Entity {
|
||||
for _, ref := range refs {
|
||||
if e, ok := r.Get(ref).(mo.Entity); ok {
|
||||
if name == e.Entity().Name {
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindReference returns the 1st match found in refs, or nil if not found.
|
||||
func FindReference(refs []types.ManagedObjectReference, match ...types.ManagedObjectReference) *types.ManagedObjectReference {
|
||||
for _, ref := range refs {
|
||||
for _, m := range match {
|
||||
if ref == m {
|
||||
return &ref
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AppendReference appends the given refs to field.
|
||||
func (r *Registry) AppendReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref ...types.ManagedObjectReference) {
|
||||
r.WithLock(obj, func() {
|
||||
*field = append(*field, ref...)
|
||||
})
|
||||
}
|
||||
|
||||
// AddReference appends ref to field if not already in the given field.
|
||||
func (r *Registry) AddReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
|
||||
r.WithLock(obj, func() {
|
||||
if FindReference(*field, ref) == nil {
|
||||
*field = append(*field, ref)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveReference removes ref from the given field.
|
||||
func RemoveReference(field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
|
||||
for i, r := range *field {
|
||||
if r == ref {
|
||||
*field = append((*field)[:i], (*field)[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveReference removes ref from the given field.
|
||||
func (r *Registry) RemoveReference(obj mo.Reference, field *[]types.ManagedObjectReference, ref types.ManagedObjectReference) {
|
||||
r.WithLock(obj, func() {
|
||||
RemoveReference(field, ref)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Registry) removeString(obj mo.Reference, field *[]string, val string) {
|
||||
r.WithLock(obj, func() {
|
||||
for i, name := range *field {
|
||||
if name == val {
|
||||
*field = append((*field)[:i], (*field)[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Registry) content() types.ServiceContent {
|
||||
return r.Get(vim25.ServiceInstance).(*ServiceInstance).Content
|
||||
}
|
||||
|
||||
// IsESX returns true if this Registry maps an ESX model
|
||||
func (r *Registry) IsESX() bool {
|
||||
return r.content().About.ApiType == "HostAgent"
|
||||
}
|
||||
|
||||
// IsVPX returns true if this Registry maps a VPX model
|
||||
func (r *Registry) IsVPX() bool {
|
||||
return !r.IsESX()
|
||||
}
|
||||
|
||||
// SearchIndex returns the SearchIndex singleton
|
||||
func (r *Registry) SearchIndex() *SearchIndex {
|
||||
return r.Get(r.content().SearchIndex.Reference()).(*SearchIndex)
|
||||
}
|
||||
|
||||
// EventManager returns the EventManager singleton
|
||||
func (r *Registry) EventManager() *EventManager {
|
||||
return r.Get(r.content().EventManager.Reference()).(*EventManager)
|
||||
}
|
||||
|
||||
// FileManager returns the FileManager singleton
|
||||
func (r *Registry) FileManager() *FileManager {
|
||||
return r.Get(r.content().FileManager.Reference()).(*FileManager)
|
||||
}
|
||||
|
||||
type VirtualDiskManagerInterface interface {
|
||||
mo.Reference
|
||||
MO() mo.VirtualDiskManager
|
||||
CreateVirtualDiskTask(*Context, *types.CreateVirtualDisk_Task) soap.HasFault
|
||||
DeleteVirtualDiskTask(*Context, *types.DeleteVirtualDisk_Task) soap.HasFault
|
||||
MoveVirtualDiskTask(*Context, *types.MoveVirtualDisk_Task) soap.HasFault
|
||||
CopyVirtualDiskTask(*Context, *types.CopyVirtualDisk_Task) soap.HasFault
|
||||
QueryVirtualDiskUuid(*Context, *types.QueryVirtualDiskUuid) soap.HasFault
|
||||
SetVirtualDiskUuid(*Context, *types.SetVirtualDiskUuid) soap.HasFault
|
||||
}
|
||||
|
||||
// VirtualDiskManager returns the VirtualDiskManager singleton
|
||||
func (r *Registry) VirtualDiskManager() VirtualDiskManagerInterface {
|
||||
return r.Get(r.content().VirtualDiskManager.Reference()).(VirtualDiskManagerInterface)
|
||||
}
|
||||
|
||||
// ViewManager returns the ViewManager singleton
|
||||
func (r *Registry) ViewManager() *ViewManager {
|
||||
return r.Get(r.content().ViewManager.Reference()).(*ViewManager)
|
||||
}
|
||||
|
||||
// UserDirectory returns the UserDirectory singleton
|
||||
func (r *Registry) UserDirectory() *UserDirectory {
|
||||
return r.Get(r.content().UserDirectory.Reference()).(*UserDirectory)
|
||||
}
|
||||
|
||||
// SessionManager returns the SessionManager singleton
|
||||
func (r *Registry) SessionManager() *SessionManager {
|
||||
return r.Get(r.content().SessionManager.Reference()).(*SessionManager)
|
||||
}
|
||||
|
||||
// OptionManager returns the OptionManager singleton
|
||||
func (r *Registry) OptionManager() *OptionManager {
|
||||
return r.Get(r.content().Setting.Reference()).(*OptionManager)
|
||||
}
|
||||
|
||||
// CustomFieldsManager returns CustomFieldsManager singleton
|
||||
func (r *Registry) CustomFieldsManager() *CustomFieldsManager {
|
||||
return r.Get(r.content().CustomFieldsManager.Reference()).(*CustomFieldsManager)
|
||||
}
|
||||
|
||||
func (r *Registry) MarshalJSON() ([]byte, error) {
|
||||
r.m.Lock()
|
||||
defer r.m.Unlock()
|
||||
|
||||
vars := struct {
|
||||
Objects int
|
||||
Locks int
|
||||
}{
|
||||
len(r.objects),
|
||||
len(r.locks),
|
||||
}
|
||||
|
||||
return json.Marshal(vars)
|
||||
}
|
||||
|
||||
func (r *Registry) locker(obj mo.Reference) sync.Locker {
|
||||
var ref types.ManagedObjectReference
|
||||
|
||||
switch x := obj.(type) {
|
||||
case types.ManagedObjectReference:
|
||||
ref = x
|
||||
obj = r.Get(ref) // to check for sync.Locker
|
||||
case *types.ManagedObjectReference:
|
||||
ref = *x
|
||||
obj = r.Get(ref) // to check for sync.Locker
|
||||
default:
|
||||
ref = obj.Reference()
|
||||
}
|
||||
|
||||
if mu, ok := obj.(sync.Locker); ok {
|
||||
return mu
|
||||
}
|
||||
|
||||
r.m.Lock()
|
||||
mu, ok := r.locks[ref]
|
||||
if !ok {
|
||||
mu = new(sync.Mutex)
|
||||
r.locks[ref] = mu
|
||||
}
|
||||
r.m.Unlock()
|
||||
|
||||
return mu
|
||||
}
|
||||
|
||||
var enableLocker = os.Getenv("VCSIM_LOCKER") != "false"
|
||||
|
||||
// WithLock holds a lock for the given object while then given function is run.
|
||||
func (r *Registry) WithLock(obj mo.Reference, f func()) {
|
||||
if enableLocker {
|
||||
mu := r.locker(obj)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
}
|
||||
f()
|
||||
}
|
||||
|
||||
// nopLocker can be embedded to opt-out of auto-locking (see Registry.WithLock)
|
||||
type nopLocker struct{}
|
||||
|
||||
func (*nopLocker) Lock() {}
|
||||
func (*nopLocker) Unlock() {}
|
|
@ -1,402 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type ResourcePool struct {
|
||||
mo.ResourcePool
|
||||
}
|
||||
|
||||
func asResourcePoolMO(obj mo.Reference) (*mo.ResourcePool, bool) {
|
||||
rp, ok := getManagedObject(obj).Addr().Interface().(*mo.ResourcePool)
|
||||
return rp, ok
|
||||
}
|
||||
|
||||
func NewResourcePool() *ResourcePool {
|
||||
pool := &ResourcePool{
|
||||
ResourcePool: esx.ResourcePool,
|
||||
}
|
||||
|
||||
if Map.IsVPX() {
|
||||
pool.DisabledMethod = nil // Enable VApp methods for VC
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
func allResourceFieldsSet(info *types.ResourceAllocationInfo) bool {
|
||||
return info.Reservation != nil &&
|
||||
info.Limit != nil &&
|
||||
info.ExpandableReservation != nil &&
|
||||
info.Shares != nil
|
||||
}
|
||||
|
||||
func allResourceFieldsValid(info *types.ResourceAllocationInfo) bool {
|
||||
if info.Reservation != nil {
|
||||
if *info.Reservation < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if info.Limit != nil {
|
||||
if *info.Limit < -1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if info.Shares != nil {
|
||||
if info.Shares.Level == types.SharesLevelCustom {
|
||||
if info.Shares.Shares < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if info.OverheadLimit != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *ResourcePool) createChild(name string, spec types.ResourceConfigSpec) (*ResourcePool, *soap.Fault) {
|
||||
if e := Map.FindByName(name, p.ResourcePool.ResourcePool); e != nil {
|
||||
return nil, Fault("", &types.DuplicateName{
|
||||
Name: e.Entity().Name,
|
||||
Object: e.Reference(),
|
||||
})
|
||||
}
|
||||
|
||||
if !(allResourceFieldsSet(&spec.CpuAllocation) && allResourceFieldsValid(&spec.CpuAllocation)) {
|
||||
return nil, Fault("", &types.InvalidArgument{
|
||||
InvalidProperty: "spec.cpuAllocation",
|
||||
})
|
||||
}
|
||||
|
||||
if !(allResourceFieldsSet(&spec.MemoryAllocation) && allResourceFieldsValid(&spec.MemoryAllocation)) {
|
||||
return nil, Fault("", &types.InvalidArgument{
|
||||
InvalidProperty: "spec.memoryAllocation",
|
||||
})
|
||||
}
|
||||
|
||||
child := NewResourcePool()
|
||||
|
||||
child.Name = name
|
||||
child.Owner = p.Owner
|
||||
child.Summary.GetResourcePoolSummary().Name = name
|
||||
child.Config.CpuAllocation = spec.CpuAllocation
|
||||
child.Config.MemoryAllocation = spec.MemoryAllocation
|
||||
child.Config.Entity = spec.Entity
|
||||
|
||||
return child, nil
|
||||
}
|
||||
|
||||
func (p *ResourcePool) CreateResourcePool(c *types.CreateResourcePool) soap.HasFault {
|
||||
body := &methods.CreateResourcePoolBody{}
|
||||
|
||||
child, err := p.createChild(c.Name, c.Spec)
|
||||
if err != nil {
|
||||
body.Fault_ = err
|
||||
return body
|
||||
}
|
||||
|
||||
Map.PutEntity(p, Map.NewEntity(child))
|
||||
|
||||
p.ResourcePool.ResourcePool = append(p.ResourcePool.ResourcePool, child.Reference())
|
||||
|
||||
body.Res = &types.CreateResourcePoolResponse{
|
||||
Returnval: child.Reference(),
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func updateResourceAllocation(kind string, src, dst *types.ResourceAllocationInfo) types.BaseMethodFault {
|
||||
if !allResourceFieldsValid(src) {
|
||||
return &types.InvalidArgument{
|
||||
InvalidProperty: fmt.Sprintf("spec.%sAllocation", kind),
|
||||
}
|
||||
}
|
||||
|
||||
if src.Reservation != nil {
|
||||
dst.Reservation = src.Reservation
|
||||
}
|
||||
|
||||
if src.Limit != nil {
|
||||
dst.Limit = src.Limit
|
||||
}
|
||||
|
||||
if src.Shares != nil {
|
||||
dst.Shares = src.Shares
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ResourcePool) UpdateConfig(c *types.UpdateConfig) soap.HasFault {
|
||||
body := &methods.UpdateConfigBody{}
|
||||
|
||||
if c.Name != "" {
|
||||
if e := Map.FindByName(c.Name, p.ResourcePool.ResourcePool); e != nil {
|
||||
body.Fault_ = Fault("", &types.DuplicateName{
|
||||
Name: e.Entity().Name,
|
||||
Object: e.Reference(),
|
||||
})
|
||||
return body
|
||||
}
|
||||
|
||||
p.Name = c.Name
|
||||
}
|
||||
|
||||
spec := c.Config
|
||||
|
||||
if spec != nil {
|
||||
if err := updateResourceAllocation("memory", &spec.MemoryAllocation, &p.Config.MemoryAllocation); err != nil {
|
||||
body.Fault_ = Fault("", err)
|
||||
return body
|
||||
}
|
||||
|
||||
if err := updateResourceAllocation("cpu", &spec.CpuAllocation, &p.Config.CpuAllocation); err != nil {
|
||||
body.Fault_ = Fault("", err)
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
body.Res = &types.UpdateConfigResponse{}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (a *VirtualApp) ImportVApp(ctx *Context, req *types.ImportVApp) soap.HasFault {
|
||||
return (&ResourcePool{ResourcePool: a.ResourcePool}).ImportVApp(ctx, req)
|
||||
}
|
||||
|
||||
func (p *ResourcePool) ImportVApp(ctx *Context, req *types.ImportVApp) soap.HasFault {
|
||||
body := new(methods.ImportVAppBody)
|
||||
|
||||
spec, ok := req.Spec.(*types.VirtualMachineImportSpec)
|
||||
if !ok {
|
||||
body.Fault_ = Fault(fmt.Sprintf("%T: type not supported", spec), &types.InvalidArgument{InvalidProperty: "spec"})
|
||||
return body
|
||||
}
|
||||
|
||||
dc := ctx.Map.getEntityDatacenter(p)
|
||||
folder := ctx.Map.Get(dc.VmFolder).(*Folder)
|
||||
if req.Folder != nil {
|
||||
if p.Self.Type == "VirtualApp" {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "pool"})
|
||||
return body
|
||||
}
|
||||
folder = ctx.Map.Get(*req.Folder).(*Folder)
|
||||
}
|
||||
|
||||
res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
|
||||
This: folder.Self,
|
||||
Config: spec.ConfigSpec,
|
||||
Pool: p.Self,
|
||||
Host: req.Host,
|
||||
})
|
||||
|
||||
ctask := Map.Get(res.(*methods.CreateVM_TaskBody).Res.Returnval).(*Task)
|
||||
if ctask.Info.Error != nil {
|
||||
body.Fault_ = Fault("", ctask.Info.Error.Fault)
|
||||
return body
|
||||
}
|
||||
|
||||
lease := NewHttpNfcLease(ctx, ctask.Info.Result.(types.ManagedObjectReference))
|
||||
ref := lease.Reference()
|
||||
lease.Info.Lease = ref
|
||||
|
||||
vm := ctx.Map.Get(lease.Info.Entity).(*VirtualMachine)
|
||||
device := object.VirtualDeviceList(vm.Config.Hardware.Device)
|
||||
ndevice := make(map[string]int)
|
||||
for _, d := range device {
|
||||
info, ok := d.GetVirtualDevice().Backing.(types.BaseVirtualDeviceFileBackingInfo)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var file object.DatastorePath
|
||||
file.FromString(info.GetVirtualDeviceFileBackingInfo().FileName)
|
||||
name := path.Base(file.Path)
|
||||
ds := vm.findDatastore(file.Datastore)
|
||||
lease.files[name] = path.Join(ds.Info.GetDatastoreInfo().Url, file.Path)
|
||||
|
||||
_, disk := d.(*types.VirtualDisk)
|
||||
kind := device.Type(d)
|
||||
n := ndevice[kind]
|
||||
ndevice[kind]++
|
||||
|
||||
lease.Info.DeviceUrl = append(lease.Info.DeviceUrl, types.HttpNfcLeaseDeviceUrl{
|
||||
Key: fmt.Sprintf("/%s/%s:%d", vm.Self.Value, kind, n),
|
||||
ImportKey: fmt.Sprintf("/%s/%s:%d", vm.Name, kind, n),
|
||||
Url: (&url.URL{
|
||||
Scheme: "https",
|
||||
Host: "*",
|
||||
Path: nfcPrefix + path.Join(ref.Value, name),
|
||||
}).String(),
|
||||
SslThumbprint: "",
|
||||
Disk: types.NewBool(disk),
|
||||
TargetId: name,
|
||||
DatastoreKey: "",
|
||||
FileSize: 0,
|
||||
})
|
||||
}
|
||||
|
||||
body.Res = &types.ImportVAppResponse{
|
||||
Returnval: ref,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
type VirtualApp struct {
|
||||
mo.VirtualApp
|
||||
}
|
||||
|
||||
func NewVAppConfigSpec() types.VAppConfigSpec {
|
||||
spec := types.VAppConfigSpec{
|
||||
Annotation: "vcsim",
|
||||
VmConfigSpec: types.VmConfigSpec{
|
||||
Product: []types.VAppProductSpec{
|
||||
{
|
||||
Info: &types.VAppProductInfo{
|
||||
Name: "vcsim",
|
||||
Vendor: "VMware",
|
||||
VendorUrl: "http://www.vmware.com/",
|
||||
Version: "0.1",
|
||||
},
|
||||
ArrayUpdateSpec: types.ArrayUpdateSpec{
|
||||
Operation: types.ArrayUpdateOperationAdd,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return spec
|
||||
}
|
||||
|
||||
func (p *ResourcePool) CreateVApp(req *types.CreateVApp) soap.HasFault {
|
||||
body := &methods.CreateVAppBody{}
|
||||
|
||||
pool, err := p.createChild(req.Name, req.ResSpec)
|
||||
if err != nil {
|
||||
body.Fault_ = err
|
||||
return body
|
||||
}
|
||||
|
||||
child := &VirtualApp{}
|
||||
child.ResourcePool = pool.ResourcePool
|
||||
child.Self.Type = "VirtualApp"
|
||||
child.ParentFolder = req.VmFolder
|
||||
|
||||
if child.ParentFolder == nil {
|
||||
folder := Map.getEntityDatacenter(p).VmFolder
|
||||
child.ParentFolder = &folder
|
||||
}
|
||||
|
||||
child.VAppConfig = &types.VAppConfigInfo{
|
||||
VmConfigInfo: types.VmConfigInfo{},
|
||||
Annotation: req.ConfigSpec.Annotation,
|
||||
}
|
||||
|
||||
for _, product := range req.ConfigSpec.Product {
|
||||
child.VAppConfig.Product = append(child.VAppConfig.Product, *product.Info)
|
||||
}
|
||||
|
||||
Map.PutEntity(p, Map.NewEntity(child))
|
||||
|
||||
p.ResourcePool.ResourcePool = append(p.ResourcePool.ResourcePool, child.Reference())
|
||||
|
||||
body.Res = &types.CreateVAppResponse{
|
||||
Returnval: child.Reference(),
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (a *VirtualApp) CreateChildVMTask(ctx *Context, req *types.CreateChildVM_Task) soap.HasFault {
|
||||
body := &methods.CreateChildVM_TaskBody{}
|
||||
|
||||
folder := Map.Get(*a.ParentFolder).(*Folder)
|
||||
|
||||
res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
|
||||
This: folder.Self,
|
||||
Config: req.Config,
|
||||
Host: req.Host,
|
||||
Pool: req.This,
|
||||
})
|
||||
|
||||
body.Res = &types.CreateChildVM_TaskResponse{
|
||||
Returnval: res.(*methods.CreateVM_TaskBody).Res.Returnval,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (a *VirtualApp) DestroyTask(req *types.Destroy_Task) soap.HasFault {
|
||||
return (&ResourcePool{ResourcePool: a.ResourcePool}).DestroyTask(req)
|
||||
}
|
||||
|
||||
func (p *ResourcePool) DestroyTask(req *types.Destroy_Task) soap.HasFault {
|
||||
task := CreateTask(p, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
if strings.HasSuffix(p.Parent.Type, "ComputeResource") {
|
||||
// Can't destroy the root pool
|
||||
return nil, &types.InvalidArgument{}
|
||||
}
|
||||
|
||||
parent, _ := asResourcePoolMO(Map.Get(*p.Parent))
|
||||
|
||||
// Remove child reference from rp
|
||||
Map.RemoveReference(parent, &parent.ResourcePool, req.This)
|
||||
|
||||
// The grandchildren become children of the parent (rp)
|
||||
Map.AppendReference(parent, &parent.ResourcePool, p.ResourcePool.ResourcePool...)
|
||||
|
||||
// And VMs move to the parent
|
||||
vms := p.ResourcePool.Vm
|
||||
for _, ref := range vms {
|
||||
vm := Map.Get(ref).(*VirtualMachine)
|
||||
Map.WithLock(vm, func() { vm.ResourcePool = &parent.Self })
|
||||
}
|
||||
|
||||
Map.AppendReference(parent, &parent.Vm, vms...)
|
||||
|
||||
Map.Remove(req.This)
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.Destroy_TaskBody{
|
||||
Res: &types.Destroy_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,256 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type SearchIndex struct {
|
||||
mo.SearchIndex
|
||||
}
|
||||
|
||||
func (s *SearchIndex) FindByDatastorePath(r *types.FindByDatastorePath) soap.HasFault {
|
||||
res := &methods.FindByDatastorePathBody{Res: new(types.FindByDatastorePathResponse)}
|
||||
|
||||
for ref, obj := range Map.objects {
|
||||
vm, ok := asVirtualMachineMO(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if vm.Config.Files.VmPathName == r.Path {
|
||||
res.Res.Returnval = &ref
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *SearchIndex) FindByInventoryPath(req *types.FindByInventoryPath) soap.HasFault {
|
||||
body := &methods.FindByInventoryPathBody{Res: new(types.FindByInventoryPathResponse)}
|
||||
|
||||
split := func(c rune) bool {
|
||||
return c == '/'
|
||||
}
|
||||
path := strings.FieldsFunc(req.InventoryPath, split)
|
||||
if len(path) < 1 {
|
||||
return body
|
||||
}
|
||||
|
||||
root := Map.content().RootFolder
|
||||
o := &root
|
||||
|
||||
for _, name := range path {
|
||||
f := s.FindChild(&types.FindChild{Entity: *o, Name: name})
|
||||
|
||||
o = f.(*methods.FindChildBody).Res.Returnval
|
||||
if o == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
body.Res.Returnval = o
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SearchIndex) FindChild(req *types.FindChild) soap.HasFault {
|
||||
body := &methods.FindChildBody{}
|
||||
|
||||
obj := Map.Get(req.Entity)
|
||||
|
||||
if obj == nil {
|
||||
body.Fault_ = Fault("", &types.ManagedObjectNotFound{Obj: req.Entity})
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = new(types.FindChildResponse)
|
||||
|
||||
var children []types.ManagedObjectReference
|
||||
|
||||
switch e := obj.(type) {
|
||||
case *Datacenter:
|
||||
children = []types.ManagedObjectReference{e.VmFolder, e.HostFolder, e.DatastoreFolder, e.NetworkFolder}
|
||||
case *Folder:
|
||||
children = e.ChildEntity
|
||||
case *mo.ComputeResource:
|
||||
children = e.Host
|
||||
children = append(children, *e.ResourcePool)
|
||||
case *ClusterComputeResource:
|
||||
children = e.Host
|
||||
children = append(children, *e.ResourcePool)
|
||||
case *ResourcePool:
|
||||
children = e.ResourcePool.ResourcePool
|
||||
children = append(children, e.Vm...)
|
||||
case *VirtualApp:
|
||||
children = e.ResourcePool.ResourcePool
|
||||
children = append(children, e.Vm...)
|
||||
}
|
||||
|
||||
match := Map.FindByName(req.Name, children)
|
||||
|
||||
if match != nil {
|
||||
ref := match.Reference()
|
||||
body.Res.Returnval = &ref
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SearchIndex) FindByUuid(req *types.FindByUuid) soap.HasFault {
|
||||
body := &methods.FindByUuidBody{Res: new(types.FindByUuidResponse)}
|
||||
|
||||
if req.VmSearch {
|
||||
// Find Virtual Machine using UUID
|
||||
for ref, obj := range Map.objects {
|
||||
vm, ok := asVirtualMachineMO(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if req.InstanceUuid != nil && *req.InstanceUuid {
|
||||
if vm.Config.InstanceUuid == req.Uuid {
|
||||
body.Res.Returnval = &ref
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if vm.Config.Uuid == req.Uuid {
|
||||
body.Res.Returnval = &ref
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Find Host System using UUID
|
||||
for ref, obj := range Map.objects {
|
||||
host, ok := asHostSystemMO(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if host.Summary.Hardware.Uuid == req.Uuid {
|
||||
body.Res.Returnval = &ref
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SearchIndex) FindByDnsName(req *types.FindByDnsName) soap.HasFault {
|
||||
body := &methods.FindByDnsNameBody{Res: new(types.FindByDnsNameResponse)}
|
||||
|
||||
all := types.FindAllByDnsName(*req)
|
||||
|
||||
switch r := s.FindAllByDnsName(&all).(type) {
|
||||
case *methods.FindAllByDnsNameBody:
|
||||
if len(r.Res.Returnval) > 0 {
|
||||
body.Res.Returnval = &r.Res.Returnval[0]
|
||||
}
|
||||
default:
|
||||
// no need until FindAllByDnsName below returns a Fault
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SearchIndex) FindAllByDnsName(req *types.FindAllByDnsName) soap.HasFault {
|
||||
body := &methods.FindAllByDnsNameBody{Res: new(types.FindAllByDnsNameResponse)}
|
||||
|
||||
if req.VmSearch {
|
||||
// Find Virtual Machine using DNS name
|
||||
for ref, obj := range Map.objects {
|
||||
vm, ok := asVirtualMachineMO(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if vm.Guest.HostName == req.DnsName {
|
||||
body.Res.Returnval = append(body.Res.Returnval, ref)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Find Host System using DNS name
|
||||
for ref, obj := range Map.objects {
|
||||
host, ok := asHostSystemMO(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, net := range host.Config.Network.NetStackInstance {
|
||||
if net.DnsConfig.GetHostDnsConfig().HostName == req.DnsName {
|
||||
body.Res.Returnval = append(body.Res.Returnval, ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SearchIndex) FindByIp(req *types.FindByIp) soap.HasFault {
|
||||
body := &methods.FindByIpBody{Res: new(types.FindByIpResponse)}
|
||||
|
||||
all := types.FindAllByIp(*req)
|
||||
|
||||
switch r := s.FindAllByIp(&all).(type) {
|
||||
case *methods.FindAllByIpBody:
|
||||
if len(r.Res.Returnval) > 0 {
|
||||
body.Res.Returnval = &r.Res.Returnval[0]
|
||||
}
|
||||
default:
|
||||
// no need until FindAllByIp below returns a Fault
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SearchIndex) FindAllByIp(req *types.FindAllByIp) soap.HasFault {
|
||||
body := &methods.FindAllByIpBody{Res: new(types.FindAllByIpResponse)}
|
||||
|
||||
if req.VmSearch {
|
||||
// Find Virtual Machine using IP
|
||||
for ref, obj := range Map.objects {
|
||||
vm, ok := asVirtualMachineMO(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if vm.Guest.IpAddress == req.Ip {
|
||||
body.Res.Returnval = append(body.Res.Returnval, ref)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Find Host System using IP
|
||||
for ref, obj := range Map.objects {
|
||||
host, ok := asHostSystemMO(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, net := range host.Config.Network.Vnic {
|
||||
if net.Spec.Ip.IpAddress == req.Ip {
|
||||
body.Res.Returnval = append(body.Res.Returnval, ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/vmware/govmomi/simulator/internal"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type ServiceInstance struct {
|
||||
mo.ServiceInstance
|
||||
}
|
||||
|
||||
func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *ServiceInstance {
|
||||
Map = NewRegistry()
|
||||
|
||||
s := &ServiceInstance{}
|
||||
|
||||
s.Self = vim25.ServiceInstance
|
||||
s.Content = content
|
||||
|
||||
Map.Put(s)
|
||||
|
||||
f := &Folder{Folder: folder}
|
||||
Map.Put(f)
|
||||
|
||||
if content.About.ApiType == "HostAgent" {
|
||||
CreateDefaultESX(internalContext, f)
|
||||
} else {
|
||||
content.About.InstanceUuid = uuid.New().String()
|
||||
}
|
||||
|
||||
refs := mo.References(content)
|
||||
|
||||
for i := range refs {
|
||||
if Map.Get(refs[i]) != nil {
|
||||
continue
|
||||
}
|
||||
content := types.ObjectContent{Obj: refs[i]}
|
||||
o, err := loadObject(content)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
Map.Put(o)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ServiceInstance) RetrieveServiceContent(*types.RetrieveServiceContent) soap.HasFault {
|
||||
return &methods.RetrieveServiceContentBody{
|
||||
Res: &types.RetrieveServiceContentResponse{
|
||||
Returnval: s.Content,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (*ServiceInstance) CurrentTime(*types.CurrentTime) soap.HasFault {
|
||||
return &methods.CurrentTimeBody{
|
||||
Res: &types.CurrentTimeResponse{
|
||||
Returnval: time.Now(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceInstance) RetrieveInternalContent(*internal.RetrieveInternalContent) soap.HasFault {
|
||||
return &internal.RetrieveInternalContentBody{
|
||||
Res: &internal.RetrieveInternalContentResponse{
|
||||
Returnval: internal.InternalServiceInstanceContent{
|
||||
NfcService: types.ManagedObjectReference{Type: "NfcService", Value: "NfcService"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,454 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/vmware/govmomi/session"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type SessionManager struct {
|
||||
mo.SessionManager
|
||||
nopLocker
|
||||
|
||||
ServiceHostName string
|
||||
TLSCert func() string
|
||||
|
||||
sessions map[string]Session
|
||||
}
|
||||
|
||||
func (m *SessionManager) init(*Registry) {
|
||||
m.sessions = make(map[string]Session)
|
||||
}
|
||||
|
||||
var (
|
||||
// SessionIdleTimeout duration used to expire idle sessions
|
||||
SessionIdleTimeout time.Duration
|
||||
|
||||
sessionMutex sync.Mutex
|
||||
)
|
||||
|
||||
func createSession(ctx *Context, name string, locale string) types.UserSession {
|
||||
now := time.Now().UTC()
|
||||
|
||||
if locale == "" {
|
||||
locale = session.Locale
|
||||
}
|
||||
|
||||
session := Session{
|
||||
UserSession: types.UserSession{
|
||||
Key: uuid.New().String(),
|
||||
UserName: name,
|
||||
FullName: name,
|
||||
LoginTime: now,
|
||||
LastActiveTime: now,
|
||||
Locale: locale,
|
||||
MessageLocale: locale,
|
||||
ExtensionSession: types.NewBool(false),
|
||||
},
|
||||
Registry: NewRegistry(),
|
||||
}
|
||||
|
||||
ctx.SetSession(session, true)
|
||||
|
||||
return ctx.Session.UserSession
|
||||
}
|
||||
|
||||
func (m *SessionManager) getSession(id string) (Session, bool) {
|
||||
sessionMutex.Lock()
|
||||
defer sessionMutex.Unlock()
|
||||
s, ok := m.sessions[id]
|
||||
return s, ok
|
||||
}
|
||||
|
||||
func (m *SessionManager) delSession(id string) {
|
||||
sessionMutex.Lock()
|
||||
defer sessionMutex.Unlock()
|
||||
delete(m.sessions, id)
|
||||
}
|
||||
|
||||
func (m *SessionManager) putSession(s Session) {
|
||||
sessionMutex.Lock()
|
||||
defer sessionMutex.Unlock()
|
||||
m.sessions[s.Key] = s
|
||||
}
|
||||
|
||||
func (s *SessionManager) validLogin(ctx *Context, req *types.Login) bool {
|
||||
if ctx.Session != nil {
|
||||
return false
|
||||
}
|
||||
user := ctx.svc.Listen.User
|
||||
if user == nil || user == DefaultLogin {
|
||||
return req.UserName != "" && req.Password != ""
|
||||
}
|
||||
pass, _ := user.Password()
|
||||
return req.UserName == user.Username() && req.Password == pass
|
||||
}
|
||||
|
||||
func (s *SessionManager) Login(ctx *Context, req *types.Login) soap.HasFault {
|
||||
body := new(methods.LoginBody)
|
||||
|
||||
if s.validLogin(ctx, req) {
|
||||
body.Res = &types.LoginResponse{
|
||||
Returnval: createSession(ctx, req.UserName, req.Locale),
|
||||
}
|
||||
} else {
|
||||
body.Fault_ = invalidLogin
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SessionManager) LoginExtensionByCertificate(ctx *Context, req *types.LoginExtensionByCertificate) soap.HasFault {
|
||||
body := new(methods.LoginExtensionByCertificateBody)
|
||||
|
||||
if ctx.req.TLS == nil || len(ctx.req.TLS.PeerCertificates) == 0 {
|
||||
body.Fault_ = Fault("", new(types.NoClientCertificate))
|
||||
return body
|
||||
}
|
||||
|
||||
if req.ExtensionKey == "" || ctx.Session != nil {
|
||||
body.Fault_ = invalidLogin
|
||||
} else {
|
||||
body.Res = &types.LoginExtensionByCertificateResponse{
|
||||
Returnval: createSession(ctx, req.ExtensionKey, req.Locale),
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SessionManager) LoginByToken(ctx *Context, req *types.LoginByToken) soap.HasFault {
|
||||
body := new(methods.LoginByTokenBody)
|
||||
|
||||
if ctx.Session != nil {
|
||||
body.Fault_ = invalidLogin
|
||||
} else {
|
||||
var subject struct {
|
||||
ID string `xml:"Assertion>Subject>NameID"`
|
||||
}
|
||||
|
||||
if s, ok := ctx.Header.Security.(*Element); ok {
|
||||
_ = s.Decode(&subject)
|
||||
}
|
||||
|
||||
if subject.ID == "" {
|
||||
body.Fault_ = invalidLogin
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.LoginByTokenResponse{
|
||||
Returnval: createSession(ctx, subject.ID, req.Locale),
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SessionManager) Logout(ctx *Context, _ *types.Logout) soap.HasFault {
|
||||
session := ctx.Session
|
||||
s.delSession(session.Key)
|
||||
pc := Map.content().PropertyCollector
|
||||
|
||||
for ref, obj := range ctx.Session.Registry.objects {
|
||||
if ref == pc {
|
||||
continue // don't unregister the PropertyCollector singleton
|
||||
}
|
||||
if _, ok := obj.(RegisterObject); ok {
|
||||
ctx.Map.Remove(ref) // Remove RegisterObject handlers
|
||||
}
|
||||
}
|
||||
|
||||
ctx.postEvent(&types.UserLogoutSessionEvent{
|
||||
IpAddress: session.IpAddress,
|
||||
UserAgent: session.UserAgent,
|
||||
SessionId: session.Key,
|
||||
LoginTime: &session.LoginTime,
|
||||
})
|
||||
|
||||
return &methods.LogoutBody{Res: new(types.LogoutResponse)}
|
||||
}
|
||||
|
||||
func (s *SessionManager) TerminateSession(ctx *Context, req *types.TerminateSession) soap.HasFault {
|
||||
body := new(methods.TerminateSessionBody)
|
||||
|
||||
for _, id := range req.SessionId {
|
||||
if id == ctx.Session.Key {
|
||||
body.Fault_ = Fault("", new(types.InvalidArgument))
|
||||
return body
|
||||
}
|
||||
if _, ok := s.getSession(id); !ok {
|
||||
body.Fault_ = Fault("", new(types.NotFound))
|
||||
return body
|
||||
}
|
||||
s.delSession(id)
|
||||
}
|
||||
|
||||
body.Res = new(types.TerminateSessionResponse)
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SessionManager) SessionIsActive(ctx *Context, req *types.SessionIsActive) soap.HasFault {
|
||||
body := new(methods.SessionIsActiveBody)
|
||||
|
||||
if ctx.Map.IsESX() {
|
||||
body.Fault_ = Fault("", new(types.NotImplemented))
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = new(types.SessionIsActiveResponse)
|
||||
|
||||
if session, exists := s.getSession(req.SessionID); exists {
|
||||
body.Res.Returnval = session.UserName == req.UserName
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SessionManager) AcquireCloneTicket(ctx *Context, _ *types.AcquireCloneTicket) soap.HasFault {
|
||||
session := *ctx.Session
|
||||
session.Key = uuid.New().String()
|
||||
s.putSession(session)
|
||||
|
||||
return &methods.AcquireCloneTicketBody{
|
||||
Res: &types.AcquireCloneTicketResponse{
|
||||
Returnval: session.Key,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SessionManager) CloneSession(ctx *Context, ticket *types.CloneSession) soap.HasFault {
|
||||
body := new(methods.CloneSessionBody)
|
||||
|
||||
session, exists := s.getSession(ticket.CloneTicket)
|
||||
|
||||
if exists {
|
||||
s.delSession(ticket.CloneTicket) // A clone ticket can only be used once
|
||||
session.Key = uuid.New().String()
|
||||
ctx.SetSession(session, true)
|
||||
|
||||
body.Res = &types.CloneSessionResponse{
|
||||
Returnval: session.UserSession,
|
||||
}
|
||||
} else {
|
||||
body.Fault_ = invalidLogin
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGenericServiceTicket) soap.HasFault {
|
||||
return &methods.AcquireGenericServiceTicketBody{
|
||||
Res: &types.AcquireGenericServiceTicketResponse{
|
||||
Returnval: types.SessionManagerGenericServiceTicket{
|
||||
Id: uuid.New().String(),
|
||||
HostName: s.ServiceHostName,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// internalContext is the session for use by the in-memory client (Service.RoundTrip)
|
||||
var internalContext = &Context{
|
||||
Context: context.Background(),
|
||||
Session: &Session{
|
||||
UserSession: types.UserSession{
|
||||
Key: uuid.New().String(),
|
||||
},
|
||||
Registry: NewRegistry(),
|
||||
},
|
||||
Map: Map,
|
||||
}
|
||||
|
||||
var invalidLogin = Fault("Login failure", new(types.InvalidLogin))
|
||||
|
||||
// Context provides per-request Session management.
|
||||
type Context struct {
|
||||
req *http.Request
|
||||
res http.ResponseWriter
|
||||
svc *Service
|
||||
|
||||
context.Context
|
||||
Session *Session
|
||||
Header soap.Header
|
||||
Caller *types.ManagedObjectReference
|
||||
Map *Registry
|
||||
}
|
||||
|
||||
// mapSession maps an HTTP cookie to a Session.
|
||||
func (c *Context) mapSession() {
|
||||
if cookie, err := c.req.Cookie(soap.SessionCookieName); err == nil {
|
||||
if val, ok := c.svc.sm.getSession(cookie.Value); ok {
|
||||
c.SetSession(val, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SessionManager) expiredSession(id string, now time.Time) bool {
|
||||
expired := true
|
||||
|
||||
s, ok := m.getSession(id)
|
||||
if ok {
|
||||
expired = now.Sub(s.LastActiveTime) > SessionIdleTimeout
|
||||
if expired {
|
||||
m.delSession(id)
|
||||
}
|
||||
}
|
||||
|
||||
return expired
|
||||
}
|
||||
|
||||
// SessionIdleWatch starts a goroutine that calls func expired() at SessionIdleTimeout intervals.
|
||||
// The goroutine exits if the func returns true.
|
||||
func SessionIdleWatch(ctx context.Context, id string, expired func(string, time.Time) bool) {
|
||||
if SessionIdleTimeout == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for t := time.NewTimer(SessionIdleTimeout); ; {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case now := <-t.C:
|
||||
if expired(id, now) {
|
||||
return
|
||||
}
|
||||
t.Reset(SessionIdleTimeout)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// SetSession should be called after successful authentication.
|
||||
func (c *Context) SetSession(session Session, login bool) {
|
||||
session.UserAgent = c.req.UserAgent()
|
||||
session.IpAddress = strings.Split(c.req.RemoteAddr, ":")[0]
|
||||
session.LastActiveTime = time.Now()
|
||||
session.CallCount++
|
||||
|
||||
c.svc.sm.putSession(session)
|
||||
c.Session = &session
|
||||
|
||||
if login {
|
||||
http.SetCookie(c.res, &http.Cookie{
|
||||
Name: soap.SessionCookieName,
|
||||
Value: session.Key,
|
||||
})
|
||||
|
||||
c.postEvent(&types.UserLoginSessionEvent{
|
||||
SessionId: session.Key,
|
||||
IpAddress: session.IpAddress,
|
||||
UserAgent: session.UserAgent,
|
||||
Locale: session.Locale,
|
||||
})
|
||||
|
||||
SessionIdleWatch(c.Context, session.Key, c.svc.sm.expiredSession)
|
||||
}
|
||||
}
|
||||
|
||||
// WithLock holds a lock for the given object while then given function is run.
|
||||
func (c *Context) WithLock(obj mo.Reference, f func()) {
|
||||
if c.Caller != nil && *c.Caller == obj.Reference() {
|
||||
// Internal method invocation, obj is already locked
|
||||
f()
|
||||
return
|
||||
}
|
||||
Map.WithLock(obj, f)
|
||||
}
|
||||
|
||||
// postEvent wraps EventManager.PostEvent for internal use, with a lock on the EventManager.
|
||||
func (c *Context) postEvent(events ...types.BaseEvent) {
|
||||
m := Map.EventManager()
|
||||
c.WithLock(m, func() {
|
||||
for _, event := range events {
|
||||
m.PostEvent(c, &types.PostEvent{EventToPost: event})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Session combines a UserSession and a Registry for per-session managed objects.
|
||||
type Session struct {
|
||||
types.UserSession
|
||||
*Registry
|
||||
}
|
||||
|
||||
func (s *Session) setReference(item mo.Reference) {
|
||||
ref := item.Reference()
|
||||
if ref.Value == "" {
|
||||
ref.Value = fmt.Sprintf("session[%s]%s", s.Key, uuid.New())
|
||||
}
|
||||
if ref.Type == "" {
|
||||
ref.Type = typeName(item)
|
||||
}
|
||||
s.Registry.setReference(item, ref)
|
||||
}
|
||||
|
||||
// Put wraps Registry.Put, setting the moref value to include the session key.
|
||||
func (s *Session) Put(item mo.Reference) mo.Reference {
|
||||
s.setReference(item)
|
||||
return s.Registry.Put(item)
|
||||
}
|
||||
|
||||
// Get wraps Registry.Get, session-izing singleton objects such as SessionManager and the root PropertyCollector.
|
||||
func (s *Session) Get(ref types.ManagedObjectReference) mo.Reference {
|
||||
obj := s.Registry.Get(ref)
|
||||
if obj != nil {
|
||||
return obj
|
||||
}
|
||||
|
||||
// Return a session "view" of certain singleton objects
|
||||
switch ref.Type {
|
||||
case "SessionManager":
|
||||
// Clone SessionManager so the PropertyCollector can properly report CurrentSession
|
||||
m := *Map.SessionManager()
|
||||
m.CurrentSession = &s.UserSession
|
||||
|
||||
// TODO: we could maintain SessionList as part of the SessionManager singleton
|
||||
sessionMutex.Lock()
|
||||
for _, session := range m.sessions {
|
||||
m.SessionList = append(m.SessionList, session.UserSession)
|
||||
}
|
||||
sessionMutex.Unlock()
|
||||
|
||||
return &m
|
||||
case "PropertyCollector":
|
||||
if ref == Map.content().PropertyCollector {
|
||||
// Per-session instance of the PropertyCollector singleton.
|
||||
// Using reflection here as PropertyCollector might be wrapped with a custom type.
|
||||
obj = Map.Get(ref)
|
||||
pc := reflect.New(reflect.TypeOf(obj).Elem())
|
||||
obj = pc.Interface().(mo.Reference)
|
||||
s.Registry.setReference(obj, ref)
|
||||
return s.Put(obj)
|
||||
}
|
||||
}
|
||||
|
||||
return Map.Get(ref)
|
||||
}
|
|
@ -1,920 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/find"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/simulator/internal"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"github.com/vmware/govmomi/vim25/xml"
|
||||
)
|
||||
|
||||
var (
|
||||
// Trace when set to true, writes SOAP traffic to stderr
|
||||
Trace = false
|
||||
|
||||
// TraceFile is the output file when Trace = true
|
||||
TraceFile = os.Stderr
|
||||
|
||||
// DefaultLogin for authentication
|
||||
DefaultLogin = url.UserPassword("user", "pass")
|
||||
)
|
||||
|
||||
// Method encapsulates a decoded SOAP client request
|
||||
type Method struct {
|
||||
Name string
|
||||
This types.ManagedObjectReference
|
||||
Header soap.Header
|
||||
Body types.AnyType
|
||||
}
|
||||
|
||||
// Service decodes incoming requests and dispatches to a Handler
|
||||
type Service struct {
|
||||
client *vim25.Client
|
||||
sm *SessionManager
|
||||
sdk map[string]*Registry
|
||||
funcs []handleFunc
|
||||
delay *DelayConfig
|
||||
|
||||
readAll func(io.Reader) ([]byte, error)
|
||||
|
||||
Listen *url.URL
|
||||
TLS *tls.Config
|
||||
ServeMux *http.ServeMux
|
||||
// RegisterEndpoints will initialize any endpoints added via RegisterEndpoint
|
||||
RegisterEndpoints bool
|
||||
}
|
||||
|
||||
// Server provides a simulator Service over HTTP
|
||||
type Server struct {
|
||||
*internal.Server
|
||||
URL *url.URL
|
||||
Tunnel int
|
||||
|
||||
caFile string
|
||||
}
|
||||
|
||||
// New returns an initialized simulator Service instance
|
||||
func New(instance *ServiceInstance) *Service {
|
||||
s := &Service{
|
||||
readAll: ioutil.ReadAll,
|
||||
sm: Map.SessionManager(),
|
||||
sdk: make(map[string]*Registry),
|
||||
}
|
||||
|
||||
s.client, _ = vim25.NewClient(context.Background(), s)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type serverFaultBody struct {
|
||||
Reason *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
|
||||
}
|
||||
|
||||
func (b *serverFaultBody) Fault() *soap.Fault { return b.Reason }
|
||||
|
||||
func serverFault(msg string) soap.HasFault {
|
||||
return &serverFaultBody{Reason: Fault(msg, &types.InvalidRequest{})}
|
||||
}
|
||||
|
||||
// Fault wraps the given message and fault in a soap.Fault
|
||||
func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
|
||||
f := &soap.Fault{
|
||||
Code: "ServerFaultCode",
|
||||
String: msg,
|
||||
}
|
||||
|
||||
f.Detail.Fault = fault
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
|
||||
handler := ctx.Map.Get(method.This)
|
||||
session := ctx.Session
|
||||
ctx.Caller = &method.This
|
||||
|
||||
if session == nil {
|
||||
switch method.Name {
|
||||
case "RetrieveServiceContent", "PbmRetrieveServiceContent", "Fetch", "List", "Login", "LoginByToken", "LoginExtensionByCertificate", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
|
||||
// ok for now, TODO: authz
|
||||
default:
|
||||
fault := &types.NotAuthenticated{
|
||||
NoPermission: types.NoPermission{
|
||||
Object: method.This,
|
||||
PrivilegeId: "System.View",
|
||||
},
|
||||
}
|
||||
return &serverFaultBody{Reason: Fault("", fault)}
|
||||
}
|
||||
} else {
|
||||
// Prefer the Session.Registry, ServiceContent.PropertyCollector filter field for example is per-session
|
||||
if h := session.Get(method.This); h != nil {
|
||||
handler = h
|
||||
}
|
||||
}
|
||||
|
||||
if handler == nil {
|
||||
msg := fmt.Sprintf("managed object not found: %s", method.This)
|
||||
log.Print(msg)
|
||||
fault := &types.ManagedObjectNotFound{Obj: method.This}
|
||||
return &serverFaultBody{Reason: Fault(msg, fault)}
|
||||
}
|
||||
|
||||
// Lowercase methods can't be accessed outside their package
|
||||
name := strings.Title(method.Name)
|
||||
|
||||
if strings.HasSuffix(name, vTaskSuffix) {
|
||||
// Make golint happy renaming "Foo_Task" -> "FooTask"
|
||||
name = name[:len(name)-len(vTaskSuffix)] + sTaskSuffix
|
||||
}
|
||||
|
||||
m := reflect.ValueOf(handler).MethodByName(name)
|
||||
if !m.IsValid() {
|
||||
msg := fmt.Sprintf("%s does not implement: %s", method.This, method.Name)
|
||||
log.Print(msg)
|
||||
fault := &types.MethodNotFound{Receiver: method.This, Method: method.Name}
|
||||
return &serverFaultBody{Reason: Fault(msg, fault)}
|
||||
}
|
||||
|
||||
if e, ok := handler.(mo.Entity); ok {
|
||||
for _, dm := range e.Entity().DisabledMethod {
|
||||
if name == dm {
|
||||
msg := fmt.Sprintf("%s method is disabled: %s", method.This, method.Name)
|
||||
fault := &types.MethodDisabled{}
|
||||
return &serverFaultBody{Reason: Fault(msg, fault)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We have a valid call. Introduce a delay if requested
|
||||
//
|
||||
if s.delay != nil {
|
||||
d := 0
|
||||
if s.delay.Delay > 0 {
|
||||
d = s.delay.Delay
|
||||
}
|
||||
if md, ok := s.delay.MethodDelay[method.Name]; ok {
|
||||
d += md
|
||||
}
|
||||
if s.delay.DelayJitter > 0 {
|
||||
d += int(rand.NormFloat64() * s.delay.DelayJitter * float64(d))
|
||||
}
|
||||
if d > 0 {
|
||||
//fmt.Printf("Delaying method %s %d ms\n", name, d)
|
||||
time.Sleep(time.Duration(d) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
var args, res []reflect.Value
|
||||
if m.Type().NumIn() == 2 {
|
||||
args = append(args, reflect.ValueOf(ctx))
|
||||
}
|
||||
args = append(args, reflect.ValueOf(method.Body))
|
||||
ctx.Map.WithLock(handler, func() {
|
||||
res = m.Call(args)
|
||||
})
|
||||
|
||||
return res[0].Interface().(soap.HasFault)
|
||||
}
|
||||
|
||||
// RoundTrip implements the soap.RoundTripper interface in process.
|
||||
// Rather than encode/decode SOAP over HTTP, this implementation uses reflection.
|
||||
func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault) error {
|
||||
field := func(r soap.HasFault, name string) reflect.Value {
|
||||
return reflect.ValueOf(r).Elem().FieldByName(name)
|
||||
}
|
||||
|
||||
// Every struct passed to soap.RoundTrip has "Req" and "Res" fields
|
||||
req := field(request, "Req")
|
||||
|
||||
// Every request has a "This" field.
|
||||
this := req.Elem().FieldByName("This")
|
||||
|
||||
method := &Method{
|
||||
Name: req.Elem().Type().Name(),
|
||||
This: this.Interface().(types.ManagedObjectReference),
|
||||
Body: req.Interface(),
|
||||
}
|
||||
|
||||
res := s.call(&Context{
|
||||
Map: Map,
|
||||
Context: ctx,
|
||||
Session: internalContext.Session,
|
||||
}, method)
|
||||
|
||||
if err := res.Fault(); err != nil {
|
||||
return soap.WrapSoapFault(err)
|
||||
}
|
||||
|
||||
field(response, "Res").Set(field(res, "Res"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// soapEnvelope is a copy of soap.Envelope, with namespace changed to "soapenv",
|
||||
// and additional namespace attributes required by some client libraries.
|
||||
// Go still has issues decoding with such a namespace, but encoding is ok.
|
||||
type soapEnvelope struct {
|
||||
XMLName xml.Name `xml:"soapenv:Envelope"`
|
||||
Enc string `xml:"xmlns:soapenc,attr"`
|
||||
Env string `xml:"xmlns:soapenv,attr"`
|
||||
XSD string `xml:"xmlns:xsd,attr"`
|
||||
XSI string `xml:"xmlns:xsi,attr"`
|
||||
Body interface{} `xml:"soapenv:Body"`
|
||||
}
|
||||
|
||||
type faultDetail struct {
|
||||
Fault types.AnyType
|
||||
}
|
||||
|
||||
// soapFault is a copy of soap.Fault, with the same changes as soapEnvelope
|
||||
type soapFault struct {
|
||||
XMLName xml.Name `xml:"soapenv:Fault"`
|
||||
Code string `xml:"faultcode"`
|
||||
String string `xml:"faultstring"`
|
||||
Detail struct {
|
||||
Fault *faultDetail
|
||||
} `xml:"detail"`
|
||||
}
|
||||
|
||||
// MarshalXML renames the start element from "Fault" to "${Type}Fault"
|
||||
func (d *faultDetail) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
kind := reflect.TypeOf(d.Fault).Elem().Name()
|
||||
start.Name.Local = kind + "Fault"
|
||||
start.Attr = append(start.Attr,
|
||||
xml.Attr{
|
||||
Name: xml.Name{Local: "xmlns"},
|
||||
Value: "urn:" + vim25.Namespace,
|
||||
},
|
||||
xml.Attr{
|
||||
Name: xml.Name{Local: "xsi:type"},
|
||||
Value: kind,
|
||||
})
|
||||
return e.EncodeElement(d.Fault, start)
|
||||
}
|
||||
|
||||
// response sets xml.Name.Space when encoding Body.
|
||||
// Note that namespace is intentionally omitted in the vim25/methods/methods.go Body.Res field tags.
|
||||
type response struct {
|
||||
Namespace string
|
||||
Body soap.HasFault
|
||||
}
|
||||
|
||||
func (r *response) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
val := reflect.ValueOf(r.Body).Elem().FieldByName("Res")
|
||||
if !val.IsValid() {
|
||||
return fmt.Errorf("%T: invalid response type (missing 'Res' field)", r.Body)
|
||||
}
|
||||
if val.IsNil() {
|
||||
return fmt.Errorf("%T: invalid response (nil 'Res' field)", r.Body)
|
||||
}
|
||||
res := xml.StartElement{
|
||||
Name: xml.Name{
|
||||
Space: "urn:" + r.Namespace,
|
||||
Local: val.Elem().Type().Name(),
|
||||
},
|
||||
}
|
||||
if err := e.EncodeToken(start); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := e.EncodeElement(val.Interface(), res); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.EncodeToken(start.End())
|
||||
}
|
||||
|
||||
// About generates some info about the simulator.
|
||||
func (s *Service) About(w http.ResponseWriter, r *http.Request) {
|
||||
var about struct {
|
||||
Methods []string
|
||||
Types []string
|
||||
}
|
||||
|
||||
seen := make(map[string]bool)
|
||||
|
||||
f := reflect.TypeOf((*soap.HasFault)(nil)).Elem()
|
||||
|
||||
for _, obj := range Map.objects {
|
||||
kind := obj.Reference().Type
|
||||
if seen[kind] {
|
||||
continue
|
||||
}
|
||||
seen[kind] = true
|
||||
|
||||
about.Types = append(about.Types, kind)
|
||||
|
||||
t := reflect.TypeOf(obj)
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
m := t.Method(i)
|
||||
if seen[m.Name] {
|
||||
continue
|
||||
}
|
||||
seen[m.Name] = true
|
||||
|
||||
in := m.Type.NumIn()
|
||||
if in < 2 || in > 3 { // at least 2 params (receiver and request), optionally a 3rd param (context)
|
||||
continue
|
||||
}
|
||||
if m.Type.NumOut() != 1 || m.Type.Out(0) != f { // all methods return soap.HasFault
|
||||
continue
|
||||
}
|
||||
|
||||
about.Methods = append(about.Methods, strings.Replace(m.Name, "Task", "_Task", 1))
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(about.Methods)
|
||||
sort.Strings(about.Types)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
_ = enc.Encode(&about)
|
||||
}
|
||||
|
||||
var endpoints []func(*Service, *Registry)
|
||||
|
||||
// RegisterEndpoint funcs are called after the Server is initialized if Service.RegisterEndpoints=true.
|
||||
// Such a func would typically register a SOAP endpoint via Service.RegisterSDK or REST endpoint via Service.Handle
|
||||
func RegisterEndpoint(endpoint func(*Service, *Registry)) {
|
||||
endpoints = append(endpoints, endpoint)
|
||||
}
|
||||
|
||||
// Handle registers the handler for the given pattern with Service.ServeMux.
|
||||
func (s *Service) Handle(pattern string, handler http.Handler) {
|
||||
s.ServeMux.Handle(pattern, handler)
|
||||
// Not ideal, but avoids having to add yet another registration mechanism
|
||||
// so we can optionally use vapi/simulator internally.
|
||||
if m, ok := handler.(tagManager); ok {
|
||||
s.sdk[vim25.Path].tagManager = m
|
||||
}
|
||||
}
|
||||
|
||||
type muxHandleFunc interface {
|
||||
HandleFunc(string, func(http.ResponseWriter, *http.Request))
|
||||
}
|
||||
|
||||
type handleFunc struct {
|
||||
pattern string
|
||||
handler func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
// HandleFunc dispatches to http.ServeMux.HandleFunc after all endpoints have been registered.
|
||||
// This allows dispatching to an endpoint's HandleFunc impl, such as vapi/simulator for example.
|
||||
func (s *Service) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
||||
s.funcs = append(s.funcs, handleFunc{pattern, handler})
|
||||
}
|
||||
|
||||
// RegisterSDK adds an HTTP handler for the Registry's Path and Namespace.
|
||||
func (s *Service) RegisterSDK(r *Registry) {
|
||||
if s.ServeMux == nil {
|
||||
s.ServeMux = http.NewServeMux()
|
||||
}
|
||||
|
||||
s.sdk[r.Path] = r
|
||||
s.ServeMux.HandleFunc(r.Path, s.ServeSDK)
|
||||
}
|
||||
|
||||
// StatusSDK can be used to simulate an /sdk HTTP response code other than 200.
|
||||
// The value of StatusSDK is restored to http.StatusOK after 1 response.
|
||||
// This can be useful to test vim25.Retry() for example.
|
||||
var StatusSDK = http.StatusOK
|
||||
|
||||
// ServeSDK implements the http.Handler interface
|
||||
func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if StatusSDK != http.StatusOK {
|
||||
w.WriteHeader(StatusSDK)
|
||||
StatusSDK = http.StatusOK // reset
|
||||
return
|
||||
}
|
||||
|
||||
body, err := s.readAll(r.Body)
|
||||
_ = r.Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("error reading body: %s", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if Trace {
|
||||
fmt.Fprintf(TraceFile, "Request: %s\n", string(body))
|
||||
}
|
||||
|
||||
ctx := &Context{
|
||||
req: r,
|
||||
res: w,
|
||||
svc: s,
|
||||
|
||||
Map: s.sdk[r.URL.Path],
|
||||
Context: context.Background(),
|
||||
}
|
||||
ctx.Map.WithLock(s.sm, ctx.mapSession)
|
||||
|
||||
var res soap.HasFault
|
||||
var soapBody interface{}
|
||||
|
||||
method, err := UnmarshalBody(ctx.Map.typeFunc, body)
|
||||
if err != nil {
|
||||
res = serverFault(err.Error())
|
||||
} else {
|
||||
ctx.Header = method.Header
|
||||
if method.Name == "Fetch" {
|
||||
// Redirect any Fetch method calls to the PropertyCollector singleton
|
||||
method.This = ctx.Map.content().PropertyCollector
|
||||
}
|
||||
res = s.call(ctx, method)
|
||||
}
|
||||
|
||||
if f := res.Fault(); f != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
// the generated method/*Body structs use the '*soap.Fault' type,
|
||||
// so we need our own Body type to use the modified '*soapFault' type.
|
||||
soapBody = struct {
|
||||
Fault *soapFault
|
||||
}{
|
||||
&soapFault{
|
||||
Code: f.Code,
|
||||
String: f.String,
|
||||
Detail: struct {
|
||||
Fault *faultDetail
|
||||
}{&faultDetail{f.Detail.Fault}},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
soapBody = &response{ctx.Map.Namespace, res}
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
fmt.Fprint(&out, xml.Header)
|
||||
e := xml.NewEncoder(&out)
|
||||
err = e.Encode(&soapEnvelope{
|
||||
Enc: "http://schemas.xmlsoap.org/soap/encoding/",
|
||||
Env: "http://schemas.xmlsoap.org/soap/envelope/",
|
||||
XSD: "http://www.w3.org/2001/XMLSchema",
|
||||
XSI: "http://www.w3.org/2001/XMLSchema-instance",
|
||||
Body: soapBody,
|
||||
})
|
||||
if err == nil {
|
||||
err = e.Flush()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("error encoding %s response: %s", method.Name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if Trace {
|
||||
fmt.Fprintf(TraceFile, "Response: %s\n", out.String())
|
||||
}
|
||||
|
||||
_, _ = w.Write(out.Bytes())
|
||||
}
|
||||
|
||||
func (s *Service) findDatastore(query url.Values) (*Datastore, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
finder := find.NewFinder(s.client, false)
|
||||
dc, err := finder.DatacenterOrDefault(ctx, query.Get("dcPath"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
finder.SetDatacenter(dc)
|
||||
|
||||
ds, err := finder.DatastoreOrDefault(ctx, query.Get("dsName"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Map.Get(ds.Reference()).(*Datastore), nil
|
||||
}
|
||||
|
||||
const folderPrefix = "/folder/"
|
||||
|
||||
// ServeDatastore handler for Datastore access via /folder path.
|
||||
func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
|
||||
ds, ferr := s.findDatastore(r.URL.Query())
|
||||
if ferr != nil {
|
||||
log.Printf("failed to locate datastore with query params: %s", r.URL.RawQuery)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
r.URL.Path = strings.TrimPrefix(r.URL.Path, folderPrefix)
|
||||
p := path.Join(ds.Info.GetDatastoreInfo().Url, r.URL.Path)
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
_, err := os.Stat(p)
|
||||
if err == nil {
|
||||
// File exists
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
return
|
||||
}
|
||||
|
||||
// File does not exist, fallthrough to create via PUT logic
|
||||
fallthrough
|
||||
case http.MethodPut:
|
||||
dir := path.Dir(p)
|
||||
_ = os.MkdirAll(dir, 0700)
|
||||
|
||||
f, err := os.Create(p)
|
||||
if err != nil {
|
||||
log.Printf("failed to %s '%s': %s", r.Method, p, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, _ = io.Copy(f, r.Body)
|
||||
default:
|
||||
fs := http.FileServer(http.Dir(ds.Info.GetDatastoreInfo().Url))
|
||||
|
||||
fs.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceVersions handler for the /sdk/vimServiceVersions.xml path.
|
||||
func (s *Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
|
||||
const versions = xml.Header + `<namespaces version="1.0">
|
||||
<namespace>
|
||||
<name>urn:vim25</name>
|
||||
<version>%s</version>
|
||||
<priorVersions>
|
||||
<version>6.0</version>
|
||||
<version>5.5</version>
|
||||
</priorVersions>
|
||||
</namespace>
|
||||
</namespaces>
|
||||
`
|
||||
fmt.Fprintf(w, versions, s.client.ServiceContent.About.ApiVersion)
|
||||
}
|
||||
|
||||
// defaultIP returns addr.IP if specified, otherwise attempts to find a non-loopback ipv4 IP
|
||||
func defaultIP(addr *net.TCPAddr) string {
|
||||
if !addr.IP.IsUnspecified() {
|
||||
return addr.IP.String()
|
||||
}
|
||||
|
||||
nics, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return addr.IP.String()
|
||||
}
|
||||
|
||||
for _, nic := range nics {
|
||||
if nic.Name == "docker0" || strings.HasPrefix(nic.Name, "vmnet") {
|
||||
continue
|
||||
}
|
||||
addrs, aerr := nic.Addrs()
|
||||
if aerr != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() {
|
||||
if ip.IP.To4() != nil {
|
||||
return ip.IP.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return addr.IP.String()
|
||||
}
|
||||
|
||||
// NewServer returns an http Server instance for the given service
|
||||
func (s *Service) NewServer() *Server {
|
||||
s.RegisterSDK(Map)
|
||||
|
||||
mux := s.ServeMux
|
||||
vim := Map.Path + "/vimService"
|
||||
s.sdk[vim] = s.sdk[vim25.Path]
|
||||
mux.HandleFunc(vim, s.ServeSDK)
|
||||
mux.HandleFunc(Map.Path+"/vimServiceVersions.xml", s.ServiceVersions)
|
||||
mux.HandleFunc(folderPrefix, s.ServeDatastore)
|
||||
mux.HandleFunc(guestPrefix, ServeGuest)
|
||||
mux.HandleFunc(nfcPrefix, ServeNFC)
|
||||
mux.HandleFunc("/about", s.About)
|
||||
|
||||
if s.Listen == nil {
|
||||
s.Listen = new(url.URL)
|
||||
}
|
||||
ts := internal.NewUnstartedServer(mux, s.Listen.Host)
|
||||
addr := ts.Listener.Addr().(*net.TCPAddr)
|
||||
port := strconv.Itoa(addr.Port)
|
||||
u := &url.URL{
|
||||
Scheme: "http",
|
||||
Host: net.JoinHostPort(defaultIP(addr), port),
|
||||
Path: Map.Path,
|
||||
}
|
||||
if s.TLS != nil {
|
||||
u.Scheme += "s"
|
||||
}
|
||||
|
||||
// Redirect clients to this http server, rather than HostSystem.Name
|
||||
Map.SessionManager().ServiceHostName = u.Host
|
||||
|
||||
// Add vcsim config to OptionManager for use by SDK handlers (see lookup/simulator for example)
|
||||
m := Map.OptionManager()
|
||||
for i := range m.Setting {
|
||||
setting := m.Setting[i].GetOptionValue()
|
||||
|
||||
if strings.HasSuffix(setting.Key, ".uri") {
|
||||
// Rewrite any URIs with vcsim's host:port
|
||||
endpoint, err := url.Parse(setting.Value.(string))
|
||||
if err == nil {
|
||||
endpoint.Scheme = u.Scheme
|
||||
endpoint.Host = u.Host
|
||||
setting.Value = endpoint.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
m.Setting = append(m.Setting,
|
||||
&types.OptionValue{
|
||||
Key: "vcsim.server.url",
|
||||
Value: u.String(),
|
||||
},
|
||||
)
|
||||
|
||||
u.User = s.Listen.User
|
||||
if u.User == nil {
|
||||
u.User = DefaultLogin
|
||||
}
|
||||
s.Listen = u
|
||||
|
||||
if s.RegisterEndpoints {
|
||||
for i := range endpoints {
|
||||
endpoints[i](s, Map)
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range s.funcs {
|
||||
pattern := &url.URL{Path: f.pattern}
|
||||
endpoint, _ := s.ServeMux.Handler(&http.Request{URL: pattern})
|
||||
|
||||
if mux, ok := endpoint.(muxHandleFunc); ok {
|
||||
mux.HandleFunc(f.pattern, f.handler) // e.g. vapi/simulator
|
||||
} else {
|
||||
s.ServeMux.HandleFunc(f.pattern, f.handler)
|
||||
}
|
||||
}
|
||||
|
||||
if s.TLS != nil {
|
||||
ts.TLS = s.TLS
|
||||
ts.TLS.ClientAuth = tls.RequestClientCert // Used by SessionManager.LoginExtensionByCertificate
|
||||
Map.SessionManager().TLSCert = func() string {
|
||||
return base64.StdEncoding.EncodeToString(ts.TLS.Certificates[0].Certificate[0])
|
||||
}
|
||||
ts.StartTLS()
|
||||
} else {
|
||||
ts.Start()
|
||||
}
|
||||
|
||||
return &Server{
|
||||
Server: ts,
|
||||
URL: u,
|
||||
}
|
||||
}
|
||||
|
||||
// Certificate returns the TLS certificate for the Server if started with TLS enabled.
|
||||
// This method will panic if TLS is not enabled for the server.
|
||||
func (s *Server) Certificate() *x509.Certificate {
|
||||
// By default httptest.StartTLS uses http/internal.LocalhostCert, which we can access here:
|
||||
cert, _ := x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0])
|
||||
return cert
|
||||
}
|
||||
|
||||
// CertificateInfo returns Server.Certificate() as object.HostCertificateInfo
|
||||
func (s *Server) CertificateInfo() *object.HostCertificateInfo {
|
||||
info := new(object.HostCertificateInfo)
|
||||
info.FromCertificate(s.Certificate())
|
||||
return info
|
||||
}
|
||||
|
||||
// CertificateFile returns a file name, where the file contains the PEM encoded Server.Certificate.
|
||||
// The temporary file is removed when Server.Close() is called.
|
||||
func (s *Server) CertificateFile() (string, error) {
|
||||
if s.caFile != "" {
|
||||
return s.caFile, nil
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "vcsim-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s.caFile = f.Name()
|
||||
cert := s.Certificate()
|
||||
return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
||||
}
|
||||
|
||||
// proxy tunnels SDK requests
|
||||
func (s *Server) proxy(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodConnect {
|
||||
http.Error(w, "", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
dst, err := net.Dial("tcp", s.URL.Host)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
src, _, err := w.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
go io.Copy(src, dst)
|
||||
go func() {
|
||||
_, _ = io.Copy(dst, src)
|
||||
_ = dst.Close()
|
||||
_ = src.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
// StartTunnel runs an HTTP proxy for tunneling SDK requests that require TLS client certificate authentication.
|
||||
func (s *Server) StartTunnel() error {
|
||||
tunnel := &http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", s.URL.Hostname(), s.Tunnel),
|
||||
Handler: http.HandlerFunc(s.proxy),
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", tunnel.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.Tunnel == 0 {
|
||||
s.Tunnel = l.Addr().(*net.TCPAddr).Port
|
||||
}
|
||||
|
||||
// Set client proxy port (defaults to vCenter host port 80 in real life)
|
||||
q := s.URL.Query()
|
||||
q.Set("GOVMOMI_TUNNEL_PROXY_PORT", strconv.Itoa(s.Tunnel))
|
||||
s.URL.RawQuery = q.Encode()
|
||||
|
||||
go tunnel.Serve(l)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close shuts down the server and blocks until all outstanding
|
||||
// requests on this server have completed.
|
||||
func (s *Server) Close() {
|
||||
s.Server.Close()
|
||||
if s.caFile != "" {
|
||||
_ = os.Remove(s.caFile)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
vim25MapType = types.TypeFunc()
|
||||
)
|
||||
|
||||
func defaultMapType(name string) (reflect.Type, bool) {
|
||||
typ, ok := vim25MapType(name)
|
||||
if !ok {
|
||||
// See TestIssue945, in which case Go does not resolve the namespace and name == "ns1:TraversalSpec"
|
||||
// Without this hack, the SelectSet would be all nil's
|
||||
kind := strings.SplitN(name, ":", 2)
|
||||
if len(kind) == 2 {
|
||||
typ, ok = vim25MapType(kind[1])
|
||||
}
|
||||
}
|
||||
return typ, ok
|
||||
}
|
||||
|
||||
// Element can be used to defer decoding of an XML node.
|
||||
type Element struct {
|
||||
start xml.StartElement
|
||||
inner struct {
|
||||
Content string `xml:",innerxml"`
|
||||
}
|
||||
typeFunc func(string) (reflect.Type, bool)
|
||||
}
|
||||
|
||||
func (e *Element) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
e.start = start
|
||||
|
||||
return d.DecodeElement(&e.inner, &start)
|
||||
}
|
||||
|
||||
func (e *Element) decoder() *xml.Decoder {
|
||||
decoder := xml.NewDecoder(strings.NewReader(e.inner.Content))
|
||||
decoder.TypeFunc = e.typeFunc // required to decode interface types
|
||||
return decoder
|
||||
}
|
||||
|
||||
func (e *Element) Decode(val interface{}) error {
|
||||
return e.decoder().DecodeElement(val, &e.start)
|
||||
}
|
||||
|
||||
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
|
||||
func UnmarshalBody(typeFunc func(string) (reflect.Type, bool), data []byte) (*Method, error) {
|
||||
body := &Element{typeFunc: typeFunc}
|
||||
req := soap.Envelope{
|
||||
Header: &soap.Header{
|
||||
Security: new(Element),
|
||||
},
|
||||
Body: body,
|
||||
}
|
||||
|
||||
err := xml.Unmarshal(data, &req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xml.Unmarshal: %s", err)
|
||||
}
|
||||
|
||||
var start xml.StartElement
|
||||
var ok bool
|
||||
decoder := body.decoder()
|
||||
|
||||
for {
|
||||
tok, derr := decoder.Token()
|
||||
if derr != nil {
|
||||
return nil, fmt.Errorf("decoding: %s", derr)
|
||||
}
|
||||
if start, ok = tok.(xml.StartElement); ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("decoding: method token not found")
|
||||
}
|
||||
|
||||
kind := start.Name.Local
|
||||
rtype, ok := typeFunc(kind)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no vmomi type defined for '%s'", kind)
|
||||
}
|
||||
|
||||
val := reflect.New(rtype).Interface()
|
||||
|
||||
err = decoder.DecodeElement(val, &start)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decoding %s: %s", kind, err)
|
||||
}
|
||||
|
||||
method := &Method{Name: kind, Header: *req.Header, Body: val}
|
||||
|
||||
field := reflect.ValueOf(val).Elem().FieldByName("This")
|
||||
|
||||
method.This = field.Interface().(types.ManagedObjectReference)
|
||||
|
||||
return method, nil
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type VirtualMachineSnapshot struct {
|
||||
mo.VirtualMachineSnapshot
|
||||
}
|
||||
|
||||
func (v *VirtualMachineSnapshot) createSnapshotFiles() types.BaseMethodFault {
|
||||
vm := Map.Get(v.Vm).(*VirtualMachine)
|
||||
|
||||
snapshotDirectory := vm.Config.Files.SnapshotDirectory
|
||||
if snapshotDirectory == "" {
|
||||
snapshotDirectory = vm.Config.Files.VmPathName
|
||||
}
|
||||
|
||||
index := 1
|
||||
for {
|
||||
fileName := fmt.Sprintf("%s-Snapshot%d.vmsn", vm.Name, index)
|
||||
f, err := vm.createFile(snapshotDirectory, fileName, false)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *types.FileAlreadyExists:
|
||||
index++
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_ = f.Close()
|
||||
|
||||
p, _ := parseDatastorePath(snapshotDirectory)
|
||||
vm.useDatastore(p.Datastore)
|
||||
datastorePath := object.DatastorePath{
|
||||
Datastore: p.Datastore,
|
||||
Path: path.Join(p.Path, fileName),
|
||||
}
|
||||
|
||||
dataLayoutKey := vm.addFileLayoutEx(datastorePath, 0)
|
||||
vm.addSnapshotLayout(v.Self, dataLayoutKey)
|
||||
vm.addSnapshotLayoutEx(v.Self, dataLayoutKey, -1)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VirtualMachineSnapshot) removeSnapshotFiles(ctx *Context) types.BaseMethodFault {
|
||||
// TODO: also remove delta disks that were created when snapshot was taken
|
||||
|
||||
vm := Map.Get(v.Vm).(*VirtualMachine)
|
||||
|
||||
for idx, sLayout := range vm.Layout.Snapshot {
|
||||
if sLayout.Key == v.Self {
|
||||
vm.Layout.Snapshot = append(vm.Layout.Snapshot[:idx], vm.Layout.Snapshot[idx+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for idx, sLayoutEx := range vm.LayoutEx.Snapshot {
|
||||
if sLayoutEx.Key == v.Self {
|
||||
for _, file := range vm.LayoutEx.File {
|
||||
if file.Key == sLayoutEx.DataKey || file.Key == sLayoutEx.MemoryKey {
|
||||
p, fault := parseDatastorePath(file.Name)
|
||||
if fault != nil {
|
||||
return fault
|
||||
}
|
||||
|
||||
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
|
||||
datastore := Map.FindByName(p.Datastore, host.Datastore).(*Datastore)
|
||||
dFilePath := path.Join(datastore.Info.GetDatastoreInfo().Url, p.Path)
|
||||
|
||||
_ = os.Remove(dFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
vm.LayoutEx.Snapshot = append(vm.LayoutEx.Snapshot[:idx], vm.LayoutEx.Snapshot[idx+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
vm.RefreshStorageInfo(ctx, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VirtualMachineSnapshot) RemoveSnapshotTask(ctx *Context, req *types.RemoveSnapshot_Task) soap.HasFault {
|
||||
task := CreateTask(v, "removeSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
var changes []types.PropertyChange
|
||||
|
||||
vm := Map.Get(v.Vm).(*VirtualMachine)
|
||||
Map.WithLock(vm, func() {
|
||||
if vm.Snapshot.CurrentSnapshot != nil && *vm.Snapshot.CurrentSnapshot == req.This {
|
||||
parent := findParentSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This)
|
||||
changes = append(changes, types.PropertyChange{Name: "snapshot.currentSnapshot", Val: parent})
|
||||
}
|
||||
|
||||
rootSnapshots := removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
|
||||
changes = append(changes, types.PropertyChange{Name: "snapshot.rootSnapshotList", Val: rootSnapshots})
|
||||
|
||||
if len(rootSnapshots) == 0 {
|
||||
changes = []types.PropertyChange{
|
||||
{Name: "snapshot", Val: nil},
|
||||
}
|
||||
}
|
||||
|
||||
Map.Get(req.This).(*VirtualMachineSnapshot).removeSnapshotFiles(ctx)
|
||||
|
||||
Map.Update(vm, changes)
|
||||
})
|
||||
|
||||
Map.Remove(req.This)
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.RemoveSnapshot_TaskBody{
|
||||
Res: &types.RemoveSnapshot_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VirtualMachineSnapshot) RevertToSnapshotTask(req *types.RevertToSnapshot_Task) soap.HasFault {
|
||||
task := CreateTask(v, "revertToSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
|
||||
vm := Map.Get(v.Vm).(*VirtualMachine)
|
||||
|
||||
Map.WithLock(vm, func() {
|
||||
Map.Update(vm, []types.PropertyChange{
|
||||
{Name: "snapshot.currentSnapshot", Val: v.Self},
|
||||
})
|
||||
})
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.RevertToSnapshot_TaskBody{
|
||||
Res: &types.RevertToSnapshot_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type StorageResourceManager struct {
|
||||
mo.StorageResourceManager
|
||||
}
|
||||
|
||||
func (m *StorageResourceManager) ConfigureStorageDrsForPodTask(req *types.ConfigureStorageDrsForPod_Task) soap.HasFault {
|
||||
task := CreateTask(m, "configureStorageDrsForPod", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
cluster := Map.Get(req.Pod).(*StoragePod)
|
||||
|
||||
if s := req.Spec.PodConfigSpec; s != nil {
|
||||
config := &cluster.PodStorageDrsEntry.StorageDrsConfig.PodConfig
|
||||
|
||||
if s.Enabled != nil {
|
||||
config.Enabled = *s.Enabled
|
||||
}
|
||||
if s.DefaultVmBehavior != "" {
|
||||
config.DefaultVmBehavior = s.DefaultVmBehavior
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.ConfigureStorageDrsForPod_TaskBody{
|
||||
Res: &types.ConfigureStorageDrsForPod_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *StorageResourceManager) pod(ref *types.ManagedObjectReference) *StoragePod {
|
||||
if ref == nil {
|
||||
return nil
|
||||
}
|
||||
cluster := Map.Get(*ref).(*StoragePod)
|
||||
config := &cluster.PodStorageDrsEntry.StorageDrsConfig.PodConfig
|
||||
|
||||
if !config.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(cluster.ChildEntity) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cluster
|
||||
}
|
||||
|
||||
func (m *StorageResourceManager) RecommendDatastores(req *types.RecommendDatastores) soap.HasFault {
|
||||
spec := req.StorageSpec.PodSelectionSpec
|
||||
body := new(methods.RecommendDatastoresBody)
|
||||
res := new(types.RecommendDatastoresResponse)
|
||||
key := 0
|
||||
invalid := func(prop string) soap.HasFault {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{
|
||||
InvalidProperty: prop,
|
||||
})
|
||||
return body
|
||||
}
|
||||
add := func(cluster *StoragePod, ds types.ManagedObjectReference) {
|
||||
key++
|
||||
res.Returnval.Recommendations = append(res.Returnval.Recommendations, types.ClusterRecommendation{
|
||||
Key: strconv.Itoa(key),
|
||||
Type: "V1",
|
||||
Time: time.Now(),
|
||||
Rating: 1,
|
||||
Reason: "storagePlacement",
|
||||
ReasonText: "Satisfy storage initial placement requests",
|
||||
WarningText: "",
|
||||
WarningDetails: (*types.LocalizableMessage)(nil),
|
||||
Prerequisite: nil,
|
||||
Action: []types.BaseClusterAction{
|
||||
&types.StoragePlacementAction{
|
||||
ClusterAction: types.ClusterAction{
|
||||
Type: "StoragePlacementV1",
|
||||
Target: (*types.ManagedObjectReference)(nil),
|
||||
},
|
||||
Vm: (*types.ManagedObjectReference)(nil),
|
||||
RelocateSpec: types.VirtualMachineRelocateSpec{
|
||||
Service: (*types.ServiceLocator)(nil),
|
||||
Folder: (*types.ManagedObjectReference)(nil),
|
||||
Datastore: &ds,
|
||||
DiskMoveType: "moveAllDiskBackingsAndAllowSharing",
|
||||
Pool: (*types.ManagedObjectReference)(nil),
|
||||
Host: (*types.ManagedObjectReference)(nil),
|
||||
Disk: nil,
|
||||
Transform: "",
|
||||
DeviceChange: nil,
|
||||
Profile: nil,
|
||||
},
|
||||
Destination: ds,
|
||||
SpaceUtilBefore: 5.00297212600708,
|
||||
SpaceDemandBefore: 5.00297212600708,
|
||||
SpaceUtilAfter: 5.16835880279541,
|
||||
SpaceDemandAfter: 5.894514083862305,
|
||||
IoLatencyBefore: 0,
|
||||
},
|
||||
},
|
||||
Target: &cluster.Self,
|
||||
})
|
||||
}
|
||||
|
||||
var devices object.VirtualDeviceList
|
||||
|
||||
switch types.StoragePlacementSpecPlacementType(req.StorageSpec.Type) {
|
||||
case types.StoragePlacementSpecPlacementTypeCreate:
|
||||
if req.StorageSpec.ResourcePool == nil {
|
||||
return invalid("resourcePool")
|
||||
}
|
||||
if req.StorageSpec.ConfigSpec == nil {
|
||||
return invalid("configSpec")
|
||||
}
|
||||
for _, d := range req.StorageSpec.ConfigSpec.DeviceChange {
|
||||
devices = append(devices, d.GetVirtualDeviceConfigSpec().Device)
|
||||
}
|
||||
cluster := m.pod(spec.StoragePod)
|
||||
if cluster == nil {
|
||||
if f := req.StorageSpec.ConfigSpec.Files; f == nil || f.VmPathName == "" {
|
||||
return invalid("configSpec.files")
|
||||
}
|
||||
}
|
||||
case types.StoragePlacementSpecPlacementTypeClone:
|
||||
if req.StorageSpec.Folder == nil {
|
||||
return invalid("folder")
|
||||
}
|
||||
if req.StorageSpec.Vm == nil {
|
||||
return invalid("vm")
|
||||
}
|
||||
if req.StorageSpec.CloneName == "" {
|
||||
return invalid("cloneName")
|
||||
}
|
||||
if req.StorageSpec.CloneSpec == nil {
|
||||
return invalid("cloneSpec")
|
||||
}
|
||||
}
|
||||
|
||||
for _, placement := range spec.InitialVmConfig {
|
||||
cluster := m.pod(&placement.StoragePod)
|
||||
if cluster == nil {
|
||||
return invalid("podSelectionSpec.storagePod")
|
||||
}
|
||||
|
||||
for _, disk := range placement.Disk {
|
||||
if devices.FindByKey(disk.DiskId) == nil {
|
||||
return invalid("podSelectionSpec.initialVmConfig.disk.fileBacking")
|
||||
}
|
||||
}
|
||||
|
||||
for _, ds := range cluster.ChildEntity {
|
||||
add(cluster, ds)
|
||||
}
|
||||
}
|
||||
|
||||
body.Res = res
|
||||
return body
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
const vTaskSuffix = "_Task" // vmomi suffix
|
||||
const sTaskSuffix = "Task" // simulator suffix (avoiding golint warning)
|
||||
|
||||
type Task struct {
|
||||
mo.Task
|
||||
|
||||
Execute func(*Task) (types.AnyType, types.BaseMethodFault)
|
||||
}
|
||||
|
||||
func NewTask(runner TaskRunner) *Task {
|
||||
ref := runner.Reference()
|
||||
name := reflect.TypeOf(runner).Elem().Name()
|
||||
name = strings.Replace(name, "VM", "Vm", 1) // "VM" for the type to make go-lint happy, but "Vm" for the vmodl ID
|
||||
return CreateTask(ref, name, runner.Run)
|
||||
}
|
||||
|
||||
func CreateTask(e mo.Reference, name string, run func(*Task) (types.AnyType, types.BaseMethodFault)) *Task {
|
||||
ref := e.Reference()
|
||||
id := name
|
||||
|
||||
if strings.HasSuffix(id, sTaskSuffix) {
|
||||
id = id[:len(id)-len(sTaskSuffix)]
|
||||
name = id + vTaskSuffix
|
||||
}
|
||||
|
||||
task := &Task{
|
||||
Execute: run,
|
||||
}
|
||||
|
||||
task.Self = Map.newReference(task)
|
||||
task.Info.Key = task.Self.Value
|
||||
task.Info.Task = task.Self
|
||||
task.Info.Name = ucFirst(name)
|
||||
task.Info.DescriptionId = fmt.Sprintf("%s.%s", ref.Type, id)
|
||||
task.Info.Entity = &ref
|
||||
task.Info.EntityName = ref.Value
|
||||
task.Info.Reason = &types.TaskReasonUser{UserName: "vcsim"} // TODO: Context.Session.User
|
||||
task.Info.QueueTime = time.Now()
|
||||
task.Info.State = types.TaskInfoStateQueued
|
||||
|
||||
Map.Put(task)
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
type TaskRunner interface {
|
||||
mo.Reference
|
||||
|
||||
Run(*Task) (types.AnyType, types.BaseMethodFault)
|
||||
}
|
||||
|
||||
func (t *Task) Run() types.ManagedObjectReference {
|
||||
now := time.Now()
|
||||
|
||||
Map.Update(t, []types.PropertyChange{
|
||||
{Name: "info.startTime", Val: now},
|
||||
{Name: "info.state", Val: types.TaskInfoStateRunning},
|
||||
})
|
||||
|
||||
res, err := t.Execute(t)
|
||||
state := types.TaskInfoStateSuccess
|
||||
var fault interface{}
|
||||
if err != nil {
|
||||
state = types.TaskInfoStateError
|
||||
fault = types.LocalizedMethodFault{
|
||||
Fault: err,
|
||||
LocalizedMessage: fmt.Sprintf("%T", err),
|
||||
}
|
||||
}
|
||||
|
||||
now = time.Now()
|
||||
|
||||
Map.Update(t, []types.PropertyChange{
|
||||
{Name: "info.completeTime", Val: now},
|
||||
{Name: "info.state", Val: state},
|
||||
{Name: "info.result", Val: res},
|
||||
{Name: "info.error", Val: fault},
|
||||
})
|
||||
|
||||
return t.Self
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/vmware/govmomi/simulator/esx"
|
||||
"github.com/vmware/govmomi/simulator/vpx"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var recentTaskMax = 200 // the VC limit
|
||||
|
||||
type TaskManager struct {
|
||||
mo.TaskManager
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (m *TaskManager) init(r *Registry) {
|
||||
if len(m.Description.MethodInfo) == 0 {
|
||||
if r.IsVPX() {
|
||||
m.Description = vpx.Description
|
||||
} else {
|
||||
m.Description = esx.Description
|
||||
}
|
||||
}
|
||||
r.AddHandler(m)
|
||||
}
|
||||
|
||||
func (m *TaskManager) PutObject(obj mo.Reference) {
|
||||
ref := obj.Reference()
|
||||
if ref.Type != "Task" {
|
||||
return
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
recent := append(m.RecentTask, ref)
|
||||
if len(recent) > recentTaskMax {
|
||||
recent = recent[1:]
|
||||
}
|
||||
|
||||
Map.Update(m, []types.PropertyChange{{Name: "recentTask", Val: recent}})
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
func (*TaskManager) RemoveObject(types.ManagedObjectReference) {}
|
||||
|
||||
func (*TaskManager) UpdateObject(mo.Reference, []types.PropertyChange) {}
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var DefaultUserGroup = []*types.UserSearchResult{
|
||||
{FullName: "root", Group: true, Principal: "root"},
|
||||
{FullName: "root", Group: false, Principal: "root"},
|
||||
{FullName: "administrator", Group: false, Principal: "admin"},
|
||||
}
|
||||
|
||||
type UserDirectory struct {
|
||||
mo.UserDirectory
|
||||
|
||||
userGroup []*types.UserSearchResult
|
||||
}
|
||||
|
||||
func (m *UserDirectory) init(*Registry) {
|
||||
m.userGroup = DefaultUserGroup
|
||||
}
|
||||
|
||||
func (u *UserDirectory) RetrieveUserGroups(req *types.RetrieveUserGroups) soap.HasFault {
|
||||
compare := compareFunc(req.SearchStr, req.ExactMatch)
|
||||
|
||||
res := u.search(req.FindUsers, req.FindGroups, compare)
|
||||
|
||||
body := &methods.RetrieveUserGroupsBody{
|
||||
Res: &types.RetrieveUserGroupsResponse{
|
||||
Returnval: res,
|
||||
},
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (u *UserDirectory) search(findUsers, findGroups bool, compare func(string) bool) (res []types.BaseUserSearchResult) {
|
||||
for _, ug := range u.userGroup {
|
||||
if findUsers && !ug.Group || findGroups && ug.Group {
|
||||
if compare(ug.Principal) {
|
||||
res = append(res, ug)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (u *UserDirectory) addUser(id string) {
|
||||
u.add(id, false)
|
||||
}
|
||||
|
||||
func (u *UserDirectory) removeUser(id string) {
|
||||
u.remove(id, false)
|
||||
}
|
||||
|
||||
func (u *UserDirectory) add(id string, group bool) {
|
||||
user := &types.UserSearchResult{
|
||||
FullName: id,
|
||||
Group: group,
|
||||
Principal: id,
|
||||
}
|
||||
|
||||
u.userGroup = append(u.userGroup, user)
|
||||
}
|
||||
|
||||
func (u *UserDirectory) remove(id string, group bool) {
|
||||
for i, ug := range u.userGroup {
|
||||
if ug.Group == group && ug.Principal == id {
|
||||
u.userGroup = append(u.userGroup[:i], u.userGroup[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compareFunc(compared string, exactly bool) func(string) bool {
|
||||
return func(s string) bool {
|
||||
if exactly {
|
||||
return s == compared
|
||||
}
|
||||
return strings.Contains(strings.ToLower(s), strings.ToLower(compared))
|
||||
}
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type ViewManager struct {
|
||||
mo.ViewManager
|
||||
|
||||
entities map[string]bool
|
||||
}
|
||||
|
||||
var entities = []struct {
|
||||
Type reflect.Type
|
||||
Container bool
|
||||
}{
|
||||
{reflect.TypeOf((*mo.ManagedEntity)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.Folder)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.StoragePod)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.Datacenter)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.ComputeResource)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.ClusterComputeResource)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.HostSystem)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.ResourcePool)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.VirtualApp)(nil)).Elem(), true},
|
||||
{reflect.TypeOf((*mo.VirtualMachine)(nil)).Elem(), false},
|
||||
{reflect.TypeOf((*mo.Datastore)(nil)).Elem(), false},
|
||||
{reflect.TypeOf((*mo.Network)(nil)).Elem(), false},
|
||||
{reflect.TypeOf((*mo.OpaqueNetwork)(nil)).Elem(), false},
|
||||
{reflect.TypeOf((*mo.DistributedVirtualPortgroup)(nil)).Elem(), false},
|
||||
{reflect.TypeOf((*mo.DistributedVirtualSwitch)(nil)).Elem(), false},
|
||||
{reflect.TypeOf((*mo.VmwareDistributedVirtualSwitch)(nil)).Elem(), false},
|
||||
}
|
||||
|
||||
func (m *ViewManager) init(*Registry) {
|
||||
m.entities = make(map[string]bool, len(entities))
|
||||
for _, e := range entities {
|
||||
m.entities[e.Type.Name()] = e.Container
|
||||
}
|
||||
}
|
||||
|
||||
func destroyView(ref types.ManagedObjectReference) soap.HasFault {
|
||||
return &methods.DestroyViewBody{
|
||||
Res: &types.DestroyViewResponse{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ViewManager) CreateContainerView(ctx *Context, req *types.CreateContainerView) soap.HasFault {
|
||||
body := &methods.CreateContainerViewBody{}
|
||||
|
||||
root := Map.Get(req.Container)
|
||||
if root == nil {
|
||||
body.Fault_ = Fault("", &types.ManagedObjectNotFound{Obj: req.Container})
|
||||
return body
|
||||
}
|
||||
|
||||
if !m.entities[root.Reference().Type] {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "container"})
|
||||
return body
|
||||
}
|
||||
|
||||
container := &ContainerView{
|
||||
mo.ContainerView{
|
||||
Container: root.Reference(),
|
||||
Recursive: req.Recursive,
|
||||
Type: req.Type,
|
||||
},
|
||||
make(map[string]bool),
|
||||
}
|
||||
|
||||
for _, ctype := range container.Type {
|
||||
if _, ok := m.entities[ctype]; !ok {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "type"})
|
||||
return body
|
||||
}
|
||||
|
||||
container.types[ctype] = true
|
||||
|
||||
for _, e := range entities {
|
||||
// Check for embedded types
|
||||
if f, ok := e.Type.FieldByName(ctype); ok && f.Anonymous {
|
||||
container.types[e.Type.Name()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Session.setReference(container)
|
||||
|
||||
body.Res = &types.CreateContainerViewResponse{
|
||||
Returnval: container.Self,
|
||||
}
|
||||
|
||||
seen := make(map[types.ManagedObjectReference]bool)
|
||||
container.add(root, seen)
|
||||
|
||||
ctx.Session.Registry.Put(container)
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
type ContainerView struct {
|
||||
mo.ContainerView
|
||||
|
||||
types map[string]bool
|
||||
}
|
||||
|
||||
func (v *ContainerView) DestroyView(ctx *Context, c *types.DestroyView) soap.HasFault {
|
||||
ctx.Session.Remove(c.This)
|
||||
return destroyView(c.This)
|
||||
}
|
||||
|
||||
func (v *ContainerView) include(o types.ManagedObjectReference) bool {
|
||||
if len(v.types) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return v.types[o.Type]
|
||||
}
|
||||
|
||||
func walk(root mo.Reference, f func(child types.ManagedObjectReference)) {
|
||||
if _, ok := root.(types.ManagedObjectReference); ok || root == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var children []types.ManagedObjectReference
|
||||
|
||||
switch e := getManagedObject(root).Addr().Interface().(type) {
|
||||
case *mo.Datacenter:
|
||||
children = []types.ManagedObjectReference{e.VmFolder, e.HostFolder, e.DatastoreFolder, e.NetworkFolder}
|
||||
case *mo.Folder:
|
||||
children = e.ChildEntity
|
||||
case *mo.ComputeResource:
|
||||
children = e.Host
|
||||
children = append(children, *e.ResourcePool)
|
||||
case *mo.ClusterComputeResource:
|
||||
children = e.Host
|
||||
children = append(children, *e.ResourcePool)
|
||||
case *mo.ResourcePool:
|
||||
children = e.ResourcePool
|
||||
children = append(children, e.Vm...)
|
||||
case *mo.VirtualApp:
|
||||
children = e.ResourcePool.ResourcePool
|
||||
children = append(children, e.Vm...)
|
||||
case *mo.HostSystem:
|
||||
children = e.Vm
|
||||
}
|
||||
|
||||
for _, child := range children {
|
||||
f(child)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *ContainerView) add(root mo.Reference, seen map[types.ManagedObjectReference]bool) {
|
||||
walk(root, func(child types.ManagedObjectReference) {
|
||||
if v.include(child) {
|
||||
if !seen[child] {
|
||||
seen[child] = true
|
||||
v.View = append(v.View, child)
|
||||
}
|
||||
}
|
||||
|
||||
if v.Recursive {
|
||||
v.add(Map.Get(child), seen)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (m *ViewManager) CreateListView(ctx *Context, req *types.CreateListView) soap.HasFault {
|
||||
body := new(methods.CreateListViewBody)
|
||||
list := new(ListView)
|
||||
|
||||
if err := list.add(req.Obj); err != nil {
|
||||
body.Fault_ = Fault("", err)
|
||||
return body
|
||||
}
|
||||
|
||||
ctx.Session.Put(list)
|
||||
|
||||
body.Res = &types.CreateListViewResponse{
|
||||
Returnval: list.Self,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
type ListView struct {
|
||||
mo.ListView
|
||||
}
|
||||
|
||||
func (v *ListView) update() {
|
||||
Map.Update(v, []types.PropertyChange{{Name: "view", Val: v.View}})
|
||||
}
|
||||
|
||||
func (v *ListView) add(refs []types.ManagedObjectReference) *types.ManagedObjectNotFound {
|
||||
for _, ref := range refs {
|
||||
obj := Map.Get(ref)
|
||||
if obj == nil {
|
||||
return &types.ManagedObjectNotFound{Obj: ref}
|
||||
}
|
||||
v.View = append(v.View, ref)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *ListView) DestroyView(ctx *Context, c *types.DestroyView) soap.HasFault {
|
||||
ctx.Session.Remove(c.This)
|
||||
return destroyView(c.This)
|
||||
}
|
||||
|
||||
func (v *ListView) ModifyListView(req *types.ModifyListView) soap.HasFault {
|
||||
body := new(methods.ModifyListViewBody)
|
||||
|
||||
for _, ref := range req.Remove {
|
||||
RemoveReference(&v.View, ref)
|
||||
}
|
||||
|
||||
if err := v.add(req.Add); err != nil {
|
||||
body.Fault_ = Fault("", err)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = new(types.ModifyListViewResponse)
|
||||
|
||||
if len(req.Remove) != 0 || len(req.Add) != 0 {
|
||||
v.update()
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (v *ListView) ResetListView(req *types.ResetListView) soap.HasFault {
|
||||
body := new(methods.ResetListViewBody)
|
||||
|
||||
v.View = nil
|
||||
|
||||
if err := v.add(req.Obj); err != nil {
|
||||
body.Fault_ = Fault("", err)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = new(types.ResetListViewResponse)
|
||||
|
||||
v.update()
|
||||
|
||||
return body
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type VirtualDiskManager struct {
|
||||
mo.VirtualDiskManager
|
||||
}
|
||||
|
||||
func (m *VirtualDiskManager) MO() mo.VirtualDiskManager {
|
||||
return m.VirtualDiskManager
|
||||
}
|
||||
|
||||
func vdmNames(name string) []string {
|
||||
return []string{
|
||||
strings.Replace(name, ".vmdk", "-flat.vmdk", 1),
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
func vdmCreateVirtualDisk(op types.VirtualDeviceConfigSpecFileOperation, req *types.CreateVirtualDisk_Task) types.BaseMethodFault {
|
||||
fm := Map.FileManager()
|
||||
|
||||
file, fault := fm.resolve(req.Datacenter, req.Name)
|
||||
if fault != nil {
|
||||
return fault
|
||||
}
|
||||
|
||||
shouldReplace := op == types.VirtualDeviceConfigSpecFileOperationReplace
|
||||
shouldExist := op == ""
|
||||
for _, name := range vdmNames(file) {
|
||||
_, err := os.Stat(name)
|
||||
if err == nil {
|
||||
if shouldExist {
|
||||
return nil
|
||||
}
|
||||
if shouldReplace {
|
||||
if err = os.Truncate(file, 0); err != nil {
|
||||
return fm.fault(name, err, new(types.CannotCreateFile))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fm.fault(name, nil, new(types.FileAlreadyExists))
|
||||
} else if shouldExist {
|
||||
return fm.fault(name, nil, new(types.FileNotFound))
|
||||
}
|
||||
|
||||
f, err := os.Create(name)
|
||||
if err != nil {
|
||||
return fm.fault(name, err, new(types.CannotCreateFile))
|
||||
}
|
||||
|
||||
_ = f.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *VirtualDiskManager) CreateVirtualDiskTask(_ *Context, req *types.CreateVirtualDisk_Task) soap.HasFault {
|
||||
task := CreateTask(m, "createVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
if err := vdmCreateVirtualDisk(types.VirtualDeviceConfigSpecFileOperationCreate, req); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return req.Name, nil
|
||||
})
|
||||
|
||||
return &methods.CreateVirtualDisk_TaskBody{
|
||||
Res: &types.CreateVirtualDisk_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VirtualDiskManager) DeleteVirtualDiskTask(_ *Context, req *types.DeleteVirtualDisk_Task) soap.HasFault {
|
||||
task := CreateTask(m, "deleteVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
fm := Map.FileManager()
|
||||
|
||||
for _, name := range vdmNames(req.Name) {
|
||||
err := fm.deleteDatastoreFile(&types.DeleteDatastoreFile_Task{
|
||||
Name: name,
|
||||
Datacenter: req.Datacenter,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.DeleteVirtualDisk_TaskBody{
|
||||
Res: &types.DeleteVirtualDisk_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VirtualDiskManager) MoveVirtualDiskTask(_ *Context, req *types.MoveVirtualDisk_Task) soap.HasFault {
|
||||
task := CreateTask(m, "moveVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
fm := Map.FileManager()
|
||||
|
||||
dest := vdmNames(req.DestName)
|
||||
|
||||
for i, name := range vdmNames(req.SourceName) {
|
||||
err := fm.moveDatastoreFile(&types.MoveDatastoreFile_Task{
|
||||
SourceName: name,
|
||||
SourceDatacenter: req.SourceDatacenter,
|
||||
DestinationName: dest[i],
|
||||
DestinationDatacenter: req.DestDatacenter,
|
||||
Force: req.Force,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.MoveVirtualDisk_TaskBody{
|
||||
Res: &types.MoveVirtualDisk_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VirtualDiskManager) CopyVirtualDiskTask(_ *Context, req *types.CopyVirtualDisk_Task) soap.HasFault {
|
||||
task := CreateTask(m, "copyVirtualDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
if req.DestSpec != nil {
|
||||
if Map.IsVPX() {
|
||||
return nil, new(types.NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
fm := Map.FileManager()
|
||||
|
||||
dest := vdmNames(req.DestName)
|
||||
|
||||
for i, name := range vdmNames(req.SourceName) {
|
||||
err := fm.copyDatastoreFile(&types.CopyDatastoreFile_Task{
|
||||
SourceName: name,
|
||||
SourceDatacenter: req.SourceDatacenter,
|
||||
DestinationName: dest[i],
|
||||
DestinationDatacenter: req.DestDatacenter,
|
||||
Force: req.Force,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.CopyVirtualDisk_TaskBody{
|
||||
Res: &types.CopyVirtualDisk_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VirtualDiskManager) QueryVirtualDiskUuid(_ *Context, req *types.QueryVirtualDiskUuid) soap.HasFault {
|
||||
body := new(methods.QueryVirtualDiskUuidBody)
|
||||
|
||||
fm := Map.FileManager()
|
||||
|
||||
file, fault := fm.resolve(req.Datacenter, req.Name)
|
||||
if fault != nil {
|
||||
body.Fault_ = Fault("", fault)
|
||||
return body
|
||||
}
|
||||
|
||||
_, err := os.Stat(file)
|
||||
if err != nil {
|
||||
fault = fm.fault(req.Name, err, new(types.CannotAccessFile))
|
||||
body.Fault_ = Fault(fmt.Sprintf("File %s was not found", req.Name), fault)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.QueryVirtualDiskUuidResponse{
|
||||
Returnval: uuid.NewSHA1(uuid.NameSpaceOID, []byte(file)).String(),
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *VirtualDiskManager) SetVirtualDiskUuid(_ *Context, req *types.SetVirtualDiskUuid) soap.HasFault {
|
||||
body := new(methods.SetVirtualDiskUuidBody)
|
||||
// TODO: validate uuid format and persist
|
||||
body.Res = new(types.SetVirtualDiskUuidResponse)
|
||||
return body
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package vpx contains SOAP responses from a vCenter server, captured using `govc ... -dump`.
|
||||
*/
|
||||
package vpx
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package vpx
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var RootFolder = mo.Folder{
|
||||
ManagedEntity: mo.ManagedEntity{
|
||||
ExtensibleManagedObject: mo.ExtensibleManagedObject{
|
||||
Self: types.ManagedObjectReference{Type: "Folder", Value: "group-d1"},
|
||||
Value: nil,
|
||||
AvailableField: nil,
|
||||
},
|
||||
Parent: (*types.ManagedObjectReference)(nil),
|
||||
CustomValue: nil,
|
||||
OverallStatus: "green",
|
||||
ConfigStatus: "green",
|
||||
ConfigIssue: nil,
|
||||
EffectiveRole: []int32{-1},
|
||||
Permission: []types.Permission{
|
||||
{
|
||||
DynamicData: types.DynamicData{},
|
||||
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "group-d1"},
|
||||
Principal: "VSPHERE.LOCAL\\Administrator",
|
||||
Group: false,
|
||||
RoleId: -1,
|
||||
Propagate: true,
|
||||
},
|
||||
{
|
||||
DynamicData: types.DynamicData{},
|
||||
Entity: &types.ManagedObjectReference{Type: "Folder", Value: "group-d1"},
|
||||
Principal: "VSPHERE.LOCAL\\Administrators",
|
||||
Group: true,
|
||||
RoleId: -1,
|
||||
Propagate: true,
|
||||
},
|
||||
},
|
||||
Name: "Datacenters",
|
||||
DisabledMethod: nil,
|
||||
RecentTask: nil,
|
||||
DeclaredAlarmState: nil,
|
||||
AlarmActionsEnabled: (*bool)(nil),
|
||||
Tag: nil,
|
||||
},
|
||||
ChildType: []string{"Folder", "Datacenter"},
|
||||
ChildEntity: nil,
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package vpx
|
||||
|
||||
import "github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
// ServiceContent is the default template for the ServiceInstance content property.
|
||||
// Capture method:
|
||||
// govc object.collect -s -dump - content
|
||||
var ServiceContent = types.ServiceContent{
|
||||
RootFolder: types.ManagedObjectReference{Type: "Folder", Value: "group-d1"},
|
||||
PropertyCollector: types.ManagedObjectReference{Type: "PropertyCollector", Value: "propertyCollector"},
|
||||
ViewManager: &types.ManagedObjectReference{Type: "ViewManager", Value: "ViewManager"},
|
||||
About: types.AboutInfo{
|
||||
Name: "VMware vCenter Server",
|
||||
FullName: "VMware vCenter Server 6.5.0 build-5973321",
|
||||
Vendor: "VMware, Inc.",
|
||||
Version: "6.5.0",
|
||||
Build: "5973321",
|
||||
LocaleVersion: "INTL",
|
||||
LocaleBuild: "000",
|
||||
OsType: "linux-x64",
|
||||
ProductLineId: "vpx",
|
||||
ApiType: "VirtualCenter",
|
||||
ApiVersion: "6.5",
|
||||
InstanceUuid: "dbed6e0c-bd88-4ef6-b594-21283e1c677f",
|
||||
LicenseProductName: "VMware VirtualCenter Server",
|
||||
LicenseProductVersion: "6.0",
|
||||
},
|
||||
Setting: &types.ManagedObjectReference{Type: "OptionManager", Value: "VpxSettings"},
|
||||
UserDirectory: &types.ManagedObjectReference{Type: "UserDirectory", Value: "UserDirectory"},
|
||||
SessionManager: &types.ManagedObjectReference{Type: "SessionManager", Value: "SessionManager"},
|
||||
AuthorizationManager: &types.ManagedObjectReference{Type: "AuthorizationManager", Value: "AuthorizationManager"},
|
||||
ServiceManager: &types.ManagedObjectReference{Type: "ServiceManager", Value: "ServiceMgr"},
|
||||
PerfManager: &types.ManagedObjectReference{Type: "PerformanceManager", Value: "PerfMgr"},
|
||||
ScheduledTaskManager: &types.ManagedObjectReference{Type: "ScheduledTaskManager", Value: "ScheduledTaskManager"},
|
||||
AlarmManager: &types.ManagedObjectReference{Type: "AlarmManager", Value: "AlarmManager"},
|
||||
EventManager: &types.ManagedObjectReference{Type: "EventManager", Value: "EventManager"},
|
||||
TaskManager: &types.ManagedObjectReference{Type: "TaskManager", Value: "TaskManager"},
|
||||
ExtensionManager: &types.ManagedObjectReference{Type: "ExtensionManager", Value: "ExtensionManager"},
|
||||
CustomizationSpecManager: &types.ManagedObjectReference{Type: "CustomizationSpecManager", Value: "CustomizationSpecManager"},
|
||||
CustomFieldsManager: &types.ManagedObjectReference{Type: "CustomFieldsManager", Value: "CustomFieldsManager"},
|
||||
AccountManager: (*types.ManagedObjectReference)(nil),
|
||||
DiagnosticManager: &types.ManagedObjectReference{Type: "DiagnosticManager", Value: "DiagMgr"},
|
||||
LicenseManager: &types.ManagedObjectReference{Type: "LicenseManager", Value: "LicenseManager"},
|
||||
SearchIndex: &types.ManagedObjectReference{Type: "SearchIndex", Value: "SearchIndex"},
|
||||
FileManager: &types.ManagedObjectReference{Type: "FileManager", Value: "FileManager"},
|
||||
DatastoreNamespaceManager: &types.ManagedObjectReference{Type: "DatastoreNamespaceManager", Value: "DatastoreNamespaceManager"},
|
||||
VirtualDiskManager: &types.ManagedObjectReference{Type: "VirtualDiskManager", Value: "virtualDiskManager"},
|
||||
VirtualizationManager: (*types.ManagedObjectReference)(nil),
|
||||
SnmpSystem: &types.ManagedObjectReference{Type: "HostSnmpSystem", Value: "SnmpSystem"},
|
||||
VmProvisioningChecker: &types.ManagedObjectReference{Type: "VirtualMachineProvisioningChecker", Value: "ProvChecker"},
|
||||
VmCompatibilityChecker: &types.ManagedObjectReference{Type: "VirtualMachineCompatibilityChecker", Value: "CompatChecker"},
|
||||
OvfManager: &types.ManagedObjectReference{Type: "OvfManager", Value: "OvfManager"},
|
||||
IpPoolManager: &types.ManagedObjectReference{Type: "IpPoolManager", Value: "IpPoolManager"},
|
||||
DvSwitchManager: &types.ManagedObjectReference{Type: "DistributedVirtualSwitchManager", Value: "DVSManager"},
|
||||
HostProfileManager: &types.ManagedObjectReference{Type: "HostProfileManager", Value: "HostProfileManager"},
|
||||
ClusterProfileManager: &types.ManagedObjectReference{Type: "ClusterProfileManager", Value: "ClusterProfileManager"},
|
||||
ComplianceManager: &types.ManagedObjectReference{Type: "ProfileComplianceManager", Value: "MoComplianceManager"},
|
||||
LocalizationManager: &types.ManagedObjectReference{Type: "LocalizationManager", Value: "LocalizationManager"},
|
||||
StorageResourceManager: &types.ManagedObjectReference{Type: "StorageResourceManager", Value: "StorageResourceManager"},
|
||||
GuestOperationsManager: &types.ManagedObjectReference{Type: "GuestOperationsManager", Value: "guestOperationsManager"},
|
||||
OverheadMemoryManager: &types.ManagedObjectReference{Type: "OverheadMemoryManager", Value: "OverheadMemoryManager"},
|
||||
CertificateManager: &types.ManagedObjectReference{Type: "CertificateManager", Value: "certificateManager"},
|
||||
IoFilterManager: &types.ManagedObjectReference{Type: "IoFilterManager", Value: "IoFilterManager"},
|
||||
VStorageObjectManager: &types.ManagedObjectReference{Type: "VcenterVStorageObjectManager", Value: "VStorageObjectManager"},
|
||||
HostSpecManager: &types.ManagedObjectReference{Type: "HostSpecificationManager", Value: "HostSpecificationManager"},
|
||||
CryptoManager: &types.ManagedObjectReference{Type: "CryptoManagerKmip", Value: "CryptoManager"},
|
||||
HealthUpdateManager: &types.ManagedObjectReference{Type: "HealthUpdateManager", Value: "HealthUpdateManager"},
|
||||
FailoverClusterConfigurator: &types.ManagedObjectReference{Type: "FailoverClusterConfigurator", Value: "FailoverClusterConfigurator"},
|
||||
FailoverClusterManager: &types.ManagedObjectReference{Type: "FailoverClusterManager", Value: "FailoverClusterManager"},
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package vpx
|
||||
|
||||
import "github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
// Setting is captured from VC's ServiceContent.OptionManager.setting
|
||||
var Setting = []types.BaseOptionValue{
|
||||
// This list is currently pruned to include sso options only with sso.enabled set to false
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.sts.uri",
|
||||
Value: "https://127.0.0.1/sts/STSService/vsphere.local",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.solutionUser.privateKey",
|
||||
Value: "/etc/vmware-vpx/ssl/vcsoluser.key",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.solutionUser.name",
|
||||
Value: "vpxd-b643d01c-928f-469b-96a5-d571d762a78e@vsphere.local",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.solutionUser.certificate",
|
||||
Value: "/etc/vmware-vpx/ssl/vcsoluser.crt",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.groupcheck.uri",
|
||||
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.enabled",
|
||||
Value: "false",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.default.isGroup",
|
||||
Value: "false",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.default.admin",
|
||||
Value: "Administrator@vsphere.local",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "config.vpxd.sso.admin.uri",
|
||||
Value: "https://127.0.0.1/sso-adminserver/sdk/vsphere.local",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "VirtualCenter.InstanceName",
|
||||
Value: "127.0.0.1",
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "event.batchsize",
|
||||
Value: int32(2000),
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "event.maxAge",
|
||||
Value: int32(30),
|
||||
},
|
||||
&types.OptionValue{
|
||||
Key: "event.maxAgeEnabled",
|
||||
Value: bool(true),
|
||||
},
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,451 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package simulator
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type VStorageObject struct {
|
||||
types.VStorageObject
|
||||
types.VStorageObjectSnapshotInfo
|
||||
}
|
||||
|
||||
type VcenterVStorageObjectManager struct {
|
||||
mo.VcenterVStorageObjectManager
|
||||
|
||||
objects map[types.ManagedObjectReference]map[types.ID]*VStorageObject
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) init(*Registry) {
|
||||
m.objects = make(map[types.ManagedObjectReference]map[types.ID]*VStorageObject)
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) object(ds types.ManagedObjectReference, id types.ID) *VStorageObject {
|
||||
if objects, ok := m.objects[ds]; ok {
|
||||
return objects[id]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) ListVStorageObject(req *types.ListVStorageObject) soap.HasFault {
|
||||
body := &methods.ListVStorageObjectBody{
|
||||
Res: &types.ListVStorageObjectResponse{},
|
||||
}
|
||||
|
||||
if objects, ok := m.objects[req.Datastore]; ok {
|
||||
for id := range objects {
|
||||
body.Res.Returnval = append(body.Res.Returnval, id)
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) RetrieveVStorageObject(ctx *Context, req *types.RetrieveVStorageObject) soap.HasFault {
|
||||
body := new(methods.RetrieveVStorageObjectBody)
|
||||
|
||||
obj := m.object(req.Datastore, req.Id)
|
||||
if obj == nil {
|
||||
body.Fault_ = Fault("", new(types.NotFound))
|
||||
} else {
|
||||
stat := m.statDatastoreBacking(ctx, req.Datastore, &req.Id)
|
||||
if err := stat[req.Id]; err != nil {
|
||||
body.Fault_ = Fault(err.Error(), new(types.NotFound))
|
||||
return body
|
||||
}
|
||||
body.Res = &types.RetrieveVStorageObjectResponse{
|
||||
Returnval: obj.VStorageObject,
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
// statDatastoreBacking checks if object(s) backing file exists on the given datastore ref.
|
||||
func (m *VcenterVStorageObjectManager) statDatastoreBacking(ctx *Context, ref types.ManagedObjectReference, id *types.ID) map[types.ID]error {
|
||||
objs := m.objects[ref] // default to checking all objects
|
||||
if id != nil {
|
||||
// check for a specific object
|
||||
objs = map[types.ID]*VStorageObject{
|
||||
*id: objs[*id],
|
||||
}
|
||||
}
|
||||
res := make(map[types.ID]error, len(objs))
|
||||
ds := ctx.Map.Get(ref).(*Datastore)
|
||||
dc := ctx.Map.getEntityDatacenter(ds)
|
||||
fm := ctx.Map.FileManager()
|
||||
|
||||
for _, obj := range objs {
|
||||
backing := obj.Config.Backing.(*types.BaseConfigInfoDiskFileBackingInfo)
|
||||
file, _ := fm.resolve(&dc.Self, backing.FilePath)
|
||||
_, res[obj.Config.Id] = os.Stat(file)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) ReconcileDatastoreInventoryTask(ctx *Context, req *types.ReconcileDatastoreInventory_Task) soap.HasFault {
|
||||
task := CreateTask(m, "reconcileDatastoreInventory", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
objs := m.objects[req.Datastore]
|
||||
stat := m.statDatastoreBacking(ctx, req.Datastore, nil)
|
||||
|
||||
for id, err := range stat {
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("removing disk %s from inventory: %s", id.Id, err)
|
||||
delete(objs, id)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.ReconcileDatastoreInventory_TaskBody{
|
||||
Res: &types.ReconcileDatastoreInventory_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) RegisterDisk(ctx *Context, req *types.RegisterDisk) soap.HasFault {
|
||||
body := new(methods.RegisterDiskBody)
|
||||
|
||||
invalid := func() soap.HasFault {
|
||||
body.Fault_ = Fault("", &types.InvalidArgument{InvalidProperty: "path"})
|
||||
return body
|
||||
}
|
||||
|
||||
u, err := url.Parse(req.Path)
|
||||
if err != nil {
|
||||
return invalid()
|
||||
}
|
||||
u.Path = strings.TrimPrefix(u.Path, folderPrefix)
|
||||
|
||||
ds, err := ctx.svc.findDatastore(u.Query())
|
||||
if err != nil {
|
||||
return invalid()
|
||||
}
|
||||
|
||||
st, err := os.Stat(filepath.Join(ds.Info.GetDatastoreInfo().Url, u.Path))
|
||||
if err != nil {
|
||||
return invalid()
|
||||
|
||||
}
|
||||
if st.IsDir() {
|
||||
return invalid()
|
||||
}
|
||||
|
||||
path := (&object.DatastorePath{Datastore: ds.Name, Path: u.Path}).String()
|
||||
|
||||
for _, obj := range m.objects[ds.Self] {
|
||||
backing := obj.Config.Backing.(*types.BaseConfigInfoDiskFileBackingInfo)
|
||||
if backing.FilePath == path {
|
||||
return invalid()
|
||||
}
|
||||
}
|
||||
|
||||
creq := &types.CreateDisk_Task{
|
||||
Spec: types.VslmCreateSpec{
|
||||
Name: req.Name,
|
||||
BackingSpec: &types.VslmCreateSpecDiskFileBackingSpec{
|
||||
VslmCreateSpecBackingSpec: types.VslmCreateSpecBackingSpec{
|
||||
Datastore: ds.Self,
|
||||
Path: u.Path,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
obj, fault := m.createObject(creq, true)
|
||||
if fault != nil {
|
||||
body.Fault_ = Fault("", fault)
|
||||
return body
|
||||
}
|
||||
|
||||
body.Res = &types.RegisterDiskResponse{
|
||||
Returnval: *obj,
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) createObject(req *types.CreateDisk_Task, register bool) (*types.VStorageObject, types.BaseMethodFault) {
|
||||
dir := "fcd"
|
||||
ref := req.Spec.BackingSpec.GetVslmCreateSpecBackingSpec().Datastore
|
||||
ds := Map.Get(ref).(*Datastore)
|
||||
dc := Map.getEntityDatacenter(ds)
|
||||
|
||||
objects, ok := m.objects[ds.Self]
|
||||
if !ok {
|
||||
objects = make(map[types.ID]*VStorageObject)
|
||||
m.objects[ds.Self] = objects
|
||||
_ = os.Mkdir(filepath.Join(ds.Info.GetDatastoreInfo().Url, dir), 0750)
|
||||
}
|
||||
|
||||
id := uuid.New().String()
|
||||
obj := types.VStorageObject{
|
||||
Config: types.VStorageObjectConfigInfo{
|
||||
BaseConfigInfo: types.BaseConfigInfo{
|
||||
Id: types.ID{
|
||||
Id: id,
|
||||
},
|
||||
Name: req.Spec.Name,
|
||||
CreateTime: time.Now(),
|
||||
KeepAfterDeleteVm: req.Spec.KeepAfterDeleteVm,
|
||||
RelocationDisabled: types.NewBool(false),
|
||||
NativeSnapshotSupported: types.NewBool(false),
|
||||
ChangedBlockTrackingEnabled: types.NewBool(false),
|
||||
Iofilter: nil,
|
||||
},
|
||||
CapacityInMB: req.Spec.CapacityInMB,
|
||||
ConsumptionType: []string{"disk"},
|
||||
ConsumerId: nil,
|
||||
},
|
||||
}
|
||||
|
||||
backing := req.Spec.BackingSpec.(*types.VslmCreateSpecDiskFileBackingSpec)
|
||||
path := object.DatastorePath{
|
||||
Datastore: ds.Name,
|
||||
Path: backing.Path,
|
||||
}
|
||||
if path.Path == "" {
|
||||
path.Path = dir + "/" + id + ".vmdk"
|
||||
}
|
||||
|
||||
if !register {
|
||||
err := vdmCreateVirtualDisk(types.VirtualDeviceConfigSpecFileOperationCreate, &types.CreateVirtualDisk_Task{
|
||||
Datacenter: &dc.Self,
|
||||
Name: path.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
obj.Config.Backing = &types.BaseConfigInfoDiskFileBackingInfo{
|
||||
BaseConfigInfoFileBackingInfo: types.BaseConfigInfoFileBackingInfo{
|
||||
BaseConfigInfoBackingInfo: types.BaseConfigInfoBackingInfo{
|
||||
Datastore: ds.Self,
|
||||
},
|
||||
FilePath: path.String(),
|
||||
BackingObjectId: uuid.New().String(),
|
||||
Parent: nil,
|
||||
DeltaSizeInMB: 0,
|
||||
},
|
||||
ProvisioningType: backing.ProvisioningType,
|
||||
}
|
||||
|
||||
objects[obj.Config.Id] = &VStorageObject{VStorageObject: obj}
|
||||
|
||||
return &obj, nil
|
||||
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) CreateDiskTask(req *types.CreateDisk_Task) soap.HasFault {
|
||||
task := CreateTask(m, "createDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
return m.createObject(req, false)
|
||||
})
|
||||
|
||||
return &methods.CreateDisk_TaskBody{
|
||||
Res: &types.CreateDisk_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) DeleteVStorageObjectTask(req *types.DeleteVStorageObject_Task) soap.HasFault {
|
||||
task := CreateTask(m, "deleteDisk", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
obj := m.object(req.Datastore, req.Id)
|
||||
if obj == nil {
|
||||
return nil, &types.InvalidArgument{}
|
||||
}
|
||||
|
||||
backing := obj.Config.Backing.(*types.BaseConfigInfoDiskFileBackingInfo)
|
||||
ds := Map.Get(req.Datastore).(*Datastore)
|
||||
dc := Map.getEntityDatacenter(ds)
|
||||
dm := Map.VirtualDiskManager()
|
||||
dm.DeleteVirtualDiskTask(internalContext, &types.DeleteVirtualDisk_Task{
|
||||
Name: backing.FilePath,
|
||||
Datacenter: &dc.Self,
|
||||
})
|
||||
|
||||
delete(m.objects[req.Datastore], req.Id)
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return &methods.DeleteVStorageObject_TaskBody{
|
||||
Res: &types.DeleteVStorageObject_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) RetrieveSnapshotInfo(req *types.RetrieveSnapshotInfo) soap.HasFault {
|
||||
body := new(methods.RetrieveSnapshotInfoBody)
|
||||
|
||||
obj := m.object(req.Datastore, req.Id)
|
||||
if obj == nil {
|
||||
body.Fault_ = Fault("", new(types.InvalidArgument))
|
||||
} else {
|
||||
body.Res = &types.RetrieveSnapshotInfoResponse{
|
||||
Returnval: obj.VStorageObjectSnapshotInfo,
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) VStorageObjectCreateSnapshotTask(req *types.VStorageObjectCreateSnapshot_Task) soap.HasFault {
|
||||
task := CreateTask(m, "createSnapshot", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
obj := m.object(req.Datastore, req.Id)
|
||||
if obj == nil {
|
||||
return nil, new(types.InvalidArgument)
|
||||
}
|
||||
|
||||
snapshot := types.VStorageObjectSnapshotInfoVStorageObjectSnapshot{
|
||||
Id: &types.ID{
|
||||
Id: uuid.New().String(),
|
||||
},
|
||||
BackingObjectId: uuid.New().String(),
|
||||
CreateTime: time.Now(),
|
||||
Description: req.Description,
|
||||
}
|
||||
obj.Snapshots = append(obj.Snapshots, snapshot)
|
||||
|
||||
return snapshot.Id, nil
|
||||
})
|
||||
|
||||
return &methods.VStorageObjectCreateSnapshot_TaskBody{
|
||||
Res: &types.VStorageObjectCreateSnapshot_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) DeleteSnapshotTask(req *types.DeleteSnapshot_Task) soap.HasFault {
|
||||
task := CreateTask(m, "deleteSnapshot", func(*Task) (types.AnyType, types.BaseMethodFault) {
|
||||
obj := m.object(req.Datastore, req.Id)
|
||||
if obj != nil {
|
||||
for i := range obj.Snapshots {
|
||||
if *obj.Snapshots[i].Id == req.SnapshotId {
|
||||
obj.Snapshots = append(obj.Snapshots[:i], obj.Snapshots[i+1:]...)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, new(types.InvalidArgument)
|
||||
})
|
||||
|
||||
return &methods.DeleteSnapshot_TaskBody{
|
||||
Res: &types.DeleteSnapshot_TaskResponse{
|
||||
Returnval: task.Run(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) tagID(id types.ID) types.ManagedObjectReference {
|
||||
return types.ManagedObjectReference{
|
||||
Type: "fcd",
|
||||
Value: id.Id,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) AttachTagToVStorageObject(ctx *Context, req *types.AttachTagToVStorageObject) soap.HasFault {
|
||||
body := new(methods.AttachTagToVStorageObjectBody)
|
||||
ref := m.tagID(req.Id)
|
||||
|
||||
err := ctx.Map.tagManager.AttachTag(ref, types.VslmTagEntry{
|
||||
ParentCategoryName: req.Category,
|
||||
TagName: req.Tag,
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
body.Res = new(types.AttachTagToVStorageObjectResponse)
|
||||
} else {
|
||||
body.Fault_ = Fault("", err)
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) DetachTagFromVStorageObject(ctx *Context, req *types.DetachTagFromVStorageObject) soap.HasFault {
|
||||
body := new(methods.DetachTagFromVStorageObjectBody)
|
||||
ref := m.tagID(req.Id)
|
||||
|
||||
err := ctx.Map.tagManager.DetachTag(ref, types.VslmTagEntry{
|
||||
ParentCategoryName: req.Category,
|
||||
TagName: req.Tag,
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
body.Res = new(types.DetachTagFromVStorageObjectResponse)
|
||||
} else {
|
||||
body.Fault_ = Fault("", err)
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) ListVStorageObjectsAttachedToTag(ctx *Context, req *types.ListVStorageObjectsAttachedToTag) soap.HasFault {
|
||||
body := new(methods.ListVStorageObjectsAttachedToTagBody)
|
||||
|
||||
refs, err := ctx.Map.tagManager.AttachedObjects(types.VslmTagEntry{
|
||||
ParentCategoryName: req.Category,
|
||||
TagName: req.Tag,
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
body.Res = new(types.ListVStorageObjectsAttachedToTagResponse)
|
||||
for _, ref := range refs {
|
||||
body.Res.Returnval = append(body.Res.Returnval, types.ID{Id: ref.Value})
|
||||
}
|
||||
} else {
|
||||
body.Fault_ = Fault("", err)
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func (m *VcenterVStorageObjectManager) ListTagsAttachedToVStorageObject(ctx *Context, req *types.ListTagsAttachedToVStorageObject) soap.HasFault {
|
||||
body := new(methods.ListTagsAttachedToVStorageObjectBody)
|
||||
ref := m.tagID(req.Id)
|
||||
|
||||
tags, err := ctx.Map.tagManager.AttachedTags(ref)
|
||||
|
||||
if err == nil {
|
||||
body.Res = &types.ListTagsAttachedToVStorageObjectResponse{
|
||||
Returnval: tags,
|
||||
}
|
||||
} else {
|
||||
body.Fault_ = Fault("", err)
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
|
@ -1,308 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// StorageBackings for Content Libraries
|
||||
type StorageBackings struct {
|
||||
DatastoreID string `json:"datastore_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Library provides methods to create, read, update, delete, and enumerate libraries.
|
||||
type Library struct {
|
||||
CreationTime *time.Time `json:"creation_time,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LastModifiedTime *time.Time `json:"last_modified_time,omitempty"`
|
||||
LastSyncTime *time.Time `json:"last_sync_time,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Storage []StorageBackings `json:"storage_backings,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Subscription *Subscription `json:"subscription_info,omitempty"`
|
||||
Publication *Publication `json:"publish_info,omitempty"`
|
||||
}
|
||||
|
||||
// Subscription info
|
||||
type Subscription struct {
|
||||
AuthenticationMethod string `json:"authentication_method"`
|
||||
AutomaticSyncEnabled *bool `json:"automatic_sync_enabled,omitempty"`
|
||||
OnDemand *bool `json:"on_demand,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
SslThumbprint string `json:"ssl_thumbprint,omitempty"`
|
||||
SubscriptionURL string `json:"subscription_url,omitempty"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
}
|
||||
|
||||
// Publication info
|
||||
type Publication struct {
|
||||
AuthenticationMethod string `json:"authentication_method"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
CurrentPassword string `json:"current_password,omitempty"`
|
||||
PersistJSON *bool `json:"persist_json_enabled,omitempty"`
|
||||
Published *bool `json:"published,omitempty"`
|
||||
PublishURL string `json:"publish_url,omitempty"`
|
||||
}
|
||||
|
||||
// SubscriberSummary as returned by ListSubscribers
|
||||
type SubscriberSummary struct {
|
||||
LibraryID string `json:"subscribed_library"`
|
||||
LibraryName string `json:"subscribed_library_name"`
|
||||
SubscriptionID string `json:"subscription"`
|
||||
LibraryVcenterHostname string `json:"subscribed_library_vcenter_hostname,omitempty"`
|
||||
}
|
||||
|
||||
// Placement information used to place a virtual machine template
|
||||
type Placement struct {
|
||||
ResourcePool string `json:"resource_pool,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Folder string `json:"folder,omitempty"`
|
||||
Cluster string `json:"cluster,omitempty"`
|
||||
Network string `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
// Vcenter contains information about the vCenter Server instance where a subscribed library associated with a subscription exists.
|
||||
type Vcenter struct {
|
||||
Hostname string `json:"hostname"`
|
||||
Port int `json:"https_port,omitempty"`
|
||||
ServerGUID string `json:"server_guid"`
|
||||
}
|
||||
|
||||
// Subscriber contains the detailed info for a library subscriber.
|
||||
type Subscriber struct {
|
||||
LibraryID string `json:"subscribed_library"`
|
||||
LibraryName string `json:"subscribed_library_name"`
|
||||
LibraryLocation string `json:"subscribed_library_location"`
|
||||
Placement *Placement `json:"subscribed_library_placement,omitempty"`
|
||||
Vcenter *Vcenter `json:"subscribed_library_vcenter,omitempty"`
|
||||
}
|
||||
|
||||
// SubscriberLibrary is the specification for a subscribed library to be associated with a subscription.
|
||||
type SubscriberLibrary struct {
|
||||
Target string `json:"target"`
|
||||
LibraryID string `json:"subscribed_library,omitempty"`
|
||||
Location string `json:"location"`
|
||||
Vcenter *Vcenter `json:"vcenter,omitempty"`
|
||||
Placement *Placement `json:"placement,omitempty"`
|
||||
}
|
||||
|
||||
// Patch merges updates from the given src.
|
||||
func (l *Library) Patch(src *Library) {
|
||||
if src.Name != "" {
|
||||
l.Name = src.Name
|
||||
}
|
||||
if src.Description != "" {
|
||||
l.Description = src.Description
|
||||
}
|
||||
if src.Version != "" {
|
||||
l.Version = src.Version
|
||||
}
|
||||
}
|
||||
|
||||
// Manager extends rest.Client, adding content library related methods.
|
||||
type Manager struct {
|
||||
*rest.Client
|
||||
}
|
||||
|
||||
// NewManager creates a new Manager instance with the given client.
|
||||
func NewManager(client *rest.Client) *Manager {
|
||||
return &Manager{
|
||||
Client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Find is the search criteria for finding libraries.
|
||||
type Find struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// FindLibrary returns one or more libraries that match the provided search
|
||||
// criteria.
|
||||
//
|
||||
// The provided name is case-insensitive.
|
||||
//
|
||||
// Either the name or type of library may be set to empty values in order
|
||||
// to search for all libraries, all libraries with a specific name, regardless
|
||||
// of type, or all libraries of a specified type.
|
||||
func (c *Manager) FindLibrary(ctx context.Context, search Find) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryPath).WithAction("find")
|
||||
spec := struct {
|
||||
Spec Find `json:"spec"`
|
||||
}{search}
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// CreateLibrary creates a new library with the given Type, Name,
|
||||
// Description, and CategoryID.
|
||||
func (c *Manager) CreateLibrary(ctx context.Context, library Library) (string, error) {
|
||||
spec := struct {
|
||||
Library Library `json:"create_spec"`
|
||||
}{library}
|
||||
path := internal.LocalLibraryPath
|
||||
if library.Type == "SUBSCRIBED" {
|
||||
path = internal.SubscribedLibraryPath
|
||||
sub := library.Subscription
|
||||
u, err := url.Parse(sub.SubscriptionURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if u.Scheme == "https" && sub.SslThumbprint == "" {
|
||||
thumbprint := c.Thumbprint(u.Host)
|
||||
if thumbprint == "" {
|
||||
t := c.DefaultTransport()
|
||||
if t.TLSClientConfig.InsecureSkipVerify {
|
||||
var info object.HostCertificateInfo
|
||||
_ = info.FromURL(u, t.TLSClientConfig)
|
||||
thumbprint = info.ThumbprintSHA1
|
||||
}
|
||||
sub.SslThumbprint = thumbprint
|
||||
}
|
||||
}
|
||||
}
|
||||
url := c.Resource(path)
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// SyncLibrary syncs a subscribed library.
|
||||
func (c *Manager) SyncLibrary(ctx context.Context, library *Library) error {
|
||||
path := internal.SubscribedLibraryPath
|
||||
url := c.Resource(path).WithID(library.ID).WithAction("sync")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// PublishLibrary publishes the library to specified subscriptions.
|
||||
// If no subscriptions are specified, then publishes the library to all subscriptions.
|
||||
func (c *Manager) PublishLibrary(ctx context.Context, library *Library, subscriptions []string) error {
|
||||
path := internal.LocalLibraryPath
|
||||
var spec internal.SubscriptionDestinationSpec
|
||||
for i := range subscriptions {
|
||||
spec.Subscriptions = append(spec.Subscriptions, internal.SubscriptionDestination{ID: subscriptions[i]})
|
||||
}
|
||||
url := c.Resource(path).WithID(library.ID).WithAction("publish")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, spec), nil)
|
||||
}
|
||||
|
||||
// DeleteLibrary deletes an existing library.
|
||||
func (c *Manager) DeleteLibrary(ctx context.Context, library *Library) error {
|
||||
path := internal.LocalLibraryPath
|
||||
if library.Type == "SUBSCRIBED" {
|
||||
path = internal.SubscribedLibraryPath
|
||||
}
|
||||
url := c.Resource(path).WithID(library.ID)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// ListLibraries returns a list of all content library IDs in the system.
|
||||
func (c *Manager) ListLibraries(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryPath)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryByID returns information on a library for the given ID.
|
||||
func (c *Manager) GetLibraryByID(ctx context.Context, id string) (*Library, error) {
|
||||
url := c.Resource(internal.LibraryPath).WithID(id)
|
||||
var res Library
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryByName returns information on a library for the given name.
|
||||
func (c *Manager) GetLibraryByName(ctx context.Context, name string) (*Library, error) {
|
||||
// Lookup by name
|
||||
libraries, err := c.GetLibraries(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range libraries {
|
||||
if libraries[i].Name == name {
|
||||
return &libraries[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("library name (%s) not found", name)
|
||||
}
|
||||
|
||||
// GetLibraries returns a list of all content library details in the system.
|
||||
func (c *Manager) GetLibraries(ctx context.Context) ([]Library, error) {
|
||||
ids, err := c.ListLibraries(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get libraries failed for: %s", err)
|
||||
}
|
||||
|
||||
var libraries []Library
|
||||
for _, id := range ids {
|
||||
library, err := c.GetLibraryByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library %s failed for %s", id, err)
|
||||
}
|
||||
|
||||
libraries = append(libraries, *library)
|
||||
|
||||
}
|
||||
return libraries, nil
|
||||
}
|
||||
|
||||
// ListSubscribers lists the subscriptions of the published library.
|
||||
func (c *Manager) ListSubscribers(ctx context.Context, library *Library) ([]SubscriberSummary, error) {
|
||||
url := c.Resource(internal.Subscriptions).WithParam("library", library.ID)
|
||||
var res []SubscriberSummary
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// CreateSubscriber creates a subscription of the published library.
|
||||
func (c *Manager) CreateSubscriber(ctx context.Context, library *Library, s SubscriberLibrary) (string, error) {
|
||||
var spec struct {
|
||||
Sub struct {
|
||||
SubscriberLibrary SubscriberLibrary `json:"subscribed_library"`
|
||||
} `json:"spec"`
|
||||
}
|
||||
spec.Sub.SubscriberLibrary = s
|
||||
url := c.Resource(internal.Subscriptions).WithID(library.ID)
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, &spec), &res)
|
||||
}
|
||||
|
||||
// GetSubscriber returns information about the specified subscriber of the published library.
|
||||
func (c *Manager) GetSubscriber(ctx context.Context, library *Library, subscriber string) (*Subscriber, error) {
|
||||
id := internal.SubscriptionDestination{ID: subscriber}
|
||||
url := c.Resource(internal.Subscriptions).WithID(library.ID).WithAction("get")
|
||||
var res Subscriber
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, &id), &res)
|
||||
}
|
||||
|
||||
// DeleteSubscriber deletes the specified subscription of the published library.
|
||||
// The subscribed library associated with the subscription will not be deleted.
|
||||
func (c *Manager) DeleteSubscriber(ctx context.Context, library *Library, subscriber string) error {
|
||||
id := internal.SubscriptionDestination{ID: subscriber}
|
||||
url := c.Resource(internal.Subscriptions).WithID(library.ID).WithAction("delete")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, &id), nil)
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
)
|
||||
|
||||
// Checksum provides checksum information on library item files.
|
||||
type Checksum struct {
|
||||
Algorithm string `json:"algorithm,omitempty"`
|
||||
Checksum string `json:"checksum"`
|
||||
}
|
||||
|
||||
// File provides methods to get information on library item files.
|
||||
type File struct {
|
||||
Cached *bool `json:"cached,omitempty"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Size *int64 `json:"size,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// ListLibraryItemFiles returns a list of all the files for a library item.
|
||||
func (c *Manager) ListLibraryItemFiles(ctx context.Context, id string) ([]File, error) {
|
||||
url := c.Resource(internal.LibraryItemFilePath).WithParam("library_item_id", id)
|
||||
var res []File
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemFile returns a file with the provided name for a library item.
|
||||
func (c *Manager) GetLibraryItemFile(ctx context.Context, id, fileName string) (*File, error) {
|
||||
url := c.Resource(internal.LibraryItemFilePath).WithID(id).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"name"`
|
||||
}{fileName}
|
||||
var res File
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
ItemTypeISO = "iso"
|
||||
ItemTypeOVF = "ovf"
|
||||
ItemTypeVMTX = "vm-template"
|
||||
)
|
||||
|
||||
// Item provides methods to create, read, update, delete, and enumerate library items.
|
||||
type Item struct {
|
||||
Cached bool `json:"cached,omitempty"`
|
||||
ContentVersion string `json:"content_version,omitempty"`
|
||||
CreationTime *time.Time `json:"creation_time,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LastModifiedTime *time.Time `json:"last_modified_time,omitempty"`
|
||||
LastSyncTime *time.Time `json:"last_sync_time,omitempty"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
MetadataVersion string `json:"metadata_version,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
SourceID string `json:"source_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// Patch merges updates from the given src.
|
||||
func (i *Item) Patch(src *Item) {
|
||||
if src.Name != "" {
|
||||
i.Name = src.Name
|
||||
}
|
||||
if src.Description != "" {
|
||||
i.Description = src.Description
|
||||
}
|
||||
if src.Type != "" {
|
||||
i.Type = src.Type
|
||||
}
|
||||
if src.Version != "" {
|
||||
i.Version = src.Version
|
||||
}
|
||||
}
|
||||
|
||||
// CreateLibraryItem creates a new library item
|
||||
func (c *Manager) CreateLibraryItem(ctx context.Context, item Item) (string, error) {
|
||||
type createItemSpec struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
spec := struct {
|
||||
Item createItemSpec `json:"create_spec"`
|
||||
}{
|
||||
Item: createItemSpec{
|
||||
Name: item.Name,
|
||||
Description: item.Description,
|
||||
LibraryID: item.LibraryID,
|
||||
Type: item.Type,
|
||||
},
|
||||
}
|
||||
url := c.Resource(internal.LibraryItemPath)
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// CopyLibraryItem copies a library item
|
||||
func (c *Manager) CopyLibraryItem(ctx context.Context, src *Item, dst Item) (string, error) {
|
||||
body := struct {
|
||||
Item `json:"destination_create_spec"`
|
||||
}{dst}
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(src.ID).WithAction("copy")
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, body), &res)
|
||||
}
|
||||
|
||||
// SyncLibraryItem syncs a subscribed library item
|
||||
func (c *Manager) SyncLibraryItem(ctx context.Context, item *Item, force bool) error {
|
||||
body := struct {
|
||||
Force bool `json:"force_sync_content"`
|
||||
}{force}
|
||||
url := c.Resource(internal.SubscribedLibraryItem).WithID(item.ID).WithAction("sync")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, body), nil)
|
||||
}
|
||||
|
||||
// PublishLibraryItem publishes a library item to specified subscriptions.
|
||||
// If no subscriptions are specified, then publishes the library item to all subscriptions.
|
||||
func (c *Manager) PublishLibraryItem(ctx context.Context, item *Item, force bool, subscriptions []string) error {
|
||||
body := internal.SubscriptionItemDestinationSpec{
|
||||
Force: force,
|
||||
}
|
||||
for i := range subscriptions {
|
||||
body.Subscriptions = append(body.Subscriptions, internal.SubscriptionDestination{ID: subscriptions[i]})
|
||||
}
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(item.ID).WithAction("publish")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, body), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItem deletes an existing library item.
|
||||
func (c *Manager) DeleteLibraryItem(ctx context.Context, item *Item) error {
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(item.ID)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// ListLibraryItems returns a list of all items in a content library.
|
||||
func (c *Manager) ListLibraryItems(ctx context.Context, id string) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemPath).WithParam("library_id", id)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItem returns information on a library item for the given ID.
|
||||
func (c *Manager) GetLibraryItem(ctx context.Context, id string) (*Item, error) {
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(id)
|
||||
var res Item
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItems returns a list of all the library items for the specified library.
|
||||
func (c *Manager) GetLibraryItems(ctx context.Context, libraryID string) ([]Item, error) {
|
||||
ids, err := c.ListLibraryItems(ctx, libraryID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library items failed for: %s", err)
|
||||
}
|
||||
var items []Item
|
||||
for _, id := range ids {
|
||||
item, err := c.GetLibraryItem(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library item for %s failed for %s", id, err)
|
||||
}
|
||||
items = append(items, *item)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// FindItem is the search criteria for finding library items.
|
||||
type FindItem struct {
|
||||
Cached *bool `json:"cached,omitempty"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceID string `json:"source_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// FindLibraryItems returns the IDs of all the library items that match the
|
||||
// search criteria.
|
||||
func (c *Manager) FindLibraryItems(
|
||||
ctx context.Context, search FindItem) ([]string, error) {
|
||||
|
||||
url := c.Resource(internal.LibraryItemPath).WithAction("find")
|
||||
spec := struct {
|
||||
Spec FindItem `json:"spec"`
|
||||
}{search}
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
71
vendor/github.com/vmware/govmomi/vapi/library/library_item_downloadsession_file.go
generated
vendored
71
vendor/github.com/vmware/govmomi/vapi/library/library_item_downloadsession_file.go
generated
vendored
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// DownloadFile is the specification for the downloadsession
|
||||
// operations file:add, file:get, and file:list.
|
||||
type DownloadFile struct {
|
||||
BytesTransferred int64 `json:"bytes_transferred"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
DownloadEndpoint *TransferEndpoint `json:"download_endpoint,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// GetLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) GetLibraryItemDownloadSessionFile(ctx context.Context, sessionID string, name string) (*DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithID(sessionID).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{name}
|
||||
var res DownloadFile
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Status == "ERROR" {
|
||||
return nil, res.ErrorMessage
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// ListLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) ListLibraryItemDownloadSessionFile(ctx context.Context, sessionID string) ([]DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithParam("download_session_id", sessionID)
|
||||
var res []DownloadFile
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// PrepareLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) PrepareLibraryItemDownloadSessionFile(ctx context.Context, sessionID string, name string) (*DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithID(sessionID).WithAction("prepare")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{name}
|
||||
var res DownloadFile
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// Session is used to create an initial update or download session
|
||||
type Session struct {
|
||||
ClientProgress int64 `json:"client_progress,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
ExpirationTime *time.Time `json:"expiration_time,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LibraryItemContentVersion string `json:"library_item_content_version,omitempty"`
|
||||
LibraryItemID string `json:"library_item_id,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// CreateLibraryItemUpdateSession creates a new library item
|
||||
func (c *Manager) CreateLibraryItemUpdateSession(ctx context.Context, session Session) (string, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession)
|
||||
spec := struct {
|
||||
CreateSpec Session `json:"create_spec"`
|
||||
}{session}
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemUpdateSession gets the update session information with status
|
||||
func (c *Manager) GetLibraryItemUpdateSession(ctx context.Context, id string) (*Session, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id)
|
||||
var res Session
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// ListLibraryItemUpdateSession gets the list of update sessions
|
||||
func (c *Manager) ListLibraryItemUpdateSession(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// CancelLibraryItemUpdateSession cancels an update session
|
||||
func (c *Manager) CancelLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("cancel")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// CompleteLibraryItemUpdateSession completes an update session
|
||||
func (c *Manager) CompleteLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("complete")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItemUpdateSession deletes an update session
|
||||
func (c *Manager) DeleteLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// FailLibraryItemUpdateSession fails an update session
|
||||
func (c *Manager) FailLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("fail")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// KeepAliveLibraryItemUpdateSession keeps an inactive update session alive.
|
||||
func (c *Manager) KeepAliveLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("keep-alive")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// WaitOnLibraryItemUpdateSession blocks until the update session is no longer
|
||||
// in the ACTIVE state.
|
||||
func (c *Manager) WaitOnLibraryItemUpdateSession(
|
||||
ctx context.Context, sessionID string,
|
||||
interval time.Duration, intervalCallback func()) error {
|
||||
|
||||
// Wait until the upload operation is complete to return.
|
||||
for {
|
||||
session, err := c.GetLibraryItemUpdateSession(ctx, sessionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if session.State != "ACTIVE" {
|
||||
if session.State == "ERROR" {
|
||||
return session.ErrorMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
time.Sleep(interval)
|
||||
if intervalCallback != nil {
|
||||
intervalCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateLibraryItemDownloadSession creates a new library item
|
||||
func (c *Manager) CreateLibraryItemDownloadSession(ctx context.Context, session Session) (string, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession)
|
||||
spec := struct {
|
||||
CreateSpec Session `json:"create_spec"`
|
||||
}{session}
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemDownloadSession gets the download session information with status
|
||||
func (c *Manager) GetLibraryItemDownloadSession(ctx context.Context, id string) (*Session, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id)
|
||||
var res Session
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// ListLibraryItemDownloadSession gets the list of download sessions
|
||||
func (c *Manager) ListLibraryItemDownloadSession(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// CancelLibraryItemDownloadSession cancels an download session
|
||||
func (c *Manager) CancelLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("cancel")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItemDownloadSession deletes an download session
|
||||
func (c *Manager) DeleteLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// FailLibraryItemDownloadSession fails an download session
|
||||
func (c *Manager) FailLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("fail")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// KeepAliveLibraryItemDownloadSession keeps an inactive download session alive.
|
||||
func (c *Manager) KeepAliveLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("keep-alive")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
149
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession_file.go
generated
vendored
149
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession_file.go
generated
vendored
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package library
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
)
|
||||
|
||||
// TransferEndpoint provides information on the source of a library item file.
|
||||
type TransferEndpoint struct {
|
||||
URI string `json:"uri,omitempty"`
|
||||
SSLCertificateThumbprint string `json:"ssl_certificate_thumbprint,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateFile is the specification for the updatesession
|
||||
// operations file:add, file:get, and file:list.
|
||||
type UpdateFile struct {
|
||||
BytesTransferred int64 `json:"bytes_transferred,omitempty"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
SourceEndpoint *TransferEndpoint `json:"source_endpoint,omitempty"`
|
||||
SourceType string `json:"source_type"`
|
||||
Status string `json:"status,omitempty"`
|
||||
UploadEndpoint *TransferEndpoint `json:"upload_endpoint,omitempty"`
|
||||
}
|
||||
|
||||
// AddLibraryItemFile adds a file
|
||||
func (c *Manager) AddLibraryItemFile(ctx context.Context, sessionID string, updateFile UpdateFile) (*UpdateFile, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("add")
|
||||
spec := struct {
|
||||
FileSpec UpdateFile `json:"file_spec"`
|
||||
}{updateFile}
|
||||
var res UpdateFile
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Status == "ERROR" {
|
||||
return nil, res.ErrorMessage
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// AddLibraryItemFileFromURI adds a file from a remote URI.
|
||||
func (c *Manager) AddLibraryItemFileFromURI(
|
||||
ctx context.Context,
|
||||
sessionID, fileName, uri string) (*UpdateFile, error) {
|
||||
|
||||
n, fingerprint, err := c.getContentLengthAndFingerprint(ctx, uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := c.AddLibraryItemFile(ctx, sessionID, UpdateFile{
|
||||
Name: fileName,
|
||||
SourceType: "PULL",
|
||||
Size: n,
|
||||
SourceEndpoint: &TransferEndpoint{
|
||||
URI: uri,
|
||||
SSLCertificateThumbprint: fingerprint,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, c.CompleteLibraryItemUpdateSession(ctx, sessionID)
|
||||
}
|
||||
|
||||
// GetLibraryItemUpdateSessionFile retrieves information about a specific file
|
||||
// that is a part of an update session.
|
||||
func (c *Manager) GetLibraryItemUpdateSessionFile(ctx context.Context, sessionID string, fileName string) (*UpdateFile, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{fileName}
|
||||
var res UpdateFile
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// getContentLengthAndFingerprint gets the number of bytes returned
|
||||
// by the URI as well as the SHA1 fingerprint of the peer certificate
|
||||
// if the URI's scheme is https.
|
||||
func (c *Manager) getContentLengthAndFingerprint(
|
||||
ctx context.Context, uri string) (int64, string, error) {
|
||||
resp, err := c.Head(uri)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
if resp.TLS == nil || len(resp.TLS.PeerCertificates) == 0 {
|
||||
return resp.ContentLength, "", nil
|
||||
}
|
||||
fingerprint := c.Thumbprint(resp.Request.URL.Host)
|
||||
if fingerprint == "" {
|
||||
if c.DefaultTransport().TLSClientConfig.InsecureSkipVerify {
|
||||
fingerprint = soap.ThumbprintSHA1(resp.TLS.PeerCertificates[0])
|
||||
}
|
||||
}
|
||||
return resp.ContentLength, fingerprint, nil
|
||||
}
|
||||
|
||||
// ReadManifest converts an ovf manifest to a map of file name -> Checksum.
|
||||
func ReadManifest(m io.Reader) (map[string]*Checksum, error) {
|
||||
// expected format: openssl sha1 *.{ovf,vmdk}
|
||||
c := make(map[string]*Checksum)
|
||||
|
||||
scanner := bufio.NewScanner(m)
|
||||
for scanner.Scan() {
|
||||
line := strings.SplitN(scanner.Text(), ")=", 2)
|
||||
if len(line) != 2 {
|
||||
continue
|
||||
}
|
||||
name := strings.SplitN(line[0], "(", 2)
|
||||
if len(name) != 2 {
|
||||
continue
|
||||
}
|
||||
sum := &Checksum{
|
||||
Algorithm: strings.TrimSpace(name[0]),
|
||||
Checksum: strings.TrimSpace(line[1]),
|
||||
}
|
||||
c[name[1]] = sum
|
||||
}
|
||||
|
||||
return c, scanner.Err()
|
||||
}
|
|
@ -1,304 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package vcenter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// AdditionalParams are additional OVF parameters which can be specified for a deployment target.
|
||||
// This structure is a union where based on Type, only one of each commented section will be set.
|
||||
type AdditionalParams struct {
|
||||
Class string `json:"@class"`
|
||||
Type string `json:"type"`
|
||||
|
||||
// DeploymentOptionParams
|
||||
SelectedKey string `json:"selected_key,omitempty"`
|
||||
DeploymentOptions []DeploymentOption `json:"deployment_options,omitempty"`
|
||||
|
||||
// ExtraConfigs
|
||||
ExtraConfig []ExtraConfig `json:"extra_configs,omitempty"`
|
||||
|
||||
// PropertyParams
|
||||
Properties []Property `json:"properties,omitempty"`
|
||||
|
||||
// SizeParams
|
||||
ApproximateSparseDeploymentSize int64 `json:"approximate_sparse_deployment_size,omitempty"`
|
||||
VariableDiskSize bool `json:"variable_disk_size,omitempty"`
|
||||
ApproximateDownloadSize int64 `json:"approximate_download_size,omitempty"`
|
||||
ApproximateFlatDeploymentSize int64 `json:"approximate_flat_deployment_size,omitempty"`
|
||||
|
||||
// IpAllocationParams
|
||||
SupportedAllocationScheme []string `json:"supported_allocation_scheme,omitempty"`
|
||||
SupportedIPProtocol []string `json:"supported_ip_protocol,omitempty"`
|
||||
SupportedIPAllocationPolicy []string `json:"supported_ip_allocation_policy,omitempty"`
|
||||
IPAllocationPolicy string `json:"ip_allocation_policy,omitempty"`
|
||||
IPProtocol string `json:"ip_protocol,omitempty"`
|
||||
|
||||
// UnknownSections
|
||||
UnknownSections []UnknownSection `json:"unknown_sections,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
ClassDeploymentOptionParams = "com.vmware.vcenter.ovf.deployment_option_params"
|
||||
ClassPropertyParams = "com.vmware.vcenter.ovf.property_params"
|
||||
TypeDeploymentOptionParams = "DeploymentOptionParams"
|
||||
TypeExtraConfigParams = "ExtraConfigParams"
|
||||
TypeIPAllocationParams = "IpAllocationParams"
|
||||
TypePropertyParams = "PropertyParams"
|
||||
TypeSizeParams = "SizeParams"
|
||||
)
|
||||
|
||||
// DeploymentOption contains the information about a deployment option as defined in the OVF specification
|
||||
type DeploymentOption struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
DefaultChoice bool `json:"default_choice,omitempty"`
|
||||
}
|
||||
|
||||
// ExtraConfig contains information about a vmw:ExtraConfig OVF element
|
||||
type ExtraConfig struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
VirtualSystemID string `json:"virtual_system_id,omitempty"`
|
||||
}
|
||||
|
||||
// Property contains information about a property in an OVF package
|
||||
type Property struct {
|
||||
Category string `json:"category,omitempty"`
|
||||
ClassID string `json:"class_id,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
InstanceID string `json:"instance_id,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
UIOptional bool `json:"ui_optional,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// UnknownSection contains information about an unknown section in an OVF package
|
||||
type UnknownSection struct {
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Info string `json:"info,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkMapping specifies the target network to use for sections of type ovf:NetworkSection in the OVF descriptor
|
||||
type NetworkMapping struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// StorageGroupMapping defines the storage deployment target and storage provisioning type for a section of type vmw:StorageGroupSection in the OVF descriptor
|
||||
type StorageGroupMapping struct {
|
||||
Type string `json:"type"`
|
||||
StorageProfileID string `json:"storage_profile_id,omitempty"`
|
||||
DatastoreID string `json:"datastore_id,omitempty"`
|
||||
Provisioning string `json:"provisioning,omitempty"`
|
||||
}
|
||||
|
||||
// StorageMapping specifies the target storage to use for sections of type vmw:StorageGroupSection in the OVF descriptor
|
||||
type StorageMapping struct {
|
||||
Key string `json:"key"`
|
||||
Value StorageGroupMapping `json:"value"`
|
||||
}
|
||||
|
||||
// DeploymentSpec is the deployment specification for the deployment
|
||||
type DeploymentSpec struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Annotation string `json:"annotation,omitempty"`
|
||||
AcceptAllEULA bool `json:"accept_all_EULA,omitempty"`
|
||||
NetworkMappings []NetworkMapping `json:"network_mappings,omitempty"`
|
||||
StorageMappings []StorageMapping `json:"storage_mappings,omitempty"`
|
||||
StorageProvisioning string `json:"storage_provisioning,omitempty"`
|
||||
StorageProfileID string `json:"storage_profile_id,omitempty"`
|
||||
Locale string `json:"locale,omitempty"`
|
||||
Flags []string `json:"flags,omitempty"`
|
||||
AdditionalParams []AdditionalParams `json:"additional_parameters,omitempty"`
|
||||
DefaultDatastoreID string `json:"default_datastore_id,omitempty"`
|
||||
}
|
||||
|
||||
// Target is the target for the deployment
|
||||
type Target struct {
|
||||
ResourcePoolID string `json:"resource_pool_id,omitempty"`
|
||||
HostID string `json:"host_id,omitempty"`
|
||||
FolderID string `json:"folder_id,omitempty"`
|
||||
}
|
||||
|
||||
// Deploy contains the information to start the deployment of a library OVF
|
||||
type Deploy struct {
|
||||
DeploymentSpec `json:"deployment_spec,omitempty"`
|
||||
Target `json:"target,omitempty"`
|
||||
}
|
||||
|
||||
// Error is a SERVER error
|
||||
type Error struct {
|
||||
Class string `json:"@class,omitempty"`
|
||||
Messages []rest.LocalizableMessage `json:"messages,omitempty"`
|
||||
}
|
||||
|
||||
// ParseIssue is a parse issue struct
|
||||
type ParseIssue struct {
|
||||
Category string `json:"@classcategory,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
LineNumber int64 `json:"line_number,omitempty"`
|
||||
ColumnNumber int64 `json:"column_number,omitempty"`
|
||||
Message rest.LocalizableMessage `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// OVFError is a list of errors from create or deploy
|
||||
type OVFError struct {
|
||||
Category string `json:"category,omitempty"`
|
||||
Error *Error `json:"error,omitempty"`
|
||||
Issues []ParseIssue `json:"issues,omitempty"`
|
||||
Message *rest.LocalizableMessage `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// ResourceID is a managed object reference for a deployed resource.
|
||||
type ResourceID struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Value string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// DeploymentError is an error that occurs when deploying and OVF from
|
||||
// a library item.
|
||||
type DeploymentError struct {
|
||||
Errors []OVFError `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (e *DeploymentError) Error() string {
|
||||
msg := ""
|
||||
if len(e.Errors) != 0 {
|
||||
err := e.Errors[0]
|
||||
if err.Message != nil {
|
||||
msg = err.Message.DefaultMessage
|
||||
} else if err.Error != nil && len(err.Error.Messages) != 0 {
|
||||
msg = err.Error.Messages[0].DefaultMessage
|
||||
}
|
||||
}
|
||||
if msg == "" {
|
||||
msg = fmt.Sprintf("%#v", e)
|
||||
}
|
||||
return "deploy error: " + msg
|
||||
}
|
||||
|
||||
// LibraryTarget specifies a Library or Library item
|
||||
type LibraryTarget struct {
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
LibraryItemID string `json:"library_item_id,omitempty"`
|
||||
}
|
||||
|
||||
// CreateSpec info used to create an OVF package from a VM
|
||||
type CreateSpec struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Flags []string `json:"flags,omitempty"`
|
||||
}
|
||||
|
||||
// OVF data used by CreateOVF
|
||||
type OVF struct {
|
||||
Spec CreateSpec `json:"create_spec"`
|
||||
Source ResourceID `json:"source"`
|
||||
Target LibraryTarget `json:"target"`
|
||||
}
|
||||
|
||||
// CreateResult used for decoded a CreateOVF response
|
||||
type CreateResult struct {
|
||||
Succeeded bool `json:"succeeded,omitempty"`
|
||||
ID string `json:"ovf_library_item_id,omitempty"`
|
||||
Error *DeploymentError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Deployment is the results from issuing a library OVF deployment
|
||||
type Deployment struct {
|
||||
Succeeded bool `json:"succeeded,omitempty"`
|
||||
ResourceID *ResourceID `json:"resource_id,omitempty"`
|
||||
Error *DeploymentError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// FilterRequest contains the information to start a vcenter filter call
|
||||
type FilterRequest struct {
|
||||
Target `json:"target,omitempty"`
|
||||
}
|
||||
|
||||
// FilterResponse returns information from the vcenter filter call
|
||||
type FilterResponse struct {
|
||||
EULAs []string `json:"EULAs,omitempty"`
|
||||
AdditionalParams []AdditionalParams `json:"additional_params,omitempty"`
|
||||
Annotation string `json:"Annotation,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Networks []string `json:"Networks,omitempty"`
|
||||
StorageGroups []string `json:"storage_groups,omitempty"`
|
||||
}
|
||||
|
||||
// Manager extends rest.Client, adding content library related methods.
|
||||
type Manager struct {
|
||||
*rest.Client
|
||||
}
|
||||
|
||||
// NewManager creates a new Manager instance with the given client.
|
||||
func NewManager(client *rest.Client) *Manager {
|
||||
return &Manager{
|
||||
Client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateOVF creates a library OVF item in content library from an existing VM
|
||||
func (c *Manager) CreateOVF(ctx context.Context, ovf OVF) (string, error) {
|
||||
if ovf.Source.Type == "" {
|
||||
ovf.Source.Type = "VirtualMachine"
|
||||
}
|
||||
url := c.Resource(internal.VCenterOVFLibraryItem)
|
||||
var res CreateResult
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, ovf), &res)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if res.Succeeded {
|
||||
return res.ID, nil
|
||||
}
|
||||
return "", res.Error
|
||||
}
|
||||
|
||||
// DeployLibraryItem deploys a library OVF
|
||||
func (c *Manager) DeployLibraryItem(ctx context.Context, libraryItemID string, deploy Deploy) (*types.ManagedObjectReference, error) {
|
||||
url := c.Resource(internal.VCenterOVFLibraryItem).WithID(libraryItemID).WithAction("deploy")
|
||||
var res Deployment
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, deploy), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Succeeded {
|
||||
ref := types.ManagedObjectReference(*res.ResourceID)
|
||||
return &ref, nil
|
||||
}
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
// FilterLibraryItem deploys a library OVF
|
||||
func (c *Manager) FilterLibraryItem(ctx context.Context, libraryItemID string, filter FilterRequest) (FilterResponse, error) {
|
||||
url := c.Resource(internal.VCenterOVFLibraryItem).WithID(libraryItemID).WithAction("filter")
|
||||
var res FilterResponse
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, filter), &res)
|
||||
}
|
|
@ -1,285 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2019 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package vcenter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/library"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// vcenter vm template
|
||||
// The vcenter.vm_template API provides structures and services that will let its client manage VMTX template in Content Library.
|
||||
// http://vmware.github.io/vsphere-automation-sdk-rest/6.7.1/index.html#SVC_com.vmware.vcenter.vm_template.library_items
|
||||
|
||||
// Template create spec
|
||||
type Template struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
DiskStorage *DiskStorage `json:"disk_storage,omitempty"`
|
||||
DiskStorageOverrides []DiskStorageOverride `json:"disk_storage_overrides,omitempty"`
|
||||
Library string `json:"library,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Placement *Placement `json:"placement,omitempty"`
|
||||
SourceVM string `json:"source_vm,omitempty"`
|
||||
VMHomeStorage *DiskStorage `json:"vm_home_storage,omitempty"`
|
||||
}
|
||||
|
||||
// TemplateInfo for a VM template contained in an existing library item
|
||||
type TemplateInfo struct {
|
||||
GuestOS string `json:"guest_OS,omitempty"`
|
||||
// TODO...
|
||||
}
|
||||
|
||||
// Placement information used to place the virtual machine template
|
||||
type Placement = library.Placement
|
||||
|
||||
// StoragePolicy for DiskStorage
|
||||
type StoragePolicy struct {
|
||||
Policy string `json:"policy,omitempty"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// DiskStorage defines the storage specification for VM files
|
||||
type DiskStorage struct {
|
||||
Datastore string `json:"datastore,omitempty"`
|
||||
StoragePolicy *StoragePolicy `json:"storage_policy,omitempty"`
|
||||
}
|
||||
|
||||
// DiskStorageOverride storage specification for individual disks in the virtual machine template
|
||||
type DiskStorageOverride struct {
|
||||
Key string `json:"key"`
|
||||
Value DiskStorage `json:"value"`
|
||||
}
|
||||
|
||||
// GuestCustomization spec to apply to the deployed VM
|
||||
type GuestCustomization struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// HardwareCustomization spec which specifies updates to the deployed VM
|
||||
type HardwareCustomization struct {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// DeployTemplate specification of how a library VM template clone should be deployed.
|
||||
type DeployTemplate struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
DiskStorage *DiskStorage `json:"disk_storage,omitempty"`
|
||||
DiskStorageOverrides []DiskStorageOverride `json:"disk_storage_overrides,omitempty"`
|
||||
GuestCustomization *GuestCustomization `json:"guest_customization,omitempty"`
|
||||
HardwareCustomization *HardwareCustomization `json:"hardware_customization,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Placement *Placement `json:"placement,omitempty"`
|
||||
PoweredOn bool `json:"powered_on"`
|
||||
VMHomeStorage *DiskStorage `json:"vm_home_storage,omitempty"`
|
||||
}
|
||||
|
||||
// CheckOut specification
|
||||
type CheckOut struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Placement *Placement `json:"placement,omitempty"`
|
||||
PoweredOn bool `json:"powered_on,omitempty"`
|
||||
}
|
||||
|
||||
// CheckIn specification
|
||||
type CheckIn struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// CreateTemplate creates a library VMTX item in content library from an existing VM
|
||||
func (c *Manager) CreateTemplate(ctx context.Context, vmtx Template) (string, error) {
|
||||
url := c.Resource(internal.VCenterVMTXLibraryItem)
|
||||
var res string
|
||||
spec := struct {
|
||||
Template `json:"spec"`
|
||||
}{vmtx}
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// DeployTemplateLibraryItem deploys a VM as a copy of the source VM template contained in the given library item
|
||||
func (c *Manager) DeployTemplateLibraryItem(ctx context.Context, libraryItemID string, deploy DeployTemplate) (*types.ManagedObjectReference, error) {
|
||||
url := c.Resource(path.Join(internal.VCenterVMTXLibraryItem, libraryItemID)).WithParam("action", "deploy")
|
||||
var res string
|
||||
spec := struct {
|
||||
DeployTemplate `json:"spec"`
|
||||
}{deploy}
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.ManagedObjectReference{Type: "VirtualMachine", Value: res}, nil
|
||||
}
|
||||
|
||||
// CheckOut a library item containing a VM template.
|
||||
func (c *Manager) CheckOut(ctx context.Context, libraryItemID string, checkout *CheckOut) (*types.ManagedObjectReference, error) {
|
||||
url := c.Resource(path.Join(internal.VCenterVMTXLibraryItem, libraryItemID, "check-outs")).WithParam("action", "check-out")
|
||||
var res string
|
||||
spec := struct {
|
||||
*CheckOut `json:"spec"`
|
||||
}{checkout}
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.ManagedObjectReference{Type: "VirtualMachine", Value: res}, nil
|
||||
}
|
||||
|
||||
// CheckIn a VM into the library item.
|
||||
func (c *Manager) CheckIn(ctx context.Context, libraryItemID string, vm mo.Reference, checkin *CheckIn) (string, error) {
|
||||
p := path.Join(internal.VCenterVMTXLibraryItem, libraryItemID, "check-outs", vm.Reference().Value)
|
||||
url := c.Resource(p).WithParam("action", "check-in")
|
||||
var res string
|
||||
spec := struct {
|
||||
*CheckIn `json:"spec"`
|
||||
}{checkin}
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// TemplateLibrary params for synchronizing subscription library OVF items to VM Template items
|
||||
type TemplateLibrary struct {
|
||||
Source library.Library
|
||||
Destination library.Library
|
||||
Placement Target
|
||||
Include func(library.Item, *library.Item) bool
|
||||
SyncItem func(context.Context, library.Item, *Deploy, *Template) error
|
||||
}
|
||||
|
||||
func (c *Manager) includeTemplateLibraryItem(src library.Item, dst *library.Item) bool {
|
||||
return dst == nil
|
||||
}
|
||||
|
||||
// SyncTemplateLibraryItem deploys an Library OVF item from which a VM template (vmtx) Library item is created.
|
||||
// The deployed VM is deleted after being converted to a Library vmtx item.
|
||||
func (c *Manager) SyncTemplateLibraryItem(ctx context.Context, item library.Item, deploy *Deploy, spec *Template) error {
|
||||
destroy := false
|
||||
if spec.SourceVM == "" {
|
||||
ref, err := c.DeployLibraryItem(ctx, item.ID, *deploy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destroy = true
|
||||
spec.SourceVM = ref.Value
|
||||
}
|
||||
|
||||
_, err := c.CreateTemplate(ctx, *spec)
|
||||
|
||||
if destroy {
|
||||
// Delete source VM regardless of CreateTemplate result
|
||||
url := c.Resource("/vcenter/vm/" + spec.SourceVM)
|
||||
derr := c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
if derr != nil {
|
||||
if err == nil {
|
||||
// Return Delete error if CreateTemplate was successful
|
||||
return derr
|
||||
}
|
||||
// Return CreateTemplate error and just log Delete error
|
||||
log.Printf("destroy %s: %s", spec.SourceVM, derr)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func vmtxSourceName(l library.Library, item library.Item) string {
|
||||
sum := sha1.Sum([]byte(path.Join(l.Name, item.Name)))
|
||||
return fmt.Sprintf("vmtx-src-%x", sum)
|
||||
}
|
||||
|
||||
// SyncTemplateLibrary converts TemplateLibrary.Source OVF items to VM Template items within TemplateLibrary.Destination
|
||||
// The optional TemplateLibrary.Include func can be used to filter which items are synced.
|
||||
// By default all items that don't exist in the Destination library are synced.
|
||||
// The optional TemplateLibrary.SyncItem func can be used to change how the item is synced, by default SyncTemplateLibraryItem is used.
|
||||
func (c *Manager) SyncTemplateLibrary(ctx context.Context, l TemplateLibrary, items ...library.Item) error {
|
||||
m := library.NewManager(c.Client)
|
||||
var err error
|
||||
if len(items) == 0 {
|
||||
items, err = m.GetLibraryItems(ctx, l.Source.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
templates, err := m.GetLibraryItems(ctx, l.Destination.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existing := make(map[string]*library.Item)
|
||||
for i := range templates {
|
||||
existing[templates[i].Name] = &templates[i]
|
||||
}
|
||||
|
||||
include := l.Include
|
||||
if include == nil {
|
||||
include = c.includeTemplateLibraryItem
|
||||
}
|
||||
|
||||
sync := l.SyncItem
|
||||
if sync == nil {
|
||||
sync = c.SyncTemplateLibraryItem
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
if item.Type != library.ItemTypeOVF {
|
||||
continue
|
||||
}
|
||||
|
||||
// Deploy source VM from library ovf item
|
||||
deploy := Deploy{
|
||||
DeploymentSpec: DeploymentSpec{
|
||||
Name: vmtxSourceName(l.Destination, item),
|
||||
DefaultDatastoreID: l.Destination.Storage[0].DatastoreID,
|
||||
AcceptAllEULA: true,
|
||||
},
|
||||
Target: l.Placement,
|
||||
}
|
||||
|
||||
// Create library vmtx item from source VM
|
||||
storage := &DiskStorage{
|
||||
Datastore: deploy.DeploymentSpec.DefaultDatastoreID,
|
||||
}
|
||||
spec := Template{
|
||||
Name: item.Name,
|
||||
Library: l.Destination.ID,
|
||||
DiskStorage: storage,
|
||||
VMHomeStorage: storage,
|
||||
Placement: &Placement{
|
||||
Folder: deploy.Target.FolderID,
|
||||
ResourcePool: deploy.Target.ResourcePoolID,
|
||||
},
|
||||
}
|
||||
|
||||
if !l.Include(item, existing[item.Name]) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = sync(ctx, item, &deploy, &spec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -796,19 +796,12 @@ github.com/vmware/govmomi/find
|
|||
github.com/vmware/govmomi/list
|
||||
github.com/vmware/govmomi/nfc
|
||||
github.com/vmware/govmomi/object
|
||||
github.com/vmware/govmomi/ovf
|
||||
github.com/vmware/govmomi/property
|
||||
github.com/vmware/govmomi/session
|
||||
github.com/vmware/govmomi/session/keepalive
|
||||
github.com/vmware/govmomi/simulator
|
||||
github.com/vmware/govmomi/simulator/esx
|
||||
github.com/vmware/govmomi/simulator/internal
|
||||
github.com/vmware/govmomi/simulator/vpx
|
||||
github.com/vmware/govmomi/task
|
||||
github.com/vmware/govmomi/vapi/internal
|
||||
github.com/vmware/govmomi/vapi/library
|
||||
github.com/vmware/govmomi/vapi/rest
|
||||
github.com/vmware/govmomi/vapi/vcenter
|
||||
github.com/vmware/govmomi/vim25
|
||||
github.com/vmware/govmomi/vim25/debug
|
||||
github.com/vmware/govmomi/vim25/methods
|
||||
|
@ -955,7 +948,6 @@ golang.org/x/crypto/ssh/terminal
|
|||
golang.org/x/lint
|
||||
golang.org/x/lint/golint
|
||||
# golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2
|
||||
## explicit
|
||||
golang.org/x/mobile/event/key
|
||||
# golang.org/x/mod v0.3.0
|
||||
## explicit
|
||||
|
|
Loading…
Reference in New Issue