Class: BoxGrinder::LibvirtPlugin

Inherits:
BasePlugin show all
Defined in:
lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb

Instance Attribute Summary

Attributes inherited from BasePlugin

#deliverables, #plugin_info

Instance Method Summary collapse

Methods inherited from BasePlugin

#current_platform, #deliverables_exists?, #init, #initialize, #is_supported_os?, #is_supported_platform?, #merge_plugin_config, #read_plugin_config, #register_deliverable, #register_supported_os, #register_supported_platform, #run, #set_default_config_value, #subtype, #supported_oses, #validate_plugin_config

Methods included from Plugins

#plugin

Constructor Details

This class inherits a constructor from BoxGrinder::BasePlugin

Instance Method Details

#build_xml(opts = {}) ⇒ Object

Build the XML domain definition. If the user provides a script, it will be called after the basic definition has been constructed with the XML as the sole parameter. The output from stdout of the script will be used as the new domain definition.



237
238
239
240
241
242
243
244
245
246
247
248
249
250
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
# File 'lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb', line 237

def build_xml(opts = {})
  opts = {:bus => @bus, :os_type => :hvm}.merge!(opts)

  builder = Builder::XmlMarkup.new(:indent => 2)

  xml = builder.domain(:type => opts[:domain_type].to_s) do |domain|
    domain.name(@appliance_name)
    domain.description(@appliance_config.summary)
    domain.memory(@appliance_config.hardware.memory * 1024) #KB
    domain.vcpu(@appliance_config.hardware.cpus)
    domain.os do |os|
      os.type(opts[:os_type].to_s, :arch => @appliance_config.hardware.arch)
      os.boot(:dev => 'hd')
    end
    domain.devices do |devices|
      devices.disk(:type => 'file', :device => 'disk') do |disk|
        disk.source(:file => "#{@libvirt_image_uri}/#{File.basename(@previous_deliverables.disk)}")
        disk.target(:dev => 'hda', :bus => opts[:bus].to_s)
      end
      devices.interface(:type => 'network') do |interface|
        interface.source(:network => @network)
        interface.mac(:address => @mac) if @mac
      end
      devices.console(:type => 'pty') unless @noautoconsole
      devices.graphics(:type => 'vnc', :port => -1) unless @novnc
    end
    domain.features do |features|
      features.pae if @appliance_config.os.pae
    end
  end
  @log.debug xml

  # Let the user modify the XML specification to their requirements
  if @script
    @log.info "Attempting to run user provided script for modifying libVirt XML..."
    xml = IO::popen("#{@script} --domain '#{xml}'").read
    @log.debug "Response was: #{xml}"
  end
  xml
end

#determine_locallyObject

Make no external connections, just dump a basic XML skeleton and provide sensible defaults where user provided values are not given.



201
202
203
204
205
206
207
208
# File 'lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb', line 201

def determine_locally
  domain = @libvirt_capabilities.get_plugin(@previous_plugin_info).domain_rank.last
  generate_xml(OpenStruct.new({
    :domain_type => domain.name,
    :os_type => domain.virt_rank.last,
    :bus => domain.bus
  }))
end

#determine_remotelyObject

Interact with a libvirtd, attempt to determine optimal settings where possible. Register the appliance as a new domain.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb', line 160

def determine_remotely
  # Remove password field from URI, as libvirt doesn't support it directly. We can use it for passphrase if needed.
  lv_uri = URI::Generic.build(:scheme => @connection_uri.scheme, :userinfo => @connection_uri.user,
                              :host => @connection_uri.host, :path => @connection_uri.path,
                              :query => @connection_uri.query)

  # The authentication only pertains to libvirtd itself and _not_ the transport (e.g. SSH).
  conn = Libvirt::open_auth(lv_uri.to_s, [Libvirt::CRED_AUTHNAME, Libvirt::CRED_PASSPHRASE]) do |cred|
    case cred["type"]
      when Libvirt::CRED_AUTHNAME
        @connection_uri.user
      when Libvirt::CRED_PASSPHRASE
        @connection_uri.password
    end
  end

  if dom = get_existing_domain(conn, @appliance_name)
    unless @overwrite
      @log.fatal("A domain already exists with the name #{@appliance_name}. Set overwrite:true to automatically destroy and undefine it.")
      raise RuntimeError, "Domain '#{@appliance_name}' already exists"  #Make better specific exception
    end
    @log.info("Undefining existing domain #{@appliance_name}")
    undefine_domain(dom)
  end

  guest = @libvirt_capabilities.determine_capabilities(conn, @previous_plugin_info)

  raise "Remote libvirt machine offered no viable guests!" if guest.nil?

  xml = generate_xml(guest)
  @log.info("Defining domain #{@appliance_name}")
  conn.define_domain_xml(xml)
  xml
ensure
  if conn
    conn.close unless conn.closed?
  end
end

#executeObject



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb', line 139

def execute
  if @image_delivery_uri.scheme =~ /sftp/
    @log.info("Transferring file via SFTP...")
    upload_image
  else
    @log.info("Copying disk #{@previous_deliverables.disk} to: #{@image_delivery_uri.path}...")
    FileUtils.cp(@previous_deliverables.disk, @image_delivery_uri.path)
  end

  if @xml_only
    @log.info("Determining locally only.")
    xml = determine_locally
  else
    @log.info("Determining remotely.")
    xml = determine_remotely
  end
  write_xml(xml)
end

#generate_xml(guest) ⇒ Object

Preferentially choose user settings



228
229
230
231
232
# File 'lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb', line 228

def generate_xml(guest)
  build_xml(:domain_type => (@domain_type || guest.domain_type),
            :os_type => (@virt_type || guest.os_type),
            :bus => (@bus || guest.bus))
end

#set_defaultsObject



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb', line 97

def set_defaults
  set_default_config_value('connection_uri', '')
  set_default_config_value('script', false)
  set_default_config_value('image_delivery_uri', '/var/lib/libvirt/images')
  set_default_config_value('libvirt_image_uri', false)
  set_default_config_value('remote_no_verify', true)
  set_default_config_value('overwrite', false)
  set_default_config_value('default_permissions', 0770)
  set_default_config_value('xml_only', false)
  # Manual overrides
  set_default_config_value('appliance_name', [@appliance_config.name, @appliance_config.version, @appliance_config.release,
                                              @appliance_config.os.name, @appliance_config.os.version, @appliance_config.hardware.arch,
                                              current_platform].join("-"))
  set_default_config_value('domain_type', false)
  set_default_config_value('virt_type', false)
  set_default_config_value('bus', false)
  set_default_config_value('network', 'default')
  set_default_config_value('mac', false)
  set_default_config_value('noautoconsole', false)

  libvirt_code_patch
end

#upload_imageObject

Upload an image via SFTP



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb', line 211

def upload_image
  uploader = SFTPHelper.new(:log => @log)

  #SFTP library automagically uses keys registered with the OS first before trying a password.
  uploader.connect(@image_delivery_uri.host,
  (@image_delivery_uri.user || Etc.getlogin),
  :password => @image_delivery_uri.password)

  uploader.upload_files(@image_delivery_uri.path,
                        @default_permissions,
                        @overwrite,
                        File.basename(@previous_deliverables.disk) => @previous_deliverables.disk)
ensure
  uploader.disconnect if uploader.connected?
end

#validateObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb', line 120

def validate
  set_defaults

  ['connection_uri', 'xml_only', 'network', 'domain_type', 'virt_type', 'script',
   'bus', 'appliance_name', 'default_permissions', 'overwrite', 'noautoconsole',
  'mac'].each do |v|
    self.instance_variable_set(:"@#{v}", @plugin_config[v])
  end

  @libvirt_capabilities = LibvirtCapabilities.new(:log => @log)
  @image_delivery_uri = URI.parse(@plugin_config['image_delivery_uri'])
  @libvirt_image_uri = (@plugin_config['libvirt_image_uri'] || @image_delivery_uri.path)

  @remote_no_verify = @plugin_config['remote_no_verify'] ? 1 : 0

  (@connection_uri.include?('?') ? '&' : '?') + "no_verify=#{@remote_no_verify}"
  @connection_uri = URI.parse(@plugin_config['connection_uri'])
end