model.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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. "context"
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "path"
  20. "github.com/vmware/govmomi/object"
  21. "github.com/vmware/govmomi/simulator/esx"
  22. "github.com/vmware/govmomi/simulator/vpx"
  23. "github.com/vmware/govmomi/vim25/mo"
  24. "github.com/vmware/govmomi/vim25/types"
  25. )
  26. type DelayConfig struct {
  27. // Delay specifies the number of milliseconds to delay serving a SOAP call. 0 means no delay.
  28. // This can be used to simulate a poorly performing vCenter or network lag.
  29. Delay int
  30. // Delay specifies the number of milliseconds to delay serving a specific method.
  31. // Each entry in the map represents the name of a method and its associated delay in milliseconds,
  32. // This can be used to simulate a poorly performing vCenter or network lag.
  33. MethodDelay map[string]int
  34. // DelayJitter defines the delay jitter as a coefficient of variation (stddev/mean).
  35. // This can be used to simulate unpredictable delay. 0 means no jitter, i.e. all invocations get the same delay.
  36. DelayJitter float64
  37. }
  38. // Model is used to populate a Model with an initial set of managed entities.
  39. // This is a simple helper for tests running against a simulator, to populate an inventory
  40. // with commonly used models.
  41. type Model struct {
  42. Service *Service `json:"-"`
  43. ServiceContent types.ServiceContent `json:"-"`
  44. RootFolder mo.Folder `json:"-"`
  45. // Autostart will power on Model created VMs when true
  46. Autostart bool `json:"-"`
  47. // Datacenter specifies the number of Datacenter entities to create
  48. Datacenter int
  49. // Portgroup specifies the number of DistributedVirtualPortgroup entities to create per Datacenter
  50. Portgroup int
  51. // Host specifies the number of standalone HostSystems entities to create per Datacenter
  52. Host int `json:",omitempty"`
  53. // Cluster specifies the number of ClusterComputeResource entities to create per Datacenter
  54. Cluster int
  55. // ClusterHost specifies the number of HostSystems entities to create within a Cluster
  56. ClusterHost int `json:",omitempty"`
  57. // Pool specifies the number of ResourcePool entities to create per Cluster
  58. Pool int
  59. // Datastore specifies the number of Datastore entities to create
  60. // Each Datastore will have temporary local file storage and will be mounted
  61. // on every HostSystem created by the ModelConfig
  62. Datastore int
  63. // Machine specifies the number of VirtualMachine entities to create per ResourcePool
  64. Machine int
  65. // Folder specifies the number of Datacenter to place within a Folder.
  66. // This includes a folder for the Datacenter itself and its host, vm, network and datastore folders.
  67. // All resources for the Datacenter are placed within these folders, rather than the top-level folders.
  68. Folder int
  69. // App specifies the number of VirtualApp to create per Cluster
  70. App int
  71. // Pod specifies the number of StoragePod to create per Cluster
  72. Pod int
  73. // Delay configurations
  74. DelayConfig DelayConfig
  75. // total number of inventory objects, set by Count()
  76. total int
  77. dirs []string
  78. }
  79. // ESX is the default Model for a standalone ESX instance
  80. func ESX() *Model {
  81. return &Model{
  82. ServiceContent: esx.ServiceContent,
  83. RootFolder: esx.RootFolder,
  84. Autostart: true,
  85. Datastore: 1,
  86. Machine: 2,
  87. DelayConfig: DelayConfig{
  88. Delay: 0,
  89. DelayJitter: 0,
  90. MethodDelay: nil,
  91. },
  92. }
  93. }
  94. // VPX is the default Model for a vCenter instance
  95. func VPX() *Model {
  96. return &Model{
  97. ServiceContent: vpx.ServiceContent,
  98. RootFolder: vpx.RootFolder,
  99. Autostart: true,
  100. Datacenter: 1,
  101. Portgroup: 1,
  102. Host: 1,
  103. Cluster: 1,
  104. ClusterHost: 3,
  105. Datastore: 1,
  106. Machine: 2,
  107. DelayConfig: DelayConfig{
  108. Delay: 0,
  109. DelayJitter: 0,
  110. MethodDelay: nil,
  111. },
  112. }
  113. }
  114. // Count returns a Model with total number of each existing type
  115. func (m *Model) Count() Model {
  116. count := Model{}
  117. for ref, obj := range Map.objects {
  118. if _, ok := obj.(mo.Entity); !ok {
  119. continue
  120. }
  121. count.total++
  122. switch ref.Type {
  123. case "Datacenter":
  124. count.Datacenter++
  125. case "DistributedVirtualPortgroup":
  126. count.Portgroup++
  127. case "ClusterComputeResource":
  128. count.Cluster++
  129. case "Datastore":
  130. count.Datastore++
  131. case "HostSystem":
  132. count.Host++
  133. case "VirtualMachine":
  134. count.Machine++
  135. case "ResourcePool":
  136. count.Pool++
  137. case "VirtualApp":
  138. count.App++
  139. case "Folder":
  140. count.Folder++
  141. case "StoragePod":
  142. count.Pod++
  143. }
  144. }
  145. return count
  146. }
  147. func (*Model) fmtName(prefix string, num int) string {
  148. return fmt.Sprintf("%s%d", prefix, num)
  149. }
  150. // Create populates the Model with the given ModelConfig
  151. func (m *Model) Create() error {
  152. m.Service = New(NewServiceInstance(m.ServiceContent, m.RootFolder))
  153. ctx := context.Background()
  154. client := m.Service.client
  155. root := object.NewRootFolder(client)
  156. // After all hosts are created, this var is used to mount the host datastores.
  157. var hosts []*object.HostSystem
  158. hostMap := make(map[string][]*object.HostSystem)
  159. // We need to defer VM creation until after the datastores are created.
  160. var vms []func() error
  161. // 1 DVS per DC, added to all hosts
  162. var dvs *object.DistributedVirtualSwitch
  163. // 1 NIC per VM, backed by a DVPG if Model.Portgroup > 0
  164. vmnet := esx.EthernetCard.Backing
  165. // addHost adds a cluster host or a stanalone host.
  166. addHost := func(name string, f func(types.HostConnectSpec) (*object.Task, error)) (*object.HostSystem, error) {
  167. spec := types.HostConnectSpec{
  168. HostName: name,
  169. }
  170. task, err := f(spec)
  171. if err != nil {
  172. return nil, err
  173. }
  174. info, err := task.WaitForResult(context.Background(), nil)
  175. if err != nil {
  176. return nil, err
  177. }
  178. host := object.NewHostSystem(client, info.Result.(types.ManagedObjectReference))
  179. hosts = append(hosts, host)
  180. if dvs != nil {
  181. config := &types.DVSConfigSpec{
  182. Host: []types.DistributedVirtualSwitchHostMemberConfigSpec{{
  183. Operation: string(types.ConfigSpecOperationAdd),
  184. Host: host.Reference(),
  185. }},
  186. }
  187. _, _ = dvs.Reconfigure(ctx, config)
  188. }
  189. return host, nil
  190. }
  191. // addMachine returns a func to create a VM.
  192. addMachine := func(prefix string, host *object.HostSystem, pool *object.ResourcePool, folders *object.DatacenterFolders) {
  193. nic := esx.EthernetCard
  194. nic.Backing = vmnet
  195. ds := types.ManagedObjectReference{}
  196. f := func() error {
  197. for i := 0; i < m.Machine; i++ {
  198. name := m.fmtName(prefix+"_VM", i)
  199. config := types.VirtualMachineConfigSpec{
  200. Name: name,
  201. GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
  202. Files: &types.VirtualMachineFileInfo{
  203. VmPathName: "[LocalDS_0]",
  204. },
  205. }
  206. if pool == nil {
  207. pool, _ = host.ResourcePool(ctx)
  208. }
  209. var devices object.VirtualDeviceList
  210. scsi, _ := devices.CreateSCSIController("pvscsi")
  211. ide, _ := devices.CreateIDEController()
  212. cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController))
  213. disk := devices.CreateDisk(scsi.(types.BaseVirtualController), ds,
  214. config.Files.VmPathName+" "+path.Join(name, "disk1.vmdk"))
  215. disk.CapacityInKB = 1024
  216. devices = append(devices, scsi, cdrom, disk, &nic)
  217. config.DeviceChange, _ = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
  218. task, err := folders.VmFolder.CreateVM(ctx, config, pool, host)
  219. if err != nil {
  220. return err
  221. }
  222. info, err := task.WaitForResult(ctx, nil)
  223. if err != nil {
  224. return err
  225. }
  226. vm := object.NewVirtualMachine(client, info.Result.(types.ManagedObjectReference))
  227. if m.Autostart {
  228. _, _ = vm.PowerOn(ctx)
  229. }
  230. }
  231. return nil
  232. }
  233. vms = append(vms, f)
  234. }
  235. nfolder := 0
  236. for ndc := 0; ndc < m.Datacenter; ndc++ {
  237. dcName := m.fmtName("DC", ndc)
  238. folder := root
  239. fName := m.fmtName("F", nfolder)
  240. // If Datacenter > Folder, don't create folders for the first N DCs.
  241. if nfolder < m.Folder && ndc >= (m.Datacenter-m.Folder) {
  242. f, err := folder.CreateFolder(ctx, fName)
  243. if err != nil {
  244. return err
  245. }
  246. folder = f
  247. }
  248. dc, err := folder.CreateDatacenter(ctx, dcName)
  249. if err != nil {
  250. return err
  251. }
  252. folders, err := dc.Folders(ctx)
  253. if err != nil {
  254. return err
  255. }
  256. if m.Pod > 0 {
  257. for pod := 0; pod < m.Pod; pod++ {
  258. _, _ = folders.DatastoreFolder.CreateStoragePod(ctx, m.fmtName(dcName+"_POD", pod))
  259. }
  260. }
  261. if folder != root {
  262. // Create sub-folders and use them to create any resources that follow
  263. subs := []**object.Folder{&folders.DatastoreFolder, &folders.HostFolder, &folders.NetworkFolder, &folders.VmFolder}
  264. for _, sub := range subs {
  265. f, err := (*sub).CreateFolder(ctx, fName)
  266. if err != nil {
  267. return err
  268. }
  269. *sub = f
  270. }
  271. nfolder++
  272. }
  273. if m.Portgroup > 0 {
  274. var spec types.DVSCreateSpec
  275. spec.ConfigSpec = &types.VMwareDVSConfigSpec{}
  276. spec.ConfigSpec.GetDVSConfigSpec().Name = m.fmtName("DVS", 0)
  277. task, err := folders.NetworkFolder.CreateDVS(ctx, spec)
  278. if err != nil {
  279. return err
  280. }
  281. info, err := task.WaitForResult(ctx, nil)
  282. if err != nil {
  283. return err
  284. }
  285. dvs = object.NewDistributedVirtualSwitch(client, info.Result.(types.ManagedObjectReference))
  286. for npg := 0; npg < m.Portgroup; npg++ {
  287. name := m.fmtName(dcName+"_DVPG", npg)
  288. task, err = dvs.AddPortgroup(ctx, []types.DVPortgroupConfigSpec{{Name: name}})
  289. if err != nil {
  290. return err
  291. }
  292. err = task.Wait(ctx)
  293. if err != nil {
  294. return err
  295. }
  296. // Use the 1st DVPG for the VMs eth0 backing
  297. if npg == 0 {
  298. // AddPortgroup_Task does not return the moid, so we look it up by name
  299. net := Map.Get(folders.NetworkFolder.Reference()).(*Folder)
  300. pg := Map.FindByName(name, net.ChildEntity)
  301. vmnet, _ = object.NewDistributedVirtualPortgroup(client, pg.Reference()).EthernetCardBackingInfo(ctx)
  302. }
  303. }
  304. }
  305. for nhost := 0; nhost < m.Host; nhost++ {
  306. name := m.fmtName(dcName+"_H", nhost)
  307. host, err := addHost(name, func(spec types.HostConnectSpec) (*object.Task, error) {
  308. return folders.HostFolder.AddStandaloneHost(ctx, spec, true, nil, nil)
  309. })
  310. if err != nil {
  311. return err
  312. }
  313. addMachine(name, host, nil, folders)
  314. }
  315. for ncluster := 0; ncluster < m.Cluster; ncluster++ {
  316. clusterName := m.fmtName(dcName+"_C", ncluster)
  317. cluster, err := folders.HostFolder.CreateCluster(ctx, clusterName, types.ClusterConfigSpecEx{})
  318. if err != nil {
  319. return err
  320. }
  321. for nhost := 0; nhost < m.ClusterHost; nhost++ {
  322. name := m.fmtName(clusterName+"_H", nhost)
  323. _, err = addHost(name, func(spec types.HostConnectSpec) (*object.Task, error) {
  324. return cluster.AddHost(ctx, spec, true, nil, nil)
  325. })
  326. if err != nil {
  327. return err
  328. }
  329. }
  330. pool, err := cluster.ResourcePool(ctx)
  331. if err != nil {
  332. return err
  333. }
  334. prefix := clusterName + "_RP"
  335. addMachine(prefix+"0", nil, pool, folders)
  336. for npool := 1; npool <= m.Pool; npool++ {
  337. spec := types.DefaultResourceConfigSpec()
  338. _, err = pool.Create(ctx, m.fmtName(prefix, npool), spec)
  339. if err != nil {
  340. return err
  341. }
  342. }
  343. prefix = clusterName + "_APP"
  344. for napp := 0; napp < m.App; napp++ {
  345. rspec := types.DefaultResourceConfigSpec()
  346. vspec := NewVAppConfigSpec()
  347. name := m.fmtName(prefix, napp)
  348. vapp, err := pool.CreateVApp(ctx, name, rspec, vspec, nil)
  349. if err != nil {
  350. return err
  351. }
  352. addMachine(name, nil, vapp.ResourcePool, folders)
  353. }
  354. }
  355. hostMap[dcName] = hosts
  356. hosts = nil
  357. }
  358. if m.ServiceContent.RootFolder == esx.RootFolder.Reference() {
  359. // ESX model
  360. host := object.NewHostSystem(client, esx.HostSystem.Reference())
  361. dc := object.NewDatacenter(client, esx.Datacenter.Reference())
  362. folders, err := dc.Folders(ctx)
  363. if err != nil {
  364. return err
  365. }
  366. hostMap[dc.Reference().Value] = append(hosts, host)
  367. addMachine(host.Reference().Value, host, nil, folders)
  368. }
  369. for dc, dchosts := range hostMap {
  370. for i := 0; i < m.Datastore; i++ {
  371. err := m.createLocalDatastore(dc, m.fmtName("LocalDS_", i), dchosts)
  372. if err != nil {
  373. return err
  374. }
  375. }
  376. }
  377. for _, createVM := range vms {
  378. err := createVM()
  379. if err != nil {
  380. return err
  381. }
  382. }
  383. // Turn on delay AFTER we're done building the service content
  384. m.Service.delay = &m.DelayConfig
  385. return nil
  386. }
  387. func (m *Model) createLocalDatastore(dc string, name string, hosts []*object.HostSystem) error {
  388. ctx := context.Background()
  389. dir, err := ioutil.TempDir("", fmt.Sprintf("govcsim-%s-%s-", dc, name))
  390. if err != nil {
  391. return err
  392. }
  393. m.dirs = append(m.dirs, dir)
  394. for _, host := range hosts {
  395. dss, err := host.ConfigManager().DatastoreSystem(ctx)
  396. if err != nil {
  397. return err
  398. }
  399. _, err = dss.CreateLocalDatastore(ctx, name, dir)
  400. if err != nil {
  401. return err
  402. }
  403. }
  404. return nil
  405. }
  406. // Remove cleans up items created by the Model, such as local datastore directories
  407. func (m *Model) Remove() {
  408. for _, dir := range m.dirs {
  409. _ = os.RemoveAll(dir)
  410. }
  411. }