blob: adc5d1102c3a1600ba4ca6f92adcb65521aebb00 [file] [log] [blame]
#!/usr/bin/env ruby
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require 'rubygems'
require 'optparse'
require 'uri'
require 'deltacloud'
require 'plain_formatter'
include DeltaCloud::PlainFormatter
options = {
:verbose => false
}
@optparse = OptionParser.new do |opts|
opts.banner = <<BANNER
Usage:
deltacloudc collection operation [options]
URL format:
API_URL=http://[user]:[password]@[api_url][port][/uri]
OR
API_URL=http://[api_url][port][/uri] with -U and -P for credentials
Examples:
1. To list collections for deltacloud api on port 3333 of server deltacloud.foo
deltacloudc -l -u http://user:password@deltacloud.foo:3333/api
2. To list the operations for the 'images' collection:
deltacloudc images -l -U username -P password -u http://deltacloud.foo:3333/api
3. To list all images (i.e. call the 'index' operation of 'images'):
deltacloudc images index -u http://user:password@deltacloud.foo:3333/api
4. To get the details of image '5':
deltacloudc images show -i 5 -U username -P password -u http://deltacloud.foo:3333/api
Options:
BANNER
opts.on( '-i', '--id ID', 'ID for operation') { |id| options[:id] = id }
opts.on( '-d', '--image-id ID', 'Image ID') { |id| options[:image_id] = id }
opts.on( '-b', '--bucket-id ID', 'Bucket ID') {|id| options[:bucket_id] = id }
opts.on( '-r', '--bucket-location NAME', 'Bucket location') {|name| options[:bucket_location] = name }
opts.on( '-a', '--arch ARCH', 'Architecture (x86, x86_64)') { |id| options[:architecture] = id }
opts.on( '-p', '--hardware-profile HARDWARE_PROFILE', 'Hardware Profile') { |id| options[:hwp_id] = id }
opts.on( '-n', '--name NAME', 'Name (for instance eg.)') { |name| options[:name] = name }
opts.on( '-s', '--state STATE', 'Instance state (RUNNING, STOPPED)') { |state| options[:state] = state }
opts.on( '-u', '--url URL', 'API url ($API_URL variable)') { |url| options[:api_url] = url }
opts.on( '-U', '--user USER', 'API username ($API_USERNAME variable)') { |u| options[:api_user] = u }
opts.on( '-P', '--password PASSWORD', 'API password ($API_PASSWORD variable)') { |p| options[:api_password] = p }
opts.on( '-l', '--list', 'List collections/operations') { |id| options[:list] = true }
opts.on( '-h', '--help', 'Display this screen' ) { puts @optparse; Kernel.exit! }
opts.on( '-v', '--version', 'Display API version' ) { options[:version]=true }
opts.on( '-V', '--verbose', 'Print verbose messages' ) { options[:verbose]=true }
opts.on( '-f', '--file-path PATH', 'local path for new blob data') {|path| options[:file_path]=path }
opts.on( '-m', '--blob-metadata k1=v1, k2=v2', 'Comma seperated k=v pairs for blob metadata (for create operation)') do |meta|
blob_meta = {}
meta.gsub!(/ /,"")
meta.scan(/(\w+)=(\w+)/).map {|k,v| blob_meta[k] = v }
options[:blob_metadata] = blob_meta
end
end
def invalid_usage(error_msg='')
puts "\n ERROR: #{error_msg} \n\n"
puts @optparse
exit(1)
end
begin
@optparse.parse!
rescue Exception => e
invalid_usage(e.message)
end
# First try to get API_URL from environment
options[:api_url] = ENV['API_URL'] if options[:api_url].nil?
if(options[:api_url].nil?)
invalid_usage("You must supply the url to the deltacloud api; either use '-u' flag or set the 'API_URL' environment variable")
end
url = URI.parse(options[:api_url])
api_url = "http://#{url.host}#{url.port ? ":#{url.port}" : ''}#{url.path}"
options[:collection] = ARGV[0]
options[:operation] = ARGV[1]
# Connect to Deltacloud API and fetch all entry points
client = DeltaCloud.new(options[:api_user] || url.user || ENV['API_USER'], options[:api_password] || url.password || ENV['API_PASSWORD'], api_url)
collections = client.entry_points.keys
# Exclude collection which don't have methods in client library yet
collections.delete(:instance_states)
#add blob collection if buckets is present
collections << :blob if collections.include?(:buckets)
# If list parameter passed print out available collection
# with API documentation
if options[:list] and options[:collection].nil?
collections.each do |c|
puts sprintf("%-22s", c.to_s[0, 22])
end
exit(0)
end
# If collection parameter is present and user requested list
# print all operation defined for collection with API documentation
if options[:list] and options[:collection]
#deal with blobs again - bypass 'normal' docs procedure
if options[:collection] =~ /blob/i
puts "create \t\t: Create a new blob in a specified bucket (POST /api/buckets/:bucket)"
puts "destroy \t: Delete a specified blob in a specified bucket (DELETE /api/buckets/:bucket/:blob)"
puts "show \t\t: Get details of a specified blob in a specified bucket (GET /api/buckets/:bucket/:blob)"
puts "data \t\t: Get the contents of a specified blob - the blob data content itself"
exit(0)
end
doc = client.documentation(options[:collection])
doc.operations.each do |c|
puts sprintf("%-20s: %s", c.operation, c.description)
end
exit(0)
end
if options[:version]
puts "Deltacloud API(#{client.driver_name}) 0.1"
exit(0)
end
# List items from collection (typically /instances)
# Do same if 'index' operation is set
if options[:collection] and ( options[:operation].nil? or options[:operation].eql?('index') )
invalid_usage("Unknown collection: #{options[:collection]}") unless collections.include?(options[:collection].to_sym)
#cannot list blobs - can only show a specific blob
invalid_usage("You must specify a particular blob with -i and a particular bucket with -b") if options[:collection] =~ (/blob/i)
params = {}
params.merge!(:architecture => options[:architecture]) if options[:architecture]
params.merge!(:id => options[:id]) if options[:id]
params.merge!(:state => options[:state]) if options[:state]
client.send(options[:collection].to_s, params).each do |model|
puts format(model)
end
exit(0)
end
if options[:collection] and options[:operation]
invalid_usage("Unknown collection: #{options[:collection]}") unless collections.include?(options[:collection].to_sym)
params = {}
params.merge!(:id => options[:id]) if options[:id]
# If collection is set and requested operation is 'show' just 'singularize'
# collection name and print item with specified id (-i parameter)
#Blobs are a special case so deal with first -
if options[:collection] =~ (/blob/i)
invalid_usage("Please specify the bucket for this blob using the -b option") unless options[:bucket_id]
invalid_usage("Missing blob ID, please specify with -i option") unless options[:id]
params = {}
params.merge!(:id => options[:id], 'bucket' => options[:bucket_id])
params.merge!('metadata'=>options[:blob_metadata]) unless options[:blob_metadata].nil?
case options[:operation]
when 'show' then puts format(client.send( options[:collection], params))
when 'data' then puts client.blob_data(params)
when 'create' then
invalid_usage("Specify the location of the new blob data (full local path) using -f option") unless options[:file_path]
params.merge!('file_path'=>options[:file_path])
blob = client.create_blob(params)
puts format(blob)
when 'destroy' then client.destroy_blob(params)
else invalid_usage("Please specify a valid operation for the blob collection - try -l to see available operations")
end
exit(0)
end
if options[:operation].eql?('show')
invalid_usage("Missing ID, must be provided with --id") unless options[:id]
puts format(client.send(options[:collection].gsub(/s$/, ''), options[:id]))
exit(0)
end
# If collection is set and requested operation is create new instance,
# --image-id, --hardware-profile and --name parameters are used
# Returns created instance in plain form
if options[:collection].eql?('instances') and options[:operation].eql?('create')
invalid_usage("Missing image-id") unless options[:image_id]
if options[:name] and ! client.feature?(:instances, :user_name)
invalid_usage("Driver does not support user-supplied name")
end
params.merge!(:name => options[:name]) if options[:name]
params.merge!(:image_id => options[:image_id]) if options[:image_id]
params.merge!(:hwp_id => options[:hwp_id]) if options[:hwp_id]
instance = client.create_instance(options[:image_id], params)
puts format(instance)
exit(0)
end
#Create and Destroy a bucket - require the bucket id:
if options[:collection].eql?('buckets')
if options[:operation].eql?('create')
invalid_usage("Please specify an id for the new bucket with -i") unless options[:id]
bucket = client.create_bucket('id'=>options[:id], 'bucket_location'=>options[:bucket_location])
puts format(bucket)
elsif options[:operation].eql?('destroy')
invalid_usage("Please specify the bucket you wish to destroy with -i") unless options[:id]
client.destroy_bucket('id'=>options[:id])
else
invalid_usage("Please specify a valid operation on buckets - use -l to see valid operations")
end
exit(0)
end
# All other operations above collections is done there:
if options[:collection].eql?('instances')
instance = client.instance(options[:id])
instance.send("#{options[:operation]}!".to_s)
instance = client.instance(options[:id])
puts format(instance)
exit(0)
end
end
# If all above passed (eg. no parameters)
puts @optparse