cloud_stack_client 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "cloud_stack_client/cli"
4
+ cli = CloudStackClient::CLI.new
5
+ result = cli.run $0, ARGV
6
+ exit 1 unless result
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cloud_stack_client/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "cloud_stack_client"
8
+ gem.version = CloudStackClient::VERSION
9
+ gem.authors = ["Roman Messer"]
10
+ gem.email = ["roman.messer@ict-cloud.com"]
11
+ gem.description = %q{Execute any command against a CloudStack Infrastructure. Also contains a commandline utility with auto paging and support to read credentials from a config file.}
12
+ gem.summary = %q{A simple library to work with the CloudStack API.}
13
+ # gem.homepage = ""
14
+
15
+ if File.exist? '.git'
16
+ gem.files = `git ls-files`.split($/)
17
+ elsif File.exist? '.hg'
18
+ gem.files = `hg manifest`.split($/)
19
+ end
20
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
+ gem.require_paths = ["lib"]
23
+ end
@@ -0,0 +1,8 @@
1
+ require "cloud_stack_client/version"
2
+ require "cloud_stack_client/definitions"
3
+ require "cloud_stack_client/api"
4
+ require "cloud_stack_client/connector"
5
+
6
+ # Execute queries against the CloudStack API
7
+ module CloudStackClient
8
+ end
@@ -0,0 +1,325 @@
1
+ # An API to interact with the Citrix CloudStack Platform
2
+
3
+ require "cloud_stack_client/helpers"
4
+ require "cloud_stack_client/definitions"
5
+ require "digest/md5"
6
+ autoload :URI, "uri"
7
+ autoload :OpenSSL, "openssl"
8
+ autoload :Base64, "base64"
9
+ autoload :JSON, "json"
10
+ # Autoloading Net submodules
11
+ # @private
12
+ module Net
13
+ autoload :HTTP, "net/http"
14
+ end
15
+ # Autoloading REXML submodules
16
+ # @private
17
+ module REXML
18
+ autoload :Document, "rexml/document"
19
+ autoload :QuickPath, "rexml/quickpath"
20
+ end
21
+
22
+ module CloudStackClient
23
+ # Collection of functions to work with the API
24
+ module API
25
+
26
+ # Login with username, password hash and domain to get session credentials
27
+ #
28
+ # @param api_uri [String] HTTP URL of the CloudStack API
29
+ # @param username [String] CloudStack login user
30
+ # @param hashed_password [String] CloudStack login password (use {CloudStackClient::Helpers::hash_password})
31
+ # @param domain [String] CloudStack login domain
32
+ # @param http_method [Symbol] The HTTP method to submit the query (:post or :get)
33
+ # @param ssl_check [Boolean] Check Server SSL certificate?
34
+ # @return [Hash] Credentials for subsequent queries
35
+ # @see CloudStackClient::Helpers::hash_password
36
+ def self.get_credentials(api_uri, username, hashed_password, domain = "", http_method = nil , ssl_check = nil)
37
+ http_method ||= API_DEFAULTS[:http_method]
38
+ ssl_check ||= API_DEFAULTS[:ssl_check]
39
+ parameters = {:command => "login", :response => "json", :username => username, :password => hashed_password, :domain => parse_domain(domain)}
40
+ headers, body = execute_query(api_uri, generate_query(parameters), [], http_method, ssl_check)
41
+ credentials = {}
42
+ if headers.has_key? "set-cookie" # Expect lowercase names from Net::HTTPHeader
43
+ credentials[:session_cookies] = headers["set-cookie"].map {|raw| raw.split(";").first}
44
+ end
45
+ json = JSON.parse body
46
+ return nil unless CloudStackClient::API::response_is_success? json
47
+ credentials[:session_key] = json["loginresponse"]["sessionkey"]
48
+ credentials[:user_id] = json["loginresponse"]["userid"]
49
+ credentials
50
+ end
51
+
52
+ # Test connection with supplied Secret key and API key
53
+ #
54
+ # @param api_uri [String] HTTP URL of the CloudStack API
55
+ # @param api_key [String] CloudStack API Key
56
+ # @param secret_key [String] Cloudstack Secret Key
57
+ # @param http_method [Symbol] The HTTP method to submit the query (:post or :get)
58
+ # @param ssl_check [Boolean] Check Server SSL certificate?
59
+ # @return [Boolean]
60
+ def self.verify_keys(api_uri, api_key, secret_key, http_method = nil, ssl_check = nil)
61
+ http_method ||= API_DEFAULTS[:http_method]
62
+ ssl_check ||= API_DEFAULTS[:ssl_check]
63
+ CloudStackClient::API::response_is_success? query(api_uri, {:command => "listCapabilities"}, {:api_key => api_key, :secret_key => secret_key}, http_method, ssl_check)
64
+ end
65
+
66
+ # Execute a query
67
+ #
68
+ # @param api_uri [String] HTTP URL of the CloudStack API
69
+ # @param parameters [Hash] A Hash containing all query parameters. The keys can be specified as Symbol or String
70
+ # @param credentials [Hash] Contains either :api_key and :secret_key or :session_key and :session_cookies as obtained from {get_credentials}
71
+ # @param http_method [Symbol] The HTTP method to submit the query (:post or :get)
72
+ # @param ssl_check [Boolean] Check Server SSL certificate?
73
+ # @return [REXML::Document, Hash] An XML Document or the parsed JSON (commonly a Hash)
74
+ def self.query(api_uri, parameters, credentials, http_method = nil, ssl_check = nil)
75
+ http_method ||= API_DEFAULTS[:http_method]
76
+ ssl_check ||= API_DEFAULTS[:ssl_check]
77
+ if credentials.is_a?(Hash) && credentials.has_key?(:api_key) && credentials.has_key?(:secret_key)
78
+ parameters[:apiKey] = credentials[:api_key]
79
+ parameters[:secret] = credentials[:secret_key]
80
+ parameters[:timestamp] = Time.new.to_i
81
+ headers, body = self.execute_query(api_uri, generate_query(parameters), [], http_method, ssl_check)
82
+ elsif credentials.is_a? Hash
83
+ parameters[:sessionkey] = credentials[:session_key] if credentials.has_key? :session_key
84
+ headers, body = self.execute_query(api_uri, generate_query(parameters), credentials[:session_cookies], http_method, ssl_check)
85
+ end
86
+
87
+ format = nil
88
+ if headers["content-type"]
89
+ format = headers["content-type"][0].split(";").first
90
+ end
91
+
92
+ case format
93
+ when "text/javascript"
94
+ JSON.parse body
95
+ when "text/xml"
96
+ REXML::Document.new body
97
+ else
98
+ body
99
+ end
100
+ end
101
+
102
+ # Executes the query like query() but merges all result pages into one
103
+ #
104
+ # @param api_uri [String] HTTP URL of the CloudStack API
105
+ # @param parameters [Hash] A Hash containing all query parameters. The keys can be specified as Symbol or String
106
+ # @param credentials [Hash] Contains either :api_key and :secret_key or :session_key and :session_cookies as obtained from {get_credentials}
107
+ # @param http_method [Symbol] The HTTP method to submit the query (:post or :get)
108
+ # @param ssl_check [Boolean] Check Server SSL certificate?
109
+ # @return [REXML::Document, Hash] An XML Document or the parsed JSON (commonly a Hash)
110
+ def self.query_with_auto_paging(api_uri, parameters, credentials, page_size = nil, http_method = nil, ssl_check = nil)
111
+ page_size ||= API_DEFAULTS[:page_size]
112
+ http_method ||= API_DEFAULTS[:http_method]
113
+ ssl_check ||= API_DEFAULTS[:ssl_check]
114
+ output = nil
115
+ parameters[:page] = 0
116
+ parameters[:pageSize] = page_size
117
+ has_count = false
118
+
119
+ begin
120
+ parameters[:page] += 1
121
+ data = self.query api_uri, parameters, credentials, http_method, ssl_check
122
+ return nil unless data
123
+ format = :json
124
+ format = :xml if data.is_a? REXML::Document
125
+
126
+ # Remove count attribute from result data
127
+ if format == :xml
128
+ count_data = REXML::QuickPath.first(data.root, "//count")
129
+ if count_data
130
+ count = count_data.text.to_i
131
+ count_data.remove
132
+ has_count = true
133
+ else
134
+ count = 0
135
+ end
136
+ elsif format == :json
137
+ if data.first[1].has_key? "count"
138
+ count = data.first[1]["count"]
139
+ data.first[1].delete "count"
140
+ has_count = true
141
+ else
142
+ count = 0
143
+ end
144
+ end
145
+
146
+ # Copy first page in total
147
+ if !output
148
+ output = data
149
+ loop_done = count < parameters[:pageSize]
150
+ next
151
+ end
152
+
153
+ if count <= 0
154
+ # No elements
155
+ loop_done = true
156
+ elsif count >= parameters[:pageSize]
157
+ # Copy all elements if page is full
158
+ if format == :xml
159
+ data.root.elements.each {|record| output.root << record}
160
+ elsif format == :json
161
+ data.first[1].first[1].each {|record| output.first[1].first[1] << record}
162
+ end
163
+ else
164
+ # Handle last page
165
+ loop_done = true
166
+ # Detect and ignore duplicates that have already been extracted.
167
+ # E.g. guest-utilities during listIsos command
168
+ if format == :xml
169
+ pos_output = output.root.elements.size - count + 1
170
+ end_output = output.root.elements.size + 1
171
+ while pos_output < end_output
172
+ if output.root.elements[pos_output].to_s != data.root.elements[1].to_s
173
+ output.root << data.root.elements[1] # Move node to output tree
174
+ end
175
+ pos_output += 1
176
+ end
177
+ elsif format == :json
178
+ pos_data = 0
179
+ pos_output = output.first[1].first[1].size - count
180
+ end_output = output.first[1].first[1].size
181
+ while pos_output < end_output
182
+ if output.first[1].first[1][pos_output] != data.first[1].first[1][pos_data]
183
+ output.first[1].first[1] << data.first[1].first[1][pos_data]
184
+ end
185
+ pos_data += 1
186
+ pos_output += 1
187
+ end
188
+ end
189
+ end
190
+ end until loop_done
191
+
192
+ # Recalculate count attribute
193
+ if has_count && CloudStackClient::API::response_is_success?(output)
194
+ if format == :xml && output.root && output.root.has_elements?
195
+ new_count = REXML::Element.new "count"
196
+ new_count.add_text output.root.elements.count.to_s
197
+ output.root << new_count
198
+ elsif format == :json && output.first && output.first[1].any?
199
+ output.first[1]["count"] = output.first[1].first[1].count
200
+ end
201
+ end
202
+ output
203
+ end
204
+
205
+ # Generate a single sign on link for the management console
206
+ #
207
+ # @param web_uri [String] The base URL of Webinterface
208
+ # @param username [String] CloudStack login user
209
+ # @param hashed_password [String] CloudStack login password (use {CloudStackClient::Helpers::hash_password})
210
+ # @param domain [String] CloudStack login domain
211
+ # @param login_parameter [String] The name of the CloudStack URL parameter that has been specified in scripts/cloud.core.callbacks.js to contain the login credentials
212
+ # @param timestamp_offset [Fixnum] Custom scripts/cloud.core.callbacks.js parameter to invalidate the login link after the specified number of seconds
213
+ # @return [String] A complete URL
214
+ # @see CloudStackClient::Helpers::hash_password
215
+ def self.generate_management_console_link(web_uri, username, hashed_password, domain = "", login_parameter = nil, timestamp_offset = nil)
216
+ login_parameter ||= API_DEFAULTS[:login_parameter]
217
+ timestamp_offset ||= API_DEFAULTS[:timestamp_offset]
218
+ uri = "#{web_uri}?#{login_parameter}="
219
+ uri += escape(generate_query({:command => "login", :response => "json", :username => username, :password => hashed_password, :domain => parse_domain(domain)}))
220
+ uri += "&timestamp=#{Time.new.to_i + timestamp_offset}" if timestamp_offset
221
+ return uri
222
+ end
223
+
224
+ private
225
+
226
+ # Sanitize the domain
227
+ def self.parse_domain(domain = "")
228
+ domain = domain.to_s
229
+ if domain.start_with? "/"
230
+ domain
231
+ else
232
+ "/" + domain
233
+ end
234
+ end
235
+
236
+ # Make string URL safe
237
+ def self.escape(string)
238
+ URI.escape(string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
239
+ end
240
+
241
+ # Construct a query from a Hash of parameters or an Array of [key, value] Arrays
242
+ def self.generate_query(parameters = {})
243
+ if parameters.is_a? Enumerable
244
+ # Escape the key / value pairs
245
+ parameters = parameters.map {|param| [escape(param[0].to_s), escape(param[1].to_s)]}
246
+ else
247
+ parameters = [[escape(parameters.to_s), ""]]
248
+ end
249
+ secret = nil
250
+ parameters.delete_if {|key, value| secret = value if key == "secret"}
251
+
252
+ # Sign the query if secret is available
253
+ if secret
254
+ # Sort the parameters
255
+ parameters.sort!
256
+ # Generate query string
257
+ query = parameters.map {|param| param.join "="}.join("&")
258
+ # Generate signature by calculating SHA1 HMAC
259
+ hmac = OpenSSL::HMAC.digest("sha1", secret, query.downcase)
260
+ # Base64 encode 8 bit data and discard newline
261
+ base64 = Base64.encode64(hmac).strip
262
+ # Make signature URL safe
263
+ signature = escape(base64)
264
+ # Append signature to query
265
+ query += "&signature=" + signature
266
+ else
267
+ query = parameters.map {|param| param.join "="}.join("&")
268
+ end
269
+ query
270
+ end
271
+
272
+ # Submit the query to the server
273
+ def self.execute_query(api_uri, query, cookies = [], http_method = API_DEFAULTS[:http_method], ssl_check = API_DEFAULTS[:ssl_check])
274
+ uri = URI.parse api_uri
275
+ http = Net::HTTP.new uri.host, uri.port
276
+ if uri.scheme == "https"
277
+ http.use_ssl = true
278
+ # Verify server SSL certificate
279
+ if ssl_check
280
+ # On Ruby before 1.9 a local CA store is needed to verify SSL certificates.
281
+ ruby_version = RUBY_VERSION.split(".").map{|num| num.to_i}
282
+ if ruby_version[0] == 1 && ruby_version[1] < 9 || ruby_version[0] < 1
283
+ http.ca_file = File.join(File.dirname(__FILE__), "../../assets/cacert.pem")
284
+ end
285
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
286
+ end
287
+ end
288
+ request_cookies = nil
289
+ request_cookies = cookies.join(';') if cookies.is_a?(Array) && cookies.any?
290
+ if http_method == :post
291
+ request = Net::HTTP::Post.new uri.path
292
+ request["Cookie"] = request_cookies if request_cookies
293
+ response = http.request request, query
294
+ elsif http_method == :get
295
+ request = Net::HTTP::Get.new "#{uri.path}?#{query}"
296
+ request["Cookie"] = request_cookies if request_cookies
297
+ response = http.request request
298
+ else
299
+ raise "Only HTTP methods :post and :get are implemented."
300
+ end
301
+ return response.to_hash, response.body # Hash of HTTP Response header and HTTP Body
302
+ end
303
+
304
+ # Returns the format of a given reponse. Eigther :xml or :json
305
+ def self.response_format(response)
306
+ if response.is_a?(REXML::Document)
307
+ :xml
308
+ else
309
+ :json
310
+ end
311
+ end
312
+
313
+ # Checks the given response whether the command was successful
314
+ def self.response_is_success?(response)
315
+ if response.is_a? REXML::Document
316
+ !(response.root.elements["errorcode"] || response.root.name == "errorresponse")
317
+ elsif response.is_a?(Hash) && response.any?
318
+ !(response.first[1]["errorcode"] || response.first[0] == "errorresponse")
319
+ else
320
+ false
321
+ end
322
+ end
323
+
324
+ end
325
+ end
@@ -0,0 +1,239 @@
1
+ require "cloud_stack_client"
2
+ require "yaml"
3
+
4
+ module CloudStackClient
5
+ # The commandline client 'cloud-stack.rb'
6
+ #
7
+ # Plesae run +cloud-stack.rb --help+ for additional info.
8
+ class CLI
9
+ # Default options for the command line. Can be overridden by parameters and a config file
10
+ DEFAULT_OPTIONS = {
11
+ :api_uri => nil,
12
+ :api_key => nil,
13
+ :secret_key => nil,
14
+ :username => nil,
15
+ :password => nil,
16
+ :domain => nil,
17
+ :response => "json",
18
+ :output => nil,
19
+ :debug => false,
20
+ :auto_paging => true,
21
+ :page_size => 500
22
+ }.freeze
23
+
24
+ # Create new commandline instance
25
+ def initialize
26
+ @options = DEFAULT_OPTIONS.dup
27
+ @parameters = {}
28
+ end
29
+
30
+ # Execute the command
31
+ #
32
+ # @param command [String] The name of the executable ($0)
33
+ # @param arguments [Array] Commandline parameters (ARGV)
34
+ # @return [Boolean] true if successful
35
+ def run(command, arguments)
36
+ # Parse commandline
37
+ pos = arguments.find_index("--config")
38
+ if pos
39
+ arguments.delete_at pos
40
+ raise "Path to config file missing" unless arguments[pos]
41
+ filename = arguments.delete_at pos
42
+ yaml = YAML.load_file filename
43
+ yaml["default"].each {|key, value| @options[key.to_sym] = value}
44
+ end
45
+ if arguments.empty?
46
+ print_help File.basename(command), DEFAULT_OPTIONS
47
+ return false
48
+ end
49
+ while arguments.any? do
50
+ arg = arguments.shift
51
+ raise "Unknown parameter #{arg}" unless arg.start_with? '--'
52
+ key = arg.slice(2, arg.length - 2)
53
+ case key
54
+ when 'debug', 'ignore-ssl', 'auto-paging', 'no-auto-paging'
55
+ # Single parameter options
56
+ if key.start_with? 'no-'
57
+ key = key.slice(3, arg.length - 3)
58
+ @options[key.gsub('-', '_').to_sym] = false
59
+ else
60
+ @options[key.gsub('-', '_').to_sym] = true
61
+ end
62
+ when 'api-key', 'secret-key', 'username', 'password', 'domain', 'api-uri', 'response', 'output', 'pagesize', 'config'
63
+ # Second parameter options
64
+ raise "Missing value for #{arg}" if arguments.empty?
65
+ value = arguments.shift
66
+ if key == 'pagesize'
67
+ value = value.to_i
68
+ end
69
+ @options[key.gsub('-', '_').to_sym] = value
70
+ when 'help'
71
+ print_help File.basename(command), DEFAULT_OPTIONS
72
+ return false
73
+ when 'config-stub'
74
+ print_config DEFAULT_OPTIONS
75
+ return false
76
+ else
77
+ raise "Missing value for #{arg}" if arguments.empty?
78
+ @parameters[key] = arguments.shift
79
+ end
80
+ end
81
+
82
+ # Check existence of base URL
83
+ unless @options[:api_uri] && !@options[:api_uri].empty?
84
+ raise "Server API URL is missing. Specify with --api-uri parameter."
85
+ end
86
+
87
+ # Check existence of a command
88
+ unless @parameters['command']
89
+ warn "No command specified. Use --command COMMAND to specify a remote command."
90
+ end
91
+
92
+ # Check that auto paging is only used on list* commands
93
+ unless @parameters['command'] && @parameters['command'].downcase.start_with?("list")
94
+ @options[:auto_paging] = false
95
+ warn "Auto Paging disabled because command does not start with 'list'." if @options[:debug]
96
+ end
97
+
98
+ # Set response format if specified
99
+ if @options[:response]
100
+ warn "Unknown response format #{@options[:response]}." unless ['xml', 'json'].include? @options[:response]
101
+ @parameters['response'] = @options[:response]
102
+ end
103
+
104
+ # Login to CloudStack API
105
+ api = CloudStackClient::Connector.new({:api_uri => @options[:api_uri], :page_size => @options[:page_size], :ssl_check => !@options[:ignore_ssl]})
106
+ if @options[:api_key] && @options[:secret_key]
107
+ unless api.login_with_keys @options[:api_key], @options[:secret_key]
108
+ raise "Login using API key and secret key (--api-key KEY --secret-key SECRET) failed."
109
+ end
110
+ elsif @options[:username] && @options[:password]
111
+ unless api.login @options[:username], @options[:password], @options[:domain]
112
+ raise "Login using username and password failed."
113
+ end
114
+ else
115
+ warn "No credentials specified." if @options[:debug]
116
+ end
117
+
118
+ # Execute query
119
+ warn "Parameters:\n#{@parameters.inspect}\n" if @options[:debug]
120
+ if @options[:auto_paging]
121
+ output = api.query_all_pages @parameters
122
+ else
123
+ output = api.query @parameters
124
+ end
125
+
126
+ # Verify result
127
+ errorcode = nil
128
+ message = "Unrecognizable Result"
129
+ unless CloudStackClient::API::response_is_success? output
130
+ case CloudStackClient::API::response_format(output)
131
+ when :xml
132
+ errorcode = REXML::XPath.first(output.root, "//errorcode").text.to_i
133
+ message = REXML::XPath.first(output.root, "//errortext").text
134
+ when :json
135
+ errorcode = output.first[1]["errorcode"]
136
+ message = output.first[1]["errortext"]
137
+ else
138
+ message = "Unknown format #{CloudStackClient::API::response_format(output)}"
139
+ end
140
+ warn "Error #{errorcode} occured: #{message}\n"
141
+ end
142
+
143
+ # Output result
144
+ if not @options[:output]
145
+ # If no output is specified show formatted result on STDOUT
146
+ case CloudStackClient::API::response_format(output)
147
+ when :xml
148
+ puts "XML Response:"
149
+ puts output
150
+ when :json
151
+ puts "JSON Response:"
152
+ puts output.to_yaml
153
+ else
154
+ warn "Unknown format #{CloudStackClient::API::response_format(output)}" if @options[:debug]
155
+ end
156
+ elsif @options[:output] == '-'
157
+ # Pipe to STDOUT
158
+ STDOUT << output
159
+ puts # Fix missing newline
160
+ else
161
+ # Write to file
162
+ file = File.new(@options[:output], 'w')
163
+ file << output
164
+ file.close
165
+ end
166
+
167
+ !!errorcode
168
+ end
169
+
170
+ private
171
+
172
+ # Show help
173
+ def print_help(command, options)
174
+ puts "CloudStack API Commandline Interface (version #{CloudStackClient::VERSION})"
175
+ puts "Usage: #{command} [options]"
176
+ puts "Options:"
177
+ puts "\t--api-key api-key Your unique API key"
178
+ puts "\t--secret-key secret-key Your secret signature key"
179
+ puts "\t--username Your login username"
180
+ puts "\t--password Your login password"
181
+ puts "\t--domain Your login domain"
182
+ puts "\t--config yaml-file A YAML configuration file"
183
+ puts "\t--api-uri url The URL of the API. Example:"
184
+ puts "\t https://example.com/client/api"
185
+ puts "\t--response format Specify eighter json or xml (default: #{options[:response]})"
186
+ puts "\t--output filename Execute command and save the response"
187
+ puts "\t--[no-]auto-paging Aggregate all output pages (default: #{options[:auto_paging] ? "on" : "off"})"
188
+ puts "\t--pagesize num Server's maximum page size (default: #{options[:page_size]})"
189
+ puts "\t--ignore-ssl Ignore SSL certificate errors"
190
+ puts "\t--debug Additional output"
191
+ puts "\t--config-stub Prints a skeleton for the configuration file"
192
+ puts "\t--help This help"
193
+ puts
194
+ puts "Any API parameter can be passed as additional parameter:"
195
+ puts "\t--parameter value Adds 'parameter=value' to the query"
196
+ puts
197
+ puts "You can authenticate wit secret and API keys or using username, password and domain."
198
+ puts
199
+ puts "Examples:"
200
+ puts "#{command} --api-uri https://example.com/client/api \\"
201
+ puts "\t--api-key mYaPiKeY --secret-key MySeCrEt --command listTemplates \\"
202
+ puts "\t--templatefilter executable"
203
+ puts "#{command} --api-uri http://example.com/api --username Me --password \\"
204
+ puts "\tMyPass --domain My/Domain --command deployVirtualMachine --zoneid 123 \\"
205
+ puts "\t--serviceofferingid 234 --templateid 456"
206
+ puts "#{command} --config credentials.yml --command queryAsyncJobResult \\"
207
+ puts "\t--jobid 567"
208
+ puts "#{command} --config credentials.yml --command createPortForwardingRule \\"
209
+ puts "\t--ipaddressid 678 --protocol TCP --publicport 443 --privateport 443 \\"
210
+ puts "\t--virtualmachineid 789"
211
+ end
212
+
213
+ # Print config file skeleton
214
+ def print_config(options)
215
+ puts <<-YAML
216
+ # Specify default options for cloud-stack.rb.
217
+ #
218
+ # CloudStack Authentiction can eighter be API key and Secret key or username,
219
+ # password and domain.
220
+ #
221
+ # api_uri is the URL of the API e.g. https://www.example.com/client/api
222
+ #
223
+ # The supported response formats are json and xml.
224
+
225
+ default:
226
+ api_uri:
227
+ api_key:
228
+ secret_key:
229
+ username:
230
+ password:
231
+ domain:
232
+ # response: xml
233
+ # debug: true
234
+ # auto_paging: false
235
+ # page_size: 100
236
+ YAML
237
+ end
238
+ end
239
+ end