PageRenderTime 110ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://gitlab.com/unofficial-mirrors/kubernetes
Go | 935 lines | 693 code | 154 blank | 88 comment | 166 complexity | 9fe6ee9706f59970457acc6121ad7dcb MD5 | raw file
  1. /*
  2. Copyright (c) 2015-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 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. DeviceTypeNone = "-"
  27. DeviceTypeCdrom = "cdrom"
  28. DeviceTypeDisk = "disk"
  29. DeviceTypeEthernet = "ethernet"
  30. DeviceTypeFloppy = "floppy"
  31. )
  32. // VirtualDeviceList provides helper methods for working with a list of virtual devices.
  33. type VirtualDeviceList []types.BaseVirtualDevice
  34. // SCSIControllerTypes are used for adding a new SCSI controller to a VM.
  35. func SCSIControllerTypes() VirtualDeviceList {
  36. // Return a mutable list of SCSI controller types, initialized with defaults.
  37. return VirtualDeviceList([]types.BaseVirtualDevice{
  38. &types.VirtualLsiLogicController{},
  39. &types.VirtualBusLogicController{},
  40. &types.ParaVirtualSCSIController{},
  41. &types.VirtualLsiLogicSASController{},
  42. }).Select(func(device types.BaseVirtualDevice) bool {
  43. c := device.(types.BaseVirtualSCSIController).GetVirtualSCSIController()
  44. c.SharedBus = types.VirtualSCSISharingNoSharing
  45. c.BusNumber = -1
  46. return true
  47. })
  48. }
  49. // EthernetCardTypes are used for adding a new ethernet card to a VM.
  50. func EthernetCardTypes() VirtualDeviceList {
  51. return VirtualDeviceList([]types.BaseVirtualDevice{
  52. &types.VirtualE1000{},
  53. &types.VirtualE1000e{},
  54. &types.VirtualVmxnet2{},
  55. &types.VirtualVmxnet3{},
  56. &types.VirtualPCNet32{},
  57. &types.VirtualSriovEthernetCard{},
  58. }).Select(func(device types.BaseVirtualDevice) bool {
  59. c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
  60. c.GetVirtualDevice().Key = -1
  61. return true
  62. })
  63. }
  64. // Select returns a new list containing all elements of the list for which the given func returns true.
  65. func (l VirtualDeviceList) Select(f func(device types.BaseVirtualDevice) bool) VirtualDeviceList {
  66. var found VirtualDeviceList
  67. for _, device := range l {
  68. if f(device) {
  69. found = append(found, device)
  70. }
  71. }
  72. return found
  73. }
  74. // SelectByType returns a new list with devices that are equal to or extend the given type.
  75. func (l VirtualDeviceList) SelectByType(deviceType types.BaseVirtualDevice) VirtualDeviceList {
  76. dtype := reflect.TypeOf(deviceType)
  77. if dtype == nil {
  78. return nil
  79. }
  80. dname := dtype.Elem().Name()
  81. return l.Select(func(device types.BaseVirtualDevice) bool {
  82. t := reflect.TypeOf(device)
  83. if t == dtype {
  84. return true
  85. }
  86. _, ok := t.Elem().FieldByName(dname)
  87. return ok
  88. })
  89. }
  90. // SelectByBackingInfo returns a new list with devices matching the given backing info.
  91. // If the value of backing is nil, any device with a backing of the same type will be returned.
  92. func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBackingInfo) VirtualDeviceList {
  93. t := reflect.TypeOf(backing)
  94. return l.Select(func(device types.BaseVirtualDevice) bool {
  95. db := device.GetVirtualDevice().Backing
  96. if db == nil {
  97. return false
  98. }
  99. if reflect.TypeOf(db) != t {
  100. return false
  101. }
  102. if reflect.ValueOf(backing).IsNil() {
  103. // selecting by backing type
  104. return true
  105. }
  106. switch a := db.(type) {
  107. case *types.VirtualEthernetCardNetworkBackingInfo:
  108. b := backing.(*types.VirtualEthernetCardNetworkBackingInfo)
  109. return a.DeviceName == b.DeviceName
  110. case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
  111. b := backing.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo)
  112. return a.Port.SwitchUuid == b.Port.SwitchUuid &&
  113. a.Port.PortgroupKey == b.Port.PortgroupKey
  114. case *types.VirtualDiskFlatVer2BackingInfo:
  115. b := backing.(*types.VirtualDiskFlatVer2BackingInfo)
  116. if a.Parent != nil && b.Parent != nil {
  117. return a.Parent.FileName == b.Parent.FileName
  118. }
  119. return a.FileName == b.FileName
  120. case *types.VirtualSerialPortURIBackingInfo:
  121. b := backing.(*types.VirtualSerialPortURIBackingInfo)
  122. return a.ServiceURI == b.ServiceURI
  123. case types.BaseVirtualDeviceFileBackingInfo:
  124. b := backing.(types.BaseVirtualDeviceFileBackingInfo)
  125. return a.GetVirtualDeviceFileBackingInfo().FileName == b.GetVirtualDeviceFileBackingInfo().FileName
  126. default:
  127. return false
  128. }
  129. })
  130. }
  131. // Find returns the device matching the given name.
  132. func (l VirtualDeviceList) Find(name string) types.BaseVirtualDevice {
  133. for _, device := range l {
  134. if l.Name(device) == name {
  135. return device
  136. }
  137. }
  138. return nil
  139. }
  140. // FindByKey returns the device matching the given key.
  141. func (l VirtualDeviceList) FindByKey(key int32) types.BaseVirtualDevice {
  142. for _, device := range l {
  143. if device.GetVirtualDevice().Key == key {
  144. return device
  145. }
  146. }
  147. return nil
  148. }
  149. // FindIDEController will find the named IDE controller if given, otherwise will pick an available controller.
  150. // An error is returned if the named controller is not found or not an IDE controller. Or, if name is not
  151. // given and no available controller can be found.
  152. func (l VirtualDeviceList) FindIDEController(name string) (*types.VirtualIDEController, error) {
  153. if name != "" {
  154. d := l.Find(name)
  155. if d == nil {
  156. return nil, fmt.Errorf("device '%s' not found", name)
  157. }
  158. if c, ok := d.(*types.VirtualIDEController); ok {
  159. return c, nil
  160. }
  161. return nil, fmt.Errorf("%s is not an IDE controller", name)
  162. }
  163. c := l.PickController((*types.VirtualIDEController)(nil))
  164. if c == nil {
  165. return nil, errors.New("no available IDE controller")
  166. }
  167. return c.(*types.VirtualIDEController), nil
  168. }
  169. // CreateIDEController creates a new IDE controller.
  170. func (l VirtualDeviceList) CreateIDEController() (types.BaseVirtualDevice, error) {
  171. ide := &types.VirtualIDEController{}
  172. ide.Key = l.NewKey()
  173. return ide, nil
  174. }
  175. // FindSCSIController will find the named SCSI controller if given, otherwise will pick an available controller.
  176. // An error is returned if the named controller is not found or not an SCSI controller. Or, if name is not
  177. // given and no available controller can be found.
  178. func (l VirtualDeviceList) FindSCSIController(name string) (*types.VirtualSCSIController, error) {
  179. if name != "" {
  180. d := l.Find(name)
  181. if d == nil {
  182. return nil, fmt.Errorf("device '%s' not found", name)
  183. }
  184. if c, ok := d.(types.BaseVirtualSCSIController); ok {
  185. return c.GetVirtualSCSIController(), nil
  186. }
  187. return nil, fmt.Errorf("%s is not an SCSI controller", name)
  188. }
  189. c := l.PickController((*types.VirtualSCSIController)(nil))
  190. if c == nil {
  191. return nil, errors.New("no available SCSI controller")
  192. }
  193. return c.(types.BaseVirtualSCSIController).GetVirtualSCSIController(), nil
  194. }
  195. // CreateSCSIController creates a new SCSI controller of type name if given, otherwise defaults to lsilogic.
  196. func (l VirtualDeviceList) CreateSCSIController(name string) (types.BaseVirtualDevice, error) {
  197. ctypes := SCSIControllerTypes()
  198. if name == "scsi" || name == "" {
  199. name = ctypes.Type(ctypes[0])
  200. }
  201. found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
  202. return l.Type(device) == name
  203. })
  204. if len(found) == 0 {
  205. return nil, fmt.Errorf("unknown SCSI controller type '%s'", name)
  206. }
  207. c, ok := found[0].(types.BaseVirtualSCSIController)
  208. if !ok {
  209. return nil, fmt.Errorf("invalid SCSI controller type '%s'", name)
  210. }
  211. scsi := c.GetVirtualSCSIController()
  212. scsi.BusNumber = l.newSCSIBusNumber()
  213. scsi.Key = l.NewKey()
  214. scsi.ScsiCtlrUnitNumber = 7
  215. return c.(types.BaseVirtualDevice), nil
  216. }
  217. var scsiBusNumbers = []int{0, 1, 2, 3}
  218. // newSCSIBusNumber returns the bus number to use for adding a new SCSI bus device.
  219. // -1 is returned if there are no bus numbers available.
  220. func (l VirtualDeviceList) newSCSIBusNumber() int32 {
  221. var used []int
  222. for _, d := range l.SelectByType((*types.VirtualSCSIController)(nil)) {
  223. num := d.(types.BaseVirtualSCSIController).GetVirtualSCSIController().BusNumber
  224. if num >= 0 {
  225. used = append(used, int(num))
  226. } // else caller is creating a new vm using SCSIControllerTypes
  227. }
  228. sort.Ints(used)
  229. for i, n := range scsiBusNumbers {
  230. if i == len(used) || n != used[i] {
  231. return int32(n)
  232. }
  233. }
  234. return -1
  235. }
  236. // FindNVMEController will find the named NVME controller if given, otherwise will pick an available controller.
  237. // An error is returned if the named controller is not found or not an NVME controller. Or, if name is not
  238. // given and no available controller can be found.
  239. func (l VirtualDeviceList) FindNVMEController(name string) (*types.VirtualNVMEController, error) {
  240. if name != "" {
  241. d := l.Find(name)
  242. if d == nil {
  243. return nil, fmt.Errorf("device '%s' not found", name)
  244. }
  245. if c, ok := d.(*types.VirtualNVMEController); ok {
  246. return c, nil
  247. }
  248. return nil, fmt.Errorf("%s is not an NVME controller", name)
  249. }
  250. c := l.PickController((*types.VirtualNVMEController)(nil))
  251. if c == nil {
  252. return nil, errors.New("no available NVME controller")
  253. }
  254. return c.(*types.VirtualNVMEController), nil
  255. }
  256. // CreateNVMEController creates a new NVMWE controller.
  257. func (l VirtualDeviceList) CreateNVMEController() (types.BaseVirtualDevice, error) {
  258. nvme := &types.VirtualNVMEController{}
  259. nvme.BusNumber = l.newNVMEBusNumber()
  260. nvme.Key = l.NewKey()
  261. return nvme, nil
  262. }
  263. var nvmeBusNumbers = []int{0, 1, 2, 3}
  264. // newNVMEBusNumber returns the bus number to use for adding a new NVME bus device.
  265. // -1 is returned if there are no bus numbers available.
  266. func (l VirtualDeviceList) newNVMEBusNumber() int32 {
  267. var used []int
  268. for _, d := range l.SelectByType((*types.VirtualNVMEController)(nil)) {
  269. num := d.(types.BaseVirtualController).GetVirtualController().BusNumber
  270. if num >= 0 {
  271. used = append(used, int(num))
  272. } // else caller is creating a new vm using NVMEControllerTypes
  273. }
  274. sort.Ints(used)
  275. for i, n := range nvmeBusNumbers {
  276. if i == len(used) || n != used[i] {
  277. return int32(n)
  278. }
  279. }
  280. return -1
  281. }
  282. // FindDiskController will find an existing ide or scsi disk controller.
  283. func (l VirtualDeviceList) FindDiskController(name string) (types.BaseVirtualController, error) {
  284. switch {
  285. case name == "ide":
  286. return l.FindIDEController("")
  287. case name == "scsi" || name == "":
  288. return l.FindSCSIController("")
  289. case name == "nvme":
  290. return l.FindNVMEController("")
  291. default:
  292. if c, ok := l.Find(name).(types.BaseVirtualController); ok {
  293. return c, nil
  294. }
  295. return nil, fmt.Errorf("%s is not a valid controller", name)
  296. }
  297. }
  298. // PickController returns a controller of the given type(s).
  299. // If no controllers are found or have no available slots, then nil is returned.
  300. func (l VirtualDeviceList) PickController(kind types.BaseVirtualController) types.BaseVirtualController {
  301. l = l.SelectByType(kind.(types.BaseVirtualDevice)).Select(func(device types.BaseVirtualDevice) bool {
  302. num := len(device.(types.BaseVirtualController).GetVirtualController().Device)
  303. switch device.(type) {
  304. case types.BaseVirtualSCSIController:
  305. return num < 15
  306. case *types.VirtualIDEController:
  307. return num < 2
  308. case *types.VirtualNVMEController:
  309. return num < 8
  310. default:
  311. return true
  312. }
  313. })
  314. if len(l) == 0 {
  315. return nil
  316. }
  317. return l[0].(types.BaseVirtualController)
  318. }
  319. // newUnitNumber returns the unit number to use for attaching a new device to the given controller.
  320. func (l VirtualDeviceList) newUnitNumber(c types.BaseVirtualController) int32 {
  321. units := make([]bool, 30)
  322. switch sc := c.(type) {
  323. case types.BaseVirtualSCSIController:
  324. // The SCSI controller sits on its own bus
  325. units[sc.GetVirtualSCSIController().ScsiCtlrUnitNumber] = true
  326. }
  327. key := c.GetVirtualController().Key
  328. for _, device := range l {
  329. d := device.GetVirtualDevice()
  330. if d.ControllerKey == key && d.UnitNumber != nil {
  331. units[int(*d.UnitNumber)] = true
  332. }
  333. }
  334. for unit, used := range units {
  335. if !used {
  336. return int32(unit)
  337. }
  338. }
  339. return -1
  340. }
  341. // NewKey returns the key to use for adding a new device to the device list.
  342. // The device list we're working with here may not be complete (e.g. when
  343. // we're only adding new devices), so any positive keys could conflict with device keys
  344. // that are already in use. To avoid this type of conflict, we can use negative keys
  345. // here, which will be resolved to positive keys by vSphere as the reconfiguration is done.
  346. func (l VirtualDeviceList) NewKey() int32 {
  347. var key int32 = -200
  348. for _, device := range l {
  349. d := device.GetVirtualDevice()
  350. if d.Key < key {
  351. key = d.Key
  352. }
  353. }
  354. return key - 1
  355. }
  356. // AssignController assigns a device to a controller.
  357. func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c types.BaseVirtualController) {
  358. d := device.GetVirtualDevice()
  359. d.ControllerKey = c.GetVirtualController().Key
  360. d.UnitNumber = new(int32)
  361. *d.UnitNumber = l.newUnitNumber(c)
  362. if d.Key == 0 {
  363. d.Key = -1
  364. }
  365. }
  366. // CreateDisk creates a new VirtualDisk device which can be added to a VM.
  367. func (l VirtualDeviceList) CreateDisk(c types.BaseVirtualController, ds types.ManagedObjectReference, name string) *types.VirtualDisk {
  368. // If name is not specified, one will be chosen for you.
  369. // But if when given, make sure it ends in .vmdk, otherwise it will be treated as a directory.
  370. if len(name) > 0 && filepath.Ext(name) != ".vmdk" {
  371. name += ".vmdk"
  372. }
  373. device := &types.VirtualDisk{
  374. VirtualDevice: types.VirtualDevice{
  375. Backing: &types.VirtualDiskFlatVer2BackingInfo{
  376. DiskMode: string(types.VirtualDiskModePersistent),
  377. ThinProvisioned: types.NewBool(true),
  378. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  379. FileName: name,
  380. Datastore: &ds,
  381. },
  382. },
  383. },
  384. }
  385. l.AssignController(device, c)
  386. return device
  387. }
  388. // ChildDisk creates a new VirtualDisk device, linked to the given parent disk, which can be added to a VM.
  389. func (l VirtualDeviceList) ChildDisk(parent *types.VirtualDisk) *types.VirtualDisk {
  390. disk := *parent
  391. backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
  392. p := new(DatastorePath)
  393. p.FromString(backing.FileName)
  394. p.Path = ""
  395. // Use specified disk as parent backing to a new disk.
  396. disk.Backing = &types.VirtualDiskFlatVer2BackingInfo{
  397. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  398. FileName: p.String(),
  399. Datastore: backing.Datastore,
  400. },
  401. Parent: backing,
  402. DiskMode: backing.DiskMode,
  403. ThinProvisioned: backing.ThinProvisioned,
  404. }
  405. return &disk
  406. }
  407. func (l VirtualDeviceList) connectivity(device types.BaseVirtualDevice, v bool) error {
  408. c := device.GetVirtualDevice().Connectable
  409. if c == nil {
  410. return fmt.Errorf("%s is not connectable", l.Name(device))
  411. }
  412. c.Connected = v
  413. c.StartConnected = v
  414. return nil
  415. }
  416. // Connect changes the device to connected, returns an error if the device is not connectable.
  417. func (l VirtualDeviceList) Connect(device types.BaseVirtualDevice) error {
  418. return l.connectivity(device, true)
  419. }
  420. // Disconnect changes the device to disconnected, returns an error if the device is not connectable.
  421. func (l VirtualDeviceList) Disconnect(device types.BaseVirtualDevice) error {
  422. return l.connectivity(device, false)
  423. }
  424. // FindCdrom finds a cdrom device with the given name, defaulting to the first cdrom device if any.
  425. func (l VirtualDeviceList) FindCdrom(name string) (*types.VirtualCdrom, error) {
  426. if name != "" {
  427. d := l.Find(name)
  428. if d == nil {
  429. return nil, fmt.Errorf("device '%s' not found", name)
  430. }
  431. if c, ok := d.(*types.VirtualCdrom); ok {
  432. return c, nil
  433. }
  434. return nil, fmt.Errorf("%s is not a cdrom device", name)
  435. }
  436. c := l.SelectByType((*types.VirtualCdrom)(nil))
  437. if len(c) == 0 {
  438. return nil, errors.New("no cdrom device found")
  439. }
  440. return c[0].(*types.VirtualCdrom), nil
  441. }
  442. // CreateCdrom creates a new VirtualCdrom device which can be added to a VM.
  443. func (l VirtualDeviceList) CreateCdrom(c *types.VirtualIDEController) (*types.VirtualCdrom, error) {
  444. device := &types.VirtualCdrom{}
  445. l.AssignController(device, c)
  446. l.setDefaultCdromBacking(device)
  447. device.Connectable = &types.VirtualDeviceConnectInfo{
  448. AllowGuestControl: true,
  449. Connected: true,
  450. StartConnected: true,
  451. }
  452. return device, nil
  453. }
  454. // InsertIso changes the cdrom device backing to use the given iso file.
  455. func (l VirtualDeviceList) InsertIso(device *types.VirtualCdrom, iso string) *types.VirtualCdrom {
  456. device.Backing = &types.VirtualCdromIsoBackingInfo{
  457. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  458. FileName: iso,
  459. },
  460. }
  461. return device
  462. }
  463. // EjectIso removes the iso file based backing and replaces with the default cdrom backing.
  464. func (l VirtualDeviceList) EjectIso(device *types.VirtualCdrom) *types.VirtualCdrom {
  465. l.setDefaultCdromBacking(device)
  466. return device
  467. }
  468. func (l VirtualDeviceList) setDefaultCdromBacking(device *types.VirtualCdrom) {
  469. device.Backing = &types.VirtualCdromAtapiBackingInfo{
  470. VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
  471. DeviceName: fmt.Sprintf("%s-%d-%d", DeviceTypeCdrom, device.ControllerKey, device.UnitNumber),
  472. UseAutoDetect: types.NewBool(false),
  473. },
  474. }
  475. }
  476. // FindFloppy finds a floppy device with the given name, defaulting to the first floppy device if any.
  477. func (l VirtualDeviceList) FindFloppy(name string) (*types.VirtualFloppy, error) {
  478. if name != "" {
  479. d := l.Find(name)
  480. if d == nil {
  481. return nil, fmt.Errorf("device '%s' not found", name)
  482. }
  483. if c, ok := d.(*types.VirtualFloppy); ok {
  484. return c, nil
  485. }
  486. return nil, fmt.Errorf("%s is not a floppy device", name)
  487. }
  488. c := l.SelectByType((*types.VirtualFloppy)(nil))
  489. if len(c) == 0 {
  490. return nil, errors.New("no floppy device found")
  491. }
  492. return c[0].(*types.VirtualFloppy), nil
  493. }
  494. // CreateFloppy creates a new VirtualFloppy device which can be added to a VM.
  495. func (l VirtualDeviceList) CreateFloppy() (*types.VirtualFloppy, error) {
  496. device := &types.VirtualFloppy{}
  497. c := l.PickController((*types.VirtualSIOController)(nil))
  498. if c == nil {
  499. return nil, errors.New("no available SIO controller")
  500. }
  501. l.AssignController(device, c)
  502. l.setDefaultFloppyBacking(device)
  503. device.Connectable = &types.VirtualDeviceConnectInfo{
  504. AllowGuestControl: true,
  505. Connected: true,
  506. StartConnected: true,
  507. }
  508. return device, nil
  509. }
  510. // InsertImg changes the floppy device backing to use the given img file.
  511. func (l VirtualDeviceList) InsertImg(device *types.VirtualFloppy, img string) *types.VirtualFloppy {
  512. device.Backing = &types.VirtualFloppyImageBackingInfo{
  513. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  514. FileName: img,
  515. },
  516. }
  517. return device
  518. }
  519. // EjectImg removes the img file based backing and replaces with the default floppy backing.
  520. func (l VirtualDeviceList) EjectImg(device *types.VirtualFloppy) *types.VirtualFloppy {
  521. l.setDefaultFloppyBacking(device)
  522. return device
  523. }
  524. func (l VirtualDeviceList) setDefaultFloppyBacking(device *types.VirtualFloppy) {
  525. device.Backing = &types.VirtualFloppyDeviceBackingInfo{
  526. VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
  527. DeviceName: fmt.Sprintf("%s-%d", DeviceTypeFloppy, device.UnitNumber),
  528. UseAutoDetect: types.NewBool(false),
  529. },
  530. }
  531. }
  532. // FindSerialPort finds a serial port device with the given name, defaulting to the first serial port device if any.
  533. func (l VirtualDeviceList) FindSerialPort(name string) (*types.VirtualSerialPort, error) {
  534. if name != "" {
  535. d := l.Find(name)
  536. if d == nil {
  537. return nil, fmt.Errorf("device '%s' not found", name)
  538. }
  539. if c, ok := d.(*types.VirtualSerialPort); ok {
  540. return c, nil
  541. }
  542. return nil, fmt.Errorf("%s is not a serial port device", name)
  543. }
  544. c := l.SelectByType((*types.VirtualSerialPort)(nil))
  545. if len(c) == 0 {
  546. return nil, errors.New("no serial port device found")
  547. }
  548. return c[0].(*types.VirtualSerialPort), nil
  549. }
  550. // CreateSerialPort creates a new VirtualSerialPort device which can be added to a VM.
  551. func (l VirtualDeviceList) CreateSerialPort() (*types.VirtualSerialPort, error) {
  552. device := &types.VirtualSerialPort{
  553. YieldOnPoll: true,
  554. }
  555. c := l.PickController((*types.VirtualSIOController)(nil))
  556. if c == nil {
  557. return nil, errors.New("no available SIO controller")
  558. }
  559. l.AssignController(device, c)
  560. l.setDefaultSerialPortBacking(device)
  561. return device, nil
  562. }
  563. // ConnectSerialPort connects a serial port to a server or client uri.
  564. func (l VirtualDeviceList) ConnectSerialPort(device *types.VirtualSerialPort, uri string, client bool, proxyuri string) *types.VirtualSerialPort {
  565. if strings.HasPrefix(uri, "[") {
  566. device.Backing = &types.VirtualSerialPortFileBackingInfo{
  567. VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
  568. FileName: uri,
  569. },
  570. }
  571. return device
  572. }
  573. direction := types.VirtualDeviceURIBackingOptionDirectionServer
  574. if client {
  575. direction = types.VirtualDeviceURIBackingOptionDirectionClient
  576. }
  577. device.Backing = &types.VirtualSerialPortURIBackingInfo{
  578. VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
  579. Direction: string(direction),
  580. ServiceURI: uri,
  581. ProxyURI: proxyuri,
  582. },
  583. }
  584. return device
  585. }
  586. // DisconnectSerialPort disconnects the serial port backing.
  587. func (l VirtualDeviceList) DisconnectSerialPort(device *types.VirtualSerialPort) *types.VirtualSerialPort {
  588. l.setDefaultSerialPortBacking(device)
  589. return device
  590. }
  591. func (l VirtualDeviceList) setDefaultSerialPortBacking(device *types.VirtualSerialPort) {
  592. device.Backing = &types.VirtualSerialPortURIBackingInfo{
  593. VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
  594. Direction: "client",
  595. ServiceURI: "localhost:0",
  596. },
  597. }
  598. }
  599. // CreateEthernetCard creates a new VirtualEthernetCard of the given name name and initialized with the given backing.
  600. func (l VirtualDeviceList) CreateEthernetCard(name string, backing types.BaseVirtualDeviceBackingInfo) (types.BaseVirtualDevice, error) {
  601. ctypes := EthernetCardTypes()
  602. if name == "" {
  603. name = ctypes.deviceName(ctypes[0])
  604. }
  605. found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
  606. return l.deviceName(device) == name
  607. })
  608. if len(found) == 0 {
  609. return nil, fmt.Errorf("unknown ethernet card type '%s'", name)
  610. }
  611. c, ok := found[0].(types.BaseVirtualEthernetCard)
  612. if !ok {
  613. return nil, fmt.Errorf("invalid ethernet card type '%s'", name)
  614. }
  615. c.GetVirtualEthernetCard().Backing = backing
  616. return c.(types.BaseVirtualDevice), nil
  617. }
  618. // PrimaryMacAddress returns the MacAddress field of the primary VirtualEthernetCard
  619. func (l VirtualDeviceList) PrimaryMacAddress() string {
  620. eth0 := l.Find("ethernet-0")
  621. if eth0 == nil {
  622. return ""
  623. }
  624. return eth0.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard().MacAddress
  625. }
  626. // convert a BaseVirtualDevice to a BaseVirtualMachineBootOptionsBootableDevice
  627. var bootableDevices = map[string]func(device types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice{
  628. DeviceTypeNone: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  629. return &types.VirtualMachineBootOptionsBootableDevice{}
  630. },
  631. DeviceTypeCdrom: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  632. return &types.VirtualMachineBootOptionsBootableCdromDevice{}
  633. },
  634. DeviceTypeDisk: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  635. return &types.VirtualMachineBootOptionsBootableDiskDevice{
  636. DeviceKey: d.GetVirtualDevice().Key,
  637. }
  638. },
  639. DeviceTypeEthernet: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  640. return &types.VirtualMachineBootOptionsBootableEthernetDevice{
  641. DeviceKey: d.GetVirtualDevice().Key,
  642. }
  643. },
  644. DeviceTypeFloppy: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
  645. return &types.VirtualMachineBootOptionsBootableFloppyDevice{}
  646. },
  647. }
  648. // BootOrder returns a list of devices which can be used to set boot order via VirtualMachine.SetBootOptions.
  649. // The order can be any of "ethernet", "cdrom", "floppy" or "disk" or by specific device name.
  650. // A value of "-" will clear the existing boot order on the VC/ESX side.
  651. func (l VirtualDeviceList) BootOrder(order []string) []types.BaseVirtualMachineBootOptionsBootableDevice {
  652. var devices []types.BaseVirtualMachineBootOptionsBootableDevice
  653. for _, name := range order {
  654. if kind, ok := bootableDevices[name]; ok {
  655. if name == DeviceTypeNone {
  656. // Not covered in the API docs, nor obvious, but this clears the boot order on the VC/ESX side.
  657. devices = append(devices, new(types.VirtualMachineBootOptionsBootableDevice))
  658. continue
  659. }
  660. for _, device := range l {
  661. if l.Type(device) == name {
  662. devices = append(devices, kind(device))
  663. }
  664. }
  665. continue
  666. }
  667. if d := l.Find(name); d != nil {
  668. if kind, ok := bootableDevices[l.Type(d)]; ok {
  669. devices = append(devices, kind(d))
  670. }
  671. }
  672. }
  673. return devices
  674. }
  675. // SelectBootOrder returns an ordered list of devices matching the given bootable device order
  676. func (l VirtualDeviceList) SelectBootOrder(order []types.BaseVirtualMachineBootOptionsBootableDevice) VirtualDeviceList {
  677. var devices VirtualDeviceList
  678. for _, bd := range order {
  679. for _, device := range l {
  680. if kind, ok := bootableDevices[l.Type(device)]; ok {
  681. if reflect.DeepEqual(kind(device), bd) {
  682. devices = append(devices, device)
  683. }
  684. }
  685. }
  686. }
  687. return devices
  688. }
  689. // TypeName returns the vmodl type name of the device
  690. func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string {
  691. dtype := reflect.TypeOf(device)
  692. if dtype == nil {
  693. return ""
  694. }
  695. return dtype.Elem().Name()
  696. }
  697. var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|EthernetCard|Device|Controller)?$`)
  698. func (l VirtualDeviceList) deviceName(device types.BaseVirtualDevice) string {
  699. name := "device"
  700. typeName := l.TypeName(device)
  701. m := deviceNameRegexp.FindStringSubmatch(typeName)
  702. if len(m) == 2 {
  703. name = strings.ToLower(m[1])
  704. }
  705. return name
  706. }
  707. // Type returns a human-readable name for the given device
  708. func (l VirtualDeviceList) Type(device types.BaseVirtualDevice) string {
  709. switch device.(type) {
  710. case types.BaseVirtualEthernetCard:
  711. return DeviceTypeEthernet
  712. case *types.ParaVirtualSCSIController:
  713. return "pvscsi"
  714. case *types.VirtualLsiLogicSASController:
  715. return "lsilogic-sas"
  716. case *types.VirtualNVMEController:
  717. return "nvme"
  718. default:
  719. return l.deviceName(device)
  720. }
  721. }
  722. // Name returns a stable, human-readable name for the given device
  723. func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
  724. var key string
  725. var UnitNumber int32
  726. d := device.GetVirtualDevice()
  727. if d.UnitNumber != nil {
  728. UnitNumber = *d.UnitNumber
  729. }
  730. dtype := l.Type(device)
  731. switch dtype {
  732. case DeviceTypeEthernet:
  733. key = fmt.Sprintf("%d", UnitNumber-7)
  734. case DeviceTypeDisk:
  735. key = fmt.Sprintf("%d-%d", d.ControllerKey, UnitNumber)
  736. default:
  737. key = fmt.Sprintf("%d", d.Key)
  738. }
  739. return fmt.Sprintf("%s-%s", dtype, key)
  740. }
  741. // ConfigSpec creates a virtual machine configuration spec for
  742. // the specified operation, for the list of devices in the device list.
  743. func (l VirtualDeviceList) ConfigSpec(op types.VirtualDeviceConfigSpecOperation) ([]types.BaseVirtualDeviceConfigSpec, error) {
  744. var fop types.VirtualDeviceConfigSpecFileOperation
  745. switch op {
  746. case types.VirtualDeviceConfigSpecOperationAdd:
  747. fop = types.VirtualDeviceConfigSpecFileOperationCreate
  748. case types.VirtualDeviceConfigSpecOperationEdit:
  749. fop = types.VirtualDeviceConfigSpecFileOperationReplace
  750. case types.VirtualDeviceConfigSpecOperationRemove:
  751. fop = types.VirtualDeviceConfigSpecFileOperationDestroy
  752. default:
  753. panic("unknown op")
  754. }
  755. var res []types.BaseVirtualDeviceConfigSpec
  756. for _, device := range l {
  757. config := &types.VirtualDeviceConfigSpec{
  758. Device: device,
  759. Operation: op,
  760. }
  761. if disk, ok := device.(*types.VirtualDisk); ok {
  762. config.FileOperation = fop
  763. // Special case to attach an existing disk
  764. if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
  765. childDisk := false
  766. if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
  767. childDisk = b.Parent != nil
  768. }
  769. if !childDisk {
  770. // Existing disk, clear file operation
  771. config.FileOperation = ""
  772. }
  773. }
  774. }
  775. res = append(res, config)
  776. }
  777. return res, nil
  778. }