Skip to content

Commit f45677d

Browse files
committed
Adding command to get vms from abs
1 parent 9b80b6b commit f45677d

12 files changed

+119
-106
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ floaty get centos-7-x86_64=2 debian-7-x86_64 windows-10=3 --token mytokenstring
6666

6767
### vmfloaty dotfile
6868

69-
If you do not wish to continuely specify various config options with the cli, you can have a dotfile in your home directory for some defaults. For example:
69+
If you do not wish to continually specify various config options with the cli, you can have a dotfile in your home directory for some defaults. For example:
7070

7171
#### Basic configuration
7272

@@ -135,6 +135,7 @@ services:
135135
url: 'https://abs.example.net/api/v2'
136136
token: 'abs-tokenstring'
137137
type: 'abs' # <-- 'type' is necessary for any non-vmpooler service
138+
138139
```
139140

140141
With this configuration, you could list available OS types from nspooler like this:

lib/vmfloaty.rb

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def run
3636
c.option '--notoken', 'Makes a request without a token'
3737
c.option '--force', 'Forces vmfloaty to get requested vms'
3838
c.option '--json', 'Prints retrieved vms in JSON format'
39+
c.option '--job', 'Check on status of Job (ABS Only)'
3940
c.action do |args, options|
4041
verbose = options.verbose || config['verbose']
4142
service = Service.new(options, config)
@@ -84,6 +85,7 @@ def run
8485
c.option '--url STRING', String, 'URL of pooler service'
8586
c.action do |args, options|
8687
verbose = options.verbose || config['verbose']
88+
8789
service = Service.new(options, config)
8890
filter = args[0]
8991

lib/vmfloaty/abs.rb

+68-58
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def self.list(verbose, url, os_filter = nil)
1313
os_list = []
1414

1515
response = conn.get 'status/platforms/vmpooler'
16+
1617
response_body = JSON.parse(response.body)
1718
os_list << "*** VMPOOLER Pools ***"
1819
os_list = os_list + JSON.parse(response_body["vmpooler_platforms"])
@@ -31,89 +32,98 @@ def self.list(verbose, url, os_filter = nil)
3132

3233
os_list.delete 'ok'
3334

34-
puts os_list
35-
3635
os_filter ? os_list.select { |i| i[/#{os_filter}/] } : os_list
3736
end
3837

39-
# List active VMs from ABS
40-
def self.list_active(verbose, url, token)
41-
status = Auth.token_status(verbose, url, token)
42-
status['reserved_hosts'] || []
43-
end
38+
# Retrieve an OS from ABS.
39+
def self.retrieve(verbose, os_types, token, url, user)
40+
#
41+
# Contents of post must be:j
42+
#
43+
# {
44+
# "resources": {
45+
# "centos-7-i386": 1,
46+
# "ubuntu-1404-x86_64": 2
47+
# },
48+
# "job": {
49+
# "id": "12345",
50+
# "tags": {
51+
# "user": "jenkins",
52+
# "jenkins_build_url": "https://jenkins/job/platform_puppet_intn-van-sys_master"
53+
# }
54+
# }
55+
# }
4456

45-
def self.retrieve(verbose, os_type, token, url)
4657
conn = Http.get_conn(verbose, url)
4758
conn.headers['X-AUTH-TOKEN'] = token if token
4859

49-
os_string = os_type.map { |os, num| Array(os) * num }.flatten.join('+')
50-
raise MissingParamError, 'No operating systems provided to obtain.' if os_string.empty?
51-
52-
response = conn.post "host/#{os_string}"
53-
54-
res_body = JSON.parse(response.body)
60+
saved_job_id = Time.now.to_i
5561

56-
if res_body['ok']
57-
res_body
58-
elsif response.status == 401
59-
raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
60-
else
61-
raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/host/#{os_string}. #{res_body}"
62-
end
63-
end
62+
reqObj = {
63+
resources: os_types,
64+
job: {
65+
id: saved_job_id,
66+
tags: {
67+
user: user,
68+
url_string: "floaty://#{user}/#{saved_job_id}"
69+
}
70+
}
71+
}
6472

65-
def self.modify(verbose, url, hostname, token, modify_hash)
66-
raise TokenError, 'Token provided was nil; Request cannot be made to modify VM' if token.nil?
73+
# os_string = os_type.map { |os, num| Array(os) * num }.flatten.join('+')
74+
# raise MissingParamError, 'No operating systems provided to obtain.' if os_string.empty?
75+
puts "Requesting VMs with job_id: #{saved_job_id}. Will retry for up to an hour."
76+
response = conn.post "api/v2/request", reqObj.to_json
6777

68-
modify_hash.each do |key, _value|
69-
raise ModifyError, "Configured service type does not support modification of #{key}" unless %i[reason reserved_for_reason].include? key
70-
end
78+
i = 0
79+
retries = 360
7180

72-
if modify_hash[:reason]
73-
# "reason" is easier to type than "reserved_for_reason", but nspooler needs the latter
74-
modify_hash[:reserved_for_reason] = modify_hash.delete :reason
81+
if response.status == 401
82+
raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
7583
end
7684

77-
conn = Http.get_conn(verbose, url)
78-
conn.headers['X-AUTH-TOKEN'] = token
85+
(1..retries).each do |i|
86+
queue_place, res_body = check_queue(conn, saved_job_id, reqObj)
87+
if res_body
88+
return translated(res_body)
89+
end
7990

80-
response = conn.put do |req|
81-
req.url "host/#{hostname}"
82-
req.body = modify_hash.to_json
91+
puts "Waiting 10 seconds to check if ABS request has been filled. Queue Position: #{queue_place}... (x#{i})"
92+
sleep(10)
8393
end
84-
85-
response.body.empty? ? {} : JSON.parse(response.body)
94+
return nil
8695
end
8796

88-
def self.disk(_verbose, _url, _hostname, _token, _disk)
89-
raise ModifyError, 'Configured service type does not support modification of disk space'
90-
end
91-
92-
def self.snapshot(_verbose, _url, _hostname, _token)
93-
raise ModifyError, 'Configured service type does not support snapshots'
94-
end
97+
#
98+
# We should fix the ABS API to be more like the vmpooler or nspooler api, but for now
99+
#
100+
def self.translated res_body
101+
vmpooler_formatted_body = Hash.new
102+
103+
res_body.each do |host|
104+
if vmpooler_formatted_body[host["type"]] && vmpooler_formatted_body[host["type"]]["hostname"].class == Array
105+
vmpooler_formatted_body[host["type"]]["hostname"] << host["hostname"]
106+
else
107+
vmpooler_formatted_body[host["type"]] = { "hostname" => [host["hostname"]] }
108+
end
109+
end
110+
vmpooler_formatted_body["ok"] = true
95111

96-
def self.revert(_verbose, _url, _hostname, _token, _snapshot_sha)
97-
raise ModifyError, 'Configured service type does not support snapshots'
112+
return vmpooler_formatted_body
98113
end
99114

100-
def self.delete(verbose, url, hosts, token)
101-
raise TokenError, 'Token provided was nil; Request cannot be made to delete VM' if token.nil?
115+
def self.check_queue conn, job_id, reqObj
116+
queue_info_response = conn.get "/status/queue/info/#{job_id}"
117+
queue_info = JSON.parse(queue_info_response.body)
102118

103-
conn = Http.get_conn(verbose, url)
119+
response = conn.post "api/v2/request", reqObj.to_json
104120

105-
conn.headers['X-AUTH-TOKEN'] = token if token
106121

107-
response_body = {}
108-
109-
hosts = hosts.split(',') unless hosts.is_a? Array
110-
hosts.each do |host|
111-
response = conn.delete "host/#{host}"
122+
if response.body.length > 0
112123
res_body = JSON.parse(response.body)
113-
response_body[host] = res_body
124+
return queue_info["queue_place"], res_body
114125
end
115-
116-
response_body
126+
return queue_info["queue_place"], nil
117127
end
118128

119129
def self.status(verbose, url)

lib/vmfloaty/nonstandard_pooler.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def self.list_active(verbose, url, token)
2222
status['reserved_hosts'] || []
2323
end
2424

25-
def self.retrieve(verbose, os_type, token, url)
25+
def self.retrieve(verbose, os_type, token, url, user)
2626
conn = Http.get_conn(verbose, url)
2727
conn.headers['X-AUTH-TOKEN'] = token if token
2828

lib/vmfloaty/service.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def type
3636

3737
def user
3838
unless @config['user']
39-
puts 'Enter your pooler service username:'
39+
puts "Enter your #{@config["url"]} service username:"
4040
@config['user'] = STDIN.gets.chomp
4141
end
4242
@config['user']
@@ -58,7 +58,7 @@ def get_new_token(verbose)
5858

5959
def delete_token(verbose, token_value = @config['token'])
6060
username = user
61-
pass = Commander::UI.password 'Enter your pooler service password:', '*'
61+
pass = Commander::UI.password "Enter your #{@config["url"]} service password:", '*'
6262
Auth.delete_token(verbose, url, username, pass, token_value)
6363
end
6464

@@ -78,7 +78,7 @@ def list_active(verbose)
7878
def retrieve(verbose, os_types, use_token = true)
7979
puts 'Requesting a vm without a token...' unless use_token
8080
token_value = use_token ? token : nil
81-
@service_object.retrieve verbose, os_types, token_value, url
81+
@service_object.retrieve verbose, os_types, token_value, url, user
8282
end
8383

8484
def ssh(verbose, host_os, use_token = true)

lib/vmfloaty/utils.rb

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def self.standardize_hostnames(response_body)
3131
# }
3232
# }
3333

34+
# abs pooler response body example when `floaty get` arguments are `{"hostname"=>"thin-soutane.delivery.puppetlabs.net", "type"=>"centos-7.2-tmpfs-x86_64", "engine"=>"vmpooler"}`
35+
3436
raise ArgumentError, "Bad GET response passed to format_hosts: #{response_body.to_json}" unless response_body.delete('ok')
3537

3638
# vmpooler reports the domain separately from the hostname

spec/vmfloaty/abs/auth_spec.rb

+13-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
describe Pooler do
77
before :each do
8-
@abs_url = 'https://abs.example.com'
8+
@abs_url = 'https://abs.example.com/api/v2'
99
end
1010

1111
describe '#get_token' do
@@ -14,18 +14,18 @@
1414
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
1515
end
1616

17-
it 'returns a token from vmpooler' do
18-
stub_request(:post, 'https://first.last:[email protected]/api/v2/token')
19-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length' => '0', 'User-Agent' => 'Faraday v0.9.2' })
17+
it 'returns a token from abs' do
18+
stub_request(:post, "https://first.last:[email protected]/api/v2/token")
19+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Basic Zmlyc3QubGFzdDpwYXNzd29yZA==', 'Content-Length'=>'0'})
2020
.to_return(:status => 200, :body => @get_token_response, :headers => {})
2121

2222
token = Auth.get_token(false, @abs_url, 'first.last', 'password')
2323
expect(token).to eq @token
2424
end
2525

2626
it 'raises a token error if something goes wrong' do
27-
stub_request(:post, 'https://first.last:[email protected]/api/v2/token')
28-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length' => '0', 'User-Agent' => 'Faraday v0.9.2' })
27+
stub_request(:post, "https://first.last:[email protected]/api/v2/token")
28+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Basic Zmlyc3QubGFzdDpwYXNzd29yZA==', 'Content-Length'=>'0'})
2929
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
3030

3131
expect { Auth.get_token(false, @abs_url, 'first.last', 'password') }.to raise_error(TokenError)
@@ -39,16 +39,16 @@
3939
end
4040

4141
it 'deletes the specified token' do
42-
stub_request(:delete, 'https://first.last:[email protected]/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
43-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Faraday v0.9.2' })
44-
.to_return(:status => 200, :body => @delete_token_response, :headers => {})
42+
stub_request(:delete, "https://first.last:[email protected]/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
43+
.with(headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Basic Zmlyc3QubGFzdDpwYXNzd29yZA=='})
44+
.to_return(:status => 200, :body => @delete_token_response, :headers => {})
4545

4646
expect(Auth.delete_token(false, @abs_url, 'first.last', 'password', @token)).to eq JSON.parse(@delete_token_response)
4747
end
4848

4949
it 'raises a token error if something goes wrong' do
50-
stub_request(:delete, 'https://first.last:[email protected]/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
51-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Faraday v0.9.2' })
50+
stub_request(:delete, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
51+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3'})
5252
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
5353

5454
expect { Auth.delete_token(false, @abs_url, 'first.last', 'password', @token) }.to raise_error(TokenError)
@@ -67,15 +67,15 @@
6767

6868
it 'checks the status of a token' do
6969
stub_request(:get, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
70-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Faraday v0.9.2' })
70+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3'})
7171
.to_return(:status => 200, :body => @token_status_response, :headers => {})
7272

7373
expect(Auth.token_status(false, @abs_url, @token)).to eq JSON.parse(@token_status_response)
7474
end
7575

7676
it 'raises a token error if something goes wrong' do
7777
stub_request(:get, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
78-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Faraday v0.9.2' })
78+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3'})
7979
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
8080

8181
expect { Auth.token_status(false, @abs_url, @token) }.to raise_error(TokenError)

spec/vmfloaty/auth_spec.rb

+6-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
it 'returns a token from vmpooler' do
1818
stub_request(:post, 'https://first.last:[email protected]/token')
19-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length' => '0', 'User-Agent' => 'Faraday v0.9.2' })
19+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length' => '0' })
2020
.to_return(:status => 200, :body => @get_token_response, :headers => {})
2121

2222
token = Auth.get_token(false, @vmpooler_url, 'first.last', 'password')
@@ -25,7 +25,7 @@
2525

2626
it 'raises a token error if something goes wrong' do
2727
stub_request(:post, 'https://first.last:[email protected]/token')
28-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length' => '0', 'User-Agent' => 'Faraday v0.9.2' })
28+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length' => '0' })
2929
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
3030

3131
expect { Auth.get_token(false, @vmpooler_url, 'first.last', 'password') }.to raise_error(TokenError)
@@ -40,15 +40,15 @@
4040

4141
it 'deletes the specified token' do
4242
stub_request(:delete, 'https://first.last:[email protected]/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
43-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Faraday v0.9.2' })
43+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3' })
4444
.to_return(:status => 200, :body => @delete_token_response, :headers => {})
4545

4646
expect(Auth.delete_token(false, @vmpooler_url, 'first.last', 'password', @token)).to eq JSON.parse(@delete_token_response)
4747
end
4848

4949
it 'raises a token error if something goes wrong' do
5050
stub_request(:delete, 'https://first.last:[email protected]/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
51-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Faraday v0.9.2' })
51+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3' })
5252
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
5353

5454
expect { Auth.delete_token(false, @vmpooler_url, 'first.last', 'password', @token) }.to raise_error(TokenError)
@@ -67,15 +67,15 @@
6767

6868
it 'checks the status of a token' do
6969
stub_request(:get, "#{@vmpooler_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
70-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Faraday v0.9.2' })
70+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3' })
7171
.to_return(:status => 200, :body => @token_status_response, :headers => {})
7272

7373
expect(Auth.token_status(false, @vmpooler_url, @token)).to eq JSON.parse(@token_status_response)
7474
end
7575

7676
it 'raises a token error if something goes wrong' do
7777
stub_request(:get, "#{@vmpooler_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
78-
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Faraday v0.9.2' })
78+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3' })
7979
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
8080

8181
expect { Auth.token_status(false, @vmpooler_url, @token) }.to raise_error(TokenError)

0 commit comments

Comments
 (0)