@@ -31,6 +31,8 @@ import (
31
31
"github.com/juju/juju/worker/catacomb"
32
32
)
33
33
34
+ //go:generate go run ../../../../generate/filetoconst/filetoconst.go UbuntuOVF ubuntu.ovf ovf_ubuntu.go 2017 vsphereclient
35
+
34
36
// CreateVirtualMachineParams contains the parameters required for creating
35
37
// a new virtual machine.
36
38
type CreateVirtualMachineParams struct {
@@ -42,11 +44,8 @@ type CreateVirtualMachineParams struct {
42
44
// in which to create the VM.
43
45
Folder string
44
46
45
- // OVAContentsDir is the directory containing the extracted OVA contents.
46
- OVADir string
47
-
48
- // OVF contains the OVF content.
49
- OVF string
47
+ // VMDK is the URL to the VMDK to use.
48
+ //VMDK *url.URL
50
49
51
50
// UserData is the cloud-init user-data.
52
51
UserData string
@@ -90,6 +89,7 @@ type CreateVirtualMachineParams struct {
90
89
91
90
// CreateVirtualMachine creates and powers on a new VM.
92
91
//
92
+ // TODO(axw) revise below
93
93
// This method imports an OVF template using the vSphere API. This process
94
94
// comprises the following steps:
95
95
// 1. Download the OVA archive, extract it, and load the OVF file contained
@@ -114,12 +114,6 @@ func (c *Client) CreateVirtualMachine(
114
114
args CreateVirtualMachineParams ,
115
115
) (* mo.VirtualMachine , error ) {
116
116
117
- args .UpdateProgress ("creating import spec" )
118
- spec , err := c .createImportSpec (ctx , args )
119
- if err != nil {
120
- return nil , errors .Annotate (err , "creating import spec" )
121
- }
122
-
123
117
// Locate the folder in which to create the VM.
124
118
finder , datacenter , err := c .finder (ctx )
125
119
if err != nil {
@@ -135,66 +129,31 @@ func (c *Client) CreateVirtualMachine(
135
129
return nil , errors .Trace (err )
136
130
}
137
131
132
+ // Select the datastore.
133
+ datastoreMo , err := c .selectDatastore (ctx , args )
134
+ if err != nil {
135
+ return nil , errors .Trace (err )
136
+ }
137
+ datastore := object .NewDatastore (c .client .Client , datastoreMo .Reference ())
138
+ datastore .SetInventoryPath (path .Join (folders .DatastoreFolder .InventoryPath , datastoreMo .Name ))
139
+
138
140
// Import the VApp.
141
+ args .UpdateProgress ("creating import spec" )
142
+ spec , err := c .createImportSpec (ctx , args , datastore )
143
+ if err != nil {
144
+ return nil , errors .Annotate (err , "creating import spec" )
145
+ }
139
146
args .UpdateProgress (fmt .Sprintf ("creating VM %q" , args .Name ))
140
147
c .logger .Debugf ("creating VM in folder %s" , vmFolder )
141
148
rp := object .NewResourcePool (c .client .Client , * args .ComputeResource .ResourcePool )
142
149
lease , err := rp .ImportVApp (ctx , spec .ImportSpec , vmFolder , nil )
143
150
if err != nil {
144
151
return nil , errors .Annotatef (err , "failed to import vapp" )
145
152
}
146
-
147
- // Upload the VMDK.
148
- info , err := lease .Wait (ctx , spec .FileItem )
153
+ info , err := lease .Wait (ctx , nil )
149
154
if err != nil {
150
155
return nil , errors .Trace (err )
151
156
}
152
- type uploadItem struct {
153
- item types.OvfFileItem
154
- url * url.URL
155
- }
156
- var uploadItems []uploadItem
157
- for _ , device := range info .DeviceUrl {
158
- for _ , item := range spec .FileItem {
159
- if device .ImportKey != item .DeviceId {
160
- continue
161
- }
162
- u , err := c .client .Client .ParseURL (device .Url )
163
- if err != nil {
164
- return nil , errors .Trace (err )
165
- }
166
- uploadItems = append (uploadItems , uploadItem {
167
- item : item ,
168
- url : u ,
169
- })
170
- }
171
- }
172
- leaseUpdaterContext := leaseUpdaterContext {lease : lease }
173
- for _ , item := range uploadItems {
174
- leaseUpdaterContext .total += item .item .Size
175
- }
176
- for _ , item := range uploadItems {
177
- leaseUpdaterContext .size = item .item .Size
178
- if err := uploadImage (
179
- ctx ,
180
- c .client .Client ,
181
- item .item ,
182
- args .OVADir ,
183
- item .url ,
184
- args .UpdateProgress ,
185
- args .UpdateProgressInterval ,
186
- leaseUpdaterContext ,
187
- args .Clock ,
188
- c .logger ,
189
- ); err != nil {
190
- return nil , errors .Annotatef (
191
- err , "uploading %s to %s" ,
192
- filepath .Base (item .item .Path ),
193
- item .url ,
194
- )
195
- }
196
- leaseUpdaterContext .start += leaseUpdaterContext .size
197
- }
198
157
if err := lease .Complete (ctx ); err != nil {
199
158
return nil , errors .Trace (err )
200
159
}
@@ -220,6 +179,7 @@ func (c *Client) CreateVirtualMachine(
220
179
func (c * Client ) createImportSpec (
221
180
ctx context.Context ,
222
181
args CreateVirtualMachineParams ,
182
+ datastore * object.Datastore ,
223
183
) (* types.OvfCreateImportSpecResult , error ) {
224
184
cisp := types.OvfCreateImportSpecParams {
225
185
EntityName : args .Name ,
@@ -256,12 +216,8 @@ func (c *Client) createImportSpec(
256
216
257
217
ovfManager := ovf .NewManager (c .client .Client )
258
218
resourcePool := object .NewReference (c .client .Client , * args .ComputeResource .ResourcePool )
259
- datastore , err := c .selectDatastore (ctx , args )
260
- if err != nil {
261
- return nil , errors .Trace (err )
262
- }
263
219
264
- spec , err := ovfManager .CreateImportSpec (ctx , args . OVF , resourcePool , datastore , cisp )
220
+ spec , err := ovfManager .CreateImportSpec (ctx , UbuntuOVF , resourcePool , datastore , cisp )
265
221
if err != nil {
266
222
return nil , errors .Trace (err )
267
223
} else if spec .Error != nil {
@@ -283,31 +239,13 @@ func (c *Client) createImportSpec(
283
239
Reservation : cpuPower ,
284
240
}
285
241
}
286
- for _ , d := range s .DeviceChange {
287
- disk , ok := d .GetVirtualDeviceConfigSpec ().Device .(* types.VirtualDisk )
288
- if ! ok {
289
- continue
290
- }
291
- var rootDisk int64
292
- if args .Constraints .RootDisk != nil {
293
- rootDisk = int64 (* args .Constraints .RootDisk ) * 1024
294
- }
295
- if disk .CapacityInKB < rootDisk {
296
- disk .CapacityInKB = rootDisk
297
- }
298
- // Set UnitNumber to -1 if it is unset in ovf file template
299
- // (in this case it is parses as 0), because 0 causes an error
300
- // for disk devices.
301
- var unitNumber int32
302
- if disk .UnitNumber != nil {
303
- unitNumber = * disk .UnitNumber
304
- }
305
- if unitNumber == 0 {
306
- unitNumber = - 1
307
- disk .UnitNumber = & unitNumber
308
- }
242
+ if err := c .addRootDisk (s , args , datastore ); err != nil {
243
+ return nil , errors .Trace (err )
309
244
}
310
245
246
+ // We don't upload the VMDK, so clear out the file items.
247
+ spec .FileItem = nil
248
+
311
249
// Apply metadata. Note that we do not have the ability set create or
312
250
// apply tags that will show up in vCenter, as that requires a separate
313
251
// vSphere Automation that we do not have an SDK for.
@@ -329,10 +267,58 @@ func (c *Client) createImportSpec(
329
267
return spec , nil
330
268
}
331
269
270
+ func (c * Client ) addRootDisk (
271
+ s * types.VirtualMachineConfigSpec ,
272
+ args CreateVirtualMachineParams ,
273
+ diskDatastore * object.Datastore ,
274
+ ) error {
275
+ // TODO(axw)
276
+ vmdkName := "ubuntu-16.04-server-cloudimg-amd64-disk1.vmdk"
277
+ vmdkPath := diskDatastore .Path (vmdkName )
278
+ ds := diskDatastore .Reference ()
279
+
280
+ for _ , d := range s .DeviceChange {
281
+ deviceConfigSpec := d .GetVirtualDeviceConfigSpec ()
282
+ existingDisk , ok := deviceConfigSpec .Device .(* types.VirtualDisk )
283
+ if ! ok {
284
+ continue
285
+ }
286
+ // Create a linked disk to avoid copying the VMDK for each VM.
287
+ parentDisk := & types.VirtualDisk {
288
+ VirtualDevice : types.VirtualDevice {
289
+ Key : existingDisk .VirtualDevice .Key ,
290
+ ControllerKey : existingDisk .VirtualDevice .ControllerKey ,
291
+ UnitNumber : existingDisk .VirtualDevice .UnitNumber ,
292
+ Backing : & types.VirtualDiskFlatVer2BackingInfo {
293
+ DiskMode : string (types .VirtualDiskModePersistent ),
294
+ ThinProvisioned : types .NewBool (true ),
295
+ VirtualDeviceFileBackingInfo : types.VirtualDeviceFileBackingInfo {
296
+ FileName : vmdkPath ,
297
+ Datastore : & ds ,
298
+ },
299
+ },
300
+ },
301
+ }
302
+ var l object.VirtualDeviceList
303
+ disk := l .ChildDisk (parentDisk )
304
+ // TODO(axw) override root disk size. We need to
305
+ // fetch the size of the existing disk first.
306
+ //var rootDiskKB int64
307
+ //if args.Constraints.RootDisk != nil {
308
+ // rootDiskKB = int64(*args.Constraints.RootDisk) * 1024
309
+ //}
310
+ //if disk.CapacityInKB < rootDiskKB {
311
+ // disk.CapacityInKB = rootDiskKB
312
+ //}
313
+ deviceConfigSpec .Device = disk
314
+ }
315
+ return nil
316
+ }
317
+
332
318
func (c * Client ) selectDatastore (
333
319
ctx context.Context ,
334
320
args CreateVirtualMachineParams ,
335
- ) (* object .Datastore , error ) {
321
+ ) (* mo .Datastore , error ) {
336
322
// Select a datastore. If the user specified one, use that; otherwise
337
323
// choose the first one in the list that is accessible.
338
324
refs := make ([]types.ManagedObjectReference , len (args .ComputeResource .Datastore ))
@@ -346,15 +332,15 @@ func (c *Client) selectDatastore(
346
332
if args .Datastore != "" {
347
333
for _ , ds := range datastores {
348
334
if ds .Name == args .Datastore {
349
- return object . NewDatastore ( c . client . Client , ds . Reference ()) , nil
335
+ return & ds , nil
350
336
}
351
337
}
352
338
return nil , errors .Errorf ("could not find datastore %q" , args .Datastore )
353
339
}
354
340
for _ , ds := range datastores {
355
341
if ds .Summary .Accessible {
356
342
c .logger .Debugf ("using datastore %q" , ds .Name )
357
- return object . NewDatastore ( c . client . Client , ds . Reference ()) , nil
343
+ return & ds , nil
358
344
}
359
345
}
360
346
return nil , errors .New ("could not find an accessible datastore" )
0 commit comments