vmfloaty 1.0.0 → 1.4.0

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.
data/lib/vmfloaty/http.rb CHANGED
@@ -21,13 +21,11 @@ class Http
21
21
 
22
22
  url = "https://#{url}" unless url?(url)
23
23
 
24
- conn = Faraday.new(:url => url, :ssl => { :verify => false }) do |faraday|
24
+ Faraday.new(url: url, ssl: { verify: false }) do |faraday|
25
25
  faraday.request :url_encoded
26
26
  faraday.response :logger if verbose
27
27
  faraday.adapter Faraday.default_adapter
28
28
  end
29
-
30
- conn
31
29
  end
32
30
 
33
31
  def self.get_conn_with_auth(verbose, url, user, password)
@@ -37,13 +35,11 @@ class Http
37
35
 
38
36
  url = "https://#{url}" unless url?(url)
39
37
 
40
- conn = Faraday.new(:url => url, :ssl => { :verify => false }) do |faraday|
38
+ Faraday.new(url: url, ssl: { verify: false }) do |faraday|
41
39
  faraday.request :url_encoded
42
40
  faraday.request :basic_auth, user, password
43
41
  faraday.response :logger if verbose
44
42
  faraday.adapter Faraday.default_adapter
45
43
  end
46
-
47
- conn
48
44
  end
49
45
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
3
5
  class FloatyLogger < ::Logger
@@ -17,11 +19,25 @@ class FloatyLogger < ::Logger
17
19
  FloatyLogger.logger.error msg
18
20
  end
19
21
 
22
+ def self.setlevel=(level)
23
+ level = level.downcase
24
+ case level
25
+ when 'debug'
26
+ logger.level = ::Logger::DEBUG
27
+ when 'info'
28
+ logger.level = ::Logger::INFO
29
+ when 'error'
30
+ logger.level = ::Logger::ERROR
31
+ else
32
+ error('set loglevel to debug, info or error')
33
+ end
34
+ end
35
+
20
36
  def initialize
21
- super(STDERR)
37
+ super($stderr)
22
38
  self.level = ::Logger::INFO
23
- self.formatter = proc do |severity, datetime, progname, msg|
24
- "#{msg}\n"
39
+ self.formatter = proc do |_severity, _datetime, _progname, msg|
40
+ "#{msg}\n"
25
41
  end
26
42
  end
27
43
  end
@@ -22,7 +22,7 @@ class NonstandardPooler
22
22
  status['reserved_hosts'] || []
23
23
  end
24
24
 
25
- def self.retrieve(verbose, os_type, token, url, _user, _options, ondemand = nil)
25
+ def self.retrieve(verbose, os_type, token, url, _user, _options, _ondemand = nil, _continue = nil)
26
26
  conn = Http.get_conn(verbose, url)
27
27
  conn.headers['X-AUTH-TOKEN'] = token if token
28
28
 
@@ -46,7 +46,8 @@ class NonstandardPooler
46
46
  raise TokenError, 'Token provided was nil; Request cannot be made to modify VM' if token.nil?
47
47
 
48
48
  modify_hash.each do |key, _value|
49
- raise ModifyError, "Configured service type does not support modification of #{key}" unless %i[reason reserved_for_reason].include? key
49
+ raise ModifyError, "Configured service type does not support modification of #{key}" unless %i[reason
50
+ reserved_for_reason].include? key
50
51
  end
51
52
 
52
53
  if modify_hash[:reason]
@@ -12,13 +12,11 @@ class Pooler
12
12
  response = conn.get 'vm'
13
13
  response_body = JSON.parse(response.body)
14
14
 
15
- hosts = if os_filter
16
- response_body.select { |i| i[/#{os_filter}/] }
17
- else
18
- response_body
19
- end
20
-
21
- hosts
15
+ if os_filter
16
+ response_body.select { |i| i[/#{os_filter}/] }
17
+ else
18
+ response_body
19
+ end
22
20
  end
23
21
 
24
22
  def self.list_active(verbose, url, token, _user)
@@ -28,7 +26,7 @@ class Pooler
28
26
  vms
29
27
  end
30
28
 
31
- def self.retrieve(verbose, os_type, token, url, _user, _options, ondemand = nil)
29
+ def self.retrieve(verbose, os_type, token, url, _user, _options, ondemand = nil, _continue = nil)
32
30
  # NOTE:
33
31
  # Developers can use `Utils.generate_os_hash` to
34
32
  # generate the os_type param.
@@ -50,7 +48,10 @@ class Pooler
50
48
  elsif response.status == 403
51
49
  raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/vm/#{os_string}. Request exceeds the configured per pool maximum. #{res_body}"
52
50
  else
53
- raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/vm/#{os_string}. #{res_body}" unless ondemand
51
+ unless ondemand
52
+ raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/vm/#{os_string}. #{res_body}"
53
+ end
54
+
54
55
  raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/ondemandvm/#{os_string}. #{res_body}"
55
56
  end
56
57
  end
@@ -63,7 +64,7 @@ class Pooler
63
64
  FloatyLogger.info "waiting for request #{request_id} to be fulfilled"
64
65
  sleep 5
65
66
  end
66
- FloatyLogger.info "The request has been fulfilled"
67
+ FloatyLogger.info 'The request has been fulfilled'
67
68
  check_ondemandvm(verbose, request_id, url)
68
69
  end
69
70
 
@@ -84,8 +85,9 @@ class Pooler
84
85
  def self.modify(verbose, url, hostname, token, modify_hash)
85
86
  raise TokenError, 'Token provided was nil. Request cannot be made to modify vm' if token.nil?
86
87
 
87
- modify_hash.keys.each do |key|
88
- raise ModifyError, "Configured service type does not support modification of #{key}." unless %i[tags lifetime disk].include? key
88
+ modify_hash.each_key do |key|
89
+ raise ModifyError, "Configured service type does not support modification of #{key}." unless %i[tags lifetime
90
+ disk].include? key
89
91
  end
90
92
 
91
93
  conn = Http.get_conn(verbose, url)
@@ -120,8 +122,7 @@ class Pooler
120
122
 
121
123
  response = conn.post "vm/#{hostname}/disk/#{disk}"
122
124
 
123
- res_body = JSON.parse(response.body)
124
- res_body
125
+ JSON.parse(response.body)
125
126
  end
126
127
 
127
128
  def self.delete(verbose, url, hosts, token, _user)
@@ -146,25 +147,21 @@ class Pooler
146
147
  conn = Http.get_conn(verbose, url)
147
148
 
148
149
  response = conn.get '/status'
149
- res_body = JSON.parse(response.body)
150
- res_body
150
+ JSON.parse(response.body)
151
151
  end
152
152
 
153
153
  def self.summary(verbose, url)
154
154
  conn = Http.get_conn(verbose, url)
155
155
 
156
156
  response = conn.get '/summary'
157
- res_body = JSON.parse(response.body)
158
- res_body
157
+ JSON.parse(response.body)
159
158
  end
160
159
 
161
160
  def self.query(verbose, url, hostname)
162
161
  conn = Http.get_conn(verbose, url)
163
162
 
164
163
  response = conn.get "vm/#{hostname}"
165
- res_body = JSON.parse(response.body)
166
-
167
- res_body
164
+ JSON.parse(response.body)
168
165
  end
169
166
 
170
167
  def self.snapshot(verbose, url, hostname, token)
@@ -174,8 +171,7 @@ class Pooler
174
171
  conn.headers['X-AUTH-TOKEN'] = token
175
172
 
176
173
  response = conn.post "vm/#{hostname}/snapshot"
177
- res_body = JSON.parse(response.body)
178
- res_body
174
+ JSON.parse(response.body)
179
175
  end
180
176
 
181
177
  def self.revert(verbose, url, hostname, token, snapshot_sha)
@@ -187,7 +183,6 @@ class Pooler
187
183
  raise "Snapshot SHA provided was nil, could not revert #{hostname}" if snapshot_sha.nil?
188
184
 
189
185
  response = conn.post "vm/#{hostname}/snapshot/#{snapshot_sha}"
190
- res_body = JSON.parse(response.body)
191
- res_body
186
+ JSON.parse(response.body)
192
187
  end
193
188
  end
@@ -39,7 +39,7 @@ class Service
39
39
  def user
40
40
  unless @config['user']
41
41
  FloatyLogger.info "Enter your #{@config['url']} service username:"
42
- @config['user'] = STDIN.gets.chomp
42
+ @config['user'] = $stdin.gets.chomp
43
43
  end
44
44
  @config['user']
45
45
  end
@@ -77,10 +77,10 @@ class Service
77
77
  @service_object.list_active verbose, url, token, user
78
78
  end
79
79
 
80
- def retrieve(verbose, os_types, use_token = true, ondemand = nil)
80
+ def retrieve(verbose, os_types, use_token = true, ondemand = nil, continue = nil)
81
81
  FloatyLogger.info 'Requesting a vm without a token...' unless use_token
82
82
  token_value = use_token ? token : nil
83
- @service_object.retrieve verbose, os_types, token_value, url, user, @config, ondemand
83
+ @service_object.retrieve verbose, os_types, token_value, url, user, @config, ondemand, continue
84
84
  end
85
85
 
86
86
  def wait_for_request(verbose, requestid)
@@ -139,9 +139,9 @@ class Service
139
139
 
140
140
  # some methods do not exist for ABS, and if possible should target the Pooler service
141
141
  def maybe_use_vmpooler
142
- if @service_object.is_a?(ABS.class)
143
- if !self.silent
144
- FloatyLogger.info "The service in use is ABS, but the requested method should run against vmpooler directly, using fallback_vmpooler config from ~/.vmfloaty.yml"
142
+ if @service_object == ABS # this is not an instance
143
+ unless silent
144
+ FloatyLogger.info 'The service in use is ABS, but the requested method should run against vmpooler directly, using fallback_vmpooler config from ~/.vmfloaty.yml'
145
145
  self.silent = true
146
146
  end
147
147
 
@@ -39,7 +39,10 @@ class Utils
39
39
  # "engine"=>"vmpooler"
40
40
  # }
41
41
 
42
- raise ArgumentError, "Bad GET response passed to format_hosts: #{response_body.to_json}" unless response_body.delete('ok')
42
+ unless response_body.delete('ok')
43
+ raise ArgumentError,
44
+ "Bad GET response passed to format_hosts: #{response_body.to_json}"
45
+ end
43
46
 
44
47
  # vmpooler reports the domain separately from the hostname
45
48
  domain = response_body.delete('domain')
@@ -50,7 +53,7 @@ class Utils
50
53
  abs_job_id = response_body.delete('job_id')
51
54
  result['job_id'] = abs_job_id unless abs_job_id.nil?
52
55
 
53
- filtered_response_body = response_body.reject { |key, _| key == 'request_id' || key == 'ready' }
56
+ filtered_response_body = response_body.reject { |key, _| %w[request_id ready].include?(key) }
54
57
  filtered_response_body.each do |os, value|
55
58
  hostnames = Array(value['hostname'])
56
59
  hostnames.map! { |host| "#{host}.#{domain}" } if domain
@@ -106,7 +109,7 @@ class Utils
106
109
  def self.pretty_print_hosts(verbose, service, hostnames = [], print_to_stderr = false, indent = 0)
107
110
  output_target = print_to_stderr ? $stderr : $stdout
108
111
 
109
- fetched_data = self.get_host_data(verbose, service, hostnames)
112
+ fetched_data = get_host_data(verbose, service, hostnames)
110
113
  fetched_data.each do |hostname, host_data|
111
114
  case service.type
112
115
  when 'ABS'
@@ -116,13 +119,14 @@ class Utils
116
119
 
117
120
  output_target.puts "- [JobID:#{host_data['request']['job']['id']}] <#{host_data['state']}>"
118
121
  host_data['allocated_resources'].each do |allocated_resources, _i|
119
- if allocated_resources['engine'] == "vmpooler"
122
+ if (allocated_resources['engine'] == 'vmpooler' || allocated_resources['engine'] == 'ondemand') && service.config['vmpooler_fallback']
120
123
  vmpooler_service = service.clone
121
124
  vmpooler_service.silent = true
122
125
  vmpooler_service.maybe_use_vmpooler
123
- self.pretty_print_hosts(verbose, vmpooler_service, allocated_resources['hostname'].split('.')[0], print_to_stderr, indent+2)
126
+ pretty_print_hosts(verbose, vmpooler_service, allocated_resources['hostname'].split('.')[0],
127
+ print_to_stderr, indent + 2)
124
128
  else
125
- #TODO we could add more specific metadata for the other services, nspooler and aws
129
+ # TODO: we could add more specific metadata for the other services, nspooler and aws
126
130
  output_target.puts " - #{allocated_resources['hostname']} (#{allocated_resources['type']})"
127
131
  end
128
132
  end
@@ -130,8 +134,13 @@ class Utils
130
134
  tag_pairs = []
131
135
  tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
132
136
  duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
133
- metadata = [host_data['template'], duration, *tag_pairs]
134
- output_target.puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})".gsub(/^/, ' ' * indent)
137
+ metadata = [host_data['state'], host_data['template'], duration, *tag_pairs]
138
+ message = "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})".gsub(/^/, ' ' * indent)
139
+ if host_data['state'] && host_data['state'] == 'destroyed'
140
+ output_target.puts message.colorize(:red)
141
+ else
142
+ output_target.puts message
143
+ end
135
144
  when 'NonstandardPooler'
136
145
  line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
137
146
  line += ", #{host_data['hours_left_on_reservation']}h remaining"
@@ -148,30 +157,26 @@ class Utils
148
157
  result = {}
149
158
  hostnames = [hostnames] unless hostnames.is_a? Array
150
159
  hostnames.each do |hostname|
151
- begin
152
- response = service.query(verbose, hostname)
153
- host_data = response[hostname]
154
- if block_given?
155
- yield host_data result
160
+ response = service.query(verbose, hostname)
161
+ host_data = response[hostname]
162
+ if block_given?
163
+ yield host_data result
164
+ else
165
+ case service.type
166
+ when 'ABS'
167
+ # For ABS, 'hostname' variable is the jobID
168
+ result[hostname] = host_data if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
169
+ when 'Pooler'
170
+ result[hostname] = host_data
171
+ when 'NonstandardPooler'
172
+ result[hostname] = host_data
156
173
  else
157
- case service.type
158
- when 'ABS'
159
- # For ABS, 'hostname' variable is the jobID
160
- if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
161
- result[hostname] = host_data
162
- end
163
- when 'Pooler'
164
- result[hostname] = host_data
165
- when 'NonstandardPooler'
166
- result[hostname] = host_data
167
- else
168
- raise "Invalid service type #{service.type}"
169
- end
174
+ raise "Invalid service type #{service.type}"
170
175
  end
171
- rescue StandardError => e
172
- FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
173
- FloatyLogger.error(e)
174
176
  end
177
+ rescue StandardError => e
178
+ FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
179
+ FloatyLogger.error(e)
175
180
  end
176
181
  result
177
182
  end
@@ -187,16 +192,14 @@ class Utils
187
192
 
188
193
  width = pools.keys.map(&:length).max
189
194
  pools.each do |name, pool|
190
- begin
191
- max = pool['max']
192
- ready = pool['ready']
193
- pending = pool['pending']
194
- missing = max - ready - pending
195
- char = 'o'
196
- puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
197
- rescue StandardError => e
198
- FloatyLogger.error "#{name.ljust(width)} #{e.red}"
199
- end
195
+ max = pool['max']
196
+ ready = pool['ready']
197
+ pending = pool['pending']
198
+ missing = max - ready - pending
199
+ char = 'o'
200
+ puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
201
+ rescue StandardError => e
202
+ FloatyLogger.error "#{name.ljust(width)} #{e.red}"
200
203
  end
201
204
  puts message.colorize(status_response['status']['ok'] ? :default : :red)
202
205
  when 'NonstandardPooler'
@@ -206,16 +209,14 @@ class Utils
206
209
 
207
210
  width = pools.keys.map(&:length).max
208
211
  pools.each do |name, pool|
209
- begin
210
- max = pool['total_hosts']
211
- ready = pool['available_hosts']
212
- pending = pool['pending'] || 0 # not available for nspooler
213
- missing = max - ready - pending
214
- char = 'o'
215
- puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
216
- rescue StandardError => e
217
- FloatyLogger.error "#{name.ljust(width)} #{e.red}"
218
- end
212
+ max = pool['total_hosts']
213
+ ready = pool['available_hosts']
214
+ pending = pool['pending'] || 0 # not available for nspooler
215
+ missing = max - ready - pending
216
+ char = 'o'
217
+ puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
218
+ rescue StandardError => e
219
+ FloatyLogger.error "#{name.ljust(width)} #{e.red}"
219
220
  end
220
221
  when 'ABS'
221
222
  FloatyLogger.error 'ABS Not OK' unless status_response
@@ -251,10 +252,11 @@ class Utils
251
252
  def self.get_service_config(config, options)
252
253
  # The top-level url, user, and token values in the config file are treated as defaults
253
254
  service_config = {
254
- 'url' => config['url'],
255
- 'user' => config['user'],
255
+ 'url' => config['url'],
256
+ 'user' => config['user'],
256
257
  'token' => config['token'],
257
- 'type' => config['type'] || 'vmpooler',
258
+ 'vmpooler_fallback' => config['vmpooler_fallback'],
259
+ 'type' => config['type'] || 'vmpooler'
258
260
  }
259
261
 
260
262
  if config['services']
@@ -265,7 +267,10 @@ class Utils
265
267
  service_config.merge! values
266
268
  else
267
269
  # If the user provided a service name at the command line, use that service if posible, or fail
268
- raise ArgumentError, "Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml" unless config['services'][options.service]
270
+ unless config['services'][options.service]
271
+ raise ArgumentError,
272
+ "Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml"
273
+ end
269
274
 
270
275
  # If the service is configured but some values are missing, use the top-level defaults to fill them in
271
276
  service_config.merge! config['services'][options.service]
@@ -289,22 +294,22 @@ class Utils
289
294
  config = Conf.read_config
290
295
  # The top-level url, user, and token values in the config file are treated as defaults
291
296
  service_config = {
292
- 'url' => config['url'],
293
- 'user' => config['user'],
294
- 'token' => config['token'],
295
- 'type' => 'vmpooler',
297
+ 'url' => config['url'],
298
+ 'user' => config['user'],
299
+ 'token' => config['token'],
300
+ 'type' => 'vmpooler'
296
301
  }
297
302
 
298
303
  # at a minimum, the url needs to be configured
299
304
  if config['services'] && config['services'][vmpooler_fallback] && config['services'][vmpooler_fallback]['url']
300
305
  # If the service is configured but some values are missing, use the top-level defaults to fill them in
301
306
  service_config.merge! config['services'][vmpooler_fallback]
307
+ elsif vmpooler_fallback.nil?
308
+ raise ArgumentError,
309
+ "The abs service should have a key named 'vmpooler_fallback' in ~/.vmfloaty.yml with a value that points to a vmpooler service name use this format:\nservices:\n myabs:\n url: 'http://abs.com'\n user: 'superman'\n token: 'kryptonite'\n vmpooler_fallback: 'myvmpooler'\n myvmpooler:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
302
310
  else
303
- if vmpooler_fallback.nil?
304
- raise ArgumentError, "The abs service should have a key named 'vmpooler_fallback' in ~/.vmfloaty.yml with a value that points to a vmpooler service name use this format:\nservices:\n myabs:\n url: 'http://abs.com'\n user: 'superman'\n token: 'kryptonite'\n vmpooler_fallback: 'myvmpooler'\n myvmpooler:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
305
- else
306
- raise ArgumentError, "Could not find a configured service named '#{vmpooler_fallback}' in ~/.vmfloaty.yml use this format:\nservices:\n #{vmpooler_fallback}:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
307
- end
311
+ raise ArgumentError,
312
+ "Could not find a configured service named '#{vmpooler_fallback}' in ~/.vmfloaty.yml use this format:\nservices:\n #{vmpooler_fallback}:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
308
313
  end
309
314
 
310
315
  service_config