Skip to content

Commit d3d6ebe

Browse files
authored
Merge pull request #86 from McdonaldSeanp/json_output
Json output for delete/list + better ABS error handling
2 parents 13fee7e + 1eaa807 commit d3d6ebe

File tree

3 files changed

+96
-34
lines changed

3 files changed

+96
-34
lines changed

lib/vmfloaty.rb

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def run # rubocop:disable Metrics/AbcSize
8787
c.option '--verbose', 'Enables verbose output'
8888
c.option '--service STRING', String, 'Configured pooler service name'
8989
c.option '--active', 'Prints information about active vms for a given token'
90+
c.option '--json', 'Prints information as JSON'
9091
c.option '--token STRING', String, 'Token for pooler service'
9192
c.option '--url STRING', String, 'URL of pooler service'
9293
c.action do |args, options|
@@ -100,10 +101,18 @@ def run # rubocop:disable Metrics/AbcSize
100101
running_vms = service.list_active(verbose)
101102
host = URI.parse(service.url).host
102103
if running_vms.empty?
103-
puts "You have no running VMs on #{host}"
104+
if options.json
105+
puts {}.to_json
106+
else
107+
FloatyLogger.info "You have no running VMs on #{host}"
108+
end
104109
else
105-
puts "Your VMs on #{host}:"
106-
Utils.pretty_print_hosts(verbose, service, running_vms)
110+
if options.json
111+
puts Utils.get_host_data(verbose, service, running_vms).to_json
112+
else
113+
puts "Your VMs on #{host}:"
114+
Utils.pretty_print_hosts(verbose, service, running_vms)
115+
end
107116
end
108117
else
109118
# list available vms from pooler
@@ -200,6 +209,7 @@ def run # rubocop:disable Metrics/AbcSize
200209
c.option '--service STRING', String, 'Configured pooler service name'
201210
c.option '--all', 'Deletes all vms acquired by a token'
202211
c.option '-f', 'Does not prompt user when deleting all vms'
212+
c.option '--json', 'Outputs hosts scheduled for deletion as JSON'
203213
c.option '--token STRING', String, 'Token for pooler service'
204214
c.option '--url STRING', String, 'URL of pooler service'
205215
c.action do |args, options|
@@ -215,12 +225,18 @@ def run # rubocop:disable Metrics/AbcSize
215225
if delete_all
216226
running_vms = service.list_active(verbose)
217227
if running_vms.empty?
218-
puts 'You have no running VMs.'
228+
if options.json
229+
puts {}.to_json
230+
else
231+
FloatyLogger.info "You have no running VMs."
232+
end
219233
else
220-
Utils.pretty_print_hosts(verbose, service, running_vms, true)
221-
# Confirm deletion
222234
confirmed = true
223-
confirmed = agree('Delete all these VMs? [y/N]') unless force
235+
unless force
236+
Utils.pretty_print_hosts(verbose, service, running_vms, true)
237+
# Confirm deletion
238+
confirmed = agree('Delete all these VMs? [y/N]')
239+
end
224240
if confirmed
225241
response = service.delete(verbose, running_vms)
226242
response.each do |hostname, result|
@@ -257,9 +273,15 @@ def run # rubocop:disable Metrics/AbcSize
257273

258274
unless successes.empty?
259275
FloatyLogger.info unless failures.empty?
260-
puts 'Scheduled the following VMs for deletion:'
261-
successes.each do |hostname|
262-
puts "- #{hostname}"
276+
if options.json
277+
puts successes.to_json
278+
else
279+
puts 'Scheduled the following VMs for deletion:'
280+
output = ''
281+
successes.each do |hostname|
282+
output += "- #{hostname}\n"
283+
end
284+
puts output
263285
end
264286
end
265287

lib/vmfloaty/abs.rb

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,10 @@ def self.retrieve(verbose, os_types, token, url, user, options, _ondemand = nil)
229229

230230
retries = 360
231231

232-
raise AuthError, "HTTP #{res.status}: The token provided could not authenticate to the pooler.\n#{res_body}" if res.status == 401
232+
validate_queue_status_response(res.status, res.body, "Initial request", verbose)
233233

234234
(1..retries).each do |i|
235-
queue_place, res_body = check_queue(conn, saved_job_id, req_obj)
235+
queue_place, res_body = check_queue(conn, saved_job_id, req_obj, verbose)
236236
return translated(res_body) if res_body
237237

238238
sleep_seconds = 10 if i >= 10
@@ -262,11 +262,12 @@ def self.translated(res_body)
262262
vmpooler_formatted_body
263263
end
264264

265-
def self.check_queue(conn, job_id, req_obj)
265+
def self.check_queue(conn, job_id, req_obj, verbose)
266266
queue_info_res = conn.get "status/queue/info/#{job_id}"
267267
queue_info = JSON.parse(queue_info_res.body)
268268

269269
res = conn.post 'request', req_obj.to_json
270+
validate_queue_status_response(res.status, res.body, "Check queue request", verbose)
270271

271272
unless res.body.empty?
272273
res_body = JSON.parse(res.body)
@@ -315,4 +316,21 @@ def self.disk(_verbose, _url, _hostname, _token, _disk)
315316
def self.revert(_verbose, _url, _hostname, _token, _snapshot_sha)
316317
raise NoMethodError, 'revert is not defined for ABS'
317318
end
319+
320+
# Validate the http code returned during a queue status request.
321+
#
322+
# Return a success message that can be displayed if the status code is
323+
# success, otherwise raise an error.
324+
def self.validate_queue_status_response(status_code, body, request_name, verbose)
325+
case status_code
326+
when 200
327+
"#{request_name} returned success (Code 200)" if verbose
328+
when 202
329+
"#{request_name} returned accepted, processing (Code 202)" if verbose
330+
when 401
331+
raise AuthError, "HTTP #{status_code}: The token provided could not authenticate.\n#{body}"
332+
else
333+
raise "HTTP #{status_code}: #{request_name} request to ABS failed!\n#{body}"
334+
end
335+
end
318336
end

lib/vmfloaty/utils.rb

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,40 +79,62 @@ def self.generate_os_hash(os_args)
7979
end
8080

8181
def self.pretty_print_hosts(verbose, service, hostnames = [], print_to_stderr = false)
82+
fetched_data = self.get_host_data(verbose, service, hostnames)
83+
fetched_data.each do |hostname, host_data|
84+
case service.type
85+
when 'ABS'
86+
# For ABS, 'hostname' variable is the jobID
87+
host_data['allocated_resources'].each do |vm_name, _i|
88+
puts "- [JobID:#{host_data['request']['job']['id']}] #{vm_name['hostname']} (#{vm_name['type']}) <#{host_data['state']}>"
89+
end
90+
when 'Pooler'
91+
tag_pairs = []
92+
tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
93+
duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
94+
metadata = [host_data['template'], duration, *tag_pairs]
95+
puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})"
96+
when 'NonstandardPooler'
97+
line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
98+
line += ", #{host_data['hours_left_on_reservation']}h remaining"
99+
line += ", reason: #{host_data['reserved_for_reason']}" unless host_data['reserved_for_reason'].empty?
100+
line += ')'
101+
puts line
102+
else
103+
raise "Invalid service type #{service.type}"
104+
end
105+
end
106+
end
107+
108+
def self.get_host_data(verbose, service, hostnames = [])
109+
result = {}
82110
hostnames = [hostnames] unless hostnames.is_a? Array
83111
hostnames.each do |hostname|
84112
begin
85113
response = service.query(verbose, hostname)
86114
host_data = response[hostname]
87-
88-
case service.type
89-
when 'ABS'
90-
# For ABS, 'hostname' variable is the jobID
91-
if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
92-
host_data['allocated_resources'].each do |vm_name, _i|
93-
puts "- [JobID:#{host_data['request']['job']['id']}] #{vm_name['hostname']} (#{vm_name['type']}) <#{host_data['state']}>"
115+
if block_given?
116+
yield host_data result
117+
else
118+
case service.type
119+
when 'ABS'
120+
# For ABS, 'hostname' variable is the jobID
121+
if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
122+
result[hostname] = host_data
94123
end
124+
when 'Pooler'
125+
result[hostname] = host_data
126+
when 'NonstandardPooler'
127+
result[hostname] = host_data
128+
else
129+
raise "Invalid service type #{service.type}"
95130
end
96-
when 'Pooler'
97-
tag_pairs = []
98-
tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
99-
duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
100-
metadata = [host_data['template'], duration, *tag_pairs]
101-
puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})"
102-
when 'NonstandardPooler'
103-
line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
104-
line += ", #{host_data['hours_left_on_reservation']}h remaining"
105-
line += ", reason: #{host_data['reserved_for_reason']}" unless host_data['reserved_for_reason'].empty?
106-
line += ')'
107-
puts line
108-
else
109-
raise "Invalid service type #{service.type}"
110131
end
111132
rescue StandardError => e
112133
FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
113134
FloatyLogger.error(e)
114135
end
115136
end
137+
result
116138
end
117139

118140
def self.pretty_print_status(verbose, service)

0 commit comments

Comments
 (0)