Class: Fog::Compute::Libvirt::Server

Inherits:
Model
  • Object
show all
Includes:
Fog::Compute::LibvirtUtil
Defined in:
lib/fog/libvirt/models/compute/server.rb

Instance Attribute Summary collapse

Attributes inherited from Model

#collection, #connection

Instance Method Summary collapse

Methods included from Fog::Compute::LibvirtUtil

#document

Methods inherited from Model

#inspect, #reload, #to_json, #wait_for

Methods included from Attributes::ClassMethods

#_load, #aliases, #attribute, #attributes, #identity, #ignore_attributes, #ignored_attributes

Methods included from Attributes::InstanceMethods

#_dump, #attributes, #dup, #identity, #identity=, #merge_attributes, #new_record?, #requires, #requires_one

Constructor Details

#initialize(attributes = {}) ⇒ Server

Can be created by passing in :xml => “<xml to create domain/server>” or by providing :template_options =>

             :name => "", :cpus => 1, :memory_size => 256 , :volume_template
:


49
50
51
52
53
54
55
56
57
# File 'lib/fog/libvirt/models/compute/server.rb', line 49

def initialize(attributes={} )

  # setup all attributes.
  defaults.merge(attributes).each do |k,v|
    eval("self.#{k}= v") if self.respond_to?("#{k}=")
  end

  super
end

Instance Attribute Details

#iso_dirObject

The following attributes are only needed when creating a new vm



36
37
38
# File 'lib/fog/libvirt/models/compute/server.rb', line 36

def iso_dir
  @iso_dir
end

#iso_fileObject

The following attributes are only needed when creating a new vm



36
37
38
# File 'lib/fog/libvirt/models/compute/server.rb', line 36

def iso_file
  @iso_file
end

#network_bridge_nameObject

Returns the value of attribute network_bridge_name.



37
38
39
# File 'lib/fog/libvirt/models/compute/server.rb', line 37

def network_bridge_name
  @network_bridge_name
end

#network_interface_typeObject

Returns the value of attribute network_interface_type.



37
38
39
# File 'lib/fog/libvirt/models/compute/server.rb', line 37

def network_interface_type
  @network_interface_type
end

#network_nat_networkObject

Returns the value of attribute network_nat_network.



37
38
39
# File 'lib/fog/libvirt/models/compute/server.rb', line 37

def network_nat_network
  @network_nat_network
end

#passwordObject

Returns the value of attribute password.



40
41
42
# File 'lib/fog/libvirt/models/compute/server.rb', line 40

def password
  @password
end

#private_keyObject



367
368
369
# File 'lib/fog/libvirt/models/compute/server.rb', line 367

def private_key
  @private_key ||= private_key_path && File.read(private_key_path)
end

#private_key_pathObject



362
363
364
365
# File 'lib/fog/libvirt/models/compute/server.rb', line 362

def private_key_path
  @private_key_path ||= Fog.credentials[:private_key_path]
  @private_key_path &&= File.expand_path(@private_key_path)
end

#public_keyObject



376
377
378
# File 'lib/fog/libvirt/models/compute/server.rb', line 376

def public_key
  @public_key ||= public_key_path && File.read(public_key_path)
end

#public_key_pathObject



371
372
373
374
# File 'lib/fog/libvirt/models/compute/server.rb', line 371

def public_key_path
  @public_key_path ||= Fog.credentials[:public_key_path]
  @public_key_path &&= File.expand_path(@public_key_path)
end

#usernameObject



157
158
159
# File 'lib/fog/libvirt/models/compute/server.rb', line 157

def username
  @username ||= 'root'
end

#volume_allocationObject

Returns the value of attribute volume_allocation.



38
39
40
# File 'lib/fog/libvirt/models/compute/server.rb', line 38

def volume_allocation
  @volume_allocation
end

#volume_capacityObject

Returns the value of attribute volume_capacity.



38
39
40
# File 'lib/fog/libvirt/models/compute/server.rb', line 38

def volume_capacity
  @volume_capacity
end

#volume_format_typeObject

Returns the value of attribute volume_format_type.



38
39
40
# File 'lib/fog/libvirt/models/compute/server.rb', line 38

def volume_format_type
  @volume_format_type
end

#volume_nameObject

Returns the value of attribute volume_name.



38
39
40
# File 'lib/fog/libvirt/models/compute/server.rb', line 38

def volume_name
  @volume_name
end

#volume_pathObject

Returns the value of attribute volume_path.



38
39
40
# File 'lib/fog/libvirt/models/compute/server.rb', line 38

def volume_path
  @volume_path
end

#volume_pool_nameObject

Returns the value of attribute volume_pool_name.



38
39
40
# File 'lib/fog/libvirt/models/compute/server.rb', line 38

def volume_pool_name
  @volume_pool_name
end

#volume_template_nameObject

Returns the value of attribute volume_template_name.



38
39
40
# File 'lib/fog/libvirt/models/compute/server.rb', line 38

def volume_template_name
  @volume_template_name
end

Instance Method Details

#addresses(options = {}) ⇒ Object

This retrieves the ip address of the mac address It returns an array of public and private ip addresses Currently only one ip address is returned, but in the future this could be multiple if the server has multiple network interface

TODO: move this into the util class



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/fog/libvirt/models/compute/server.rb', line 251

def addresses(options={})
  mac=self.mac

  # Aug 24 17:34:41 juno arpwatch: new station 10.247.4.137 52:54:00:88:5a:0a eth0.4
  # Aug 24 17:37:19 juno arpwatch: changed ethernet address 10.247.4.137 52:54:00:27:33:00 (52:54:00:88:5a:0a) eth0.4
  # Check if another ip_command string was provided
  ip_command_global=@connection.ip_command.nil? ? 'grep $mac /var/log/arpwatch.log|sed -e "s/new station//"|sed -e "s/changed ethernet address//g" |sed -e "s/reused old ethernet //" |tail -1 |cut -d ":" -f 4-| cut -d " " -f 3' : @connection.ip_command
  ip_command_local=options[:ip_command].nil? ? ip_command_global : options[:ip_command]

  ip_command="mac=#{mac}; server_name=#{name}; "+ip_command_local

  ip_address=nil

  if @connection.uri.ssh_enabled?

    # Retrieve the parts we need from the connection to setup our ssh options
    user=connection.uri.user #could be nil
    host=connection.uri.host
    keyfile=connection.uri.keyfile
    port=connection.uri.port

    # Setup the options
    ssh_options={}
    ssh_options[:keys]=[ keyfile ] unless keyfile.nil?
    ssh_options[:port]=port unless keyfile.nil?
    ssh_options[:paranoid]=true if connection.uri.no_verify?

    # TODO: we need to take the time into account, when IP's are re-allocated, we might be executing
    # On the wrong host

    begin
      result=Fog::SSH.new(host, user, ssh_options).run(ip_command)
    rescue Errno::ECONNREFUSED
      raise Fog::Errors::Error.new("Connection was refused to host #{host} to retrieve the ip_address for #{mac}")
    rescue Net::SSH::AuthenticationFailed
      raise Fog::Errors::Error.new("Error authenticating over ssh to host #{host} and user #{user}")
    end

    #TODO: We currently just retrieve the ip address through the ip_command
    #TODO: We need to check if that ip_address is still valid for that mac-address

    # Check for a clean exit code
    if result.first.status == 0
      ip_address=result.first.stdout.strip
    else
      # We got a failure executing the command
      raise Fog::Errors::Error.new("The command #{ip_command} failed to execute with a clean exit code")
    end

  else
    # It's not ssh enabled, so we assume it is
    if @connection.uri.transport=="tls"
      raise Fog::Errors::Error.new("TlS remote transport is not currently supported, only ssh")
    end

    # Execute the ip_command locally
    # Initialize empty ip_address string
    ip_address=""

    IO.popen("#{ip_command}") do |p|
      p.each_line do |l|
        ip_address+=l
      end
      status=Process.waitpid2(p.pid)[1].exitstatus
      if status!=0
        raise Fog::Errors::Error.new("The command #{ip_command} failed to execute with a clean exit code")
      end
    end

    #Strip any new lines from the string
    ip_address=ip_address.chomp
  end


  # The Ip-address command has been run either local or remote now

  if ip_address==""
    #The grep didn't find an ip address result"
    ip_address=nil
  else
    # To be sure that the command didn't return another random string
    # We check if the result is an actual ip-address
    # otherwise we return nil
    unless ip_address=~/^(\d{1,3}\.){3}\d{1,3}$/
      raise Fog::Errors::Error.new(
        "The result of #{ip_command} does not have valid ip-address format\n"+
        "Result was: #{ip_address}\n"
    )
    end
  end

  return { :public => [ip_address], :private => [ip_address]}
end

#create_or_clone_volumeObject

TODO move all of this to the volume class



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/fog/libvirt/models/compute/server.rb', line 86

def create_or_clone_volume

  volume_options = Hash.new

  volume_options[:name] = volume_name || default_volume_name

  # Check if a disk template was specified
  unless self.volume_template_name.nil?

    template_volumes = connection.volumes.all(:name => self.volume_template_name)

    raise Fog::Errors::Error.new("Template #{self.volume_template_name} not found") unless template_volumes.empty?

    orig_volume = template_volumes.first
    volume      = orig_volume.clone("#{volume_options[:name]}")

    # if we cloned it, it has the original volume type.
    self.volume_format_type = orig_volume.format_type
    # This gets passed to the domain to know the path of the disk
    self.volume_path        = volume.path

  else
    # If no template volume was given, let's create our own volume

    volume_options[:format_type] = self.volume_format_type if volume_format_type
    volume_options[:capacity]    = self.volume_capacity    if volume_capacity
    volume_options[:allocation]  = self.volume_allocation  if volume_allocation

    begin
      volume                    = connection.volumes.create(volume_options)
      self.volume_path          = volume.path
      self.volume_format_type ||= volume.format_type
    rescue
      raise Fog::Errors::Error.new("Error creating the volume : #{$!}")
    end

  end
end

#destroy(options = { :destroy_volumes => false}) ⇒ Object

In libvirt a destroy means a hard power-off of the domain In fog a destroy means the remove of a machine



176
177
178
179
180
181
182
183
184
185
186
# File 'lib/fog/libvirt/models/compute/server.rb', line 176

def destroy(options={ :destroy_volumes => false})
  requires :raw
  poweroff unless stopped?
  #TODO: add handling of failure
  @raw.undefine

  if options[:destroy_volumes]
    # volumes.all filters do not handle nil keys well
    connection.volumes.all(:path => disk_path).each { |vol| vol.destroy}
  end
end

#disk_pathObject



464
465
466
467
468
# File 'lib/fog/libvirt/models/compute/server.rb', line 464

def disk_path
  document("domain/devices/disk/source", "file")
rescue
  []
end

#haltObject

Alias for poweroff



194
195
196
# File 'lib/fog/libvirt/models/compute/server.rb', line 194

def halt
  poweroff
end

#ip_address(key) ⇒ Object



353
354
355
356
357
358
359
360
# File 'lib/fog/libvirt/models/compute/server.rb', line 353

def ip_address(key)
  ips=addresses[key]
  unless ips.nil?
    return ips.first
  else
    return nil
  end
end

#macObject

Retrieves the mac address from parsing the XML of the domain



456
457
458
# File 'lib/fog/libvirt/models/compute/server.rb', line 456

def mac
  mac = document("domain/devices/interface/mac", "address")
end

#new?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'lib/fog/libvirt/models/compute/server.rb', line 59

def new?
  uuid.nil?
end

#poweroffObject

In libvirt a destroy means a hard power-off of the domain In fog a destroy means the remove of a machine



200
201
202
203
# File 'lib/fog/libvirt/models/compute/server.rb', line 200

def poweroff
  requires :raw
  @raw.destroy
end

#private_ip_addressObject



345
346
347
# File 'lib/fog/libvirt/models/compute/server.rb', line 345

def private_ip_address
  ip_address(:private)
end

#public_ip_addressObject



349
350
351
# File 'lib/fog/libvirt/models/compute/server.rb', line 349

def public_ip_address
  ip_address(:public)
end

#ready?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'lib/fog/libvirt/models/compute/server.rb', line 236

def ready?
  state == "running"
end

#rebootObject



188
189
190
191
# File 'lib/fog/libvirt/models/compute/server.rb', line 188

def reboot
  requires :raw
  @raw.reboot
end

#resumeObject



210
211
212
213
# File 'lib/fog/libvirt/models/compute/server.rb', line 210

def resume
  requires :raw
  @raw.resume
end

#saveObject

Raises:



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/fog/libvirt/models/compute/server.rb', line 63

def save

  raise Fog::Errors::Error.new('Resaving an existing server may create a duplicate') unless new?

  validate_template_options

  xml ||= xml_from_template

  create_or_clone_volume

  xml = xml_from_template

  # We either now have xml provided by the user or generated by the template
  begin
    if xml
      self.raw = persistent ? connection.raw.define_domain_xml(xml) : connection.raw.create_domain_xml(xml)
    end
  rescue
    raise Fog::Errors::Error.new("Error saving the server: #{$!}")
  end
end

#scp(local_path, remote_path, upload_options = {}) ⇒ Object

Transfers a file



409
410
411
412
413
414
415
416
417
418
# File 'lib/fog/libvirt/models/compute/server.rb', line 409

def scp(local_path, remote_path, upload_options = {})
  requires :public_ip_address, :username

  scp_options = {}
  scp_options[:password] = password unless self.password.nil?
  scp_options[:key_data] = [private_key] if self.private_key
  scp_options[:proxy]= ssh_proxy unless self.ssh_proxy.nil?

  Fog::SCP.new(public_ip_address, username, scp_options).upload(local_path, remote_path, upload_options)
end

#setup(credentials = {}) ⇒ Object

Sets up a new key



422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/fog/libvirt/models/compute/server.rb', line 422

def setup(credentials = {})
  requires :public_key, :public_ip_address, :username
  require 'multi_json'

  credentials[:proxy]= ssh_proxy unless ssh_proxy.nil?
  credentials[:password] = password unless self.password.nil?
  credentails[:key_data] = [private_key] if self.private_key

  commands = [
    %{mkdir .ssh},
    #              %{passwd -l #{username}}, #Not sure if we need this here
    #              %{echo "#{MultiJson.encode(attributes)}" >> ~/attributes.json}
  ]
  if public_key
    commands << %{echo "#{public_key}" >> ~/.ssh/authorized_keys}
  end

  # wait for domain to be ready
  Timeout::timeout(360) do
    begin
      Timeout::timeout(8) do
        Fog::SSH.new(public_ip_address, username, credentials.merge(:timeout => 4)).run('pwd')
      end
    rescue Errno::ECONNREFUSED
      sleep(2)
      retry
    rescue Net::SSH::AuthenticationFailed, Timeout::Error
      retry
    end
  end
  Fog::SSH.new(public_ip_address, username, credentials).run(commands)
end

#shutdownObject



205
206
207
208
# File 'lib/fog/libvirt/models/compute/server.rb', line 205

def shutdown
  requires :raw
  @raw.shutdown
end

#ssh(commands) ⇒ Object



380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/fog/libvirt/models/compute/server.rb', line 380

def ssh(commands)
  requires :public_ip_address, :username

  #requires :password, :private_key
  ssh_options={}
  ssh_options[:password] = password unless password.nil?
  ssh_options[:key_data] = [private_key] if private_key
  ssh_options[:proxy]= ssh_proxy unless ssh_proxy.nil?

  Fog::SSH.new(public_ip_address, @username, ssh_options).run(commands)

end

#ssh_proxyObject



394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/fog/libvirt/models/compute/server.rb', line 394

def ssh_proxy
  proxy=nil
  if @connection.uri.ssh_enabled?
    relay=connection.uri.host
    user_string=""
    user_string="-l #{connection.uri.user}" unless connection.uri.user.nil?
    proxy = Net::SSH::Proxy::Command.new("ssh #{user_string} "+relay+" nc %h %p")
    return proxy
  else
    return nil
    # This is a direct connection, so we don't need a proxy to be set
  end
end

#startObject



161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/fog/libvirt/models/compute/server.rb', line 161

def start
  requires :raw

  unless @raw.active?
    begin
      @raw.create
      true
    rescue
      false
    end
  end
end

#stopObject



240
241
242
243
# File 'lib/fog/libvirt/models/compute/server.rb', line 240

def stop
  requires :raw
  @raw.shutdown
end

#stopped?Boolean

Returns:

  • (Boolean)


232
233
234
# File 'lib/fog/libvirt/models/compute/server.rb', line 232

def stopped?
  state == "shutoff"
end

#suspendObject



215
216
217
218
# File 'lib/fog/libvirt/models/compute/server.rb', line 215

def suspend
  requires :raw
  @raw.suspend
end

#to_fog_state(raw_state) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
# File 'lib/fog/libvirt/models/compute/server.rb', line 220

def to_fog_state(raw_state)
  state=case raw_state
        when 0 then "nostate"
        when 1 then "running"
        when 2 then "blocked"
        when 3 then "paused"
        when 4 then "shutting-down"
        when 5 then "shutoff"
        when 6 then "crashed"
        end
end

#validate_template_optionsObject



125
126
127
128
129
# File 'lib/fog/libvirt/models/compute/server.rb', line 125

def validate_template_options
  unless self.network_interface_type.nil?
    raise Fog::Errors::Error.new("#{self.network_interface_type} is not a supported interface type") unless ["nat", "bridge"].include?(self.network_interface_type)
  end
end

#vnc_portObject



460
461
462
# File 'lib/fog/libvirt/models/compute/server.rb', line 460

def vnc_port
  port = document("domain/devices/graphics[@type='vnc']", "port")
end

#xml_from_templateObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/fog/libvirt/models/compute/server.rb', line 131

def xml_from_template

  template_options={
    :cpus                   => cpus,
    :memory_size            => memory_size,
    :domain_type            => domain_type,
    :name                   => name,
    :iso_file               => iso_file,
    :iso_dir                => iso_dir,
    :os_type                => os_type,
    :arch                   => arch,
    :volume_path            => volume_path,
    :volume_format_type     => volume_format_type,
    :network_interface_type => network_interface_type,
    :network_nat_network    => network_nat_network,
    :network_bridge_name    => network_bridge_name
  }
  vars          = ErbBinding.new(template_options)
  template_path = File.join(File.dirname(__FILE__),"templates","server.xml.erb")
  template      = File.open(template_path).readlines.join
  erb           = ERB.new(template)
  vars_binding  = vars.send(:get_binding)

  erb.result(vars_binding)
end