Skip to content

Commit

Permalink
(DIO-2675) Undo pool size template overrides
Browse files Browse the repository at this point in the history
This implements a delete method for pooltemplate that removes the
override hash from redis and then resets triggers a reset of the pool.

It also implements a delete method for poolsize that removes the
override hash from redis. Unlike pooltemplate, it does not reset the
pool.
  • Loading branch information
genebean committed Oct 27, 2021
1 parent b9a5b52 commit fb05341
Show file tree
Hide file tree
Showing 6 changed files with 388 additions and 2 deletions.
42 changes: 42 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,27 @@ $ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"2","deb
}
```

##### DELETE /config/poolsize/<pool>

Delete an overridden pool size. This results in the values from VMPooler's config being used.

Return codes:
* 200 - when nothing was changed but no error occurred
* 201 - size reset successful
* 401 - when not authorized
* 404 - pool does not exist
* 405 - The endpoint is disabled because experimental features are disabled

```
$ curl -X DELETE -u jdoe --url vmpooler.example.com/api/v1/poolsize/almalinux-8-x86_64
Enter host password for user 'jdoe':
```
```json
{
"ok": true
}
```

##### POST /config/pooltemplate

Change the template configured for a pool, and replenish the pool with instances built from the new template.
Expand Down Expand Up @@ -775,6 +796,27 @@ $ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"templat
}
```

##### DELETE /config/pooltemplate/<pool>

Delete an overridden pool template. This results in the values from VMPooler's config being used.

Return codes:
* 200 - when nothing was changed but no error occurred
* 201 - template reset successful
* 401 - when not authorized
* 404 - pool does not exist
* 405 - The endpoint is disabled because experimental features are disabled

```
$ curl -X DELETE -u jdoe --url vmpooler.example.com/api/v1/pooltemplate/almalinux-8-x86_64
Enter host password for user 'jdoe':
```
```json
{
"ok": true
}
```

##### POST /poolreset

Clear all pending and ready instances in a pool, and deploy replacements
Expand Down
5 changes: 5 additions & 0 deletions lib/vmpooler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,13 @@ def self.config(filepath = 'vmpooler.yaml')
parsed_config[:pools] = load_pools_from_redis(redis)
end

# retain a copy of the pools that were observed at startup
serialized_pools = Marshal.dump(parsed_config[:pools])
parsed_config[:pools_at_startup] = Marshal.load(serialized_pools)

# Create an index of pools by title
parsed_config[:pool_index] = pool_index(parsed_config[:pools])
parsed_config[:pool_index_at_startup] = pool_index(parsed_config[:pools_at_startup])

parsed_config[:pools].each do |pool|
parsed_config[:pool_names] << pool['name']
Expand Down
98 changes: 98 additions & 0 deletions lib/vmpooler/api/v1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ def pools
Vmpooler::API.settings.config[:pools]
end

def pools_at_startup
Vmpooler::API.settings.config[:pools_at_startup]
end

def pool_exists?(template)
Vmpooler::API.settings.config[:pool_names].include?(template)
end
Expand Down Expand Up @@ -289,6 +293,33 @@ def update_user_metrics(operation, vmname)
puts 'd', "[!] [#{poolname}] failed while evaluating usage labels on '#{vmname}' with an error: #{e}"
end

def reset_pool_size(poolname)
result = { 'ok' => false }

pool_index_live = pool_index(pools)
pool_index_original = pool_index(pools_at_startup)

pools_updated = 0
sync_pool_sizes

pool_size_now = pools[pool_index_live[poolname]]['size']
pool_size_original = pools_at_startup[pool_index_original[poolname]]['size']
result['pool_size_before_reset'] = pool_size_now
result['pool_size_before_overrides'] = pool_size_original

unless pool_size_now.to_i == pool_size_original.to_i
pools[pool_index_live[poolname]]['size'] = pool_size_original.to_i
backend.hdel('vmpooler__config__poolsize', poolname)
backend.sadd('vmpooler__pool__undo_size_override', poolname)
pools_updated += 1
status 201
end

status 200 unless pools_updated > 0
result['ok'] = true
result
end

def update_pool_size(payload)
result = { 'ok' => false }

Expand All @@ -309,6 +340,33 @@ def update_pool_size(payload)
result
end

def reset_pool_template(poolname)
result = { 'ok' => false }

pool_index_live = pool_index(pools)
pool_index_original = pool_index(pools_at_startup)

pools_updated = 0
sync_pool_templates

template_now = pools[pool_index_live[poolname]]['template']
template_original = pools_at_startup[pool_index_original[poolname]]['template']
result['template_before_reset'] = template_now
result['template_before_overrides'] = template_original

unless template_now == template_original
pools[pool_index_live[poolname]]['template'] = template_original
backend.hdel('vmpooler__config__template', poolname)
backend.sadd('vmpooler__pool__undo_template_override', poolname)
pools_updated += 1
status 201
end

status 200 unless pools_updated > 0
result['ok'] = true
result
end

def update_pool_template(payload)
result = { 'ok' => false }

Expand Down Expand Up @@ -1375,6 +1433,26 @@ def delete_ondemand_request(request_id)
JSON.pretty_generate(result)
end

delete "#{api_prefix}/config/poolsize/:pool/?" do
content_type :json
result = { 'ok' => false }

if config['experimental_features']
need_token! if Vmpooler::API.settings.config[:auth]

if pool_exists?(params[:pool])
result = reset_pool_size(params[:pool])
else
metrics.increment('config.invalid.unknown')
status 404
end
else
status 405
end

JSON.pretty_generate(result)
end

post "#{api_prefix}/config/poolsize/?" do
content_type :json
result = { 'ok' => false }
Expand Down Expand Up @@ -1406,6 +1484,26 @@ def delete_ondemand_request(request_id)
JSON.pretty_generate(result)
end

delete "#{api_prefix}/config/pooltemplate/:pool/?" do
content_type :json
result = { 'ok' => false }

if config['experimental_features']
need_token! if Vmpooler::API.settings.config[:auth]

if pool_exists?(params[:pool])
result = reset_pool_template(params[:pool])
else
metrics.increment('config.invalid.unknown')
status 404
end
else
status 405
end

JSON.pretty_generate(result)
end

post "#{api_prefix}/config/pooltemplate/?" do
content_type :json
result = { 'ok' => false }
Expand Down
52 changes: 50 additions & 2 deletions lib/vmpooler/pool_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,10 @@ def time_passed?(_event, time)
# - Fires when a pool reset is requested
# - Additional options
# :poolname
# :undo_override
# - Fires when a pool override removal is requested
# - Additional options
# :poolname
#
def sleep_with_wakeup_events(loop_delay, wakeup_period = 5, options = {})
exit_by = Time.now + loop_delay
Expand Down Expand Up @@ -826,6 +830,11 @@ def sleep_with_wakeup_events(loop_delay, wakeup_period = 5, options = {})
break if pending
end

if options[:undo_override]
break if redis.sismember('vmpooler__pool__undo_template_override', options[:poolname])
break if redis.sismember('vmpooler__pool__undo_size_override', options[:poolname])
end

if options[:pending_vm]
pending_vm_count = redis.scard("vmpooler__pending__#{options[:poolname]}")
break unless pending_vm_count == 0
Expand Down Expand Up @@ -880,7 +889,7 @@ def check_pool(pool,
loop_delay = (loop_delay * loop_delay_decay).to_i
loop_delay = loop_delay_max if loop_delay > loop_delay_max
end
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)
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)

unless maxloop == 0
break if loop_count >= maxloop
Expand Down Expand Up @@ -1044,11 +1053,14 @@ def update_pool_size(pool)
break if poolsize.nil?

poolsize = Integer(poolsize)
break if poolsize == pool['size']
pool_size_now = pool['size']
break if poolsize == pool_size_now

mutex.synchronize do
pool['size'] = poolsize
end

$logger.log('s', "[*] [#{pool['name']}] size updated from #{pool_size_now} to #{poolsize}")
end
end

Expand All @@ -1066,6 +1078,39 @@ def reset_pool(pool)
end
end

def undo_override(pool, provider)
poolname = pool['name']
mutex = pool_mutex(poolname)
return if mutex.locked?


@redis.with_metrics do |redis|
break unless redis.sismember('vmpooler__pool__undo_template_override', poolname)

redis.srem('vmpooler__pool__undo_template_override', poolname)
template_now = pool['template']
template_original = $config[:pools_at_startup][$config[:pool_index_at_startup][poolname]]['template']

mutex.synchronize do
update_pool_template(pool, provider, template_original, template_now, redis)
end
end

@redis.with_metrics do |redis|
break unless redis.sismember('vmpooler__pool__undo_size_override', poolname)

redis.srem('vmpooler__pool__undo_size_override', poolname)
pool_size_now = pool['size']
pool_size_original = $config[:pools_at_startup][$config[:pool_index_at_startup][poolname]]['size']

mutex.synchronize do
pool['size'] = pool_size_original
end

$logger.log('s', "[*] [#{poolname}] size updated from #{pool_size_now} to #{pool_size_original}")
end
end

def create_inventory(pool, provider, pool_check_response)
inventory = {}
begin
Expand Down Expand Up @@ -1300,6 +1345,9 @@ def _check_pool(pool, provider)
# Reset a pool when poolreset is requested from the API
reset_pool(pool)

# Undo overrides submitted via the api
undo_override(pool, provider)

pool_check_response
end

Expand Down
Loading

0 comments on commit fb05341

Please sign in to comment.