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+
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.
# 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.
# 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.
# 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.
# 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.
# 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.
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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