fog-vsphere 1.7.0 → 1.7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -1
  3. data/.rubocop.yml +8 -0
  4. data/.rubocop_todo.yml +217 -0
  5. data/.travis.yml +6 -7
  6. data/CHANGELOG.md +2 -6
  7. data/CONTRIBUTORS.md +10 -0
  8. data/Rakefile +10 -1
  9. data/lib/fog/vsphere/compute.rb +0 -2
  10. data/lib/fog/vsphere/models/compute/scsicontroller.rb +1 -6
  11. data/lib/fog/vsphere/models/compute/server.rb +7 -29
  12. data/lib/fog/vsphere/models/compute/ticket.rb +16 -0
  13. data/lib/fog/vsphere/models/compute/tickets.rb +25 -0
  14. data/lib/fog/vsphere/models/compute/volume.rb +28 -46
  15. data/lib/fog/vsphere/models/compute/volumes.rb +1 -1
  16. data/lib/fog/vsphere/requests/compute/create_vm.rb +44 -32
  17. data/lib/fog/vsphere/requests/compute/host_finish_maintenance.rb +14 -0
  18. data/lib/fog/vsphere/requests/compute/host_shutdown.rb +14 -0
  19. data/lib/fog/vsphere/requests/compute/host_start_maintenance.rb +14 -0
  20. data/lib/fog/vsphere/requests/compute/list_vm_scsi_controllers.rb +11 -4
  21. data/lib/fog/vsphere/requests/compute/list_vm_volumes.rb +1 -2
  22. data/lib/fog/vsphere/requests/compute/modify_vm_controller.rb +14 -2
  23. data/lib/fog/vsphere/requests/compute/modify_vm_volume.rb +3 -3
  24. data/lib/fog/vsphere/requests/compute/vm_acquire_ticket.rb +34 -0
  25. data/lib/fog/vsphere/requests/compute/vm_reconfig_volumes.rb +15 -26
  26. data/lib/fog/vsphere/requests/compute/vm_relocate.rb +54 -0
  27. data/lib/fog/vsphere/requests/compute/vm_remove_snapshot.rb +29 -0
  28. data/lib/fog/vsphere/requests/compute/vm_rename.rb +24 -0
  29. data/lib/fog/vsphere/requests/compute/vm_revert_snapshot.rb +29 -0
  30. data/lib/fog/vsphere/requests/compute/vm_suspend.rb +54 -0
  31. data/lib/fog/vsphere/version.rb +1 -1
  32. data/tests/class_from_string_tests.rb +3 -3
  33. data/tests/compute_tests.rb +16 -17
  34. data/tests/helpers/mock_helper.rb +3 -3
  35. data/tests/models/compute/cluster_tests.rb +4 -5
  36. data/tests/models/compute/hosts_tests.rb +2 -4
  37. data/tests/models/compute/rules_tests.rb +10 -16
  38. data/tests/models/compute/server_tests.rb +33 -29
  39. data/tests/models/compute/servers_tests.rb +2 -4
  40. data/tests/models/compute/ticket_tests.rb +12 -0
  41. data/tests/models/compute/tickets_tests.rb +8 -0
  42. data/tests/requests/compute/current_time_tests.rb +2 -4
  43. data/tests/requests/compute/folder_destroy_tests.rb +5 -7
  44. data/tests/requests/compute/get_network_tests.rb +18 -22
  45. data/tests/requests/compute/list_child_snapshots_tests.rb +1 -2
  46. data/tests/requests/compute/list_clusters_tests.rb +5 -6
  47. data/tests/requests/compute/list_datastores_tests.rb +6 -7
  48. data/tests/requests/compute/list_hosts_tests.rb +3 -4
  49. data/tests/requests/compute/list_networks_tests.rb +6 -7
  50. data/tests/requests/compute/list_storage_pods_test.rb +3 -4
  51. data/tests/requests/compute/list_virtual_machines_tests.rb +16 -20
  52. data/tests/requests/compute/list_vm_cdroms_tests.rb +1 -2
  53. data/tests/requests/compute/list_vm_snapshots_tests.rb +1 -2
  54. data/tests/requests/compute/modify_vm_cdrom_tests.rb +3 -4
  55. data/tests/requests/compute/revert_to_snapshot_tests.rb +2 -4
  56. data/tests/requests/compute/set_vm_customvalue_tests.rb +0 -2
  57. data/tests/requests/compute/vm_clone_tests.rb +20 -20
  58. data/tests/requests/compute/vm_config_vnc_tests.rb +3 -4
  59. data/tests/requests/compute/vm_destroy_tests.rb +1 -4
  60. data/tests/requests/compute/vm_migrate_tests.rb +1 -2
  61. data/tests/requests/compute/vm_power_off_tests.rb +2 -4
  62. data/tests/requests/compute/vm_power_on_tests.rb +1 -3
  63. data/tests/requests/compute/vm_reboot_tests.rb +2 -4
  64. data/tests/requests/compute/vm_reconfig_cdrom_tests.rb +2 -3
  65. data/tests/requests/compute/vm_reconfig_cpus_tests.rb +1 -3
  66. data/tests/requests/compute/vm_reconfig_hardware_tests.rb +2 -4
  67. data/tests/requests/compute/vm_reconfig_memory_tests.rb +1 -3
  68. data/tests/requests/compute/vm_suspend_tests.rb +23 -0
  69. data/tests/requests/compute/vm_take_snapshot_tests.rb +1 -3
  70. metadata +22 -4
  71. data/gemfiles/Gemfile.1.9.2+ +0 -9
@@ -0,0 +1,16 @@
1
+ require 'fog/compute/models/server'
2
+
3
+ module Fog
4
+ module Compute
5
+ class Vsphere
6
+ class Ticket < Fog::Model
7
+ attribute :server_id
8
+
9
+ attribute :ticket
10
+ attribute :host
11
+ attribute :port
12
+ attribute :ssl_thumbprint
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module Fog
2
+ module Compute
3
+ class Vsphere
4
+ class Tickets < Fog::Collection
5
+ autoload :Ticket, File.expand_path('../ticket', __FILE__)
6
+
7
+ model Fog::Compute::Vsphere::Ticket
8
+
9
+ attr_accessor :server
10
+
11
+ def create(opts = {})
12
+ requires :server
13
+ raise 'server must be a Fog::Compute::Vsphere::Server' unless server.is_a?(Fog::Compute::Vsphere::Server)
14
+ new(
15
+ service.vm_acquire_ticket(
16
+ opts.merge(
17
+ 'instance_uuid' => server.instance_uuid
18
+ )
19
+ )
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -5,7 +5,6 @@ module Fog
5
5
  DISK_SIZE_TO_GB = 1048576
6
6
  identity :id
7
7
 
8
- has_one :server, Server
9
8
  attribute :datastore
10
9
  attribute :storage_pod
11
10
  attribute :mode
@@ -17,9 +16,12 @@ module Fog
17
16
  attribute :size_gb
18
17
  attribute :key
19
18
  attribute :unit_number
20
- attribute :controller_key, :type => :integer
19
+ attribute :server_id
20
+
21
+ def initialize(attributes={} )
22
+ # Assign server first to prevent race condition with persisted?
23
+ self.server_id = attributes.delete(:server_id)
21
24
 
22
- def initialize(attributes={})
23
25
  super defaults.merge(attributes)
24
26
  end
25
27
 
@@ -29,7 +31,6 @@ module Fog
29
31
 
30
32
  def size_gb= s
31
33
  attributes[:size] = s.to_i * DISK_SIZE_TO_GB if s
32
- attributes[:size_gb] = s.to_i if s
33
34
  end
34
35
 
35
36
  def to_s
@@ -47,7 +48,25 @@ module Fog
47
48
  raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
48
49
  requires :server_id, :size, :datastore
49
50
 
50
- set_unit_number
51
+ # When adding volumes to vsphere, if our unit_number is 7 or higher, vsphere will increment the unit_number
52
+ # This is due to SCSI ID 7 being reserved for the pvscsi controller
53
+ # When referring to a volume that already added using a unit_id of 7 or higher, we must refer to the actual SCSI ID
54
+ if unit_number.nil?
55
+ # Vsphere maps unit_numbers 7 and greater to a higher SCSI ID since the pvscsi driver reserves SCSI ID 7
56
+ used_unit_numbers = server.volumes.map { |volume| volume.unit_number < 7 ? volume.unit_number : volume.unit_number - 1 }
57
+ max_unit_number = used_unit_numbers.max
58
+
59
+ if max_unit_number > server.volumes.size
60
+ # If the max ID exceeds the number of volumes, there must be a hole in the range. Find a hole and use it.
61
+ self.unit_number = max_unit_number.times.to_a.find { |i| used_unit_numbers.exclude?(i) }
62
+ else
63
+ self.unit_number = max_unit_number + 1
64
+ end
65
+ else
66
+ if server.volumes.any? { |volume| volume.unit_number == self.unit_number && volume.id != self.id }
67
+ raise "A volume already exists with that unit_number, so we can't save the new volume"
68
+ end
69
+ end
51
70
 
52
71
  data = service.add_vm_volume(self)
53
72
 
@@ -61,7 +80,6 @@ module Fog
61
80
 
62
81
  self.id = created.id
63
82
  self.key = created.key
64
- self.controller_key = created.controllerKey
65
83
  self.filename = created.filename
66
84
 
67
85
  true
@@ -70,33 +88,9 @@ module Fog
70
88
  end
71
89
  end
72
90
 
73
- def server_id
74
- requires :server
75
- server.id
76
- end
77
-
78
- def set_unit_number
79
- # When adding volumes to vsphere, if our unit_number is 7 or higher, vsphere will increment the unit_number
80
- # This is due to SCSI ID 7 being reserved for the pvscsi controller
81
- # When referring to a volume that already added using a unit_id of 7 or higher, we must refer to the actual SCSI ID
82
- if unit_number.nil?
83
- self.unit_number = calculate_free_unit_number
84
- else
85
- if server.volumes.select { |vol| vol.controller_key == controller_key }.any? { |volume| volume.unit_number == self.unit_number && volume.id != self.id }
86
- raise "A volume already exists with that unit_number, so we can't save the new volume"
87
- end
88
- end
89
- end
90
-
91
- def set_key
92
- requires :controller_key, :unit_number
93
-
94
- return unless key.nil?
95
-
96
- # controller key is based on 1000 + controller bus
97
- # disk key is based on 2000 + the SCSI ID + the controller bus * 16
98
- controller_bus = controller_key - 1000
99
- self.key = 2000 + (controller_bus * 16) + unit_number
91
+ def server
92
+ requires :server_id
93
+ service.servers.get(server_id)
100
94
  end
101
95
 
102
96
  private
@@ -105,21 +99,9 @@ module Fog
105
99
  {
106
100
  :thin => true,
107
101
  :name => "Hard disk",
108
- :mode => "persistent",
109
- :controller_key => 1000
102
+ :mode => "persistent"
110
103
  }
111
104
  end
112
-
113
- def calculate_free_unit_number
114
- requires :controller_key
115
-
116
- # Vsphere maps unit_numbers 7 and greater to a higher SCSI ID since the pvscsi driver reserves SCSI ID 7
117
- used_unit_numbers = server.volumes
118
- .select { |vol| vol.unit_number && vol.controller_key == controller_key }.map(&:unit_number) + [7]
119
- free_unit_numbers = (0..15).to_a - used_unit_numbers
120
-
121
- free_unit_numbers.first
122
- end
123
105
  end
124
106
  end
125
107
  end
@@ -20,7 +20,7 @@ module Fog
20
20
  raise 'volumes should have vm or template'
21
21
  end
22
22
 
23
- self.each { |volume| volume.server = server }
23
+ self.each { |volume| volume.server_id = server.id }
24
24
  self
25
25
  end
26
26
 
@@ -20,7 +20,7 @@ module Fog
20
20
  vm_cfg[:cpuHotAddEnabled] = attributes[:cpuHotAddEnabled] if attributes.key?(:cpuHotAddEnabled)
21
21
  vm_cfg[:memoryHotAddEnabled] = attributes[:memoryHotAddEnabled] if attributes.key?(:memoryHotAddEnabled)
22
22
  vm_cfg[:firmware] = attributes[:firmware] if attributes.key?(:firmware)
23
- vm_cfg[:bootOptions] = boot_options(attributes, vm_cfg) if attributes.key?(:boot_order) || attributes.key?(:boot_retry)
23
+ vm_cfg[:bootOptions] = boot_options(attributes) if attributes.key?(:boot_order) || attributes.key?(:boot_retry)
24
24
  resource_pool = if attributes[:resource_pool] && attributes[:resource_pool] != 'Resources'
25
25
  get_raw_resource_pool(attributes[:resource_pool], attributes[:cluster], attributes[:datacenter])
26
26
  else
@@ -97,12 +97,9 @@ module Fog
97
97
  devices << nics.map { |nic| create_interface(nic, nics.index(nic), :add, attributes) }
98
98
  end
99
99
 
100
- if (scsi_controllers = (attributes[:scsi_controllers] || attributes["scsi_controller"]))
101
- devices << scsi_controllers.each_with_index.map { |controller, index| create_controller(controller, index) }
102
- end
103
-
104
100
  if (disks = attributes[:volumes])
105
- devices << disks.map { |disk| create_disk(disk, :add, get_storage_pod(attributes)) }
101
+ devices << create_controller(attributes[:scsi_controller]||attributes["scsi_controller"]||{})
102
+ devices << disks.map { |disk| create_disk(disk, disks.index(disk), :add, 1000, get_storage_pod(attributes)) }
106
103
  end
107
104
 
108
105
  if (cdroms = attributes[:cdroms])
@@ -111,12 +108,12 @@ module Fog
111
108
  devices.flatten
112
109
  end
113
110
 
114
- def boot_options(attributes, vm_cfg)
111
+ def boot_options attributes
115
112
  # NOTE: you must be using vsphere_rev 5.0 or greater to set boot_order
116
113
  # e.g. Fog::Compute.new(provider: "vsphere", vsphere_rev: "5.5", etc)
117
114
  options = {}
118
115
  if @vsphere_rev.to_f >= 5 and attributes[:boot_order]
119
- options[:bootOrder] = boot_order(attributes, vm_cfg)
116
+ options[:bootOrder] = boot_order(attributes)
120
117
  end
121
118
 
122
119
  # Set attributes[:boot_retry] to a delay in miliseconds to enable boot retries
@@ -124,11 +121,11 @@ module Fog
124
121
  options[:bootRetryEnabled] = true
125
122
  options[:bootRetryDelay] = attributes[:boot_retry]
126
123
  end
127
-
124
+
128
125
  options.empty? ? nil : RbVmomi::VIM::VirtualMachineBootOptions.new(options)
129
126
  end
130
127
 
131
- def boot_order(attributes, vm_cfg)
128
+ def boot_order attributes
132
129
  # attributes[:boot_order] may be an array like this ['network', 'disk']
133
130
  # stating, that we want to prefer network boots over disk boots
134
131
  boot_order = []
@@ -145,17 +142,18 @@ module Fog
145
142
  end
146
143
  end
147
144
  when 'disk'
148
- disks = vm_cfg[:deviceChange].map {|dev| dev[:device]}.select { |dev| dev.is_a? RbVmomi::VIM::VirtualDisk }
149
- disks.each do |disk|
150
- # we allow booting from all harddisks, the first disk has the highest priority
151
- boot_order << RbVmomi::VIM::VirtualMachineBootOptionsBootableDiskDevice.new(
152
- :deviceKey => disk.key
153
- )
145
+ if disks = attributes[:volumes]
146
+ disks.each do |disk|
147
+ # we allow booting from all harddisks, the first disk has the highest priority
148
+ boot_order << RbVmomi::VIM::VirtualMachineBootOptionsBootableDiskDevice.new(
149
+ :deviceKey => disk.key || get_disk_device_key(disks.index(disk)),
150
+ )
151
+ end
154
152
  end
155
153
  when 'cdrom'
156
- boot_order << RbVmomi::VIM::VirtualMachineBootOptionsBootableCdromDevice.new
154
+ boot_order << RbVmomi::VIM::VirtualMachineBootOptionsBootableCdromDevice.new()
157
155
  when 'floppy'
158
- boot_order << RbVmomi::VIM::VirtualMachineBootOptionsBootableFloppyDevice.new
156
+ boot_order << RbVmomi::VIM::VirtualMachineBootOptionsBootableFloppyDevice.new()
159
157
  else
160
158
  raise "failed to create boot device because \"#{boot_device}\" is unknown"
161
159
  end
@@ -163,6 +161,19 @@ module Fog
163
161
  boot_order
164
162
  end
165
163
 
164
+ def get_disk_device_key(index)
165
+ # disk key is based on 2000 + the SCSI ID + the controller bus * 16
166
+ # the scsi host adapter appears as SCSI ID 7, so we have to skip that
167
+ # host adapter key is based on 1000 + bus id
168
+ # fog assumes that there is only a single scsi controller, see device_change()
169
+ if (index > 6) then
170
+ _index = index + 1
171
+ else
172
+ _index = index
173
+ end
174
+ 2000 + _index
175
+ end
176
+
166
177
  def create_nic_backing nic, attributes
167
178
  raw_network = get_raw_network(nic.network, attributes[:datacenter], if nic.virtualswitch then nic.virtualswitch end)
168
179
 
@@ -193,9 +204,9 @@ module Fog
193
204
  }
194
205
  end
195
206
 
196
- def create_controller(controller=nil, index = 0)
197
- options=if controller
198
- controller_default_options.merge(controller.attributes)
207
+ def create_controller options=nil
208
+ options=if options
209
+ controller_default_options.merge(Hash[options.map{|k,v| [k.to_sym,v] }])
199
210
  else
200
211
  controller_default_options
201
212
  end
@@ -207,15 +218,15 @@ module Fog
207
218
  {
208
219
  :operation => options[:operation],
209
220
  :device => controller_class.new({
210
- :key => options[:key] || (1000 + index),
211
- :busNumber => options[:bus_id] || index,
221
+ :key => options[:key],
222
+ :busNumber => options[:bus_id],
212
223
  :sharedBus => controller_get_shared_from_options(options),
213
224
  })
214
225
  }
215
226
  end
216
227
 
217
228
  def controller_default_options
218
- {:operation => :add, :type => RbVmomi::VIM.VirtualLsiLogicController.class, :shared => false }
229
+ {:operation => "add", :type => RbVmomi::VIM.VirtualLsiLogicController.class, :key => 1000, :bus_id => 0, :shared => false }
219
230
  end
220
231
 
221
232
  def controller_get_shared_from_options options
@@ -230,29 +241,30 @@ module Fog
230
241
  end
231
242
  end
232
243
 
233
- def create_disk(disk, operation = :add, storage_pod = nil)
244
+ def create_disk disk, index = 0, operation = :add, controller_key = 1000, storage_pod = nil
245
+ if (index > 6) then
246
+ _index = index + 1
247
+ else
248
+ _index = index
249
+ end
234
250
  # If we deploy the vm on a storage pod, datastore has to be an empty string
235
251
  if storage_pod
236
252
  datastore = ''
237
253
  else
238
254
  datastore = "[#{disk.datastore}]"
239
255
  end
240
-
241
- disk.set_unit_number
242
- disk.set_key
243
-
244
256
  payload = {
245
257
  :operation => operation,
246
258
  :fileOperation => operation == :add ? :create : :destroy,
247
259
  :device => RbVmomi::VIM.VirtualDisk(
248
- :key => disk.key,
260
+ :key => disk.key || _index,
249
261
  :backing => RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo(
250
262
  :fileName => datastore,
251
263
  :diskMode => disk.mode.to_sym,
252
264
  :thinProvisioned => disk.thin
253
265
  ),
254
- :controllerKey => disk.controller_key,
255
- :unitNumber => disk.unit_number,
266
+ :controllerKey => controller_key,
267
+ :unitNumber => _index,
256
268
  :capacityInKB => disk.size
257
269
  )
258
270
  }
@@ -0,0 +1,14 @@
1
+ module Fog
2
+ module Compute
3
+ class Vsphere
4
+ class Real
5
+ def host_finish_maintenance(name, cluster_name, datacenter_name, timeout = 0)
6
+ host_ref = get_host(name, cluster_name, datacenter_name)
7
+ task = host_ref.ExitMaintenanceMode_Task(timeout: timeout)
8
+ task.wait_for_completion
9
+ { 'task_state' => task.info.state }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Fog
2
+ module Compute
3
+ class Vsphere
4
+ class Real
5
+ def host_shutdown(name, cluster_name, datacenter_name, force = false)
6
+ host_ref = get_host(name, cluster_name, datacenter_name)
7
+ task = host_ref.ShutdownHost_Task(force: force)
8
+ task.wait_for_completion
9
+ { 'task_state' => task.info.state }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Fog
2
+ module Compute
3
+ class Vsphere
4
+ class Real
5
+ def host_start_maintenance(name, cluster_name, datacenter_name, timeout = 0, evacuate_powered_off_vms = false)
6
+ host_ref = get_host(name, cluster_name, datacenter_name)
7
+ task = host_ref.EnterMaintenanceMode_Task(timeout: timeout, evacuatePoweredOffVms: evacuate_powered_off_vms)
8
+ task.wait_for_completion
9
+ { 'task_state' => task.info.state }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -11,14 +11,21 @@ module Fog
11
11
  def list_vm_scsi_controllers_raw(vm_id)
12
12
  get_vm_ref(vm_id).config.hardware.device.grep(RbVmomi::VIM::VirtualSCSIController).map do |ctrl|
13
13
  {
14
- :type => ctrl.class.to_s,
15
- :shared_bus => ctrl.sharedBus.to_s,
16
- :unit_number => ctrl.scsiCtlrUnitNumber,
17
- :key => ctrl.key,
14
+ type: ctrl.class.to_s,
15
+ shared_bus: ctrl.sharedBus.to_s,
16
+ unit_number: ctrl.scsiCtlrUnitNumber,
17
+ key: ctrl.key
18
18
  }
19
19
  end
20
20
  end
21
21
  end
22
+ class Mock
23
+ def list_vm_scsi_controllers(vm_id)
24
+ raise Fog::Compute::Vsphere::NotFound, 'VM not Found' unless data[:servers].key?(vm_id)
25
+ return [] unless data[:servers][vm_id].key?('scsi_controllers')
26
+ data[:servers][vm_id]['scsi_controllers'].map { |h| h.merge(server_id: vm_id) }
27
+ end
28
+ end
22
29
  end
23
30
  end
24
31
  end
@@ -38,8 +38,7 @@ module Fog
38
38
  :size => vol.capacityInKB,
39
39
  :name => vol.deviceInfo.label,
40
40
  :key => vol.key,
41
- :unit_number => vol.unitNumber,
42
- :controller_key => vol.controllerKey
41
+ :unit_number => vol.unitNumber
43
42
  }
44
43
  end
45
44
  end
@@ -3,13 +3,25 @@ module Fog
3
3
  class Vsphere
4
4
  class Real
5
5
  def add_vm_controller(controller)
6
- vm_reconfig_hardware('instance_uuid' => controller.server_id, 'hardware_spec' => {'deviceChange'=>[create_controller(controller)]})
6
+ options = {}
7
+ options[:operation] = 'add'
8
+ options[:type] = controller.type
9
+ options[:key] = controller.key
10
+ options[:shared] = controller.shared_bus
11
+
12
+ vm_reconfig_hardware('instance_uuid' => controller.server_id, 'hardware_spec' => {'deviceChange'=>[create_controller(options)]})
7
13
  end
8
14
  end
9
15
 
10
16
  class Mock
11
17
  def add_vm_controller(controller)
12
- vm_reconfig_hardware('instance_uuid' => controller.server_id, 'hardware_spec' => {'deviceChange'=>[create_controller(controller)]})
18
+ options = {}
19
+ options[:operation] = 'add'
20
+ options[:type] = RbVmomi::VIM.VirtualLsiLogicController.class
21
+ options[:key] = 1000
22
+ options[:shared] = false
23
+
24
+ vm_reconfig_hardware('instance_uuid' => controller.server_id, 'hardware_spec' => {'deviceChange'=>[create_controller(options)]})
13
25
  end
14
26
  end
15
27
  end