Class BoxGrinder::LibvirtPlugin
In: lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb
lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb
Parent: BasePlugin

@plugin_config [String] connection_uri Libvirt endpoint address. If you are

  using authenticated transport such as +ssh+ you should register your keys with
  an ssh agent. See: {http://libvirt.org/uri.html Libvirt Connection URIs}.
  * Default: +empty string+
  * Examples: <tt>qemu+ssh://user@example.com/system</tt>
  * +qemu:///system+

@plugin_config [String] image_delivery_uri Where to deliver the image to. This must be a

  local path or an SFTP address. The local ssh-agent is used for keys if available.
  * Default: +/var/lib/libvirt/images+
  * Examples: +sftp\://user@example.com/some/path+
  * +sftp\://user:pass@example.com/some/path+ It is advisable to use keys with ssh-agent.

@plugin_config [String] libvirt_image_uri Where the image will be on the Libvirt machine.

  * Default: +image_delivery_uri+ _path_ element.
  * Example: +/var/lib/libvirt/images+

@plugin_config [Int] default_permissions Permissions of delivered image. Examples:

  * Default: +0770+
  * Examples: +0755+, +0775+

@plugin_config [Int] overwrite Overwrite any identically named file at the delivery path.

  Also undefines any existing domain of the same name.
  * Default: +false+

@plugin_config [String] script Path to user provided script to modify XML before registration

  with Libvirt. Plugin passes the raw XML, and consumes stdout to use as revised XML document.

@plugin_config [Bool] remote_no_verify Disable certificate verification procedures

  * Default: +true+

@plugin_config [Bool] xml_only Do not connect to the Libvirt hypervisor, just assume sensible

  defaults where no user values are provided, and produce the XML domain.
  * Default: +false+

@plugin_config [String] appliance_name Name for the appliance to be registered as in Libvirt.

  At present the user can only specify literal strings.
  * Default: +name-version-release-os_name-os_version-arch-platform+
  * Example: +boxgrinder-f16-rocks+

@plugin_config [String] domain_type Libvirt domain type.

  * Default is a calculated value. Unless you are using +xml_only+ the remote instance will
    be contacted and an attempt to determine the best value will be made. If +xml_only+
    is set then a safe pre-determined default is used. User-set values take precedence.
    See _type_: {http://libvirt.org/formatdomain.html#elements Domain format}
  * Examples: +qemu+, +kvm+, +xen+

@plugin_config [String] virt_type Libvirt virt type.

  * Default is a calculated value. Where available paravirtual is preferred.
    See _type_: {http://libvirt.org/formatdomain.html#elementsOSBIOS BIOS bootloader}.
  * Examples: +hvm+, +xen+, +linux+

@plugin_config [String] bus Disk bus.

  * Default is a pre-determined value depending on the domain type. User-set values take
    precedence
  * Examples: +virtio+, +ide+

@plugin_config [String] network Network name. If you require a more complex setup

  than a simple network name, then you should create and set a +script+.
  * Default: +default+

Methods

Public Instance methods

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.

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 237
237:     def build_xml(opts = {})
238:       opts = {:bus => @bus, :os_type => :hvm}.merge!(opts)
239: 
240:       builder = Builder::XmlMarkup.new(:indent => 2)
241: 
242:       xml = builder.domain(:type => opts[:domain_type].to_s) do |domain|
243:         domain.name(@appliance_name)
244:         domain.description(@appliance_config.summary)
245:         domain.memory(@appliance_config.hardware.memory * 1024) #KB
246:         domain.vcpu(@appliance_config.hardware.cpus)
247:         domain.os do |os|
248:           os.type(opts[:os_type].to_s, :arch => @appliance_config.hardware.arch)
249:           os.boot(:dev => 'hd')
250:         end
251:         domain.devices do |devices|
252:           devices.disk(:type => 'file', :device => 'disk') do |disk|
253:             disk.source(:file => "#{@libvirt_image_uri}/#{File.basename(@previous_deliverables.disk)}")
254:             disk.target(:dev => 'hda', :bus => opts[:bus].to_s)
255:           end
256:           devices.interface(:type => 'network') do |interface|
257:             interface.source(:network => @network)
258:             interface.mac(:address => @mac) if @mac
259:           end
260:           devices.console(:type => 'pty') unless @noautoconsole
261:           devices.graphics(:type => 'vnc', :port => -1) unless @novnc
262:         end
263:         domain.features do |features|
264:           features.pae if @appliance_config.os.pae
265:         end
266:       end
267:       @log.debug xml
268: 
269:       # Let the user modify the XML specification to their requirements
270:       if @script
271:         @log.info "Attempting to run user provided script for modifying libVirt XML..."
272:         xml = IO::popen("#{@script} --domain '#{xml}'").read
273:         @log.debug "Response was: #{xml}"
274:       end
275:       xml
276:     end

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.

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 237
237:     def build_xml(opts = {})
238:       opts = {:bus => @bus, :os_type => :hvm}.merge!(opts)
239: 
240:       builder = Builder::XmlMarkup.new(:indent => 2)
241: 
242:       xml = builder.domain(:type => opts[:domain_type].to_s) do |domain|
243:         domain.name(@appliance_name)
244:         domain.description(@appliance_config.summary)
245:         domain.memory(@appliance_config.hardware.memory * 1024) #KB
246:         domain.vcpu(@appliance_config.hardware.cpus)
247:         domain.os do |os|
248:           os.type(opts[:os_type].to_s, :arch => @appliance_config.hardware.arch)
249:           os.boot(:dev => 'hd')
250:         end
251:         domain.devices do |devices|
252:           devices.disk(:type => 'file', :device => 'disk') do |disk|
253:             disk.source(:file => "#{@libvirt_image_uri}/#{File.basename(@previous_deliverables.disk)}")
254:             disk.target(:dev => 'hda', :bus => opts[:bus].to_s)
255:           end
256:           devices.interface(:type => 'network') do |interface|
257:             interface.source(:network => @network)
258:             interface.mac(:address => @mac) if @mac
259:           end
260:           devices.console(:type => 'pty') unless @noautoconsole
261:           devices.graphics(:type => 'vnc', :port => -1) unless @novnc
262:         end
263:         domain.features do |features|
264:           features.pae if @appliance_config.os.pae
265:         end
266:       end
267:       @log.debug xml
268: 
269:       # Let the user modify the XML specification to their requirements
270:       if @script
271:         @log.info "Attempting to run user provided script for modifying libVirt XML..."
272:         xml = IO::popen("#{@script} --domain '#{xml}'").read
273:         @log.debug "Response was: #{xml}"
274:       end
275:       xml
276:     end

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

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 201
201:     def determine_locally
202:       domain = @libvirt_capabilities.get_plugin(@previous_plugin_info).domain_rank.last
203:       generate_xml(OpenStruct.new({
204:         :domain_type => domain.name,
205:         :os_type => domain.virt_rank.last,
206:         :bus => domain.bus
207:       }))
208:     end

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

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 201
201:     def determine_locally
202:       domain = @libvirt_capabilities.get_plugin(@previous_plugin_info).domain_rank.last
203:       generate_xml(OpenStruct.new({
204:         :domain_type => domain.name,
205:         :os_type => domain.virt_rank.last,
206:         :bus => domain.bus
207:       }))
208:     end

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

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 160
160:     def determine_remotely
161:       # Remove password field from URI, as libvirt doesn't support it directly. We can use it for passphrase if needed.
162:       lv_uri = URI::Generic.build(:scheme => @connection_uri.scheme, :userinfo => @connection_uri.user,
163:                                   :host => @connection_uri.host, :path => @connection_uri.path,
164:                                   :query => @connection_uri.query)
165: 
166:       # The authentication only pertains to libvirtd itself and _not_ the transport (e.g. SSH).
167:       conn = Libvirt::open_auth(lv_uri.to_s, [Libvirt::CRED_AUTHNAME, Libvirt::CRED_PASSPHRASE]) do |cred|
168:         case cred["type"]
169:           when Libvirt::CRED_AUTHNAME
170:             @connection_uri.user
171:           when Libvirt::CRED_PASSPHRASE
172:             @connection_uri.password
173:         end
174:       end
175: 
176:       if dom = get_existing_domain(conn, @appliance_name)
177:         unless @overwrite
178:           @log.fatal("A domain already exists with the name #{@appliance_name}. Set overwrite:true to automatically destroy and undefine it.")
179:           raise RuntimeError, "Domain '#{@appliance_name}' already exists"  #Make better specific exception
180:         end
181:         @log.info("Undefining existing domain #{@appliance_name}")
182:         undefine_domain(dom)
183:       end
184: 
185:       guest = @libvirt_capabilities.determine_capabilities(conn, @previous_plugin_info)
186: 
187:       raise "Remote libvirt machine offered no viable guests!" if guest.nil?
188: 
189:       xml = generate_xml(guest)
190:       @log.info("Defining domain #{@appliance_name}")
191:       conn.define_domain_xml(xml)
192:       xml
193:     ensure
194:       if conn
195:         conn.close unless conn.closed?
196:       end
197:     end

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

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 160
160:     def determine_remotely
161:       # Remove password field from URI, as libvirt doesn't support it directly. We can use it for passphrase if needed.
162:       lv_uri = URI::Generic.build(:scheme => @connection_uri.scheme, :userinfo => @connection_uri.user,
163:                                   :host => @connection_uri.host, :path => @connection_uri.path,
164:                                   :query => @connection_uri.query)
165: 
166:       # The authentication only pertains to libvirtd itself and _not_ the transport (e.g. SSH).
167:       conn = Libvirt::open_auth(lv_uri.to_s, [Libvirt::CRED_AUTHNAME, Libvirt::CRED_PASSPHRASE]) do |cred|
168:         case cred["type"]
169:           when Libvirt::CRED_AUTHNAME
170:             @connection_uri.user
171:           when Libvirt::CRED_PASSPHRASE
172:             @connection_uri.password
173:         end
174:       end
175: 
176:       if dom = get_existing_domain(conn, @appliance_name)
177:         unless @overwrite
178:           @log.fatal("A domain already exists with the name #{@appliance_name}. Set overwrite:true to automatically destroy and undefine it.")
179:           raise RuntimeError, "Domain '#{@appliance_name}' already exists"  #Make better specific exception
180:         end
181:         @log.info("Undefining existing domain #{@appliance_name}")
182:         undefine_domain(dom)
183:       end
184: 
185:       guest = @libvirt_capabilities.determine_capabilities(conn, @previous_plugin_info)
186: 
187:       raise "Remote libvirt machine offered no viable guests!" if guest.nil?
188: 
189:       xml = generate_xml(guest)
190:       @log.info("Defining domain #{@appliance_name}")
191:       conn.define_domain_xml(xml)
192:       xml
193:     ensure
194:       if conn
195:         conn.close unless conn.closed?
196:       end
197:     end

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 139
139:     def execute
140:       if @image_delivery_uri.scheme =~ /sftp/
141:         @log.info("Transferring file via SFTP...")
142:         upload_image
143:       else
144:         @log.info("Copying disk #{@previous_deliverables.disk} to: #{@image_delivery_uri.path}...")
145:         FileUtils.cp(@previous_deliverables.disk, @image_delivery_uri.path)
146:       end
147: 
148:       if @xml_only
149:         @log.info("Determining locally only.")
150:         xml = determine_locally
151:       else
152:         @log.info("Determining remotely.")
153:         xml = determine_remotely
154:       end
155:       write_xml(xml)
156:     end

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 139
139:     def execute
140:       if @image_delivery_uri.scheme =~ /sftp/
141:         @log.info("Transferring file via SFTP...")
142:         upload_image
143:       else
144:         @log.info("Copying disk #{@previous_deliverables.disk} to: #{@image_delivery_uri.path}...")
145:         FileUtils.cp(@previous_deliverables.disk, @image_delivery_uri.path)
146:       end
147: 
148:       if @xml_only
149:         @log.info("Determining locally only.")
150:         xml = determine_locally
151:       else
152:         @log.info("Determining remotely.")
153:         xml = determine_remotely
154:       end
155:       write_xml(xml)
156:     end

Preferentially choose user settings

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 228
228:     def generate_xml(guest)
229:       build_xml(:domain_type => (@domain_type || guest.domain_type),
230:                 :os_type => (@virt_type || guest.os_type),
231:                 :bus => (@bus || guest.bus))
232:     end

Preferentially choose user settings

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 228
228:     def generate_xml(guest)
229:       build_xml(:domain_type => (@domain_type || guest.domain_type),
230:                 :os_type => (@virt_type || guest.os_type),
231:                 :bus => (@bus || guest.bus))
232:     end

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 97
 97:     def set_defaults
 98:       set_default_config_value('connection_uri', '')
 99:       set_default_config_value('script', false)
100:       set_default_config_value('image_delivery_uri', '/var/lib/libvirt/images')
101:       set_default_config_value('libvirt_image_uri', false)
102:       set_default_config_value('remote_no_verify', true)
103:       set_default_config_value('overwrite', false)
104:       set_default_config_value('default_permissions', 0770)
105:       set_default_config_value('xml_only', false)
106:       # Manual overrides
107:       set_default_config_value('appliance_name', [@appliance_config.name, @appliance_config.version, @appliance_config.release,
108:                                                   @appliance_config.os.name, @appliance_config.os.version, @appliance_config.hardware.arch,
109:                                                   current_platform].join("-"))
110:       set_default_config_value('domain_type', false)
111:       set_default_config_value('virt_type', false)
112:       set_default_config_value('bus', false)
113:       set_default_config_value('network', 'default')
114:       set_default_config_value('mac', false)
115:       set_default_config_value('noautoconsole', false)
116: 
117:       libvirt_code_patch
118:     end

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 97
 97:     def set_defaults
 98:       set_default_config_value('connection_uri', '')
 99:       set_default_config_value('script', false)
100:       set_default_config_value('image_delivery_uri', '/var/lib/libvirt/images')
101:       set_default_config_value('libvirt_image_uri', false)
102:       set_default_config_value('remote_no_verify', true)
103:       set_default_config_value('overwrite', false)
104:       set_default_config_value('default_permissions', 0770)
105:       set_default_config_value('xml_only', false)
106:       # Manual overrides
107:       set_default_config_value('appliance_name', [@appliance_config.name, @appliance_config.version, @appliance_config.release,
108:                                                   @appliance_config.os.name, @appliance_config.os.version, @appliance_config.hardware.arch,
109:                                                   current_platform].join("-"))
110:       set_default_config_value('domain_type', false)
111:       set_default_config_value('virt_type', false)
112:       set_default_config_value('bus', false)
113:       set_default_config_value('network', 'default')
114:       set_default_config_value('mac', false)
115:       set_default_config_value('noautoconsole', false)
116: 
117:       libvirt_code_patch
118:     end

Upload an image via SFTP

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 211
211:     def upload_image
212:       uploader = SFTPHelper.new(:log => @log)
213: 
214:       #SFTP library automagically uses keys registered with the OS first before trying a password.
215:       uploader.connect(@image_delivery_uri.host,
216:       (@image_delivery_uri.user || Etc.getlogin),
217:       :password => @image_delivery_uri.password)
218: 
219:       uploader.upload_files(@image_delivery_uri.path,
220:                             @default_permissions,
221:                             @overwrite,
222:                             File.basename(@previous_deliverables.disk) => @previous_deliverables.disk)
223:     ensure
224:       uploader.disconnect if uploader.connected?
225:     end

Upload an image via SFTP

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 211
211:     def upload_image
212:       uploader = SFTPHelper.new(:log => @log)
213: 
214:       #SFTP library automagically uses keys registered with the OS first before trying a password.
215:       uploader.connect(@image_delivery_uri.host,
216:       (@image_delivery_uri.user || Etc.getlogin),
217:       :password => @image_delivery_uri.password)
218: 
219:       uploader.upload_files(@image_delivery_uri.path,
220:                             @default_permissions,
221:                             @overwrite,
222:                             File.basename(@previous_deliverables.disk) => @previous_deliverables.disk)
223:     ensure
224:       uploader.disconnect if uploader.connected?
225:     end

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 120
120:     def validate
121:       set_defaults
122: 
123:       ['connection_uri', 'xml_only', 'network', 'domain_type', 'virt_type', 'script',
124:        'bus', 'appliance_name', 'default_permissions', 'overwrite', 'noautoconsole',
125:       'mac'].each do |v|
126:         self.instance_variable_set("@#{v}""@#{v}", @plugin_config[v])
127:       end
128: 
129:       @libvirt_capabilities = LibvirtCapabilities.new(:log => @log)
130:       @image_delivery_uri = URI.parse(@plugin_config['image_delivery_uri'])
131:       @libvirt_image_uri = (@plugin_config['libvirt_image_uri'] || @image_delivery_uri.path)
132: 
133:       @remote_no_verify = @plugin_config['remote_no_verify'] ? 1 : 0
134: 
135:       (@connection_uri.include?('?') ? '&' : '?') + "no_verify=#{@remote_no_verify}"
136:       @connection_uri = URI.parse(@plugin_config['connection_uri'])
137:     end

[Source]

     # File lib/boxgrinder-build/plugins/delivery/libvirt/libvirt-plugin.rb, line 120
120:     def validate
121:       set_defaults
122: 
123:       ['connection_uri', 'xml_only', 'network', 'domain_type', 'virt_type', 'script',
124:        'bus', 'appliance_name', 'default_permissions', 'overwrite', 'noautoconsole',
125:       'mac'].each do |v|
126:         self.instance_variable_set("@#{v}""@#{v}", @plugin_config[v])
127:       end
128: 
129:       @libvirt_capabilities = LibvirtCapabilities.new(:log => @log)
130:       @image_delivery_uri = URI.parse(@plugin_config['image_delivery_uri'])
131:       @libvirt_image_uri = (@plugin_config['libvirt_image_uri'] || @image_delivery_uri.path)
132: 
133:       @remote_no_verify = @plugin_config['remote_no_verify'] ? 1 : 0
134: 
135:       (@connection_uri.include?('?') ? '&' : '?') + "no_verify=#{@remote_no_verify}"
136:       @connection_uri = URI.parse(@plugin_config['connection_uri'])
137:     end

[Validate]