godeps: update vmware/govmomi to v0.20 release

This commit is contained in:
Doug MacEachern
2019-03-20 10:31:41 -07:00
parent 055061637a
commit 243da8c365
76 changed files with 54784 additions and 303 deletions

View File

@@ -5,12 +5,14 @@ go_library(
srcs = [
"authorization_manager.go",
"cluster_compute_resource.go",
"container.go",
"custom_fields_manager.go",
"datacenter.go",
"datastore.go",
"doc.go",
"dvs.go",
"entity.go",
"environment_browser.go",
"event_manager.go",
"file_manager.go",
"folder.go",
@@ -24,6 +26,7 @@ go_library(
"ip_pool_manager.go",
"license_manager.go",
"model.go",
"object.go",
"option_manager.go",
"os_unix.go",
"os_windows.go",
@@ -38,12 +41,14 @@ go_library(
"session_manager.go",
"simulator.go",
"snapshot.go",
"storage_resource_manager.go",
"task.go",
"task_manager.go",
"user_directory.go",
"view_manager.go",
"virtual_disk_manager.go",
"virtual_machine.go",
"vstorage_object_manager.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/vmware/govmomi/simulator",
importpath = "github.com/vmware/govmomi/simulator",

View File

@@ -33,6 +33,10 @@ type ClusterComputeResource struct {
ruleKey int32
}
func (c *ClusterComputeResource) RenameTask(req *types.Rename_Task) soap.HasFault {
return RenameTask(c, req)
}
type addHost struct {
*ClusterComputeResource
@@ -300,6 +304,7 @@ func CreateClusterComputeResource(f *Folder, name string, spec types.ClusterConf
}
cluster := &ClusterComputeResource{}
cluster.EnvironmentBrowser = newEnvironmentBrowser()
cluster.Name = name
cluster.Summary = &types.ClusterComputeResourceSummary{
UsageSummary: new(types.ClusterUsageSummary),

167
vendor/github.com/vmware/govmomi/simulator/container.go generated vendored Normal file
View File

@@ -0,0 +1,167 @@
/*
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"
"encoding/json"
"log"
"os/exec"
"strings"
"github.com/vmware/govmomi/vim25/types"
)
// container provides methods to manage a container within a simulator VM lifecycle.
type container struct {
id 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 {
Gateway string
IPAddress string
IPPrefixLen int
MacAddress string
}
}
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
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
}
// 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
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}
}
break
}
}
if len(args) == 0 {
return
}
args = append([]string{"run", "-d", "--name", vm.Name}, args...)
cmd := exec.Command("docker", 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
}
cmd := exec.Command("docker", "rm", "-f", c.id)
err := cmd.Run()
if err != nil {
log.Printf("%s %s: %s", vm.Name, cmd.Args, err)
}
}

View File

@@ -36,7 +36,70 @@ func NewCustomFieldsManager(ref types.ManagedObjectReference) object.Reference {
return m
}
func (c *CustomFieldsManager) find(key int32) (int, *types.CustomFieldDef) {
// 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]
@@ -49,6 +112,15 @@ func (c *CustomFieldsManager) find(key int32) (int, *types.CustomFieldDef) {
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,
@@ -58,6 +130,14 @@ func (c *CustomFieldsManager) AddCustomFieldDef(req *types.AddCustomFieldDef) so
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++
@@ -70,12 +150,14 @@ func (c *CustomFieldsManager) AddCustomFieldDef(req *types.AddCustomFieldDef) so
func (c *CustomFieldsManager) RemoveCustomFieldDef(req *types.RemoveCustomFieldDef) soap.HasFault {
body := &methods.RemoveCustomFieldDefBody{}
i, field := c.find(req.Key)
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{}
@@ -85,7 +167,7 @@ func (c *CustomFieldsManager) RemoveCustomFieldDef(req *types.RemoveCustomFieldD
func (c *CustomFieldsManager) RenameCustomFieldDef(req *types.RenameCustomFieldDef) soap.HasFault {
body := &methods.RenameCustomFieldDefBody{}
_, field := c.find(req.Key)
_, field := c.findByKey(req.Key)
if field == nil {
body.Fault_ = Fault("", &types.NotFound{})
return body
@@ -93,19 +175,30 @@ func (c *CustomFieldsManager) RenameCustomFieldDef(req *types.RenameCustomFieldD
field.Name = req.Name
entitiesFieldRename(*field)
body.Res = &types.RenameCustomFieldDefResponse{}
return body
}
func (c *CustomFieldsManager) SetField(req *types.SetField) soap.HasFault {
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()
Map.WithLock(entity, func() {
entity.CustomValue = append(entity.CustomValue, &types.CustomFieldStringValue{
CustomFieldValue: types.CustomFieldValue{Key: req.Key},
Value: req.Value,
})
ctx.WithLock(entity, func() {
entity.CustomValue = append(entity.CustomValue, newValue)
entity.Value = append(entity.Value, newValue)
})
body.Res = &types.SetFieldResponse{}

View File

@@ -31,6 +31,9 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
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
@@ -71,17 +74,28 @@ func (s *DistributedVirtualSwitch) AddDVPortgroupTask(c *types.AddDVPortgroup_Ta
pg.PortKeys = []string{}
s.Portgroup = append(s.Portgroup, pg.Self)
s.Summary.PortgroupName = append(s.Summary.PortgroupName, pg.Name)
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
})
@@ -96,6 +110,8 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
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 {
@@ -110,13 +126,27 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
return nil, &types.AlreadyExists{Name: host.Name}
}
Map.AppendReference(host, &host.Network, s.Portgroup...)
s.Summary.HostMember = append(s.Summary.HostMember, member.Host)
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)
Map.AddReference(pg, &pg.Host, member.Host)
pgs = append(pgs, ref)
pgHosts := append(pg.Host, member.Host)
Map.Update(pg, []types.PropertyChange{
{Name: "host", Val: pgHosts},
})
}
Map.Update(parent, []types.PropertyChange{
{Name: "network", Val: pgs},
})
case types.ConfigSpecOperationRemove:
for _, ref := range host.Vm {
vm := Map.Get(ref).(*VirtualMachine)
@@ -128,12 +158,16 @@ func (s *DistributedVirtualSwitch) ReconfigureDvsTask(req *types.ReconfigureDvs_
}
}
RemoveReference(&s.Summary.HostMember, member.Host)
RemoveReference(&members, member.Host)
case types.ConfigSpecOperationEdit:
return nil, &types.NotSupported{}
}
}
Map.Update(s, []types.PropertyChange{
{Name: "summary.hostMember", Val: members},
})
return nil, nil
})

View File

@@ -33,7 +33,7 @@ func RenameTask(e mo.Entity, r *types.Rename_Task) soap.HasFault {
}
}
obj.Name = r.NewName
Map.Update(e, []types.PropertyChange{{Name: "name", Val: r.NewName}})
return nil, nil
})

View File

@@ -0,0 +1,65 @@
/*
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 (
"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) 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 (b *EnvironmentBrowser) QueryConfigOptionEx(req *types.QueryConfigOptionEx) soap.HasFault {
body := new(methods.QueryConfigOptionExBody)
opt := &types.VirtualMachineConfigOption{
Version: esx.HardwareVersion,
DefaultDevice: esx.VirtualDevice,
}
body.Res = &types.QueryConfigOptionExResponse{
Returnval: opt,
}
return body
}

View File

@@ -13,10 +13,12 @@ go_library(
"host_storage_device_info.go",
"host_system.go",
"performance_manager.go",
"performance_manager_data.go",
"resource_pool.go",
"root_folder.go",
"service_content.go",
"setting.go",
"task_manager.go",
"virtual_device.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/vmware/govmomi/simulator/esx",

View File

@@ -77,6 +77,12 @@ var EventInfo = []types.EventDescriptionEventDetail{
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",

View File

@@ -355,7 +355,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -489,7 +489,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -524,7 +524,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -555,7 +555,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -586,7 +586,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -617,7 +617,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -648,7 +648,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -679,7 +679,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -710,7 +710,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,
@@ -741,7 +741,7 @@ var HostConfigInfo = types.HostConfigInfo{
SubnetMask: "255.0.0.0",
IpV6Config: (*types.HostIpConfigIpV6AddressConfiguration)(nil),
},
Mac: "00:0c:29:81:d8:a0",
Mac: "00:0c:29:81:d8:a0",
DistributedVirtualPort: (*types.DistributedVirtualSwitchPortConnection)(nil),
Portgroup: "Management Network",
Mtu: 1500,

View File

@@ -1730,7 +1730,7 @@ var HostSystem = mo.HostSystem{
OverallMemoryUsage: 1404,
DistributedCpuFairness: 0,
DistributedMemoryFairness: 0,
Uptime: 77229,
Uptime: 77229,
},
OverallStatus: "gray",
RebootRequired: false,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -60,7 +60,7 @@ var ResourcePool = mo.ResourcePool{
DynamicData: types.DynamicData{},
Reservation: types.NewInt64(4121),
ExpandableReservation: types.NewBool(false),
Limit: types.NewInt64(4121),
Limit: types.NewInt64(4121),
Shares: &types.SharesInfo{
DynamicData: types.DynamicData{},
Shares: 9000,
@@ -72,7 +72,7 @@ var ResourcePool = mo.ResourcePool{
DynamicData: types.DynamicData{},
Reservation: types.NewInt64(961),
ExpandableReservation: types.NewBool(false),
Limit: types.NewInt64(961),
Limit: types.NewInt64(961),
Shares: &types.SharesInfo{
DynamicData: types.DynamicData{},
Shares: 9000,
@@ -140,7 +140,7 @@ var ResourcePool = mo.ResourcePool{
DynamicData: types.DynamicData{},
Reservation: types.NewInt64(4121),
ExpandableReservation: types.NewBool(false),
Limit: types.NewInt64(4121),
Limit: types.NewInt64(4121),
Shares: &types.SharesInfo{
DynamicData: types.DynamicData{},
Shares: 9000,
@@ -152,7 +152,7 @@ var ResourcePool = mo.ResourcePool{
DynamicData: types.DynamicData{},
Reservation: types.NewInt64(961),
ExpandableReservation: types.NewBool(false),
Limit: types.NewInt64(961),
Limit: types.NewInt64(961),
Shares: &types.SharesInfo{
DynamicData: types.DynamicData{},
Shares: 9000,

View File

@@ -18,6 +18,9 @@ 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

File diff suppressed because it is too large Load Diff

View File

@@ -199,7 +199,7 @@ var VirtualDevice = []types.BaseVirtualDevice{
ControllerKey: 100,
UnitNumber: types.NewInt32(17),
},
Id: -1,
Id: -1,
AllowUnrestrictedCommunication: types.NewBool(false),
FilterEnable: types.NewBool(true),
FilterInfo: (*types.VirtualMachineVMCIDeviceFilterInfo)(nil),

View File

@@ -138,11 +138,13 @@ func (m *EventManager) formatMessage(event types.BaseEvent) {
}
}
var buf bytes.Buffer
if err := t.Execute(&buf, event); err != nil {
log.Print(err)
if t != nil {
var buf bytes.Buffer
if err := t.Execute(&buf, event); err != nil {
log.Print(err)
}
e.FullFormattedMessage = buf.String()
}
e.FullFormattedMessage = buf.String()
if logEvents {
log.Printf("[%s] %s", id, e.FullFormattedMessage)
@@ -181,6 +183,7 @@ type EventHistoryCollector struct {
m *EventManager
page *ring.Ring
pos int
}
// doEntityEventArgument calls f for each entity argument in the event.
@@ -328,6 +331,7 @@ func (c *EventHistoryCollector) eventMatches(event types.BaseEvent) bool {
// 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
@@ -392,6 +396,66 @@ func (c *EventHistoryCollector) SetCollectorPageSize(ctx *Context, req *types.Se
return body
}
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)

View File

@@ -178,7 +178,7 @@ func (f *FileManager) moveDatastoreFile(req *types.MoveDatastoreFile_Task) types
if !isTrue(req.Force) {
_, err := os.Stat(dst)
if err == nil {
return f.fault(dst, nil, new(types.FileAlreadyExistsFault))
return f.fault(dst, nil, new(types.FileAlreadyExists))
}
}
@@ -216,7 +216,7 @@ func (f *FileManager) copyDatastoreFile(req *types.CopyDatastoreFile_Task) types
if !isTrue(req.Force) {
_, err := os.Stat(dst)
if err == nil {
return f.fault(dst, nil, new(types.FileAlreadyExistsFault))
return f.fault(dst, nil, new(types.FileAlreadyExists))
}
}

View File

@@ -23,7 +23,7 @@ import (
"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"
@@ -177,6 +177,9 @@ func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault {
pod.Name = c.Name
pod.ChildType = []string{"Datastore"}
pod.Summary = new(types.StoragePodSummary)
pod.PodStorageDrsEntry = new(types.PodStorageDrsEntry)
pod.PodStorageDrsEntry.StorageDrsConfig.PodConfig.Enabled = true
f.putChild(pod)
@@ -191,7 +194,10 @@ func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault {
}
func (p *StoragePod) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
return (&Folder{Folder: p.Folder}).MoveIntoFolderTask(c)
f := &Folder{Folder: p.Folder}
res := f.MoveIntoFolderTask(c)
p.ChildEntity = append(p.ChildEntity, f.ChildEntity...)
return res
}
func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.HasFault {
@@ -250,9 +256,26 @@ type createVM struct {
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.Folder.Self, &c.req.Config)
if err != nil {
c.Folder.removeChild(vm)
return nil, err
}
@@ -270,7 +293,7 @@ func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
hosts = cr.Host
}
// Assuming for now that all hosts have access to the datastore
hosts = hostsWithDatastore(hosts, c.req.Config.Files.VmPathName)
host := hosts[rand.Intn(len(hosts))]
vm.Runtime.Host = &host
} else {
@@ -290,13 +313,13 @@ func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
err = vm.create(&c.req.Config, c.register)
if err != nil {
c.Folder.removeChild(vm)
return nil, err
}
c.Folder.putChild(vm)
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)
@@ -333,6 +356,8 @@ func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
},
)
vm.RefreshStorageInfo(c.ctx, nil)
return vm.Reference(), nil
}

View File

@@ -187,7 +187,7 @@ func (s *searchDatastore) search(ds *types.ManagedObjectReference, folder string
return nil
}
func (s *searchDatastore) Run(Task *Task) (types.AnyType, types.BaseMethodFault) {
func (s *searchDatastore) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
p, fault := parseDatastorePath(s.DatastorePath)
if fault != nil {
return nil, fault
@@ -199,6 +199,8 @@ func (s *searchDatastore) Run(Task *Task) (types.AnyType, types.BaseMethodFault)
}
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)

View File

@@ -17,6 +17,7 @@ limitations under the License.
package simulator
import (
"os"
"time"
"github.com/google/uuid"
@@ -27,11 +28,21 @@ import (
"github.com/vmware/govmomi/vim25/types"
)
var (
hostPortUnique = os.Getenv("VCSIM_HOST_PORT_UNIQUE") == "true"
)
type HostSystem struct {
mo.HostSystem
}
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{
@@ -71,6 +82,16 @@ func NewHostSystem(host mo.HostSystem) *HostSystem {
return hs
}
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,
@@ -121,6 +142,7 @@ func CreateDefaultESX(f *Folder) {
addComputeResource(summary, host)
cr := &mo.ComputeResource{Summary: summary}
cr.EnvironmentBrowser = newEnvironmentBrowser()
cr.Self = *host.Parent
cr.Name = host.Name
cr.Host = append(cr.Host, host.Reference())
@@ -155,7 +177,8 @@ func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, t
ConfigurationEx: &types.ComputeResourceConfigInfo{
VmSwapPlacement: string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory),
},
Summary: summary,
Summary: summary,
EnvironmentBrowser: newEnvironmentBrowser(),
}
Map.PutEntity(cr, Map.NewEntity(host))
@@ -173,12 +196,14 @@ func CreateStandaloneHost(f *Folder, spec types.HostConnectSpec) (*HostSystem, t
return host, nil
}
func (h *HostSystem) DestroyTask(req *types.Destroy_Task) soap.HasFault {
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)
f.removeChild(h.Reference())

View File

@@ -30,8 +30,8 @@ import (
)
var ipPool = MustNewIpPool(&types.IpPool{
Id: 1,
Name: "ip-pool",
Id: 1,
Name: "ip-pool",
AvailableIpv4Addresses: 250,
AvailableIpv6Addresses: 250,
AllocatedIpv6Addresses: 0,

View File

@@ -113,6 +113,43 @@ func (m *LicenseManager) RemoveLicense(req *types.RemoveLicense) soap.HasFault {
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
}

View File

@@ -30,6 +30,21 @@ import (
"github.com/vmware/govmomi/vim25/types"
)
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.
@@ -79,6 +94,9 @@ type Model struct {
// Pod specifies the number of StoragePod to create per Cluster
Pod int
// Delay configurations
DelayConfig DelayConfig
// total number of inventory objects, set by Count()
total int
@@ -93,6 +111,11 @@ func ESX() *Model {
Autostart: true,
Datastore: 1,
Machine: 2,
DelayConfig: DelayConfig{
Delay: 0,
DelayJitter: 0,
MethodDelay: nil,
},
}
}
@@ -109,6 +132,11 @@ func VPX() *Model {
ClusterHost: 3,
Datastore: 1,
Machine: 2,
DelayConfig: DelayConfig{
Delay: 0,
DelayJitter: 0,
MethodDelay: nil,
},
}
}
@@ -454,6 +482,9 @@ func (m *Model) Create() error {
}
}
// Turn on delay AFTER we're done building the service content
m.Service.delay = &m.DelayConfig
return nil
}

51
vendor/github.com/vmware/govmomi/simulator/object.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
/*
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 (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
func SetCustomValue(ctx *Context, req *types.SetCustomValue) soap.HasFault {
ctx.Caller = &req.This
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
}

View File

@@ -17,19 +17,243 @@ limitations under the License.
package simulator
import (
"math/rand"
"strconv"
"time"
"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"
)
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 NewPerformanceManager(ref types.ManagedObjectReference) object.Reference {
m := &PerformanceManager{}
m.Self = ref
m.PerfCounter = esx.PerfCounter
if Map.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
}
return m
}
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

@@ -337,6 +337,7 @@ func (rr *retrieveResult) collect(ctx *Context, ref types.ManagedObjectReference
}
rtype := rval.Type()
match := false
for _, spec := range rr.req.SpecSet {
for _, p := range spec.PropSet {
@@ -348,7 +349,7 @@ func (rr *retrieveResult) collect(ctx *Context, ref types.ManagedObjectReference
continue
}
}
match = true
if isTrue(p.All) {
rr.collectAll(ctx, rval, rtype, &content)
continue
@@ -358,7 +359,7 @@ func (rr *retrieveResult) collect(ctx *Context, ref types.ManagedObjectReference
}
}
if len(content.PropSet) != 0 || len(content.MissingSet) != 0 {
if match {
rr.Objects = append(rr.Objects, content)
}
@@ -498,7 +499,12 @@ func (pc *PropertyCollector) RetrievePropertiesEx(ctx *Context, r *types.Retriev
res, fault := pc.collect(ctx, r)
if fault != nil {
body.Fault_ = Fault("", fault)
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 {
@@ -627,9 +633,14 @@ func (pc *PropertyCollector) apply(ctx *Context, update *types.UpdateSet) types.
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 {
wait, cancel = context.WithTimeout(context.Background(), time.Second*time.Duration(*max))
// 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()
@@ -688,6 +699,10 @@ func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpda
pc.updates = nil // clear updates collected by the managed object CRUD listeners
pc.mu.Unlock()
if len(updates) == 0 {
if oneUpdate == true {
body.Res.Returnval = nil
return body
}
continue
}
@@ -732,6 +747,10 @@ func (pc *PropertyCollector) WaitForUpdatesEx(ctx *Context, r *types.WaitForUpda
if len(set.FilterSet) != 0 {
return body
}
if oneUpdate == true {
body.Res.Returnval = nil
return body
}
}
}
}

View File

@@ -47,9 +47,18 @@ func (f *PropertyFilter) DestroyPropertyFilter(ctx *Context, c *types.DestroyPro
// 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 {
continue
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) {

View File

@@ -57,14 +57,24 @@ type RegisterObject interface {
// 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
counter int64
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
@@ -177,6 +187,24 @@ func (r *Registry) Any(kind string) 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()
@@ -443,6 +471,11 @@ 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()

View File

@@ -163,3 +163,101 @@ func (s *SearchIndex) FindByUuid(req *types.FindByUuid) soap.HasFault {
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 := obj.(*VirtualMachine)
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 := obj.(*HostSystem)
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 := obj.(*VirtualMachine)
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 := obj.(*HostSystem)
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

@@ -69,6 +69,14 @@ func NewServiceInstance(content types.ServiceContent, folder mo.Folder) *Service
NewTaskManager(*s.Content.TaskManager),
NewUserDirectory(*s.Content.UserDirectory),
NewOptionManager(s.Content.Setting, setting),
NewStorageResourceManager(*s.Content.StorageResourceManager),
}
switch content.VStorageObjectManager.Type {
case "HostVStorageObjectManager":
// TODO: NewHostVStorageObjectManager(*content.VStorageObjectManager)
case "VcenterVStorageObjectManager":
objects = append(objects, NewVcenterVStorageObjectManager(*content.VStorageObjectManager))
}
if s.Content.CustomFieldsManager != nil {

View File

@@ -24,7 +24,6 @@ import (
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/session"
"github.com/vmware/govmomi/vim25/methods"
@@ -173,6 +172,23 @@ func (s *SessionManager) TerminateSession(ctx *Context, req *types.TerminateSess
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.sessions[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()
@@ -234,7 +250,7 @@ var invalidLogin = Fault("Login failure", new(types.InvalidLogin))
type Context struct {
req *http.Request
res http.ResponseWriter
m *SessionManager
svc *Service
context.Context
Session *Session
@@ -246,7 +262,7 @@ type Context struct {
// 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.m.sessions[cookie.Value]; ok {
if val, ok := c.svc.sm.sessions[cookie.Value]; ok {
c.SetSession(val, false)
}
}
@@ -258,7 +274,7 @@ func (c *Context) SetSession(session Session, login bool) {
session.IpAddress = strings.Split(c.req.RemoteAddr, ":")[0]
session.LastActiveTime = time.Now()
c.m.sessions[session.Key] = session
c.svc.sm.sessions[session.Key] = session
c.Session = &session
if login {

View File

@@ -29,6 +29,7 @@ import (
"io"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"net/http/httptest"
@@ -39,6 +40,7 @@ import (
"sort"
"strconv"
"strings"
"time"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
@@ -65,6 +67,7 @@ type Service struct {
client *vim25.Client
sm *SessionManager
sdk map[string]*Registry
delay *DelayConfig
readAll func(io.Reader) ([]byte, error)
@@ -122,7 +125,7 @@ func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
if session == nil {
switch method.Name {
case "RetrieveServiceContent", "List", "Login", "LoginByToken", "LoginExtensionByCertificate", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
case "RetrieveServiceContent", "PbmRetrieveServiceContent", "List", "Login", "LoginByToken", "LoginExtensionByCertificate", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
// ok for now, TODO: authz
default:
fault := &types.NotAuthenticated{
@@ -147,7 +150,8 @@ func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
return &serverFaultBody{Reason: Fault(msg, fault)}
}
name := method.Name
// 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"
@@ -172,6 +176,25 @@ func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
}
}
// 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))
@@ -230,16 +253,36 @@ type soapEnvelope struct {
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 types.AnyType `xml:",any,typeattr"`
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)
}
// About generates some info about the simulator.
func (s *Service) About(w http.ResponseWriter, r *http.Request) {
var about struct {
@@ -289,6 +332,16 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) {
_ = enc.Encode(&about)
}
// 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
}
}
// RegisterSDK adds an HTTP handler for the Registry's Path and Namespace.
func (s *Service) RegisterSDK(r *Registry) {
if s.ServeMux == nil {
@@ -321,7 +374,7 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
ctx := &Context{
req: r,
res: w,
m: s.sm,
svc: s,
Map: s.sdk[r.URL.Path],
Context: context.Background(),
@@ -350,7 +403,9 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
&soapFault{
Code: f.Code,
String: f.String,
Detail: f.Detail,
Detail: struct {
Fault *faultDetail
}{&faultDetail{f.Detail.Fault}},
},
}
} else {
@@ -431,6 +486,9 @@ func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
// File does not exist, fallthrough to create via PUT logic
fallthrough
case "PUT":
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)
@@ -465,6 +523,37 @@ func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, versions)
}
// 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)
@@ -478,11 +567,12 @@ func (s *Service) NewServer() *Server {
// for use in main.go, where Start() blocks, we can still set ServiceHostName
ts := httptest.NewUnstartedServer(mux)
addr := ts.Listener.Addr().(*net.TCPAddr)
port := strconv.Itoa(addr.Port)
u := &url.URL{
Scheme: "http",
Host: ts.Listener.Addr().String(),
Host: net.JoinHostPort(defaultIP(addr), port),
Path: Map.Path,
User: url.UserPassword("user", "pass"),
}
// Redirect clients to this http server, rather than HostSystem.Name
@@ -510,7 +600,7 @@ func (s *Service) NewServer() *Server {
m.Setting = append(m.Setting,
&types.OptionValue{
Key: "vcsim.server.url",
Value: ts.URL,
Value: u.String(),
},
&types.OptionValue{
Key: "vcsim.server.cert",
@@ -518,6 +608,8 @@ func (s *Service) NewServer() *Server {
},
)
u.User = url.UserPassword("user", "pass")
return &Server{
Server: ts,
URL: u,

View File

@@ -17,6 +17,11 @@ 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"
@@ -27,24 +32,110 @@ type VirtualMachineSnapshot struct {
mo.VirtualMachineSnapshot
}
func (v *VirtualMachineSnapshot) RemoveSnapshotTask(req *types.RemoveSnapshot_Task) soap.HasFault {
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) {
Map.Remove(req.This)
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)
vm.Snapshot.CurrentSnapshot = parent
changes = append(changes, types.PropertyChange{Name: "snapshot.currentSnapshot", Val: parent})
}
vm.Snapshot.RootSnapshotList = removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
rootSnapshots := removeSnapshotInTree(vm.Snapshot.RootSnapshotList, req.This, req.RemoveChildren)
changes = append(changes, types.PropertyChange{Name: "snapshot.rootSnapshotList", Val: rootSnapshots})
if len(vm.Snapshot.RootSnapshotList) == 0 {
vm.Snapshot = nil
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
})
@@ -59,7 +150,11 @@ func (v *VirtualMachineSnapshot) RevertToSnapshotTask(req *types.RevertToSnapsho
task := CreateTask(v, "revertToSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
vm := Map.Get(v.Vm).(*VirtualMachine)
Map.WithLock(vm, func() { vm.Snapshot.CurrentSnapshot = &v.Self })
Map.WithLock(vm, func() {
Map.Update(vm, []types.PropertyChange{
{Name: "snapshot.currentSnapshot", Val: v.Self},
})
})
return nil, nil
})

View File

@@ -0,0 +1,191 @@
/*
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 NewStorageResourceManager(ref types.ManagedObjectReference) object.Reference {
m := &StorageResourceManager{}
m.Self = ref
return m
}
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

@@ -20,6 +20,8 @@ import (
"sync"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/simulator/vpx"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
@@ -34,6 +36,11 @@ type TaskManager struct {
func NewTaskManager(ref types.ManagedObjectReference) object.Reference {
s := &TaskManager{}
s.Self = ref
if Map.IsESX() {
s.Description = esx.Description
} else {
s.Description = vpx.Description
}
Map.AddHandler(s)
return s
}

View File

@@ -132,7 +132,7 @@ func (m *VirtualDiskManager) MoveVirtualDiskTask(req *types.MoveVirtualDisk_Task
SourceDatacenter: req.SourceDatacenter,
DestinationName: dest[i],
DestinationDatacenter: req.DestDatacenter,
Force: req.Force,
Force: req.Force,
})
if err != nil {
@@ -168,7 +168,7 @@ func (m *VirtualDiskManager) CopyVirtualDiskTask(req *types.CopyVirtualDisk_Task
SourceDatacenter: req.SourceDatacenter,
DestinationName: dest[i],
DestinationDatacenter: req.DestDatacenter,
Force: req.Force,
Force: req.Force,
})
if err != nil {

View File

@@ -19,17 +19,18 @@ package simulator
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator/esx"
"github.com/vmware/govmomi/vim25/methods"
@@ -43,18 +44,21 @@ type VirtualMachine struct {
log string
sid int32
run container
}
func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualMachineConfigSpec) (*VirtualMachine, types.BaseMethodFault) {
vm := &VirtualMachine{}
vm.Parent = &parent
Map.Get(parent).(*Folder).putChild(vm)
if spec.Name == "" {
return nil, &types.InvalidVmConfig{Property: "configSpec.name"}
return vm, &types.InvalidVmConfig{Property: "configSpec.name"}
}
if spec.Files == nil || spec.Files.VmPathName == "" {
return nil, &types.InvalidVmConfig{Property: "configSpec.files.vmPathName"}
return vm, &types.InvalidVmConfig{Property: "configSpec.files.vmPathName"}
}
rspec := types.DefaultResourceConfigSpec()
@@ -65,6 +69,10 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
MemoryAllocation: &rspec.MemoryAllocation,
CpuAllocation: &rspec.CpuAllocation,
}
vm.Layout = &types.VirtualMachineFileLayout{}
vm.LayoutEx = &types.VirtualMachineFileLayoutEx{
Timestamp: time.Now(),
}
vm.Snapshot = nil // intentionally set to nil until a snapshot is created
vm.Storage = &types.VirtualMachineStorageInfo{
Timestamp: time.Now(),
@@ -92,7 +100,7 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
MemoryMB: 32,
Uuid: uuid.New().String(),
InstanceUuid: uuid.New().String(),
Version: "vmx-11",
Version: esx.HardwareVersion,
Files: &types.VirtualMachineFileInfo{
SnapshotDirectory: dsPath,
SuspendDirectory: dsPath,
@@ -105,7 +113,7 @@ func NewVirtualMachine(parent types.ManagedObjectReference, spec *types.VirtualM
err := vm.configure(&defaults)
if err != nil {
return nil, err
return vm, err
}
vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
@@ -166,6 +174,7 @@ func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
{spec.Files.VmPathName, &vm.Config.Files.VmPathName},
{spec.Files.VmPathName, &vm.Summary.Config.VmPathName},
{spec.Files.SnapshotDirectory, &vm.Config.Files.SnapshotDirectory},
{spec.Files.SuspendDirectory, &vm.Config.Files.SuspendDirectory},
{spec.Files.LogDirectory, &vm.Config.Files.LogDirectory},
}
@@ -259,7 +268,33 @@ func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
vm.Config.Hardware.NumCoresPerSocket = spec.NumCoresPerSocket
}
vm.Config.ExtraConfig = append(vm.Config.ExtraConfig, spec.ExtraConfig...)
var changes []types.PropertyChange
for _, c := range spec.ExtraConfig {
val := c.GetOptionValue()
key := strings.TrimPrefix(val.Key, "SET.")
if key == val.Key {
vm.Config.ExtraConfig = append(vm.Config.ExtraConfig, c)
continue
}
changes = append(changes, types.PropertyChange{Name: key, Val: val.Value})
switch key {
case "guest.ipAddress":
ip := val.Value.(string)
vm.Guest.Net[0].IpAddress = []string{ip}
changes = append(changes,
types.PropertyChange{Name: "summary." + key, Val: ip},
types.PropertyChange{Name: "guest.net", Val: vm.Guest.Net},
)
case "guest.hostName":
changes = append(changes,
types.PropertyChange{Name: "summary." + key, Val: val.Value},
)
}
}
if len(changes) != 0 {
Map.Update(vm, changes)
}
vm.Config.Modified = time.Now()
}
@@ -298,6 +333,400 @@ func (vm *VirtualMachine) configure(spec *types.VirtualMachineConfigSpec) types.
return vm.configureDevices(spec)
}
func getVMFileType(fileName string) types.VirtualMachineFileLayoutExFileType {
var fileType types.VirtualMachineFileLayoutExFileType
fileExt := path.Ext(fileName)
fileNameNoExt := strings.TrimSuffix(fileName, fileExt)
switch fileExt {
case ".vmx":
fileType = types.VirtualMachineFileLayoutExFileTypeConfig
case ".core":
fileType = types.VirtualMachineFileLayoutExFileTypeCore
case ".vmdk":
fileType = types.VirtualMachineFileLayoutExFileTypeDiskDescriptor
if strings.HasSuffix(fileNameNoExt, "-digest") {
fileType = types.VirtualMachineFileLayoutExFileTypeDigestDescriptor
}
extentSuffixes := []string{"-flat", "-delta", "-s", "-rdm", "-rdmp"}
for _, suffix := range extentSuffixes {
if strings.HasSuffix(fileNameNoExt, suffix) {
fileType = types.VirtualMachineFileLayoutExFileTypeDiskExtent
} else if strings.HasSuffix(fileNameNoExt, "-digest"+suffix) {
fileType = types.VirtualMachineFileLayoutExFileTypeDigestExtent
}
}
case ".psf":
fileType = types.VirtualMachineFileLayoutExFileTypeDiskReplicationState
case ".vmxf":
fileType = types.VirtualMachineFileLayoutExFileTypeExtendedConfig
case ".vmft":
fileType = types.VirtualMachineFileLayoutExFileTypeFtMetadata
case ".log":
fileType = types.VirtualMachineFileLayoutExFileTypeLog
case ".nvram":
fileType = types.VirtualMachineFileLayoutExFileTypeNvram
case ".png", ".bmp":
fileType = types.VirtualMachineFileLayoutExFileTypeScreenshot
case ".vmsn":
fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotData
case ".vmsd":
fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotList
case ".xml":
if strings.HasSuffix(fileNameNoExt, "-aux") {
fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotManifestList
}
case ".stat":
fileType = types.VirtualMachineFileLayoutExFileTypeStat
case ".vmss":
fileType = types.VirtualMachineFileLayoutExFileTypeSuspend
case ".vmem":
if strings.Contains(fileNameNoExt, "Snapshot") {
fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotMemory
} else {
fileType = types.VirtualMachineFileLayoutExFileTypeSuspendMemory
}
case ".vswp":
if strings.HasPrefix(fileNameNoExt, "vmx-") {
fileType = types.VirtualMachineFileLayoutExFileTypeUwswap
} else {
fileType = types.VirtualMachineFileLayoutExFileTypeSwap
}
case "":
if strings.HasPrefix(fileNameNoExt, "imcf-") {
fileType = types.VirtualMachineFileLayoutExFileTypeGuestCustomization
}
}
return fileType
}
func (vm *VirtualMachine) addFileLayoutEx(datastorePath object.DatastorePath, fileSize int64) int32 {
var newKey int32
for _, layoutFile := range vm.LayoutEx.File {
if layoutFile.Name == datastorePath.String() {
return layoutFile.Key
}
if layoutFile.Key >= newKey {
newKey = layoutFile.Key + 1
}
}
fileType := getVMFileType(filepath.Base(datastorePath.Path))
switch fileType {
case types.VirtualMachineFileLayoutExFileTypeNvram, types.VirtualMachineFileLayoutExFileTypeSnapshotList:
vm.addConfigLayout(datastorePath.Path)
case types.VirtualMachineFileLayoutExFileTypeLog:
vm.addLogLayout(datastorePath.Path)
case types.VirtualMachineFileLayoutExFileTypeSwap:
vm.addSwapLayout(datastorePath.String())
}
vm.LayoutEx.File = append(vm.LayoutEx.File, types.VirtualMachineFileLayoutExFileInfo{
Accessible: types.NewBool(true),
BackingObjectId: "",
Key: newKey,
Name: datastorePath.String(),
Size: fileSize,
Type: string(fileType),
UniqueSize: fileSize,
})
vm.LayoutEx.Timestamp = time.Now()
vm.updateStorage()
return newKey
}
func (vm *VirtualMachine) addConfigLayout(name string) {
for _, config := range vm.Layout.ConfigFile {
if config == name {
return
}
}
vm.Layout.ConfigFile = append(vm.Layout.ConfigFile, name)
vm.updateStorage()
}
func (vm *VirtualMachine) addLogLayout(name string) {
for _, log := range vm.Layout.LogFile {
if log == name {
return
}
}
vm.Layout.LogFile = append(vm.Layout.LogFile, name)
vm.updateStorage()
}
func (vm *VirtualMachine) addSwapLayout(name string) {
vm.Layout.SwapFile = name
vm.updateStorage()
}
func (vm *VirtualMachine) addSnapshotLayout(snapshot types.ManagedObjectReference, dataKey int32) {
for _, snapshotLayout := range vm.Layout.Snapshot {
if snapshotLayout.Key == snapshot {
return
}
}
var snapshotFiles []string
for _, file := range vm.LayoutEx.File {
if file.Key == dataKey || file.Type == "diskDescriptor" {
snapshotFiles = append(snapshotFiles, file.Name)
}
}
vm.Layout.Snapshot = append(vm.Layout.Snapshot, types.VirtualMachineFileLayoutSnapshotLayout{
Key: snapshot,
SnapshotFile: snapshotFiles,
})
vm.updateStorage()
}
func (vm *VirtualMachine) addSnapshotLayoutEx(snapshot types.ManagedObjectReference, dataKey int32, memoryKey int32) {
for _, snapshotLayoutEx := range vm.LayoutEx.Snapshot {
if snapshotLayoutEx.Key == snapshot {
return
}
}
vm.LayoutEx.Snapshot = append(vm.LayoutEx.Snapshot, types.VirtualMachineFileLayoutExSnapshotLayout{
DataKey: dataKey,
Disk: vm.LayoutEx.Disk,
Key: snapshot,
MemoryKey: memoryKey,
})
vm.LayoutEx.Timestamp = time.Now()
vm.updateStorage()
}
// Updates both vm.Layout.Disk and vm.LayoutEx.Disk
func (vm *VirtualMachine) updateDiskLayouts() types.BaseMethodFault {
var disksLayout []types.VirtualMachineFileLayoutDiskLayout
var disksLayoutEx []types.VirtualMachineFileLayoutExDiskLayout
disks := object.VirtualDeviceList(vm.Config.Hardware.Device).SelectByType((*types.VirtualDisk)(nil))
for _, disk := range disks {
disk := disk.(*types.VirtualDisk)
diskBacking := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
diskLayout := &types.VirtualMachineFileLayoutDiskLayout{Key: disk.Key}
diskLayoutEx := &types.VirtualMachineFileLayoutExDiskLayout{Key: disk.Key}
// Iterate through disk and its parents
for {
dFileName := diskBacking.GetVirtualDeviceFileBackingInfo().FileName
var fileKeys []int32
dm := Map.VirtualDiskManager()
// Add disk descriptor and extent files
for _, diskName := range dm.names(dFileName) {
// get full path including datastore location
p, fault := parseDatastorePath(diskName)
if fault != nil {
return fault
}
datastore := vm.useDatastore(p.Datastore)
dFilePath := path.Join(datastore.Info.GetDatastoreInfo().Url, p.Path)
var fileSize int64
// If file can not be opened - fileSize will be 0
if dFileInfo, err := os.Stat(dFilePath); err == nil {
fileSize = dFileInfo.Size()
}
diskKey := vm.addFileLayoutEx(*p, fileSize)
fileKeys = append(fileKeys, diskKey)
}
diskLayout.DiskFile = append(diskLayout.DiskFile, dFileName)
diskLayoutEx.Chain = append(diskLayoutEx.Chain, types.VirtualMachineFileLayoutExDiskUnit{
FileKey: fileKeys,
})
if parent := diskBacking.Parent; parent != nil {
diskBacking = parent
} else {
break
}
}
disksLayout = append(disksLayout, *diskLayout)
disksLayoutEx = append(disksLayoutEx, *diskLayoutEx)
}
vm.Layout.Disk = disksLayout
vm.LayoutEx.Disk = disksLayoutEx
vm.LayoutEx.Timestamp = time.Now()
vm.updateStorage()
return nil
}
func (vm *VirtualMachine) updateStorage() types.BaseMethodFault {
// Committed - sum of Size for each file in vm.LayoutEx.File
// Unshared - sum of Size for each disk (.vmdk) in vm.LayoutEx.File
// Uncommitted - disk capacity minus disk usage (only currently used disk)
var datastoresUsage []types.VirtualMachineUsageOnDatastore
disks := object.VirtualDeviceList(vm.Config.Hardware.Device).SelectByType((*types.VirtualDisk)(nil))
for _, file := range vm.LayoutEx.File {
p, fault := parseDatastorePath(file.Name)
if fault != nil {
return fault
}
datastore := vm.useDatastore(p.Datastore)
dsUsage := &types.VirtualMachineUsageOnDatastore{
Datastore: datastore.Self,
}
for idx, usage := range datastoresUsage {
if usage.Datastore == datastore.Self {
datastoresUsage = append(datastoresUsage[:idx], datastoresUsage[idx+1:]...)
dsUsage = &usage
break
}
}
dsUsage.Committed = file.Size
if path.Ext(file.Name) == ".vmdk" {
dsUsage.Unshared = file.Size
}
for _, disk := range disks {
disk := disk.(*types.VirtualDisk)
backing := disk.Backing.(types.BaseVirtualDeviceFileBackingInfo).GetVirtualDeviceFileBackingInfo()
if backing.FileName == file.Name {
dsUsage.Uncommitted = disk.CapacityInBytes
}
}
datastoresUsage = append(datastoresUsage, *dsUsage)
}
vm.Storage.PerDatastoreUsage = datastoresUsage
vm.Storage.Timestamp = time.Now()
storageSummary := &types.VirtualMachineStorageSummary{
Timestamp: time.Now(),
}
for _, usage := range datastoresUsage {
storageSummary.Committed += usage.Committed
storageSummary.Uncommitted += usage.Uncommitted
storageSummary.Unshared += usage.Unshared
}
vm.Summary.Storage = storageSummary
return nil
}
func (vm *VirtualMachine) RefreshStorageInfo(ctx *Context, req *types.RefreshStorageInfo) soap.HasFault {
body := new(methods.RefreshStorageInfoBody)
if vm.Runtime.Host == nil {
// VM not fully created
return body
}
// Validate that all files in vm.LayoutEx.File can still be found
for idx := len(vm.LayoutEx.File) - 1; idx >= 0; idx-- {
file := vm.LayoutEx.File[idx]
p, fault := parseDatastorePath(file.Name)
if fault != nil {
body.Fault_ = Fault("", fault)
return body
}
if _, err := os.Stat(p.String()); err != nil {
vm.LayoutEx.File = append(vm.LayoutEx.File[:idx], vm.LayoutEx.File[idx+1:]...)
}
}
// Directories will be used to locate VM files.
// Does not include information about virtual disk file locations.
locations := []string{
vm.Config.Files.VmPathName,
vm.Config.Files.SnapshotDirectory,
vm.Config.Files.LogDirectory,
vm.Config.Files.SuspendDirectory,
vm.Config.Files.FtMetadataDirectory,
}
for _, directory := range locations {
if directory == "" {
continue
}
p, fault := parseDatastorePath(directory)
if fault != nil {
body.Fault_ = Fault("", fault)
return body
}
datastore := vm.useDatastore(p.Datastore)
directory := path.Join(datastore.Info.GetDatastoreInfo().Url, p.Path)
if path.Ext(p.Path) == ".vmx" {
directory = path.Dir(directory) // vm.Config.Files.VmPathName can be a directory or full path to .vmx
}
if _, err := os.Stat(directory); err != nil {
// Can not access the directory
continue
}
files, err := ioutil.ReadDir(directory)
if err != nil {
body.Fault_ = soap.ToSoapFault(err)
return body
}
for _, file := range files {
datastorePath := object.DatastorePath{
Datastore: p.Datastore,
Path: strings.TrimPrefix(file.Name(), datastore.Info.GetDatastoreInfo().Url),
}
vm.addFileLayoutEx(datastorePath, file.Size())
}
}
fault := vm.updateDiskLayouts()
if fault != nil {
body.Fault_ = Fault("", fault)
return body
}
vm.LayoutEx.Timestamp = time.Now()
return body
}
func (vm *VirtualMachine) useDatastore(name string) *Datastore {
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
@@ -321,8 +750,8 @@ func (vm *VirtualMachine) createFile(spec string, name string, register bool) (*
file := path.Join(ds.Info.GetDatastoreInfo().Url, p.Path)
if name != "" {
if path.Ext(file) != "" {
file = path.Dir(file)
if path.Ext(p.Path) == ".vmx" {
file = path.Dir(file) // vm.Config.Files.VmPathName can be a directory or full path to .vmx
}
file = path.Join(file, name)
@@ -380,6 +809,15 @@ func (vm *VirtualMachine) logPrintf(format string, v ...interface{}) {
func (vm *VirtualMachine) create(spec *types.VirtualMachineConfigSpec, register bool) types.BaseMethodFault {
vm.apply(spec)
if spec.Version != "" {
v := strings.TrimPrefix(spec.Version, "vmx-")
_, err := strconv.Atoi(v)
if err != nil {
log.Printf("unsupported hardware version: %s", spec.Version)
return new(types.NotSupported)
}
}
files := []struct {
spec string
name string
@@ -456,20 +894,18 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
d := device.GetVirtualDevice()
var controller types.BaseVirtualController
if d.Key < 0 {
// Choose a unique key
if d.Key == -1 {
d.Key = devices.NewKey()
}
if d.Key <= 0 {
// Keys can't be negative; Key 0 is reserved
d.Key = devices.NewKey()
d.Key *= -1
}
for {
if devices.FindByKey(d.Key) == nil {
break
}
d.Key++
// Choose a unique key
for {
if devices.FindByKey(d.Key) == nil {
break
}
d.Key++
}
label := devices.Name(device)
@@ -481,10 +917,12 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
case types.BaseVirtualEthernetCard:
controller = devices.PickController((*types.VirtualPCIController)(nil))
var net types.ManagedObjectReference
var name string
switch b := d.Backing.(type) {
case *types.VirtualEthernetCardNetworkBackingInfo:
summary = b.DeviceName
name = b.DeviceName
summary = name
net = Map.FindByName(b.DeviceName, dc.Network).Reference()
b.Network = &net
case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
@@ -493,20 +931,35 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
net.Value = b.Port.PortgroupKey
}
vm.Network = append(vm.Network, net)
Map.Update(vm, []types.PropertyChange{
{Name: "summary.config.numEthernetCards", Val: vm.Summary.Config.NumEthernetCards + 1},
{Name: "network", Val: append(vm.Network, net)},
})
c := x.GetVirtualEthernetCard()
if c.MacAddress == "" {
c.MacAddress = vm.generateMAC()
}
if spec.Operation == types.VirtualDeviceConfigSpecOperationAdd {
vm.Guest.Net = append(vm.Guest.Net, types.GuestNicInfo{
Network: name,
IpAddress: nil,
MacAddress: c.MacAddress,
Connected: true,
DeviceConfigId: c.Key,
})
}
case *types.VirtualDisk:
summary = fmt.Sprintf("%s KB", numberToString(x.CapacityInKB, ','))
switch b := d.Backing.(type) {
case types.BaseVirtualDeviceFileBackingInfo:
info := b.GetVirtualDeviceFileBackingInfo()
var path object.DatastorePath
path.FromString(info.FileName)
if info.FileName == "" {
filename, err := vm.genVmdkPath()
if path.Path == "" {
filename, err := vm.genVmdkPath(path)
if err != nil {
return err
}
@@ -522,6 +975,10 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
return err
}
Map.Update(vm, []types.PropertyChange{
{Name: "summary.config.numVirtualDisks", Val: vm.Summary.Config.NumVirtualDisks + 1},
})
p, _ := parseDatastorePath(info.FileName)
host := Map.Get(*vm.Runtime.Host).(*HostSystem)
@@ -535,6 +992,8 @@ func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec
// XXX: compare disk size and free space until windows stat is supported
ds.Summary.FreeSpace -= getDiskSize(x)
ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
vm.updateDiskLayouts()
}
}
@@ -591,6 +1050,11 @@ func (vm *VirtualMachine) removeDevice(devices object.VirtualDeviceList, spec *t
})
}
}
Map.Update(vm, []types.PropertyChange{
{Name: "summary.config.numVirtualDisks", Val: vm.Summary.Config.NumVirtualDisks - 1},
})
vm.updateDiskLayouts()
case types.BaseVirtualEthernetCard:
var net types.ManagedObjectReference
@@ -602,7 +1066,12 @@ func (vm *VirtualMachine) removeDevice(devices object.VirtualDeviceList, spec *t
net.Value = b.Port.PortgroupKey
}
RemoveReference(&vm.Network, net)
networks := vm.Network
RemoveReference(&networks, net)
Map.Update(vm, []types.PropertyChange{
{Name: "summary.config.numEthernetCards", Val: vm.Summary.Config.NumEthernetCards - 1},
{Name: "network", Val: networks},
})
}
break
@@ -611,9 +1080,16 @@ func (vm *VirtualMachine) removeDevice(devices object.VirtualDeviceList, spec *t
return devices
}
func (vm *VirtualMachine) genVmdkPath() (string, types.BaseMethodFault) {
vmdir := path.Dir(vm.Config.Files.VmPathName)
func (vm *VirtualMachine) genVmdkPath(p object.DatastorePath) (string, types.BaseMethodFault) {
if p.Datastore == "" {
p.FromString(vm.Config.Files.VmPathName)
}
if p.Path == "" {
p.Path = vm.Config.Name
} else {
p.Path = path.Dir(p.Path)
}
vmdir := p.String()
index := 0
for {
var filename string
@@ -651,13 +1127,22 @@ func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec)
switch dspec.Operation {
case types.VirtualDeviceConfigSpecOperationAdd:
if devices.FindByKey(device.Key) != nil {
if vm.Self.Value != "" { // moid isn't set until CreateVM is done
return invalid
if devices.FindByKey(device.Key) != nil && device.ControllerKey == 0 {
// Note: real ESX does not allow adding base controllers (ControllerKey = 0)
// after VM is created (returns success but device is not added).
continue
} else if device.UnitNumber != nil && devices.SelectByType(dspec.Device).Select(func(d types.BaseVirtualDevice) bool {
base := d.GetVirtualDevice()
if base.UnitNumber != nil {
if base.ControllerKey != device.ControllerKey {
return false
}
return *base.UnitNumber == *device.UnitNumber
}
// In this case, the CreateVM() spec included one of the default devices
devices = vm.removeDevice(devices, dspec)
return false
}) != nil {
// UnitNumber for this device type is taken
return invalid
}
err := vm.configureDevice(devices, dspec)
@@ -686,7 +1171,11 @@ func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec)
}
}
vm.Config.Hardware.Device = []types.BaseVirtualDevice(devices)
Map.Update(vm, []types.PropertyChange{
{Name: "config.hardware.device", Val: []types.BaseVirtualDevice(devices)},
})
vm.updateDiskLayouts()
return nil
}
@@ -717,16 +1206,19 @@ func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
event := c.event()
switch c.state {
case types.VirtualMachinePowerStatePoweredOn:
c.run.start(c.VirtualMachine)
c.ctx.postEvent(
&types.VmStartingEvent{VmEvent: event},
&types.VmPoweredOnEvent{VmEvent: event},
)
case types.VirtualMachinePowerStatePoweredOff:
c.run.stop(c.VirtualMachine)
c.ctx.postEvent(
&types.VmStoppingEvent{VmEvent: event},
&types.VmPoweredOffEvent{VmEvent: event},
)
case types.VirtualMachinePowerStateSuspended:
c.run.pause(c.VirtualMachine)
c.ctx.postEvent(
&types.VmSuspendingEvent{VmEvent: event},
&types.VmSuspendedEvent{VmEvent: event},
@@ -823,6 +1315,25 @@ func (vm *VirtualMachine) ReconfigVMTask(ctx *Context, req *types.ReconfigVM_Tas
}
}
func (vm *VirtualMachine) UpgradeVMTask(req *types.UpgradeVM_Task) soap.HasFault {
body := &methods.UpgradeVM_TaskBody{}
task := CreateTask(vm, "upgradeVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
if vm.Config.Version != esx.HardwareVersion {
Map.Update(vm, []types.PropertyChange{{
Name: "config.version", Val: esx.HardwareVersion,
}})
}
return nil, nil
})
body.Res = &types.UpgradeVM_TaskResponse{
Returnval: task.Run(),
}
return body
}
func (vm *VirtualMachine) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
task := CreateTask(vm, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
r := vm.UnregisterVM(ctx, &types.UnregisterVM{
@@ -848,6 +1359,8 @@ func (vm *VirtualMachine) DestroyTask(ctx *Context, req *types.Destroy_Task) soa
Datacenter: &dc,
})
vm.run.remove(vm)
return nil, nil
})
@@ -858,6 +1371,10 @@ func (vm *VirtualMachine) DestroyTask(ctx *Context, req *types.Destroy_Task) soa
}
}
func (vm *VirtualMachine) SetCustomValue(ctx *Context, req *types.SetCustomValue) soap.HasFault {
return SetCustomValue(ctx, req)
}
func (vm *VirtualMachine) UnregisterVM(ctx *Context, c *types.UnregisterVM) soap.HasFault {
r := &methods.UnregisterVMBody{}
@@ -917,21 +1434,24 @@ func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soa
},
}
for _, device := range vm.Config.Hardware.Device {
defaultDevices := object.VirtualDeviceList(esx.VirtualDevice)
devices := vm.Config.Hardware.Device
for _, device := range devices {
var fop types.VirtualDeviceConfigSpecFileOperation
switch device.(type) {
if defaultDevices.Find(object.VirtualDeviceList(devices).Name(device)) != nil {
// Default devices are added during CreateVMTask
continue
}
switch disk := device.(type) {
case *types.VirtualDisk:
// TODO: consider VirtualMachineCloneSpec.DiskMoveType
fop = types.VirtualDeviceConfigSpecFileOperationCreate
device = &types.VirtualDisk{
VirtualDevice: types.VirtualDevice{
Backing: &types.VirtualDiskFlatVer2BackingInfo{
DiskMode: string(types.VirtualDiskModePersistent),
// Leave FileName empty so CreateVM will just create a new one under VmPathName
},
},
}
// Leave FileName empty so CreateVM will just create a new one under VmPathName
disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).FileName = ""
disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).Parent = nil
}
config.DeviceChange = append(config.DeviceChange, &types.VirtualDeviceConfigSpec{
@@ -956,6 +1476,9 @@ func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soa
ref := ctask.Info.Result.(types.ManagedObjectReference)
clone := Map.Get(ref).(*VirtualMachine)
clone.configureDevices(&types.VirtualMachineConfigSpec{DeviceChange: req.Spec.Location.DeviceChange})
if req.Spec.Config != nil && req.Spec.Config.DeviceChange != nil {
clone.configureDevices(&types.VirtualMachineConfigSpec{DeviceChange: req.Spec.Config.DeviceChange})
}
ctx.postEvent(&types.VmClonedEvent{
VmCloneEvent: types.VmCloneEvent{VmEvent: clone.event()},
@@ -980,7 +1503,7 @@ func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFau
ds := Map.Get(*ref).(*Datastore)
Map.RemoveReference(ds, &ds.Vm, *ref)
// TODO: migrate vm.Config.Files (and vm.Summary.Config.VmPathName)
// TODO: migrate vm.Config.Files, vm.Summary.Config.VmPathName, vm.Layout and vm.LayoutEx
changes = append(changes, types.PropertyChange{Name: "datastore", Val: []types.ManagedObjectReference{*ref}})
}
@@ -989,7 +1512,7 @@ func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFau
pool := Map.Get(*ref).(*ResourcePool)
Map.RemoveReference(pool, &pool.Vm, *ref)
changes = append(changes, types.PropertyChange{Name: "resourcePool", Val: *ref})
changes = append(changes, types.PropertyChange{Name: "resourcePool", Val: ref})
}
if ref := req.Spec.Host; ref != nil {
@@ -997,8 +1520,8 @@ func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFau
Map.RemoveReference(host, &host.Vm, *ref)
changes = append(changes,
types.PropertyChange{Name: "runtime.host", Val: *ref},
types.PropertyChange{Name: "summary.runtime.host", Val: *ref},
types.PropertyChange{Name: "runtime.host", Val: ref},
types.PropertyChange{Name: "summary.runtime.host", Val: ref},
)
}
@@ -1016,6 +1539,8 @@ func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFau
func (vm *VirtualMachine) CreateSnapshotTask(req *types.CreateSnapshot_Task) soap.HasFault {
task := CreateTask(vm, "createSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
var changes []types.PropertyChange
if vm.Snapshot == nil {
vm.Snapshot = &types.VirtualMachineSnapshotInfo{}
}
@@ -1047,10 +1572,16 @@ func (vm *VirtualMachine) CreateSnapshotTask(req *types.CreateSnapshot_Task) soa
ss := findSnapshotInTree(vm.Snapshot.RootSnapshotList, *cur)
ss.ChildSnapshotList = append(ss.ChildSnapshotList, treeItem)
} else {
vm.Snapshot.RootSnapshotList = append(vm.Snapshot.RootSnapshotList, treeItem)
changes = append(changes, types.PropertyChange{
Name: "snapshot.rootSnapshotList",
Val: append(vm.Snapshot.RootSnapshotList, treeItem),
})
}
vm.Snapshot.CurrentSnapshot = &snapshot.Self
snapshot.createSnapshotFiles()
changes = append(changes, types.PropertyChange{Name: "snapshot.currentSnapshot", Val: snapshot.Self})
Map.Update(vm, changes)
return nil, nil
})
@@ -1082,7 +1613,7 @@ func (vm *VirtualMachine) RevertToCurrentSnapshotTask(req *types.RevertToCurrent
return body
}
func (vm *VirtualMachine) RemoveAllSnapshotsTask(req *types.RemoveAllSnapshots_Task) soap.HasFault {
func (vm *VirtualMachine) RemoveAllSnapshotsTask(ctx *Context, req *types.RemoveAllSnapshots_Task) soap.HasFault {
task := CreateTask(vm, "RemoveAllSnapshots", func(t *Task) (types.AnyType, types.BaseMethodFault) {
if vm.Snapshot == nil {
return nil, nil
@@ -1090,9 +1621,12 @@ func (vm *VirtualMachine) RemoveAllSnapshotsTask(req *types.RemoveAllSnapshots_T
refs := allSnapshotsInTree(vm.Snapshot.RootSnapshotList)
vm.Snapshot = nil
Map.Update(vm, []types.PropertyChange{
{Name: "snapshot", Val: nil},
})
for _, ref := range refs {
Map.Get(ref).(*VirtualMachineSnapshot).removeSnapshotFiles(ctx)
Map.Remove(ref)
}
@@ -1126,6 +1660,7 @@ func (vm *VirtualMachine) ShutdownGuest(ctx *Context, c *types.ShutdownGuest) so
&types.VmGuestShutdownEvent{VmEvent: event},
&types.VmPoweredOffEvent{VmEvent: event},
)
vm.run.stop(vm)
Map.Update(vm, []types.PropertyChange{
{Name: "runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},

View File

@@ -4,9 +4,12 @@ go_library(
name = "go_default_library",
srcs = [
"doc.go",
"performance_manager.go",
"performance_manager_data.go",
"root_folder.go",
"service_content.go",
"setting.go",
"task_manager.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/vmware/govmomi/simulator/vpx",
importpath = "github.com/vmware/govmomi/simulator/vpx",

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

@@ -0,0 +1,404 @@
/*
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 (
"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 NewVcenterVStorageObjectManager(ref types.ManagedObjectReference) object.Reference {
m := &VcenterVStorageObjectManager{}
m.Self = ref
m.objects = make(map[types.ManagedObjectReference]map[types.ID]*VStorageObject)
return m
}
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(req *types.RetrieveVStorageObject) soap.HasFault {
body := new(methods.RetrieveVStorageObjectBody)
obj := m.object(req.Datastore, req.Id)
if obj == nil {
body.Fault_ = Fault("", new(types.InvalidArgument))
} else {
body.Res = &types.RetrieveVStorageObjectResponse{
Returnval: obj.VStorageObject,
}
}
return body
}
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.BaseConfigInfo.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)
dm := Map.VirtualDiskManager()
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), 0755)
}
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 == false {
err := dm.createVirtualDisk(types.VirtualDeviceConfigSpecFileOperationCreate, &types.CreateVirtualDisk_Task{
Datacenter: &dc.Self,
Name: path.String(),
})
if err != nil {
return nil, err
}
}
obj.Config.BaseConfigInfo.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(&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
}