vmfloaty 1.0.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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