vmpooler 0.13.1 → 0.14.2

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: 3f6f3dafe8bbb4d65b5dc088ff36c24b12a3e6382aa2ec84f5a504e58e36eb6c
4
- data.tar.gz: e488162ea027f566d44c762aa10095529d5610048bff7674e4695c16b37d61a1
3
+ metadata.gz: 2b0e071edb016f25c0379ebc2d7768e945096333d9be2f1f31ce66fc32474fd1
4
+ data.tar.gz: 1bbac7250090c48a74bf671d316cb9aa45d1dc55af1f69351e9afd27f7c4ed9d
5
5
  SHA512:
6
- metadata.gz: 3ba72cb12cce3ab544437948751662434ce936014fdb68e63bbd6484bcceddaade2918b51852a6fd7d8171ea9a28d14135102cbe23cbae521fcbeb7f40ad6f7a
7
- data.tar.gz: 484fd410cd7f96bde01bc928e3afc4cb5fb9eeebffdda8ebf615fd0414d1db10a294f28afdacc0e5cac3cf7448fc8c3a4636b50b2d02c4dc017034da6a0c4f41
6
+ metadata.gz: 21bc1b2a3c83e82a2e4f3ad6f54a41a380a76a331ca8613b49acf0f7cd2df6ff69162e600f7d449e482e110d1ac7e3e5d9534cd1479d990dd4ff4f8c2c079b74
7
+ data.tar.gz: 109ae1cbfa75cc80a3afff19938677856f9ac0fd858cb546d6ea42b8dbbddf515cad3cdb6cfcb340addbe863c5ee1fc9c4025704bdb298584a7ab8696dd27de9
@@ -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.metrics_prefix}_http"
42
+ use Prometheus::Middleware::Exporter, path: metrics.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
@@ -334,9 +336,10 @@ module Vmpooler
334
336
  end
335
337
 
336
338
  def too_many_requested?(payload)
337
- payload&.each do |_poolname, count|
339
+ payload&.each do |poolname, count|
338
340
  next unless count.to_i > config['max_ondemand_instances_per_request']
339
341
 
342
+ metrics.increment('ondemandrequest_fail.toomanyrequests.' + poolname)
340
343
  return true
341
344
  end
342
345
  false
@@ -357,9 +360,10 @@ module Vmpooler
357
360
  request_id ||= generate_request_id
358
361
  result['request_id'] = request_id
359
362
 
360
- if backend.exists("vmpooler__odrequest__#{request_id}")
363
+ if backend.exists?("vmpooler__odrequest__#{request_id}")
361
364
  result['message'] = "request_id '#{request_id}' has already been created"
362
365
  status 409
366
+ metrics.increment('ondemandrequest_generate.duplicaterequests')
363
367
  return result
364
368
  end
365
369
 
@@ -383,6 +387,7 @@ module Vmpooler
383
387
 
384
388
  result['domain'] = config['domain'] if config['domain']
385
389
  result[:ok] = true
390
+ metrics.increment('ondemandrequest_generate.success')
386
391
  result
387
392
  end
388
393
 
@@ -804,6 +809,7 @@ module Vmpooler
804
809
 
805
810
  post "#{api_prefix}/ondemandvm/?" do
806
811
  content_type :json
812
+ metrics.increment('api_vm.post.ondemand.requestid')
807
813
 
808
814
  need_token! if Vmpooler::API.settings.config[:auth]
809
815
 
@@ -819,12 +825,12 @@ module Vmpooler
819
825
  else
820
826
  result[:bad_templates] = invalid
821
827
  invalid.each do |bad_template|
822
- metrics.increment('ondemandrequest.invalid.' + bad_template)
828
+ metrics.increment('ondemandrequest_fail.invalid.' + bad_template)
823
829
  end
824
830
  status 404
825
831
  end
826
832
  else
827
- metrics.increment('ondemandrequest.invalid.unknown')
833
+ metrics.increment('ondemandrequest_fail.invalid.unknown')
828
834
  status 404
829
835
  end
830
836
  rescue JSON::ParserError
@@ -841,6 +847,7 @@ module Vmpooler
841
847
  post "#{api_prefix}/ondemandvm/:template/?" do
842
848
  content_type :json
843
849
  result = { 'ok' => false }
850
+ metrics.increment('api_vm.delete.ondemand.template')
844
851
 
845
852
  need_token! if Vmpooler::API.settings.config[:auth]
846
853
 
@@ -853,12 +860,12 @@ module Vmpooler
853
860
  else
854
861
  result[:bad_templates] = invalid
855
862
  invalid.each do |bad_template|
856
- metrics.increment('ondemandrequest.invalid.' + bad_template)
863
+ metrics.increment('ondemandrequest_fail.invalid.' + bad_template)
857
864
  end
858
865
  status 404
859
866
  end
860
867
  else
861
- metrics.increment('ondemandrequest.invalid.unknown')
868
+ metrics.increment('ondemandrequest_fail.invalid.unknown')
862
869
  status 404
863
870
  end
864
871
 
@@ -867,6 +874,7 @@ module Vmpooler
867
874
 
868
875
  get "#{api_prefix}/ondemandvm/:requestid/?" do
869
876
  content_type :json
877
+ metrics.increment('api_vm.get.ondemand.request')
870
878
 
871
879
  status 404
872
880
  result = check_ondemand_request(params[:requestid])
@@ -877,6 +885,7 @@ module Vmpooler
877
885
  delete "#{api_prefix}/ondemandvm/:requestid/?" do
878
886
  content_type :json
879
887
  need_token! if Vmpooler::API.settings.config[:auth]
888
+ metrics.increment('api_vm.delete.ondemand.request')
880
889
 
881
890
  status 404
882
891
  result = delete_ondemand_request(params[:requestid])
@@ -887,6 +896,7 @@ module Vmpooler
887
896
  post "#{api_prefix}/vm/?" do
888
897
  content_type :json
889
898
  result = { 'ok' => false }
899
+ metrics.increment('api_vm.post.vm.checkout')
890
900
 
891
901
  payload = JSON.parse(request.body.read)
892
902
 
@@ -974,11 +984,14 @@ module Vmpooler
974
984
 
975
985
  if request_hash['status'] == 'ready'
976
986
  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 }
987
+ Parsing.get_platform_pool_count(request_hash['requested']) do |platform_alias, pool, _count|
988
+ instances = backend.smembers("vmpooler__#{request_id}__#{platform_alias}__#{pool}")
989
+
990
+ if result.key?(platform_alias)
991
+ result[platform_alias][:hostname] = result[platform_alias][:hostname] + instances
992
+ else
993
+ result[platform_alias] = { 'hostname': instances }
994
+ end
982
995
  end
983
996
  result['domain'] = config['domain'] if config['domain']
984
997
  status 200
@@ -989,14 +1002,19 @@ module Vmpooler
989
1002
  result['message'] = 'The request has been deleted'
990
1003
  status 200
991
1004
  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
- }
1005
+ Parsing.get_platform_pool_count(request_hash['requested']) do |platform_alias, pool, count|
1006
+ instance_count = backend.scard("vmpooler__#{request_id}__#{platform_alias}__#{pool}")
1007
+ instances_pending = count.to_i - instance_count.to_i
1008
+
1009
+ if result.key?(platform_alias) && result[platform_alias].key?(:ready)
1010
+ result[platform_alias][:ready] = (result[platform_alias][:ready].to_i + instance_count).to_s
1011
+ result[platform_alias][:pending] = (result[platform_alias][:pending].to_i + instances_pending).to_s
1012
+ else
1013
+ result[platform_alias] = {
1014
+ 'ready': instance_count.to_s,
1015
+ 'pending': instances_pending.to_s
1016
+ }
1017
+ end
1000
1018
  end
1001
1019
  end
1002
1020
 
@@ -1017,12 +1035,11 @@ module Vmpooler
1017
1035
  else
1018
1036
  backend.hset("vmpooler__odrequest__#{request_id}", 'status', 'deleted')
1019
1037
 
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|
1038
+ Parsing.get_platform_pool_count(platforms) do |platform_alias, pool, _count|
1039
+ backend.smembers("vmpooler__#{request_id}__#{platform_alias}__#{pool}")&.each do |vm|
1023
1040
  backend.smove("vmpooler__running__#{pool}", "vmpooler__completed__#{pool}", vm)
1024
1041
  end
1025
- backend.del("vmpooler__#{request_id}__#{pool_alias}__#{pool}")
1042
+ backend.del("vmpooler__#{request_id}__#{platform_alias}__#{pool}")
1026
1043
  end
1027
1044
  backend.expire("vmpooler__odrequest__#{request_id}", 129_600_0)
1028
1045
  end
@@ -1034,6 +1051,7 @@ module Vmpooler
1034
1051
  post "#{api_prefix}/vm/:template/?" do
1035
1052
  content_type :json
1036
1053
  result = { 'ok' => false }
1054
+ metrics.increment('api_vm.get.vm.template')
1037
1055
 
1038
1056
  payload = extract_templates_from_query_params(params[:template])
1039
1057
 
@@ -1057,6 +1075,7 @@ module Vmpooler
1057
1075
 
1058
1076
  get "#{api_prefix}/vm/:hostname/?" do
1059
1077
  content_type :json
1078
+ metrics.increment('api_vm.get.vm.hostname')
1060
1079
 
1061
1080
  result = {}
1062
1081
 
@@ -1129,6 +1148,7 @@ module Vmpooler
1129
1148
 
1130
1149
  delete "#{api_prefix}/vm/:hostname/?" do
1131
1150
  content_type :json
1151
+ metrics.increment('api_vm.delete.vm.hostname')
1132
1152
 
1133
1153
  result = {}
1134
1154
 
@@ -1146,8 +1166,9 @@ module Vmpooler
1146
1166
 
1147
1167
  status 200
1148
1168
  result['ok'] = true
1169
+ metrics.increment('delete.success')
1149
1170
  else
1150
- metrics.increment('delete.srem.failed')
1171
+ metrics.increment('delete.failed')
1151
1172
  end
1152
1173
  end
1153
1174
 
@@ -1156,6 +1177,7 @@ module Vmpooler
1156
1177
 
1157
1178
  put "#{api_prefix}/vm/:hostname/?" do
1158
1179
  content_type :json
1180
+ metrics.increment('api_vm.put.vm.modify')
1159
1181
 
1160
1182
  status 404
1161
1183
  result = { 'ok' => false }
@@ -1164,7 +1186,7 @@ module Vmpooler
1164
1186
 
1165
1187
  params[:hostname] = hostname_shorten(params[:hostname], config['domain'])
1166
1188
 
1167
- if backend.exists('vmpooler__vm__' + params[:hostname])
1189
+ if backend.exists?('vmpooler__vm__' + params[:hostname])
1168
1190
  begin
1169
1191
  jdata = JSON.parse(request.body.read)
1170
1192
  rescue StandardError
@@ -1232,6 +1254,7 @@ module Vmpooler
1232
1254
 
1233
1255
  post "#{api_prefix}/vm/:hostname/disk/:size/?" do
1234
1256
  content_type :json
1257
+ metrics.increment('api_vm.post.vm.disksize')
1235
1258
 
1236
1259
  need_token! if Vmpooler::API.settings.config[:auth]
1237
1260
 
@@ -1240,7 +1263,7 @@ module Vmpooler
1240
1263
 
1241
1264
  params[:hostname] = hostname_shorten(params[:hostname], config['domain'])
1242
1265
 
1243
- if ((params[:size].to_i > 0 )and (backend.exists('vmpooler__vm__' + params[:hostname])))
1266
+ if ((params[:size].to_i > 0 )and (backend.exists?('vmpooler__vm__' + params[:hostname])))
1244
1267
  result[params[:hostname]] = {}
1245
1268
  result[params[:hostname]]['disk'] = "+#{params[:size]}gb"
1246
1269
 
@@ -1255,6 +1278,7 @@ module Vmpooler
1255
1278
 
1256
1279
  post "#{api_prefix}/vm/:hostname/snapshot/?" do
1257
1280
  content_type :json
1281
+ metrics.increment('api_vm.post.vm.snapshot')
1258
1282
 
1259
1283
  need_token! if Vmpooler::API.settings.config[:auth]
1260
1284
 
@@ -1263,7 +1287,7 @@ module Vmpooler
1263
1287
 
1264
1288
  params[:hostname] = hostname_shorten(params[:hostname], config['domain'])
1265
1289
 
1266
- if backend.exists('vmpooler__vm__' + params[:hostname])
1290
+ if backend.exists?('vmpooler__vm__' + params[:hostname])
1267
1291
  result[params[:hostname]] = {}
1268
1292
 
1269
1293
  o = [('a'..'z'), ('0'..'9')].map(&:to_a).flatten
@@ -1280,6 +1304,7 @@ module Vmpooler
1280
1304
 
1281
1305
  post "#{api_prefix}/vm/:hostname/snapshot/:snapshot/?" do
1282
1306
  content_type :json
1307
+ metrics.increment('api_vm.post.vm.disksize')
1283
1308
 
1284
1309
  need_token! if Vmpooler::API.settings.config[:auth]
1285
1310