1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777 |
- /*
- Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package simulator
- import (
- "bytes"
- "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"
- "github.com/vmware/govmomi/vim25/mo"
- "github.com/vmware/govmomi/vim25/soap"
- "github.com/vmware/govmomi/vim25/types"
- )
- type VirtualMachine struct {
- mo.VirtualMachine
- 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 vm, &types.InvalidVmConfig{Property: "configSpec.name"}
- }
- if spec.Files == nil || spec.Files.VmPathName == "" {
- return vm, &types.InvalidVmConfig{Property: "configSpec.files.vmPathName"}
- }
- rspec := types.DefaultResourceConfigSpec()
- vm.Guest = &types.GuestInfo{}
- vm.Config = &types.VirtualMachineConfigInfo{
- ExtraConfig: []types.BaseOptionValue{&types.OptionValue{Key: "govcsim", Value: "TRUE"}},
- Tools: &types.ToolsConfigInfo{},
- 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(),
- }
- vm.Summary.Guest = &types.VirtualMachineGuestSummary{}
- vm.Summary.Vm = &vm.Self
- vm.Summary.Storage = &types.VirtualMachineStorageSummary{
- Timestamp: time.Now(),
- }
- // Append VM Name as the directory name if not specified
- if strings.HasSuffix(spec.Files.VmPathName, "]") { // e.g. "[datastore1]"
- spec.Files.VmPathName += " " + spec.Name
- }
- if !strings.HasSuffix(spec.Files.VmPathName, ".vmx") {
- spec.Files.VmPathName = path.Join(spec.Files.VmPathName, spec.Name+".vmx")
- }
- dsPath := path.Dir(spec.Files.VmPathName)
- defaults := types.VirtualMachineConfigSpec{
- NumCPUs: 1,
- NumCoresPerSocket: 1,
- MemoryMB: 32,
- Uuid: uuid.New().String(),
- InstanceUuid: uuid.New().String(),
- Version: esx.HardwareVersion,
- Files: &types.VirtualMachineFileInfo{
- SnapshotDirectory: dsPath,
- SuspendDirectory: dsPath,
- LogDirectory: dsPath,
- },
- }
- // Add the default devices
- defaults.DeviceChange, _ = object.VirtualDeviceList(esx.VirtualDevice).ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
- err := vm.configure(&defaults)
- if err != nil {
- return vm, err
- }
- vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
- vm.Runtime.ConnectionState = types.VirtualMachineConnectionStateConnected
- vm.Summary.Runtime = vm.Runtime
- vm.Summary.QuickStats.GuestHeartbeatStatus = types.ManagedEntityStatusGray
- vm.Summary.OverallStatus = types.ManagedEntityStatusGreen
- vm.ConfigStatus = types.ManagedEntityStatusGreen
- return vm, nil
- }
- func (vm *VirtualMachine) event() types.VmEvent {
- host := Map.Get(*vm.Runtime.Host).(*HostSystem)
- return types.VmEvent{
- Event: types.Event{
- Datacenter: datacenterEventArgument(host),
- ComputeResource: host.eventArgumentParent(),
- Host: host.eventArgument(),
- Vm: &types.VmEventArgument{
- EntityEventArgument: types.EntityEventArgument{Name: vm.Name},
- Vm: vm.Self,
- },
- },
- }
- }
- func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
- if spec.Files == nil {
- spec.Files = new(types.VirtualMachineFileInfo)
- }
- apply := []struct {
- src string
- dst *string
- }{
- {spec.AlternateGuestName, &vm.Config.AlternateGuestName},
- {spec.Annotation, &vm.Config.Annotation},
- {spec.Firmware, &vm.Config.Firmware},
- {spec.InstanceUuid, &vm.Config.InstanceUuid},
- {spec.LocationId, &vm.Config.LocationId},
- {spec.NpivWorldWideNameType, &vm.Config.NpivWorldWideNameType},
- {spec.Name, &vm.Name},
- {spec.Name, &vm.Config.Name},
- {spec.Name, &vm.Summary.Config.Name},
- {spec.GuestId, &vm.Config.GuestId},
- {spec.GuestId, &vm.Config.GuestFullName},
- {spec.GuestId, &vm.Summary.Guest.GuestId},
- {spec.GuestId, &vm.Summary.Config.GuestId},
- {spec.GuestId, &vm.Summary.Config.GuestFullName},
- {spec.Uuid, &vm.Config.Uuid},
- {spec.Uuid, &vm.Summary.Config.Uuid},
- {spec.InstanceUuid, &vm.Config.InstanceUuid},
- {spec.InstanceUuid, &vm.Summary.Config.InstanceUuid},
- {spec.Version, &vm.Config.Version},
- {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},
- }
- for _, f := range apply {
- if f.src != "" {
- *f.dst = f.src
- }
- }
- applyb := []struct {
- src *bool
- dst **bool
- }{
- {spec.NestedHVEnabled, &vm.Config.NestedHVEnabled},
- {spec.CpuHotAddEnabled, &vm.Config.CpuHotAddEnabled},
- {spec.CpuHotRemoveEnabled, &vm.Config.CpuHotRemoveEnabled},
- {spec.GuestAutoLockEnabled, &vm.Config.GuestAutoLockEnabled},
- {spec.MemoryHotAddEnabled, &vm.Config.MemoryHotAddEnabled},
- {spec.MemoryReservationLockedToMax, &vm.Config.MemoryReservationLockedToMax},
- {spec.MessageBusTunnelEnabled, &vm.Config.MessageBusTunnelEnabled},
- {spec.NpivTemporaryDisabled, &vm.Config.NpivTemporaryDisabled},
- {spec.NpivOnNonRdmDisks, &vm.Config.NpivOnNonRdmDisks},
- {spec.ChangeTrackingEnabled, &vm.Config.ChangeTrackingEnabled},
- }
- for _, f := range applyb {
- if f.src != nil {
- *f.dst = f.src
- }
- }
- if spec.Flags != nil {
- vm.Config.Flags = *spec.Flags
- }
- if spec.LatencySensitivity != nil {
- vm.Config.LatencySensitivity = spec.LatencySensitivity
- }
- if spec.ManagedBy != nil {
- vm.Config.ManagedBy = spec.ManagedBy
- }
- if spec.BootOptions != nil {
- vm.Config.BootOptions = spec.BootOptions
- }
- if spec.RepConfig != nil {
- vm.Config.RepConfig = spec.RepConfig
- }
- if spec.Tools != nil {
- vm.Config.Tools = spec.Tools
- }
- if spec.ConsolePreferences != nil {
- vm.Config.ConsolePreferences = spec.ConsolePreferences
- }
- if spec.CpuAffinity != nil {
- vm.Config.CpuAffinity = spec.CpuAffinity
- }
- if spec.CpuAllocation != nil {
- vm.Config.CpuAllocation = spec.CpuAllocation
- }
- if spec.MemoryAffinity != nil {
- vm.Config.MemoryAffinity = spec.MemoryAffinity
- }
- if spec.MemoryAllocation != nil {
- vm.Config.MemoryAllocation = spec.MemoryAllocation
- }
- if spec.LatencySensitivity != nil {
- vm.Config.LatencySensitivity = spec.LatencySensitivity
- }
- if spec.MemoryMB != 0 {
- vm.Config.Hardware.MemoryMB = int32(spec.MemoryMB)
- vm.Summary.Config.MemorySizeMB = vm.Config.Hardware.MemoryMB
- }
- if spec.NumCPUs != 0 {
- vm.Config.Hardware.NumCPU = spec.NumCPUs
- vm.Summary.Config.NumCpu = vm.Config.Hardware.NumCPU
- }
- if spec.NumCoresPerSocket != 0 {
- vm.Config.Hardware.NumCoresPerSocket = spec.NumCoresPerSocket
- }
- 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()
- }
- func validateGuestID(id string) types.BaseMethodFault {
- for _, x := range GuestID {
- if id == string(x) {
- return nil
- }
- }
- return &types.InvalidArgument{InvalidProperty: "configSpec.guestId"}
- }
- func (vm *VirtualMachine) configure(spec *types.VirtualMachineConfigSpec) types.BaseMethodFault {
- vm.apply(spec)
- if spec.MemoryAllocation != nil {
- if err := updateResourceAllocation("memory", spec.MemoryAllocation, vm.Config.MemoryAllocation); err != nil {
- return err
- }
- }
- if spec.CpuAllocation != nil {
- if err := updateResourceAllocation("cpu", spec.CpuAllocation, vm.Config.CpuAllocation); err != nil {
- return err
- }
- }
- if spec.GuestId != "" {
- if err := validateGuestID(spec.GuestId); err != nil {
- return err
- }
- }
- 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)
- ds := Map.FindByName(name, host.Datastore).(*Datastore)
- if FindReference(vm.Datastore, ds.Self) == nil {
- vm.Datastore = append(vm.Datastore, ds.Self)
- }
- return ds
- }
- func (vm *VirtualMachine) createFile(spec string, name string, register bool) (*os.File, types.BaseMethodFault) {
- p, fault := parseDatastorePath(spec)
- if fault != nil {
- return nil, fault
- }
- ds := vm.useDatastore(p.Datastore)
- file := path.Join(ds.Info.GetDatastoreInfo().Url, p.Path)
- if name != "" {
- 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)
- }
- if register {
- f, err := os.Open(file)
- if err != nil {
- log.Printf("register %s: %s", vm.Reference(), err)
- if os.IsNotExist(err) {
- return nil, &types.NotFound{}
- }
- return nil, &types.InvalidArgument{}
- }
- return f, nil
- }
- dir := path.Dir(file)
- _ = os.MkdirAll(dir, 0700)
- _, err := os.Stat(file)
- if err == nil {
- return nil, &types.FileAlreadyExists{
- FileFault: types.FileFault{
- File: file,
- },
- }
- }
- f, err := os.Create(file)
- if err != nil {
- return nil, &types.FileFault{
- File: file,
- }
- }
- return f, nil
- }
- // Rather than keep an fd open for each VM, open/close the log for each messages.
- // This is ok for now as we do not do any heavy VM logging.
- func (vm *VirtualMachine) logPrintf(format string, v ...interface{}) {
- f, err := os.OpenFile(vm.log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0)
- if err != nil {
- log.Println(err)
- return
- }
- log.New(f, "vmx ", log.Flags()).Printf(format, v...)
- _ = f.Close()
- }
- 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
- use *string
- }{
- {vm.Config.Files.VmPathName, "", nil},
- {vm.Config.Files.VmPathName, fmt.Sprintf("%s.nvram", vm.Name), nil},
- {vm.Config.Files.LogDirectory, "vmware.log", &vm.log},
- }
- for _, file := range files {
- f, err := vm.createFile(file.spec, file.name, register)
- if err != nil {
- return err
- }
- if file.use != nil {
- *file.use = f.Name()
- }
- _ = f.Close()
- }
- vm.logPrintf("created")
- return vm.configureDevices(spec)
- }
- var vmwOUI = net.HardwareAddr([]byte{0x0, 0xc, 0x29})
- // From http://pubs.vmware.com/vsphere-60/index.jsp?topic=%2Fcom.vmware.vsphere.networking.doc%2FGUID-DC7478FF-DC44-4625-9AD7-38208C56A552.html
- // "The host generates generateMAC addresses that consists of the VMware OUI 00:0C:29 and the last three octets in hexadecimal
- // format of the virtual machine UUID. The virtual machine UUID is based on a hash calculated by using the UUID of the
- // ESXi physical machine and the path to the configuration file (.vmx) of the virtual machine."
- func (vm *VirtualMachine) generateMAC() string {
- id := uuid.New() // Random is fine for now.
- offset := len(id) - len(vmwOUI)
- mac := append(vmwOUI, id[offset:]...)
- return mac.String()
- }
- func numberToString(n int64, sep rune) string {
- buf := &bytes.Buffer{}
- if n < 0 {
- n = -n
- buf.WriteRune('-')
- }
- s := strconv.FormatInt(n, 10)
- pos := 3 - (len(s) % 3)
- for i := 0; i < len(s); i++ {
- if pos == 3 {
- if i != 0 {
- buf.WriteRune(sep)
- }
- pos = 0
- }
- pos++
- buf.WriteByte(s[i])
- }
- return buf.String()
- }
- func getDiskSize(disk *types.VirtualDisk) int64 {
- if disk.CapacityInBytes == 0 {
- return disk.CapacityInKB * 1024
- }
- return disk.CapacityInBytes
- }
- func (vm *VirtualMachine) configureDevice(devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) types.BaseMethodFault {
- device := spec.Device
- d := device.GetVirtualDevice()
- var controller types.BaseVirtualController
- if d.Key <= 0 {
- // Keys can't be negative; Key 0 is reserved
- d.Key = devices.NewKey()
- d.Key *= -1
- }
- // Choose a unique key
- for {
- if devices.FindByKey(d.Key) == nil {
- break
- }
- d.Key++
- }
- label := devices.Name(device)
- summary := label
- dc := Map.getEntityDatacenter(Map.Get(*vm.Parent).(mo.Entity))
- dm := Map.VirtualDiskManager()
- switch x := device.(type) {
- case types.BaseVirtualEthernetCard:
- controller = devices.PickController((*types.VirtualPCIController)(nil))
- var net types.ManagedObjectReference
- var name string
- switch b := d.Backing.(type) {
- case *types.VirtualEthernetCardNetworkBackingInfo:
- name = b.DeviceName
- summary = name
- net = Map.FindByName(b.DeviceName, dc.Network).Reference()
- b.Network = &net
- case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
- summary = fmt.Sprintf("DVSwitch: %s", b.Port.SwitchUuid)
- net.Type = "DistributedVirtualPortgroup"
- net.Value = b.Port.PortgroupKey
- }
- 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 path.Path == "" {
- filename, err := vm.genVmdkPath(path)
- if err != nil {
- return err
- }
- info.FileName = filename
- }
- err := dm.createVirtualDisk(spec.FileOperation, &types.CreateVirtualDisk_Task{
- Datacenter: &dc.Self,
- Name: info.FileName,
- })
- if err != nil {
- 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)
- entity := Map.FindByName(p.Datastore, host.Datastore)
- ref := entity.Reference()
- info.Datastore = &ref
- ds := entity.(*Datastore)
- // 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()
- }
- }
- if d.UnitNumber == nil && controller != nil {
- devices.AssignController(device, controller)
- }
- if d.DeviceInfo == nil {
- d.DeviceInfo = &types.Description{
- Label: label,
- Summary: summary,
- }
- }
- return nil
- }
- func (vm *VirtualMachine) removeDevice(devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) object.VirtualDeviceList {
- key := spec.Device.GetVirtualDevice().Key
- for i, d := range devices {
- if d.GetVirtualDevice().Key != key {
- continue
- }
- devices = append(devices[:i], devices[i+1:]...)
- switch device := spec.Device.(type) {
- case *types.VirtualDisk:
- if spec.FileOperation == types.VirtualDeviceConfigSpecFileOperationDestroy {
- var file string
- switch b := device.Backing.(type) {
- case types.BaseVirtualDeviceFileBackingInfo:
- file = b.GetVirtualDeviceFileBackingInfo().FileName
- p, _ := parseDatastorePath(file)
- host := Map.Get(*vm.Runtime.Host).(*HostSystem)
- ds := Map.FindByName(p.Datastore, host.Datastore).(*Datastore)
- ds.Summary.FreeSpace += getDiskSize(device)
- ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
- }
- if file != "" {
- dc := Map.getEntityDatacenter(Map.Get(*vm.Parent).(mo.Entity))
- dm := Map.VirtualDiskManager()
- dm.DeleteVirtualDiskTask(&types.DeleteVirtualDisk_Task{
- Name: file,
- Datacenter: &dc.Self,
- })
- }
- }
- Map.Update(vm, []types.PropertyChange{
- {Name: "summary.config.numVirtualDisks", Val: vm.Summary.Config.NumVirtualDisks - 1},
- })
- vm.updateDiskLayouts()
- case types.BaseVirtualEthernetCard:
- var net types.ManagedObjectReference
- switch b := device.GetVirtualEthernetCard().Backing.(type) {
- case *types.VirtualEthernetCardNetworkBackingInfo:
- net = *b.Network
- case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
- net.Type = "DistributedVirtualPortgroup"
- net.Value = b.Port.PortgroupKey
- }
- 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
- }
- return devices
- }
- 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
- if index == 0 {
- filename = fmt.Sprintf("%s.vmdk", vm.Config.Name)
- } else {
- filename = fmt.Sprintf("%s_%d.vmdk", vm.Config.Name, index)
- }
- f, err := vm.createFile(vmdir, filename, false)
- if err != nil {
- switch err.(type) {
- case *types.FileAlreadyExists:
- index++
- continue
- default:
- return "", err
- }
- }
- _ = f.Close()
- _ = os.Remove(f.Name())
- return path.Join(vmdir, filename), nil
- }
- }
- func (vm *VirtualMachine) configureDevices(spec *types.VirtualMachineConfigSpec) types.BaseMethodFault {
- devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
- for i, change := range spec.DeviceChange {
- dspec := change.GetVirtualDeviceConfigSpec()
- device := dspec.Device.GetVirtualDevice()
- invalid := &types.InvalidDeviceSpec{DeviceIndex: int32(i)}
- switch dspec.Operation {
- case types.VirtualDeviceConfigSpecOperationAdd:
- 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
- }
- return false
- }) != nil {
- // UnitNumber for this device type is taken
- return invalid
- }
- err := vm.configureDevice(devices, dspec)
- if err != nil {
- return err
- }
- devices = append(devices, dspec.Device)
- case types.VirtualDeviceConfigSpecOperationEdit:
- rspec := *dspec
- rspec.Device = devices.FindByKey(device.Key)
- if rspec.Device == nil {
- return invalid
- }
- devices = vm.removeDevice(devices, &rspec)
- device.DeviceInfo = nil // regenerate summary + label
- err := vm.configureDevice(devices, dspec)
- if err != nil {
- return err
- }
- devices = append(devices, dspec.Device)
- case types.VirtualDeviceConfigSpecOperationRemove:
- devices = vm.removeDevice(devices, dspec)
- }
- }
- Map.Update(vm, []types.PropertyChange{
- {Name: "config.hardware.device", Val: []types.BaseVirtualDevice(devices)},
- })
- vm.updateDiskLayouts()
- return nil
- }
- type powerVMTask struct {
- *VirtualMachine
- state types.VirtualMachinePowerState
- ctx *Context
- }
- func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
- c.logPrintf("running power task: requesting %s, existing %s",
- c.state, c.VirtualMachine.Runtime.PowerState)
- if c.VirtualMachine.Runtime.PowerState == c.state {
- return nil, &types.InvalidPowerState{
- RequestedState: c.state,
- ExistingState: c.VirtualMachine.Runtime.PowerState,
- }
- }
- var boot types.AnyType
- if c.state == types.VirtualMachinePowerStatePoweredOn {
- boot = time.Now()
- }
- 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},
- )
- }
- Map.Update(c.VirtualMachine, []types.PropertyChange{
- {Name: "runtime.powerState", Val: c.state},
- {Name: "summary.runtime.powerState", Val: c.state},
- {Name: "summary.runtime.bootTime", Val: boot},
- })
- return nil, nil
- }
- func (vm *VirtualMachine) PowerOnVMTask(ctx *Context, c *types.PowerOnVM_Task) soap.HasFault {
- if vm.Config.Template {
- return &methods.PowerOnVM_TaskBody{
- Fault_: Fault("cannot powerOn a template", &types.InvalidState{}),
- }
- }
- runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOn, ctx}
- task := CreateTask(runner.Reference(), "powerOn", runner.Run)
- return &methods.PowerOnVM_TaskBody{
- Res: &types.PowerOnVM_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (vm *VirtualMachine) PowerOffVMTask(ctx *Context, c *types.PowerOffVM_Task) soap.HasFault {
- runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOff, ctx}
- task := CreateTask(runner.Reference(), "powerOff", runner.Run)
- return &methods.PowerOffVM_TaskBody{
- Res: &types.PowerOffVM_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (vm *VirtualMachine) SuspendVMTask(ctx *Context, req *types.SuspendVM_Task) soap.HasFault {
- runner := &powerVMTask{vm, types.VirtualMachinePowerStateSuspended, ctx}
- task := CreateTask(runner.Reference(), "suspend", runner.Run)
- return &methods.SuspendVM_TaskBody{
- Res: &types.SuspendVM_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (vm *VirtualMachine) ResetVMTask(ctx *Context, req *types.ResetVM_Task) soap.HasFault {
- task := CreateTask(vm, "reset", func(task *Task) (types.AnyType, types.BaseMethodFault) {
- res := vm.PowerOffVMTask(ctx, &types.PowerOffVM_Task{This: vm.Self})
- ctask := Map.Get(res.(*methods.PowerOffVM_TaskBody).Res.Returnval).(*Task)
- if ctask.Info.Error != nil {
- return nil, ctask.Info.Error.Fault
- }
- _ = vm.PowerOnVMTask(ctx, &types.PowerOnVM_Task{This: vm.Self})
- return nil, nil
- })
- return &methods.ResetVM_TaskBody{
- Res: &types.ResetVM_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (vm *VirtualMachine) ReconfigVMTask(ctx *Context, req *types.ReconfigVM_Task) soap.HasFault {
- task := CreateTask(vm, "reconfigVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
- err := vm.configure(&req.Spec)
- if err != nil {
- return nil, err
- }
- ctx.postEvent(&types.VmReconfiguredEvent{
- VmEvent: vm.event(),
- ConfigSpec: req.Spec,
- })
- return nil, nil
- })
- return &methods.ReconfigVM_TaskBody{
- Res: &types.ReconfigVM_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- 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{
- This: req.This,
- })
- if r.Fault() != nil {
- return nil, r.Fault().VimFault().(types.BaseMethodFault)
- }
- // Remove all devices
- devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
- spec, _ := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationRemove)
- vm.configureDevices(&types.VirtualMachineConfigSpec{DeviceChange: spec})
- // Delete VM files from the datastore (ignoring result for now)
- m := Map.FileManager()
- dc := Map.getEntityDatacenter(vm).Reference()
- _ = m.DeleteDatastoreFileTask(&types.DeleteDatastoreFile_Task{
- This: m.Reference(),
- Name: vm.Config.Files.LogDirectory,
- Datacenter: &dc,
- })
- vm.run.remove(vm)
- return nil, nil
- })
- return &methods.Destroy_TaskBody{
- Res: &types.Destroy_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- 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{}
- if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
- r.Fault_ = Fault("", &types.InvalidPowerState{
- RequestedState: types.VirtualMachinePowerStatePoweredOff,
- ExistingState: vm.Runtime.PowerState,
- })
- return r
- }
- host := Map.Get(*vm.Runtime.Host).(*HostSystem)
- Map.RemoveReference(host, &host.Vm, vm.Self)
- switch pool := Map.Get(*vm.ResourcePool).(type) {
- case *ResourcePool:
- Map.RemoveReference(pool, &pool.Vm, vm.Self)
- case *VirtualApp:
- Map.RemoveReference(pool, &pool.Vm, vm.Self)
- }
- for i := range vm.Datastore {
- ds := Map.Get(vm.Datastore[i]).(*Datastore)
- Map.RemoveReference(ds, &ds.Vm, vm.Self)
- }
- ctx.postEvent(&types.VmRemovedEvent{VmEvent: vm.event()})
- Map.getEntityParent(vm, "Folder").(*Folder).removeChild(c.This)
- r.Res = new(types.UnregisterVMResponse)
- return r
- }
- func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soap.HasFault {
- ctx.Caller = &vm.Self
- folder := Map.Get(req.Folder).(*Folder)
- host := Map.Get(*vm.Runtime.Host).(*HostSystem)
- event := vm.event()
- ctx.postEvent(&types.VmBeingClonedEvent{
- VmCloneEvent: types.VmCloneEvent{
- VmEvent: event,
- },
- DestFolder: folder.eventArgument(),
- DestName: req.Name,
- DestHost: *host.eventArgument(),
- })
- task := CreateTask(vm, "cloneVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
- config := types.VirtualMachineConfigSpec{
- Name: req.Name,
- GuestId: vm.Config.GuestId,
- Files: &types.VirtualMachineFileInfo{
- VmPathName: strings.Replace(vm.Config.Files.VmPathName, vm.Name, req.Name, -1),
- },
- }
- defaultDevices := object.VirtualDeviceList(esx.VirtualDevice)
- devices := vm.Config.Hardware.Device
- for _, device := range devices {
- var fop types.VirtualDeviceConfigSpecFileOperation
- 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
- // 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{
- Operation: types.VirtualDeviceConfigSpecOperationAdd,
- Device: device,
- FileOperation: fop,
- })
- }
- res := folder.CreateVMTask(ctx, &types.CreateVM_Task{
- This: folder.Self,
- Config: config,
- Pool: *vm.ResourcePool,
- Host: vm.Runtime.Host,
- })
- ctask := Map.Get(res.(*methods.CreateVM_TaskBody).Res.Returnval).(*Task)
- if ctask.Info.Error != nil {
- return nil, ctask.Info.Error.Fault
- }
- 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()},
- SourceVm: *event.Vm,
- })
- return ref, nil
- })
- return &methods.CloneVM_TaskBody{
- Res: &types.CloneVM_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (vm *VirtualMachine) RelocateVMTask(req *types.RelocateVM_Task) soap.HasFault {
- task := CreateTask(vm, "relocateVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
- var changes []types.PropertyChange
- if ref := req.Spec.Datastore; ref != nil {
- ds := Map.Get(*ref).(*Datastore)
- Map.RemoveReference(ds, &ds.Vm, *ref)
- // 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}})
- }
- if ref := req.Spec.Pool; ref != nil {
- pool := Map.Get(*ref).(*ResourcePool)
- Map.RemoveReference(pool, &pool.Vm, *ref)
- changes = append(changes, types.PropertyChange{Name: "resourcePool", Val: ref})
- }
- if ref := req.Spec.Host; ref != nil {
- host := Map.Get(*ref).(*HostSystem)
- Map.RemoveReference(host, &host.Vm, *ref)
- changes = append(changes,
- types.PropertyChange{Name: "runtime.host", Val: ref},
- types.PropertyChange{Name: "summary.runtime.host", Val: ref},
- )
- }
- Map.Update(vm, changes)
- return nil, nil
- })
- return &methods.RelocateVM_TaskBody{
- Res: &types.RelocateVM_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- 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{}
- }
- snapshot := &VirtualMachineSnapshot{}
- snapshot.Vm = vm.Reference()
- snapshot.Config = *vm.Config
- Map.Put(snapshot)
- treeItem := types.VirtualMachineSnapshotTree{
- Snapshot: snapshot.Self,
- Vm: snapshot.Vm,
- Name: req.Name,
- Description: req.Description,
- Id: atomic.AddInt32(&vm.sid, 1),
- CreateTime: time.Now(),
- State: vm.Runtime.PowerState,
- Quiesced: req.Quiesce,
- BackupManifest: "",
- ReplaySupported: types.NewBool(false),
- }
- cur := vm.Snapshot.CurrentSnapshot
- if cur != nil {
- parent := Map.Get(*cur).(*VirtualMachineSnapshot)
- parent.ChildSnapshot = append(parent.ChildSnapshot, snapshot.Self)
- ss := findSnapshotInTree(vm.Snapshot.RootSnapshotList, *cur)
- ss.ChildSnapshotList = append(ss.ChildSnapshotList, treeItem)
- } else {
- changes = append(changes, types.PropertyChange{
- Name: "snapshot.rootSnapshotList",
- Val: append(vm.Snapshot.RootSnapshotList, treeItem),
- })
- }
- snapshot.createSnapshotFiles()
- changes = append(changes, types.PropertyChange{Name: "snapshot.currentSnapshot", Val: snapshot.Self})
- Map.Update(vm, changes)
- return nil, nil
- })
- return &methods.CreateSnapshot_TaskBody{
- Res: &types.CreateSnapshot_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (vm *VirtualMachine) RevertToCurrentSnapshotTask(req *types.RevertToCurrentSnapshot_Task) soap.HasFault {
- body := &methods.RevertToCurrentSnapshot_TaskBody{}
- if vm.Snapshot == nil || vm.Snapshot.CurrentSnapshot == nil {
- body.Fault_ = Fault("snapshot not found", &types.NotFound{})
- return body
- }
- task := CreateTask(vm, "revertSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
- return nil, nil
- })
- body.Res = &types.RevertToCurrentSnapshot_TaskResponse{
- Returnval: task.Run(),
- }
- return body
- }
- 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
- }
- refs := allSnapshotsInTree(vm.Snapshot.RootSnapshotList)
- Map.Update(vm, []types.PropertyChange{
- {Name: "snapshot", Val: nil},
- })
- for _, ref := range refs {
- Map.Get(ref).(*VirtualMachineSnapshot).removeSnapshotFiles(ctx)
- Map.Remove(ref)
- }
- return nil, nil
- })
- return &methods.RemoveAllSnapshots_TaskBody{
- Res: &types.RemoveAllSnapshots_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (vm *VirtualMachine) ShutdownGuest(ctx *Context, c *types.ShutdownGuest) soap.HasFault {
- r := &methods.ShutdownGuestBody{}
- // should be poweron
- if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOff {
- r.Fault_ = Fault("", &types.InvalidPowerState{
- RequestedState: types.VirtualMachinePowerStatePoweredOn,
- ExistingState: vm.Runtime.PowerState,
- })
- return r
- }
- // change state
- vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
- vm.Summary.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
- event := vm.event()
- ctx.postEvent(
- &types.VmGuestShutdownEvent{VmEvent: event},
- &types.VmPoweredOffEvent{VmEvent: event},
- )
- vm.run.stop(vm)
- Map.Update(vm, []types.PropertyChange{
- {Name: "runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
- {Name: "summary.runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
- })
- r.Res = new(types.ShutdownGuestResponse)
- return r
- }
- func (vm *VirtualMachine) MarkAsTemplate(req *types.MarkAsTemplate) soap.HasFault {
- r := &methods.MarkAsTemplateBody{}
- if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
- r.Fault_ = Fault("", &types.InvalidPowerState{
- RequestedState: types.VirtualMachinePowerStatePoweredOff,
- ExistingState: vm.Runtime.PowerState,
- })
- return r
- }
- vm.Config.Template = true
- r.Res = &types.MarkAsTemplateResponse{}
- return r
- }
- func findSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.VirtualMachineSnapshotTree {
- if tree == nil {
- return nil
- }
- for i, ss := range tree {
- if ss.Snapshot == ref {
- return &tree[i]
- }
- target := findSnapshotInTree(ss.ChildSnapshotList, ref)
- if target != nil {
- return target
- }
- }
- return nil
- }
- func findParentSnapshot(tree types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.ManagedObjectReference {
- for _, ss := range tree.ChildSnapshotList {
- if ss.Snapshot == ref {
- return &tree.Snapshot
- }
- res := findParentSnapshot(ss, ref)
- if res != nil {
- return res
- }
- }
- return nil
- }
- func findParentSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.ManagedObjectReference {
- if tree == nil {
- return nil
- }
- for _, ss := range tree {
- res := findParentSnapshot(ss, ref)
- if res != nil {
- return res
- }
- }
- return nil
- }
- func removeSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference, removeChildren bool) []types.VirtualMachineSnapshotTree {
- if tree == nil {
- return tree
- }
- var result []types.VirtualMachineSnapshotTree
- for _, ss := range tree {
- if ss.Snapshot == ref {
- if !removeChildren {
- result = append(result, ss.ChildSnapshotList...)
- }
- } else {
- ss.ChildSnapshotList = removeSnapshotInTree(ss.ChildSnapshotList, ref, removeChildren)
- result = append(result, ss)
- }
- }
- return result
- }
- func allSnapshotsInTree(tree []types.VirtualMachineSnapshotTree) []types.ManagedObjectReference {
- var result []types.ManagedObjectReference
- if tree == nil {
- return result
- }
- for _, ss := range tree {
- result = append(result, ss.Snapshot)
- result = append(result, allSnapshotsInTree(ss.ChildSnapshotList)...)
- }
- return result
- }
|