123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- /*
- Copyright (c) 2017 VMware, Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package simulator
- import (
- "fmt"
- "math/rand"
- "path"
- "strings"
- "github.com/google/uuid"
- "github.com/vmware/govmomi/object"
- "github.com/vmware/govmomi/vim25/methods"
- "github.com/vmware/govmomi/vim25/mo"
- "github.com/vmware/govmomi/vim25/soap"
- "github.com/vmware/govmomi/vim25/types"
- )
- type Folder struct {
- mo.Folder
- }
- func (f *Folder) eventArgument() types.FolderEventArgument {
- return types.FolderEventArgument{
- Folder: f.Self,
- EntityEventArgument: types.EntityEventArgument{Name: f.Name},
- }
- }
- // update references when objects are added/removed from a Folder
- func (f *Folder) update(o mo.Reference, u func(mo.Reference, *[]types.ManagedObjectReference, types.ManagedObjectReference)) {
- ref := o.Reference()
- if f.Parent == nil {
- return // this is the root folder
- }
- switch ref.Type {
- case "Datacenter", "Folder":
- return // nothing to update
- }
- dc := Map.getEntityDatacenter(f)
- switch ref.Type {
- case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup":
- u(dc, &dc.Network, ref)
- case "Datastore":
- u(dc, &dc.Datastore, ref)
- }
- }
- func networkSummary(n *mo.Network) *types.NetworkSummary {
- return &types.NetworkSummary{
- Network: &n.Self,
- Name: n.Name,
- Accessible: true,
- }
- }
- func (f *Folder) putChild(o mo.Entity) {
- Map.PutEntity(f, o)
- f.ChildEntity = append(f.ChildEntity, o.Reference())
- f.update(o, Map.AddReference)
- switch e := o.(type) {
- case *mo.Network:
- e.Summary = networkSummary(e)
- case *mo.OpaqueNetwork:
- e.Summary = networkSummary(&e.Network)
- case *DistributedVirtualPortgroup:
- e.Summary = networkSummary(&e.Network)
- }
- }
- func (f *Folder) removeChild(o mo.Reference) {
- Map.Remove(o.Reference())
- RemoveReference(&f.ChildEntity, o.Reference())
- f.update(o, Map.RemoveReference)
- }
- func (f *Folder) hasChildType(kind string) bool {
- for _, t := range f.ChildType {
- if t == kind {
- return true
- }
- }
- return false
- }
- func (f *Folder) typeNotSupported() *soap.Fault {
- return Fault(fmt.Sprintf("%s supports types: %#v", f.Self, f.ChildType), &types.NotSupported{})
- }
- type addStandaloneHost struct {
- *Folder
- req *types.AddStandaloneHost_Task
- }
- func (add *addStandaloneHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
- host, err := CreateStandaloneHost(add.Folder, add.req.Spec)
- if err != nil {
- return nil, err
- }
- if add.req.AddConnected {
- host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
- }
- return host.Reference(), nil
- }
- func (f *Folder) AddStandaloneHostTask(a *types.AddStandaloneHost_Task) soap.HasFault {
- r := &methods.AddStandaloneHost_TaskBody{}
- if f.hasChildType("ComputeResource") && f.hasChildType("Folder") {
- r.Res = &types.AddStandaloneHost_TaskResponse{
- Returnval: NewTask(&addStandaloneHost{f, a}).Run(),
- }
- } else {
- r.Fault_ = f.typeNotSupported()
- }
- return r
- }
- func (f *Folder) CreateFolder(c *types.CreateFolder) soap.HasFault {
- r := &methods.CreateFolderBody{}
- if f.hasChildType("Folder") {
- folder := &Folder{}
- folder.Name = c.Name
- folder.ChildType = f.ChildType
- f.putChild(folder)
- r.Res = &types.CreateFolderResponse{
- Returnval: folder.Self,
- }
- } else {
- r.Fault_ = f.typeNotSupported()
- }
- return r
- }
- // StoragePod aka "Datastore Cluster"
- type StoragePod struct {
- mo.StoragePod
- }
- func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault {
- r := &methods.CreateStoragePodBody{}
- if f.hasChildType("StoragePod") {
- pod := &StoragePod{}
- pod.Name = c.Name
- pod.ChildType = []string{"Datastore"}
- pod.Summary = new(types.StoragePodSummary)
- pod.PodStorageDrsEntry = new(types.PodStorageDrsEntry)
- pod.PodStorageDrsEntry.StorageDrsConfig.PodConfig.Enabled = true
- f.putChild(pod)
- r.Res = &types.CreateStoragePodResponse{
- Returnval: pod.Self,
- }
- } else {
- r.Fault_ = f.typeNotSupported()
- }
- return r
- }
- func (p *StoragePod) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
- 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 {
- r := &methods.CreateDatacenterBody{}
- if f.hasChildType("Datacenter") && f.hasChildType("Folder") {
- dc := NewDatacenter(f)
- dc.Name = c.Name
- r.Res = &types.CreateDatacenterResponse{
- Returnval: dc.Self,
- }
- ctx.postEvent(&types.DatacenterCreatedEvent{
- DatacenterEvent: types.DatacenterEvent{
- Event: types.Event{
- Datacenter: datacenterEventArgument(dc),
- },
- },
- Parent: f.eventArgument(),
- })
- } else {
- r.Fault_ = f.typeNotSupported()
- }
- return r
- }
- func (f *Folder) CreateClusterEx(c *types.CreateClusterEx) soap.HasFault {
- r := &methods.CreateClusterExBody{}
- if f.hasChildType("ComputeResource") && f.hasChildType("Folder") {
- cluster, err := CreateClusterComputeResource(f, c.Name, c.Spec)
- if err != nil {
- r.Fault_ = Fault("", err)
- return r
- }
- r.Res = &types.CreateClusterExResponse{
- Returnval: cluster.Self,
- }
- } else {
- r.Fault_ = f.typeNotSupported()
- }
- return r
- }
- type createVM struct {
- *Folder
- ctx *Context
- req *types.CreateVM_Task
- register bool
- }
- // hostsWithDatastore returns hosts that have access to the given datastore path
- func hostsWithDatastore(hosts []types.ManagedObjectReference, path string) []types.ManagedObjectReference {
- attached := hosts[:0]
- var p object.DatastorePath
- p.FromString(path)
- for _, host := range hosts {
- h := Map.Get(host).(*HostSystem)
- if Map.FindByName(p.Datastore, h.Datastore) != nil {
- attached = append(attached, host)
- }
- }
- return attached
- }
- func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
- vm, err := NewVirtualMachine(c.Folder.Self, &c.req.Config)
- if err != nil {
- c.Folder.removeChild(vm)
- return nil, err
- }
- vm.ResourcePool = &c.req.Pool
- if c.req.Host == nil {
- var hosts []types.ManagedObjectReference
- pool := Map.Get(c.req.Pool).(mo.Entity)
- switch cr := Map.getEntityComputeResource(pool).(type) {
- case *mo.ComputeResource:
- hosts = cr.Host
- case *ClusterComputeResource:
- hosts = cr.Host
- }
- hosts = hostsWithDatastore(hosts, c.req.Config.Files.VmPathName)
- host := hosts[rand.Intn(len(hosts))]
- vm.Runtime.Host = &host
- } else {
- vm.Runtime.Host = c.req.Host
- }
- vm.Guest = &types.GuestInfo{
- ToolsStatus: types.VirtualMachineToolsStatusToolsNotInstalled,
- ToolsVersion: "0",
- }
- vm.Summary.Guest = &types.VirtualMachineGuestSummary{
- ToolsStatus: vm.Guest.ToolsStatus,
- }
- vm.Summary.Config.VmPathName = vm.Config.Files.VmPathName
- vm.Summary.Runtime.Host = vm.Runtime.Host
- err = vm.create(&c.req.Config, c.register)
- if err != nil {
- c.Folder.removeChild(vm)
- return nil, err
- }
- host := Map.Get(*vm.Runtime.Host).(*HostSystem)
- Map.AppendReference(host, &host.Vm, vm.Self)
- vm.EnvironmentBrowser = *hostParent(&host.HostSystem).EnvironmentBrowser
- for i := range vm.Datastore {
- ds := Map.Get(vm.Datastore[i]).(*Datastore)
- Map.AppendReference(ds, &ds.Vm, vm.Self)
- }
- pool := Map.Get(*vm.ResourcePool)
- // This can be an internal call from VirtualApp.CreateChildVMTask, where pool is already locked.
- c.ctx.WithLock(pool, func() {
- switch rp := pool.(type) {
- case *ResourcePool:
- rp.Vm = append(rp.Vm, vm.Self)
- case *VirtualApp:
- rp.Vm = append(rp.Vm, vm.Self)
- }
- })
- event := vm.event()
- c.ctx.postEvent(
- &types.VmBeingCreatedEvent{
- VmEvent: event,
- ConfigSpec: &c.req.Config,
- },
- &types.VmInstanceUuidAssignedEvent{
- VmEvent: event,
- InstanceUuid: vm.Config.InstanceUuid,
- },
- &types.VmUuidAssignedEvent{
- VmEvent: event,
- Uuid: vm.Config.Uuid,
- },
- &types.VmCreatedEvent{
- VmEvent: event,
- },
- )
- vm.RefreshStorageInfo(c.ctx, nil)
- return vm.Reference(), nil
- }
- func (f *Folder) CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault {
- return &methods.CreateVM_TaskBody{
- Res: &types.CreateVM_TaskResponse{
- Returnval: NewTask(&createVM{f, ctx, c, false}).Run(),
- },
- }
- }
- type registerVM struct {
- *Folder
- ctx *Context
- req *types.RegisterVM_Task
- }
- func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
- host := c.req.Host
- pool := c.req.Pool
- if c.req.AsTemplate {
- if host == nil {
- return nil, &types.InvalidArgument{InvalidProperty: "host"}
- } else if pool != nil {
- return nil, &types.InvalidArgument{InvalidProperty: "pool"}
- }
- pool = hostParent(&Map.Get(*host).(*HostSystem).HostSystem).ResourcePool
- } else {
- if pool == nil {
- return nil, &types.InvalidArgument{InvalidProperty: "pool"}
- }
- }
- if c.req.Path == "" {
- return nil, &types.InvalidArgument{InvalidProperty: "path"}
- }
- s := Map.SearchIndex()
- r := s.FindByDatastorePath(&types.FindByDatastorePath{
- This: s.Reference(),
- Path: c.req.Path,
- Datacenter: Map.getEntityDatacenter(c.Folder).Reference(),
- })
- if ref := r.(*methods.FindByDatastorePathBody).Res.Returnval; ref != nil {
- return nil, &types.AlreadyExists{Name: ref.Value}
- }
- if c.req.Name == "" {
- p, err := parseDatastorePath(c.req.Path)
- if err != nil {
- return nil, err
- }
- c.req.Name = path.Dir(p.Path)
- }
- create := NewTask(&createVM{
- Folder: c.Folder,
- register: true,
- ctx: c.ctx,
- req: &types.CreateVM_Task{
- This: c.Folder.Reference(),
- Config: types.VirtualMachineConfigSpec{
- Name: c.req.Name,
- Files: &types.VirtualMachineFileInfo{
- VmPathName: c.req.Path,
- },
- },
- Pool: *pool,
- Host: host,
- },
- })
- create.Run()
- if create.Info.Error != nil {
- return nil, create.Info.Error.Fault
- }
- return create.Info.Result, nil
- }
- func (f *Folder) RegisterVMTask(ctx *Context, c *types.RegisterVM_Task) soap.HasFault {
- ctx.Caller = &f.Self
- return &methods.RegisterVM_TaskBody{
- Res: &types.RegisterVM_TaskResponse{
- Returnval: NewTask(®isterVM{f, ctx, c}).Run(),
- },
- }
- }
- func (f *Folder) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
- task := CreateTask(f, "moveIntoFolder", func(t *Task) (types.AnyType, types.BaseMethodFault) {
- for _, ref := range c.List {
- obj := Map.Get(ref).(mo.Entity)
- parent, ok := Map.Get(*(obj.Entity()).Parent).(*Folder)
- if !ok || !f.hasChildType(ref.Type) {
- return nil, &types.NotSupported{}
- }
- parent.removeChild(ref)
- f.putChild(obj)
- }
- return nil, nil
- })
- return &methods.MoveIntoFolder_TaskBody{
- Res: &types.MoveIntoFolder_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
- task := CreateTask(f, "createDVS", func(t *Task) (types.AnyType, types.BaseMethodFault) {
- spec := req.Spec.ConfigSpec.GetDVSConfigSpec()
- dvs := &DistributedVirtualSwitch{}
- dvs.Name = spec.Name
- dvs.Entity().Name = dvs.Name
- if Map.FindByName(dvs.Name, f.ChildEntity) != nil {
- return nil, &types.InvalidArgument{InvalidProperty: "name"}
- }
- dvs.Uuid = uuid.New().String()
- f.putChild(dvs)
- dvs.Summary = types.DVSSummary{
- Name: dvs.Name,
- Uuid: dvs.Uuid,
- NumPorts: spec.NumStandalonePorts,
- ProductInfo: req.Spec.ProductInfo,
- Description: spec.Description,
- }
- configInfo := &types.VMwareDVSConfigInfo{
- DVSConfigInfo: types.DVSConfigInfo{
- Uuid: dvs.Uuid,
- Name: spec.Name,
- ConfigVersion: spec.ConfigVersion,
- NumStandalonePorts: spec.NumStandalonePorts,
- MaxPorts: spec.MaxPorts,
- UplinkPortPolicy: spec.UplinkPortPolicy,
- UplinkPortgroup: spec.UplinkPortgroup,
- DefaultPortConfig: spec.DefaultPortConfig,
- ExtensionKey: spec.ExtensionKey,
- Description: spec.Description,
- Policy: spec.Policy,
- VendorSpecificConfig: spec.VendorSpecificConfig,
- SwitchIpAddress: spec.SwitchIpAddress,
- DefaultProxySwitchMaxNumPorts: spec.DefaultProxySwitchMaxNumPorts,
- InfrastructureTrafficResourceConfig: spec.InfrastructureTrafficResourceConfig,
- NetworkResourceControlVersion: spec.NetworkResourceControlVersion,
- },
- }
- if spec.Contact != nil {
- configInfo.Contact = *spec.Contact
- }
- dvs.Config = configInfo
- if dvs.Summary.ProductInfo == nil {
- product := Map.content().About
- dvs.Summary.ProductInfo = &types.DistributedVirtualSwitchProductSpec{
- Name: "DVS",
- Vendor: product.Vendor,
- Version: product.Version,
- Build: product.Build,
- ForwardingClass: "etherswitch",
- }
- }
- dvs.AddDVPortgroupTask(&types.AddDVPortgroup_Task{
- Spec: []types.DVPortgroupConfigSpec{{
- Name: dvs.Name + "-DVUplinks" + strings.TrimPrefix(dvs.Self.Value, "dvs"),
- DefaultPortConfig: &types.VMwareDVSPortSetting{
- Vlan: &types.VmwareDistributedVirtualSwitchTrunkVlanSpec{
- VlanId: []types.NumericRange{{Start: 0, End: 4094}},
- },
- },
- }},
- })
- return dvs.Reference(), nil
- })
- return &methods.CreateDVS_TaskBody{
- Res: &types.CreateDVS_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
- func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault {
- return RenameTask(f, r)
- }
- func (f *Folder) DestroyTask(req *types.Destroy_Task) soap.HasFault {
- type destroyer interface {
- mo.Reference
- DestroyTask(*types.Destroy_Task) soap.HasFault
- }
- task := CreateTask(f, "destroy", func(*Task) (types.AnyType, types.BaseMethodFault) {
- // Attempt to destroy all children
- for _, c := range f.ChildEntity {
- obj, ok := Map.Get(c).(destroyer)
- if !ok {
- continue
- }
- var fault types.BaseMethodFault
- Map.WithLock(obj, func() {
- id := obj.DestroyTask(&types.Destroy_Task{
- This: c,
- }).(*methods.Destroy_TaskBody).Res.Returnval
- t := Map.Get(id).(*Task)
- if t.Info.Error != nil {
- fault = t.Info.Error.Fault // For example, can't destroy a powered on VM
- }
- })
- if fault != nil {
- return nil, fault
- }
- }
- // Remove the folder itself
- Map.Get(*f.Parent).(*Folder).removeChild(f.Self)
- return nil, nil
- })
- return &methods.Destroy_TaskBody{
- Res: &types.Destroy_TaskResponse{
- Returnval: task.Run(),
- },
- }
- }
|