Class: Server

Inherits:
Object
  • Object
show all
Extended by:
RightScale::Api::BaseExtend, RightScale::Api::TaggableExtend
Includes:
RightScale::Api::Base, RightScale::Api::Taggable, SshHax
Defined in:
lib/rest_connection/rightscale/server.rb

Overview

API 1.0

Direct Known Subclasses

McServer

Constant Summary

Constants included from SshHax

SshHax::SSH_RETRY_COUNT

Instance Attribute Summary collapse

Attributes included from RightScale::Api::Base

#params

Class Method Summary collapse

Instance Method Summary collapse

Methods included from RightScale::Api::BaseExtend

[], create, deny_methods, filters, find, find_all, find_by, find_by_cloud_id, find_by_id, find_by_nickname, find_by_nickname_speed, find_with_filter, resource_plural_name, resource_singular_name

Methods included from RightScale::Api::BaseConnection

#connection

Methods included from RightScale::Api::TaggableExtend

find_by_tags

Methods included from RightScale::Api::Taggable

#get_info_tags, #remove_info_tags, #remove_tags_by_namespace, #set_info_tags, #set_tags_by_namespace, #set_tags_to

Methods included from SshHax

#spot_check, #spot_check_command, #spot_check_command?, #ssh_key_config

Methods included from RightScale::Api::Base

#[], #[]=, #destroy, #method_missing, #reload, #resource_plural_name, #resource_singular_name, #rs_id

Constructor Details

#initialize(*args, &block) ⇒ Server

Returns a new instance of Server.



61
62
63
64
65
66
# File 'lib/rest_connection/rightscale/server.rb', line 61

def initialize(*args, &block)
  super(*args, &block)
  if RightScale::Api::api0_1?
    @internal = ServerInternal.new(*args, &block)
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class RightScale::Api::Base

Instance Attribute Details

#internalObject

Returns the value of attribute internal.



34
35
36
# File 'lib/rest_connection/rightscale/server.rb', line 34

def internal
  @internal
end

Class Method Details

.create(opts) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/rest_connection/rightscale/server.rb', line 48

def self.create(opts)
  create_options = Hash.new
  create_options[self.resource_singular_name.to_sym] = opts
  create_options["cloud_id"] = opts[:cloud_id] if opts[:cloud_id]
  create_options[self.resource_singular_name.to_sym][:mci_href] = nil
  create_options[self.resource_singular_name.to_sym][:inputs] = nil
  location = connection.post(self.resource_plural_name,create_options)
  newrecord = self.new('href' => location)
  newrecord.reload
  newrecord.parameters #transform the parameters!
  newrecord
end

.filtersObject



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/rest_connection/rightscale/server.rb', line 36

def self.filters
  [
    :aws_id,
    :created_at,
    :deployment_href,
    :ip_address,
    :nickname,
    :private_ip_address,
    :updated_at
  ]
end

Instance Method Details

#add_tags(*args) ⇒ Object



431
432
433
434
435
436
437
438
# File 'lib/rest_connection/rightscale/server.rb', line 431

def add_tags(*args)
  self.reload_as_next if self.href =~ /current/
  return false if args.empty?
  args.uniq!
  Tag.set(self.href, args)
  Tag.set(self.current_instance_href, args) if self.current_instance_href
  self.tags(true)
end

#alert_specsObject



317
318
319
# File 'lib/rest_connection/rightscale/server.rb', line 317

def alert_specs
  # TODO
end

#attach_volume(params) ⇒ Object



288
289
290
291
292
293
# File 'lib/rest_connection/rightscale/server.rb', line 288

def attach_volume(params)
  hash = {}
  hash[:server] = params
  serv_href = URI.parse(self.href)
  connection.post(serv_href.path + "/attach_volume", hash)
end


149
150
151
152
153
154
155
# File 'lib/rest_connection/rightscale/server.rb', line 149

def audit_link
  # proof of concept for now
#    server_id = self.href.split(/\//).last
#    audit_href = "https://my.rightscale.com/servers/#{server_id}#auditentries"
  audit_href = self.href.gsub(/api\//,"") + "#auditentries"
  "<a href='#{audit_href}'>#{audit_href}</a>"
end

#clear_tags(namespace = nil) ⇒ Object



465
466
467
468
469
470
# File 'lib/rest_connection/rightscale/server.rb', line 465

def clear_tags(namespace = nil)
  tags = self.tags(true)
  tags.deep_merge! self.current_tags if self.current_instance_href
  tags = tags.select { |tag| tag.start_with?("#{namespace}:") } if namespace
  self.remove_tags(*tags)
end

#cloud_idObject

Complex logic for determining the cloud_id of even a stopped server



356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/rest_connection/rightscale/server.rb', line 356

def cloud_id
  self.settings
  if self.state == "operational"
    return self["cloud_id"]
  end
  cloud_ids = RightScale::Api::AWS_CLOUDS.map { |hsh| hsh["cloud_id"] }

  # Try ssh keys
  if self.ec2_ssh_key_href and RightScale::Api::api0_1?
    ref = self.ec2_ssh_key_href
    cloud_ids.each { |cloud|
      if Ec2SshKeyInternal.find_by_cloud_id(cloud.to_s).select { |o| o.href == ref }.first
        return cloud
      end
    }
  end

  # Try security groups
  if self.ec2_security_groups_href
    self.ec2_security_groups_href.each { |sg|
      cloud_ids.each { |cloud|
        if Ec2SecurityGroup.find_by_cloud_id(cloud.to_s).select { |o| o.href == sg }.first
          return cloud
        end
      }
    }
  end

  raise "Could not determine cloud_id...try setting an ssh key or security group"
end

#current_tags(reload = true) ⇒ Object



422
423
424
425
426
427
428
429
# File 'lib/rest_connection/rightscale/server.rb', line 422

def current_tags(reload=true)
  self.reload_as_next if self.href =~ /current/
  ret = []
  if self.current_instance_href
    ret = Tag.search_by_href(self.current_instance_href).map { |h| h["name"] }
  end
  ret
end

#dns_nameObject



391
392
393
394
395
396
397
# File 'lib/rest_connection/rightscale/server.rb', line 391

def dns_name
  self.settings unless self["dns_name"]
  if self.current_instance_href
    self["dns_name"] ||= get_tags_by_namespace("server")["current_instance"]["public_ip_0"]
  end
  self["dns_name"]
end

#force_stopObject



176
177
178
179
180
181
182
183
184
# File 'lib/rest_connection/rightscale/server.rb', line 176

def force_stop
  if self.current_instance_href
    t = URI.parse(self.href)
    connection.post(t.path + '/stop')
    connection.post(t.path + '/stop')
  else
    connection.logger("WARNING: was in #{self.state} and had a current_instance_href so skiping stop call")
  end
end

#get_sketchy_data(params = {}) ⇒ Object



295
296
297
298
# File 'lib/rest_connection/rightscale/server.rb', line 295

def get_sketchy_data(params = {})
  serv_href = URI.parse(self.href)
  @params.merge! connection.get(serv_href.path + "/get_sketchy_data", params)
end

#get_tags_by_namespace(namespace) ⇒ Object



449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/rest_connection/rightscale/server.rb', line 449

def get_tags_by_namespace(namespace)
  ret = {}
  tags = {"self" => self.tags(true)}
  tags["current_instance"] = self.current_tags if self.current_instance_href
  tags.each { |res,ary|
    ret[res] ||= {}
    ary.each { |tag|
      next unless tag.start_with?("#{namespace}:")
      key = tag.split("=").first.split(":")[1..-1].join(":")
      value = tag.split(":")[1..-1].join(":").split("=")[1..-1].join("=")
      ret[res][key] = value
    }
  }
  return ret
end

#lockObject



472
473
474
475
476
477
478
479
480
# File 'lib/rest_connection/rightscale/server.rb', line 472

def lock
  unless self.settings['locked']
    serv_href = URI.parse(self.href)
    res = connection.put(serv_href.path, :server => {:lock => 'true'})
    res.is_a?(Net::HTTPSuccess) ? true : false
  else
    connection.logger("Server is already locked")
  end
end

#monitoringObject



300
301
302
303
# File 'lib/rest_connection/rightscale/server.rb', line 300

def monitoring
  serv_href = URI.parse(self.href)
  @params.merge! connection.get(serv_href.path + "/monitoring")
end

#parametersObject

Since RightScale hands back the parameters with a “name” and “value” tags we should transform them into the proper hash. This it the same for setting and getting.



80
81
82
83
84
85
86
# File 'lib/rest_connection/rightscale/server.rb', line 80

def parameters
  # if the parameters are an array of hashes, that means we need to transform.
  if @params['parameters'].is_a?(Array)
    @params['parameters'] = transform_parameters(@params['parameters'])
  end
  @params['parameters'] ||= {}
end

#private_ipObject



399
400
401
402
403
404
405
# File 'lib/rest_connection/rightscale/server.rb', line 399

def private_ip
  self.settings unless @params["private-ip-address"]
  if self.current_instance_href
    self["private-ip-address"] ||= get_tags_by_namespace("server")["current_instance"]["private_ip_0"]
  end
  @params["private-ip-address"]
end

#reachable_ipObject



407
408
409
410
411
412
413
414
# File 'lib/rest_connection/rightscale/server.rb', line 407

def reachable_ip
  if ret = self.dns_name
    return ret
  elsif ret = self.private_ip
    return ret
  end
  nil
end

#reboot(wait_for_state = false, timeout = 60*7) ⇒ Object

takes Bool argument to wait for state change (insurance that we can detect a reboot happened) *timeout <~Integer> optional, how long to wait for the stopped state before declare failure (in seconds).



307
308
309
310
311
312
313
314
315
# File 'lib/rest_connection/rightscale/server.rb', line 307

def reboot(wait_for_state = false, timeout = 60*7)
  reload
  old_state = self.state
  serv_href = URI.parse(self.href)
  connection.post(serv_href.path + "/reboot")
  if wait_for_state
    wait_for_state_change(old_state, timeout)
  end
end

#relaunch(timeout = 1200) ⇒ Object

*timeout <~Integer> optional, how long to wait for the stopped state before declare failure (in seconds).



322
323
324
325
326
# File 'lib/rest_connection/rightscale/server.rb', line 322

def relaunch(timeout=1200)
  self.stop
  self.wait_for_state("stopped", timeout)
  self.start
end

#reload_as_currentObject

Reload the server’s basic information from the current server instance



344
345
346
347
# File 'lib/rest_connection/rightscale/server.rb', line 344

def reload_as_current
  uri = URI.parse(self.href + "/current")
  @params.merge! connection.get(uri.path)
end

#reload_as_nextObject

Reload the server’s basic information from the next server instance



350
351
352
353
# File 'lib/rest_connection/rightscale/server.rb', line 350

def reload_as_next
  uri = URI.parse(self.href.gsub(/\/current/,""))
  @params.merge! connection.get(uri.path)
end

#remove_tags(*args) ⇒ Object



440
441
442
443
444
445
446
447
# File 'lib/rest_connection/rightscale/server.rb', line 440

def remove_tags(*args)
  self.reload_as_next if self.href =~ /current/
  return false if args.empty?
  args.uniq!
  Tag.unset(self.href, args)
  Tag.unset(self.current_instance_href, args) if self.current_instance_href
  self.tags(true)
end

#run_executable(executable, opts = nil, ignore_lock = false) ⇒ Object

Works on v4 and v5 images. *executable can be an <~Executable> or <~RightScript>



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/rest_connection/rightscale/server.rb', line 211

def run_executable(executable, opts=nil, ignore_lock=false)
  script_options = Hash.new
  script_options[:server] = Hash.new
  if executable.is_a?(Executable)
    if executable.recipe?
      script_options[:server][:recipe] = executable.recipe
    else
      script_options[:server][:right_script_href] = executable.right_script.href
    end
  elsif executable.is_a?(RightScript)
    script_options[:server][:right_script_href] = executable.href
  else
    raise "Invalid class passed to run_executable, needs Executable or RightScript, was:#{executable.class}"
  end

  if not opts.nil? and opts.has_key?(:ignore_lock)
    script_options[:server][:ignore_lock] = "true"
    opts.delete(:ignore_lock)
  end

  serv_href = URI.parse(self.href)
  script_options[:server][:parameters] = opts unless opts.nil?
  script_options[:server][:ignore_lock] = "true" if ignore_lock
  location = connection.post(serv_href.path + '/run_executable', script_options)
  AuditEntry.new('href' => location)
end

#run_executable_and_wait_for_completed(executable, opts = nil) ⇒ Object



387
388
389
# File 'lib/rest_connection/rightscale/server.rb', line 387

def run_executable_and_wait_for_completed(executable, opts=nil)
  run_executable(executable, opts).wait_for_completed
end

#run_script(script, opts = nil) ⇒ Object

This should be used with v4 images only.



239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/rest_connection/rightscale/server.rb', line 239

def run_script(script,opts=nil)
  if script.is_a?(Executable)
    script = script.right_script
  end
  serv_href = URI.parse(self.href)
  script_options = Hash.new
  script_options[:server] = Hash.new
  script_options[:server][:right_script_href] = script.href
  script_options[:server][:parameters] = opts unless opts.nil?
  location = connection.post(serv_href.path + '/run_script', script_options)
  Status.new('href' => location)
end

#saveObject

This is overriding the default save with one that can massage the parameters



89
90
91
92
93
94
# File 'lib/rest_connection/rightscale/server.rb', line 89

def save
  self.parameters #transform the parameters, if they're not already!!
  self.tags #transform the tags, if they're not already!!
  uri = URI.parse(self.href)
  connection.put(uri.path, resource_singular_name.to_sym => @params)
end

#set_current_inputs(hash = {}) ⇒ Object



262
263
264
265
266
267
268
269
270
# File 'lib/rest_connection/rightscale/server.rb', line 262

def set_current_inputs(hash = {})
  if self.current_instance_href and self.state != "stopped"
    self.reload_as_current
    serv_href = URI.parse(self.href)
    connection.put(serv_href.path, :server => {:parameters => hash})
    settings
    self.reload_as_next
  end
end

#set_input(name, value) ⇒ Object



252
253
254
255
# File 'lib/rest_connection/rightscale/server.rb', line 252

def set_input(name, value)
  serv_href = URI.parse(self.href)
  connection.put(serv_href.path, :server => {:parameters => {name.to_sym => value} })
end

#set_inputs(hash = {}) ⇒ Object



257
258
259
260
# File 'lib/rest_connection/rightscale/server.rb', line 257

def set_inputs(hash = {})
  set_current_inputs(hash)
  set_next_inputs(hash)
end

#set_next_inputs(hash = {}) ⇒ Object



272
273
274
275
276
# File 'lib/rest_connection/rightscale/server.rb', line 272

def set_next_inputs(hash = {})
  serv_href = URI.parse(self.href)
  connection.put(serv_href.path, :server => {:parameters => hash})
  settings
end

#set_template(href) ⇒ Object



278
279
280
281
# File 'lib/rest_connection/rightscale/server.rb', line 278

def set_template(href)
  serv_href = URI.parse(self.href)
  connection.put(serv_href.path, :server => {:server_template_href => href})
end

#settingsObject



283
284
285
286
# File 'lib/rest_connection/rightscale/server.rb', line 283

def settings
  serv_href = URI.parse(self.href)
  @params.merge! connection.get(serv_href.path + "/settings")
end

#startObject



157
158
159
160
161
162
163
164
# File 'lib/rest_connection/rightscale/server.rb', line 157

def start
  if self.state == "stopped"
    t = URI.parse(self.href)
    return connection.post(t.path + '/start')
  else
    connection.logger("WARNING: was in #{self.state} so skiping start call")
  end
end

#start_ebsObject

Uses ServerInternal api to start and stop EBS based instances



187
188
189
190
191
192
193
194
195
196
# File 'lib/rest_connection/rightscale/server.rb', line 187

def start_ebs
#    @server_internal = ServerInternal.new(:href => self.href)
#    @server_internal.start
  if self.state == "stopped"
    t = URI.parse(self.href)
    return connection.post(t.path + '/start_ebs')
  else
    connection.logger("WARNING: was in #{self.state} so skiping start_ebs call")
  end
end

#stopObject



166
167
168
169
170
171
172
173
174
# File 'lib/rest_connection/rightscale/server.rb', line 166

def stop
  # All instances will have a valid href including EBS instances that are "stopped"
  if self.current_instance_href
    t = URI.parse(self.href)
    connection.post(t.path + '/stop')
  else
    connection.logger("WARNING: was in #{self.state} and had a current_instance_href so skiping stop call")
  end
end

#stop_ebsObject



198
199
200
201
202
203
204
205
206
207
# File 'lib/rest_connection/rightscale/server.rb', line 198

def stop_ebs
#    @server_internal = ServerInternal.new(:href => self.href)
#    @server_internal.stop
  if self.current_instance_href
    t = URI.parse(self.href)
    connection.post(t.path + '/stop_ebs')
  else
    connection.logger("WARNING: was in #{self.state} and had a current_instance_href so skiping stop_ebs call")
  end
end

#tags(*args) ⇒ Object

Override Taggable mixin so that it sets tags on both next and current instances



417
418
419
420
# File 'lib/rest_connection/rightscale/server.rb', line 417

def tags(*args)
  self.reload_as_next if self.href =~ /current/
  super(*args)
end

#transform_parameters(parameters) ⇒ Object

The RightScale api returns the server parameters as a hash with “name” and “value”. This must be transformed into a hash in case we want to PUT this back to the API.



70
71
72
73
74
75
76
# File 'lib/rest_connection/rightscale/server.rb', line 70

def transform_parameters(parameters)
  new_params_hash = {}
  parameters.each do |parameter_hash|
    new_params_hash[parameter_hash["name"]] = parameter_hash["value"]
  end
  new_params_hash
end

#unlockObject



482
483
484
485
486
487
488
489
490
# File 'lib/rest_connection/rightscale/server.rb', line 482

def unlock
  if self.settings['locked']
    serv_href = URI.parse(self.href)
    res = connection.put(serv_href.path, :server => {:lock => 'false'})
    res.is_a?(Net::HTTPSuccess) ? true : false
  else
    connection.logger("Server is already unlocked")
  end
end

#wait_for_operational_with_dns(operational_timeout_in_seconds = 1200, dns_timeout_in_seconds = operational_timeout_in_seconds / 2) ⇒ Object

waits until the server is operational and dns_name is available



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/rest_connection/rightscale/server.rb', line 124

def wait_for_operational_with_dns(operational_timeout_in_seconds = 1200, dns_timeout_in_seconds = operational_timeout_in_seconds / 2)
  # First, wait for the server to go operational
  wait_for_state("operational", operational_timeout_in_seconds)

  # Log that we are switching to waiting for dns
  connection.logger "#{self.nickname} is operational, now checking for dns name/ip address..."

  # Now poll for valid dns
  dns_timeout = dns_timeout_in_seconds
  dns_step = 15
  while(dns_timeout > 0)
    self.settings
    if self.reachable_ip
      # Got a dns name/ip address so log that and return (note that "reachable_ip" returns a dns name on AWS and an ip address on generic clouds)
      connection.logger "Got dns name/ip address: #{self.reachable_ip}."
      return
    end
    connection.logger "Waiting #{dns_step} seconds before checking for dns name/ip address on #{self.nickname}..."
    sleep dns_step
    dns_timeout -= dns_step
  end

  raise "FATAL, server #{self.nickname}, #{self.audit_link} timed out waiting for dns name/ip address, waited for #{dns_timeout_in_seconds}."
end

#wait_for_state(st, timeout = 1200) ⇒ Object

waits until the specified state is reached for this Server *st <~String> the name of the state to wait for, eg. “operational” *timeout <~Integer> optional, how long to wait for the state before declare failure (in seconds).



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/rest_connection/rightscale/server.rb', line 99

def wait_for_state(st,timeout=1200)
  reload
  connection.logger("#{nickname} is #{self.state}")
  step = 15
  catch_early_terminated = 1200 / step
  while(timeout > 0)
    return true if state =~ /#{st}/
    return true if state =~ /terminated|stopped/ && st =~ /terminated|stopped/
    raise "FATAL error, this server is stranded and needs to be #{st}: #{nickname}, see audit: #{self.audit_link}" if state.include?('stranded') && !st.include?('stranded')
    raise "FATAL error, this server went to error state and needs to be #{st}: #{nickname}, see audit: #{self.audit_link}" if state.include?('error') && st !~ /error|terminated|stopped/
    connection.logger("waiting for server #{nickname} to go #{st}, state is #{state}")
    if state =~ /terminated|stopped|inactive|error/ and st !~ /terminated|stopped|inactive|error/
      if catch_early_terminated <= 0
        raise "FATAL error, this server entered #{state} state waiting for #{st}: #{nickname}"
      end
      catch_early_terminated -= 1
    end
    sleep step
    timeout -= step
    reload
  end
  raise "FATAL, this server #{self.audit_link} timed out waiting for the state to be #{st}" if timeout <= 0
end

#wait_for_state_change(old_state = nil, timeout = 60*7) ⇒ Object

*timeout <~Integer> optional, how long to wait for the stopped state before declare failure (in seconds).



329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/rest_connection/rightscale/server.rb', line 329

def wait_for_state_change(old_state = nil, timeout = 60*7)
  timer = 0
  while(timer < timeout)
    reload
    old_state = self.state unless old_state
    connection.logger("#{nickname} is #{self.state}")
    return true if self.state != old_state
    sleep 30
    timer += 30
    connection.logger("waiting for server #{nickname} to change from #{old_state} state.")
  end
  raise("FATAL: timeout after #{timeout}s waiting for state change")
end