~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/govmomi/object/virtual_device_list.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2014 VMware, Inc. All Rights Reserved.
 
3
 
 
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
 
7
 
 
8
    http://www.apache.org/licenses/LICENSE-2.0
 
9
 
 
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.
 
15
*/
 
16
 
 
17
package object
 
18
 
 
19
import (
 
20
        "errors"
 
21
        "fmt"
 
22
        "path/filepath"
 
23
        "reflect"
 
24
        "regexp"
 
25
        "sort"
 
26
        "strings"
 
27
 
 
28
        "github.com/juju/govmomi/vim25/types"
 
29
)
 
30
 
 
31
// Type values for use in BootOrder
 
32
const (
 
33
        DeviceTypeCdrom    = "cdrom"
 
34
        DeviceTypeDisk     = "disk"
 
35
        DeviceTypeEthernet = "ethernet"
 
36
        DeviceTypeFloppy   = "floppy"
 
37
)
 
38
 
 
39
// VirtualDeviceList provides helper methods for working with a list of virtual devices.
 
40
type VirtualDeviceList []types.BaseVirtualDevice
 
41
 
 
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
 
53
                c.BusNumber = -1
 
54
                return true
 
55
        })
 
56
}
 
57
 
 
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
 
68
                return true
 
69
        })
 
70
}
 
71
 
 
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
 
75
 
 
76
        for _, device := range l {
 
77
                if f(device) {
 
78
                        found = append(found, device)
 
79
                }
 
80
        }
 
81
 
 
82
        return found
 
83
}
 
84
 
 
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()
 
89
 
 
90
        return l.Select(func(device types.BaseVirtualDevice) bool {
 
91
                t := reflect.TypeOf(device)
 
92
 
 
93
                if t == dtype {
 
94
                        return true
 
95
                }
 
96
 
 
97
                _, ok := t.Elem().FieldByName(dname)
 
98
 
 
99
                return ok
 
100
        })
 
101
}
 
102
 
 
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)
 
106
 
 
107
        return l.Select(func(device types.BaseVirtualDevice) bool {
 
108
                db := device.GetVirtualDevice().Backing
 
109
                if db == nil {
 
110
                        return false
 
111
                }
 
112
 
 
113
                if reflect.TypeOf(db) != t {
 
114
                        return false
 
115
                }
 
116
 
 
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
 
128
                        }
 
129
                        return a.FileName == b.FileName
 
130
                case types.BaseVirtualDeviceFileBackingInfo:
 
131
                        b := backing.(types.BaseVirtualDeviceFileBackingInfo)
 
132
                        return a.GetVirtualDeviceFileBackingInfo().FileName == b.GetVirtualDeviceFileBackingInfo().FileName
 
133
                default:
 
134
                        return false
 
135
                }
 
136
        })
 
137
}
 
138
 
 
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 {
 
143
                        return device
 
144
                }
 
145
        }
 
146
        return nil
 
147
}
 
148
 
 
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 {
 
153
                        return device
 
154
                }
 
155
        }
 
156
        return nil
 
157
}
 
158
 
 
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) {
 
163
        if name != "" {
 
164
                d := l.Find(name)
 
165
                if d == nil {
 
166
                        return nil, fmt.Errorf("device '%s' not found", name)
 
167
                }
 
168
                if c, ok := d.(*types.VirtualIDEController); ok {
 
169
                        return c, nil
 
170
                }
 
171
                return nil, fmt.Errorf("%s is not an IDE controller", name)
 
172
        }
 
173
 
 
174
        c := l.PickController((*types.VirtualIDEController)(nil))
 
175
        if c == nil {
 
176
                return nil, errors.New("no available IDE controller")
 
177
        }
 
178
 
 
179
        return c.(*types.VirtualIDEController), nil
 
180
}
 
181
 
 
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) {
 
186
        if name != "" {
 
187
                d := l.Find(name)
 
188
                if d == nil {
 
189
                        return nil, fmt.Errorf("device '%s' not found", name)
 
190
                }
 
191
                if c, ok := d.(types.BaseVirtualSCSIController); ok {
 
192
                        return c.GetVirtualSCSIController(), nil
 
193
                }
 
194
                return nil, fmt.Errorf("%s is not an SCSI controller", name)
 
195
        }
 
196
 
 
197
        c := l.PickController((*types.VirtualSCSIController)(nil))
 
198
        if c == nil {
 
199
                return nil, errors.New("no available SCSI controller")
 
200
        }
 
201
 
 
202
        return c.(types.BaseVirtualSCSIController).GetVirtualSCSIController(), nil
 
203
}
 
204
 
 
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()
 
208
 
 
209
        if name == "scsi" || name == "" {
 
210
                name = ctypes.Type(ctypes[0])
 
211
        }
 
212
 
 
213
        found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
 
214
                return l.Type(device) == name
 
215
        })
 
216
 
 
217
        if len(found) == 0 {
 
218
                return nil, fmt.Errorf("unknown SCSI controller type '%s'", name)
 
219
        }
 
220
 
 
221
        c, ok := found[0].(types.BaseVirtualSCSIController)
 
222
        if !ok {
 
223
                return nil, fmt.Errorf("invalid SCSI controller type '%s'", name)
 
224
        }
 
225
 
 
226
        scsi := c.GetVirtualSCSIController()
 
227
 
 
228
        scsi.BusNumber = l.newSCSIBusNumber()
 
229
 
 
230
        return c.(types.BaseVirtualDevice), nil
 
231
}
 
232
 
 
233
var scsiBusNumbers = []int{0, 1, 2, 3}
 
234
 
 
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 {
 
238
        var used []int
 
239
 
 
240
        for _, d := range l.SelectByType((*types.VirtualSCSIController)(nil)) {
 
241
                num := d.(types.BaseVirtualSCSIController).GetVirtualSCSIController().BusNumber
 
242
                if num >= 0 {
 
243
                        used = append(used, num)
 
244
                } // else caller is creating a new vm using SCSIControllerTypes
 
245
        }
 
246
 
 
247
        sort.Ints(used)
 
248
 
 
249
        for i, n := range scsiBusNumbers {
 
250
                if i == len(used) || n != used[i] {
 
251
                        return n
 
252
                }
 
253
        }
 
254
 
 
255
        return -1
 
256
}
 
257
 
 
258
// FindDiskController will find an existing ide or scsi disk controller.
 
259
func (l VirtualDeviceList) FindDiskController(name string) (types.BaseVirtualController, error) {
 
260
        switch {
 
261
        case name == "ide":
 
262
                return l.FindIDEController("")
 
263
        case name == "scsi" || name == "":
 
264
                return l.FindSCSIController("")
 
265
        default:
 
266
                if c, ok := l.Find(name).(types.BaseVirtualController); ok {
 
267
                        return c, nil
 
268
                }
 
269
                return nil, fmt.Errorf("%s is not a valid controller", name)
 
270
        }
 
271
}
 
272
 
 
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)
 
278
 
 
279
                switch device.(type) {
 
280
                case types.BaseVirtualSCSIController:
 
281
                        return num < 15
 
282
                case *types.VirtualIDEController:
 
283
                        return num < 2
 
284
                default:
 
285
                        return true
 
286
                }
 
287
        })
 
288
 
 
289
        if len(l) == 0 {
 
290
                return nil
 
291
        }
 
292
 
 
293
        return l[0].(types.BaseVirtualController)
 
294
}
 
295
 
 
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
 
299
        max := -1
 
300
 
 
301
        for _, device := range l {
 
302
                d := device.GetVirtualDevice()
 
303
 
 
304
                if d.ControllerKey == key {
 
305
                        if d.UnitNumber > max {
 
306
                                max = d.UnitNumber
 
307
                        }
 
308
                }
 
309
        }
 
310
 
 
311
        return max + 1
 
312
}
 
313
 
 
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)
 
319
        d.Key = -1
 
320
}
 
321
 
 
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" {
 
327
                name += ".vmdk"
 
328
        }
 
329
 
 
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{
 
336
                                        FileName: name,
 
337
                                },
 
338
                        },
 
339
                },
 
340
        }
 
341
 
 
342
        l.AssignController(device, c)
 
343
 
 
344
        if device.UnitNumber == 0 {
 
345
                device.UnitNumber = -1 // TODO: this field is annotated as omitempty
 
346
        }
 
347
 
 
348
        return device
 
349
}
 
350
 
 
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 {
 
353
        disk := *parent
 
354
        backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
 
355
        ds := strings.SplitN(backing.FileName[1:], "]", 2)
 
356
 
 
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]),
 
361
                },
 
362
                Parent:          backing,
 
363
                DiskMode:        backing.DiskMode,
 
364
                ThinProvisioned: backing.ThinProvisioned,
 
365
        }
 
366
 
 
367
        return &disk
 
368
}
 
369
 
 
370
func (l VirtualDeviceList) connectivity(device types.BaseVirtualDevice, v bool) error {
 
371
        c := device.GetVirtualDevice().Connectable
 
372
        if c == nil {
 
373
                return fmt.Errorf("%s is not connectable", l.Name(device))
 
374
        }
 
375
 
 
376
        c.Connected = v
 
377
        c.StartConnected = v
 
378
 
 
379
        return nil
 
380
}
 
381
 
 
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)
 
385
}
 
386
 
 
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)
 
390
}
 
391
 
 
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) {
 
394
        if name != "" {
 
395
                d := l.Find(name)
 
396
                if d == nil {
 
397
                        return nil, fmt.Errorf("device '%s' not found", name)
 
398
                }
 
399
                if c, ok := d.(*types.VirtualCdrom); ok {
 
400
                        return c, nil
 
401
                }
 
402
                return nil, fmt.Errorf("%s is not a cdrom device", name)
 
403
        }
 
404
 
 
405
        c := l.SelectByType((*types.VirtualCdrom)(nil))
 
406
        if len(c) == 0 {
 
407
                return nil, errors.New("no cdrom device found")
 
408
        }
 
409
 
 
410
        return c[0].(*types.VirtualCdrom), nil
 
411
}
 
412
 
 
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{}
 
416
 
 
417
        l.AssignController(device, c)
 
418
 
 
419
        l.setDefaultCdromBacking(device)
 
420
 
 
421
        device.Connectable = &types.VirtualDeviceConnectInfo{
 
422
                AllowGuestControl: true,
 
423
                Connected:         true,
 
424
                StartConnected:    true,
 
425
        }
 
426
 
 
427
        return device, nil
 
428
}
 
429
 
 
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{
 
434
                        FileName: iso,
 
435
                },
 
436
        }
 
437
 
 
438
        return device
 
439
}
 
440
 
 
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)
 
444
        return device
 
445
}
 
446
 
 
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),
 
452
                },
 
453
        }
 
454
}
 
455
 
 
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) {
 
458
        if name != "" {
 
459
                d := l.Find(name)
 
460
                if d == nil {
 
461
                        return nil, fmt.Errorf("device '%s' not found", name)
 
462
                }
 
463
                if c, ok := d.(*types.VirtualFloppy); ok {
 
464
                        return c, nil
 
465
                }
 
466
                return nil, fmt.Errorf("%s is not a floppy device", name)
 
467
        }
 
468
 
 
469
        c := l.SelectByType((*types.VirtualFloppy)(nil))
 
470
        if len(c) == 0 {
 
471
                return nil, errors.New("no floppy device found")
 
472
        }
 
473
 
 
474
        return c[0].(*types.VirtualFloppy), nil
 
475
}
 
476
 
 
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{}
 
480
 
 
481
        c := l.PickController((*types.VirtualSIOController)(nil))
 
482
        if c == nil {
 
483
                return nil, errors.New("no available SIO controller")
 
484
        }
 
485
 
 
486
        l.AssignController(device, c)
 
487
 
 
488
        l.setDefaultFloppyBacking(device)
 
489
 
 
490
        device.Connectable = &types.VirtualDeviceConnectInfo{
 
491
                AllowGuestControl: true,
 
492
                Connected:         true,
 
493
                StartConnected:    true,
 
494
        }
 
495
 
 
496
        return device, nil
 
497
}
 
498
 
 
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{
 
503
                        FileName: img,
 
504
                },
 
505
        }
 
506
 
 
507
        return device
 
508
}
 
509
 
 
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)
 
513
        return device
 
514
}
 
515
 
 
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),
 
521
                },
 
522
        }
 
523
}
 
524
 
 
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) {
 
527
        if name != "" {
 
528
                d := l.Find(name)
 
529
                if d == nil {
 
530
                        return nil, fmt.Errorf("device '%s' not found", name)
 
531
                }
 
532
                if c, ok := d.(*types.VirtualSerialPort); ok {
 
533
                        return c, nil
 
534
                }
 
535
                return nil, fmt.Errorf("%s is not a serial port device", name)
 
536
        }
 
537
 
 
538
        c := l.SelectByType((*types.VirtualSerialPort)(nil))
 
539
        if len(c) == 0 {
 
540
                return nil, errors.New("no serial port device found")
 
541
        }
 
542
 
 
543
        return c[0].(*types.VirtualSerialPort), nil
 
544
}
 
545
 
 
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{
 
549
                YieldOnPoll: true,
 
550
        }
 
551
 
 
552
        c := l.PickController((*types.VirtualSIOController)(nil))
 
553
        if c == nil {
 
554
                return nil, errors.New("no available SIO controller")
 
555
        }
 
556
 
 
557
        l.AssignController(device, c)
 
558
 
 
559
        l.setDefaultSerialPortBacking(device)
 
560
 
 
561
        return device, nil
 
562
}
 
563
 
 
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
 
567
        if client {
 
568
                direction = types.VirtualDeviceURIBackingOptionDirectionClient
 
569
        }
 
570
 
 
571
        device.Backing = &types.VirtualSerialPortURIBackingInfo{
 
572
                VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
 
573
                        Direction:  string(direction),
 
574
                        ServiceURI: uri,
 
575
                },
 
576
        }
 
577
 
 
578
        return device
 
579
}
 
580
 
 
581
// DisconnectSerialPort disconnects the serial port backing.
 
582
func (l VirtualDeviceList) DisconnectSerialPort(device *types.VirtualSerialPort) *types.VirtualSerialPort {
 
583
        l.setDefaultSerialPortBacking(device)
 
584
        return device
 
585
}
 
586
 
 
587
func (l VirtualDeviceList) setDefaultSerialPortBacking(device *types.VirtualSerialPort) {
 
588
        device.Backing = &types.VirtualSerialPortURIBackingInfo{
 
589
                VirtualDeviceURIBackingInfo: types.VirtualDeviceURIBackingInfo{
 
590
                        Direction:  "client",
 
591
                        ServiceURI: "localhost:0",
 
592
                },
 
593
        }
 
594
}
 
595
 
 
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()
 
599
 
 
600
        if name == "" {
 
601
                name = ctypes.deviceName(ctypes[0])
 
602
        }
 
603
 
 
604
        found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
 
605
                return l.deviceName(device) == name
 
606
        })
 
607
 
 
608
        if len(found) == 0 {
 
609
                return nil, fmt.Errorf("unknown ethernet card type '%s'", name)
 
610
        }
 
611
 
 
612
        c, ok := found[0].(types.BaseVirtualEthernetCard)
 
613
        if !ok {
 
614
                return nil, fmt.Errorf("invalid ethernet card type '%s'", name)
 
615
        }
 
616
 
 
617
        c.GetVirtualEthernetCard().Backing = backing
 
618
 
 
619
        return c.(types.BaseVirtualDevice), nil
 
620
}
 
621
 
 
622
// PrimaryMacAddress returns the MacAddress field of the primary VirtualEthernetCard
 
623
func (l VirtualDeviceList) PrimaryMacAddress() string {
 
624
        eth0 := l.Find("ethernet-0")
 
625
 
 
626
        if eth0 == nil {
 
627
                return ""
 
628
        }
 
629
 
 
630
        return eth0.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard().MacAddress
 
631
}
 
632
 
 
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{}
 
637
        },
 
638
        DeviceTypeDisk: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
 
639
                return &types.VirtualMachineBootOptionsBootableDiskDevice{
 
640
                        DeviceKey: d.GetVirtualDevice().Key,
 
641
                }
 
642
        },
 
643
        DeviceTypeEthernet: func(d types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
 
644
                return &types.VirtualMachineBootOptionsBootableEthernetDevice{
 
645
                        DeviceKey: d.GetVirtualDevice().Key,
 
646
                }
 
647
        },
 
648
        DeviceTypeFloppy: func(types.BaseVirtualDevice) types.BaseVirtualMachineBootOptionsBootableDevice {
 
649
                return &types.VirtualMachineBootOptionsBootableFloppyDevice{}
 
650
        },
 
651
}
 
652
 
 
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
 
657
 
 
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))
 
663
                                }
 
664
 
 
665
                        }
 
666
                        continue
 
667
                }
 
668
 
 
669
                if d := l.Find(name); d != nil {
 
670
                        if kind, ok := bootableDevices[l.Type(d)]; ok {
 
671
                                devices = append(devices, kind(d))
 
672
                        }
 
673
                }
 
674
        }
 
675
 
 
676
        return devices
 
677
}
 
678
 
 
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
 
682
 
 
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)
 
688
                                }
 
689
                        }
 
690
                }
 
691
        }
 
692
 
 
693
        return devices
 
694
}
 
695
 
 
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()
 
699
}
 
700
 
 
701
var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|Device|Controller)?$`)
 
702
 
 
703
func (l VirtualDeviceList) deviceName(device types.BaseVirtualDevice) string {
 
704
        name := "device"
 
705
        typeName := l.TypeName(device)
 
706
 
 
707
        m := deviceNameRegexp.FindStringSubmatch(typeName)
 
708
        if len(m) == 2 {
 
709
                name = strings.ToLower(m[1])
 
710
        }
 
711
 
 
712
        return name
 
713
}
 
714
 
 
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:
 
721
                return "pvscsi"
 
722
        case *types.VirtualLsiLogicSASController:
 
723
                return "lsilogic-sas"
 
724
        default:
 
725
                return l.deviceName(device)
 
726
        }
 
727
}
 
728
 
 
729
// Name returns a stable, human-readable name for the given device
 
730
func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
 
731
        var key string
 
732
        d := device.GetVirtualDevice()
 
733
        dtype := l.Type(device)
 
734
 
 
735
        switch dtype {
 
736
        case DeviceTypeEthernet:
 
737
                key = fmt.Sprintf("%d", d.UnitNumber-7)
 
738
        case DeviceTypeDisk:
 
739
                key = fmt.Sprintf("%d-%d", d.ControllerKey, d.UnitNumber)
 
740
        default:
 
741
                key = fmt.Sprintf("%d", d.Key)
 
742
        }
 
743
 
 
744
        return fmt.Sprintf("%s-%s", dtype, key)
 
745
}