vmpooler 0.13.2 → 0.14.3

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