mirror of
https://github.com/puppetlabs/vmfloaty.git
synced 2026-01-26 05:28:40 -05:00
Integrate nonstandard pooler service into vmfloaty
This commit is contained in:
parent
e78bcc6216
commit
ca5b0f5e8b
12 changed files with 1190 additions and 482 deletions
36
README.md
36
README.md
|
|
@ -95,20 +95,46 @@ services:
|
||||||
token: 'alternate-tokenstring'
|
token: 'alternate-tokenstring'
|
||||||
```
|
```
|
||||||
|
|
||||||
vmfloaty will now use the top-level keys (just "user" above) as default values, and you will be able to specify a service name with `--service` when you run floaty. If you don't specify a service name, vmfloaty will first try to use the default, top-level values. If there is no default URL or token specified in the config file, vmfloaty will then use the first configured service as the default.
|
- If you run `floaty` without a `--service <name>` option, vmfloaty will use the first configured service by default.
|
||||||
|
With the config file above, the default would be to use the 'main' vmpooler instance.
|
||||||
|
- If keys are missing for a configured service, vmfloaty will attempt to fall back to the top-level values.
|
||||||
|
With the config file above, 'brian' will be used as the username for both configured services, since neither specifies a username.
|
||||||
|
|
||||||
Examples using the above configuration:
|
Examples using the above configuration:
|
||||||
|
|
||||||
List available vm types from our main vmpooler instance:
|
List available vm types from our main vmpooler instance:
|
||||||
```sh
|
```sh
|
||||||
floaty list --service main --active
|
floaty list --service main
|
||||||
# or, since the first configured service is the default:
|
# or, since the first configured service is used by default:
|
||||||
floaty list --active
|
floaty list
|
||||||
```
|
```
|
||||||
|
|
||||||
List available vm types from our alternate vmpooler instance:
|
List available vm types from our alternate vmpooler instance:
|
||||||
```sh
|
```sh
|
||||||
floaty list --service alternate --active
|
floaty list --service alternate
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using a Nonstandard Pooler service
|
||||||
|
|
||||||
|
vmfloaty is capable of working with Puppet's [nonstandard pooler](https://github.com/puppetlabs/nspooler) in addition to the default vmpooler API. To add a nonstandard pooler service, specify an API `type` value in your service configuration, like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# file at /Users/me/.vmfloaty.yml
|
||||||
|
user: 'brian'
|
||||||
|
services:
|
||||||
|
vm:
|
||||||
|
url: 'https://vmpooler.mycompany.net/api/v1'
|
||||||
|
token: 'tokenstring'
|
||||||
|
ns:
|
||||||
|
url: 'https://nspooler.mycompany.net/api/v1'
|
||||||
|
token: 'nspooler-tokenstring'
|
||||||
|
type: 'nonstandard' # <-- 'type' is necessary for any non-vmpooler service
|
||||||
|
```
|
||||||
|
|
||||||
|
With this configuration, you could list available OS types from nspooler like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
floaty list --service ns
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Valid config keys
|
#### Valid config keys
|
||||||
|
|
|
||||||
438
lib/vmfloaty.rb
438
lib/vmfloaty.rb
|
|
@ -5,11 +5,13 @@ require 'commander'
|
||||||
require 'colorize'
|
require 'colorize'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'pp'
|
require 'pp'
|
||||||
|
require 'uri'
|
||||||
require 'vmfloaty/auth'
|
require 'vmfloaty/auth'
|
||||||
require 'vmfloaty/pooler'
|
require 'vmfloaty/pooler'
|
||||||
require 'vmfloaty/version'
|
require 'vmfloaty/version'
|
||||||
require 'vmfloaty/conf'
|
require 'vmfloaty/conf'
|
||||||
require 'vmfloaty/utils'
|
require 'vmfloaty/utils'
|
||||||
|
require 'vmfloaty/service'
|
||||||
require 'vmfloaty/ssh'
|
require 'vmfloaty/ssh'
|
||||||
|
|
||||||
class Vmfloaty
|
class Vmfloaty
|
||||||
|
|
@ -35,11 +37,8 @@ class Vmfloaty
|
||||||
c.option '--force', 'Forces vmfloaty to get requested vms'
|
c.option '--force', 'Forces vmfloaty to get requested vms'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
token = options.token || service_config['token'] || config['token']
|
use_token = !options.notoken
|
||||||
user = options.user ||= service_config['user'] || config['user']
|
|
||||||
url = options.url ||= service_config['url'] || config['url']
|
|
||||||
no_token = options.notoken
|
|
||||||
force = options.force
|
force = options.force
|
||||||
|
|
||||||
if args.empty?
|
if args.empty?
|
||||||
|
|
@ -50,61 +49,20 @@ class Vmfloaty
|
||||||
os_types = Utils.generate_os_hash(args)
|
os_types = Utils.generate_os_hash(args)
|
||||||
|
|
||||||
max_pool_request = 5
|
max_pool_request = 5
|
||||||
large_pool_requests = os_types.select{|k,v| v > max_pool_request}
|
large_pool_requests = os_types.select{|_,v| v > max_pool_request}
|
||||||
if ! large_pool_requests.empty? and ! force
|
if ! large_pool_requests.empty? and ! force
|
||||||
STDERR.puts "Requesting vms over #{max_pool_request} requires a --force flag."
|
STDERR.puts "Requesting vms over #{max_pool_request} requires a --force flag."
|
||||||
STDERR.puts "Try again with `floaty get --force`"
|
STDERR.puts "Try again with `floaty get --force`"
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
unless os_types.empty?
|
if os_types.empty?
|
||||||
if no_token
|
|
||||||
begin
|
|
||||||
response = Pooler.retrieve(verbose, os_types, nil, url)
|
|
||||||
rescue MissingParamError
|
|
||||||
STDERR.puts e
|
|
||||||
STDERR.puts "See `floaty get --help` for more information on how to get VMs."
|
|
||||||
rescue AuthError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
puts Utils.format_hosts(response)
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
unless token
|
|
||||||
puts "No token found. Retrieving a token..."
|
|
||||||
if !user
|
|
||||||
STDERR.puts "You did not provide a user to authenticate to the pooler service with"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
pass = password "Enter your pooler service password please:", '*'
|
|
||||||
begin
|
|
||||||
token = Auth.get_token(verbose, url, user, pass)
|
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
puts "\nToken retrieved!"
|
|
||||||
puts token
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
response = Pooler.retrieve(verbose, os_types, token, url)
|
|
||||||
rescue MissingParamError
|
|
||||||
STDERR.puts e
|
|
||||||
STDERR.puts "See `floaty get --help` for more information on how to get VMs."
|
|
||||||
rescue AuthError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
puts Utils.format_hosts(response)
|
|
||||||
exit 0
|
|
||||||
end
|
|
||||||
else
|
|
||||||
STDERR.puts "No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs."
|
STDERR.puts "No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs."
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
response = service.retrieve(verbose, os_types, use_token)
|
||||||
|
puts Utils.format_hosts(response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -120,30 +78,22 @@ class Vmfloaty
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--url STRING', String, 'URL of pooler service'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
filter = args[0]
|
filter = args[0]
|
||||||
url = options.url ||= service_config['url'] ||=config['url']
|
|
||||||
token = options.token || service_config['token'] || config['token']
|
|
||||||
active = options.active
|
|
||||||
|
|
||||||
if active
|
if options.active
|
||||||
# list active vms
|
# list active vms
|
||||||
begin
|
running_vms = service.list_active(verbose)
|
||||||
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
host = URI.parse(service.url).host
|
||||||
rescue TokenError => e
|
if running_vms.empty?
|
||||||
STDERR.puts e
|
puts "You have no running VMs on #{host}"
|
||||||
exit 1
|
else
|
||||||
rescue Exception => e
|
puts "Your VMs on #{host}:"
|
||||||
STDERR.puts e
|
Utils.pretty_print_hosts(verbose, service, running_vms)
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if ! running_vms.nil?
|
|
||||||
Utils.prettyprint_hosts(running_vms, verbose, url)
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# list available vms from pooler
|
# list available vms from pooler
|
||||||
os_list = Pooler.list(verbose, url, filter)
|
os_list = service.list(verbose, filter)
|
||||||
puts os_list
|
puts os_list
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -159,136 +109,67 @@ class Vmfloaty
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--url STRING', String, 'URL of pooler service'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
|
||||||
hostname = args[0]
|
hostname = args[0]
|
||||||
|
|
||||||
query_req = Pooler.query(verbose, url, hostname)
|
query_req = service.query(verbose, hostname)
|
||||||
pp query_req
|
pp query_req
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
command :modify do |c|
|
command :modify do |c|
|
||||||
c.syntax = 'floaty modify hostname [options]'
|
c.syntax = 'floaty modify hostname [options]'
|
||||||
c.summary = 'Modify a vms tags, time to live, and disk space'
|
c.summary = 'Modify a VM\'s tags, time to live, disk space, or reservation reason'
|
||||||
c.description = 'This command makes modifications to the virtual machines state in the pooler service. You can either append tags to the vm, increase how long it stays active for, or increase the amount of disk space.'
|
c.description = 'This command makes modifications to the virtual machines state in the pooler service. You can either append tags to the vm, increase how long it stays active for, or increase the amount of disk space.'
|
||||||
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag', 'floaty modify myhost1 --lifetime 12 --url https://myurl --token mytokenstring --tags \'{"tag":"myvalue"}\''
|
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag', 'floaty modify myhost1 --lifetime 12 --url https://myurl --token mytokenstring --tags \'{"tag":"myvalue"}\''
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--service STRING', String, 'Configured pooler service name'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--url STRING', String, 'URL of pooler service'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
c.option '--token STRING', String, 'Token for pooler service'
|
||||||
c.option '--lifetime INT', Integer, 'VM TTL (Integer, in hours)'
|
c.option '--lifetime INT', Integer, 'VM TTL (Integer, in hours) [vmpooler only]'
|
||||||
c.option '--disk INT', Integer, 'Increases VM disk space (Integer, in gb)'
|
c.option '--disk INT', Integer, 'Increases VM disk space (Integer, in gb) [vmpooler only]'
|
||||||
c.option '--tags STRING', String, 'free-form VM tagging (json)'
|
c.option '--tags STRING', String, 'free-form VM tagging (json) [vmpooler only]'
|
||||||
|
c.option '--reason STRING', String, 'VM reservation reason [nspooler only]'
|
||||||
c.option '--all', 'Modifies all vms acquired by a token'
|
c.option '--all', 'Modifies all vms acquired by a token'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
|
||||||
hostname = args[0]
|
hostname = args[0]
|
||||||
lifetime = options.lifetime
|
|
||||||
disk = options.disk
|
|
||||||
tags = JSON.parse(options.tags) if options.tags
|
|
||||||
token = options.token || service_config['token'] || config['token']
|
|
||||||
modify_all = options.all
|
modify_all = options.all
|
||||||
|
|
||||||
running_vms = nil
|
if hostname.nil? and !modify_all
|
||||||
|
STDERR.puts "ERROR: Provide a hostname or specify --all."
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
running_vms = modify_all ? service.list_active(verbose) : hostname.split(",")
|
||||||
|
|
||||||
|
tags = options.tags ? JSON.parse(options.tags) : nil
|
||||||
|
modify_hash = {
|
||||||
|
lifetime: options.lifetime,
|
||||||
|
disk: options.disk,
|
||||||
|
tags: tags,
|
||||||
|
reason: options.reason
|
||||||
|
}
|
||||||
|
modify_hash.delete_if { |_, value| value.nil? }
|
||||||
|
|
||||||
|
unless modify_hash.empty?
|
||||||
|
ok = true
|
||||||
|
modified_hash = {}
|
||||||
|
running_vms.each do |vm|
|
||||||
|
begin
|
||||||
|
modified_hash[vm] = service.modify(verbose, vm, modify_hash)
|
||||||
|
rescue ModifyError => e
|
||||||
|
STDERR.puts e
|
||||||
|
ok = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if ok
|
||||||
if modify_all
|
if modify_all
|
||||||
begin
|
puts "Successfully modified all VMs."
|
||||||
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
|
||||||
rescue Exception => e
|
|
||||||
STDERR.puts e
|
|
||||||
end
|
|
||||||
elsif hostname.include? ","
|
|
||||||
running_vms = hostname.split(",")
|
|
||||||
end
|
|
||||||
|
|
||||||
if lifetime || tags
|
|
||||||
# all vms
|
|
||||||
if !running_vms.nil?
|
|
||||||
begin
|
|
||||||
modify_hash = {}
|
|
||||||
modify_flag = true
|
|
||||||
|
|
||||||
running_vms.each do |vm|
|
|
||||||
modify_hash[vm] = Pooler.modify(verbose, url, vm, token, lifetime, tags)
|
|
||||||
end
|
|
||||||
|
|
||||||
modify_hash.each do |hostname,status|
|
|
||||||
if status == false
|
|
||||||
STDERR.puts "Could not modify #{hostname}."
|
|
||||||
modify_flag = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if modify_flag
|
|
||||||
puts "Successfully modified all vms. Use `floaty list --active` to see the results."
|
|
||||||
end
|
|
||||||
rescue Exception => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
# Single Vm
|
puts "Successfully modified VM #{hostname}."
|
||||||
begin
|
|
||||||
modify_req = Pooler.modify(verbose, url, hostname, token, lifetime, tags)
|
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if modify_req["ok"]
|
|
||||||
puts "Successfully modified vm #{hostname}."
|
|
||||||
else
|
|
||||||
STDERR.puts "Could not modify given host #{hostname} at #{url}."
|
|
||||||
puts modify_req
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if disk
|
|
||||||
# all vms
|
|
||||||
if !running_vms.nil?
|
|
||||||
begin
|
|
||||||
modify_hash = {}
|
|
||||||
modify_flag = true
|
|
||||||
|
|
||||||
running_vms.each do |vm|
|
|
||||||
modify_hash[vm] = Pooler.disk(verbose, url, vm, token, disk)
|
|
||||||
end
|
|
||||||
|
|
||||||
modify_hash.each do |hostname,status|
|
|
||||||
if status == false
|
|
||||||
STDERR.puts "Could not update disk space on #{hostname}."
|
|
||||||
modify_flag = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if modify_flag
|
|
||||||
puts "Successfully made request to update disk space on all vms."
|
|
||||||
end
|
|
||||||
rescue Exception => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# single vm
|
|
||||||
begin
|
|
||||||
disk_req = Pooler.disk(verbose, url, hostname, token, disk)
|
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if disk_req["ok"]
|
|
||||||
puts "Successfully made request to update disk space of vm #{hostname}."
|
|
||||||
else
|
|
||||||
STDERR.puts "Could not modify given host #{hostname} at #{url}."
|
|
||||||
puts disk_req
|
|
||||||
exit 1
|
|
||||||
end
|
end
|
||||||
|
puts "Use `floaty list --active` to see the results."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -307,67 +188,69 @@ class Vmfloaty
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--url STRING', String, 'URL of pooler service'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
hostnames = args[0]
|
hostnames = args[0]
|
||||||
token = options.token || service_config['token'] || config['token']
|
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
|
||||||
delete_all = options.all
|
delete_all = options.all
|
||||||
force = options.f
|
force = options.f
|
||||||
|
|
||||||
|
failures = []
|
||||||
|
successes = []
|
||||||
|
|
||||||
if delete_all
|
if delete_all
|
||||||
# get vms with token
|
running_vms = service.list_active(verbose)
|
||||||
begin
|
if running_vms.empty?
|
||||||
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
STDERR.puts "You have no running VMs."
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
rescue Exception => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if ! running_vms.nil?
|
|
||||||
Utils.prettyprint_hosts(running_vms, verbose, url)
|
|
||||||
# query y/n
|
|
||||||
puts
|
|
||||||
|
|
||||||
if force
|
|
||||||
ans = true
|
|
||||||
else
|
else
|
||||||
ans = agree("Delete all VMs associated with token #{token}? [y/N]")
|
Utils.pretty_print_hosts(verbose, service, running_vms)
|
||||||
|
# Confirm deletion
|
||||||
|
puts
|
||||||
|
confirmed = true
|
||||||
|
unless force
|
||||||
|
confirmed = agree('Delete all these VMs? [y/N]')
|
||||||
end
|
end
|
||||||
|
if confirmed
|
||||||
if ans
|
response = service.delete(verbose, running_vms)
|
||||||
# delete vms
|
response.each do |hostname, result|
|
||||||
puts "Scheduling all vms for for deletion"
|
if result['ok']
|
||||||
response = Pooler.delete(verbose, url, running_vms, token)
|
successes << hostname
|
||||||
response.each do |host,vals|
|
else
|
||||||
if vals['ok'] == false
|
failures << hostname
|
||||||
STDERR.puts "There was a problem with your request for vm #{host}."
|
|
||||||
STDERR.puts vals
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
elsif hostnames || args
|
||||||
exit 0
|
hostnames = hostnames.split(',')
|
||||||
|
results = service.delete(verbose, hostnames)
|
||||||
|
results.each do |hostname, result|
|
||||||
|
if result['ok']
|
||||||
|
successes << hostname
|
||||||
|
else
|
||||||
|
failures << hostname
|
||||||
end
|
end
|
||||||
|
end
|
||||||
if hostnames.nil?
|
else
|
||||||
STDERR.puts "You did not provide any hosts to delete"
|
STDERR.puts "You did not provide any hosts to delete"
|
||||||
exit 1
|
exit 1
|
||||||
else
|
|
||||||
hosts = hostnames.split(',')
|
|
||||||
begin
|
|
||||||
Pooler.delete(verbose, url, hosts, token)
|
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "Scheduled pooler service to delete vms #{hosts}."
|
unless failures.empty?
|
||||||
exit 0
|
STDERR.puts 'Unable to delete the following VMs:'
|
||||||
|
failures.each do |hostname|
|
||||||
|
STDERR.puts "- #{hostname}"
|
||||||
end
|
end
|
||||||
|
STDERR.puts 'Check `floaty list --active`; Do you need to specify a different service?'
|
||||||
|
end
|
||||||
|
|
||||||
|
unless successes.empty?
|
||||||
|
puts unless failures.empty?
|
||||||
|
puts 'Scheduled the following VMs for deletion:'
|
||||||
|
successes.each do |hostname|
|
||||||
|
puts "- #{hostname}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
exit 1 unless failures.empty?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -382,14 +265,12 @@ class Vmfloaty
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
c.option '--token STRING', String, 'Token for pooler service'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
|
||||||
hostname = args[0]
|
hostname = args[0]
|
||||||
token = options.token ||= service_config['token'] ||= config['token']
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
snapshot_req = Pooler.snapshot(verbose, url, hostname, token)
|
snapshot_req = service.snapshot(verbose, hostname)
|
||||||
rescue TokenError => e
|
rescue TokenError, ModifyError => e
|
||||||
STDERR.puts e
|
STDERR.puts e
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
@ -411,10 +292,8 @@ class Vmfloaty
|
||||||
c.option '--snapshot STRING', String, 'SHA of snapshot'
|
c.option '--snapshot STRING', String, 'SHA of snapshot'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
|
||||||
hostname = args[0]
|
hostname = args[0]
|
||||||
token = options.token || service_config['token'] || config['token']
|
|
||||||
snapshot_sha = args[1] || options.snapshot
|
snapshot_sha = args[1] || options.snapshot
|
||||||
|
|
||||||
if args[1] && options.snapshot
|
if args[1] && options.snapshot
|
||||||
|
|
@ -422,8 +301,8 @@ class Vmfloaty
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
revert_req = Pooler.revert(verbose, url, hostname, token, snapshot_sha)
|
revert_req = service.revert(verbose, hostname, snapshot_sha)
|
||||||
rescue TokenError => e
|
rescue TokenError, ModifyError => e
|
||||||
STDERR.puts e
|
STDERR.puts e
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
@ -441,22 +320,14 @@ class Vmfloaty
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--service STRING', String, 'Configured pooler service name'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--url STRING', String, 'URL of pooler service'
|
||||||
c.option '--json', 'Prints status in JSON format'
|
c.option '--json', 'Prints status in JSON format'
|
||||||
c.action do |args, options|
|
c.action do |_, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
|
||||||
|
|
||||||
status = Pooler.status(verbose, url)
|
|
||||||
message = status['status']['message']
|
|
||||||
pools = status['pools']
|
|
||||||
|
|
||||||
if options.json
|
if options.json
|
||||||
pp status
|
pp service.status(verbose)
|
||||||
else
|
else
|
||||||
Utils.prettyprint_status(status, message, pools, verbose)
|
Utils.pretty_print_status(verbose, service)
|
||||||
end
|
end
|
||||||
|
|
||||||
exit status['status']['ok']
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -468,12 +339,11 @@ class Vmfloaty
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--service STRING', String, 'Configured pooler service name'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--url STRING', String, 'URL of pooler service'
|
||||||
c.action do |args, options|
|
c.action do |_, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
|
||||||
|
|
||||||
summary = Pooler.summary(verbose, url)
|
summary = service.summary(verbose)
|
||||||
pp summary
|
pp summary
|
||||||
exit 0
|
exit 0
|
||||||
end
|
end
|
||||||
|
|
@ -491,47 +361,36 @@ class Vmfloaty
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
c.option '--token STRING', String, 'Token for pooler service'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
action = args.first
|
action = args.first
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
|
||||||
token = args[1] ||= options.token ||= service_config['token'] ||= config['token']
|
|
||||||
user = options.user ||= service_config['user'] ||= config['user']
|
|
||||||
|
|
||||||
|
begin
|
||||||
case action
|
case action
|
||||||
when "get"
|
when 'get'
|
||||||
pass = password "Enter your pooler service password please:", '*'
|
token = service.get_new_token(verbose)
|
||||||
begin
|
|
||||||
token = Auth.get_token(verbose, url, user, pass)
|
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
puts token
|
puts token
|
||||||
exit 0
|
when 'delete'
|
||||||
when "delete"
|
result = service.delete_token(verbose, options.token)
|
||||||
pass = password "Enter your pooler service password please:", '*'
|
|
||||||
begin
|
|
||||||
result = Auth.delete_token(verbose, url, user, pass, token)
|
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
puts result
|
puts result
|
||||||
exit 0
|
when 'status'
|
||||||
when "status"
|
token_value = options.token
|
||||||
begin
|
if token_value.nil?
|
||||||
status = Auth.token_status(verbose, url, token)
|
token_value = args[1]
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
exit 1
|
|
||||||
end
|
end
|
||||||
|
status = service.token_status(verbose, token_value)
|
||||||
puts status
|
puts status
|
||||||
exit 0
|
|
||||||
when nil
|
when nil
|
||||||
STDERR.puts "No action provided"
|
STDERR.puts 'No action provided'
|
||||||
|
exit 1
|
||||||
else
|
else
|
||||||
STDERR.puts "Unknown action: #{action}"
|
STDERR.puts "Unknown action: #{action}"
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
|
rescue TokenError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
exit 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -548,11 +407,8 @@ class Vmfloaty
|
||||||
c.option '--notoken', 'Makes a request without a token'
|
c.option '--notoken', 'Makes a request without a token'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service_config = Utils.get_service_from_config(config, options.service)
|
service = Service.new(options, config)
|
||||||
url = options.url ||= service_config['url'] ||= config['url']
|
use_token = !options.notoken
|
||||||
token = options.token ||= service_config['token'] ||= config['token']
|
|
||||||
user = options.user ||= service_config['user'] ||= config['user']
|
|
||||||
no_token = options.notoken
|
|
||||||
|
|
||||||
if args.empty?
|
if args.empty?
|
||||||
STDERR.puts "No operating systems provided to obtain. See `floaty ssh --help` for more information on how to get VMs."
|
STDERR.puts "No operating systems provided to obtain. See `floaty ssh --help` for more information on how to get VMs."
|
||||||
|
|
@ -561,25 +417,11 @@ class Vmfloaty
|
||||||
|
|
||||||
host_os = args.first
|
host_os = args.first
|
||||||
|
|
||||||
if !no_token && !token
|
if args.length > 1
|
||||||
puts "No token found. Retrieving a token..."
|
STDERR.puts "Can't ssh to multiple hosts; Using #{host_os} only..."
|
||||||
if !user
|
|
||||||
STDERR.puts "You did not provide a user to authenticate to the pooler service with"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
pass = password "Enter your pooler service password please:", '*'
|
|
||||||
begin
|
|
||||||
token = Auth.get_token(verbose, url, user, pass)
|
|
||||||
rescue TokenError => e
|
|
||||||
STDERR.puts e
|
|
||||||
STDERR.puts 'Could not get token...requesting vm without a token anyway...'
|
|
||||||
else
|
|
||||||
puts "\nToken retrieved!"
|
|
||||||
puts token
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Ssh.ssh(verbose, host_os, token, url)
|
service.ssh(verbose, host_os, use_token)
|
||||||
exit 0
|
exit 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -596,7 +438,7 @@ class Vmfloaty
|
||||||
EOF
|
EOF
|
||||||
c.example 'Gets path to bash tab completion script', 'floaty completion --shell bash'
|
c.example 'Gets path to bash tab completion script', 'floaty completion --shell bash'
|
||||||
c.option '--shell STRING', String, 'Shell to request completion script for'
|
c.option '--shell STRING', String, 'Shell to request completion script for'
|
||||||
c.action do |args, options|
|
c.action do |_, options|
|
||||||
shell = (options.shell || 'bash').downcase.strip
|
shell = (options.shell || 'bash').downcase.strip
|
||||||
completion_file = File.expand_path(File.join('..', '..', 'extras', 'completions', "floaty.#{shell}"), __FILE__)
|
completion_file = File.expand_path(File.join('..', '..', 'extras', 'completions', "floaty.#{shell}"), __FILE__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,3 +15,9 @@ class MissingParamError < StandardError
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ModifyError < StandardError
|
||||||
|
def initialize(msg="Could not modify VM")
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
||||||
135
lib/vmfloaty/nonstandard_pooler.rb
Normal file
135
lib/vmfloaty/nonstandard_pooler.rb
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
require 'vmfloaty/errors'
|
||||||
|
require 'vmfloaty/http'
|
||||||
|
require 'faraday'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
class NonstandardPooler
|
||||||
|
def self.list(verbose, url, os_filter = nil)
|
||||||
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
|
response = conn.get 'status'
|
||||||
|
response_body = JSON.parse(response.body)
|
||||||
|
os_list = response_body.keys.sort
|
||||||
|
os_list.delete 'ok'
|
||||||
|
|
||||||
|
os_filter ? os_list.select { |i| i[/#{os_filter}/] } : os_list
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.list_active(verbose, url, token)
|
||||||
|
status = Auth.token_status(verbose, url, token)
|
||||||
|
status['reserved_hosts'] || []
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.retrieve(verbose, os_type, token, url)
|
||||||
|
conn = Http.get_conn(verbose, url)
|
||||||
|
conn.headers['X-AUTH-TOKEN'] = token if token
|
||||||
|
|
||||||
|
os_string = ''
|
||||||
|
os_type.each do |os, num|
|
||||||
|
num.times do |_i|
|
||||||
|
os_string << os + '+'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
os_string = os_string.chomp('+')
|
||||||
|
|
||||||
|
if os_string.empty?
|
||||||
|
raise MissingParamError, 'No operating systems provided to obtain.'
|
||||||
|
end
|
||||||
|
|
||||||
|
response = conn.post "host/#{os_string}"
|
||||||
|
|
||||||
|
res_body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
if res_body['ok']
|
||||||
|
res_body
|
||||||
|
elsif response.status == 401
|
||||||
|
raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
|
||||||
|
else
|
||||||
|
raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/host/#{os_string}. #{res_body}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.modify(verbose, url, hostname, token, modify_hash)
|
||||||
|
if token.nil?
|
||||||
|
raise TokenError, 'Token provided was nil; Request cannot be made to modify VM'
|
||||||
|
end
|
||||||
|
|
||||||
|
modify_hash.each do |key, value|
|
||||||
|
unless [:reason, :reserved_for_reason].include? key
|
||||||
|
raise ModifyError, "Configured service type does not support modification of #{key}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if modify_hash[:reason]
|
||||||
|
# "reason" is easier to type than "reserved_for_reason", but nspooler needs the latter
|
||||||
|
modify_hash[:reserved_for_reason] = modify_hash.delete :reason
|
||||||
|
end
|
||||||
|
|
||||||
|
conn = Http.get_conn(verbose, url)
|
||||||
|
conn.headers['X-AUTH-TOKEN'] = token
|
||||||
|
|
||||||
|
response = conn.put do |req|
|
||||||
|
req.url "host/#{hostname}"
|
||||||
|
req.body = modify_hash.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
response.body.empty? ? {} : JSON.parse(response.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.disk(verbose, url, hostname, token, disk)
|
||||||
|
raise ModifyError, 'Configured service type does not support modification of disk space'
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.snapshot(verbose, url, hostname, token)
|
||||||
|
raise ModifyError, 'Configured service type does not support snapshots'
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.revert(verbose, url, hostname, token, snapshot_sha)
|
||||||
|
raise ModifyError, 'Configured service type does not support snapshots'
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete(verbose, url, hosts, token)
|
||||||
|
if token.nil?
|
||||||
|
raise TokenError, 'Token provided was nil; Request cannot be made to delete VM'
|
||||||
|
end
|
||||||
|
|
||||||
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
|
conn.headers['X-AUTH-TOKEN'] = token if token
|
||||||
|
|
||||||
|
response_body = {}
|
||||||
|
|
||||||
|
unless hosts.is_a? Array
|
||||||
|
hosts = hosts.split(',')
|
||||||
|
end
|
||||||
|
hosts.each do |host|
|
||||||
|
response = conn.delete "host/#{host}"
|
||||||
|
res_body = JSON.parse(response.body)
|
||||||
|
response_body[host] = res_body
|
||||||
|
end
|
||||||
|
|
||||||
|
response_body
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.status(verbose, url)
|
||||||
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
|
response = conn.get '/status'
|
||||||
|
JSON.parse(response.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.summary(verbose, url)
|
||||||
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
|
response = conn.get '/summary'
|
||||||
|
JSON.parse(response.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.query(verbose, url, hostname)
|
||||||
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
|
response = conn.get "host/#{hostname}"
|
||||||
|
JSON.parse(response.body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -19,6 +19,15 @@ class Pooler
|
||||||
hosts
|
hosts
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.list_active(verbose, url, token)
|
||||||
|
status = Auth.token_status(verbose, url, token)
|
||||||
|
vms = []
|
||||||
|
if status[token] && status[token]['vms']
|
||||||
|
vms = status[token]['vms']['running']
|
||||||
|
end
|
||||||
|
vms
|
||||||
|
end
|
||||||
|
|
||||||
def self.retrieve(verbose, os_type, token, url)
|
def self.retrieve(verbose, os_type, token, url)
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# Developers can use `Utils.generate_os_hash` to
|
# Developers can use `Utils.generate_os_hash` to
|
||||||
|
|
@ -54,25 +63,28 @@ class Pooler
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.modify(verbose, url, hostname, token, lifetime, tags)
|
def self.modify(verbose, url, hostname, token, modify_hash)
|
||||||
if token.nil?
|
if token.nil?
|
||||||
raise TokenError, "Token provided was nil. Request cannot be made to modify vm"
|
raise TokenError, "Token provided was nil. Request cannot be made to modify vm"
|
||||||
end
|
end
|
||||||
|
|
||||||
modify_body = {}
|
modify_hash.keys.each do |key|
|
||||||
if lifetime
|
unless [:tags, :lifetime, :disk].include? key
|
||||||
modify_body['lifetime'] = lifetime
|
raise ModifyError, "Configured service type does not support modification of #{key}."
|
||||||
end
|
end
|
||||||
if tags
|
|
||||||
modify_body['tags'] = tags
|
|
||||||
end
|
end
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
conn.headers['X-AUTH-TOKEN'] = token
|
conn.headers['X-AUTH-TOKEN'] = token
|
||||||
|
|
||||||
|
if modify_hash['disk']
|
||||||
|
disk(verbose, url, hostname, token, modify_hash['disk'])
|
||||||
|
modify_hash.delete 'disk'
|
||||||
|
end
|
||||||
|
|
||||||
response = conn.put do |req|
|
response = conn.put do |req|
|
||||||
req.url "vm/#{hostname}"
|
req.url "vm/#{hostname}"
|
||||||
req.body = modify_body.to_json
|
req.body = modify_hash.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
res_body = JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
|
|
||||||
133
lib/vmfloaty/service.rb
Normal file
133
lib/vmfloaty/service.rb
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
require 'commander/user_interaction'
|
||||||
|
require 'commander/command'
|
||||||
|
require 'vmfloaty/utils'
|
||||||
|
require 'vmfloaty/ssh'
|
||||||
|
|
||||||
|
class Service
|
||||||
|
|
||||||
|
attr_reader :config
|
||||||
|
|
||||||
|
def initialize(options, config_hash = {})
|
||||||
|
options ||= Commander::Command::Options.new
|
||||||
|
@config = Utils.get_service_config config_hash, options
|
||||||
|
@service_object = Utils.get_service_object @config['type']
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(m, *args, &block)
|
||||||
|
if @service_object.respond_to? m
|
||||||
|
@service_object.send(m, *args, &block)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def url
|
||||||
|
@config['url']
|
||||||
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
@service_object.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def user
|
||||||
|
unless @config['user']
|
||||||
|
puts "Enter your pooler service username:"
|
||||||
|
@config['user'] = STDIN.gets.chomp
|
||||||
|
end
|
||||||
|
@config['user']
|
||||||
|
end
|
||||||
|
|
||||||
|
def token
|
||||||
|
unless @config['token']
|
||||||
|
puts "No token found. Retrieving a token..."
|
||||||
|
@config['token'] = get_new_token(nil)
|
||||||
|
end
|
||||||
|
@config['token']
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_new_token(verbose)
|
||||||
|
username = user
|
||||||
|
pass = Commander::UI::password "Enter your pooler service password:", '*'
|
||||||
|
Auth.get_token(verbose, url, username, pass)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_token(verbose, token_value = @config['token'])
|
||||||
|
username = user
|
||||||
|
pass = Commander::UI::password "Enter your pooler service password:", '*'
|
||||||
|
Auth.delete_token(verbose, url, username, pass, token_value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def token_status(verbose, token_value)
|
||||||
|
token_value ||= @config['token']
|
||||||
|
Auth.token_status(verbose, url, token_value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def list(verbose, os_filter = nil)
|
||||||
|
@service_object.list verbose, url, os_filter
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_active(verbose)
|
||||||
|
@service_object.list_active verbose, url, token
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieve(verbose, os_types, use_token = true)
|
||||||
|
puts 'Requesting a vm without a token...' unless use_token
|
||||||
|
token_value = use_token ? token : nil
|
||||||
|
@service_object.retrieve verbose, os_types, token_value, url
|
||||||
|
end
|
||||||
|
|
||||||
|
def ssh(verbose, host_os, use_token = true)
|
||||||
|
token_value = nil
|
||||||
|
if use_token
|
||||||
|
begin
|
||||||
|
token_value = token || get_new_token(verbose)
|
||||||
|
rescue TokenError => e
|
||||||
|
STDERR.puts e
|
||||||
|
STDERR.puts 'Could not get token... requesting vm without a token anyway...'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Ssh.ssh(verbose, host_os, token_value, url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_print_running(verbose, hostnames = [])
|
||||||
|
if hostnames.empty?
|
||||||
|
puts "You have no running VMs."
|
||||||
|
else
|
||||||
|
puts "Running VMs:"
|
||||||
|
@service_object.pretty_print_hosts(verbose, hostnames, url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def query(verbose, hostname)
|
||||||
|
@service_object.query verbose, url, hostname
|
||||||
|
end
|
||||||
|
|
||||||
|
def modify(verbose, hostname, modify_hash)
|
||||||
|
@service_object.modify verbose, url, hostname, token, modify_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(verbose, hosts)
|
||||||
|
@service_object.delete verbose, url, hosts, token
|
||||||
|
end
|
||||||
|
|
||||||
|
def status(verbose)
|
||||||
|
@service_object.status verbose, url
|
||||||
|
end
|
||||||
|
|
||||||
|
def summary(verbose)
|
||||||
|
@service_object.summary verbose, url
|
||||||
|
end
|
||||||
|
|
||||||
|
def snapshot(verbose, hostname)
|
||||||
|
@service_object.snapshot verbose, url, hostname, token
|
||||||
|
end
|
||||||
|
|
||||||
|
def revert(verbose, hostname, snapshot_sha)
|
||||||
|
@service_object.revert verbose, url, hostname, token, snapshot_sha
|
||||||
|
end
|
||||||
|
|
||||||
|
def disk(verbose, hostname, disk)
|
||||||
|
@service_object.disk(verbose, url, hostname, token, disk)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -1,27 +1,62 @@
|
||||||
|
|
||||||
require 'vmfloaty/pooler'
|
require 'vmfloaty/pooler'
|
||||||
|
require 'vmfloaty/nonstandard_pooler'
|
||||||
|
|
||||||
class Utils
|
class Utils
|
||||||
# TODO: Takes the json response body from an HTTP GET
|
# TODO: Takes the json response body from an HTTP GET
|
||||||
# request and "pretty prints" it
|
# request and "pretty prints" it
|
||||||
def self.format_hosts(hostname_hash)
|
def self.format_hosts(response_body)
|
||||||
host_hash = {}
|
# vmpooler response body example when `floaty get` arguments are `ubuntu-1610-x86_64=2 centos-7-x86_64`:
|
||||||
|
# {
|
||||||
|
# "ok": true,
|
||||||
|
# "domain": "delivery.mycompany.net",
|
||||||
|
# "ubuntu-1610-x86_64": {
|
||||||
|
# "hostname": ["gdoy8q3nckuob0i", "ctnktsd0u11p9tm"]
|
||||||
|
# },
|
||||||
|
# "centos-7-x86_64": {
|
||||||
|
# "hostname": "dlgietfmgeegry2"
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
hostname_hash.delete("ok")
|
# nonstandard pooler response body example when `floaty get` arguments are `solaris-11-sparc=2 ubuntu-16.04-power8`:
|
||||||
domain = hostname_hash["domain"]
|
# {
|
||||||
hostname_hash.each do |type, hosts|
|
# "ok": true,
|
||||||
if type != "domain"
|
# "solaris-10-sparc": {
|
||||||
if hosts["hostname"].kind_of?(Array)
|
# "hostname": ["sol10-10.delivery.mycompany.net", "sol10-11.delivery.mycompany.net"]
|
||||||
hosts["hostname"].map!{|host| host + "." + domain }
|
# },
|
||||||
|
# "ubuntu-16.04-power8": {
|
||||||
|
# "hostname": "power8-ubuntu1604-6.delivery.mycompany.net"
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
unless response_body.delete('ok')
|
||||||
|
raise ArgumentError, "Bad GET response passed to format_hosts: #{response_body.to_json}"
|
||||||
|
end
|
||||||
|
|
||||||
|
hostnames = []
|
||||||
|
|
||||||
|
# vmpooler reports the domain separately from the hostname
|
||||||
|
domain = response_body.delete('domain')
|
||||||
|
|
||||||
|
if domain
|
||||||
|
# vmpooler output
|
||||||
|
response_body.each do |os, hosts|
|
||||||
|
if hosts['hostname'].kind_of?(Array)
|
||||||
|
hosts['hostname'].map!{ |host| hostnames << host + "." + domain + " (#{os})"}
|
||||||
else
|
else
|
||||||
hosts["hostname"] = hosts["hostname"] + "." + domain
|
hostnames << hosts["hostname"] + ".#{domain} (#{os})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response_body.each do |os, hosts|
|
||||||
|
if hosts['hostname'].kind_of?(Array)
|
||||||
|
hosts['hostname'].map!{ |host| hostnames << host + " (#{os})" }
|
||||||
|
else
|
||||||
|
hostnames << hosts['hostname'] + " (#{os})"
|
||||||
end
|
end
|
||||||
|
|
||||||
host_hash[type] = hosts["hostname"]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
host_hash.to_json
|
hostnames.map { |hostname| puts "- #{hostname}" }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.generate_os_hash(os_args)
|
def self.generate_os_hash(os_args)
|
||||||
|
|
@ -46,55 +81,48 @@ class Utils
|
||||||
os_types
|
os_types
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_vm_info(hosts, verbose, url)
|
def self.pretty_print_hosts(verbose, service, hostnames = [])
|
||||||
vms = {}
|
hostnames = [hostnames] unless hostnames.is_a? Array
|
||||||
hosts.each do |host|
|
hostnames.each do |hostname|
|
||||||
vm_info = Pooler.query(verbose, url, host)
|
begin
|
||||||
if vm_info['ok']
|
response = service.query(verbose, hostname)
|
||||||
vms[host] = {}
|
host_data = response[hostname]
|
||||||
vms[host]['domain'] = vm_info[host]['domain']
|
|
||||||
vms[host]['template'] = vm_info[host]['template']
|
|
||||||
vms[host]['lifetime'] = vm_info[host]['lifetime']
|
|
||||||
vms[host]['running'] = vm_info[host]['running']
|
|
||||||
vms[host]['tags'] = vm_info[host]['tags']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
vms
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.prettyprint_hosts(hosts, verbose, url)
|
case service.type
|
||||||
puts "Running VMs:"
|
when 'Pooler'
|
||||||
vm_info = get_vm_info(hosts, verbose, url)
|
tag_pairs = []
|
||||||
vm_info.each do |vm,info|
|
unless host_data['tags'].nil?
|
||||||
domain = info['domain']
|
tag_pairs = host_data['tags'].map {|key, value| "#{key}: #{value}"}
|
||||||
template = info['template']
|
end
|
||||||
lifetime = info['lifetime']
|
duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
|
||||||
running = info['running']
|
metadata = [host_data['template'], duration, *tag_pairs]
|
||||||
tags = info['tags'] || {}
|
puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(", ")})"
|
||||||
|
when 'NonstandardPooler'
|
||||||
tag_pairs = tags.map {|key,value| "#{key}: #{value}" }
|
line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
|
||||||
duration = "#{running}/#{lifetime} hours"
|
line += ", #{host_data['hours_left_on_reservation']}h remaining"
|
||||||
metadata = [template, duration, *tag_pairs]
|
unless host_data['reserved_for_reason'].empty?
|
||||||
|
line += ", reason: #{host_data['reserved_for_reason']}"
|
||||||
puts "- #{vm}.#{domain} (#{metadata.join(", ")})"
|
end
|
||||||
|
line += ')'
|
||||||
|
puts line
|
||||||
|
else
|
||||||
|
raise "Invalid service type #{service.type}"
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
STDERR.puts("Something went wrong while trying to gather information on #{hostname}:")
|
||||||
|
STDERR.puts(e)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_all_token_vms(verbose, url, token)
|
def self.pretty_print_status(verbose, service)
|
||||||
# get vms with token
|
status_response = service.status(verbose)
|
||||||
status = Auth.token_status(verbose, url, token)
|
|
||||||
|
|
||||||
vms = status[token]['vms']
|
case service.type
|
||||||
if vms.nil?
|
when 'Pooler'
|
||||||
raise "You have no running vms"
|
message = status_response['status']['message']
|
||||||
end
|
pools = status_response['pools']
|
||||||
|
pools.select! {|_, pool| pool['ready'] < pool['max']} unless verbose
|
||||||
running_vms = vms['running']
|
|
||||||
running_vms
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.prettyprint_status(status, message, pools, verbose)
|
|
||||||
pools.select! {|name,pool| pool['ready'] < pool['max']} if ! verbose
|
|
||||||
|
|
||||||
width = pools.keys.map(&:length).max
|
width = pools.keys.map(&:length).max
|
||||||
pools.each do |name, pool|
|
pools.each do |name, pool|
|
||||||
|
|
@ -109,9 +137,28 @@ class Utils
|
||||||
puts "#{name.ljust(width)} #{e.red}"
|
puts "#{name.ljust(width)} #{e.red}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
puts message.colorize(status_response['status']['ok'] ? :default : :red)
|
||||||
|
when 'NonstandardPooler'
|
||||||
|
pools = status_response
|
||||||
|
pools.delete 'ok'
|
||||||
|
pools.select! {|_, pool| pool['available_hosts'] < pool['total_hosts']} unless verbose
|
||||||
|
|
||||||
puts
|
width = pools.keys.map(&:length).max
|
||||||
puts message.colorize(status['status']['ok'] ? :default : :red)
|
pools.each do |name, pool|
|
||||||
|
begin
|
||||||
|
max = pool['total_hosts']
|
||||||
|
ready = pool['available_hosts']
|
||||||
|
pending = pool['pending'] || 0 # not available for nspooler
|
||||||
|
missing = max - ready - pending
|
||||||
|
char = 'o'
|
||||||
|
puts "#{name.ljust(width)} #{(char*ready).green}#{(char*pending).yellow}#{(char*missing).red}"
|
||||||
|
rescue => e
|
||||||
|
puts "#{name.ljust(width)} #{e.red}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Invalid service type #{service.type}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Adapted from ActiveSupport
|
# Adapted from ActiveSupport
|
||||||
|
|
@ -122,34 +169,46 @@ class Utils
|
||||||
str.gsub(/^[ \t]{#{min_indent_size}}/, '')
|
str.gsub(/^[ \t]{#{min_indent_size}}/, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_service_from_config(config, service_name = '')
|
def self.get_service_object(type = '')
|
||||||
# The top-level url, user, and token values are treated as defaults
|
nspooler_strings = ['ns', 'nspooler', 'nonstandard', 'nonstandard_pooler']
|
||||||
service = {
|
if nspooler_strings.include? type.downcase
|
||||||
|
NonstandardPooler
|
||||||
|
else
|
||||||
|
Pooler
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_service_config(config, options)
|
||||||
|
# The top-level url, user, and token values in the config file are treated as defaults
|
||||||
|
service_config = {
|
||||||
'url' => config['url'],
|
'url' => config['url'],
|
||||||
'user' => config['user'],
|
'user' => config['user'],
|
||||||
'token' => config['token']
|
'token' => config['token'],
|
||||||
|
'type' => config['type'] || 'vmpooler'
|
||||||
}
|
}
|
||||||
|
|
||||||
# If no named services have been configured, use the default values
|
if config['services']
|
||||||
return service unless config['services'] and config['services'].length
|
if options.service.nil?
|
||||||
|
# If the user did not specify a service name at the command line, but configured services do exist,
|
||||||
if not service_name.empty?
|
# use the first configured service in the list by default.
|
||||||
if config['services'][service_name]
|
_, values = config['services'].first
|
||||||
# If the user specified a configured service name, use that service
|
service_config.merge! values
|
||||||
# If values are missing, use the top-level defaults
|
|
||||||
service.merge!(config['services'][service_name]) { |key, default, value| value }
|
|
||||||
else
|
else
|
||||||
STDERR.puts "WARNING: Could not find a configured service matching the name #{service_name} at #{Dir.home}/.vmfloaty.yml"
|
# If the user provided a service name at the command line, use that service if posible, or fail
|
||||||
return {}
|
if config['services'][options.service]
|
||||||
end
|
# If the service is configured but some values are missing, use the top-level defaults to fill them in
|
||||||
|
service_config.merge! config['services'][options.service]
|
||||||
else
|
else
|
||||||
# Otherwise, use the first service configured under the 'services' key
|
raise ArgumentError, "Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml"
|
||||||
# If values are missing, use the top-level defaults
|
end
|
||||||
name, config_hash = config['services'].first
|
end
|
||||||
service.merge!(config_hash) { |key, default, value| value }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
service
|
# Prioritize an explicitly specified url, user, or token if the user provided one
|
||||||
end
|
service_config['url'] = options.url unless options.url.nil?
|
||||||
|
service_config['token'] = options.token unless options.token.nil?
|
||||||
|
service_config['user'] = options.user unless options.user.nil?
|
||||||
|
|
||||||
|
service_config
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
require 'vmfloaty'
|
require 'vmfloaty'
|
||||||
require 'webmock/rspec'
|
require 'webmock/rspec'
|
||||||
|
|
||||||
|
# Mock Commander Options object to allow pre-population with values
|
||||||
|
class MockOptions < Commander::Command::Options
|
||||||
|
def initialize(values = {})
|
||||||
|
@table = values
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
config.color = true
|
config.color = true
|
||||||
config.tty = true
|
config.tty = true
|
||||||
|
|
|
||||||
325
spec/vmfloaty/nonstandard_pooler_spec.rb
Normal file
325
spec/vmfloaty/nonstandard_pooler_spec.rb
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'vmfloaty/utils'
|
||||||
|
require 'vmfloaty/errors'
|
||||||
|
require 'vmfloaty/nonstandard_pooler'
|
||||||
|
|
||||||
|
describe NonstandardPooler do
|
||||||
|
before :each do
|
||||||
|
@nspooler_url = 'https://nspooler.example.com'
|
||||||
|
@post_request_headers = {
|
||||||
|
'Accept' => '*/*',
|
||||||
|
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
||||||
|
'User-Agent' => 'Faraday v0.9.2',
|
||||||
|
'X-Auth-Token' => 'token-value'
|
||||||
|
}
|
||||||
|
@get_request_headers = {
|
||||||
|
'Accept' => '*/*',
|
||||||
|
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
||||||
|
'User-Agent' => 'Faraday v0.9.2',
|
||||||
|
'X-Auth-Token' => 'token-value'
|
||||||
|
}
|
||||||
|
@get_request_headers_notoken = @get_request_headers.tap do |headers|
|
||||||
|
headers.delete('X-Auth-Token')
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#list' do
|
||||||
|
before :each do
|
||||||
|
@status_response_body = <<-BODY
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"solaris-10-sparc": {
|
||||||
|
"total_hosts": 11,
|
||||||
|
"available_hosts": 11
|
||||||
|
},
|
||||||
|
"ubuntu-16.04-power8": {
|
||||||
|
"total_hosts": 10,
|
||||||
|
"available_hosts": 10
|
||||||
|
},
|
||||||
|
"aix-7.2-power": {
|
||||||
|
"total_hosts": 5,
|
||||||
|
"available_hosts": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BODY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an array with operating systems from the pooler' do
|
||||||
|
stub_request(:get, "#{@nspooler_url}/status")
|
||||||
|
.to_return(status: 200, body: @status_response_body, headers: {})
|
||||||
|
|
||||||
|
list = NonstandardPooler.list(false, @nspooler_url, nil)
|
||||||
|
expect(list).to be_an_instance_of Array
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters operating systems based on the filter param' do
|
||||||
|
stub_request(:get, "#{@nspooler_url}/status")
|
||||||
|
.to_return(status: 200, body: @status_response_body, headers: {})
|
||||||
|
|
||||||
|
list = NonstandardPooler.list(false, @nspooler_url, 'aix')
|
||||||
|
expect(list).to be_an_instance_of Array
|
||||||
|
expect(list.size).to equal 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns nothing if the filter does not match' do
|
||||||
|
stub_request(:get, "#{@nspooler_url}/status")
|
||||||
|
.to_return(status: 199, body: @status_response_body, headers: {})
|
||||||
|
|
||||||
|
list = NonstandardPooler.list(false, @nspooler_url, 'windows')
|
||||||
|
expect(list).to be_an_instance_of Array
|
||||||
|
expect(list.size).to equal 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#list_active' do
|
||||||
|
before :each do
|
||||||
|
@token_status_body_active = <<-BODY
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"user": "first.last",
|
||||||
|
"created": "2017-09-18 01:25:41 +0000",
|
||||||
|
"last_accessed": "2017-09-21 19:46:25 +0000",
|
||||||
|
"reserved_hosts": ["sol10-9", "sol10-11"]
|
||||||
|
}
|
||||||
|
BODY
|
||||||
|
@token_status_body_empty = <<-BODY
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"user": "first.last",
|
||||||
|
"created": "2017-09-18 01:25:41 +0000",
|
||||||
|
"last_accessed": "2017-09-21 19:46:25 +0000",
|
||||||
|
"reserved_hosts": []
|
||||||
|
}
|
||||||
|
BODY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prints an output of fqdn, template, and duration' do
|
||||||
|
allow(Auth).to receive(:token_status)
|
||||||
|
.with(false, @nspooler_url, 'token-value')
|
||||||
|
.and_return(JSON.parse(@token_status_body_active))
|
||||||
|
|
||||||
|
list = NonstandardPooler.list_active(false, @nspooler_url, 'token-value')
|
||||||
|
expect(list).to eql ['sol10-9', 'sol10-11']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#retrieve' do
|
||||||
|
before :each do
|
||||||
|
@retrieve_response_body_single = <<-BODY
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"solaris-11-sparc": {
|
||||||
|
"hostname": "sol11-4.delivery.puppetlabs.net"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BODY
|
||||||
|
@retrieve_response_body_many = <<-BODY
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"solaris-10-sparc": {
|
||||||
|
"hostname": [
|
||||||
|
"sol10-9.delivery.puppetlabs.net",
|
||||||
|
"sol10-10.delivery.puppetlabs.net"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"aix-7.1-power": {
|
||||||
|
"hostname": "pe-aix-71-ci-acceptance.delivery.puppetlabs.net"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BODY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an AuthError if the token is invalid' do
|
||||||
|
stub_request(:post, "#{@nspooler_url}/host/solaris-11-sparc")
|
||||||
|
.with(headers: @post_request_headers)
|
||||||
|
.to_return(status: 401, body: '{"ok":false,"reason": "token: token-value does not exist"}', headers: {})
|
||||||
|
|
||||||
|
vm_hash = { 'solaris-11-sparc' => 1 }
|
||||||
|
expect { NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url) }.to raise_error(AuthError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'retrieves a single vm with a token' do
|
||||||
|
stub_request(:post, "#{@nspooler_url}/host/solaris-11-sparc")
|
||||||
|
.with(headers: @post_request_headers)
|
||||||
|
.to_return(status: 200, body: @retrieve_response_body_single, headers: {})
|
||||||
|
|
||||||
|
vm_hash = { 'solaris-11-sparc' => 1 }
|
||||||
|
vm_req = NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url)
|
||||||
|
expect(vm_req).to be_an_instance_of Hash
|
||||||
|
expect(vm_req['ok']).to equal true
|
||||||
|
expect(vm_req['solaris-11-sparc']['hostname']).to eq 'sol11-4.delivery.puppetlabs.net'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'retrieves a multiple vms with a token' do
|
||||||
|
stub_request(:post,"#{@nspooler_url}/host/aix-7.1-power+solaris-10-sparc+solaris-10-sparc")
|
||||||
|
.with(headers: @post_request_headers)
|
||||||
|
.to_return(status: 200, body: @retrieve_response_body_many, headers: {})
|
||||||
|
|
||||||
|
vm_hash = { 'aix-7.1-power' => 1, 'solaris-10-sparc' => 2 }
|
||||||
|
vm_req = NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url)
|
||||||
|
expect(vm_req).to be_an_instance_of Hash
|
||||||
|
expect(vm_req['ok']).to equal true
|
||||||
|
expect(vm_req['solaris-10-sparc']['hostname']).to be_an_instance_of Array
|
||||||
|
expect(vm_req['solaris-10-sparc']['hostname']).to eq ['sol10-9.delivery.puppetlabs.net', 'sol10-10.delivery.puppetlabs.net']
|
||||||
|
expect(vm_req['aix-7.1-power']['hostname']).to eq 'pe-aix-71-ci-acceptance.delivery.puppetlabs.net'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#modify' do
|
||||||
|
before :each do
|
||||||
|
@modify_response_body_success = '{"ok":true}'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an error if the user tries to modify an unsupported attribute' do
|
||||||
|
stub_request(:put, "https://nspooler.example.com/host/myfakehost").
|
||||||
|
with(body: {"{}"=>true},
|
||||||
|
headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'token-value'}).
|
||||||
|
to_return(status: 200, body: "", headers: {})
|
||||||
|
details = { lifetime: 12 }
|
||||||
|
expect { NonstandardPooler.modify(false, @nspooler_url, 'myfakehost', 'token-value', details) }
|
||||||
|
.to raise_error(ModifyError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'modifies the reason of a vm' do
|
||||||
|
modify_request_body = { '{"reserved_for_reason":"testing"}' => true }
|
||||||
|
stub_request(:put, "#{@nspooler_url}/host/myfakehost")
|
||||||
|
.with(body: modify_request_body,
|
||||||
|
headers: @post_request_headers)
|
||||||
|
.to_return(status: 200, body: '{"ok": true}', headers: {})
|
||||||
|
|
||||||
|
modify_hash = { reason: "testing" }
|
||||||
|
modify_req = NonstandardPooler.modify(false, @nspooler_url, 'myfakehost', 'token-value', modify_hash)
|
||||||
|
expect(modify_req['ok']).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#status' do
|
||||||
|
before :each do
|
||||||
|
@status_response_body = '{"capacity":{"current":716,"total":717,"percent": 99.9},"status":{"ok":true,"message":"Battle station fully armed and operational."}}'
|
||||||
|
# TODO: make this report stuff like 'broken'
|
||||||
|
@status_response_body = <<-BODY
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"solaris-10-sparc": {
|
||||||
|
"total_hosts": 11,
|
||||||
|
"available_hosts": 10
|
||||||
|
},
|
||||||
|
"ubuntu-16.04-power8": {
|
||||||
|
"total_hosts": 10,
|
||||||
|
"available_hosts": 10
|
||||||
|
},
|
||||||
|
"aix-7.2-power": {
|
||||||
|
"total_hosts": 5,
|
||||||
|
"available_hosts": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BODY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prints the status' do
|
||||||
|
stub_request(:get, "#{@nspooler_url}/status")
|
||||||
|
.with(headers: @get_request_headers)
|
||||||
|
.to_return(status: 200, body: @status_response_body, headers: {})
|
||||||
|
|
||||||
|
status = NonstandardPooler.status(false, @nspooler_url)
|
||||||
|
expect(status).to be_an_instance_of Hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#summary' do
|
||||||
|
before :each do
|
||||||
|
@status_response_body = <<-BODY
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"total": 57,
|
||||||
|
"available": 39,
|
||||||
|
"in_use": 16,
|
||||||
|
"resetting": 2,
|
||||||
|
"broken": 0
|
||||||
|
}
|
||||||
|
BODY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prints the summary' do
|
||||||
|
stub_request(:get, "#{@nspooler_url}/summary")
|
||||||
|
.with(headers: @get_request_headers)
|
||||||
|
.to_return(status: 200, body: @status_response_body, headers: {})
|
||||||
|
|
||||||
|
summary = NonstandardPooler.summary(false, @nspooler_url)
|
||||||
|
expect(summary).to be_an_instance_of Hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#query' do
|
||||||
|
before :each do
|
||||||
|
@query_response_body = <<-BODY
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"sol10-11": {
|
||||||
|
"fqdn": "sol10-11.delivery.puppetlabs.net",
|
||||||
|
"os_triple": "solaris-10-sparc",
|
||||||
|
"reserved_by_user": "first.last",
|
||||||
|
"reserved_for_reason": "testing",
|
||||||
|
"hours_left_on_reservation": 29.12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BODY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'makes a query about a vm' do
|
||||||
|
stub_request(:get, "#{@nspooler_url}/host/sol10-11")
|
||||||
|
.with(headers: @get_request_headers_notoken)
|
||||||
|
.to_return(status: 200, body: @query_response_body, headers: {})
|
||||||
|
|
||||||
|
query_req = NonstandardPooler.query(false, @nspooler_url, 'sol10-11')
|
||||||
|
expect(query_req).to be_an_instance_of Hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#delete' do
|
||||||
|
before :each do
|
||||||
|
@delete_response_success = '{"ok": true}'
|
||||||
|
@delete_response_failure = '{"ok": false, "failure": "ERROR: fakehost does not exist"}'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deletes a single existing vm' do
|
||||||
|
stub_request(:delete, "#{@nspooler_url}/host/sol11-7")
|
||||||
|
.with(headers: @post_request_headers)
|
||||||
|
.to_return(status: 200, body: @delete_response_success, headers: {})
|
||||||
|
|
||||||
|
request = NonstandardPooler.delete(false, @nspooler_url, 'sol11-7', 'token-value')
|
||||||
|
expect(request['sol11-7']['ok']).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not delete a nonexistant vm' do
|
||||||
|
stub_request(:delete, "#{@nspooler_url}/host/fakehost")
|
||||||
|
.with(headers: @post_request_headers)
|
||||||
|
.to_return(status: 401, body: @delete_response_failure, headers: {})
|
||||||
|
|
||||||
|
request = NonstandardPooler.delete(false, @nspooler_url, 'fakehost', 'token-value')
|
||||||
|
expect(request['fakehost']['ok']).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#snapshot' do
|
||||||
|
it 'logs an error explaining that snapshots are not supported' do
|
||||||
|
expect { NonstandardPooler.snapshot(false, @nspooler_url, 'myfakehost', 'token-value') }
|
||||||
|
.to raise_error(ModifyError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#revert' do
|
||||||
|
it 'logs an error explaining that snapshots are not supported' do
|
||||||
|
expect { NonstandardPooler.revert(false, @nspooler_url, 'myfakehost', 'token-value', 'snapshot-sha') }
|
||||||
|
.to raise_error(ModifyError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#disk' do
|
||||||
|
it 'logs an error explaining that disk modification is not supported' do
|
||||||
|
expect { NonstandardPooler.disk(false, @nspooler_url, 'myfakehost', 'token-value', 'diskname') }
|
||||||
|
.to raise_error(ModifyError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -91,16 +91,17 @@ describe Pooler do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises a TokenError if token provided is nil" do
|
it "raises a TokenError if token provided is nil" do
|
||||||
expect{ Pooler.modify(false, @vmpooler_url, 'myfakehost', nil, 12, nil) }.to raise_error(TokenError)
|
expect{ Pooler.modify(false, @vmpooler_url, 'myfakehost', nil, {}) }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "modifies the TTL of a vm" do
|
it "modifies the TTL of a vm" do
|
||||||
|
modify_hash = { :lifetime => 12 }
|
||||||
stub_request(:put, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6").
|
stub_request(:put, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6").
|
||||||
with(:body => {"{\"lifetime\":12}"=>true},
|
with(:body => {'{"lifetime":12}'=>true},
|
||||||
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
||||||
to_return(:status => 200, :body => @modify_response_body_success, :headers => {})
|
to_return(:status => 200, :body => @modify_response_body_success, :headers => {})
|
||||||
|
|
||||||
modify_req = Pooler.modify(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', 12, nil)
|
modify_req = Pooler.modify(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', modify_hash)
|
||||||
expect(modify_req["ok"]).to be true
|
expect(modify_req["ok"]).to be true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
79
spec/vmfloaty/service_spec.rb
Normal file
79
spec/vmfloaty/service_spec.rb
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
require_relative '../../lib/vmfloaty/service'
|
||||||
|
|
||||||
|
describe Service do
|
||||||
|
|
||||||
|
describe '#initialize' do
|
||||||
|
it 'store configuration options' do
|
||||||
|
options = MockOptions.new({})
|
||||||
|
config = {'url' => 'http://example.url'}
|
||||||
|
service = Service.new(options, config)
|
||||||
|
expect(service.config).to include config
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#get_new_token' do
|
||||||
|
it 'prompts the user for their password and retrieves a token' do
|
||||||
|
config = { 'user' => 'first.last', 'url' => 'http://default.url' }
|
||||||
|
service = Service.new(MockOptions.new, config)
|
||||||
|
allow(STDOUT).to receive(:puts).with('Enter your pooler service password:')
|
||||||
|
allow(Commander::UI).to(receive(:password)
|
||||||
|
.with('Enter your pooler service password:', '*')
|
||||||
|
.and_return('hunter2'))
|
||||||
|
allow(Auth).to(receive(:get_token)
|
||||||
|
.with(nil, config['url'], config['user'], 'hunter2')
|
||||||
|
.and_return('token-value'))
|
||||||
|
expect(service.get_new_token(nil)).to eql 'token-value'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prompts the user for their username and password if the username is unknown' do
|
||||||
|
config = { 'url' => 'http://default.url' }
|
||||||
|
service = Service.new(MockOptions.new({}), config)
|
||||||
|
allow(STDOUT).to receive(:puts).with 'Enter your pooler service username:'
|
||||||
|
allow(STDOUT).to receive(:puts).with "\n"
|
||||||
|
allow(STDIN).to receive(:gets).and_return('first.last')
|
||||||
|
allow(Commander::UI).to(receive(:password)
|
||||||
|
.with('Enter your pooler service password:', '*')
|
||||||
|
.and_return('hunter2'))
|
||||||
|
allow(Auth).to(receive(:get_token)
|
||||||
|
.with(nil, config['url'], 'first.last', 'hunter2')
|
||||||
|
.and_return('token-value'))
|
||||||
|
expect(service.get_new_token(nil)).to eql 'token-value'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#delete_token' do
|
||||||
|
it 'deletes a token' do
|
||||||
|
service = Service.new(MockOptions.new,{'user' => 'first.last', 'url' => 'http://default.url'})
|
||||||
|
allow(Commander::UI).to(receive(:password)
|
||||||
|
.with('Enter your pooler service password:', '*')
|
||||||
|
.and_return('hunter2'))
|
||||||
|
allow(Auth).to(receive(:delete_token)
|
||||||
|
.with(nil, 'http://default.url', 'first.last', 'hunter2', 'token-value')
|
||||||
|
.and_return('ok' => true))
|
||||||
|
expect(service.delete_token(nil, 'token-value')).to eql({'ok' => true})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#token_status' do
|
||||||
|
it 'reports the status of a token' do
|
||||||
|
config = {
|
||||||
|
'user' => 'first.last',
|
||||||
|
'url' => 'http://default.url'
|
||||||
|
}
|
||||||
|
options = MockOptions.new('token' => 'token-value')
|
||||||
|
service = Service.new(options, config)
|
||||||
|
status = {
|
||||||
|
'ok' => true,
|
||||||
|
'user' => config['user'],
|
||||||
|
'created' => '2017-09-22 02:04:18 +0000',
|
||||||
|
'last_accessed' => '2017-09-22 02:04:28 +0000',
|
||||||
|
'reserved_hosts' => []
|
||||||
|
}
|
||||||
|
allow(Auth).to(receive(:token_status)
|
||||||
|
.with(nil, config['url'], 'token-value')
|
||||||
|
.and_return(status))
|
||||||
|
expect(service.token_status(nil, 'token-value')).to eql(status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -1,22 +1,63 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'json'
|
require 'json'
|
||||||
|
require 'commander/command'
|
||||||
require_relative '../../lib/vmfloaty/utils'
|
require_relative '../../lib/vmfloaty/utils'
|
||||||
|
|
||||||
describe Utils do
|
describe Utils do
|
||||||
|
|
||||||
describe "#get_hosts" do
|
describe "#format_hosts" do
|
||||||
before :each do
|
before :each do
|
||||||
@hostname_hash = "{\"ok\":true,\"debian-7-i386\":{\"hostname\":[\"sc0o4xqtodlul5w\",\"4m4dkhqiufnjmxy\"]},\"debian-7-x86_64\":{\"hostname\":\"zb91y9qbrbf6d3q\"},\"domain\":\"company.com\"}"
|
@vmpooler_response_body ='{
|
||||||
@format_hash = "{\"debian-7-i386\":[\"sc0o4xqtodlul5w.company.com\",\"4m4dkhqiufnjmxy.company.com\"],\"debian-7-x86_64\":\"zb91y9qbrbf6d3q.company.com\"}"
|
"ok": true,
|
||||||
|
"domain": "delivery.mycompany.net",
|
||||||
|
"ubuntu-1610-x86_64": {
|
||||||
|
"hostname": ["gdoy8q3nckuob0i", "ctnktsd0u11p9tm"]
|
||||||
|
},
|
||||||
|
"centos-7-x86_64": {
|
||||||
|
"hostname": "dlgietfmgeegry2"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
@nonstandard_response_body = '{
|
||||||
|
"ok": true,
|
||||||
|
"solaris-10-sparc": {
|
||||||
|
"hostname": ["sol10-10.delivery.mycompany.net", "sol10-11.delivery.mycompany.net"]
|
||||||
|
},
|
||||||
|
"ubuntu-16.04-power8": {
|
||||||
|
"hostname": "power8-ubuntu16.04-6.delivery.mycompany.net"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
@vmpooler_output = <<-OUT
|
||||||
|
- gdoy8q3nckuob0i.delivery.mycompany.net (ubuntu-1610-x86_64)
|
||||||
|
- ctnktsd0u11p9tm.delivery.mycompany.net (ubuntu-1610-x86_64)
|
||||||
|
- dlgietfmgeegry2.delivery.mycompany.net (centos-7-x86_64)
|
||||||
|
OUT
|
||||||
|
@nonstandard_output = <<-OUT
|
||||||
|
- sol10-10.delivery.mycompany.net (solaris-10-sparc)
|
||||||
|
- sol10-11.delivery.mycompany.net (solaris-10-sparc)
|
||||||
|
- power8-ubuntu16.04-6.delivery.mycompany.net (ubuntu-16.04-power8)
|
||||||
|
OUT
|
||||||
end
|
end
|
||||||
|
|
||||||
it "formats a hostname hash into os, hostnames, and domain name" do
|
it "formats a hostname hash from vmpooler into a list that includes the os" do
|
||||||
|
expect { Utils.format_hosts(JSON.parse(@vmpooler_response_body)) }.to output( @vmpooler_output).to_stdout_from_any_process
|
||||||
|
end
|
||||||
|
|
||||||
expect(Utils.format_hosts(JSON.parse(@hostname_hash))).to eq @format_hash
|
it "formats a hostname hash from the nonstandard pooler into a list that includes the os" do
|
||||||
|
expect { Utils.format_hosts(JSON.parse(@nonstandard_response_body)) }.to output(@nonstandard_output).to_stdout_from_any_process
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#get_service_from_config" do
|
describe "#get_service_object" do
|
||||||
|
it "assumes vmpooler by default" do
|
||||||
|
expect(Utils.get_service_object).to be Pooler
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses nspooler when told explicitly" do
|
||||||
|
expect(Utils.get_service_object "nspooler").to be NonstandardPooler
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#get_service_config" do
|
||||||
before :each do
|
before :each do
|
||||||
@default_config = {
|
@default_config = {
|
||||||
"url" => "http://default.url",
|
"url" => "http://default.url",
|
||||||
|
|
@ -41,29 +82,34 @@ describe Utils do
|
||||||
|
|
||||||
it "returns the first service configured under 'services' as the default if available" do
|
it "returns the first service configured under 'services' as the default if available" do
|
||||||
config = @default_config.merge @services_config
|
config = @default_config.merge @services_config
|
||||||
expect(Utils.get_service_from_config(config)).to include @services_config['services']['vm']
|
options = MockOptions.new({})
|
||||||
end
|
expect(Utils.get_service_config(config, options)).to include @services_config['services']['vm']
|
||||||
|
|
||||||
it "uses top-level service config values as defaults when service values are missing" do
|
|
||||||
config = {"services" => { "vm" => {}}}
|
|
||||||
config.merge! @default_config
|
|
||||||
expect(Utils.get_service_from_config(config, 'vm')).to include @default_config
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "allows selection by configured service key" do
|
it "allows selection by configured service key" do
|
||||||
config = @default_config.merge @services_config
|
config = @default_config.merge @services_config
|
||||||
expect(Utils.get_service_from_config(config, 'ns')).to include @services_config['services']['ns']
|
options = MockOptions.new({:service => "ns"})
|
||||||
|
expect(Utils.get_service_config(config, options)).to include @services_config['services']['ns']
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fills in missing values in configured services with the defaults" do
|
it "uses top-level service config values as defaults when configured service values are missing" do
|
||||||
config = @default_config.merge @services_config
|
config = @default_config.merge @services_config
|
||||||
config["services"]['vm'].delete 'url'
|
config["services"]['vm'].delete 'url'
|
||||||
expect(Utils.get_service_from_config(config, 'vm')['url']).to eq 'http://default.url'
|
options = MockOptions.new({:service => "vm"})
|
||||||
|
expect(Utils.get_service_config(config, options)['url']).to eq 'http://default.url'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns an empty hash if passed a service name that hasn't been configured" do
|
it "raises an error if passed a service name that hasn't been configured" do
|
||||||
config = @default_config.merge @services_config
|
config = @default_config.merge @services_config
|
||||||
expect(Utils.get_service_from_config(config, 'nil')).to eq({})
|
options = MockOptions.new({:service => "none"})
|
||||||
|
expect { Utils.get_service_config(config, options) }.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
|
||||||
|
it "prioritizes values passed as command line options over configuration options" do
|
||||||
|
config = @default_config
|
||||||
|
options = MockOptions.new({:url => "http://alternate.url", :token => "alternate-token"})
|
||||||
|
expected = config.merge({"url" => "http://alternate.url", "token" => "alternate-token"})
|
||||||
|
expect(Utils.get_service_config(config, options)).to include expected
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -83,60 +129,97 @@ describe Utils do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#prettyprint_hosts' do
|
describe '#pretty_print_hosts' do
|
||||||
let(:host_without_tags) { 'mcpy42eqjxli9g2' }
|
|
||||||
let(:host_with_tags) { 'aiydvzpg23r415q' }
|
|
||||||
let(:url) { 'http://pooler.example.com' }
|
let(:url) { 'http://pooler.example.com' }
|
||||||
|
|
||||||
let(:host_info_with_tags) do
|
it 'prints a vmpooler output with host fqdn, template and duration info' do
|
||||||
{
|
hostname = 'mcpy42eqjxli9g2'
|
||||||
host_with_tags => {
|
response_body = { hostname => {
|
||||||
"template" => "redhat-7-x86_64",
|
'template' => 'ubuntu-1604-x86_64',
|
||||||
"lifetime" => 48,
|
'lifetime' => 12,
|
||||||
"running" => 7.67,
|
'running' => 9.66,
|
||||||
"tags" => {
|
'state' => 'running',
|
||||||
"user" => "bob",
|
'ip' => '127.0.0.1',
|
||||||
"role" => "agent"
|
'domain' => 'delivery.mycompany.net'
|
||||||
|
}}
|
||||||
|
output = "- mcpy42eqjxli9g2.delivery.mycompany.net (ubuntu-1604-x86_64, 9.66/12 hours)"
|
||||||
|
|
||||||
|
expect(Utils).to receive(:puts).with(output)
|
||||||
|
|
||||||
|
service = Service.new(MockOptions.new, {'url' => url})
|
||||||
|
allow(service).to receive(:query)
|
||||||
|
.with(nil, hostname)
|
||||||
|
.and_return(response_body)
|
||||||
|
|
||||||
|
Utils.pretty_print_hosts(nil, service, hostname)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prints a vmpooler output with host fqdn, template, duration info, and tags when supplied' do
|
||||||
|
hostname = 'aiydvzpg23r415q'
|
||||||
|
response_body = { hostname => {
|
||||||
|
'template' => 'redhat-7-x86_64',
|
||||||
|
'lifetime' => 48,
|
||||||
|
'running' => 7.67,
|
||||||
|
'state' => 'running',
|
||||||
|
'tags' => {
|
||||||
|
'user' => 'bob',
|
||||||
|
'role' => 'agent'
|
||||||
},
|
},
|
||||||
"domain" => "delivery.puppetlabs.net"
|
'ip' => '127.0.0.1',
|
||||||
}
|
'domain' => 'delivery.mycompany.net'
|
||||||
}
|
}}
|
||||||
|
output = "- aiydvzpg23r415q.delivery.mycompany.net (redhat-7-x86_64, 7.67/48 hours, user: bob, role: agent)"
|
||||||
|
|
||||||
|
expect(Utils).to receive(:puts).with(output)
|
||||||
|
|
||||||
|
service = Service.new(MockOptions.new, {'url' => url})
|
||||||
|
allow(service).to receive(:query)
|
||||||
|
.with(nil, hostname)
|
||||||
|
.and_return(response_body)
|
||||||
|
|
||||||
|
Utils.pretty_print_hosts(nil, service, hostname)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:host_info_without_tags) do
|
it 'prints a nonstandard pooler output with host, template, and time remaining' do
|
||||||
{
|
hostname = "sol11-9.delivery.mycompany.net"
|
||||||
host_without_tags => {
|
response_body = { hostname => {
|
||||||
"template" => "ubuntu-1604-x86_64",
|
'fqdn' => hostname,
|
||||||
"lifetime" => 12,
|
'os_triple' => 'solaris-11-sparc',
|
||||||
"running" => 9.66,
|
'reserved_by_user' => 'first.last',
|
||||||
"domain" => "delivery.puppetlabs.net"
|
'reserved_for_reason' => '',
|
||||||
}
|
'hours_left_on_reservation' => 35.89
|
||||||
}
|
}}
|
||||||
|
output = "- sol11-9.delivery.mycompany.net (solaris-11-sparc, 35.89h remaining)"
|
||||||
|
|
||||||
|
expect(Utils).to receive(:puts).with(output)
|
||||||
|
|
||||||
|
service = Service.new(MockOptions.new, {'url' => url, 'type' => 'ns'})
|
||||||
|
allow(service).to receive(:query)
|
||||||
|
.with(nil, hostname)
|
||||||
|
.and_return(response_body)
|
||||||
|
|
||||||
|
Utils.pretty_print_hosts(nil, service, hostname)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:output_with_tags) { "- #{host_with_tags}.delivery.puppetlabs.net (redhat-7-x86_64, 7.67/48 hours, user: bob, role: agent)" }
|
it 'prints a nonstandard pooler output with host, template, time remaining, and reason' do
|
||||||
let(:output_without_tags) { "- #{host_without_tags}.delivery.puppetlabs.net (ubuntu-1604-x86_64, 9.66/12 hours)" }
|
hostname = 'sol11-9.delivery.mycompany.net'
|
||||||
|
response_body = { hostname => {
|
||||||
|
'fqdn' => hostname,
|
||||||
|
'os_triple' => 'solaris-11-sparc',
|
||||||
|
'reserved_by_user' => 'first.last',
|
||||||
|
'reserved_for_reason' => 'testing',
|
||||||
|
'hours_left_on_reservation' => 35.89
|
||||||
|
}}
|
||||||
|
output = "- sol11-9.delivery.mycompany.net (solaris-11-sparc, 35.89h remaining, reason: testing)"
|
||||||
|
|
||||||
it 'prints an output with host fqdn, template and duration info' do
|
expect(Utils).to receive(:puts).with(output)
|
||||||
allow(Utils).to receive(:get_vm_info).
|
|
||||||
with(host_without_tags, false, url).
|
|
||||||
and_return(host_info_without_tags)
|
|
||||||
|
|
||||||
expect(Utils).to receive(:puts).with("Running VMs:")
|
service = Service.new(MockOptions.new, {'url' => url, 'type' => 'ns'})
|
||||||
expect(Utils).to receive(:puts).with(output_without_tags)
|
allow(service).to receive(:query)
|
||||||
|
.with(nil, hostname)
|
||||||
|
.and_return(response_body)
|
||||||
|
|
||||||
Utils.prettyprint_hosts(host_without_tags, false, url)
|
Utils.pretty_print_hosts(nil, service, hostname)
|
||||||
end
|
|
||||||
|
|
||||||
it 'prints an output with host fqdn, template, duration info, and tags when supplied' do
|
|
||||||
allow(Utils).to receive(:get_vm_info).
|
|
||||||
with(host_with_tags, false, url).
|
|
||||||
and_return(host_info_with_tags)
|
|
||||||
|
|
||||||
expect(Utils).to receive(:puts).with("Running VMs:")
|
|
||||||
expect(Utils).to receive(:puts).with(output_with_tags)
|
|
||||||
|
|
||||||
Utils.prettyprint_hosts(host_with_tags, false, url)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue