vmpooler 1.1.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/vmpooler/api/helpers.rb +7 -5
- data/lib/vmpooler/api/v1.rb +110 -0
- data/lib/vmpooler/pool_manager.rb +57 -22
- data/lib/vmpooler/public/img/logo.png +0 -0
- data/lib/vmpooler/version.rb +1 -1
- data/lib/vmpooler.rb +14 -1
- metadata +25 -24
- data/lib/vmpooler/providers/vsphere.rb +0 -1172
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69a8b533d59906b740d63c48c2f237bac2d42c3bbc770ed178e41f9981c7f955
|
4
|
+
data.tar.gz: '0609a290e75b7deb1f167a059b6342c43ce7942e600c7f67ba29b3389f411b59'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd7da108e63619bb0f7a80d1567ee38e6c05c3d87d77982fa81d117612577eb7480940f39c910c8d7fb0ec373972233a304f7bc63e80dae35c7d1f0ba9edddf4
|
7
|
+
data.tar.gz: d7023cc35609431f37f55a74fd07312afaefab627558ad55f9d03eaa320dce8929ffdb36fc4ca2ab6d0a299a1d93820a2767714680813e3f7f2fbb78fac01b27
|
data/lib/vmpooler/api/helpers.rb
CHANGED
@@ -56,14 +56,11 @@ module Vmpooler
|
|
56
56
|
return false
|
57
57
|
end
|
58
58
|
|
59
|
-
def authenticate_ldap(port, host, user_object, base, username_str, password_str)
|
59
|
+
def authenticate_ldap(port, host, encryption_hash, user_object, base, username_str, password_str)
|
60
60
|
ldap = Net::LDAP.new(
|
61
61
|
:host => host,
|
62
62
|
:port => port,
|
63
|
-
:encryption =>
|
64
|
-
:method => :start_tls,
|
65
|
-
:tls_options => { :ssl_version => 'TLSv1' }
|
66
|
-
},
|
63
|
+
:encryption => encryption_hash,
|
67
64
|
:base => base,
|
68
65
|
:auth => {
|
69
66
|
:method => :simple,
|
@@ -86,6 +83,10 @@ module Vmpooler
|
|
86
83
|
ldap_port = auth[:ldap]['port'] || 389
|
87
84
|
ldap_user_obj = auth[:ldap]['user_object']
|
88
85
|
ldap_host = auth[:ldap]['host']
|
86
|
+
ldap_encryption_hash = auth[:ldap]['encryption'] || {
|
87
|
+
:method => :start_tls,
|
88
|
+
:tls_options => { :ssl_version => 'TLSv1' }
|
89
|
+
}
|
89
90
|
|
90
91
|
unless ldap_base.is_a? Array
|
91
92
|
ldap_base = ldap_base.split
|
@@ -100,6 +101,7 @@ module Vmpooler
|
|
100
101
|
result = authenticate_ldap(
|
101
102
|
ldap_port,
|
102
103
|
ldap_host,
|
104
|
+
ldap_encryption_hash,
|
103
105
|
search_user_obj,
|
104
106
|
search_base,
|
105
107
|
username_str,
|
data/lib/vmpooler/api/v1.rb
CHANGED
@@ -28,6 +28,10 @@ module Vmpooler
|
|
28
28
|
Vmpooler::API.settings.config[:pools]
|
29
29
|
end
|
30
30
|
|
31
|
+
def pools_at_startup
|
32
|
+
Vmpooler::API.settings.config[:pools_at_startup]
|
33
|
+
end
|
34
|
+
|
31
35
|
def pool_exists?(template)
|
32
36
|
Vmpooler::API.settings.config[:pool_names].include?(template)
|
33
37
|
end
|
@@ -236,12 +240,25 @@ module Vmpooler
|
|
236
240
|
result
|
237
241
|
end
|
238
242
|
|
243
|
+
def component_to_test(match, labels_string)
|
244
|
+
return if labels_string.nil?
|
245
|
+
|
246
|
+
labels_string_parts = labels_string.split(',')
|
247
|
+
labels_string_parts.each do |part|
|
248
|
+
key, value = part.split('=')
|
249
|
+
next if value.nil?
|
250
|
+
return value if key == match
|
251
|
+
end
|
252
|
+
'none'
|
253
|
+
end
|
254
|
+
|
239
255
|
def update_user_metrics(operation, vmname)
|
240
256
|
backend.multi
|
241
257
|
backend.hget("vmpooler__vm__#{vmname}", 'tag:jenkins_build_url')
|
242
258
|
backend.hget("vmpooler__vm__#{vmname}", 'token:user')
|
243
259
|
backend.hget("vmpooler__vm__#{vmname}", 'template')
|
244
260
|
jenkins_build_url, user, poolname = backend.exec
|
261
|
+
poolname = poolname.gsub('.', '_')
|
245
262
|
|
246
263
|
if user
|
247
264
|
user = user.gsub('.', '_')
|
@@ -276,6 +293,32 @@ module Vmpooler
|
|
276
293
|
puts 'd', "[!] [#{poolname}] failed while evaluating usage labels on '#{vmname}' with an error: #{e}"
|
277
294
|
end
|
278
295
|
|
296
|
+
def reset_pool_size(poolname)
|
297
|
+
result = { 'ok' => false }
|
298
|
+
|
299
|
+
pool_index = pool_index(pools)
|
300
|
+
|
301
|
+
pools_updated = 0
|
302
|
+
sync_pool_sizes
|
303
|
+
|
304
|
+
pool_size_now = pools[pool_index[poolname]]['size'].to_i
|
305
|
+
pool_size_original = pools_at_startup[pool_index[poolname]]['size'].to_i
|
306
|
+
result['pool_size_before_reset'] = pool_size_now
|
307
|
+
result['pool_size_before_overrides'] = pool_size_original
|
308
|
+
|
309
|
+
unless pool_size_now == pool_size_original
|
310
|
+
pools[pool_index[poolname]]['size'] = pool_size_original
|
311
|
+
backend.hdel('vmpooler__config__poolsize', poolname)
|
312
|
+
backend.sadd('vmpooler__pool__undo_size_override', poolname)
|
313
|
+
pools_updated += 1
|
314
|
+
status 201
|
315
|
+
end
|
316
|
+
|
317
|
+
status 200 unless pools_updated > 0
|
318
|
+
result['ok'] = true
|
319
|
+
result
|
320
|
+
end
|
321
|
+
|
279
322
|
def update_pool_size(payload)
|
280
323
|
result = { 'ok' => false }
|
281
324
|
|
@@ -296,6 +339,33 @@ module Vmpooler
|
|
296
339
|
result
|
297
340
|
end
|
298
341
|
|
342
|
+
def reset_pool_template(poolname)
|
343
|
+
result = { 'ok' => false }
|
344
|
+
|
345
|
+
pool_index_live = pool_index(pools)
|
346
|
+
pool_index_original = pool_index(pools_at_startup)
|
347
|
+
|
348
|
+
pools_updated = 0
|
349
|
+
sync_pool_templates
|
350
|
+
|
351
|
+
template_now = pools[pool_index_live[poolname]]['template']
|
352
|
+
template_original = pools_at_startup[pool_index_original[poolname]]['template']
|
353
|
+
result['template_before_reset'] = template_now
|
354
|
+
result['template_before_overrides'] = template_original
|
355
|
+
|
356
|
+
unless template_now == template_original
|
357
|
+
pools[pool_index_live[poolname]]['template'] = template_original
|
358
|
+
backend.hdel('vmpooler__config__template', poolname)
|
359
|
+
backend.sadd('vmpooler__pool__undo_template_override', poolname)
|
360
|
+
pools_updated += 1
|
361
|
+
status 201
|
362
|
+
end
|
363
|
+
|
364
|
+
status 200 unless pools_updated > 0
|
365
|
+
result['ok'] = true
|
366
|
+
result
|
367
|
+
end
|
368
|
+
|
299
369
|
def update_pool_template(payload)
|
300
370
|
result = { 'ok' => false }
|
301
371
|
|
@@ -1362,6 +1432,26 @@ module Vmpooler
|
|
1362
1432
|
JSON.pretty_generate(result)
|
1363
1433
|
end
|
1364
1434
|
|
1435
|
+
delete "#{api_prefix}/config/poolsize/:pool/?" do
|
1436
|
+
content_type :json
|
1437
|
+
result = { 'ok' => false }
|
1438
|
+
|
1439
|
+
if config['experimental_features']
|
1440
|
+
need_token! if Vmpooler::API.settings.config[:auth]
|
1441
|
+
|
1442
|
+
if pool_exists?(params[:pool])
|
1443
|
+
result = reset_pool_size(params[:pool])
|
1444
|
+
else
|
1445
|
+
metrics.increment('config.invalid.unknown')
|
1446
|
+
status 404
|
1447
|
+
end
|
1448
|
+
else
|
1449
|
+
status 405
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
JSON.pretty_generate(result)
|
1453
|
+
end
|
1454
|
+
|
1365
1455
|
post "#{api_prefix}/config/poolsize/?" do
|
1366
1456
|
content_type :json
|
1367
1457
|
result = { 'ok' => false }
|
@@ -1393,6 +1483,26 @@ module Vmpooler
|
|
1393
1483
|
JSON.pretty_generate(result)
|
1394
1484
|
end
|
1395
1485
|
|
1486
|
+
delete "#{api_prefix}/config/pooltemplate/:pool/?" do
|
1487
|
+
content_type :json
|
1488
|
+
result = { 'ok' => false }
|
1489
|
+
|
1490
|
+
if config['experimental_features']
|
1491
|
+
need_token! if Vmpooler::API.settings.config[:auth]
|
1492
|
+
|
1493
|
+
if pool_exists?(params[:pool])
|
1494
|
+
result = reset_pool_template(params[:pool])
|
1495
|
+
else
|
1496
|
+
metrics.increment('config.invalid.unknown')
|
1497
|
+
status 404
|
1498
|
+
end
|
1499
|
+
else
|
1500
|
+
status 405
|
1501
|
+
end
|
1502
|
+
|
1503
|
+
JSON.pretty_generate(result)
|
1504
|
+
end
|
1505
|
+
|
1396
1506
|
post "#{api_prefix}/config/pooltemplate/?" do
|
1397
1507
|
content_type :json
|
1398
1508
|
result = { 'ok' => false }
|
@@ -478,18 +478,6 @@ module Vmpooler
|
|
478
478
|
dereference_mutex(vm)
|
479
479
|
end
|
480
480
|
|
481
|
-
def component_to_test(match, labels_string)
|
482
|
-
return if labels_string.nil?
|
483
|
-
|
484
|
-
labels_string_parts = labels_string.split(',')
|
485
|
-
labels_string_parts.each do |part|
|
486
|
-
key, value = part.split('=')
|
487
|
-
next if value.nil?
|
488
|
-
return value if key == match
|
489
|
-
end
|
490
|
-
'none'
|
491
|
-
end
|
492
|
-
|
493
481
|
def purge_unused_vms_and_folders
|
494
482
|
global_purge = $config[:config]['purge_unconfigured_folders']
|
495
483
|
providers = $config[:providers].keys
|
@@ -640,7 +628,7 @@ module Vmpooler
|
|
640
628
|
end
|
641
629
|
|
642
630
|
# @return [Array] - a list of used providers from the config file, defaults to the default providers
|
643
|
-
# ie. ["
|
631
|
+
# ie. ["dummy"]
|
644
632
|
def used_providers
|
645
633
|
pools = config[:pools] || []
|
646
634
|
@used_providers ||= (pools.map { |pool| pool[:provider] || pool['provider'] }.compact + default_providers).uniq
|
@@ -650,7 +638,7 @@ module Vmpooler
|
|
650
638
|
# note: vsphere is the default if user does not specify although this should not be
|
651
639
|
# if vsphere is to no longer be loaded by default please remove
|
652
640
|
def default_providers
|
653
|
-
@default_providers ||= %w[
|
641
|
+
@default_providers ||= %w[dummy]
|
654
642
|
end
|
655
643
|
|
656
644
|
def get_pool_name_for_vm(vm_name, redis)
|
@@ -796,6 +784,10 @@ module Vmpooler
|
|
796
784
|
# - Fires when a pool reset is requested
|
797
785
|
# - Additional options
|
798
786
|
# :poolname
|
787
|
+
# :undo_override
|
788
|
+
# - Fires when a pool override removal is requested
|
789
|
+
# - Additional options
|
790
|
+
# :poolname
|
799
791
|
#
|
800
792
|
def sleep_with_wakeup_events(loop_delay, wakeup_period = 5, options = {})
|
801
793
|
exit_by = Time.now + loop_delay
|
@@ -838,6 +830,11 @@ module Vmpooler
|
|
838
830
|
break if pending
|
839
831
|
end
|
840
832
|
|
833
|
+
if options[:undo_override]
|
834
|
+
break if redis.sismember('vmpooler__pool__undo_template_override', options[:poolname])
|
835
|
+
break if redis.sismember('vmpooler__pool__undo_size_override', options[:poolname])
|
836
|
+
end
|
837
|
+
|
841
838
|
if options[:pending_vm]
|
842
839
|
pending_vm_count = redis.scard("vmpooler__pending__#{options[:poolname]}")
|
843
840
|
break unless pending_vm_count == 0
|
@@ -892,7 +889,7 @@ module Vmpooler
|
|
892
889
|
loop_delay = (loop_delay * loop_delay_decay).to_i
|
893
890
|
loop_delay = loop_delay_max if loop_delay > loop_delay_max
|
894
891
|
end
|
895
|
-
sleep_with_wakeup_events(loop_delay, loop_delay_min, pool_size_change: true, poolname: pool['name'], pool_template_change: true, clone_target_change: true, pending_vm: true, pool_reset: true)
|
892
|
+
sleep_with_wakeup_events(loop_delay, loop_delay_min, pool_size_change: true, poolname: pool['name'], pool_template_change: true, clone_target_change: true, pending_vm: true, pool_reset: true, undo_override: true)
|
896
893
|
|
897
894
|
unless maxloop == 0
|
898
895
|
break if loop_count >= maxloop
|
@@ -1052,15 +1049,18 @@ module Vmpooler
|
|
1052
1049
|
return if mutex.locked?
|
1053
1050
|
|
1054
1051
|
@redis.with_metrics do |redis|
|
1055
|
-
|
1056
|
-
break if
|
1052
|
+
pool_size_requested = redis.hget('vmpooler__config__poolsize', pool['name'])
|
1053
|
+
break if pool_size_requested.nil?
|
1057
1054
|
|
1058
|
-
|
1059
|
-
|
1055
|
+
pool_size_requested = Integer(pool_size_requested)
|
1056
|
+
pool_size_currently = pool['size']
|
1057
|
+
break if pool_size_requested == pool_size_currently
|
1060
1058
|
|
1061
1059
|
mutex.synchronize do
|
1062
|
-
pool['size'] =
|
1060
|
+
pool['size'] = pool_size_requested
|
1063
1061
|
end
|
1062
|
+
|
1063
|
+
$logger.log('s', "[*] [#{pool['name']}] size updated from #{pool_size_currently} to #{pool_size_requested}")
|
1064
1064
|
end
|
1065
1065
|
end
|
1066
1066
|
|
@@ -1078,6 +1078,38 @@ module Vmpooler
|
|
1078
1078
|
end
|
1079
1079
|
end
|
1080
1080
|
|
1081
|
+
def undo_override(pool, provider)
|
1082
|
+
poolname = pool['name']
|
1083
|
+
mutex = pool_mutex(poolname)
|
1084
|
+
return if mutex.locked?
|
1085
|
+
|
1086
|
+
@redis.with_metrics do |redis|
|
1087
|
+
break unless redis.sismember('vmpooler__pool__undo_template_override', poolname)
|
1088
|
+
|
1089
|
+
redis.srem('vmpooler__pool__undo_template_override', poolname)
|
1090
|
+
template_now = pool['template']
|
1091
|
+
template_original = $config[:pools_at_startup][$config[:pool_index][poolname]]['template']
|
1092
|
+
|
1093
|
+
mutex.synchronize do
|
1094
|
+
update_pool_template(pool, provider, template_original, template_now, redis)
|
1095
|
+
end
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
@redis.with_metrics do |redis|
|
1099
|
+
break unless redis.sismember('vmpooler__pool__undo_size_override', poolname)
|
1100
|
+
|
1101
|
+
redis.srem('vmpooler__pool__undo_size_override', poolname)
|
1102
|
+
pool_size_now = pool['size']
|
1103
|
+
pool_size_original = $config[:pools_at_startup][$config[:pool_index][poolname]]['size']
|
1104
|
+
|
1105
|
+
mutex.synchronize do
|
1106
|
+
pool['size'] = pool_size_original
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
$logger.log('s', "[*] [#{poolname}] size updated from #{pool_size_now} to #{pool_size_original}")
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
|
1081
1113
|
def create_inventory(pool, provider, pool_check_response)
|
1082
1114
|
inventory = {}
|
1083
1115
|
begin
|
@@ -1312,6 +1344,9 @@ module Vmpooler
|
|
1312
1344
|
# Reset a pool when poolreset is requested from the API
|
1313
1345
|
reset_pool(pool)
|
1314
1346
|
|
1347
|
+
# Undo overrides submitted via the api
|
1348
|
+
undo_override(pool, provider)
|
1349
|
+
|
1315
1350
|
pool_check_response
|
1316
1351
|
end
|
1317
1352
|
|
@@ -1526,8 +1561,8 @@ module Vmpooler
|
|
1526
1561
|
# Set default provider for all pools that do not have one defined
|
1527
1562
|
$config[:pools].each do |pool|
|
1528
1563
|
if pool['provider'].nil?
|
1529
|
-
$logger.log('d', "[!] Setting provider for pool '#{pool['name']}' to '
|
1530
|
-
pool['provider'] = '
|
1564
|
+
$logger.log('d', "[!] Setting provider for pool '#{pool['name']}' to 'dummy' as default")
|
1565
|
+
pool['provider'] = 'dummy'
|
1531
1566
|
end
|
1532
1567
|
end
|
1533
1568
|
|
Binary file
|
data/lib/vmpooler/version.rb
CHANGED
data/lib/vmpooler.rb
CHANGED
@@ -7,7 +7,6 @@ module Vmpooler
|
|
7
7
|
require 'net/ldap'
|
8
8
|
require 'open-uri'
|
9
9
|
require 'pickup'
|
10
|
-
require 'rbvmomi'
|
11
10
|
require 'redis'
|
12
11
|
require 'set'
|
13
12
|
require 'sinatra/base'
|
@@ -119,6 +118,11 @@ module Vmpooler
|
|
119
118
|
parsed_config[:auth][:ldap]['port'] = string_to_int(ENV['LDAP_PORT']) if ENV['LDAP_PORT']
|
120
119
|
parsed_config[:auth][:ldap]['base'] = ENV['LDAP_BASE'] if ENV['LDAP_BASE']
|
121
120
|
parsed_config[:auth][:ldap]['user_object'] = ENV['LDAP_USER_OBJECT'] if ENV['LDAP_USER_OBJECT']
|
121
|
+
if parsed_config[:auth]['provider'] == 'ldap' && parsed_config[:auth][:ldap].key?('encryption')
|
122
|
+
parsed_config[:auth][:ldap]['encryption'] = parsed_config[:auth][:ldap]['encryption']
|
123
|
+
elsif parsed_config[:auth]['provider'] == 'ldap'
|
124
|
+
parsed_config[:auth][:ldap]['encryption'] = {}
|
125
|
+
end
|
122
126
|
end
|
123
127
|
|
124
128
|
# Create an index of pool aliases
|
@@ -128,8 +132,17 @@ module Vmpooler
|
|
128
132
|
parsed_config[:pools] = load_pools_from_redis(redis)
|
129
133
|
end
|
130
134
|
|
135
|
+
# Marshal.dump is paired with Marshal.load to create a copy that has its own memory space
|
136
|
+
# so that each can be edited independently
|
137
|
+
# rubocop:disable Security/MarshalLoad
|
138
|
+
|
139
|
+
# retain a copy of the pools that were observed at startup
|
140
|
+
serialized_pools = Marshal.dump(parsed_config[:pools])
|
141
|
+
parsed_config[:pools_at_startup] = Marshal.load(serialized_pools)
|
142
|
+
|
131
143
|
# Create an index of pools by title
|
132
144
|
parsed_config[:pool_index] = pool_index(parsed_config[:pools])
|
145
|
+
# rubocop:enable Security/MarshalLoad
|
133
146
|
|
134
147
|
parsed_config[:pools].each do |pool|
|
135
148
|
parsed_config[:pool_names] << pool['name']
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vmpooler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08
|
11
|
+
date: 2021-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -226,26 +226,6 @@ dependencies:
|
|
226
226
|
- - "~>"
|
227
227
|
- !ruby/object:Gem::Version
|
228
228
|
version: '13.0'
|
229
|
-
- !ruby/object:Gem::Dependency
|
230
|
-
name: rbvmomi
|
231
|
-
requirement: !ruby/object:Gem::Requirement
|
232
|
-
requirements:
|
233
|
-
- - ">="
|
234
|
-
- !ruby/object:Gem::Version
|
235
|
-
version: '2.1'
|
236
|
-
- - "<"
|
237
|
-
- !ruby/object:Gem::Version
|
238
|
-
version: '4.0'
|
239
|
-
type: :runtime
|
240
|
-
prerelease: false
|
241
|
-
version_requirements: !ruby/object:Gem::Requirement
|
242
|
-
requirements:
|
243
|
-
- - ">="
|
244
|
-
- !ruby/object:Gem::Version
|
245
|
-
version: '2.1'
|
246
|
-
- - "<"
|
247
|
-
- !ruby/object:Gem::Version
|
248
|
-
version: '4.0'
|
249
229
|
- !ruby/object:Gem::Dependency
|
250
230
|
name: redis
|
251
231
|
requirement: !ruby/object:Gem::Requirement
|
@@ -400,6 +380,26 @@ dependencies:
|
|
400
380
|
- - ">="
|
401
381
|
- !ruby/object:Gem::Version
|
402
382
|
version: 0.11.2
|
383
|
+
- !ruby/object:Gem::Dependency
|
384
|
+
name: thor
|
385
|
+
requirement: !ruby/object:Gem::Requirement
|
386
|
+
requirements:
|
387
|
+
- - "~>"
|
388
|
+
- !ruby/object:Gem::Version
|
389
|
+
version: '1.0'
|
390
|
+
- - ">="
|
391
|
+
- !ruby/object:Gem::Version
|
392
|
+
version: 1.0.1
|
393
|
+
type: :development
|
394
|
+
prerelease: false
|
395
|
+
version_requirements: !ruby/object:Gem::Requirement
|
396
|
+
requirements:
|
397
|
+
- - "~>"
|
398
|
+
- !ruby/object:Gem::Version
|
399
|
+
version: '1.0'
|
400
|
+
- - ">="
|
401
|
+
- !ruby/object:Gem::Version
|
402
|
+
version: 1.0.1
|
403
403
|
- !ruby/object:Gem::Dependency
|
404
404
|
name: yarjuf
|
405
405
|
requirement: !ruby/object:Gem::Requirement
|
@@ -444,10 +444,10 @@ files:
|
|
444
444
|
- lib/vmpooler/providers.rb
|
445
445
|
- lib/vmpooler/providers/base.rb
|
446
446
|
- lib/vmpooler/providers/dummy.rb
|
447
|
-
- lib/vmpooler/providers/vsphere.rb
|
448
447
|
- lib/vmpooler/public/bootstrap.min.css
|
449
448
|
- lib/vmpooler/public/img/bg.png
|
450
449
|
- lib/vmpooler/public/img/logo.gif
|
450
|
+
- lib/vmpooler/public/img/logo.png
|
451
451
|
- lib/vmpooler/public/img/spinner.svg
|
452
452
|
- lib/vmpooler/public/img/subtle_dots.png
|
453
453
|
- lib/vmpooler/public/img/textured_paper.png
|
@@ -479,7 +479,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
479
479
|
- !ruby/object:Gem::Version
|
480
480
|
version: '0'
|
481
481
|
requirements: []
|
482
|
-
|
482
|
+
rubyforge_project:
|
483
|
+
rubygems_version: 2.7.6.2
|
483
484
|
signing_key:
|
484
485
|
specification_version: 4
|
485
486
|
summary: vmpooler provides configurable pools of instantly-available (running) virtual
|