vmpooler 0.13.2 → 0.14.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a200f3215b389575c605363ed55bb21b9a1779084cee39fef88fe28c2a51e9ab
4
- data.tar.gz: fd48ee0ff0fd5ea1f19dbd0b19bce49d821b0a17ef6f62aca91804cbdebc7d88
3
+ metadata.gz: d003f57422358d9da10997380e2c5d19d4954b8325b3b3655b04d2c55896b3b4
4
+ data.tar.gz: 895953f29cf0f7a86a782bb54721e14e77590c3f75a01a4f5d169d9428075528
5
5
  SHA512:
6
- metadata.gz: 63df277bab9ab5f049886d30166856ba5adfc85119e3408d91339827f6b05d3b12dde49c78c051c23d90bf123590e14d6cc7e41598cd315f2df9637a5c7c7042
7
- data.tar.gz: 3f6ce744a9c792c12800d7da0b6738e995d8f9a0cbc86bd3635977b3c463765b371100f5ea3ad509a15b7bd83be5bf37e4ea72cf7004535a36afe263577ed356
6
+ metadata.gz: f8182359a75a3e6630b20700e6ce8dece058208b59789564203a9023858ca1ea39fa309dadbd175c9d54e62408e5a1b0ffdd8490152819c2bfda74b3b1089dbf
7
+ data.tar.gz: 1caad1d0bffacab27a64fe703dcf1c48033733cb9b053e7a0813ca232254ff843c58dfe8f53df4c13b9e91014c862662ca8e2c7c2f956e7b04b781dbf09db7bc
@@ -11,33 +11,38 @@ redis_connection_pool_size = config[:redis]['connection_pool_size']
11
11
  redis_connection_pool_timeout = config[:redis]['connection_pool_timeout']
12
12
  logger_file = config[:config]['logfile']
13
13
 
14
- metrics = Vmpooler.new_metrics(config)
14
+ logger = Vmpooler::Logger.new logger_file
15
+ metrics = Vmpooler::Metrics.init(logger, config)
15
16
 
16
17
  torun_threads = []
17
18
  if ARGV.count == 0
18
- torun = ['api', 'manager']
19
+ torun = %i[api manager]
19
20
  else
20
21
  torun = []
21
- torun << 'api' if ARGV.include? 'api'
22
- torun << 'manager' if ARGV.include? 'manager'
22
+ torun << :api if ARGV.include? 'api'
23
+ torun << :manager if ARGV.include? 'manager'
23
24
  exit(2) if torun.empty?
24
25
  end
25
26
 
26
- if torun.include? 'api'
27
+ if torun.include? :api
27
28
  api = Thread.new do
28
- thr = Vmpooler::API.new
29
29
  redis = Vmpooler.new_redis(redis_host, redis_port, redis_password)
30
- thr.helpers.configure(config, redis, metrics)
31
- thr.helpers.execute!
30
+ Vmpooler::API.execute(torun, config, redis, metrics, logger)
32
31
  end
33
32
  torun_threads << api
33
+ elsif metrics.respond_to?(:setup_prometheus_metrics)
34
+ # Run the cut down API - Prometheus Metrics only.
35
+ prometheus_only_api = Thread.new do
36
+ Vmpooler::API.execute(torun, config, nil, metrics, logger)
37
+ end
38
+ torun_threads << prometheus_only_api
34
39
  end
35
40
 
36
- if torun.include? 'manager'
41
+ if torun.include? :manager
37
42
  manager = Thread.new do
38
43
  Vmpooler::PoolManager.new(
39
44
  config,
40
- Vmpooler.new_logger(logger_file),
45
+ logger,
41
46
  Vmpooler.redis_connection_pool(redis_host, redis_port, redis_password, redis_connection_pool_size, redis_connection_pool_timeout, metrics),
42
47
  metrics
43
48
  ).execute!
@@ -15,7 +15,7 @@ module Vmpooler
15
15
  require 'timeout'
16
16
  require 'yaml'
17
17
 
18
- %w[api graphite logger pool_manager statsd dummy_statsd generic_connection_pool].each do |lib|
18
+ %w[api metrics logger pool_manager generic_connection_pool].each do |lib|
19
19
  require "vmpooler/#{lib}"
20
20
  end
21
21
 
@@ -84,6 +84,7 @@ module Vmpooler
84
84
  parsed_config[:config]['experimental_features'] = ENV['EXPERIMENTAL_FEATURES'] if ENV['EXPERIMENTAL_FEATURES']
85
85
  parsed_config[:config]['purge_unconfigured_folders'] = ENV['PURGE_UNCONFIGURED_FOLDERS'] if ENV['PURGE_UNCONFIGURED_FOLDERS']
86
86
  parsed_config[:config]['usage_stats'] = ENV['USAGE_STATS'] if ENV['USAGE_STATS']
87
+ parsed_config[:config]['request_logger'] = ENV['REQUEST_LOGGER'] if ENV['REQUEST_LOGGER']
87
88
 
88
89
  parsed_config[:redis] = parsed_config[:redis] || {}
89
90
  parsed_config[:redis]['server'] = ENV['REDIS_SERVER'] || parsed_config[:redis]['server'] || 'localhost'
@@ -166,7 +167,8 @@ module Vmpooler
166
167
  def self.redis_connection_pool(host, port, password, size, timeout, metrics)
167
168
  Vmpooler::PoolManager::GenericConnectionPool.new(
168
169
  metrics: metrics,
169
- metric_prefix: 'redis_connection_pool',
170
+ connpool_type: 'redis_connection_pool',
171
+ connpool_provider: 'manager',
170
172
  size: size,
171
173
  timeout: timeout
172
174
  ) do
@@ -180,20 +182,6 @@ module Vmpooler
180
182
  Redis.new(host: host, port: port, password: password)
181
183
  end
182
184
 
183
- def self.new_logger(logfile)
184
- Vmpooler::Logger.new logfile
185
- end
186
-
187
- def self.new_metrics(params)
188
- if params[:statsd]
189
- Vmpooler::Statsd.new(params[:statsd])
190
- elsif params[:graphite]
191
- Vmpooler::Graphite.new(params[:graphite])
192
- else
193
- Vmpooler::DummyStatsd.new
194
- end
195
- end
196
-
197
185
  def self.pools(conf)
198
186
  conf[:pools]
199
187
  end
@@ -2,51 +2,58 @@
2
2
 
3
3
  module Vmpooler
4
4
  class API < Sinatra::Base
5
- def initialize
6
- super
5
+ # Load API components
6
+ %w[helpers dashboard reroute v1 request_logger].each do |lib|
7
+ require "vmpooler/api/#{lib}"
7
8
  end
9
+ # Load dashboard components
10
+ require 'vmpooler/dashboard'
8
11
 
9
- not_found do
10
- content_type :json
12
+ def self.execute(torun, config, redis, metrics, logger)
13
+ self.settings.set :config, config
14
+ self.settings.set :redis, redis unless redis.nil?
15
+ self.settings.set :metrics, metrics
16
+ self.settings.set :checkoutlock, Mutex.new
11
17
 
12
- result = {
13
- ok: false
14
- }
18
+ # Deflating in all situations
19
+ # https://www.schneems.com/2017/11/08/80-smaller-rails-footprint-with-rack-deflate/
20
+ use Rack::Deflater
15
21
 
16
- JSON.pretty_generate(result)
17
- end
22
+ # not_found clause placed here to fix rspec test issue.
23
+ not_found do
24
+ content_type :json
18
25
 
19
- # Load dashboard components
20
- begin
21
- require 'dashboard'
22
- rescue LoadError
23
- require File.expand_path(File.join(File.dirname(__FILE__), 'dashboard'))
24
- end
26
+ result = {
27
+ ok: false
28
+ }
25
29
 
26
- use Vmpooler::Dashboard
30
+ JSON.pretty_generate(result)
31
+ end
27
32
 
28
- # Load API components
29
- %w[helpers dashboard reroute v1].each do |lib|
30
- begin
31
- require "api/#{lib}"
32
- rescue LoadError
33
- require File.expand_path(File.join(File.dirname(__FILE__), 'api', lib))
33
+ if metrics.respond_to?(:setup_prometheus_metrics)
34
+ # Prometheus metrics are only setup if actually specified
35
+ # in the config file.
36
+ metrics.setup_prometheus_metrics(torun)
37
+
38
+ # Using customised collector that filters out hostnames on API paths
39
+ require 'vmpooler/metrics/promstats/collector_middleware'
40
+ require 'prometheus/middleware/exporter'
41
+ use Vmpooler::Metrics::Promstats::CollectorMiddleware, metrics_prefix: "#{metrics.prometheus_prefix}_http"
42
+ use Prometheus::Middleware::Exporter, path: metrics.prometheus_endpoint
34
43
  end
35
- end
36
44
 
37
- use Vmpooler::API::Dashboard
38
- use Vmpooler::API::Reroute
39
- use Vmpooler::API::V1
45
+ if torun.include? :api
46
+ # Enable API request logging only if required
47
+ use Vmpooler::API::RequestLogger, logger: logger if config[:config]['request_logger']
40
48
 
41
- def configure(config, redis, metrics)
42
- self.settings.set :config, config
43
- self.settings.set :redis, redis
44
- self.settings.set :metrics, metrics
45
- self.settings.set :checkoutlock, Mutex.new
46
- end
49
+ use Vmpooler::Dashboard
50
+ use Vmpooler::API::Dashboard
51
+ use Vmpooler::API::Reroute
52
+ use Vmpooler::API::V1
53
+ end
47
54
 
48
- def execute!
49
- self.settings.run!
55
+ # Get thee started O WebServer
56
+ self.run!
50
57
  end
51
58
  end
52
59
  end
@@ -13,7 +13,7 @@ module Vmpooler
13
13
  def valid_token?(backend)
14
14
  return false unless has_token?
15
15
 
16
- backend.exists('vmpooler__token__' + request.env['HTTP_X_AUTH_TOKEN']) ? true : false
16
+ backend.exists?('vmpooler__token__' + request.env['HTTP_X_AUTH_TOKEN']) ? true : false
17
17
  end
18
18
 
19
19
  def validate_token(backend)
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vmpooler
4
+ class API
5
+ class RequestLogger
6
+ attr_reader :app
7
+
8
+ def initialize(app, options = {})
9
+ @app = app
10
+ @logger = options[:logger]
11
+ end
12
+
13
+ def call(env)
14
+ status, headers, body = @app.call(env)
15
+ @logger.log('s', "[ ] API: Method: #{env['REQUEST_METHOD']}, Status: #{status}, Path: #{env['PATH_INFO']}, Body: #{body}")
16
+ [status, headers, body]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'vmpooler/util/parsing'
4
+
3
5
  module Vmpooler
4
6
  class API
5
7
  class V1 < Sinatra::Base
@@ -87,18 +89,16 @@ module Vmpooler
87
89
  template_backends += aliases
88
90
  weighted_pools = get_pool_weights(template_backends)
89
91
 
90
- pickup = Pickup.new(weighted_pools) if weighted_pools.count == template_backends.count
91
- count.to_i.times do
92
- if pickup
92
+ if weighted_pools.count > 1 && weighted_pools.count == template_backends.count
93
+ pickup = Pickup.new(weighted_pools)
94
+ count.to_i.times do
93
95
  selection << pickup.pick
94
- else
96
+ end
97
+ else
98
+ count.to_i.times do
95
99
  selection << template_backends.sample
96
100
  end
97
101
  end
98
- else
99
- count.to_i.times do
100
- selection << template
101
- end
102
102
  end
103
103
 
104
104
  count_selection(selection)
@@ -334,9 +334,10 @@ module Vmpooler
334
334
  end
335
335
 
336
336
  def too_many_requested?(payload)
337
- payload&.each do |_poolname, count|
337
+ payload&.each do |poolname, count|
338
338
  next unless count.to_i > config['max_ondemand_instances_per_request']
339
339
 
340
+ metrics.increment('ondemandrequest_fail.toomanyrequests.' + poolname)
340
341
  return true
341
342
  end
342
343
  false
@@ -357,9 +358,10 @@ module Vmpooler
357
358
  request_id ||= generate_request_id
358
359
  result['request_id'] = request_id
359
360
 
360
- if backend.exists("vmpooler__odrequest__#{request_id}")
361
+ if backend.exists?("vmpooler__odrequest__#{request_id}")
361
362
  result['message'] = "request_id '#{request_id}' has already been created"
362
363
  status 409
364
+ metrics.increment('ondemandrequest_generate.duplicaterequests')
363
365
  return result
364
366
  end
365
367
 
@@ -383,6 +385,7 @@ module Vmpooler
383
385
 
384
386
  result['domain'] = config['domain'] if config['domain']
385
387
  result[:ok] = true
388
+ metrics.increment('ondemandrequest_generate.success')
386
389
  result
387
390
  end
388
391
 
@@ -804,6 +807,7 @@ module Vmpooler
804
807
 
805
808
  post "#{api_prefix}/ondemandvm/?" do
806
809
  content_type :json
810
+ metrics.increment('http_requests_vm_total.post.ondemand.requestid')
807
811
 
808
812
  need_token! if Vmpooler::API.settings.config[:auth]
809
813
 
@@ -819,12 +823,12 @@ module Vmpooler
819
823
  else
820
824
  result[:bad_templates] = invalid
821
825
  invalid.each do |bad_template|
822
- metrics.increment('ondemandrequest.invalid.' + bad_template)
826
+ metrics.increment('ondemandrequest_fail.invalid.' + bad_template)
823
827
  end
824
828
  status 404
825
829
  end
826
830
  else
827
- metrics.increment('ondemandrequest.invalid.unknown')
831
+ metrics.increment('ondemandrequest_fail.invalid.unknown')
828
832
  status 404
829
833
  end
830
834
  rescue JSON::ParserError
@@ -841,6 +845,7 @@ module Vmpooler
841
845
  post "#{api_prefix}/ondemandvm/:template/?" do
842
846
  content_type :json
843
847
  result = { 'ok' => false }
848
+ metrics.increment('http_requests_vm_total.delete.ondemand.template')
844
849
 
845
850
  need_token! if Vmpooler::API.settings.config[:auth]
846
851
 
@@ -853,12 +858,12 @@ module Vmpooler
853
858
  else
854
859
  result[:bad_templates] = invalid
855
860
  invalid.each do |bad_template|
856
- metrics.increment('ondemandrequest.invalid.' + bad_template)
861
+ metrics.increment('ondemandrequest_fail.invalid.' + bad_template)
857
862
  end
858
863
  status 404
859
864
  end
860
865
  else
861
- metrics.increment('ondemandrequest.invalid.unknown')
866
+ metrics.increment('ondemandrequest_fail.invalid.unknown')
862
867
  status 404
863
868
  end
864
869
 
@@ -867,6 +872,7 @@ module Vmpooler
867
872
 
868
873
  get "#{api_prefix}/ondemandvm/:requestid/?" do
869
874
  content_type :json
875
+ metrics.increment('http_requests_vm_total.get.ondemand.request')
870
876
 
871
877
  status 404
872
878
  result = check_ondemand_request(params[:requestid])
@@ -877,6 +883,7 @@ module Vmpooler
877
883
  delete "#{api_prefix}/ondemandvm/:requestid/?" do
878
884
  content_type :json
879
885
  need_token! if Vmpooler::API.settings.config[:auth]
886
+ metrics.increment('http_requests_vm_total.delete.ondemand.request')
880
887
 
881
888
  status 404
882
889
  result = delete_ondemand_request(params[:requestid])
@@ -887,6 +894,7 @@ module Vmpooler
887
894
  post "#{api_prefix}/vm/?" do
888
895
  content_type :json
889
896
  result = { 'ok' => false }
897
+ metrics.increment('http_requests_vm_total.post.vm.checkout')
890
898
 
891
899
  payload = JSON.parse(request.body.read)
892
900
 
@@ -974,11 +982,14 @@ module Vmpooler
974
982
 
975
983
  if request_hash['status'] == 'ready'
976
984
  result['ready'] = true
977
- platform_parts = request_hash['requested'].split(',')
978
- platform_parts.each do |platform|
979
- pool_alias, pool, _count = platform.split(':')
980
- instances = backend.smembers("vmpooler__#{request_id}__#{pool_alias}__#{pool}")
981
- result[pool_alias] = { 'hostname': instances }
985
+ Parsing.get_platform_pool_count(request_hash['requested']) do |platform_alias, pool, _count|
986
+ instances = backend.smembers("vmpooler__#{request_id}__#{platform_alias}__#{pool}")
987
+
988
+ if result.key?(platform_alias)
989
+ result[platform_alias][:hostname] = result[platform_alias][:hostname] + instances
990
+ else
991
+ result[platform_alias] = { 'hostname': instances }
992
+ end
982
993
  end
983
994
  result['domain'] = config['domain'] if config['domain']
984
995
  status 200
@@ -989,14 +1000,19 @@ module Vmpooler
989
1000
  result['message'] = 'The request has been deleted'
990
1001
  status 200
991
1002
  else
992
- platform_parts = request_hash['requested'].split(',')
993
- platform_parts.each do |platform|
994
- pool_alias, pool, count = platform.split(':')
995
- instance_count = backend.scard("vmpooler__#{request_id}__#{pool_alias}__#{pool}")
996
- result[pool_alias] = {
997
- 'ready': instance_count.to_s,
998
- 'pending': (count.to_i - instance_count.to_i).to_s
999
- }
1003
+ Parsing.get_platform_pool_count(request_hash['requested']) do |platform_alias, pool, count|
1004
+ instance_count = backend.scard("vmpooler__#{request_id}__#{platform_alias}__#{pool}")
1005
+ instances_pending = count.to_i - instance_count.to_i
1006
+
1007
+ if result.key?(platform_alias) && result[platform_alias].key?(:ready)
1008
+ result[platform_alias][:ready] = (result[platform_alias][:ready].to_i + instance_count).to_s
1009
+ result[platform_alias][:pending] = (result[platform_alias][:pending].to_i + instances_pending).to_s
1010
+ else
1011
+ result[platform_alias] = {
1012
+ 'ready': instance_count.to_s,
1013
+ 'pending': instances_pending.to_s
1014
+ }
1015
+ end
1000
1016
  end
1001
1017
  end
1002
1018
 
@@ -1017,12 +1033,11 @@ module Vmpooler
1017
1033
  else
1018
1034
  backend.hset("vmpooler__odrequest__#{request_id}", 'status', 'deleted')
1019
1035
 
1020
- platforms.split(',').each do |platform|
1021
- pool_alias, pool, _count = platform.split(':')
1022
- backend.smembers("vmpooler__#{request_id}__#{pool_alias}__#{pool}")&.each do |vm|
1036
+ Parsing.get_platform_pool_count(platforms) do |platform_alias, pool, _count|
1037
+ backend.smembers("vmpooler__#{request_id}__#{platform_alias}__#{pool}")&.each do |vm|
1023
1038
  backend.smove("vmpooler__running__#{pool}", "vmpooler__completed__#{pool}", vm)
1024
1039
  end
1025
- backend.del("vmpooler__#{request_id}__#{pool_alias}__#{pool}")
1040
+ backend.del("vmpooler__#{request_id}__#{platform_alias}__#{pool}")
1026
1041
  end
1027
1042
  backend.expire("vmpooler__odrequest__#{request_id}", 129_600_0)
1028
1043
  end
@@ -1034,6 +1049,7 @@ module Vmpooler
1034
1049
  post "#{api_prefix}/vm/:template/?" do
1035
1050
  content_type :json
1036
1051
  result = { 'ok' => false }
1052
+ metrics.increment('http_requests_vm_total.get.vm.template')
1037
1053
 
1038
1054
  payload = extract_templates_from_query_params(params[:template])
1039
1055
 
@@ -1057,6 +1073,7 @@ module Vmpooler
1057
1073
 
1058
1074
  get "#{api_prefix}/vm/:hostname/?" do
1059
1075
  content_type :json
1076
+ metrics.increment('http_requests_vm_total.get.vm.hostname')
1060
1077
 
1061
1078
  result = {}
1062
1079
 
@@ -1129,6 +1146,7 @@ module Vmpooler
1129
1146
 
1130
1147
  delete "#{api_prefix}/vm/:hostname/?" do
1131
1148
  content_type :json
1149
+ metrics.increment('http_requests_vm_total.delete.vm.hostname')
1132
1150
 
1133
1151
  result = {}
1134
1152
 
@@ -1146,8 +1164,9 @@ module Vmpooler
1146
1164
 
1147
1165
  status 200
1148
1166
  result['ok'] = true
1167
+ metrics.increment('delete.success')
1149
1168
  else
1150
- metrics.increment('delete.srem.failed')
1169
+ metrics.increment('delete.failed')
1151
1170
  end
1152
1171
  end
1153
1172
 
@@ -1156,6 +1175,7 @@ module Vmpooler
1156
1175
 
1157
1176
  put "#{api_prefix}/vm/:hostname/?" do
1158
1177
  content_type :json
1178
+ metrics.increment('http_requests_vm_total.put.vm.modify')
1159
1179
 
1160
1180
  status 404
1161
1181
  result = { 'ok' => false }
@@ -1164,7 +1184,7 @@ module Vmpooler
1164
1184
 
1165
1185
  params[:hostname] = hostname_shorten(params[:hostname], config['domain'])
1166
1186
 
1167
- if backend.exists('vmpooler__vm__' + params[:hostname])
1187
+ if backend.exists?('vmpooler__vm__' + params[:hostname])
1168
1188
  begin
1169
1189
  jdata = JSON.parse(request.body.read)
1170
1190
  rescue StandardError
@@ -1232,6 +1252,7 @@ module Vmpooler
1232
1252
 
1233
1253
  post "#{api_prefix}/vm/:hostname/disk/:size/?" do
1234
1254
  content_type :json
1255
+ metrics.increment('http_requests_vm_total.post.vm.disksize')
1235
1256
 
1236
1257
  need_token! if Vmpooler::API.settings.config[:auth]
1237
1258
 
@@ -1240,7 +1261,7 @@ module Vmpooler
1240
1261
 
1241
1262
  params[:hostname] = hostname_shorten(params[:hostname], config['domain'])
1242
1263
 
1243
- if ((params[:size].to_i > 0 )and (backend.exists('vmpooler__vm__' + params[:hostname])))
1264
+ if ((params[:size].to_i > 0 )and (backend.exists?('vmpooler__vm__' + params[:hostname])))
1244
1265
  result[params[:hostname]] = {}
1245
1266
  result[params[:hostname]]['disk'] = "+#{params[:size]}gb"
1246
1267
 
@@ -1255,6 +1276,7 @@ module Vmpooler
1255
1276
 
1256
1277
  post "#{api_prefix}/vm/:hostname/snapshot/?" do
1257
1278
  content_type :json
1279
+ metrics.increment('http_requests_vm_total.post.vm.snapshot')
1258
1280
 
1259
1281
  need_token! if Vmpooler::API.settings.config[:auth]
1260
1282
 
@@ -1263,7 +1285,7 @@ module Vmpooler
1263
1285
 
1264
1286
  params[:hostname] = hostname_shorten(params[:hostname], config['domain'])
1265
1287
 
1266
- if backend.exists('vmpooler__vm__' + params[:hostname])
1288
+ if backend.exists?('vmpooler__vm__' + params[:hostname])
1267
1289
  result[params[:hostname]] = {}
1268
1290
 
1269
1291
  o = [('a'..'z'), ('0'..'9')].map(&:to_a).flatten
@@ -1280,6 +1302,7 @@ module Vmpooler
1280
1302
 
1281
1303
  post "#{api_prefix}/vm/:hostname/snapshot/:snapshot/?" do
1282
1304
  content_type :json
1305
+ metrics.increment('http_requests_vm_total.post.vm.disksize')
1283
1306
 
1284
1307
  need_token! if Vmpooler::API.settings.config[:auth]
1285
1308