update go vendor

This commit is contained in:
sylviamoss 2021-04-09 14:43:29 +02:00 committed by Megan Marsh
parent 38fe79948b
commit f6854f5528
95 changed files with 0 additions and 86043 deletions

1
go.mod
View File

@ -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

View File

@ -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"`
}

View File

@ -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

View File

@ -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()
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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[:]))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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(),
},
}
}

View File

@ -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(),
},
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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(),
},
}
}

View File

@ -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

View File

@ -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{},
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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),
}

View File

@ -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

View File

@ -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),
},
}

View File

@ -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
}

View File

@ -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(),
},
}
}

View File

@ -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(&registerVM{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(),
},
}
}

View File

@ -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,
}

View File

@ -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

View File

@ -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
}

View File

@ -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(),
},
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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{},
}
}

View File

@ -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,
},
}
}

View File

@ -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",
},
}

View File

@ -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(),
},
}
}

View File

@ -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),
}
}

View File

@ -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()
}
}

View File

@ -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-----`)

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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...)
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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(),
},
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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() {}

View File

@ -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(),
},
}
}

View File

@ -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
}

View File

@ -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"},
},
},
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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(),
},
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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) {}

View File

@ -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))
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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,
}

View File

@ -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"},
}

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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
}

8
vendor/modules.txt vendored
View File

@ -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