PageRenderTime 1959ms CodeModel.GetById 21ms RepoModel.GetById 5ms app.codeStats 0ms

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

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