Permalink
Browse files

Preliminary work for an mcollective plugin for libvirt.

  • Loading branch information...
kbarber committed Jul 31, 2011
1 parent d1dc441 commit 05808874715ca3e899861a0af139e6a48255d3cb
Showing with 395 additions and 0 deletions.
  1. +87 −0 files/mc-plugins/agent/libvirt.ddl
  2. +231 −0 files/mc-plugins/agent/libvirt.rb
  3. +77 −0 files/mc-plugins/application/libvirt.rb
@@ -0,0 +1,87 @@
+metadata :name => "SimpleRPC Agent For Libvirt Management",
+ :description => "Agent To Manage Libvirt",
+ :author => "Ken Barber",
+ :license => "ASLv2",
+ :version => "0.0.1",
+ :url => "http://github.com/puppetlabs/puppetlabs-libvirt",
+ :timeout => 180
+
+action "domain_list", :description => "List domains" do
+ display :always
+
+ output :domain_list,
+ :description => "Hash with small amount of information per domain",
+ :display_as => "Domain List"
+
+ output :status,
+ :description => "Status of action",
+ :display_as => "Status"
+end
+
+action "domain_detail", :description => "List domains" do
+ display :always
+
+ input :uuid,
+ :prompt => "UUID of domain",
+ :description => "UUID of domain",
+ :type => :string,
+ :validation => '.',
+ :optional => false,
+ :maxlength => 250
+
+ output :domain_detail,
+ :description => "Hash with detailed information on a domain",
+ :display_as => "Domain Detail"
+
+ output :status,
+ :description => "Status of action",
+ :display_as => "Status"
+end
+
+action "domain_shutdown", :description => "Shutdown domain" do
+ display :always
+
+ input :uuid,
+ :prompt => "UUID of domain",
+ :description => "UUID of domain",
+ :type => :string,
+ :validation => '.',
+ :optional => false,
+ :maxlength => 250
+
+ output :status,
+ :description => "Status of action",
+ :display_as => "Status"
+end
+
+action "domain_destroy", :description => "Destroy domain" do
+ display :always
+
+ input :uuid,
+ :prompt => "UUID of domain",
+ :description => "UUID of domain",
+ :type => :string,
+ :validation => '.',
+ :optional => false,
+ :maxlength => 250
+
+ output :status,
+ :description => "Status of action",
+ :display_as => "Status"
+end
+
+action "domain_create", :description => "Create domain" do
+ display :always
+
+ input :name,
+ :prompt => "Name of domain",
+ :description => "Name of domain",
+ :type => :string,
+ :validation => '.',
+ :optional => false,
+ :maxlength => 100
+
+ output :status,
+ :description => "Status of action",
+ :display_as => "Status"
+end
@@ -0,0 +1,231 @@
+require 'libvirt'
+require 'xmlsimple'
+
+module MCollective
+ module Agent
+ # An agent that interacts with libvirt.
+ #
+ # See http://github.com/puppetlabs/puppetlabs-libvirt/
+ #
+ # Released under the terms of ASL 2.0, same as Puppet
+ class Libvirt<RPC::Agent
+ metadata :name => "SimpleRPC Libvirt Agent",
+ :description => "Agent to interact with libvirt",
+ :author => "Ken Barber",
+ :license => "ASLv2",
+ :version => "0.0.1",
+ :url => "http://github.com/puppetlabs/puppetlabs-libvirt/",
+ :timeout => 60
+
+ # This is a convenience wrapper around opening and closing the connection to
+ # libvirt.
+ def libvirt_transaction(uri = "qemu:///system")
+ conn = ::Libvirt::open(uri)
+
+ yield conn
+
+ conn.close
+ end
+
+ # This action returns a short list of domains for each hypervisor.
+ action "domain_list" do
+ begin
+ Log.instance.debug("Getting domain_detail for libvirt")
+
+ libvirt_transaction do |conn|
+ domains = conn.list_domains
+
+ reply["domain_list"] ||= {}
+ domains.each do |id|
+ domain = conn.lookup_domain_by_id(id)
+ reply["domain_list"][domain.uuid] = {
+ "name" => domain.name,
+ "id" => domain.id,
+ "num_vcpus" => domain.info.nr_virt_cpu || 0,
+ "memory" => domain.info.memory || 0,
+ "state" => domain.info.state,
+ }
+ end
+ end
+
+ reply["status"] = "ok"
+ rescue Exception => e
+ reply.fail "#{e}"
+ end
+ end
+
+ # This action returns detailed information gathered from
+ # libvirt for a particulur domain.
+ action "domain_detail" do
+ validate :uuid, String
+ uuid = request[:uuid]
+
+ begin
+ Log.instance.debug("Getting domain_detail for libvirt")
+
+ libvirt_transaction do |conn|
+ domain = conn.lookup_domain_by_uuid(uuid)
+
+ # Grab XML data and turn it into a hash
+ xml_desc = XmlSimple.xml_in(domain.xml_desc, {})
+
+ # The interface for information is a bit
+ # haphazard, so I'm cherrypicking to populate
+ # this hash.
+ reply["domain_detail"] = {
+ "uuid" => uuid,
+ "cpu_time" => domain.info.cpu_time,
+ "state" => domain.info.state,
+ "os_type" => domain.os_type,
+ "xml_desc" => xml_desc,
+ }
+ end
+ rescue Exception => e
+ reply.fail "#{e}"
+ end
+ end
+
+ action "domain_shutdown" do
+ validate :uuid, String
+ uuid = request[:uuid]
+
+ begin
+ Log.instance.debug("Doing shutdown_domain for libvirt")
+
+ libvirt_transaction do |conn|
+ domain = conn.lookup_domain_by_uuid(uuid)
+ domain.shutdown
+ end
+
+ reply["status"] = ["ok",uuid.to_s]
+ rescue Exception => e
+ reply.fail "#{e}"
+ end
+
+ end
+
+ # Destroy a domain
+ action "domain_destroy" do
+ validate :uuid, String
+ uuid = request[:uuid]
+
+ begin
+ Log.instance.debug("Doing domain_destroy for libvirt")
+
+ libvirt_transaction do |conn|
+ domain = conn.lookup_domain_by_uuid(uuid)
+ domain.destroy
+ end
+
+ reply["status"] = ["ok",uuid.to_s]
+ rescue Exception => e
+ reply.fail "#{e}"
+ end
+
+ end
+
+ # This action attempts to start a domain that exists.
+ action "domain_start" do
+ validate :uuid, String
+ uuid = request[:uuid]
+
+ begin
+ Log.instance.debug("Doing domain_start for libvirt")
+
+ libvirt_transaction do |conn|
+ domain = conn.lookup_domain_by_uuid(uuid)
+ domain.start
+ end
+
+ reply["status"] = ["ok", uuid.to_s]
+ rescue Exception => e
+ reply.fail "#{e}"
+ end
+
+ end
+
+ # This action creates a domain.
+ action "domain_create" do
+ validate :name, String
+ name = request[:name]
+
+ # start by creating a disk
+ # TODO: this is very much inserted just to make this 'work' for now
+ `qemu-img create -f qcow2 /srv/virt/virtuals/#{name}.img.disk.0 10000000000`
+
+ # TODO: most basic way to create a template obviously.
+ xml_template = <<-EOS
+<domain type='kvm'>
+ <name>#{name}</name>
+ <memory>256000</memory>
+ <vcpu>1</vcpu>
+ <os>
+ <type arch='x86_64'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/bin/kvm</emulator>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2'/>
+ <source file='/srv/virt/virtuals/#{name}.img.disk.0'/>
+ <target dev='vda' bus='virtio'/>
+ <alias name='virtio-disk0'/>
+ </disk>
+ <controller type='ide' index='0'>
+ <alias name='ide0'/>
+ </controller>
+ <interface type='bridge'>
+ <source bridge='virbr1'/>
+ <model type='virtio'/>
+ <alias name='net0'/>
+ </interface>
+ <serial type='pty'>
+ <target port='0'/>
+ <alias name='serial0'/>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ <alias name='serial0'/>
+ </console>
+ <input type='mouse' bus='ps2'/>
+ <graphics type='vnc' autoport='yes' listen='0.0.0.0'/>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ <alias name='video0'/>
+ </video>
+ <memballoon model='virtio'>
+ <alias name='balloon0'/>
+ </memballoon>
+ </devices>
+</domain>
+EOS
+
+ begin
+ Log.instance.debug("Doing list_nodedevices for libvirt")
+
+ libvirt_transaction do |conn|
+ domain = conn.define_domain_xml(xml_template)
+ domain.create
+ end
+
+ reply["status"] = "ok"
+ rescue Exception => e
+ reply.fail "#{e}: #{e.libvirt_message}"
+ end
+
+ end
+
+ end
+ end
+end
+
+# vi:tabstop=4:expandtab:ai:filetype=ruby
@@ -0,0 +1,77 @@
+require 'pp'
+
+class MCollective::Application::Libvirt<MCollective::Application
+ description "Libvirt Manager"
+ usage "Usage: mc libvirt [options] action [args]"
+
+ def post_option_parser(configuration)
+ configuration[:action] = ARGV.shift
+ configuration[:arguments] ||= []
+ ARGV.each do |v|
+ if v =~ /^(.+?)=(.+)$/
+ configuration[:arguments] << v
+ else
+ STDERR.puts("Could not parse --arg #{v}")
+ end
+ end
+
+ # convert arguments to symbols for keys to comply with simplerpc conventions
+ args = configuration[:arguments].clone
+ configuration[:arguments] = {}
+
+ args.each do |v|
+ if v =~ /^(.+?)=(.+)$/
+ configuration[:arguments][$1.to_sym] = $2
+ end
+ end
+ end
+
+ def validate_configuration(configuration)
+ end
+
+ def main
+ mc = rpcclient("libvirt", :options => options)
+
+ action = configuration[:action]
+
+ data = {}
+ mc.send(action, configuration[:arguments]).each do |resp|
+ if resp[:statuscode] == 0
+ data[resp[:sender]] ||= {}
+ case action
+ when "domain_list"
+ data[resp[:sender]][:domain_list] = resp[:data]["domain_list"]
+ data[resp[:sender]][:status] = resp[:data]["status"]
+ when "domain_detail"
+ data[resp[:sender]][:domain_detail] = resp[:data]["domain_detail"]
+ data[resp[:sender]][:status] = resp[:data]["status"]
+ else
+ data[resp[:sender]][:status] = resp[:data]["status"]
+ end
+ else
+ printf("%-40s error = %s\n", resp[:sender], resp[:statusmsg])
+ end
+ end
+
+ # summarize
+ case action
+ when "domain_list"
+ puts sprintf("%-36s %-4s %-8s %-4s %-4s %-6s %-15s", "UUID", "ID", "NAME", "STATUS", "VCPU", "MEM", "NODE")
+ data.each do |sender,value|
+ value[:domain_list].each do |uuid,domain_info|
+ # Basic conversion to megabytes - but probably should have a nicer way of doing
+ # this for other unit sizes
+ memory = (domain_info["memory"].to_i/1024).floor.to_s + "M"
+ status = domain_info["status"].to_i ? "running" : "stopped"
+ puts sprintf("%-36.36s %-4.4s %-8.8s %-6.6s %-4.4s %-6.6s %-15.15s", uuid, domain_info["id"], domain_info["name"], status, domain_info["num_vcpus"], memory, sender)
+ end
+ end
+ else
+ data.each do |sender,data|
+ puts "#{sender}:"
+ pp data
+ end
+ end
+ end
+end
+# vi:tabstop=4:expandtab:ai

0 comments on commit 0580887

Please sign in to comment.