PageRenderTime 35ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/github.com/vmware/govmomi/object/virtual_device_list.go

https://gitlab.com/wilane/machine
Go | 754 lines | 554 code | 131 blank | 69 comment | 125 complexity | f8527ed55fb16337ee5a352603433c33 MD5 | raw file
  1. /*
  2. Copyright (c) 2015 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 object
  14. import (
  15. "errors"
  16. "fmt"
  17. "path/filepath"
  18. "reflect"
  19. "regexp"
  20. "sort"
  21. "strings"
  22. "github.com/vmware/govmomi/vim25/types"
  23. )
  24. // Type values for use in BootOrder
  25. const (
  26. DeviceTypeCdrom = "cdrom"
  27. DeviceTypeDisk = "disk"
  28. DeviceTypeEthernet = "ethernet"
  29. DeviceTypeFloppy = "floppy"
  30. )
  31. // VirtualDeviceList provides helper methods for working with a list of virtual devices.
  32. type VirtualDeviceList []types.BaseVirtualDevice
  33. // SCSIControllerTypes are used for adding a new SCSI controller to a VM.
  34. func SCSIControllerTypes() VirtualDeviceList {
  35. // Return a mutable list of SCSI controller types, initialized with defaults.
  36. return VirtualDeviceList([]types.BaseVirtualDevice{
  37. &types.VirtualLsiLogicController{},
  38. &types.VirtualBusLogicController{},
  39. &types.ParaVirtualSCSIController{},
  40. &types.VirtualLsiLogicSASController{},
  41. }).Select(func(device types.BaseVirtualDevice) bool {
  42. c := device.(types.BaseVirtualSCSIController).GetVirtualSCSIController()
  43. c.SharedBus = types.VirtualSCSISharingNoSharing
  44. c.BusNumber = -1
  45. return true
  46. })
  47. }
  48. // EthernetCardTypes are used for adding a new ethernet card to a VM.
  49. func EthernetCardTypes() VirtualDeviceList {
  50. return VirtualDeviceList([]types.BaseVirtualDevice{
  51. &types.VirtualE1000{},
  52. &types.VirtualE1000e{},
  53. &types.VirtualVmxnet3{},
  54. }).Select(func(device types.BaseVirtualDevice) bool {
  55. c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
  56. c.AddressType = string(types.VirtualEthernetCardMacTypeGenerated)
  57. c.GetVirtualDevice().Key = -1
  58. return true
  59. })
  60. }
  61. // Select returns a new list containing all elements of the list for which the given func returns true.
  62. func (l VirtualDeviceList) Select(f func(device types.BaseVirtualDevice) bool) VirtualDeviceList {
  63. var found VirtualDeviceList
  64. for _, device := range l {
  65. if f(device) {
  66. found = append(found, device)
  67. }
  68. }
  69. return found
  70. }
  71. // SelectByType returns a new list with devices that are equal to or extend the given type.
  72. func (l VirtualDeviceList) SelectByType(deviceType types.BaseVirtualDevice) VirtualDeviceList {
  73. dtype := reflect.TypeOf(deviceType)
  74. dname := dtype.Elem().Name()
  75. return l.Select(func(device types.BaseVirtualDevice) bool {
  76. t := reflect.TypeOf(device)
  77. if t == dtype {
  78. return true
  79. }
  80. _, ok := t.Elem().FieldByName(dname)
  81. return ok
  82. })
  83. }
  84. // SelectByBackingInfo returns a new list with devices matching the given backing info.
  85. // If the value of backing is nil, any device with a backing of the same type will be returned.
  86. func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBackingInfo) VirtualDeviceList {
  87. t := reflect.TypeOf(backing)
  88. return l.Select(func(device types.BaseVirtualDevice) bool {
  89. db := device.GetVirtualDevice().Backing
  90. if db == nil {
  91. return false
  92. }
  93. if reflect.TypeOf(db) != t {
  94. return false
  95. }
  96. if reflect.ValueOf(backing).IsNil() {
  97. // selecting by backing type
  98. return true
  99. }
  100. switch a := db.(type) {
  101. case *types.VirtualEthernetCardNetworkBackingInfo:
  102. b := backing.(*types.VirtualEthernetCardNetworkBackingInfo)
  103. return a.DeviceName == b.DeviceName
  104. case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
  105. b := backing.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo)
  106. return a.Port.SwitchUuid == b.Port.SwitchUuid
  107. case *types.VirtualDiskFlatVer2BackingInfo:
  108. b := backing.(*types.VirtualDiskFlatVer2BackingInfo)
  109. if a.Parent != nil && b.Parent != nil {
  110. return a.Parent.FileName == b.Parent.FileName
  111. }
  112. return a.FileName == b.FileName
  113. case *types.VirtualSerialPortURIBackingInfo:
  114. b := backing.(*types.VirtualSerialPortURIBackingInfo)
  115. return a.ServiceURI == b.ServiceURI
  116. case types.BaseVirtualDeviceFileBackingInfo:
  117. b := backing.(types.BaseVirtualDeviceFileBackingInfo)
  118. return a.GetVirtualDeviceFileBackingInfo().FileName == b.GetVirtualDeviceFileBackingInfo().FileName
  119. default:
  120. return false
  121. }
  122. })
  123. }
  124. // Find returns the device matching the given name.
  125. func (l VirtualDeviceList) Find(name string) types.BaseVirtualDevice {
  126. for _, device := range l {
  127. if l.Name(device) == name {
  128. return device
  129. }
  130. }
  131. return nil
  132. }
  133. // FindByKey returns the device matching the given key.
  134. func (l VirtualDeviceList) FindByKey(key int) types.BaseVirtualDevice {
  135. for _, device := range l {
  136. if device.GetVirtualDevice().Key == key {
  137. return device
  138. }
  139. }
  140. return nil
  141. }
  142. // FindIDEController will find the named IDE controller if given, otherwise will pick an available controller.
  143. // An error is returned if the named controller is not found or not an IDE controller. Or, if name is not
  144. // given and no available controller can be found.
  145. func (l VirtualDeviceList) FindIDEController(name string) (*types.VirtualIDEController, error) {
  146. if name != "" {
  147. d := l.Find(name)
  148. if d == nil {
  149. return nil, fmt.Errorf("device '%s' not found", name)
  150. }
  151. if c, ok := d.(*types.VirtualIDEController); ok {
  152. return c, nil
  153. }
  154. return nil, fmt.Errorf("%s is not an IDE controller", name)
  155. }
  156. c := l.PickController((*types.VirtualIDEController)(nil))
  157. if c == nil {
  158. return nil, errors.New("no available IDE controller")
  159. }
  160. return c.(*types.VirtualIDEController), nil
  161. }
  162. // FindSCSIController will find the named SCSI controller if given, otherwise will pick an available controller.
  163. // An error is returned if the named controller is not found or not an SCSI controller. Or, if name is not
  164. // given and no available controller can be found.
  165. func (l VirtualDeviceList) FindSCSIController(name string) (*types.VirtualSCSIController, error) {
  166. if name != "" {
  167. d := l.Find(name)
  168. if d == nil {
  169. return nil, fmt.Errorf("device '%s' not found", name)
  170. }
  171. if c, ok := d.(types.BaseVirtualSCSIController); ok {
  172. return c.GetVirtualSCSIController(), nil
  173. }
  174. return nil, fmt.Errorf("%s is not an SCSI controller", name)
  175. }
  176. c := l.PickController((*types.VirtualSCSIController)(nil))
  177. if c == nil {
  178. return nil, errors.New("no available SCSI controller")
  179. }
  180. return c.(types.BaseVirtualSCSIController).GetVirtualSCSIController(), nil
  181. }
  182. // CreateSCSIController creates a new SCSI controller of type name if given, otherwise defaults to lsilogic.
  183. func (l VirtualDeviceList) CreateSCSIController(name string) (types.BaseVirtualDevice, error) {
  184. ctypes := SCSIControllerTypes()
  185. if name == "scsi" || name == "" {
  186. name = ctypes.Type(ctypes[0])
  187. }
  188. found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
  189. return l.Type(device) == name
  190. })
  191. if len(found) == 0 {
  192. return nil, fmt.Errorf("unknown SCSI controller type '%s'", name)
  193. }
  194. c, ok := found[0].(types.BaseVirtualSCSIController)
  195. if !ok {
  196. return nil, fmt.Errorf("invalid SCSI controller type '%s'", name)
  197. }
  198. scsi := c.GetVirtualSCSIController()
  199. scsi.BusNumber = l.newSCSIBusNumber()
  200. return c.(types.BaseVirtualDevice), nil
  201. }
  202. var scsiBusNumbers = []int{0, 1, 2, 3}
  203. // newSCSIBusNumber returns the bus number to use for adding a new SCSI bus device.
  204. // -1 is returned if there are no bus numbers available.
  205. func (l VirtualDeviceList) newSCSIBusNumber() int {
  206. var used []int
  207. for _, d := range l.SelectByType((*types.VirtualSCSIController)(nil)) {
  208. num := d.(types.BaseVirtualSCSIController).GetVirtualSCSIController().BusNumber
  209. if num >= 0 {
  210. used = append(used, num)
  211. } // else caller is creating a new vm using SCSIControllerTypes
  212. }
  213. sort.Ints(used)
  214. for i, n := range scsiBusNumbers {
  215. if i == len(used) || n != used[i] {
  216. return n
  217. }
  218. }
  219. return -1
  220. }
  221. // FindDiskController will find an existing ide or scsi disk controller.
  222. func (l VirtualDeviceList) FindDiskController(name string) (types.BaseVirtualController, error) {
  223. switch {
  224. case name == "ide":
  225. return l.FindIDEController("")
  226. case name == "scsi" || name == "":
  227. return l.FindSCSIController("")
  228. default:
  229. if c, ok := l.Find(name).(types.BaseVirtualController); ok {
  230. return c, nil
  231. }
  232. return nil, fmt.Errorf("%s is not a valid controller", name)
  233. }
  234. }
  235. // PickController returns a controller of the given type(s).
  236. // If no controllers are found or have no available slots, then nil is returned.
  237. func (l VirtualDeviceList) PickController(kind types.BaseVirtualController) types.BaseVirtualController {
  238. l = l.SelectByType(kind.(types.BaseVirtualDevice)).Select(func(device types.BaseVirtualDevice) bool {
  239. num := len(device.(types.BaseVirtualController).GetVirtualController().Device)
  240. switch device.(type) {
  241. case types.BaseVirtualSCSIController:
  242. return num < 15
  243. case *types.VirtualIDEController:
  244. return num < 2
  245. default:
  246. return true
  247. }
  248. })
  249. if len(l) == 0 {
  250. return nil
  251. }
  252. return l[0].(types.BaseVirtualController)
  253. }
  254. // newUnitNumber returns the unit number to use for attaching a new device to the given controller.
  255. func (l VirtualDeviceList) newUnitNumber(c types.BaseVirtualController) int {
  256. key := c.GetVirtualController().Key
  257. max := -1
  258. for _, device := range l {
  259. d := device.GetVirtualDevice()
  260. if d.ControllerKey == key {
  261. if d.UnitNumber > max {
  262. max = d.UnitNumber
  263. }
  264. }
  265. }
  266. return max + 1
  267. }
  268. // AssignController assigns a device to a controller.
  269. func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c types.BaseVirtualController) {
  270. d := device.GetVirtualDevice()
  271. d.ControllerKey = c.GetVirtualController().Key
  272. d.UnitNumber = l.newUnitNumber(c)
  273. d.Key = -1
  274. }
  275. // CreateDisk creates a new VirtualDisk device which can be added to a VM.
  276. func (l VirtualDeviceList) CreateDisk(c types.BaseVirtualController, name string) *types.VirtualDisk {
  277. // If name is not specified, one will be chosen for you.
  278. // But if when given, make sure it ends in .vmdk, otherwise it will be treated as a directory.
  279. if len(name) > 0 && filepath.Ext(name) != ".vmdk" {
  280. name += ".vmdk"
  281. }
  282. device := &types.VirtualDisk{
  283. VirtualDevice: types.VirtualDevice{
  284. Backing: &types.VirtualDiskFlatVer2BackingInfo{
  285. DiskMode: string(types.VirtualDiskModePersistent),
  286. ThinProvisioned: types.NewBool(true),
  287. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  288. FileName: name,
  289. },
  290. },
  291. },
  292. }
  293. l.AssignController(device, c)
  294. if device.UnitNumber == 0 {
  295. device.UnitNumber = -1 // TODO: this field is annotated as omitempty
  296. }
  297. return device
  298. }
  299. // ChildDisk creates a new VirtualDisk device, linked to the given parent disk, which can be added to a VM.
  300. func (l VirtualDeviceList) ChildDisk(parent *types.VirtualDisk) *types.VirtualDisk {
  301. disk := *parent
  302. backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
  303. ds := strings.SplitN(backing.FileName[1:], "]", 2)
  304. // Use specified disk as parent backing to a new disk.
  305. disk.Backing = &types.VirtualDiskFlatVer2BackingInfo{
  306. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  307. FileName: fmt.Sprintf("[%s]", ds[0]),
  308. },
  309. Parent: backing,
  310. DiskMode: backing.DiskMode,
  311. ThinProvisioned: backing.ThinProvisioned,
  312. }
  313. return &disk
  314. }
  315. func (l VirtualDeviceList) connectivity(device types.BaseVirtualDevice, v bool) error {
  316. c := device.GetVirtualDevice().Connectable
  317. if c == nil {
  318. return fmt.Errorf("%s is not connectable", l.Name(device))
  319. }
  320. c.Connected = v
  321. c.StartConnected = v
  322. return nil
  323. }
  324. // Connect changes the device to connected, returns an error if the device is not connectable.
  325. func (l VirtualDeviceList) Connect(device types.BaseVirtualDevice) error {
  326. return l.connectivity(device, true)
  327. }
  328. // Disconnect changes the device to disconnected, returns an error if the device is not connectable.
  329. func (l VirtualDeviceList) Disconnect(device types.BaseVirtualDevice) error {
  330. return l.connectivity(device, false)
  331. }
  332. // FindCdrom finds a cdrom device with the given name, defaulting to the first cdrom device if any.
  333. func (l VirtualDeviceList) FindCdrom(name string) (*types.VirtualCdrom, error) {
  334. if name != "" {
  335. d := l.Find(name)
  336. if d == nil {
  337. return nil, fmt.Errorf("device '%s' not found", name)
  338. }
  339. if c, ok := d.(*types.VirtualCdrom); ok {
  340. return c, nil
  341. }
  342. return nil, fmt.Errorf("%s is not a cdrom device", name)
  343. }
  344. c := l.SelectByType((*types.VirtualCdrom)(nil))
  345. if len(c) == 0 {
  346. return nil, errors.New("no cdrom device found")
  347. }
  348. return c[0].(*types.VirtualCdrom), nil
  349. }
  350. // CreateCdrom creates a new VirtualCdrom device which can be added to a VM.
  351. func (l VirtualDeviceList) CreateCdrom(c *types.VirtualIDEController) (*types.VirtualCdrom, error) {
  352. device := &types.VirtualCdrom{}
  353. l.AssignController(device, c)
  354. l.setDefaultCdromBacking(device)
  355. device.Connectable = &types.VirtualDeviceConnectInfo{
  356. AllowGuestControl: true,
  357. Connected: true,
  358. StartConnected: true,
  359. }
  360. return device, nil
  361. }
  362. // InsertIso changes the cdrom device backing to use the given iso file.
  363. func (l VirtualDeviceList) InsertIso(device *types.VirtualCdrom, iso string) *types.VirtualCdrom {
  364. device.Backing = &types.VirtualCdromIsoBackingInfo{
  365. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  366. FileName: iso,
  367. },
  368. }
  369. return device
  370. }
  371. // EjectIso removes the iso file based backing and replaces with the default cdrom backing.
  372. func (l VirtualDeviceList) EjectIso(device *types.VirtualCdrom) *types.VirtualCdrom {
  373. l.setDefaultCdromBacking(device)
  374. return device
  375. }
  376. func (l VirtualDeviceList) setDefaultCdromBacking(device *types.VirtualCdrom) {
  377. device.Backing = &types.VirtualCdromAtapiBackingInfo{
  378. VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
  379. DeviceName: fmt.Sprintf("%s-%d-%d", DeviceTypeCdrom, device.ControllerKey, device.UnitNumber),
  380. UseAutoDetect: types.NewBool(false),
  381. },
  382. }
  383. }
  384. // FindFloppy finds a floppy device with the given name, defaulting to the first floppy device if any.
  385. func (l VirtualDeviceList) FindFloppy(name string) (*types.VirtualFloppy, error) {
  386. if name != "" {
  387. d := l.Find(name)
  388. if d == nil {
  389. return nil, fmt.Errorf("device '%s' not found", name)
  390. }
  391. if c, ok := d.(*types.VirtualFloppy); ok {
  392. return c, nil
  393. }
  394. return nil, fmt.Errorf("%s is not a floppy device", name)
  395. }
  396. c := l.SelectByType((*types.VirtualFloppy)(nil))
  397. if len(c) == 0 {
  398. return nil, errors.New("no floppy device found")
  399. }
  400. return c[0].(*types.VirtualFloppy), nil
  401. }
  402. // CreateFloppy creates a new VirtualFloppy device which can be added to a VM.
  403. func (l VirtualDeviceList) CreateFloppy() (*types.VirtualFloppy, error) {
  404. device := &types.VirtualFloppy{}
  405. c := l.PickController((*types.VirtualSIOController)(nil))
  406. if c == nil {
  407. return nil, errors.New("no available SIO controller")
  408. }
  409. l.AssignController(device, c)
  410. l.setDefaultFloppyBacking(device)
  411. device.Connectable = &types.VirtualDeviceConnectInfo{
  412. AllowGuestControl: true,
  413. Connected: true,
  414. StartConnected: true,
  415. }
  416. return device, nil
  417. }
  418. // InsertImg changes the floppy device backing to use the given img file.
  419. func (l VirtualDeviceList) InsertImg(device *types.VirtualFloppy, img string) *types.VirtualFloppy {
  420. device.Backing = &types.VirtualFloppyImageBackingInfo{
  421. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  422. FileName: img,
  423. },
  424. }
  425. return device
  426. }
  427. // EjectImg removes the img file based backing and replaces with the default floppy backing.
  428. func (l VirtualDeviceList) EjectImg(device *types.VirtualFloppy) *types.VirtualFloppy {
  429. l.setDefaultFloppyBacking(device)
  430. return device
  431. }
  432. func (l VirtualDeviceList) setDefaultFloppyBacking(device *types.VirtualFloppy) {
  433. device.Backing = &types.VirtualFloppyDeviceBackingInfo{
  434. VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
  435. DeviceName: fmt.Sprintf("%s-%d", DeviceTypeFloppy, device.UnitNumber),
  436. UseAutoDetect: types.NewBool(false),
  437. },
  438. }
  439. }
  440. // FindSerialPort finds a serial port device with the given name, defaulting to the first serial port device if any.
  441. func (l VirtualDeviceList) FindSerialPort(name string) (*types.VirtualSerialPort, error) {
  442. if name != "" {
  443. d := l.Find(name)
  444. if d == nil {
  445. return nil, fmt.Errorf("device '%s' not found", name)
  446. }
  447. if c, ok := d.(*types.VirtualSerialPort); ok {
  448. return c, nil
  449. }
  450. return nil, fmt.Errorf("%s is not a serial port device", name)
  451. }
  452. c := l.SelectByType((*types.VirtualSerialPort)(nil))
  453. if len(c) == 0 {
  454. return nil, errors.New("no serial port device found")
  455. }
  456. return c[0].(*types.VirtualSerialPort), nil
  457. }
  458. // CreateSerialPort creates a new VirtualSerialPort device which can be added to a VM.
  459. func (l VirtualDeviceList) CreateSerialPort() (*types.VirtualSerialPort, error) {
  460. device := &types.VirtualSerialPort{
  461. YieldOnPoll: true,
  462. }
  463. c := l.PickController((*types.VirtualSIOController)(nil))
  464. if c == nil {
  465. return nil, errors.New("no available SIO controller")
  466. }
  467. l.AssignController(device, c)
  468. l.setDefaultSerialPortBacking(device)
  469. return device, nil
  470. }
  471. // ConnectSerialPort connects a serial port to a server or client uri.
  472. func (l VirtualDeviceList) ConnectSerialPort(device *types.VirtualSerialPort, uri string, client bool) *types.VirtualSerialPort {
  473. direction := types.VirtualDeviceURIBackingOptionDirectionServer
  474. if client {
  475. direction = types.VirtualDeviceURIBackingOptionDirectionClient
  476. }
  477. device.Backing = &types.VirtualSerialPortURIBackingInfo{
  478. VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
  479. Direction: string(direction),
  480. ServiceURI: uri,
  481. },
  482. }
  483. return device
  484. }
  485. // DisconnectSerialPort disconnects the serial port backing.
  486. func (l VirtualDeviceList) DisconnectSerialPort(device *types.VirtualSerialPort) *types.VirtualSerialPort {
  487. l.setDefaultSerialPortBacking(device)
  488. return device
  489. }
  490. func (l VirtualDeviceList) setDefaultSerialPortBacking(device *types.VirtualSerialPort) {
  491. device.Backing = &types.VirtualSerialPortURIBackingInfo{
  492. VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
  493. Direction: "client",
  494. ServiceURI: "localhost:0",
  495. },
  496. }
  497. }
  498. // CreateEthernetCard creates a new VirtualEthernetCard of the given name name and initialized with the given backing.
  499. func (l VirtualDeviceList) CreateEthernetCard(name string, backing types.BaseVirtualDeviceBackingInfo) (types.BaseVirtualDevice, error) {
  500. ctypes := EthernetCardTypes()
  501. if name == "" {
  502. name = ctypes.deviceName(ctypes[0])
  503. }
  504. found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
  505. return l.deviceName(device) == name
  506. })
  507. if len(found) == 0 {
  508. return nil, fmt.Errorf("unknown ethernet card type '%s'", name)
  509. }
  510. c, ok := found[0].(types.BaseVirtualEthernetCard)
  511. if !ok {
  512. return nil, fmt.Errorf("invalid ethernet card type '%s'", name)
  513. }
  514. c.GetVirtualEthernetCard().Backing = backing
  515. return c.(types.BaseVirtualDevice), nil
  516. }
  517. // PrimaryMacAddress returns the MacAddress field of the primary VirtualEthernetCard
  518. func (l VirtualDeviceList) PrimaryMacAddress() string {
  519. eth0 := l.Find("ethernet-0")
  520. if eth0 == nil {
  521. return ""
  522. }
  523. return eth0.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard().MacAddress
  524. }
  525. // convert a BaseVirtualDevice to a BaseVirtualMachineBootOptionsBootableDevice
  526. var bootableDevices = map[string]func(device types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice{
  527. DeviceTypeCdrom: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  528. return &types.VirtualMachineBootOptionsBootableCdromDevice{}
  529. },
  530. DeviceTypeDisk: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  531. return &types.VirtualMachineBootOptionsBootableDiskDevice{
  532. DeviceKey: d.GetVirtualDevice().Key,
  533. }
  534. },
  535. DeviceTypeEthernet: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  536. return &types.VirtualMachineBootOptionsBootableEthernetDevice{
  537. DeviceKey: d.GetVirtualDevice().Key,
  538. }
  539. },
  540. DeviceTypeFloppy: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  541. return &types.VirtualMachineBootOptionsBootableFloppyDevice{}
  542. },
  543. }
  544. // BootOrder returns a list of devices which can be used to set boot order via VirtualMachine.SetBootOptions.
  545. // The order can any of "ethernet", "cdrom", "floppy" or "disk" or by specific device name.
  546. func (l VirtualDeviceList) BootOrder(order []string) []types.BaseVirtualMachineBootOptionsBootableDevice {
  547. var devices []types.BaseVirtualMachineBootOptionsBootableDevice
  548. for _, name := range order {
  549. if kind, ok := bootableDevices[name]; ok {
  550. for _, device := range l {
  551. if l.Type(device) == name {
  552. devices = append(devices, kind(device))
  553. }
  554. }
  555. continue
  556. }
  557. if d := l.Find(name); d != nil {
  558. if kind, ok := bootableDevices[l.Type(d)]; ok {
  559. devices = append(devices, kind(d))
  560. }
  561. }
  562. }
  563. return devices
  564. }
  565. // SelectBootOrder returns an ordered list of devices matching the given bootable device order
  566. func (l VirtualDeviceList) SelectBootOrder(order []types.BaseVirtualMachineBootOptionsBootableDevice) VirtualDeviceList {
  567. var devices VirtualDeviceList
  568. for _, bd := range order {
  569. for _, device := range l {
  570. if kind, ok := bootableDevices[l.Type(device)]; ok {
  571. if reflect.DeepEqual(kind(device), bd) {
  572. devices = append(devices, device)
  573. }
  574. }
  575. }
  576. }
  577. return devices
  578. }
  579. // TypeName returns the vmodl type name of the device
  580. func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string {
  581. return reflect.TypeOf(device).Elem().Name()
  582. }
  583. var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|Device|Controller)?$`)
  584. func (l VirtualDeviceList) deviceName(device types.BaseVirtualDevice) string {
  585. name := "device"
  586. typeName := l.TypeName(device)
  587. m := deviceNameRegexp.FindStringSubmatch(typeName)
  588. if len(m) == 2 {
  589. name = strings.ToLower(m[1])
  590. }
  591. return name
  592. }
  593. // Type returns a human-readable name for the given device
  594. func (l VirtualDeviceList) Type(device types.BaseVirtualDevice) string {
  595. switch device.(type) {
  596. case types.BaseVirtualEthernetCard:
  597. return DeviceTypeEthernet
  598. case *types.ParaVirtualSCSIController:
  599. return "pvscsi"
  600. case *types.VirtualLsiLogicSASController:
  601. return "lsilogic-sas"
  602. default:
  603. return l.deviceName(device)
  604. }
  605. }
  606. // Name returns a stable, human-readable name for the given device
  607. func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
  608. var key string
  609. d := device.GetVirtualDevice()
  610. dtype := l.Type(device)
  611. switch dtype {
  612. case DeviceTypeEthernet:
  613. key = fmt.Sprintf("%d", d.UnitNumber-7)
  614. case DeviceTypeDisk:
  615. key = fmt.Sprintf("%d-%d", d.ControllerKey, d.UnitNumber)
  616. default:
  617. key = fmt.Sprintf("%d", d.Key)
  618. }
  619. return fmt.Sprintf("%s-%s", dtype, key)
  620. }