folder.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /*
  2. Copyright (c) 2017 VMware, Inc. All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package simulator
  14. import (
  15. "fmt"
  16. "math/rand"
  17. "path"
  18. "strings"
  19. "github.com/google/uuid"
  20. "github.com/vmware/govmomi/object"
  21. "github.com/vmware/govmomi/vim25/methods"
  22. "github.com/vmware/govmomi/vim25/mo"
  23. "github.com/vmware/govmomi/vim25/soap"
  24. "github.com/vmware/govmomi/vim25/types"
  25. )
  26. type Folder struct {
  27. mo.Folder
  28. }
  29. func (f *Folder) eventArgument() types.FolderEventArgument {
  30. return types.FolderEventArgument{
  31. Folder: f.Self,
  32. EntityEventArgument: types.EntityEventArgument{Name: f.Name},
  33. }
  34. }
  35. // update references when objects are added/removed from a Folder
  36. func (f *Folder) update(o mo.Reference, u func(mo.Reference, *[]types.ManagedObjectReference, types.ManagedObjectReference)) {
  37. ref := o.Reference()
  38. if f.Parent == nil {
  39. return // this is the root folder
  40. }
  41. switch ref.Type {
  42. case "Datacenter", "Folder":
  43. return // nothing to update
  44. }
  45. dc := Map.getEntityDatacenter(f)
  46. switch ref.Type {
  47. case "Network", "DistributedVirtualSwitch", "DistributedVirtualPortgroup":
  48. u(dc, &dc.Network, ref)
  49. case "Datastore":
  50. u(dc, &dc.Datastore, ref)
  51. }
  52. }
  53. func networkSummary(n *mo.Network) *types.NetworkSummary {
  54. return &types.NetworkSummary{
  55. Network: &n.Self,
  56. Name: n.Name,
  57. Accessible: true,
  58. }
  59. }
  60. func (f *Folder) putChild(o mo.Entity) {
  61. Map.PutEntity(f, o)
  62. f.ChildEntity = append(f.ChildEntity, o.Reference())
  63. f.update(o, Map.AddReference)
  64. switch e := o.(type) {
  65. case *mo.Network:
  66. e.Summary = networkSummary(e)
  67. case *mo.OpaqueNetwork:
  68. e.Summary = networkSummary(&e.Network)
  69. case *DistributedVirtualPortgroup:
  70. e.Summary = networkSummary(&e.Network)
  71. }
  72. }
  73. func (f *Folder) removeChild(o mo.Reference) {
  74. Map.Remove(o.Reference())
  75. RemoveReference(&f.ChildEntity, o.Reference())
  76. f.update(o, Map.RemoveReference)
  77. }
  78. func (f *Folder) hasChildType(kind string) bool {
  79. for _, t := range f.ChildType {
  80. if t == kind {
  81. return true
  82. }
  83. }
  84. return false
  85. }
  86. func (f *Folder) typeNotSupported() *soap.Fault {
  87. return Fault(fmt.Sprintf("%s supports types: %#v", f.Self, f.ChildType), &types.NotSupported{})
  88. }
  89. type addStandaloneHost struct {
  90. *Folder
  91. req *types.AddStandaloneHost_Task
  92. }
  93. func (add *addStandaloneHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
  94. host, err := CreateStandaloneHost(add.Folder, add.req.Spec)
  95. if err != nil {
  96. return nil, err
  97. }
  98. if add.req.AddConnected {
  99. host.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
  100. }
  101. return host.Reference(), nil
  102. }
  103. func (f *Folder) AddStandaloneHostTask(a *types.AddStandaloneHost_Task) soap.HasFault {
  104. r := &methods.AddStandaloneHost_TaskBody{}
  105. if f.hasChildType("ComputeResource") && f.hasChildType("Folder") {
  106. r.Res = &types.AddStandaloneHost_TaskResponse{
  107. Returnval: NewTask(&addStandaloneHost{f, a}).Run(),
  108. }
  109. } else {
  110. r.Fault_ = f.typeNotSupported()
  111. }
  112. return r
  113. }
  114. func (f *Folder) CreateFolder(c *types.CreateFolder) soap.HasFault {
  115. r := &methods.CreateFolderBody{}
  116. if f.hasChildType("Folder") {
  117. folder := &Folder{}
  118. folder.Name = c.Name
  119. folder.ChildType = f.ChildType
  120. f.putChild(folder)
  121. r.Res = &types.CreateFolderResponse{
  122. Returnval: folder.Self,
  123. }
  124. } else {
  125. r.Fault_ = f.typeNotSupported()
  126. }
  127. return r
  128. }
  129. // StoragePod aka "Datastore Cluster"
  130. type StoragePod struct {
  131. mo.StoragePod
  132. }
  133. func (f *Folder) CreateStoragePod(c *types.CreateStoragePod) soap.HasFault {
  134. r := &methods.CreateStoragePodBody{}
  135. if f.hasChildType("StoragePod") {
  136. pod := &StoragePod{}
  137. pod.Name = c.Name
  138. pod.ChildType = []string{"Datastore"}
  139. pod.Summary = new(types.StoragePodSummary)
  140. pod.PodStorageDrsEntry = new(types.PodStorageDrsEntry)
  141. pod.PodStorageDrsEntry.StorageDrsConfig.PodConfig.Enabled = true
  142. f.putChild(pod)
  143. r.Res = &types.CreateStoragePodResponse{
  144. Returnval: pod.Self,
  145. }
  146. } else {
  147. r.Fault_ = f.typeNotSupported()
  148. }
  149. return r
  150. }
  151. func (p *StoragePod) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
  152. f := &Folder{Folder: p.Folder}
  153. res := f.MoveIntoFolderTask(c)
  154. p.ChildEntity = append(p.ChildEntity, f.ChildEntity...)
  155. return res
  156. }
  157. func (f *Folder) CreateDatacenter(ctx *Context, c *types.CreateDatacenter) soap.HasFault {
  158. r := &methods.CreateDatacenterBody{}
  159. if f.hasChildType("Datacenter") && f.hasChildType("Folder") {
  160. dc := NewDatacenter(f)
  161. dc.Name = c.Name
  162. r.Res = &types.CreateDatacenterResponse{
  163. Returnval: dc.Self,
  164. }
  165. ctx.postEvent(&types.DatacenterCreatedEvent{
  166. DatacenterEvent: types.DatacenterEvent{
  167. Event: types.Event{
  168. Datacenter: datacenterEventArgument(dc),
  169. },
  170. },
  171. Parent: f.eventArgument(),
  172. })
  173. } else {
  174. r.Fault_ = f.typeNotSupported()
  175. }
  176. return r
  177. }
  178. func (f *Folder) CreateClusterEx(c *types.CreateClusterEx) soap.HasFault {
  179. r := &methods.CreateClusterExBody{}
  180. if f.hasChildType("ComputeResource") && f.hasChildType("Folder") {
  181. cluster, err := CreateClusterComputeResource(f, c.Name, c.Spec)
  182. if err != nil {
  183. r.Fault_ = Fault("", err)
  184. return r
  185. }
  186. r.Res = &types.CreateClusterExResponse{
  187. Returnval: cluster.Self,
  188. }
  189. } else {
  190. r.Fault_ = f.typeNotSupported()
  191. }
  192. return r
  193. }
  194. type createVM struct {
  195. *Folder
  196. ctx *Context
  197. req *types.CreateVM_Task
  198. register bool
  199. }
  200. // hostsWithDatastore returns hosts that have access to the given datastore path
  201. func hostsWithDatastore(hosts []types.ManagedObjectReference, path string) []types.ManagedObjectReference {
  202. attached := hosts[:0]
  203. var p object.DatastorePath
  204. p.FromString(path)
  205. for _, host := range hosts {
  206. h := Map.Get(host).(*HostSystem)
  207. if Map.FindByName(p.Datastore, h.Datastore) != nil {
  208. attached = append(attached, host)
  209. }
  210. }
  211. return attached
  212. }
  213. func (c *createVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
  214. vm, err := NewVirtualMachine(c.Folder.Self, &c.req.Config)
  215. if err != nil {
  216. c.Folder.removeChild(vm)
  217. return nil, err
  218. }
  219. vm.ResourcePool = &c.req.Pool
  220. if c.req.Host == nil {
  221. var hosts []types.ManagedObjectReference
  222. pool := Map.Get(c.req.Pool).(mo.Entity)
  223. switch cr := Map.getEntityComputeResource(pool).(type) {
  224. case *mo.ComputeResource:
  225. hosts = cr.Host
  226. case *ClusterComputeResource:
  227. hosts = cr.Host
  228. }
  229. hosts = hostsWithDatastore(hosts, c.req.Config.Files.VmPathName)
  230. host := hosts[rand.Intn(len(hosts))]
  231. vm.Runtime.Host = &host
  232. } else {
  233. vm.Runtime.Host = c.req.Host
  234. }
  235. vm.Guest = &types.GuestInfo{
  236. ToolsStatus: types.VirtualMachineToolsStatusToolsNotInstalled,
  237. ToolsVersion: "0",
  238. }
  239. vm.Summary.Guest = &types.VirtualMachineGuestSummary{
  240. ToolsStatus: vm.Guest.ToolsStatus,
  241. }
  242. vm.Summary.Config.VmPathName = vm.Config.Files.VmPathName
  243. vm.Summary.Runtime.Host = vm.Runtime.Host
  244. err = vm.create(&c.req.Config, c.register)
  245. if err != nil {
  246. c.Folder.removeChild(vm)
  247. return nil, err
  248. }
  249. host := Map.Get(*vm.Runtime.Host).(*HostSystem)
  250. Map.AppendReference(host, &host.Vm, vm.Self)
  251. vm.EnvironmentBrowser = *hostParent(&host.HostSystem).EnvironmentBrowser
  252. for i := range vm.Datastore {
  253. ds := Map.Get(vm.Datastore[i]).(*Datastore)
  254. Map.AppendReference(ds, &ds.Vm, vm.Self)
  255. }
  256. pool := Map.Get(*vm.ResourcePool)
  257. // This can be an internal call from VirtualApp.CreateChildVMTask, where pool is already locked.
  258. c.ctx.WithLock(pool, func() {
  259. switch rp := pool.(type) {
  260. case *ResourcePool:
  261. rp.Vm = append(rp.Vm, vm.Self)
  262. case *VirtualApp:
  263. rp.Vm = append(rp.Vm, vm.Self)
  264. }
  265. })
  266. event := vm.event()
  267. c.ctx.postEvent(
  268. &types.VmBeingCreatedEvent{
  269. VmEvent: event,
  270. ConfigSpec: &c.req.Config,
  271. },
  272. &types.VmInstanceUuidAssignedEvent{
  273. VmEvent: event,
  274. InstanceUuid: vm.Config.InstanceUuid,
  275. },
  276. &types.VmUuidAssignedEvent{
  277. VmEvent: event,
  278. Uuid: vm.Config.Uuid,
  279. },
  280. &types.VmCreatedEvent{
  281. VmEvent: event,
  282. },
  283. )
  284. vm.RefreshStorageInfo(c.ctx, nil)
  285. return vm.Reference(), nil
  286. }
  287. func (f *Folder) CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault {
  288. return &methods.CreateVM_TaskBody{
  289. Res: &types.CreateVM_TaskResponse{
  290. Returnval: NewTask(&createVM{f, ctx, c, false}).Run(),
  291. },
  292. }
  293. }
  294. type registerVM struct {
  295. *Folder
  296. ctx *Context
  297. req *types.RegisterVM_Task
  298. }
  299. func (c *registerVM) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
  300. host := c.req.Host
  301. pool := c.req.Pool
  302. if c.req.AsTemplate {
  303. if host == nil {
  304. return nil, &types.InvalidArgument{InvalidProperty: "host"}
  305. } else if pool != nil {
  306. return nil, &types.InvalidArgument{InvalidProperty: "pool"}
  307. }
  308. pool = hostParent(&Map.Get(*host).(*HostSystem).HostSystem).ResourcePool
  309. } else {
  310. if pool == nil {
  311. return nil, &types.InvalidArgument{InvalidProperty: "pool"}
  312. }
  313. }
  314. if c.req.Path == "" {
  315. return nil, &types.InvalidArgument{InvalidProperty: "path"}
  316. }
  317. s := Map.SearchIndex()
  318. r := s.FindByDatastorePath(&types.FindByDatastorePath{
  319. This: s.Reference(),
  320. Path: c.req.Path,
  321. Datacenter: Map.getEntityDatacenter(c.Folder).Reference(),
  322. })
  323. if ref := r.(*methods.FindByDatastorePathBody).Res.Returnval; ref != nil {
  324. return nil, &types.AlreadyExists{Name: ref.Value}
  325. }
  326. if c.req.Name == "" {
  327. p, err := parseDatastorePath(c.req.Path)
  328. if err != nil {
  329. return nil, err
  330. }
  331. c.req.Name = path.Dir(p.Path)
  332. }
  333. create := NewTask(&createVM{
  334. Folder: c.Folder,
  335. register: true,
  336. ctx: c.ctx,
  337. req: &types.CreateVM_Task{
  338. This: c.Folder.Reference(),
  339. Config: types.VirtualMachineConfigSpec{
  340. Name: c.req.Name,
  341. Files: &types.VirtualMachineFileInfo{
  342. VmPathName: c.req.Path,
  343. },
  344. },
  345. Pool: *pool,
  346. Host: host,
  347. },
  348. })
  349. create.Run()
  350. if create.Info.Error != nil {
  351. return nil, create.Info.Error.Fault
  352. }
  353. return create.Info.Result, nil
  354. }
  355. func (f *Folder) RegisterVMTask(ctx *Context, c *types.RegisterVM_Task) soap.HasFault {
  356. ctx.Caller = &f.Self
  357. return &methods.RegisterVM_TaskBody{
  358. Res: &types.RegisterVM_TaskResponse{
  359. Returnval: NewTask(&registerVM{f, ctx, c}).Run(),
  360. },
  361. }
  362. }
  363. func (f *Folder) MoveIntoFolderTask(c *types.MoveIntoFolder_Task) soap.HasFault {
  364. task := CreateTask(f, "moveIntoFolder", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  365. for _, ref := range c.List {
  366. obj := Map.Get(ref).(mo.Entity)
  367. parent, ok := Map.Get(*(obj.Entity()).Parent).(*Folder)
  368. if !ok || !f.hasChildType(ref.Type) {
  369. return nil, &types.NotSupported{}
  370. }
  371. parent.removeChild(ref)
  372. f.putChild(obj)
  373. }
  374. return nil, nil
  375. })
  376. return &methods.MoveIntoFolder_TaskBody{
  377. Res: &types.MoveIntoFolder_TaskResponse{
  378. Returnval: task.Run(),
  379. },
  380. }
  381. }
  382. func (f *Folder) CreateDVSTask(req *types.CreateDVS_Task) soap.HasFault {
  383. task := CreateTask(f, "createDVS", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  384. spec := req.Spec.ConfigSpec.GetDVSConfigSpec()
  385. dvs := &DistributedVirtualSwitch{}
  386. dvs.Name = spec.Name
  387. dvs.Entity().Name = dvs.Name
  388. if Map.FindByName(dvs.Name, f.ChildEntity) != nil {
  389. return nil, &types.InvalidArgument{InvalidProperty: "name"}
  390. }
  391. dvs.Uuid = uuid.New().String()
  392. f.putChild(dvs)
  393. dvs.Summary = types.DVSSummary{
  394. Name: dvs.Name,
  395. Uuid: dvs.Uuid,
  396. NumPorts: spec.NumStandalonePorts,
  397. ProductInfo: req.Spec.ProductInfo,
  398. Description: spec.Description,
  399. }
  400. configInfo := &types.VMwareDVSConfigInfo{
  401. DVSConfigInfo: types.DVSConfigInfo{
  402. Uuid: dvs.Uuid,
  403. Name: spec.Name,
  404. ConfigVersion: spec.ConfigVersion,
  405. NumStandalonePorts: spec.NumStandalonePorts,
  406. MaxPorts: spec.MaxPorts,
  407. UplinkPortPolicy: spec.UplinkPortPolicy,
  408. UplinkPortgroup: spec.UplinkPortgroup,
  409. DefaultPortConfig: spec.DefaultPortConfig,
  410. ExtensionKey: spec.ExtensionKey,
  411. Description: spec.Description,
  412. Policy: spec.Policy,
  413. VendorSpecificConfig: spec.VendorSpecificConfig,
  414. SwitchIpAddress: spec.SwitchIpAddress,
  415. DefaultProxySwitchMaxNumPorts: spec.DefaultProxySwitchMaxNumPorts,
  416. InfrastructureTrafficResourceConfig: spec.InfrastructureTrafficResourceConfig,
  417. NetworkResourceControlVersion: spec.NetworkResourceControlVersion,
  418. },
  419. }
  420. if spec.Contact != nil {
  421. configInfo.Contact = *spec.Contact
  422. }
  423. dvs.Config = configInfo
  424. if dvs.Summary.ProductInfo == nil {
  425. product := Map.content().About
  426. dvs.Summary.ProductInfo = &types.DistributedVirtualSwitchProductSpec{
  427. Name: "DVS",
  428. Vendor: product.Vendor,
  429. Version: product.Version,
  430. Build: product.Build,
  431. ForwardingClass: "etherswitch",
  432. }
  433. }
  434. dvs.AddDVPortgroupTask(&types.AddDVPortgroup_Task{
  435. Spec: []types.DVPortgroupConfigSpec{{
  436. Name: dvs.Name + "-DVUplinks" + strings.TrimPrefix(dvs.Self.Value, "dvs"),
  437. DefaultPortConfig: &types.VMwareDVSPortSetting{
  438. Vlan: &types.VmwareDistributedVirtualSwitchTrunkVlanSpec{
  439. VlanId: []types.NumericRange{{Start: 0, End: 4094}},
  440. },
  441. },
  442. }},
  443. })
  444. return dvs.Reference(), nil
  445. })
  446. return &methods.CreateDVS_TaskBody{
  447. Res: &types.CreateDVS_TaskResponse{
  448. Returnval: task.Run(),
  449. },
  450. }
  451. }
  452. func (f *Folder) RenameTask(r *types.Rename_Task) soap.HasFault {
  453. return RenameTask(f, r)
  454. }
  455. func (f *Folder) DestroyTask(req *types.Destroy_Task) soap.HasFault {
  456. type destroyer interface {
  457. mo.Reference
  458. DestroyTask(*types.Destroy_Task) soap.HasFault
  459. }
  460. task := CreateTask(f, "destroy", func(*Task) (types.AnyType, types.BaseMethodFault) {
  461. // Attempt to destroy all children
  462. for _, c := range f.ChildEntity {
  463. obj, ok := Map.Get(c).(destroyer)
  464. if !ok {
  465. continue
  466. }
  467. var fault types.BaseMethodFault
  468. Map.WithLock(obj, func() {
  469. id := obj.DestroyTask(&types.Destroy_Task{
  470. This: c,
  471. }).(*methods.Destroy_TaskBody).Res.Returnval
  472. t := Map.Get(id).(*Task)
  473. if t.Info.Error != nil {
  474. fault = t.Info.Error.Fault // For example, can't destroy a powered on VM
  475. }
  476. })
  477. if fault != nil {
  478. return nil, fault
  479. }
  480. }
  481. // Remove the folder itself
  482. Map.Get(*f.Parent).(*Folder).removeChild(f.Self)
  483. return nil, nil
  484. })
  485. return &methods.Destroy_TaskBody{
  486. Res: &types.Destroy_TaskResponse{
  487. Returnval: task.Run(),
  488. },
  489. }
  490. }