2
Copyright (c) 2014 VMware, Inc. All Rights Reserved.
4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
8
http://www.apache.org/licenses/LICENSE-2.0
10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
28
"github.com/juju/govmomi/vim25/types"
31
// Type values for use in BootOrder
33
DeviceTypeCdrom = "cdrom"
34
DeviceTypeDisk = "disk"
35
DeviceTypeEthernet = "ethernet"
36
DeviceTypeFloppy = "floppy"
39
// VirtualDeviceList provides helper methods for working with a list of virtual devices.
40
type VirtualDeviceList []types.BaseVirtualDevice
42
// SCSIControllerTypes are used for adding a new SCSI controller to a VM.
43
func SCSIControllerTypes() VirtualDeviceList {
44
// Return a mutable list of SCSI controller types, initialized with defaults.
45
return VirtualDeviceList([]types.BaseVirtualDevice{
46
&types.VirtualLsiLogicController{},
47
&types.VirtualBusLogicController{},
48
&types.ParaVirtualSCSIController{},
49
&types.VirtualLsiLogicSASController{},
50
}).Select(func(device types.BaseVirtualDevice) bool {
51
c := device.(types.BaseVirtualSCSIController).GetVirtualSCSIController()
52
c.SharedBus = types.VirtualSCSISharingNoSharing
58
// EthernetCardTypes are used for adding a new ethernet card to a VM.
59
func EthernetCardTypes() VirtualDeviceList {
60
return VirtualDeviceList([]types.BaseVirtualDevice{
61
&types.VirtualE1000{},
62
&types.VirtualE1000e{},
63
&types.VirtualVmxnet3{},
64
}).Select(func(device types.BaseVirtualDevice) bool {
65
c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
66
c.AddressType = string(types.VirtualEthernetCardMacTypeGenerated)
67
c.GetVirtualDevice().Key = -1
72
// Select returns a new list containing all elements of the list for which the given func returns true.
73
func (l VirtualDeviceList) Select(f func(device types.BaseVirtualDevice) bool) VirtualDeviceList {
74
var found VirtualDeviceList
76
for _, device := range l {
78
found = append(found, device)
85
// SelectByType returns a new list with devices that are equal to or extend the given type.
86
func (l VirtualDeviceList) SelectByType(deviceType types.BaseVirtualDevice) VirtualDeviceList {
87
dtype := reflect.TypeOf(deviceType)
88
dname := dtype.Elem().Name()
90
return l.Select(func(device types.BaseVirtualDevice) bool {
91
t := reflect.TypeOf(device)
97
_, ok := t.Elem().FieldByName(dname)
103
// SelectByBackingInfo returns a new list with devices matching the given backing info.
104
func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBackingInfo) VirtualDeviceList {
105
t := reflect.TypeOf(backing)
107
return l.Select(func(device types.BaseVirtualDevice) bool {
108
db := device.GetVirtualDevice().Backing
113
if reflect.TypeOf(db) != t {
117
switch a := db.(type) {
118
case *types.VirtualEthernetCardNetworkBackingInfo:
119
b := backing.(*types.VirtualEthernetCardNetworkBackingInfo)
120
return a.DeviceName == b.DeviceName
121
case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
122
b := backing.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo)
123
return a.Port.SwitchUuid == b.Port.SwitchUuid
124
case *types.VirtualDiskFlatVer2BackingInfo:
125
b := backing.(*types.VirtualDiskFlatVer2BackingInfo)
126
if a.Parent != nil && b.Parent != nil {
127
return a.Parent.FileName == b.Parent.FileName
129
return a.FileName == b.FileName
130
case types.BaseVirtualDeviceFileBackingInfo:
131
b := backing.(types.BaseVirtualDeviceFileBackingInfo)
132
return a.GetVirtualDeviceFileBackingInfo().FileName == b.GetVirtualDeviceFileBackingInfo().FileName
139
// Find returns the device matching the given name.
140
func (l VirtualDeviceList) Find(name string) types.BaseVirtualDevice {
141
for _, device := range l {
142
if l.Name(device) == name {
149
// FindByKey returns the device matching the given key.
150
func (l VirtualDeviceList) FindByKey(key int) types.BaseVirtualDevice {
151
for _, device := range l {
152
if device.GetVirtualDevice().Key == key {
159
// FindIDEController will find the named IDE controller if given, otherwise will pick an available controller.
160
// An error is returned if the named controller is not found or not an IDE controller. Or, if name is not
161
// given and no available controller can be found.
162
func (l VirtualDeviceList) FindIDEController(name string) (*types.VirtualIDEController, error) {
166
return nil, fmt.Errorf("device '%s' not found", name)
168
if c, ok := d.(*types.VirtualIDEController); ok {
171
return nil, fmt.Errorf("%s is not an IDE controller", name)
174
c := l.PickController((*types.VirtualIDEController)(nil))
176
return nil, errors.New("no available IDE controller")
179
return c.(*types.VirtualIDEController), nil
182
// FindSCSIController will find the named SCSI controller if given, otherwise will pick an available controller.
183
// An error is returned if the named controller is not found or not an SCSI controller. Or, if name is not
184
// given and no available controller can be found.
185
func (l VirtualDeviceList) FindSCSIController(name string) (*types.VirtualSCSIController, error) {
189
return nil, fmt.Errorf("device '%s' not found", name)
191
if c, ok := d.(types.BaseVirtualSCSIController); ok {
192
return c.GetVirtualSCSIController(), nil
194
return nil, fmt.Errorf("%s is not an SCSI controller", name)
197
c := l.PickController((*types.VirtualSCSIController)(nil))
199
return nil, errors.New("no available SCSI controller")
202
return c.(types.BaseVirtualSCSIController).GetVirtualSCSIController(), nil
205
// CreateSCSIController creates a new SCSI controller of type name if given, otherwise defaults to lsilogic.
206
func (l VirtualDeviceList) CreateSCSIController(name string) (types.BaseVirtualDevice, error) {
207
ctypes := SCSIControllerTypes()
209
if name == "scsi" || name == "" {
210
name = ctypes.Type(ctypes[0])
213
found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
214
return l.Type(device) == name
218
return nil, fmt.Errorf("unknown SCSI controller type '%s'", name)
221
c, ok := found[0].(types.BaseVirtualSCSIController)
223
return nil, fmt.Errorf("invalid SCSI controller type '%s'", name)
226
scsi := c.GetVirtualSCSIController()
228
scsi.BusNumber = l.newSCSIBusNumber()
230
return c.(types.BaseVirtualDevice), nil
233
var scsiBusNumbers = []int{0, 1, 2, 3}
235
// newSCSIBusNumber returns the bus number to use for adding a new SCSI bus device.
236
// -1 is returned if there are no bus numbers available.
237
func (l VirtualDeviceList) newSCSIBusNumber() int {
240
for _, d := range l.SelectByType((*types.VirtualSCSIController)(nil)) {
241
num := d.(types.BaseVirtualSCSIController).GetVirtualSCSIController().BusNumber
243
used = append(used, num)
244
} // else caller is creating a new vm using SCSIControllerTypes
249
for i, n := range scsiBusNumbers {
250
if i == len(used) || n != used[i] {
258
// FindDiskController will find an existing ide or scsi disk controller.
259
func (l VirtualDeviceList) FindDiskController(name string) (types.BaseVirtualController, error) {
262
return l.FindIDEController("")
263
case name == "scsi" || name == "":
264
return l.FindSCSIController("")
266
if c, ok := l.Find(name).(types.BaseVirtualController); ok {
269
return nil, fmt.Errorf("%s is not a valid controller", name)
273
// PickController returns a controller of the given type(s).
274
// If no controllers are found or have no available slots, then nil is returned.
275
func (l VirtualDeviceList) PickController(kind types.BaseVirtualController) types.BaseVirtualController {
276
l = l.SelectByType(kind.(types.BaseVirtualDevice)).Select(func(device types.BaseVirtualDevice) bool {
277
num := len(device.(types.BaseVirtualController).GetVirtualController().Device)
279
switch device.(type) {
280
case types.BaseVirtualSCSIController:
282
case *types.VirtualIDEController:
293
return l[0].(types.BaseVirtualController)
296
// newUnitNumber returns the unit number to use for attaching a new device to the given controller.
297
func (l VirtualDeviceList) newUnitNumber(c types.BaseVirtualController) int {
298
key := c.GetVirtualController().Key
301
for _, device := range l {
302
d := device.GetVirtualDevice()
304
if d.ControllerKey == key {
305
if d.UnitNumber > max {
314
// AssignController assigns a device to a controller.
315
func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c types.BaseVirtualController) {
316
d := device.GetVirtualDevice()
317
d.ControllerKey = c.GetVirtualController().Key
318
d.UnitNumber = l.newUnitNumber(c)
322
// CreateDisk creates a new VirtualDisk device which can be added to a VM.
323
func (l VirtualDeviceList) CreateDisk(c types.BaseVirtualController, name string) *types.VirtualDisk {
324
// If name is not specified, one will be chosen for you.
325
// But if when given, make sure it ends in .vmdk, otherwise it will be treated as a directory.
326
if len(name) > 0 && filepath.Ext(name) != ".vmdk" {
330
device := &types.VirtualDisk{
331
VirtualDevice: types.VirtualDevice{
332
Backing: &types.VirtualDiskFlatVer2BackingInfo{
333
DiskMode: string(types.VirtualDiskModePersistent),
334
ThinProvisioned: types.NewBool(true),
335
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
342
l.AssignController(device, c)
344
if device.UnitNumber == 0 {
345
device.UnitNumber = -1 // TODO: this field is annotated as omitempty
351
// ChildDisk creates a new VirtualDisk device, linked to the given parent disk, which can be added to a VM.
352
func (l VirtualDeviceList) ChildDisk(parent *types.VirtualDisk) *types.VirtualDisk {
354
backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
355
ds := strings.SplitN(backing.FileName[1:], "]", 2)
357
// Use specified disk as parent backing to a new disk.
358
disk.Backing = &types.VirtualDiskFlatVer2BackingInfo{
359
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
360
FileName: fmt.Sprintf("[%s]", ds[0]),
363
DiskMode: backing.DiskMode,
364
ThinProvisioned: backing.ThinProvisioned,
370
func (l VirtualDeviceList) connectivity(device types.BaseVirtualDevice, v bool) error {
371
c := device.GetVirtualDevice().Connectable
373
return fmt.Errorf("%s is not connectable", l.Name(device))
382
// Connect changes the device to connected, returns an error if the device is not connectable.
383
func (l VirtualDeviceList) Connect(device types.BaseVirtualDevice) error {
384
return l.connectivity(device, true)
387
// Disconnect changes the device to disconnected, returns an error if the device is not connectable.
388
func (l VirtualDeviceList) Disconnect(device types.BaseVirtualDevice) error {
389
return l.connectivity(device, false)
392
// FindCdrom finds a cdrom device with the given name, defaulting to the first cdrom device if any.
393
func (l VirtualDeviceList) FindCdrom(name string) (*types.VirtualCdrom, error) {
397
return nil, fmt.Errorf("device '%s' not found", name)
399
if c, ok := d.(*types.VirtualCdrom); ok {
402
return nil, fmt.Errorf("%s is not a cdrom device", name)
405
c := l.SelectByType((*types.VirtualCdrom)(nil))
407
return nil, errors.New("no cdrom device found")
410
return c[0].(*types.VirtualCdrom), nil
413
// CreateCdrom creates a new VirtualCdrom device which can be added to a VM.
414
func (l VirtualDeviceList) CreateCdrom(c *types.VirtualIDEController) (*types.VirtualCdrom, error) {
415
device := &types.VirtualCdrom{}
417
l.AssignController(device, c)
419
l.setDefaultCdromBacking(device)
421
device.Connectable = &types.VirtualDeviceConnectInfo{
422
AllowGuestControl: true,
424
StartConnected: true,
430
// InsertIso changes the cdrom device backing to use the given iso file.
431
func (l VirtualDeviceList) InsertIso(device *types.VirtualCdrom, iso string) *types.VirtualCdrom {
432
device.Backing = &types.VirtualCdromIsoBackingInfo{
433
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
441
// EjectIso removes the iso file based backing and replaces with the default cdrom backing.
442
func (l VirtualDeviceList) EjectIso(device *types.VirtualCdrom) *types.VirtualCdrom {
443
l.setDefaultCdromBacking(device)
447
func (l VirtualDeviceList) setDefaultCdromBacking(device *types.VirtualCdrom) {
448
device.Backing = &types.VirtualCdromAtapiBackingInfo{
449
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
450
DeviceName: fmt.Sprintf("%s-%d-%d", DeviceTypeCdrom, device.ControllerKey, device.UnitNumber),
451
UseAutoDetect: types.NewBool(false),
456
// FindFloppy finds a floppy device with the given name, defaulting to the first floppy device if any.
457
func (l VirtualDeviceList) FindFloppy(name string) (*types.VirtualFloppy, error) {
461
return nil, fmt.Errorf("device '%s' not found", name)
463
if c, ok := d.(*types.VirtualFloppy); ok {
466
return nil, fmt.Errorf("%s is not a floppy device", name)
469
c := l.SelectByType((*types.VirtualFloppy)(nil))
471
return nil, errors.New("no floppy device found")
474
return c[0].(*types.VirtualFloppy), nil
477
// CreateFloppy creates a new VirtualFloppy device which can be added to a VM.
478
func (l VirtualDeviceList) CreateFloppy() (*types.VirtualFloppy, error) {
479
device := &types.VirtualFloppy{}
481
c := l.PickController((*types.VirtualSIOController)(nil))
483
return nil, errors.New("no available SIO controller")
486
l.AssignController(device, c)
488
l.setDefaultFloppyBacking(device)
490
device.Connectable = &types.VirtualDeviceConnectInfo{
491
AllowGuestControl: true,
493
StartConnected: true,
499
// InsertImg changes the floppy device backing to use the given img file.
500
func (l VirtualDeviceList) InsertImg(device *types.VirtualFloppy, img string) *types.VirtualFloppy {
501
device.Backing = &types.VirtualFloppyImageBackingInfo{
502
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
510
// EjectImg removes the img file based backing and replaces with the default floppy backing.
511
func (l VirtualDeviceList) EjectImg(device *types.VirtualFloppy) *types.VirtualFloppy {
512
l.setDefaultFloppyBacking(device)
516
func (l VirtualDeviceList) setDefaultFloppyBacking(device *types.VirtualFloppy) {
517
device.Backing = &types.VirtualFloppyDeviceBackingInfo{
518
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
519
DeviceName: fmt.Sprintf("%s-%d", DeviceTypeFloppy, device.UnitNumber),
520
UseAutoDetect: types.NewBool(false),
525
// FindSerialPort finds a serial port device with the given name, defaulting to the first serial port device if any.
526
func (l VirtualDeviceList) FindSerialPort(name string) (*types.VirtualSerialPort, error) {
530
return nil, fmt.Errorf("device '%s' not found", name)
532
if c, ok := d.(*types.VirtualSerialPort); ok {
535
return nil, fmt.Errorf("%s is not a serial port device", name)
538
c := l.SelectByType((*types.VirtualSerialPort)(nil))
540
return nil, errors.New("no serial port device found")
543
return c[0].(*types.VirtualSerialPort), nil
546
// CreateSerialPort creates a new VirtualSerialPort device which can be added to a VM.
547
func (l VirtualDeviceList) CreateSerialPort() (*types.VirtualSerialPort, error) {
548
device := &types.VirtualSerialPort{
552
c := l.PickController((*types.VirtualSIOController)(nil))
554
return nil, errors.New("no available SIO controller")
557
l.AssignController(device, c)
559
l.setDefaultSerialPortBacking(device)
564
// ConnectSerialPort connects a serial port to a server or client uri.
565
func (l VirtualDeviceList) ConnectSerialPort(device *types.VirtualSerialPort, uri string, client bool) *types.VirtualSerialPort {
566
direction := types.VirtualDeviceURIBackingOptionDirectionServer
568
direction = types.VirtualDeviceURIBackingOptionDirectionClient
571
device.Backing = &types.VirtualSerialPortURIBackingInfo{
572
VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
573
Direction: string(direction),
581
// DisconnectSerialPort disconnects the serial port backing.
582
func (l VirtualDeviceList) DisconnectSerialPort(device *types.VirtualSerialPort) *types.VirtualSerialPort {
583
l.setDefaultSerialPortBacking(device)
587
func (l VirtualDeviceList) setDefaultSerialPortBacking(device *types.VirtualSerialPort) {
588
device.Backing = &types.VirtualSerialPortURIBackingInfo{
589
VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
591
ServiceURI: "localhost:0",
596
// CreateEthernetCard creates a new VirtualEthernetCard of the given name name and initialized with the given backing.
597
func (l VirtualDeviceList) CreateEthernetCard(name string, backing types.BaseVirtualDeviceBackingInfo) (types.BaseVirtualDevice, error) {
598
ctypes := EthernetCardTypes()
601
name = ctypes.deviceName(ctypes[0])
604
found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
605
return l.deviceName(device) == name
609
return nil, fmt.Errorf("unknown ethernet card type '%s'", name)
612
c, ok := found[0].(types.BaseVirtualEthernetCard)
614
return nil, fmt.Errorf("invalid ethernet card type '%s'", name)
617
c.GetVirtualEthernetCard().Backing = backing
619
return c.(types.BaseVirtualDevice), nil
622
// PrimaryMacAddress returns the MacAddress field of the primary VirtualEthernetCard
623
func (l VirtualDeviceList) PrimaryMacAddress() string {
624
eth0 := l.Find("ethernet-0")
630
return eth0.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard().MacAddress
633
// convert a BaseVirtualDevice to a BaseVirtualMachineBootOptionsBootableDevice
634
var bootableDevices = map[string]func(device types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice{
635
DeviceTypeCdrom: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
636
return &types.VirtualMachineBootOptionsBootableCdromDevice{}
638
DeviceTypeDisk: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
639
return &types.VirtualMachineBootOptionsBootableDiskDevice{
640
DeviceKey: d.GetVirtualDevice().Key,
643
DeviceTypeEthernet: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
644
return &types.VirtualMachineBootOptionsBootableEthernetDevice{
645
DeviceKey: d.GetVirtualDevice().Key,
648
DeviceTypeFloppy: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
649
return &types.VirtualMachineBootOptionsBootableFloppyDevice{}
653
// BootOrder returns a list of devices which can be used to set boot order via VirtualMachine.SetBootOptions.
654
// The order can any of "ethernet", "cdrom", "floppy" or "disk" or by specific device name.
655
func (l VirtualDeviceList) BootOrder(order []string) []types.BaseVirtualMachineBootOptionsBootableDevice {
656
var devices []types.BaseVirtualMachineBootOptionsBootableDevice
658
for _, name := range order {
659
if kind, ok := bootableDevices[name]; ok {
660
for _, device := range l {
661
if l.Type(device) == name {
662
devices = append(devices, kind(device))
669
if d := l.Find(name); d != nil {
670
if kind, ok := bootableDevices[l.Type(d)]; ok {
671
devices = append(devices, kind(d))
679
// SelectBootOrder returns an ordered list of devices matching the given bootable device order
680
func (l VirtualDeviceList) SelectBootOrder(order []types.BaseVirtualMachineBootOptionsBootableDevice) VirtualDeviceList {
681
var devices VirtualDeviceList
683
for _, bd := range order {
684
for _, device := range l {
685
if kind, ok := bootableDevices[l.Type(device)]; ok {
686
if reflect.DeepEqual(kind(device), bd) {
687
devices = append(devices, device)
696
// TypeName returns the vmodl type name of the device
697
func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string {
698
return reflect.TypeOf(device).Elem().Name()
701
var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|Device|Controller)?$`)
703
func (l VirtualDeviceList) deviceName(device types.BaseVirtualDevice) string {
705
typeName := l.TypeName(device)
707
m := deviceNameRegexp.FindStringSubmatch(typeName)
709
name = strings.ToLower(m[1])
715
// Type returns a human-readable name for the given device
716
func (l VirtualDeviceList) Type(device types.BaseVirtualDevice) string {
717
switch device.(type) {
718
case types.BaseVirtualEthernetCard:
719
return DeviceTypeEthernet
720
case *types.ParaVirtualSCSIController:
722
case *types.VirtualLsiLogicSASController:
723
return "lsilogic-sas"
725
return l.deviceName(device)
729
// Name returns a stable, human-readable name for the given device
730
func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
732
d := device.GetVirtualDevice()
733
dtype := l.Type(device)
736
case DeviceTypeEthernet:
737
key = fmt.Sprintf("%d", d.UnitNumber-7)
739
key = fmt.Sprintf("%d-%d", d.ControllerKey, d.UnitNumber)
741
key = fmt.Sprintf("%d", d.Key)
744
return fmt.Sprintf("%s-%s", dtype, key)