This series of patches makes changes to the new ovirt-client repo. The repo can be found at git://ovirt.org/ovirt-client; the same people that have commit access to the server repo have commit for this repo. Since I was so excited to establish a new repo, I already pushed all these patches to the new repo, but I would still appreciate comments/acks on these. The patch series has to be applied against the very first commit in the repo, which only copied files from the server to the client repo, though it's probably simpler to just check out ovirt-client and review the log of the next branch. Please read the README first before trying the ovirt CLI out - you need a very recent version of activeresource (>= 2.2.2) and the 'main' rubygem for the CLI to work. David
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 01/11] Indent with 2 spaces
Only whitespace changes, no functional changes
---
examples/script.rb | 50 ++++++++++----------
lib/ovirt.rb | 126 ++++++++++++++++++++++++++--------------------------
2 files changed, 88 insertions(+), 88 deletions(-)
diff --git a/examples/script.rb b/examples/script.rb
index 1485535..f6f6839 100755
--- a/examples/script.rb
+++ b/examples/script.rb
@@ -10,27 +10,27 @@ require 'optparse'
require 'ovirt'
def move_random_host(hosts, pool)
- host = hosts[rand(hosts.size)]
- puts "Move #{host.hostname} to #{pool.name}"
- pool.hosts << host
- pool.save
+ host = hosts[rand(hosts.size)]
+ puts "Move #{host.hostname} to #{pool.name}"
+ pool.hosts << host
+ pool.save
end
def element_path(obj)
- "[#{obj.class.element_path(obj.id)}]"
+ "[#{obj.class.element_path(obj.id)}]"
end
def print_pool(pool)
- puts "\n\nPool #{pool.name}: #{pool.hosts.size} hosts,
#{pool.storage_pools.size} storage pools #{element_path(pool)} "
- puts "=" * 75
- pool.hosts.each do |h|
- printf "%-36s %s\n", h.hostname, element_path(h)
- end
- pool.storage_pools.each do |sp|
- type = sp.nfs? ? "NFS" : "iSCSI"
- printf "%-5s %-30s %s\n", type, sp.label, element_path(sp)
- end
- puts "-" * 75
+ puts "\n\nPool #{pool.name}: #{pool.hosts.size} hosts,
#{pool.storage_pools.size} storage pools #{element_path(pool)} "
+ puts "=" * 75
+ pool.hosts.each do |h|
+ printf "%-36s %s\n", h.hostname, element_path(h)
+ end
+ pool.storage_pools.each do |sp|
+ type = sp.nfs? ? "NFS" : "iSCSI"
+ printf "%-5s %-30s %s\n", type, sp.label, element_path(sp)
+ end
+ puts "-" * 75
end
# Plumbing so we can find the OVirt server
@@ -45,17 +45,17 @@ opts.separator "Global options:"
opts.on("-s", "--server=URL", "The OVirt server. Since
there is no auth\n" +
"#{" "*37}yet, must be the mongrel server port.\n"
+
"#{" "*37}Overrides env var OVIRT_SERVER") do |val|
- OVirt::Base.site = val
+ OVirt::Base.site = val
end
opts.order(ARGV)
unless OVirt::Base.site
- $stderr.puts <<EOF
+ $stderr.puts <<EOF
You must specify the OVirt server to connect to, either with the
--server option or through the OVIRT_SERVER environment variable
EOF
- exit 1
+ exit 1
end
OVirt::Base.login
@@ -71,23 +71,23 @@ print_pool(defpool)
# Create a new hardware pool
mypool = OVirt::HardwarePool.find_by_path("/default/mypool")
unless mypool
- puts "Create mypool"
- mypool = OVirt::HardwarePool.create( { :parent_id => defpool.id,
- :name => "mypool" } )
+ puts "Create mypool"
+ mypool = OVirt::HardwarePool.create( { :parent_id => defpool.id,
+ :name => "mypool" } )
end
# Move some hosts around
puts
if defpool.hosts.size > 1
- move_random_host(defpool.hosts, mypool)
+ move_random_host(defpool.hosts, mypool)
elsif mypool.hosts.size > 0
- move_random_host(mypool.hosts, defpool)
+ move_random_host(mypool.hosts, defpool)
end
# Delete all storage pools for mypool and add a new one
mypool.storage_pools.each do |sp|
- puts "Delete storage pool #{sp.id}"
- sp.destroy
+ puts "Delete storage pool #{sp.id}"
+ sp.destroy
end
storage_pool = OVirt::StoragePool.create( { :storage_type =>
"NFS",
diff --git a/lib/ovirt.rb b/lib/ovirt.rb
index 15dc467..fc3ac2a 100644
--- a/lib/ovirt.rb
+++ b/lib/ovirt.rb
@@ -18,85 +18,85 @@ class ActiveResource::Connection
end
module OVirt
- class Base < ActiveResource::Base
- LOGIN_PATH = "login/login"
-
- def self.login
- response = nil
- begin
- response = connection.get(prefix + LOGIN_PATH)
- rescue ActiveResource::Redirection => e
- response = e.response
- end
- unless connection.session = session_cookie(response)
- raise "Authentication failed"
- end
+ class Base < ActiveResource::Base
+ LOGIN_PATH = "login/login"
+
+ def self.login
+ response = nil
+ begin
+ response = connection.get(prefix + LOGIN_PATH)
+ rescue ActiveResource::Redirection => e
+ response = e.response
end
+ unless connection.session = session_cookie(response)
+ raise "Authentication failed"
+ end
+ end
- private
- def self.session_cookie(response)
- if cookies = response.get_fields("Set-Cookie")
- cookies.find { |cookie|
- cookie.split(";")[0].split("=")[0] ==
"_ovirt_session_id"
- }
- end
+ private
+ def self.session_cookie(response)
+ if cookies = response.get_fields("Set-Cookie")
+ cookies.find { |cookie|
+ cookie.split(";")[0].split("=")[0] ==
"_ovirt_session_id"
+ }
end
+ end
+
+ end
+
+ class HardwarePool < Base
+ def self.find_by_path(path)
+ find(:first, :params => { :path => path })
+ end
+ def self.default_pool
+ find(:first, :params => { :path => "/default" })
end
+ end
- class HardwarePool < Base
- def self.find_by_path(path)
- find(:first, :params => { :path => path })
- end
+ class StoragePool < Base
+ def iscsi?
+ attributes["type"] == "IscsiStoragePool"
+ end
- def self.default_pool
- find(:first, :params => { :path => "/default" })
- end
+ def nfs?
+ attributes["type"] == "NfsStoragePool"
end
- class StoragePool < Base
- def iscsi?
- attributes["type"] == "IscsiStoragePool"
- end
-
- def nfs?
- attributes["type"] == "NfsStoragePool"
- end
-
- def label
- if iscsi?
- "#{ip_addr}:#{port}:#{target}"
- elsif nfs?
- "#{ip_addr}:#{export_path}"
- else
- raise "Unknown type #{attributes["type"]}"
- end
- end
+ def label
+ if iscsi?
+ "#{ip_addr}:#{port}:#{target}"
+ elsif nfs?
+ "#{ip_addr}:#{export_path}"
+ else
+ raise "Unknown type #{attributes["type"]}"
+ end
end
+ end
- class IscsiStoragePool < StoragePool
- def initialize(attributes = {})
- super(attributes.update( "type" =>
"IscsiStoragePool" ))
- end
+ class IscsiStoragePool < StoragePool
+ def initialize(attributes = {})
+ super(attributes.update( "type" =>
"IscsiStoragePool" ))
end
+ end
- class NfsStoragePool < StoragePool
- def initialize(attributes = {})
- super(attributes.update( "type" =>
"NfsStoragePool" ))
- end
+ class NfsStoragePool < StoragePool
+ def initialize(attributes = {})
+ super(attributes.update( "type" =>
"NfsStoragePool" ))
end
+ end
- class Host < Base
- def self.find_by_uuid(uuid)
- find(:first, :params => { :uuid => uuid })
- end
+ class Host < Base
+ def self.find_by_uuid(uuid)
+ find(:first, :params => { :uuid => uuid })
+ end
- def self.find_by_hostname(hostname)
- find(:first, :params => { :hostname => hostname })
- end
+ def self.find_by_hostname(hostname)
+ find(:first, :params => { :hostname => hostname })
+ end
- def hardware_pool
- HardwarePool.find(hardware_pool_id)
- end
+ def hardware_pool
+ HardwarePool.find(hardware_pool_id)
end
+ end
end
--
1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 02/11] Fix paths for default pool
---
examples/script.rb | 4 ++--
lib/ovirt.rb | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/examples/script.rb b/examples/script.rb
index f6f6839..522c883 100755
--- a/examples/script.rb
+++ b/examples/script.rb
@@ -69,7 +69,7 @@ defpool = OVirt::HardwarePool.default_pool
print_pool(defpool)
# Create a new hardware pool
-mypool = OVirt::HardwarePool.find_by_path("/default/mypool")
+mypool =
OVirt::HardwarePool.find_by_path("/root/hardware/default/mypool")
unless mypool
puts "Create mypool"
mypool = OVirt::HardwarePool.create( { :parent_id => defpool.id,
@@ -97,5 +97,5 @@ storage_pool = OVirt::StoragePool.create( { :storage_type
=> "NFS",
puts "Created storage pool #{storage_pool.id}"
# For some reason, mypool.reload doesn't work here
-mypool = OVirt::HardwarePool.find_by_path("/default/mypool")
+mypool =
OVirt::HardwarePool.find_by_path("/root/hardware/default/mypool")
print_pool(mypool)
diff --git a/lib/ovirt.rb b/lib/ovirt.rb
index fc3ac2a..927efe3 100644
--- a/lib/ovirt.rb
+++ b/lib/ovirt.rb
@@ -50,7 +50,7 @@ module OVirt
end
def self.default_pool
- find(:first, :params => { :path => "/default" })
+ find(:first, :params => { :path =>
"/root/hardware/default" })
end
end
--
1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 03/11] Add StoragePool.find_by_path
---
lib/ovirt.rb | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/lib/ovirt.rb b/lib/ovirt.rb
index 927efe3..ea55feb 100644
--- a/lib/ovirt.rb
+++ b/lib/ovirt.rb
@@ -72,6 +72,12 @@ module OVirt
raise "Unknown type #{attributes["type"]}"
end
end
+
+ def self.find_by_path(path)
+ hw = HardwarePool.find_by_path(path)
+ hw.nil? ? nil : hw.storage_pools
+ end
+
end
class IscsiStoragePool < StoragePool
--
1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 04/11] Add way to format size as human readable
---
lib/ovirt.rb | 31 +++++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/lib/ovirt.rb b/lib/ovirt.rb
index ea55feb..ce51e66 100644
--- a/lib/ovirt.rb
+++ b/lib/ovirt.rb
@@ -21,6 +21,31 @@ module OVirt
class Base < ActiveResource::Base
LOGIN_PATH = "login/login"
+ def to_human_readable(size)
+ size = size.to_f
+ unit = "kB"
+ if size > 512
+ size /= 1024
+ unit = "MB"
+ if size > 512
+ size /= 1024
+ unit = "GB"
+ if size > 512
+ size /= 1024
+ unit = "TB"
+ end
+ end
+ end
+ format "%5.2f %s", size, unit
+ end
+
+ def self.human_readable(attr)
+ attr = attr.to_s
+ define_method("human_readable_#{attr}") do
+ to_human_readable(attributes[attr])
+ end
+ end
+
def self.login
response = nil
begin
@@ -92,7 +117,13 @@ module OVirt
end
end
+ class StorageVolume < Base
+ human_readable :size
+ end
+
class Host < Base
+ human_readable :memory
+
def self.find_by_uuid(uuid)
find(:first, :params => { :uuid => uuid })
end
--
1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 05/11] Simple helper to format an error message
---
lib/ovirt.rb | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/lib/ovirt.rb b/lib/ovirt.rb
index ce51e66..5ce64d5 100644
--- a/lib/ovirt.rb
+++ b/lib/ovirt.rb
@@ -136,4 +136,14 @@ module OVirt
HardwarePool.find(hardware_pool_id)
end
end
+
+ def self.format_remote_exception(msg, e)
+ err = Hash.from_xml(e.response.body)["error"]
+ unless err.nil?
+ "#{msg}: #{err}"
+ else
+ msg
+ end
+ end
+
end
--
1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 06/11] Command to list nodes in a hardware pool
---
lib/ovirt/command/node.rb | 32 ++++++++++++++++++++++++++++++++
1 files changed, 32 insertions(+), 0 deletions(-)
create mode 100644 lib/ovirt/command/node.rb
diff --git a/lib/ovirt/command/node.rb b/lib/ovirt/command/node.rb
new file mode 100644
index 0000000..0728616
--- /dev/null
+++ b/lib/ovirt/command/node.rb
@@ -0,0 +1,32 @@
+module OVirt::Command::Node
+
+ module List
+ def execute(args)
+ pool = nil
+ begin
+ pool = OVirt::HardwarePool.find_by_path(args["pool"])
+ rescue ActiveResource::ResourceNotFound
+ pool = nil
+ end
+ unless pool
+ puts_error "pool #{args["pool"]} not found"
+ return
+ end
+ puts "Pool #{args["pool"]}: #{pool.hosts.size}
host(s)"
+ puts "=" * 75
+ pool.hosts.each do |h|
+ printf "%-36s %s\n", h.hostname, h.state
+ if args["verbose"]
+ printf " uuid : %s\n", h.uuid
+ printf " enabled : %s\n", h.is_disabled == 0 ?
"yes" : "no"
+ printf " arch : %s\n", h.arch
+ printf " hypervisor: %s\n", h.hypervisor_type
+ printf " memory : %s\n", h.human_readable_memory
+ printf " cpus : %d\n", h.cpus.size
+ puts "-" * 75
+ end
+ end
+ end
+ end
+
+end
--
1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 07/11] Command to list storage in a hardware pool
---
lib/ovirt/command/storage.rb | 31 +++++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
create mode 100644 lib/ovirt/command/storage.rb
diff --git a/lib/ovirt/command/storage.rb b/lib/ovirt/command/storage.rb
new file mode 100644
index 0000000..85577e9
--- /dev/null
+++ b/lib/ovirt/command/storage.rb
@@ -0,0 +1,31 @@
+module OVirt::Command::Storage
+
+ module List
+ def execute(args)
+ begin
+ pools = OVirt::StoragePool.find_by_path(args["pool"])
+ rescue ActiveResource::ResourceNotFound => e
+ puts_error "pool #{args["pool"]} not found"
+ return
+ end
+ puts "Pool #{args["pool"]}: #{pools.size} storage
pool(s)"
+ puts "=" * 75
+ pools.each do |sp|
+ type = sp.nfs? ? "NFS" : "iSCSI"
+ printf "%3d %-5s %-30s %s\n", sp.id, type, sp.label, sp.state
+ if args["verbose"]
+ sp = OVirt::StoragePool.find(sp.id)
+ sp.storage_volumes.sort_by { |vol| vol.filename }.each do |vol|
+ if sp.nfs?
+ printf " %3d %-30s %s\n", vol.id, vol.filename,
vol.human_readable_size
+ else
+ printf "FIXME\n"
+ end
+ end
+ end
+ end
+ puts "-" * 75
+ end
+ end
+
+end
--
1.6.0.6
--- lib/ovirt.rb | 2 ++ lib/ovirt/command.rb | 11 +++++++++++ 2 files changed, 13 insertions(+), 0 deletions(-) create mode 100644 lib/ovirt/command.rb diff --git a/lib/ovirt.rb b/lib/ovirt.rb index 5ce64d5..07a2c3b 100644 --- a/lib/ovirt.rb +++ b/lib/ovirt.rb @@ -147,3 +147,5 @@ module OVirt end end + +require 'ovirt/command' diff --git a/lib/ovirt/command.rb b/lib/ovirt/command.rb new file mode 100644 index 0000000..bf362f0 --- /dev/null +++ b/lib/ovirt/command.rb @@ -0,0 +1,11 @@ +module OVirt::Command + module Base + def puts_error(msg) + $stderr.puts "error: " + msg + exit_status exit_failure + end + end +end + +require 'ovirt/command/node' +require 'ovirt/command/storage' -- 1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 09/11] Command line driver
---
bin/ovirt | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 84 insertions(+), 0 deletions(-)
create mode 100755 bin/ovirt
diff --git a/bin/ovirt b/bin/ovirt
new file mode 100755
index 0000000..42b7871
--- /dev/null
+++ b/bin/ovirt
@@ -0,0 +1,84 @@
+#! /usr/bin/ruby
+
+# FIXME: Remove this, only to ease testing
+$:.unshift(File::join(File::dirname(File::dirname(__FILE__)), "lib"))
+
+# The main oVirt command line client.
+
+require 'rubygems'
+require 'ovirt'
+require 'main'
+
+require 'ruby-debug'
+
+# This is not really how we want the command line, but a temporary
+# approximation
+
+Main {
+ mixin :command do
+ include OVirt::Command::Base
+
+ def run
+ OVirt::Base::site = params["server"].value
+ OVirt::Base::login
+
+ hash = params.to_options
+ ["server", "help"].each { |k| hash.delete(k) }
+ begin
+ execute(hash)
+ rescue ActiveResource::TimeoutError
+ puts_error "request timed out"
+ rescue ActiveResource::ServerError => e
+ puts_error "server error: #{e}"
+ rescue Errno::ECONNREFUSED => e
+ puts_error "connection refused"
+ end
+ end
+ end
+
+ mixin :pool_argument do
+ argument("pool") {
+ description "the pool on which to perform the operation"
+ required
+ }
+ end
+
+ description "Control an oVirt server from a distance"
+
+ option("server=[SERVER]") {
+ validate { |val| ! val.nil? }
+ default ENV["OVIRT_SERVER"]
+ description <<DESCR
+URL of the oVirt server, in the form http://USER:PASSWORD at SERVER/ovirt
+DESCR
+ }
+
+ mode 'node' do
+
+ mixin :pool_argument
+
+ mode 'list' do
+ option("verbose", "v")
+ include OVirt::Command::Node::List
+ mixin :command
+ end
+ end
+
+ mode 'storage' do
+
+ mixin :pool_argument
+
+ mode 'list' do
+ option("verbose", "v")
+ include OVirt::Command::Storage::List
+ mixin :command
+ end
+ end
+
+ def run
+ $stderr.puts usage.to_s
+ end
+}
+# Want:
+# ovirt node /root/hardware/default mv host.example.com
/root/hardware/default/subpool
+# ovirt storage /root/hardware/default create-pool --type=nfs --ip=...
--path=..
--
1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 10/11] remove individual volumes from a storage pool or an entire storage pool
---
bin/ovirt | 25 +++++++++++++++++++++++++
lib/ovirt/command/storage.rb | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+), 0 deletions(-)
diff --git a/bin/ovirt b/bin/ovirt
index 42b7871..8c0bfcf 100755
--- a/bin/ovirt
+++ b/bin/ovirt
@@ -73,6 +73,31 @@ DESCR
include OVirt::Command::Storage::List
mixin :command
end
+
+ mode 'rm' do
+ option("pool=POOL", "p") {
+ description "the ID of the pool"
+ required
+ cast :int
+ }
+ # --all, --volume and --self are mutually exclusive
+
+ # We identify storage pools and volumes by their ID
+ # - it would be much nicer if we had some sort of label
+ # for each, but that requires we change the WUI
+ option("all", "a") {
+ description "delete all volumes from the pool"
+ }
+ option("volume=VOLUME", "v") {
+ description "delete the volume with the given id"
+ cast :int
+ }
+ option("self") {
+ description "delete the pool itself and all its volumes"
+ }
+ include OVirt::Command::Storage::Remove
+ mixin :command
+ end
end
def run
diff --git a/lib/ovirt/command/storage.rb b/lib/ovirt/command/storage.rb
index 85577e9..7cbf78a 100644
--- a/lib/ovirt/command/storage.rb
+++ b/lib/ovirt/command/storage.rb
@@ -28,4 +28,38 @@ module OVirt::Command::Storage
end
end
+ module Remove
+ def execute(args)
+ pool, volume, slf, all = args.values_at 'pool', 'volume',
'self', 'all'
+
+ begin
+ pool = OVirt::StoragePool::find(pool)
+ rescue ActiveResource::ResourceNotFound
+ puts_error "error: pool #{pool} not found"
+ return
+ end
+
+ begin
+ if volume
+ vol = pool.storage_volumes.find { |vol| vol.id == volume }
+ if vol
+ vol.destroy
+ else
+ $stderr.puts "error: no volume #{volume} in pool
#{pool.label}"
+ exit_status exit_failure
+ end
+ elsif all
+ pool.storage_volumes.each { |vol| vol.destroy }
+ elsif slf
+ pool.destroy
+ else
+ $stderr.puts "error: you must specify one of --volume, --all, or
--self"
+ exit_status exit_failure
+ end
+ rescue ActiveResource::ForbiddenAccess => e
+ $stderr.puts OVirt::format_remote_exception("Removal denied",
e)
+ exit_status exit_failure
+ end
+ end
+ end
end
--
1.6.0.6
David Lutterkort
2009-Jan-26 21:20 UTC
[Ovirt-devel] [PATCH client 11/11] Update the README
---
README | 42 ++++++++++++++++++++++++++++++++----------
1 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/README b/README
index ee1db15..ec874b4 100644
--- a/README
+++ b/README
@@ -1,17 +1,39 @@
-This is a very simple client library for accessing the OVirt API from Ruby.
+ovirt-client is a simple command line interface and client library for the
+OVirt Server Suite (http://ovirt.org)
-The file examples/script.rb contains a script that shows how this is done
-in some detail.
+Prerequisites
+============
-You must have ActiveResource installed, e.g. with 'yum install
-rubygem-activeresource'
+Before you can run the 'ovirt' command, you must have the
activeresource
+and main rubygems installed. The version of activeresource must be at least
+2.2.2, earlier versions have bugs that make it unusable.
-The server is specified with a URL of the form
- http://USER:PASSWORD at HOST/ovirt
+The simplest way to install these gems (if they are not packaged natively
+for your distribution), is by setting up a separate gem repository:
-This requires that the server is configured to allow HTTP authentication,
-since there are no mechanisms in the API to forward krb5 tickets.
+ export GEM_HOME=~/gems # could be anywhere
+ export GEM_PATH=${GEM_HOME}:/usr/lib/ruby/gems/1.8
+ mkdir $GEM_HOME
+
+and then install whatever you are missing, e.g.
+
+ gem install activeresource
+ gem install main
+
+(this will all become much easier once we package the command line client)
+
+Usage
+====+
+Run ./bin/ovirt --help
+
+API Hints
+========+
+The client bits are in lib/
+
+Before using any of them, you need to authenticate against the server by
+calling
-Before calling any other method on the API, you need to call
OVirt::Base::site = "http://USER:PASSWORD at HOST/ovirt"
OVirt::Base::login
--
1.6.0.6