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 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/fog/libvirt/models/compute/server.rb', line 49

def initialize(attributes={} )

  self.xml  ||= nil unless attributes[:xml]
  self.persistent ||=true unless attributes[:persistent]
  self.cpus ||=1 unless attributes[:cpus]
  self.memory_size ||=256 *1024 unless attributes[:memory_size]
  self.name ||="fog-#{SecureRandom.random_number*10E14.to_i.round}" unless attributes[:name]

  self.os_type ||="hvm" unless attributes[:os_type]
  self.arch ||="x86_64" unless attributes[:arch]

  self.domain_type ||="kvm" unless attributes[:domain_type]

  self.iso_file ||=nil unless attributes[:iso_file]
  self.iso_dir ||="/var/lib/libvirt/images" unless attributes[:iso_dir]

  self.volume_format_type ||=nil unless attributes[:volume_format_type]
  self.volume_capacity ||=nil unless attributes[:volume_capacity]
  self.volume_allocation ||=nil unless attributes[:volume_allocation]

  self.volume_name ||=nil unless attributes[:volume_name]
  self.volume_pool_name ||=nil unless attributes[:volume_pool_name]
  self.volume_template_name ||=nil unless attributes[:volume_template_name]

  self.network_interface_type ||="nat" unless attributes[:network_interface_type]
  self.network_nat_network ||="default" unless attributes[:network_nat_network]
  self.network_bridge_name ||="br0" unless attributes[:network_bridge_name]

  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



392
393
394
# File 'lib/fog/libvirt/models/compute/server.rb', line 392

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

#private_key_pathObject



387
388
389
390
# File 'lib/fog/libvirt/models/compute/server.rb', line 387

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

#public_keyObject



401
402
403
# File 'lib/fog/libvirt/models/compute/server.rb', line 401

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

#public_key_pathObject



396
397
398
399
# File 'lib/fog/libvirt/models/compute/server.rb', line 396

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

#usernameObject



184
185
186
# File 'lib/fog/libvirt/models/compute/server.rb', line 184

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



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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/fog/libvirt/models/compute/server.rb', line 276

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}; "+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



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/fog/libvirt/models/compute/server.rb', line 108

def create_or_clone_volume

  volume_options=Hash.new

  unless self.volume_name.nil?
    volume_options[:name]=self.volume_name
  else
    extension = self.volume_format_type.nil? ? "img" : self.volume_format_type
    volume_name = "#{self.name}.#{extension}"
    volume_options[:name]=volume_name
  end

  # 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.length==1

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

    # 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 unless self.volume_format_type.nil?
    volume_options[:capacity]=self.volume_capacity unless self.volume_capacity.nil?
    volume_options[:allocation]=self.volume_allocation unless self.volume_allocation.nil?

    begin
      volume=connection.volumes.create(volume_options)
      self.volume_path=volume.path
      self.volume_format_type=volume.format_type unless self.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



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

def destroy(options={ :destroy_volumes => false})
  requires :raw
  if @raw.active?
    @raw.destroy
  end
  @raw.undefine
end

#haltObject

Alias for poweroff



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

def halt
   poweroff
end

#ip_address(key) ⇒ Object



378
379
380
381
382
383
384
385
# File 'lib/fog/libvirt/models/compute/server.rb', line 378

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



481
482
483
484
# File 'lib/fog/libvirt/models/compute/server.rb', line 481

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

#poweroffObject

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



223
224
225
226
# File 'lib/fog/libvirt/models/compute/server.rb', line 223

def poweroff
  requires :raw
  @raw.destroy
end

#private_ip_addressObject



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

def private_ip_address
  ip_address(:private)
end

#public_ip_addressObject



374
375
376
# File 'lib/fog/libvirt/models/compute/server.rb', line 374

def public_ip_address
  ip_address(:public)
end

#ready?Boolean

Returns:

  • (Boolean)


256
257
258
# File 'lib/fog/libvirt/models/compute/server.rb', line 256

def ready?
  state == "running"
end

#rebootObject



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

def reboot
  requires :raw
  @raw.reboot
end

#resumeObject



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

def resume
  requires :raw
  @raw.resume
end

#saveObject

Raises:



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/fog/libvirt/models/compute/server.rb', line 80

def save

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

  validate_template_options

  xml=xml_from_template if xml.nil?

  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.nil?
      domain=nil
      if self.persistent
        domain=connection.raw.define_domain_xml(xml)
      else
        domain=connection.raw.create_domain_xml(xml)
      end
      self.raw=domain
    end
  rescue
    raise Fog::Errors::Error.new("Error saving the server: #{$!}")
  end
end

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

Transfers a file



434
435
436
437
438
439
440
441
442
443
# File 'lib/fog/libvirt/models/compute/server.rb', line 434

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



447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/fog/libvirt/models/compute/server.rb', line 447

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



228
229
230
231
# File 'lib/fog/libvirt/models/compute/server.rb', line 228

def shutdown
  requires :raw
  @raw.shutdown
end

#ssh(commands) ⇒ Object



405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/fog/libvirt/models/compute/server.rb', line 405

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



419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/fog/libvirt/models/compute/server.rb', line 419

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



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/fog/libvirt/models/compute/server.rb', line 188

def start
  requires :raw

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

#stopObject



260
261
262
263
264
# File 'lib/fog/libvirt/models/compute/server.rb', line 260

def stop
  requires :raw

  @raw.shutdown
end

#suspendObject



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

def suspend
  requires :raw
  @raw.suspend
end

#to_fog_state(raw_state) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/fog/libvirt/models/compute/server.rb', line 243

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
  return state
end

#validate_template_optionsObject



152
153
154
155
156
# File 'lib/fog/libvirt/models/compute/server.rb', line 152

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



486
487
488
489
490
# File 'lib/fog/libvirt/models/compute/server.rb', line 486

def vnc_port

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

#xml_from_templateObject



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/fog/libvirt/models/compute/server.rb', line 158

def xml_from_template

  template_options={
    :cpus => self.cpus,
    :memory_size => self.memory_size,
    :domain_type => self.domain_type,
    :name => self.name,
    :iso_file => self.iso_file,
    :iso_dir => self.iso_dir,
    :os_type => self.os_type,
    :arch => self.arch,
    :volume_path => self.volume_path,
    :volume_format_type => self.volume_format_type,
    :network_interface_type => self.network_interface_type,
    :network_nat_network => self.network_nat_network,
    :network_bridge_name => self.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)
  result=erb.result(vars_binding)
  return result
end