fog-softlayer 1.1.0 → 1.1.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.
- checksums.yaml +4 -4
- data/examples/compute.md +450 -444
- data/lib/fog/softlayer/models/compute/server.rb +628 -628
- data/lib/fog/softlayer/version.rb +1 -1
- metadata +2 -2
@@ -1,628 +1,628 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
-
# © Copyright IBM Corporation 2014.
|
4
|
-
#
|
5
|
-
# LICENSE: MIT (http://opensource.org/licenses/MIT)
|
6
|
-
#
|
7
|
-
|
8
|
-
require 'fog/compute/models/server'
|
9
|
-
|
10
|
-
module Fog
|
11
|
-
module Compute
|
12
|
-
class Softlayer
|
13
|
-
|
14
|
-
class Server < Fog::Compute::Server
|
15
|
-
|
16
|
-
identity :id, :type => :integer
|
17
|
-
attribute :name, :aliases => 'hostname'
|
18
|
-
attribute :domain
|
19
|
-
attribute :fqdn, :aliases => 'fullyQualifiedDomainName'
|
20
|
-
attribute :cpu, :aliases => ['startCpus', 'processorCoreAmount']
|
21
|
-
attribute :ram, :aliases => ['maxMemory', 'memory']
|
22
|
-
attribute :disk, :aliases => ['blockDevices','hardDrives']
|
23
|
-
attribute :private_ip_address, :aliases => 'primaryBackendIpAddress'
|
24
|
-
attribute :public_ip_address, :aliases => 'primaryIpAddress'
|
25
|
-
attribute :flavor_id
|
26
|
-
attribute :bare_metal, :type => :boolean
|
27
|
-
attribute :os_code
|
28
|
-
attribute :image_id
|
29
|
-
attribute :ephemeral_storage, :aliases => 'localDiskFlag'
|
30
|
-
attribute :key_pairs, :aliases => 'sshKeys'
|
31
|
-
attribute :network_components
|
32
|
-
attribute :fixed_configuration_preset, :aliases => 'fixedConfigurationPreset'
|
33
|
-
|
34
|
-
# Times
|
35
|
-
attribute :created_at, :aliases => ['createDate', 'provisionDate'], :type => :time
|
36
|
-
attribute :last_verified_date, :aliases => 'lastVerifiedDate', :type => :time
|
37
|
-
attribute :metric_poll_date, :aliases => 'metricPollDate', :type => :time
|
38
|
-
attribute :modify_date, :aliases => 'modifyDate', :type => :time
|
39
|
-
|
40
|
-
# Metadata
|
41
|
-
attribute :account_id, :aliases => 'accountId', :type => :integer
|
42
|
-
attribute :datacenter, :aliases => 'datacenter'
|
43
|
-
attribute :single_tenant, :aliases => 'dedicatedAccountHostOnlyFlag'
|
44
|
-
attribute :global_identifier, :aliases => 'globalIdentifier'
|
45
|
-
attribute :hourly_billing_flag, :aliases => 'hourlyBillingFlag'
|
46
|
-
attribute :tags, :aliases => 'tagReferences'
|
47
|
-
attribute :private_network_only, :aliases => 'privateNetworkOnlyFlag'
|
48
|
-
attribute :user_data, :aliases => 'userData'
|
49
|
-
attribute :uid, :aliases => 'globalIdentifier'
|
50
|
-
attribute :provision_script, :aliases => 'postInstallScriptUri'
|
51
|
-
|
52
|
-
def initialize(attributes = {})
|
53
|
-
# Forces every request inject bare_metal parameter
|
54
|
-
raise Exception if attributes[:collection].nil? and attributes['bare_metal'].nil?
|
55
|
-
super(attributes)
|
56
|
-
set_defaults
|
57
|
-
end
|
58
|
-
|
59
|
-
def add_tags(tags)
|
60
|
-
requires :id
|
61
|
-
raise ArgumentError, "Tags argument for #{self.class.name}##{__method__} must be Array." unless tags.is_a?(Array)
|
62
|
-
tags.each do |tag|
|
63
|
-
service.tags.new(:resource_id => self.id, :name => tag).save
|
64
|
-
end
|
65
|
-
self.reload
|
66
|
-
true
|
67
|
-
end
|
68
|
-
|
69
|
-
def bare_metal?
|
70
|
-
bare_metal
|
71
|
-
end
|
72
|
-
|
73
|
-
def bare_metal
|
74
|
-
@bare_metal
|
75
|
-
end
|
76
|
-
|
77
|
-
def datacenter=(name)
|
78
|
-
name = name['name'] if name.is_a?(Hash)
|
79
|
-
attributes[:datacenter] = { :name => name }
|
80
|
-
end
|
81
|
-
|
82
|
-
def datacenter
|
83
|
-
attributes[:datacenter][:name] unless attributes[:datacenter].nil?
|
84
|
-
end
|
85
|
-
|
86
|
-
def delete_tags(tags)
|
87
|
-
requires :id
|
88
|
-
raise ArgumentError, "Tags argument for #{self.class.name}##{__method__} must be Array." unless tags.is_a?(Array)
|
89
|
-
tags.each do |tag|
|
90
|
-
service.tags.new(:resource_id => self.id, :name => tag).destroy
|
91
|
-
end
|
92
|
-
self.reload
|
93
|
-
true
|
94
|
-
end
|
95
|
-
|
96
|
-
def destroy
|
97
|
-
requires :id
|
98
|
-
request = bare_metal? ? :delete_bare_metal_server : :delete_vm
|
99
|
-
response = service.send(request, self.id)
|
100
|
-
response.body
|
101
|
-
end
|
102
|
-
|
103
|
-
def dns_name
|
104
|
-
fqdn
|
105
|
-
end
|
106
|
-
|
107
|
-
def image_id=(uuid)
|
108
|
-
attributes[:image_id] = {:globalIdentifier => uuid}
|
109
|
-
end
|
110
|
-
|
111
|
-
def image_id
|
112
|
-
attributes[:image_id][:globalIdentifier] unless attributes[:image_id].nil?
|
113
|
-
end
|
114
|
-
|
115
|
-
def name=(set)
|
116
|
-
attributes[:hostname] = set
|
117
|
-
end
|
118
|
-
|
119
|
-
def name
|
120
|
-
attributes[:hostname]
|
121
|
-
end
|
122
|
-
|
123
|
-
def pre_save
|
124
|
-
extract_flavor
|
125
|
-
self.bare_metal = true if attributes[:fixed_configuration_preset] and not bare_metal?
|
126
|
-
validate_attributes
|
127
|
-
if self.vlan
|
128
|
-
attributes[:vlan] = { :networkVlan => { :id => self.vlan.id } }
|
129
|
-
end
|
130
|
-
if self.private_vlan
|
131
|
-
attributes[:private_vlan] = { :networkVlan => { :id => self.private_vlan.id } }
|
132
|
-
end
|
133
|
-
if self.key_pairs
|
134
|
-
attributes[:key_pairs].map! { |key| { :id => key.id } }
|
135
|
-
end
|
136
|
-
if self.network_components
|
137
|
-
self.network_components = self.network_components.map do |component|
|
138
|
-
component[
|
139
|
-
component[
|
140
|
-
component
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
if attributes[:fixed_configuration_preset].is_a? String
|
145
|
-
attributes[:fixedConfigurationPreset] = {:keyName => attributes.delete(:fixed_configuration_preset)}
|
146
|
-
end
|
147
|
-
|
148
|
-
remap_attributes(attributes, attributes_mapping)
|
149
|
-
clean_attributes
|
150
|
-
end
|
151
|
-
|
152
|
-
def private_ip # maintain backward compatibility with <0.3.13
|
153
|
-
private_ip_address
|
154
|
-
end
|
155
|
-
|
156
|
-
def public_ip # maintain backward compatibility with <0.3.13
|
157
|
-
public_ip_address
|
158
|
-
end
|
159
|
-
|
160
|
-
def os_code
|
161
|
-
attributes['operatingSystem']['softwareLicense']['softwareDescription']['referenceCode'] if attributes['operatingSystem']
|
162
|
-
end
|
163
|
-
|
164
|
-
def private_vlan
|
165
|
-
attributes[:private_vlan] ||= _get_private_vlan
|
166
|
-
end
|
167
|
-
|
168
|
-
def private_vlan=(value)
|
169
|
-
unless value.is_a?(Integer) or value.is_a?(Fog::Network::Softlayer::Network)
|
170
|
-
raise ArgumentError, "vlan argument for #{self.class.name}##{__method__} must be Integer or Fog::Network::Softlayer::Network."
|
171
|
-
end
|
172
|
-
value = network_connection.networks.get(value) if value.is_a?(Integer)
|
173
|
-
attributes[:private_vlan] = value
|
174
|
-
end
|
175
|
-
|
176
|
-
# reload the OS on a server (method name reload was already taken)
|
177
|
-
def relaunch!
|
178
|
-
requires :id
|
179
|
-
body = [ "FORCE", {}]
|
180
|
-
body[1][:sshKeyIds] = key_pairs.map {|kp| kp.id} unless key_pairs.empty?
|
181
|
-
type = bare_metal? ? :hardware_server : :virtual_guest
|
182
|
-
status = service.request(type, "#{id}/reloadOperatingSystem", :body => body, :http_method => :post).status
|
183
|
-
wait_for { not ready? } # block until the relaunch has begun
|
184
|
-
[200, 201].include?(status)
|
185
|
-
end
|
186
|
-
|
187
|
-
def key_pairs
|
188
|
-
attributes[:key_pairs]
|
189
|
-
end
|
190
|
-
|
191
|
-
def key_pairs=(keys)
|
192
|
-
raise ArgumentError, "Argument #{local_variables.first.to_s} for #{self.class.name}##{__method__} must be Array." unless keys.is_a?(Array)
|
193
|
-
attributes[:key_pairs] = []
|
194
|
-
keys.map do |key|
|
195
|
-
## This was nice but causing an intolerable number of requests on an account with lots of keys.
|
196
|
-
## ToDo: something better...
|
197
|
-
#key = self.symbolize_keys(key) if key.is_a?(Hash)
|
198
|
-
#unless key.is_a?(Fog::Compute::Softlayer::KeyPair) or (key.is_a?(Hash) and key[:id])
|
199
|
-
# raise ArgumentError, "Elements of keys array for #{self.class.name}##{__method__} must be a Hash with key 'id', or Fog::Compute::Softlayer::KeyPair"
|
200
|
-
#end
|
201
|
-
#key = service.key_pairs.get(key[:id]) unless key.is_a?(Fog::Compute::Softlayer::KeyPair)
|
202
|
-
attributes[:key_pairs] << key
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def vlan
|
207
|
-
attributes[:vlan] ||= _get_vlan
|
208
|
-
end
|
209
|
-
|
210
|
-
def vlan=(value)
|
211
|
-
unless value.is_a?(Integer) or value.is_a?(Fog::Network::Softlayer::Network)
|
212
|
-
raise ArgumentError, "vlan argument for #{self.class.name}##{__method__} must be Integer or Fog::Network::Softlayer::Network."
|
213
|
-
end
|
214
|
-
value = network_connection.networks.get(value) if value.is_a?(Integer)
|
215
|
-
attributes[:vlan] = value
|
216
|
-
end
|
217
|
-
|
218
|
-
def ram=(set)
|
219
|
-
if set.is_a?(Array) and set.first['hardwareComponentModel']
|
220
|
-
set = 1024 * set.first['hardwareComponentModel']['capacity'].to_i
|
221
|
-
end
|
222
|
-
attributes[:ram] = set
|
223
|
-
end
|
224
|
-
|
225
|
-
# @params value [String]
|
226
|
-
def user_data=(value)
|
227
|
-
attributes[:user_data] = [{'value' => value}]
|
228
|
-
end
|
229
|
-
|
230
|
-
def user_data
|
231
|
-
attributes[:user_data]
|
232
|
-
end
|
233
|
-
|
234
|
-
def provision_script=(value)
|
235
|
-
attributes[:provision_script] = value
|
236
|
-
end
|
237
|
-
|
238
|
-
def provision_script
|
239
|
-
attributes[:provision_script]
|
240
|
-
end
|
241
|
-
|
242
|
-
def network_components
|
243
|
-
if id
|
244
|
-
(public_network_components << private_network_components).flatten
|
245
|
-
else
|
246
|
-
attributes[:network_components]
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
def public_network_components
|
251
|
-
if attributes['frontendNetworkComponents']
|
252
|
-
attributes['frontendNetworkComponents'].map { |n| Fog::Compute::Softlayer::NetworkComponent.new(n) }
|
253
|
-
else
|
254
|
-
[]
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
def private_network_components
|
259
|
-
if attributes['backendNetworkComponents']
|
260
|
-
attributes['backendNetworkComponents'].map { |n| Fog::Compute::Softlayer::NetworkComponent.new(n) }
|
261
|
-
else
|
262
|
-
[]
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
def ready?
|
267
|
-
begin
|
268
|
-
if bare_metal?
|
269
|
-
state == "on"
|
270
|
-
else
|
271
|
-
state == "Running"
|
272
|
-
end
|
273
|
-
rescue Excon::Errors::InternalServerError => e
|
274
|
-
false
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
def reboot(use_hard_reboot = true)
|
279
|
-
# requires :id # TODO: debug why this breaks the tests on bare metal and uncomment this
|
280
|
-
if bare_metal?
|
281
|
-
service.reboot_bare_metal_server(id, use_hard_reboot)
|
282
|
-
else
|
283
|
-
service.reboot_vm(id, use_hard_reboot)
|
284
|
-
end
|
285
|
-
true
|
286
|
-
end
|
287
|
-
|
288
|
-
def ssh_password
|
289
|
-
requires :id
|
290
|
-
service_path = bare_metal? ? :hardware_server : :virtual_guest
|
291
|
-
@sshpass ||= service.request(service_path, id, :query => 'objectMask=mask[id,operatingSystem.passwords[password]]').body
|
292
|
-
@sshpass['operatingSystem']['passwords'][0]['password'] unless @sshpass['operatingSystem'].nil? or @sshpass['operatingSystem']['passwords'].empty?
|
293
|
-
end
|
294
|
-
|
295
|
-
def snapshot
|
296
|
-
# TODO: implement
|
297
|
-
end
|
298
|
-
|
299
|
-
def start
|
300
|
-
# requires :id # TODO: debug why this breaks the tests on bare metal and uncomment this
|
301
|
-
if bare_metal?
|
302
|
-
service.power_on_bare_metal_server(id)
|
303
|
-
else
|
304
|
-
service.power_on_vm(id)
|
305
|
-
end
|
306
|
-
true
|
307
|
-
end
|
308
|
-
|
309
|
-
# Hard power off
|
310
|
-
def stop
|
311
|
-
# requires :id # TODO: debug why this breaks the tests on bare metal and uncomment this
|
312
|
-
if bare_metal?
|
313
|
-
service.power_off_bare_metal_server(id)
|
314
|
-
else
|
315
|
-
service.power_off_vm(id, true)
|
316
|
-
end
|
317
|
-
true
|
318
|
-
end
|
319
|
-
|
320
|
-
# Soft power off
|
321
|
-
def shutdown
|
322
|
-
# requires :id # TODO: debug why this breaks the tests on bare metal and uncomment this
|
323
|
-
if bare_metal?
|
324
|
-
raise Fog::Errors::Error.new('Shutdown not supported on baremetal servers. Use #stop.')
|
325
|
-
else
|
326
|
-
service.power_off_vm(id, false)
|
327
|
-
end
|
328
|
-
true
|
329
|
-
end
|
330
|
-
|
331
|
-
def state
|
332
|
-
if bare_metal?
|
333
|
-
service.request(:hardware_server, "#{id}/getServerPowerState").body
|
334
|
-
else
|
335
|
-
service.request(:virtual_guest, "#{id}/getPowerState").body['name']
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
# Creates server
|
340
|
-
# * requires attributes: :name, :domain, and :flavor_id OR (:cpu_count && :ram && :disks)
|
341
|
-
#
|
342
|
-
# @note You should use servers.create to create servers instead calling this method directly
|
343
|
-
#
|
344
|
-
# * State Transitions
|
345
|
-
# * BUILD -> ACTIVE
|
346
|
-
# * BUILD -> ERROR (on error)
|
347
|
-
def save
|
348
|
-
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
|
349
|
-
copy = self.dup
|
350
|
-
copy.pre_save
|
351
|
-
|
352
|
-
data = if copy.bare_metal?
|
353
|
-
service.create_bare_metal_server(copy.attributes).body
|
354
|
-
else
|
355
|
-
service.create_vm(copy.attributes).body.first
|
356
|
-
end
|
357
|
-
|
358
|
-
data.delete("bare_metal")
|
359
|
-
merge_attributes(data)
|
360
|
-
true
|
361
|
-
end
|
362
|
-
|
363
|
-
def tags
|
364
|
-
attributes[:tags].map { |i| i['tag']['name'] if i['tag'] }.compact if attributes[:tags]
|
365
|
-
end
|
366
|
-
|
367
|
-
def get_active_tickets
|
368
|
-
return service.get_bare_metal_active_tickets(id).body if bare_metal?
|
369
|
-
service.get_virtual_guest_active_tickets(id).body
|
370
|
-
end
|
371
|
-
|
372
|
-
def get_users
|
373
|
-
return service.get_bare_metal_users(id).body if bare_metal?
|
374
|
-
service.get_virtual_guest_users(id).body
|
375
|
-
end
|
376
|
-
|
377
|
-
def get_upgrade_options
|
378
|
-
return service.get_bare_metal_upgrade_item_prices(id).body if bare_metal?
|
379
|
-
service.get_virtual_guest_upgrade_item_prices(id).body
|
380
|
-
end
|
381
|
-
|
382
|
-
def update(update_attributes)
|
383
|
-
raise ArgumentError if update_attributes.nil?
|
384
|
-
product_connection
|
385
|
-
prices = get_item_prices_id(update_attributes)
|
386
|
-
order = generate_upgrade_order(prices, update_attributes[:time] || update_attributes[:maintenance_window])
|
387
|
-
@product_conn.place_order(order).body
|
388
|
-
end
|
389
|
-
|
390
|
-
def generate_order_template
|
391
|
-
copy = self.dup
|
392
|
-
copy.pre_save
|
393
|
-
return service.generate_bare_metal_order_template(copy.attributes).body if copy.bare_metal?
|
394
|
-
service.generate_virtual_guest_order_template(copy.attributes).body
|
395
|
-
end
|
396
|
-
|
397
|
-
def wait_for_id(timeout=14400, delay=30)
|
398
|
-
# Cannot use self.wait_for because it calls reload which requires
|
399
|
-
# self.id which is not initially available for bare metal.
|
400
|
-
filterStr = Fog::JSON.encode({
|
401
|
-
"hardware" => {
|
402
|
-
"hostname" => {
|
403
|
-
"operation" => self.name,
|
404
|
-
},
|
405
|
-
"domain" => {
|
406
|
-
"operation" => self.domain,
|
407
|
-
},
|
408
|
-
"globalIdentifier" => {
|
409
|
-
"operation" => self.uid,
|
410
|
-
},
|
411
|
-
}
|
412
|
-
})
|
413
|
-
|
414
|
-
Fog.wait_for(timeout, delay) do
|
415
|
-
res = service.request(:account, 'getHardware', :query => {
|
416
|
-
:objectMask => 'mask[id,fullyQualifiedDomainName,provisionDate,hardwareStatus,lastTransaction[elapsedSeconds,transactionStatus[friendlyName]],operatingSystem[id,passwords[password,username]]]',
|
417
|
-
:objectFilter => filterStr,
|
418
|
-
})
|
419
|
-
|
420
|
-
server = res.body.first
|
421
|
-
|
422
|
-
yield server if block_given?
|
423
|
-
|
424
|
-
if server and server["provisionDate"]
|
425
|
-
attributes[:id] = server['id']
|
426
|
-
true
|
427
|
-
else
|
428
|
-
false
|
429
|
-
end
|
430
|
-
end
|
431
|
-
|
432
|
-
self.reload
|
433
|
-
true
|
434
|
-
end
|
435
|
-
|
436
|
-
private
|
437
|
-
|
438
|
-
def network_connection
|
439
|
-
@network_conn ||= Fog::Network.new(
|
440
|
-
:provider => :softlayer,
|
441
|
-
:softlayer_username => service.instance_variable_get(:@softlayer_username),
|
442
|
-
:softlayer_api_key => service.instance_variable_get(:@softlayer_api_key)
|
443
|
-
)
|
444
|
-
end
|
445
|
-
|
446
|
-
def product_connection
|
447
|
-
if Fog.mock?
|
448
|
-
@product_conn = Fog::Softlayer::Product.new(
|
449
|
-
:provider => :softlayer,
|
450
|
-
:softlayer_username => service.instance_variable_get(:@credentials)[:username],
|
451
|
-
:softlayer_api_key => service.instance_variable_get(:@credentials)[:api_key]
|
452
|
-
)
|
453
|
-
end
|
454
|
-
@product_conn ||= Fog::Softlayer::Product.new(
|
455
|
-
:provider => :softlayer,
|
456
|
-
:softlayer_username => service.instance_variable_get(:@softlayer_username),
|
457
|
-
:softlayer_api_key => service.instance_variable_get(:@softlayer_api_key)
|
458
|
-
)
|
459
|
-
end
|
460
|
-
|
461
|
-
def _get_private_vlan
|
462
|
-
if self.id
|
463
|
-
vlan_id = if bare_metal?
|
464
|
-
service.request(:hardware_server, "#{self.id}/get_private_vlan").body['id']
|
465
|
-
else
|
466
|
-
service.request(:virtual_guest, self.id, :query => 'objectMask=primaryBackendNetworkComponent.networkVlan').body['primaryBackendNetworkComponent']['networkVlan']['id']
|
467
|
-
end
|
468
|
-
network_connection.networks.get(vlan_id)
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
def _get_vlan
|
473
|
-
if self.id
|
474
|
-
vlan_id = if bare_metal?
|
475
|
-
service.request(:hardware_server, "#{self.id}/get_public_vlan").body['id']
|
476
|
-
else
|
477
|
-
service.request(:virtual_guest, self.id, :query => 'objectMask=primaryNetworkComponent.networkVlan').body['primaryNetworkComponent']['networkVlan']['id']
|
478
|
-
end
|
479
|
-
network_connection.networks.get(vlan_id)
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
##
|
484
|
-
# Generate mapping for use with remap_attributes
|
485
|
-
def attributes_mapping
|
486
|
-
common = {
|
487
|
-
:hourly_billing_flag => :hourlyBillingFlag,
|
488
|
-
:os_code => :operatingSystemReferenceCode,
|
489
|
-
:vlan => :primaryNetworkComponent,
|
490
|
-
:private_vlan => :primaryBackendNetworkComponent,
|
491
|
-
:key_pairs => :sshKeys,
|
492
|
-
:private_network_only => :privateNetworkOnlyFlag,
|
493
|
-
:user_data => :userData,
|
494
|
-
:provision_script => :postInstallScriptUri,
|
495
|
-
:network_components => :networkComponents,
|
496
|
-
}
|
497
|
-
|
498
|
-
conditional = if bare_metal?
|
499
|
-
{
|
500
|
-
:cpu => :processorCoreAmount,
|
501
|
-
:ram => :memoryCapacity,
|
502
|
-
:disk => :hardDrives,
|
503
|
-
:bare_metal => :bareMetalInstanceFlag,
|
504
|
-
:fixed_configuration_preset => :fixedConfigurationPreset,
|
505
|
-
}
|
506
|
-
else
|
507
|
-
{
|
508
|
-
:cpu => :startCpus,
|
509
|
-
:ram => :maxMemory,
|
510
|
-
:disk => :blockDevices,
|
511
|
-
:image_id => :blockDeviceTemplateGroup,
|
512
|
-
:ephemeral_storage => :localDiskFlag,
|
513
|
-
}
|
514
|
-
end
|
515
|
-
common.merge(conditional)
|
516
|
-
end
|
517
|
-
|
518
|
-
def bare_metal=(set)
|
519
|
-
return @bare_metal if set == @bare_metal
|
520
|
-
raise Exception, "Bare metal flag has already been set" unless @bare_metal.nil?
|
521
|
-
@bare_metal = case set
|
522
|
-
when false, 'false', 0, nil, ''
|
523
|
-
attributes[:bare_metal] = false
|
524
|
-
else
|
525
|
-
attributes[:bare_metal] = true
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
##
|
530
|
-
# Remove model attributes that aren't expected by the SoftLayer API
|
531
|
-
def clean_attributes
|
532
|
-
attributes.delete(:bare_metal)
|
533
|
-
attributes.delete(:flavor_id)
|
534
|
-
attributes.delete(:ephemeral_storage)
|
535
|
-
attributes.delete(:tags) if bare_metal?
|
536
|
-
end
|
537
|
-
|
538
|
-
##
|
539
|
-
# Expand a "flavor" into cpu, ram, and disk attributes
|
540
|
-
def extract_flavor
|
541
|
-
if attributes[:flavor_id]
|
542
|
-
flavor = @service.flavors.get(attributes[:flavor_id])
|
543
|
-
flavor.nil? and Fog::Errors::Error.new("Unrecognized flavor in #{self.class}##{__method__}")
|
544
|
-
attributes[:cpu] = flavor.cpu
|
545
|
-
attributes[:ram] = flavor.ram
|
546
|
-
attributes[:disk] = flavor.disk unless attributes[:image_id]
|
547
|
-
if bare_metal?
|
548
|
-
value = flavor.disk.first['diskImage']['capacity'] < 500 ? 250 : 500
|
549
|
-
attributes[:disk] = [{'capacity'=>value}]
|
550
|
-
attributes[:ram] = attributes[:ram] / 1024 if attributes[:ram] > 64
|
551
|
-
end
|
552
|
-
end
|
553
|
-
end
|
554
|
-
|
555
|
-
def validate_attributes
|
556
|
-
requires :name, :domain, :datacenter
|
557
|
-
if attributes[:fixed_configuration_preset]
|
558
|
-
requires :os_code
|
559
|
-
else
|
560
|
-
requires :cpu, :ram
|
561
|
-
requires_one :os_code, :image_id
|
562
|
-
requires_one :disk, :image_id
|
563
|
-
end
|
564
|
-
bare_metal? and image_id and raise ArgumentError, "Bare Metal Cloud does not support booting from Image"
|
565
|
-
end
|
566
|
-
|
567
|
-
def set_defaults
|
568
|
-
attributes[:hourly_billing_flag] = true if attributes[:hourly_billing_flag].nil?
|
569
|
-
attributes[:ephemeral_storage] = false if attributes[:ephemeral_storage].nil?
|
570
|
-
attributes[:domain] = service.softlayer_default_domain if service.softlayer_default_domain and attributes[:domain].nil?
|
571
|
-
self.datacenter = service.softlayer_default_datacenter if service.softlayer_default_datacenter and attributes[:datacenter].nil?
|
572
|
-
end
|
573
|
-
|
574
|
-
def get_item_prices_id_by_value(item_price_array, category, value)
|
575
|
-
item_prices = item_price_array.select { |item_price| item_price["categories"].find { |category_hash| category_hash["categoryCode"] == category } }
|
576
|
-
item_price = item_prices.find { |item_price| item_price['item']['capacity'] == value.to_s }
|
577
|
-
item_price.nil? ? "" : item_price["id"]
|
578
|
-
end
|
579
|
-
|
580
|
-
def get_item_prices_id(update_attributes)
|
581
|
-
item_price_array = get_upgrade_options
|
582
|
-
update_attributes.delete(:time)
|
583
|
-
update_attributes.delete(:maintenance_window)
|
584
|
-
update_attributes.map { |key, value| { :id => get_item_prices_id_by_value(item_price_array, key.to_s, value) } }
|
585
|
-
end
|
586
|
-
|
587
|
-
def bm_upgrade_order_template(value)
|
588
|
-
{
|
589
|
-
:complexType => 'SoftLayer_Container_Product_Order_Hardware_Server_Upgrade',
|
590
|
-
:hardware => [
|
591
|
-
{
|
592
|
-
:id => id
|
593
|
-
}
|
594
|
-
],
|
595
|
-
:properties => [
|
596
|
-
{
|
597
|
-
:name => 'MAINTENANCE_WINDOW_ID',
|
598
|
-
:value => value
|
599
|
-
}
|
600
|
-
]
|
601
|
-
}
|
602
|
-
end
|
603
|
-
|
604
|
-
def vm_upgrade_order_template(time)
|
605
|
-
{
|
606
|
-
:complexType => 'SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade',
|
607
|
-
:virtualGuests => [
|
608
|
-
{
|
609
|
-
:id => id
|
610
|
-
}
|
611
|
-
],
|
612
|
-
:properties => [
|
613
|
-
{
|
614
|
-
:name => 'MAINTENANCE_WINDOW',
|
615
|
-
:value => (time.nil? || time.empty?) ? Time.now.iso8601 : time.iso8601
|
616
|
-
}
|
617
|
-
]
|
618
|
-
}
|
619
|
-
end
|
620
|
-
|
621
|
-
def generate_upgrade_order(prices, value)
|
622
|
-
return bm_upgrade_order_template(value).merge({ :prices => prices }) if bare_metal?
|
623
|
-
vm_upgrade_order_template(value).merge({ :prices => prices })
|
624
|
-
end
|
625
|
-
end
|
626
|
-
end
|
627
|
-
end
|
628
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: MIT (http://opensource.org/licenses/MIT)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'fog/compute/models/server'
|
9
|
+
|
10
|
+
module Fog
|
11
|
+
module Compute
|
12
|
+
class Softlayer
|
13
|
+
|
14
|
+
class Server < Fog::Compute::Server
|
15
|
+
|
16
|
+
identity :id, :type => :integer
|
17
|
+
attribute :name, :aliases => 'hostname'
|
18
|
+
attribute :domain
|
19
|
+
attribute :fqdn, :aliases => 'fullyQualifiedDomainName'
|
20
|
+
attribute :cpu, :aliases => ['startCpus', 'processorCoreAmount']
|
21
|
+
attribute :ram, :aliases => ['maxMemory', 'memory']
|
22
|
+
attribute :disk, :aliases => ['blockDevices','hardDrives']
|
23
|
+
attribute :private_ip_address, :aliases => 'primaryBackendIpAddress'
|
24
|
+
attribute :public_ip_address, :aliases => 'primaryIpAddress'
|
25
|
+
attribute :flavor_id
|
26
|
+
attribute :bare_metal, :type => :boolean
|
27
|
+
attribute :os_code
|
28
|
+
attribute :image_id
|
29
|
+
attribute :ephemeral_storage, :aliases => 'localDiskFlag'
|
30
|
+
attribute :key_pairs, :aliases => 'sshKeys'
|
31
|
+
attribute :network_components
|
32
|
+
attribute :fixed_configuration_preset, :aliases => 'fixedConfigurationPreset'
|
33
|
+
|
34
|
+
# Times
|
35
|
+
attribute :created_at, :aliases => ['createDate', 'provisionDate'], :type => :time
|
36
|
+
attribute :last_verified_date, :aliases => 'lastVerifiedDate', :type => :time
|
37
|
+
attribute :metric_poll_date, :aliases => 'metricPollDate', :type => :time
|
38
|
+
attribute :modify_date, :aliases => 'modifyDate', :type => :time
|
39
|
+
|
40
|
+
# Metadata
|
41
|
+
attribute :account_id, :aliases => 'accountId', :type => :integer
|
42
|
+
attribute :datacenter, :aliases => 'datacenter'
|
43
|
+
attribute :single_tenant, :aliases => 'dedicatedAccountHostOnlyFlag'
|
44
|
+
attribute :global_identifier, :aliases => 'globalIdentifier'
|
45
|
+
attribute :hourly_billing_flag, :aliases => 'hourlyBillingFlag'
|
46
|
+
attribute :tags, :aliases => 'tagReferences'
|
47
|
+
attribute :private_network_only, :aliases => 'privateNetworkOnlyFlag'
|
48
|
+
attribute :user_data, :aliases => 'userData'
|
49
|
+
attribute :uid, :aliases => 'globalIdentifier'
|
50
|
+
attribute :provision_script, :aliases => 'postInstallScriptUri'
|
51
|
+
|
52
|
+
def initialize(attributes = {})
|
53
|
+
# Forces every request inject bare_metal parameter
|
54
|
+
raise Exception if attributes[:collection].nil? and attributes['bare_metal'].nil?
|
55
|
+
super(attributes)
|
56
|
+
set_defaults
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_tags(tags)
|
60
|
+
requires :id
|
61
|
+
raise ArgumentError, "Tags argument for #{self.class.name}##{__method__} must be Array." unless tags.is_a?(Array)
|
62
|
+
tags.each do |tag|
|
63
|
+
service.tags.new(:resource_id => self.id, :name => tag).save
|
64
|
+
end
|
65
|
+
self.reload
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def bare_metal?
|
70
|
+
bare_metal
|
71
|
+
end
|
72
|
+
|
73
|
+
def bare_metal
|
74
|
+
@bare_metal
|
75
|
+
end
|
76
|
+
|
77
|
+
def datacenter=(name)
|
78
|
+
name = name['name'] if name.is_a?(Hash)
|
79
|
+
attributes[:datacenter] = { :name => name }
|
80
|
+
end
|
81
|
+
|
82
|
+
def datacenter
|
83
|
+
attributes[:datacenter][:name] unless attributes[:datacenter].nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
def delete_tags(tags)
|
87
|
+
requires :id
|
88
|
+
raise ArgumentError, "Tags argument for #{self.class.name}##{__method__} must be Array." unless tags.is_a?(Array)
|
89
|
+
tags.each do |tag|
|
90
|
+
service.tags.new(:resource_id => self.id, :name => tag).destroy
|
91
|
+
end
|
92
|
+
self.reload
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
def destroy
|
97
|
+
requires :id
|
98
|
+
request = bare_metal? ? :delete_bare_metal_server : :delete_vm
|
99
|
+
response = service.send(request, self.id)
|
100
|
+
response.body
|
101
|
+
end
|
102
|
+
|
103
|
+
def dns_name
|
104
|
+
fqdn
|
105
|
+
end
|
106
|
+
|
107
|
+
def image_id=(uuid)
|
108
|
+
attributes[:image_id] = {:globalIdentifier => uuid}
|
109
|
+
end
|
110
|
+
|
111
|
+
def image_id
|
112
|
+
attributes[:image_id][:globalIdentifier] unless attributes[:image_id].nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
def name=(set)
|
116
|
+
attributes[:hostname] = set
|
117
|
+
end
|
118
|
+
|
119
|
+
def name
|
120
|
+
attributes[:hostname]
|
121
|
+
end
|
122
|
+
|
123
|
+
def pre_save
|
124
|
+
extract_flavor
|
125
|
+
self.bare_metal = true if attributes[:fixed_configuration_preset] and not bare_metal?
|
126
|
+
validate_attributes
|
127
|
+
if self.vlan
|
128
|
+
attributes[:vlan] = { :networkVlan => { :id => self.vlan.id } }
|
129
|
+
end
|
130
|
+
if self.private_vlan
|
131
|
+
attributes[:private_vlan] = { :networkVlan => { :id => self.private_vlan.id } }
|
132
|
+
end
|
133
|
+
if self.key_pairs
|
134
|
+
attributes[:key_pairs].map! { |key| { :id => key.id } }
|
135
|
+
end
|
136
|
+
if self.network_components
|
137
|
+
self.network_components = self.network_components.map do |component|
|
138
|
+
component['maxSpeed'] = component.delete('speed') if component['speed']
|
139
|
+
component['maxSpeed'] = component.delete('max_speed') if component['max_speed']
|
140
|
+
component
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
if attributes[:fixed_configuration_preset].is_a? String
|
145
|
+
attributes[:fixedConfigurationPreset] = {:keyName => attributes.delete(:fixed_configuration_preset)}
|
146
|
+
end
|
147
|
+
|
148
|
+
remap_attributes(attributes, attributes_mapping)
|
149
|
+
clean_attributes
|
150
|
+
end
|
151
|
+
|
152
|
+
def private_ip # maintain backward compatibility with <0.3.13
|
153
|
+
private_ip_address
|
154
|
+
end
|
155
|
+
|
156
|
+
def public_ip # maintain backward compatibility with <0.3.13
|
157
|
+
public_ip_address
|
158
|
+
end
|
159
|
+
|
160
|
+
def os_code
|
161
|
+
attributes['operatingSystem']['softwareLicense']['softwareDescription']['referenceCode'] if attributes['operatingSystem']
|
162
|
+
end
|
163
|
+
|
164
|
+
def private_vlan
|
165
|
+
attributes[:private_vlan] ||= _get_private_vlan
|
166
|
+
end
|
167
|
+
|
168
|
+
def private_vlan=(value)
|
169
|
+
unless value.is_a?(Integer) or value.is_a?(Fog::Network::Softlayer::Network)
|
170
|
+
raise ArgumentError, "vlan argument for #{self.class.name}##{__method__} must be Integer or Fog::Network::Softlayer::Network."
|
171
|
+
end
|
172
|
+
value = network_connection.networks.get(value) if value.is_a?(Integer)
|
173
|
+
attributes[:private_vlan] = value
|
174
|
+
end
|
175
|
+
|
176
|
+
# reload the OS on a server (method name reload was already taken)
|
177
|
+
def relaunch!
|
178
|
+
requires :id
|
179
|
+
body = [ "FORCE", {}]
|
180
|
+
body[1][:sshKeyIds] = key_pairs.map {|kp| kp.id} unless key_pairs.empty?
|
181
|
+
type = bare_metal? ? :hardware_server : :virtual_guest
|
182
|
+
status = service.request(type, "#{id}/reloadOperatingSystem", :body => body, :http_method => :post).status
|
183
|
+
wait_for { not ready? } # block until the relaunch has begun
|
184
|
+
[200, 201].include?(status)
|
185
|
+
end
|
186
|
+
|
187
|
+
def key_pairs
|
188
|
+
attributes[:key_pairs]
|
189
|
+
end
|
190
|
+
|
191
|
+
def key_pairs=(keys)
|
192
|
+
raise ArgumentError, "Argument #{local_variables.first.to_s} for #{self.class.name}##{__method__} must be Array." unless keys.is_a?(Array)
|
193
|
+
attributes[:key_pairs] = []
|
194
|
+
keys.map do |key|
|
195
|
+
## This was nice but causing an intolerable number of requests on an account with lots of keys.
|
196
|
+
## ToDo: something better...
|
197
|
+
#key = self.symbolize_keys(key) if key.is_a?(Hash)
|
198
|
+
#unless key.is_a?(Fog::Compute::Softlayer::KeyPair) or (key.is_a?(Hash) and key[:id])
|
199
|
+
# raise ArgumentError, "Elements of keys array for #{self.class.name}##{__method__} must be a Hash with key 'id', or Fog::Compute::Softlayer::KeyPair"
|
200
|
+
#end
|
201
|
+
#key = service.key_pairs.get(key[:id]) unless key.is_a?(Fog::Compute::Softlayer::KeyPair)
|
202
|
+
attributes[:key_pairs] << key
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def vlan
|
207
|
+
attributes[:vlan] ||= _get_vlan
|
208
|
+
end
|
209
|
+
|
210
|
+
def vlan=(value)
|
211
|
+
unless value.is_a?(Integer) or value.is_a?(Fog::Network::Softlayer::Network)
|
212
|
+
raise ArgumentError, "vlan argument for #{self.class.name}##{__method__} must be Integer or Fog::Network::Softlayer::Network."
|
213
|
+
end
|
214
|
+
value = network_connection.networks.get(value) if value.is_a?(Integer)
|
215
|
+
attributes[:vlan] = value
|
216
|
+
end
|
217
|
+
|
218
|
+
def ram=(set)
|
219
|
+
if set.is_a?(Array) and set.first['hardwareComponentModel']
|
220
|
+
set = 1024 * set.first['hardwareComponentModel']['capacity'].to_i
|
221
|
+
end
|
222
|
+
attributes[:ram] = set
|
223
|
+
end
|
224
|
+
|
225
|
+
# @params value [String]
|
226
|
+
def user_data=(value)
|
227
|
+
attributes[:user_data] = [{'value' => value}]
|
228
|
+
end
|
229
|
+
|
230
|
+
def user_data
|
231
|
+
attributes[:user_data]
|
232
|
+
end
|
233
|
+
|
234
|
+
def provision_script=(value)
|
235
|
+
attributes[:provision_script] = value
|
236
|
+
end
|
237
|
+
|
238
|
+
def provision_script
|
239
|
+
attributes[:provision_script]
|
240
|
+
end
|
241
|
+
|
242
|
+
def network_components
|
243
|
+
if id
|
244
|
+
(public_network_components << private_network_components).flatten
|
245
|
+
else
|
246
|
+
attributes[:network_components]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def public_network_components
|
251
|
+
if attributes['frontendNetworkComponents']
|
252
|
+
attributes['frontendNetworkComponents'].map { |n| Fog::Compute::Softlayer::NetworkComponent.new(n) }
|
253
|
+
else
|
254
|
+
[]
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def private_network_components
|
259
|
+
if attributes['backendNetworkComponents']
|
260
|
+
attributes['backendNetworkComponents'].map { |n| Fog::Compute::Softlayer::NetworkComponent.new(n) }
|
261
|
+
else
|
262
|
+
[]
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def ready?
|
267
|
+
begin
|
268
|
+
if bare_metal?
|
269
|
+
state == "on"
|
270
|
+
else
|
271
|
+
state == "Running"
|
272
|
+
end
|
273
|
+
rescue Excon::Errors::InternalServerError => e
|
274
|
+
false
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def reboot(use_hard_reboot = true)
|
279
|
+
# requires :id # TODO: debug why this breaks the tests on bare metal and uncomment this
|
280
|
+
if bare_metal?
|
281
|
+
service.reboot_bare_metal_server(id, use_hard_reboot)
|
282
|
+
else
|
283
|
+
service.reboot_vm(id, use_hard_reboot)
|
284
|
+
end
|
285
|
+
true
|
286
|
+
end
|
287
|
+
|
288
|
+
def ssh_password
|
289
|
+
requires :id
|
290
|
+
service_path = bare_metal? ? :hardware_server : :virtual_guest
|
291
|
+
@sshpass ||= service.request(service_path, id, :query => 'objectMask=mask[id,operatingSystem.passwords[password]]').body
|
292
|
+
@sshpass['operatingSystem']['passwords'][0]['password'] unless @sshpass['operatingSystem'].nil? or @sshpass['operatingSystem']['passwords'].empty?
|
293
|
+
end
|
294
|
+
|
295
|
+
def snapshot
|
296
|
+
# TODO: implement
|
297
|
+
end
|
298
|
+
|
299
|
+
def start
|
300
|
+
# requires :id # TODO: debug why this breaks the tests on bare metal and uncomment this
|
301
|
+
if bare_metal?
|
302
|
+
service.power_on_bare_metal_server(id)
|
303
|
+
else
|
304
|
+
service.power_on_vm(id)
|
305
|
+
end
|
306
|
+
true
|
307
|
+
end
|
308
|
+
|
309
|
+
# Hard power off
|
310
|
+
def stop
|
311
|
+
# requires :id # TODO: debug why this breaks the tests on bare metal and uncomment this
|
312
|
+
if bare_metal?
|
313
|
+
service.power_off_bare_metal_server(id)
|
314
|
+
else
|
315
|
+
service.power_off_vm(id, true)
|
316
|
+
end
|
317
|
+
true
|
318
|
+
end
|
319
|
+
|
320
|
+
# Soft power off
|
321
|
+
def shutdown
|
322
|
+
# requires :id # TODO: debug why this breaks the tests on bare metal and uncomment this
|
323
|
+
if bare_metal?
|
324
|
+
raise Fog::Errors::Error.new('Shutdown not supported on baremetal servers. Use #stop.')
|
325
|
+
else
|
326
|
+
service.power_off_vm(id, false)
|
327
|
+
end
|
328
|
+
true
|
329
|
+
end
|
330
|
+
|
331
|
+
def state
|
332
|
+
if bare_metal?
|
333
|
+
service.request(:hardware_server, "#{id}/getServerPowerState").body
|
334
|
+
else
|
335
|
+
service.request(:virtual_guest, "#{id}/getPowerState").body['name']
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
# Creates server
|
340
|
+
# * requires attributes: :name, :domain, and :flavor_id OR (:cpu_count && :ram && :disks)
|
341
|
+
#
|
342
|
+
# @note You should use servers.create to create servers instead calling this method directly
|
343
|
+
#
|
344
|
+
# * State Transitions
|
345
|
+
# * BUILD -> ACTIVE
|
346
|
+
# * BUILD -> ERROR (on error)
|
347
|
+
def save
|
348
|
+
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
|
349
|
+
copy = self.dup
|
350
|
+
copy.pre_save
|
351
|
+
|
352
|
+
data = if copy.bare_metal?
|
353
|
+
service.create_bare_metal_server(copy.attributes).body
|
354
|
+
else
|
355
|
+
service.create_vm(copy.attributes).body.first
|
356
|
+
end
|
357
|
+
|
358
|
+
data.delete("bare_metal")
|
359
|
+
merge_attributes(data)
|
360
|
+
true
|
361
|
+
end
|
362
|
+
|
363
|
+
def tags
|
364
|
+
attributes[:tags].map { |i| i['tag']['name'] if i['tag'] }.compact if attributes[:tags]
|
365
|
+
end
|
366
|
+
|
367
|
+
def get_active_tickets
|
368
|
+
return service.get_bare_metal_active_tickets(id).body if bare_metal?
|
369
|
+
service.get_virtual_guest_active_tickets(id).body
|
370
|
+
end
|
371
|
+
|
372
|
+
def get_users
|
373
|
+
return service.get_bare_metal_users(id).body if bare_metal?
|
374
|
+
service.get_virtual_guest_users(id).body
|
375
|
+
end
|
376
|
+
|
377
|
+
def get_upgrade_options
|
378
|
+
return service.get_bare_metal_upgrade_item_prices(id).body if bare_metal?
|
379
|
+
service.get_virtual_guest_upgrade_item_prices(id).body
|
380
|
+
end
|
381
|
+
|
382
|
+
def update(update_attributes)
|
383
|
+
raise ArgumentError if update_attributes.nil?
|
384
|
+
product_connection
|
385
|
+
prices = get_item_prices_id(update_attributes)
|
386
|
+
order = generate_upgrade_order(prices, update_attributes[:time] || update_attributes[:maintenance_window])
|
387
|
+
@product_conn.place_order(order).body
|
388
|
+
end
|
389
|
+
|
390
|
+
def generate_order_template
|
391
|
+
copy = self.dup
|
392
|
+
copy.pre_save
|
393
|
+
return service.generate_bare_metal_order_template(copy.attributes).body if copy.bare_metal?
|
394
|
+
service.generate_virtual_guest_order_template(copy.attributes).body
|
395
|
+
end
|
396
|
+
|
397
|
+
def wait_for_id(timeout=14400, delay=30)
|
398
|
+
# Cannot use self.wait_for because it calls reload which requires
|
399
|
+
# self.id which is not initially available for bare metal.
|
400
|
+
filterStr = Fog::JSON.encode({
|
401
|
+
"hardware" => {
|
402
|
+
"hostname" => {
|
403
|
+
"operation" => self.name,
|
404
|
+
},
|
405
|
+
"domain" => {
|
406
|
+
"operation" => self.domain,
|
407
|
+
},
|
408
|
+
"globalIdentifier" => {
|
409
|
+
"operation" => self.uid,
|
410
|
+
},
|
411
|
+
}
|
412
|
+
})
|
413
|
+
|
414
|
+
Fog.wait_for(timeout, delay) do
|
415
|
+
res = service.request(:account, 'getHardware', :query => {
|
416
|
+
:objectMask => 'mask[id,fullyQualifiedDomainName,provisionDate,hardwareStatus,lastTransaction[elapsedSeconds,transactionStatus[friendlyName]],operatingSystem[id,passwords[password,username]]]',
|
417
|
+
:objectFilter => filterStr,
|
418
|
+
})
|
419
|
+
|
420
|
+
server = res.body.first
|
421
|
+
|
422
|
+
yield server if block_given?
|
423
|
+
|
424
|
+
if server and server["provisionDate"]
|
425
|
+
attributes[:id] = server['id']
|
426
|
+
true
|
427
|
+
else
|
428
|
+
false
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
self.reload
|
433
|
+
true
|
434
|
+
end
|
435
|
+
|
436
|
+
private
|
437
|
+
|
438
|
+
def network_connection
|
439
|
+
@network_conn ||= Fog::Network.new(
|
440
|
+
:provider => :softlayer,
|
441
|
+
:softlayer_username => service.instance_variable_get(:@softlayer_username),
|
442
|
+
:softlayer_api_key => service.instance_variable_get(:@softlayer_api_key)
|
443
|
+
)
|
444
|
+
end
|
445
|
+
|
446
|
+
def product_connection
|
447
|
+
if Fog.mock?
|
448
|
+
@product_conn = Fog::Softlayer::Product.new(
|
449
|
+
:provider => :softlayer,
|
450
|
+
:softlayer_username => service.instance_variable_get(:@credentials)[:username],
|
451
|
+
:softlayer_api_key => service.instance_variable_get(:@credentials)[:api_key]
|
452
|
+
)
|
453
|
+
end
|
454
|
+
@product_conn ||= Fog::Softlayer::Product.new(
|
455
|
+
:provider => :softlayer,
|
456
|
+
:softlayer_username => service.instance_variable_get(:@softlayer_username),
|
457
|
+
:softlayer_api_key => service.instance_variable_get(:@softlayer_api_key)
|
458
|
+
)
|
459
|
+
end
|
460
|
+
|
461
|
+
def _get_private_vlan
|
462
|
+
if self.id
|
463
|
+
vlan_id = if bare_metal?
|
464
|
+
service.request(:hardware_server, "#{self.id}/get_private_vlan").body['id']
|
465
|
+
else
|
466
|
+
service.request(:virtual_guest, self.id, :query => 'objectMask=primaryBackendNetworkComponent.networkVlan').body['primaryBackendNetworkComponent']['networkVlan']['id']
|
467
|
+
end
|
468
|
+
network_connection.networks.get(vlan_id)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
def _get_vlan
|
473
|
+
if self.id
|
474
|
+
vlan_id = if bare_metal?
|
475
|
+
service.request(:hardware_server, "#{self.id}/get_public_vlan").body['id']
|
476
|
+
else
|
477
|
+
service.request(:virtual_guest, self.id, :query => 'objectMask=primaryNetworkComponent.networkVlan').body['primaryNetworkComponent']['networkVlan']['id']
|
478
|
+
end
|
479
|
+
network_connection.networks.get(vlan_id)
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
##
|
484
|
+
# Generate mapping for use with remap_attributes
|
485
|
+
def attributes_mapping
|
486
|
+
common = {
|
487
|
+
:hourly_billing_flag => :hourlyBillingFlag,
|
488
|
+
:os_code => :operatingSystemReferenceCode,
|
489
|
+
:vlan => :primaryNetworkComponent,
|
490
|
+
:private_vlan => :primaryBackendNetworkComponent,
|
491
|
+
:key_pairs => :sshKeys,
|
492
|
+
:private_network_only => :privateNetworkOnlyFlag,
|
493
|
+
:user_data => :userData,
|
494
|
+
:provision_script => :postInstallScriptUri,
|
495
|
+
:network_components => :networkComponents,
|
496
|
+
}
|
497
|
+
|
498
|
+
conditional = if bare_metal?
|
499
|
+
{
|
500
|
+
:cpu => :processorCoreAmount,
|
501
|
+
:ram => :memoryCapacity,
|
502
|
+
:disk => :hardDrives,
|
503
|
+
:bare_metal => :bareMetalInstanceFlag,
|
504
|
+
:fixed_configuration_preset => :fixedConfigurationPreset,
|
505
|
+
}
|
506
|
+
else
|
507
|
+
{
|
508
|
+
:cpu => :startCpus,
|
509
|
+
:ram => :maxMemory,
|
510
|
+
:disk => :blockDevices,
|
511
|
+
:image_id => :blockDeviceTemplateGroup,
|
512
|
+
:ephemeral_storage => :localDiskFlag,
|
513
|
+
}
|
514
|
+
end
|
515
|
+
common.merge(conditional)
|
516
|
+
end
|
517
|
+
|
518
|
+
def bare_metal=(set)
|
519
|
+
return @bare_metal if set == @bare_metal
|
520
|
+
raise Exception, "Bare metal flag has already been set" unless @bare_metal.nil?
|
521
|
+
@bare_metal = case set
|
522
|
+
when false, 'false', 0, nil, ''
|
523
|
+
attributes[:bare_metal] = false
|
524
|
+
else
|
525
|
+
attributes[:bare_metal] = true
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
##
|
530
|
+
# Remove model attributes that aren't expected by the SoftLayer API
|
531
|
+
def clean_attributes
|
532
|
+
attributes.delete(:bare_metal)
|
533
|
+
attributes.delete(:flavor_id)
|
534
|
+
attributes.delete(:ephemeral_storage)
|
535
|
+
attributes.delete(:tags) if bare_metal?
|
536
|
+
end
|
537
|
+
|
538
|
+
##
|
539
|
+
# Expand a "flavor" into cpu, ram, and disk attributes
|
540
|
+
def extract_flavor
|
541
|
+
if attributes[:flavor_id]
|
542
|
+
flavor = @service.flavors.get(attributes[:flavor_id])
|
543
|
+
flavor.nil? and Fog::Errors::Error.new("Unrecognized flavor in #{self.class}##{__method__}")
|
544
|
+
attributes[:cpu] = flavor.cpu
|
545
|
+
attributes[:ram] = flavor.ram
|
546
|
+
attributes[:disk] = flavor.disk unless attributes[:image_id]
|
547
|
+
if bare_metal?
|
548
|
+
value = flavor.disk.first['diskImage']['capacity'] < 500 ? 250 : 500
|
549
|
+
attributes[:disk] = [{'capacity'=>value}]
|
550
|
+
attributes[:ram] = attributes[:ram] / 1024 if attributes[:ram] > 64
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
def validate_attributes
|
556
|
+
requires :name, :domain, :datacenter
|
557
|
+
if attributes[:fixed_configuration_preset]
|
558
|
+
requires :os_code
|
559
|
+
else
|
560
|
+
requires :cpu, :ram
|
561
|
+
requires_one :os_code, :image_id
|
562
|
+
requires_one :disk, :image_id
|
563
|
+
end
|
564
|
+
bare_metal? and image_id and raise ArgumentError, "Bare Metal Cloud does not support booting from Image"
|
565
|
+
end
|
566
|
+
|
567
|
+
def set_defaults
|
568
|
+
attributes[:hourly_billing_flag] = true if attributes[:hourly_billing_flag].nil?
|
569
|
+
attributes[:ephemeral_storage] = false if attributes[:ephemeral_storage].nil?
|
570
|
+
attributes[:domain] = service.softlayer_default_domain if service.softlayer_default_domain and attributes[:domain].nil?
|
571
|
+
self.datacenter = service.softlayer_default_datacenter if service.softlayer_default_datacenter and attributes[:datacenter].nil?
|
572
|
+
end
|
573
|
+
|
574
|
+
def get_item_prices_id_by_value(item_price_array, category, value)
|
575
|
+
item_prices = item_price_array.select { |item_price| item_price["categories"].find { |category_hash| category_hash["categoryCode"] == category } }
|
576
|
+
item_price = item_prices.find { |item_price| item_price['item']['capacity'] == value.to_s }
|
577
|
+
item_price.nil? ? "" : item_price["id"]
|
578
|
+
end
|
579
|
+
|
580
|
+
def get_item_prices_id(update_attributes)
|
581
|
+
item_price_array = get_upgrade_options
|
582
|
+
update_attributes.delete(:time)
|
583
|
+
update_attributes.delete(:maintenance_window)
|
584
|
+
update_attributes.map { |key, value| { :id => get_item_prices_id_by_value(item_price_array, key.to_s, value) } }
|
585
|
+
end
|
586
|
+
|
587
|
+
def bm_upgrade_order_template(value)
|
588
|
+
{
|
589
|
+
:complexType => 'SoftLayer_Container_Product_Order_Hardware_Server_Upgrade',
|
590
|
+
:hardware => [
|
591
|
+
{
|
592
|
+
:id => id
|
593
|
+
}
|
594
|
+
],
|
595
|
+
:properties => [
|
596
|
+
{
|
597
|
+
:name => 'MAINTENANCE_WINDOW_ID',
|
598
|
+
:value => value
|
599
|
+
}
|
600
|
+
]
|
601
|
+
}
|
602
|
+
end
|
603
|
+
|
604
|
+
def vm_upgrade_order_template(time)
|
605
|
+
{
|
606
|
+
:complexType => 'SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade',
|
607
|
+
:virtualGuests => [
|
608
|
+
{
|
609
|
+
:id => id
|
610
|
+
}
|
611
|
+
],
|
612
|
+
:properties => [
|
613
|
+
{
|
614
|
+
:name => 'MAINTENANCE_WINDOW',
|
615
|
+
:value => (time.nil? || time.empty?) ? Time.now.iso8601 : time.iso8601
|
616
|
+
}
|
617
|
+
]
|
618
|
+
}
|
619
|
+
end
|
620
|
+
|
621
|
+
def generate_upgrade_order(prices, value)
|
622
|
+
return bm_upgrade_order_template(value).merge({ :prices => prices }) if bare_metal?
|
623
|
+
vm_upgrade_order_template(value).merge({ :prices => prices })
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|