Hi,
I just fixed some issue with Ruby 1.8.6 (each_char). This version should work.
--- client/bin/deltacloudc | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 160 insertions(+), 0 deletions(-) create mode 100755 client/bin/deltacloudc
diff --git a/client/bin/deltacloudc b/client/bin/deltacloudc new file mode 100755 index 0000000..b22bb51 --- /dev/null +++ b/client/bin/deltacloudc @@ -0,0 +1,160 @@ +#!/bin/env ruby +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +require 'rubygems' +require 'rest_client' +require 'optparse' +require 'nokogiri' +require 'uri' + +options = { + :format => :plain +} + +@optparse = OptionParser.new do|opts| + +opts.banner = <<BANNER +Usage: +deltacloudc collection operation [options] + +URL format: +API_URL=http://%5Buser%5D:%5Bpassword%5D@%5Bapi_url%5D%5Bport%5D%5B/uri] + +Options: +BANNER + opts.on( '-i', '--id ID', 'ID for operation') { |id| options[:id] = id } + opts.on( nil, '--image-id ID', 'Image ID') { |id| options[:image_id] = id } + opts.on( nil, '--arch ARCH', 'Architecture (x86, x86_64)') { |id| options[:architecture] = id } + opts.on( nil, '--flavor_id FLAVOR', 'Flavor') { |id| options[:flavor_id] = id } + opts.on( '-l', '--list', 'List collections/operations') { |id| options[:list] = true } + opts.on( '-u', '--url', 'API url ($API_URL variable)') { |url| options[:api_url] = url } + opts.on( nil, '--xml', 'XML output') { |format| options[:format] = :xml } + opts.on( '-h', '--help', 'Display this screen' ) { puts opts ; exit } + opts.on( '-v', '--version', 'Display API version' ) { options[:version]=true } +end + +def invalid_usage(error_msg='') + puts "ERROR: #{error_msg}" + exit(1) +end + +def convert_to_plain(xml) + doc = Nokogiri::XML(xml) + doc.root.traverse do |elem| + yield "-----------------------" if elem.children.size>1 + next unless elem.children.size.eql?(1) + if not elem.eql?(doc.root) and not elem.name.eql?('text') + yield "#{string_to_width(elem.name, 15)}: #{elem.content.strip}" + end + end + return nil +end + +def string_to_width(string, width, options = {}) + options.merge!( + :pad => ' ', :align => :left + ) + new_string = "" + i = 0 + string.each_byte do |c| + break if i == width + new_string += c.chr + i += 1 + end + # add padding + if new_string.length < width + padding = (options[:pad] * (width - new_string.length)) + new_string = options[:align] == :left ? + new_string + padding : padding + new_string + end + new_string +end + +@optparse.parse! + +# First try to get API_URL from environment +options[:api_url] = ENV['API_URL'] if options[:api_url].nil? +options[:collection] = ARGV[0] +options[:operation] = ARGV[1] + +invalid_usage("API_URL is not set") if options[:api_url].nil? + +options[:list]=true if options[:collection].nil? and options[:operation].nil? + +# Default response to XML +params = { :accept => "application/xml" } + +operations = {} +operations[:get] = [ 'index', 'show', 'new'] +operations[:post] = [ 'save', 'create' ] +operations[:put] = [ 'update' ] +operations[:delete] = [ 'delete' ] + +rest = RestClient::Resource.new( options[:api_url] , :accept => 'application/xml' ) + +doc = Nokogiri::XML(rest.get(:accept => "application/xml")) +version, driver = doc.xpath('/api/@version'), doc.xpath('/api/@driver') +collections = doc.xpath('/api/link/@rel').collect { |rel| rel.to_s } + +if options[:version] + puts "API [#{version}] Driver: [#{driver}]" + exit +end + +# List available collection for driver + +if options[:list] and not options[:collection] + puts "Available collections for driver #{driver} [#{version}]:\n\n" + collections.collect { |c| puts c } +elsif options[:list] and options[:collection] + # TODO: Place here all available collection operations (through /api/docs) +else + options[:params] = {} + options[:params]['image_id'] = options[:image_id] unless options[:image_id].nil? + options[:params]['architecture'] = options[:architecture] unless options[:architecture].nil? + options[:params]['flavor_id'] = options[:flavor_id] unless options[:flavor_id].nil? + + options[:operation] = 'index' if options[:operation].nil? + # First guess HTTP method to use + method = operations.keys.select { |k| operations[k].include?(options[:operation]) }.first + + invalid_usage("Unknown operation: #{options[:operation]} ") if method.nil? + invalid_usage("Unknown collection: #{options[:collection]}") unless collections.include?(options[:collection]) + + # 'show' and 'index' is not needed in URI + options[:operation] = nil if ['show', 'index'].include?(options[:operation]) + + # Resource means Rest URI ['/flavors', '/images/:id'] + resource = [options[:collection], options[:operation], options[:id]].compact.join('/') + + if method.eql?(:post) + puts rest[resource].send(method, params, options[:params]) + else + resource += '?'+URI.escape(options[:params].collect{|k,v| "#{k}=#{v}"}.join('&')) if options[:params] + case options[:format] + when :xml + puts rest[resource].send(method, params) + when :plain + convert_to_plain(rest[resource].send(method, params)) do |line| + puts line + end + end + end + +end +