Merge pull request #324 from puppetlabs/QENG-7201

(QENG-7201) Vmpooler pool statistic endpoint optimization
This commit is contained in:
Spencer McElmurry 2019-04-17 11:56:48 -05:00 committed by GitHub
commit ce6ea1662d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 170 additions and 1 deletions

View file

@ -629,6 +629,52 @@ The valid sections are "boot", "clone" or "tag" eg. `vmpooler.example.com/api/v1
You can further drill-down the data by specifying the second level parameter to query eg
`vmpooler.example.com/api/v1/summary/tag/created_by`
##### GET /poolstat?pool=FOO
For parameter `pool`, containing a comma separated list of pool names to query, this endpoint returns
each of the pool's ready, max and alias information. It can be used to get a fast response for
the required pools instead of using the /status API endpoint
Return codes
* 200 OK
```
$ curl https://vmpooler.example.com/api/v1/poolstat?pool=centos-6-x86_64
```
```json
{
"pools": {
"centos-6-x86_64": {
"ready": 25,
"max": 25,
"alias": [
"centos-6-64",
"centos-6-amd64"
]
}
}
}
```
##### GET /totalrunning
Fast endpoint to return the total number of VMs in a 'running' state
Return codes
* 200 OK
```
$ curl https://vmpooler.example.com/api/v1/totalrunning
```
```json
{
"running": 362
}
```
#### Managing pool configuration via API <a name="poolconfig"></a>
##### GET /config

View file

@ -341,6 +341,79 @@ module Vmpooler
JSON.pretty_generate(Hash[result.sort_by { |k, _v| k }])
end
# request statistics for specific pools by passing parameter 'pool'
# with a coma separated list of pools we want to query ?pool=ABC,DEF
# returns the ready, max numbers and the aliases (if set)
get "#{api_prefix}/poolstat/?" do
content_type :json
result = {}
poolscopy = []
if params[:pool]
subpool = params[:pool].split(",")
poolscopy = pools.select do |p|
if subpool.include?(p['name'])
true
elsif !p['alias'].nil?
if p['alias'].is_a?(Array)
(p['alias'] & subpool).any?
elsif p['alias'].is_a?(String)
subpool.include?(p['alias'])
end
end
end
end
result[:pools] = {}
poolscopy.each do |pool|
result[:pools][pool['name']] = {}
max = pool['size']
aka = pool['alias']
result[:pools][pool['name']][:max] = max
if aka
result[:pools][pool['name']][:alias] = aka
end
end
# using pipelined is much faster than querying each of the pools
res = backend.pipelined do
poolscopy.each do |pool|
backend.scard('vmpooler__ready__' + pool['name'])
end
end
res.each_with_index { |ready, i| result[:pools][poolscopy[i]['name']][:ready] = ready }
JSON.pretty_generate(Hash[result.sort_by { |k, _v| k }])
end
# requests the total number of running VMs
get "#{api_prefix}/totalrunning/?" do
content_type :json
queue = {
running: 0,
}
# using pipelined is much faster than querying each of the pools and adding them
# as we get the result.
res = backend.pipelined do
pools.each do |pool|
backend.scard('vmpooler__running__' + pool['name'])
end
end
queue[:running] = res.inject(0){ |m, x| m+x }
JSON.pretty_generate(queue)
end
get "#{api_prefix}/summary/?" do
content_type :json

View file

@ -12,7 +12,7 @@ describe Vmpooler::API::V1 do
Vmpooler::API
end
describe '/status' do
describe 'status and metrics endpoints' do
let(:prefix) { '/api/v1' }
let(:config) {
@ -186,5 +186,55 @@ describe Vmpooler::API::V1 do
expect(result["status"]).to_not be(nil)
end
end
describe 'GET /poolstat' do
it 'returns empty list when pool is not set' do
get "#{prefix}/poolstat"
expect(last_response.header['Content-Type']).to eq('application/json')
result = JSON.parse(last_response.body)
expect(result == {})
end
it 'returns empty list when pool is not found' do
get "#{prefix}/poolstat?pool=unknownpool"
expect(last_response.header['Content-Type']).to eq('application/json')
result = JSON.parse(last_response.body)
expect(result == {})
end
it 'returns one pool when requesting one with alias' do
get "#{prefix}/poolstat?pool=pool1"
expect(last_response.header['Content-Type']).to eq('application/json')
result = JSON.parse(last_response.body)
expect(result["pools"].size == 1)
expect(result["pools"]["pool1"]["ready"]).to eq(0)
expect(result["pools"]["pool1"]["max"]).to eq(5)
expect(result["pools"]["pool1"]["alias"]).to eq(['poolone', 'poolun'])
end
it 'returns one pool when requesting one without alias' do
get "#{prefix}/poolstat?pool=pool2"
expect(last_response.header['Content-Type']).to eq('application/json')
result = JSON.parse(last_response.body)
expect(result["pools"].size == 1)
expect(result["pools"]["pool2"]["ready"]).to eq(0)
expect(result["pools"]["pool2"]["max"]).to eq(10)
expect(result["pools"]["pool2"]["alias"]).to be(nil)
end
it 'returns multiple pools when requesting csv' do
get "#{prefix}/poolstat?pool=pool1,pool2"
expect(last_response.header['Content-Type']).to eq('application/json')
result = JSON.parse(last_response.body)
expect(result["pools"].size == 2)
end
end
describe 'GET /totalrunning' do
it 'returns the number of running VMs' do
get "#{prefix}/totalrunning"
expect(last_response.header['Content-Type']).to eq('application/json')
5.times {|i| create_running_vm("pool1", "vm-#{i}") }
5.times {|i| create_running_vm("pool3", "vm-#{i}") }
result = JSON.parse(last_response.body)
expect(result["running"] == 10)
end
end
end
end