vmfloaty 1.0.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/extras/completions/floaty.bash +16 -8
- data/extras/completions/floaty.zsh +13 -7
- data/lib/vmfloaty.rb +62 -50
- data/lib/vmfloaty/abs.rb +86 -69
- data/lib/vmfloaty/http.rb +2 -6
- data/lib/vmfloaty/logger.rb +19 -3
- data/lib/vmfloaty/nonstandard_pooler.rb +3 -2
- data/lib/vmfloaty/pooler.rb +20 -25
- data/lib/vmfloaty/service.rb +6 -6
- data/lib/vmfloaty/utils.rb +67 -62
- data/lib/vmfloaty/version.rb +1 -2
- data/spec/spec_helper.rb +20 -3
- data/spec/vmfloaty/abs/auth_spec.rb +26 -17
- data/spec/vmfloaty/abs_spec.rb +55 -46
- data/spec/vmfloaty/auth_spec.rb +23 -13
- data/spec/vmfloaty/nonstandard_pooler_spec.rb +32 -31
- data/spec/vmfloaty/pooler_spec.rb +29 -26
- data/spec/vmfloaty/service_spec.rb +10 -10
- data/spec/vmfloaty/ssh_spec.rb +3 -3
- data/spec/vmfloaty/utils_spec.rb +191 -159
- metadata +17 -11
    
        data/lib/vmfloaty/http.rb
    CHANGED
    
    | @@ -21,13 +21,11 @@ class Http | |
| 21 21 |  | 
| 22 22 | 
             
                url = "https://#{url}" unless url?(url)
         | 
| 23 23 |  | 
| 24 | 
            -
                 | 
| 24 | 
            +
                Faraday.new(url: url, ssl: { verify: false }) do |faraday|
         | 
| 25 25 | 
             
                  faraday.request :url_encoded
         | 
| 26 26 | 
             
                  faraday.response :logger if verbose
         | 
| 27 27 | 
             
                  faraday.adapter Faraday.default_adapter
         | 
| 28 28 | 
             
                end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                conn
         | 
| 31 29 | 
             
              end
         | 
| 32 30 |  | 
| 33 31 | 
             
              def self.get_conn_with_auth(verbose, url, user, password)
         | 
| @@ -37,13 +35,11 @@ class Http | |
| 37 35 |  | 
| 38 36 | 
             
                url = "https://#{url}" unless url?(url)
         | 
| 39 37 |  | 
| 40 | 
            -
                 | 
| 38 | 
            +
                Faraday.new(url: url, ssl: { verify: false }) do |faraday|
         | 
| 41 39 | 
             
                  faraday.request :url_encoded
         | 
| 42 40 | 
             
                  faraday.request :basic_auth, user, password
         | 
| 43 41 | 
             
                  faraday.response :logger if verbose
         | 
| 44 42 | 
             
                  faraday.adapter Faraday.default_adapter
         | 
| 45 43 | 
             
                end
         | 
| 46 | 
            -
             | 
| 47 | 
            -
                conn
         | 
| 48 44 | 
             
              end
         | 
| 49 45 | 
             
            end
         | 
    
        data/lib/vmfloaty/logger.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'logger'
         | 
| 2 4 |  | 
| 3 5 | 
             
            class FloatyLogger < ::Logger
         | 
| @@ -17,11 +19,25 @@ class FloatyLogger < ::Logger | |
| 17 19 | 
             
                FloatyLogger.logger.error msg
         | 
| 18 20 | 
             
              end
         | 
| 19 21 |  | 
| 22 | 
            +
              def self.setlevel=(level)
         | 
| 23 | 
            +
                level = level.downcase
         | 
| 24 | 
            +
                case level
         | 
| 25 | 
            +
                when 'debug'
         | 
| 26 | 
            +
                  logger.level = ::Logger::DEBUG
         | 
| 27 | 
            +
                when 'info'
         | 
| 28 | 
            +
                  logger.level = ::Logger::INFO
         | 
| 29 | 
            +
                when 'error'
         | 
| 30 | 
            +
                  logger.level = ::Logger::ERROR
         | 
| 31 | 
            +
                else
         | 
| 32 | 
            +
                  error('set loglevel to debug, info or error')
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 20 36 | 
             
              def initialize
         | 
| 21 | 
            -
                super( | 
| 37 | 
            +
                super($stderr)
         | 
| 22 38 | 
             
                self.level = ::Logger::INFO
         | 
| 23 | 
            -
                self.formatter = proc do | | 
| 24 | 
            -
             | 
| 39 | 
            +
                self.formatter = proc do |_severity, _datetime, _progname, msg|
         | 
| 40 | 
            +
                  "#{msg}\n"
         | 
| 25 41 | 
             
                end
         | 
| 26 42 | 
             
              end
         | 
| 27 43 | 
             
            end
         | 
| @@ -22,7 +22,7 @@ class NonstandardPooler | |
| 22 22 | 
             
                status['reserved_hosts'] || []
         | 
| 23 23 | 
             
              end
         | 
| 24 24 |  | 
| 25 | 
            -
              def self.retrieve(verbose, os_type, token, url, _user, _options,  | 
| 25 | 
            +
              def self.retrieve(verbose, os_type, token, url, _user, _options, _ondemand = nil, _continue = nil)
         | 
| 26 26 | 
             
                conn = Http.get_conn(verbose, url)
         | 
| 27 27 | 
             
                conn.headers['X-AUTH-TOKEN'] = token if token
         | 
| 28 28 |  | 
| @@ -46,7 +46,8 @@ class NonstandardPooler | |
| 46 46 | 
             
                raise TokenError, 'Token provided was nil; Request cannot be made to modify VM' if token.nil?
         | 
| 47 47 |  | 
| 48 48 | 
             
                modify_hash.each do |key, _value|
         | 
| 49 | 
            -
                  raise ModifyError, "Configured service type does not support modification of #{key}" unless %i[reason | 
| 49 | 
            +
                  raise ModifyError, "Configured service type does not support modification of #{key}" unless %i[reason
         | 
| 50 | 
            +
                                                                                                                 reserved_for_reason].include? key
         | 
| 50 51 | 
             
                end
         | 
| 51 52 |  | 
| 52 53 | 
             
                if modify_hash[:reason]
         | 
    
        data/lib/vmfloaty/pooler.rb
    CHANGED
    
    | @@ -12,13 +12,11 @@ class Pooler | |
| 12 12 | 
             
                response = conn.get 'vm'
         | 
| 13 13 | 
             
                response_body = JSON.parse(response.body)
         | 
| 14 14 |  | 
| 15 | 
            -
                 | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
                hosts
         | 
| 15 | 
            +
                if os_filter
         | 
| 16 | 
            +
                  response_body.select { |i| i[/#{os_filter}/] }
         | 
| 17 | 
            +
                else
         | 
| 18 | 
            +
                  response_body
         | 
| 19 | 
            +
                end
         | 
| 22 20 | 
             
              end
         | 
| 23 21 |  | 
| 24 22 | 
             
              def self.list_active(verbose, url, token, _user)
         | 
| @@ -28,7 +26,7 @@ class Pooler | |
| 28 26 | 
             
                vms
         | 
| 29 27 | 
             
              end
         | 
| 30 28 |  | 
| 31 | 
            -
              def self.retrieve(verbose, os_type, token, url, _user, _options, ondemand = nil)
         | 
| 29 | 
            +
              def self.retrieve(verbose, os_type, token, url, _user, _options, ondemand = nil, _continue = nil)
         | 
| 32 30 | 
             
                # NOTE:
         | 
| 33 31 | 
             
                #   Developers can use `Utils.generate_os_hash` to
         | 
| 34 32 | 
             
                #   generate the os_type param.
         | 
| @@ -50,7 +48,10 @@ class Pooler | |
| 50 48 | 
             
                elsif response.status == 403
         | 
| 51 49 | 
             
                  raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/vm/#{os_string}. Request exceeds the configured per pool maximum. #{res_body}"
         | 
| 52 50 | 
             
                else
         | 
| 53 | 
            -
                   | 
| 51 | 
            +
                  unless ondemand
         | 
| 52 | 
            +
                    raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/vm/#{os_string}. #{res_body}"
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 54 55 | 
             
                  raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/ondemandvm/#{os_string}. #{res_body}"
         | 
| 55 56 | 
             
                end
         | 
| 56 57 | 
             
              end
         | 
| @@ -63,7 +64,7 @@ class Pooler | |
| 63 64 | 
             
                  FloatyLogger.info "waiting for request #{request_id} to be fulfilled"
         | 
| 64 65 | 
             
                  sleep 5
         | 
| 65 66 | 
             
                end
         | 
| 66 | 
            -
                FloatyLogger.info  | 
| 67 | 
            +
                FloatyLogger.info 'The request has been fulfilled'
         | 
| 67 68 | 
             
                check_ondemandvm(verbose, request_id, url)
         | 
| 68 69 | 
             
              end
         | 
| 69 70 |  | 
| @@ -84,8 +85,9 @@ class Pooler | |
| 84 85 | 
             
              def self.modify(verbose, url, hostname, token, modify_hash)
         | 
| 85 86 | 
             
                raise TokenError, 'Token provided was nil. Request cannot be made to modify vm' if token.nil?
         | 
| 86 87 |  | 
| 87 | 
            -
                modify_hash. | 
| 88 | 
            -
                  raise ModifyError, "Configured service type does not support modification of #{key}." unless %i[tags lifetime | 
| 88 | 
            +
                modify_hash.each_key do |key|
         | 
| 89 | 
            +
                  raise ModifyError, "Configured service type does not support modification of #{key}." unless %i[tags lifetime
         | 
| 90 | 
            +
                                                                                                                  disk].include? key
         | 
| 89 91 | 
             
                end
         | 
| 90 92 |  | 
| 91 93 | 
             
                conn = Http.get_conn(verbose, url)
         | 
| @@ -120,8 +122,7 @@ class Pooler | |
| 120 122 |  | 
| 121 123 | 
             
                response = conn.post "vm/#{hostname}/disk/#{disk}"
         | 
| 122 124 |  | 
| 123 | 
            -
                 | 
| 124 | 
            -
                res_body
         | 
| 125 | 
            +
                JSON.parse(response.body)
         | 
| 125 126 | 
             
              end
         | 
| 126 127 |  | 
| 127 128 | 
             
              def self.delete(verbose, url, hosts, token, _user)
         | 
| @@ -146,25 +147,21 @@ class Pooler | |
| 146 147 | 
             
                conn = Http.get_conn(verbose, url)
         | 
| 147 148 |  | 
| 148 149 | 
             
                response = conn.get '/status'
         | 
| 149 | 
            -
                 | 
| 150 | 
            -
                res_body
         | 
| 150 | 
            +
                JSON.parse(response.body)
         | 
| 151 151 | 
             
              end
         | 
| 152 152 |  | 
| 153 153 | 
             
              def self.summary(verbose, url)
         | 
| 154 154 | 
             
                conn = Http.get_conn(verbose, url)
         | 
| 155 155 |  | 
| 156 156 | 
             
                response = conn.get '/summary'
         | 
| 157 | 
            -
                 | 
| 158 | 
            -
                res_body
         | 
| 157 | 
            +
                JSON.parse(response.body)
         | 
| 159 158 | 
             
              end
         | 
| 160 159 |  | 
| 161 160 | 
             
              def self.query(verbose, url, hostname)
         | 
| 162 161 | 
             
                conn = Http.get_conn(verbose, url)
         | 
| 163 162 |  | 
| 164 163 | 
             
                response = conn.get "vm/#{hostname}"
         | 
| 165 | 
            -
                 | 
| 166 | 
            -
             | 
| 167 | 
            -
                res_body
         | 
| 164 | 
            +
                JSON.parse(response.body)
         | 
| 168 165 | 
             
              end
         | 
| 169 166 |  | 
| 170 167 | 
             
              def self.snapshot(verbose, url, hostname, token)
         | 
| @@ -174,8 +171,7 @@ class Pooler | |
| 174 171 | 
             
                conn.headers['X-AUTH-TOKEN'] = token
         | 
| 175 172 |  | 
| 176 173 | 
             
                response = conn.post "vm/#{hostname}/snapshot"
         | 
| 177 | 
            -
                 | 
| 178 | 
            -
                res_body
         | 
| 174 | 
            +
                JSON.parse(response.body)
         | 
| 179 175 | 
             
              end
         | 
| 180 176 |  | 
| 181 177 | 
             
              def self.revert(verbose, url, hostname, token, snapshot_sha)
         | 
| @@ -187,7 +183,6 @@ class Pooler | |
| 187 183 | 
             
                raise "Snapshot SHA provided was nil, could not revert #{hostname}" if snapshot_sha.nil?
         | 
| 188 184 |  | 
| 189 185 | 
             
                response = conn.post "vm/#{hostname}/snapshot/#{snapshot_sha}"
         | 
| 190 | 
            -
                 | 
| 191 | 
            -
                res_body
         | 
| 186 | 
            +
                JSON.parse(response.body)
         | 
| 192 187 | 
             
              end
         | 
| 193 188 | 
             
            end
         | 
    
        data/lib/vmfloaty/service.rb
    CHANGED
    
    | @@ -39,7 +39,7 @@ class Service | |
| 39 39 | 
             
              def user
         | 
| 40 40 | 
             
                unless @config['user']
         | 
| 41 41 | 
             
                  FloatyLogger.info "Enter your #{@config['url']} service username:"
         | 
| 42 | 
            -
                  @config['user'] =  | 
| 42 | 
            +
                  @config['user'] = $stdin.gets.chomp
         | 
| 43 43 | 
             
                end
         | 
| 44 44 | 
             
                @config['user']
         | 
| 45 45 | 
             
              end
         | 
| @@ -77,10 +77,10 @@ class Service | |
| 77 77 | 
             
                @service_object.list_active verbose, url, token, user
         | 
| 78 78 | 
             
              end
         | 
| 79 79 |  | 
| 80 | 
            -
              def retrieve(verbose, os_types, use_token = true, ondemand = nil)
         | 
| 80 | 
            +
              def retrieve(verbose, os_types, use_token = true, ondemand = nil, continue = nil)
         | 
| 81 81 | 
             
                FloatyLogger.info 'Requesting a vm without a token...' unless use_token
         | 
| 82 82 | 
             
                token_value = use_token ? token : nil
         | 
| 83 | 
            -
                @service_object.retrieve verbose, os_types, token_value, url, user, @config, ondemand
         | 
| 83 | 
            +
                @service_object.retrieve verbose, os_types, token_value, url, user, @config, ondemand, continue
         | 
| 84 84 | 
             
              end
         | 
| 85 85 |  | 
| 86 86 | 
             
              def wait_for_request(verbose, requestid)
         | 
| @@ -139,9 +139,9 @@ class Service | |
| 139 139 |  | 
| 140 140 | 
             
              # some methods do not exist for ABS, and if possible should target the Pooler service
         | 
| 141 141 | 
             
              def maybe_use_vmpooler
         | 
| 142 | 
            -
                if @service_object | 
| 143 | 
            -
                   | 
| 144 | 
            -
                    FloatyLogger.info  | 
| 142 | 
            +
                if @service_object == ABS # this is not an instance
         | 
| 143 | 
            +
                  unless silent
         | 
| 144 | 
            +
                    FloatyLogger.info 'The service in use is ABS, but the requested method should run against vmpooler directly, using fallback_vmpooler config from ~/.vmfloaty.yml'
         | 
| 145 145 | 
             
                    self.silent = true
         | 
| 146 146 | 
             
                  end
         | 
| 147 147 |  | 
    
        data/lib/vmfloaty/utils.rb
    CHANGED
    
    | @@ -39,7 +39,10 @@ class Utils | |
| 39 39 | 
             
                #   "engine"=>"vmpooler"
         | 
| 40 40 | 
             
                # }
         | 
| 41 41 |  | 
| 42 | 
            -
                 | 
| 42 | 
            +
                unless response_body.delete('ok')
         | 
| 43 | 
            +
                  raise ArgumentError,
         | 
| 44 | 
            +
                        "Bad GET response passed to format_hosts: #{response_body.to_json}"
         | 
| 45 | 
            +
                end
         | 
| 43 46 |  | 
| 44 47 | 
             
                # vmpooler reports the domain separately from the hostname
         | 
| 45 48 | 
             
                domain = response_body.delete('domain')
         | 
| @@ -50,7 +53,7 @@ class Utils | |
| 50 53 | 
             
                abs_job_id = response_body.delete('job_id')
         | 
| 51 54 | 
             
                result['job_id'] = abs_job_id unless abs_job_id.nil?
         | 
| 52 55 |  | 
| 53 | 
            -
                filtered_response_body = response_body.reject { |key, _|  | 
| 56 | 
            +
                filtered_response_body = response_body.reject { |key, _| %w[request_id ready].include?(key) }
         | 
| 54 57 | 
             
                filtered_response_body.each do |os, value|
         | 
| 55 58 | 
             
                  hostnames = Array(value['hostname'])
         | 
| 56 59 | 
             
                  hostnames.map! { |host| "#{host}.#{domain}" } if domain
         | 
| @@ -106,7 +109,7 @@ class Utils | |
| 106 109 | 
             
              def self.pretty_print_hosts(verbose, service, hostnames = [], print_to_stderr = false, indent = 0)
         | 
| 107 110 | 
             
                output_target = print_to_stderr ? $stderr : $stdout
         | 
| 108 111 |  | 
| 109 | 
            -
                fetched_data =  | 
| 112 | 
            +
                fetched_data = get_host_data(verbose, service, hostnames)
         | 
| 110 113 | 
             
                fetched_data.each do |hostname, host_data|
         | 
| 111 114 | 
             
                  case service.type
         | 
| 112 115 | 
             
                  when 'ABS'
         | 
| @@ -116,13 +119,14 @@ class Utils | |
| 116 119 |  | 
| 117 120 | 
             
                    output_target.puts "- [JobID:#{host_data['request']['job']['id']}] <#{host_data['state']}>"
         | 
| 118 121 | 
             
                    host_data['allocated_resources'].each do |allocated_resources, _i|
         | 
| 119 | 
            -
                      if allocated_resources['engine'] ==  | 
| 122 | 
            +
                      if (allocated_resources['engine'] == 'vmpooler' || allocated_resources['engine'] == 'ondemand') && service.config['vmpooler_fallback']
         | 
| 120 123 | 
             
                        vmpooler_service = service.clone
         | 
| 121 124 | 
             
                        vmpooler_service.silent = true
         | 
| 122 125 | 
             
                        vmpooler_service.maybe_use_vmpooler
         | 
| 123 | 
            -
                         | 
| 126 | 
            +
                        pretty_print_hosts(verbose, vmpooler_service, allocated_resources['hostname'].split('.')[0],
         | 
| 127 | 
            +
                                           print_to_stderr, indent + 2)
         | 
| 124 128 | 
             
                      else
         | 
| 125 | 
            -
                        #TODO we could add more specific metadata for the other services, nspooler and aws
         | 
| 129 | 
            +
                        # TODO: we could add more specific metadata for the other services, nspooler and aws
         | 
| 126 130 | 
             
                        output_target.puts "  - #{allocated_resources['hostname']} (#{allocated_resources['type']})"
         | 
| 127 131 | 
             
                      end
         | 
| 128 132 | 
             
                    end
         | 
| @@ -130,8 +134,13 @@ class Utils | |
| 130 134 | 
             
                    tag_pairs = []
         | 
| 131 135 | 
             
                    tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
         | 
| 132 136 | 
             
                    duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
         | 
| 133 | 
            -
                    metadata = [host_data['template'], duration, *tag_pairs]
         | 
| 134 | 
            -
                     | 
| 137 | 
            +
                    metadata = [host_data['state'], host_data['template'], duration, *tag_pairs]
         | 
| 138 | 
            +
                    message = "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})".gsub(/^/, ' ' * indent)
         | 
| 139 | 
            +
                    if host_data['state'] && host_data['state'] == 'destroyed'
         | 
| 140 | 
            +
                      output_target.puts message.colorize(:red)
         | 
| 141 | 
            +
                    else
         | 
| 142 | 
            +
                      output_target.puts message
         | 
| 143 | 
            +
                    end
         | 
| 135 144 | 
             
                  when 'NonstandardPooler'
         | 
| 136 145 | 
             
                    line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
         | 
| 137 146 | 
             
                    line += ", #{host_data['hours_left_on_reservation']}h remaining"
         | 
| @@ -148,30 +157,26 @@ class Utils | |
| 148 157 | 
             
                result = {}
         | 
| 149 158 | 
             
                hostnames = [hostnames] unless hostnames.is_a? Array
         | 
| 150 159 | 
             
                hostnames.each do |hostname|
         | 
| 151 | 
            -
                   | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
                     | 
| 155 | 
            -
             | 
| 160 | 
            +
                  response = service.query(verbose, hostname)
         | 
| 161 | 
            +
                  host_data = response[hostname]
         | 
| 162 | 
            +
                  if block_given?
         | 
| 163 | 
            +
                    yield host_data result
         | 
| 164 | 
            +
                  else
         | 
| 165 | 
            +
                    case service.type
         | 
| 166 | 
            +
                    when 'ABS'
         | 
| 167 | 
            +
                      # For ABS, 'hostname' variable is the jobID
         | 
| 168 | 
            +
                      result[hostname] = host_data if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
         | 
| 169 | 
            +
                    when 'Pooler'
         | 
| 170 | 
            +
                      result[hostname] = host_data
         | 
| 171 | 
            +
                    when 'NonstandardPooler'
         | 
| 172 | 
            +
                      result[hostname] = host_data
         | 
| 156 173 | 
             
                    else
         | 
| 157 | 
            -
                       | 
| 158 | 
            -
                      when 'ABS'
         | 
| 159 | 
            -
                        # For ABS, 'hostname' variable is the jobID
         | 
| 160 | 
            -
                        if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
         | 
| 161 | 
            -
                          result[hostname] = host_data
         | 
| 162 | 
            -
                        end
         | 
| 163 | 
            -
                      when 'Pooler'
         | 
| 164 | 
            -
                        result[hostname] = host_data
         | 
| 165 | 
            -
                      when 'NonstandardPooler'
         | 
| 166 | 
            -
                        result[hostname] = host_data
         | 
| 167 | 
            -
                      else
         | 
| 168 | 
            -
                        raise "Invalid service type #{service.type}"
         | 
| 169 | 
            -
                      end
         | 
| 174 | 
            +
                      raise "Invalid service type #{service.type}"
         | 
| 170 175 | 
             
                    end
         | 
| 171 | 
            -
                  rescue StandardError => e
         | 
| 172 | 
            -
                    FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
         | 
| 173 | 
            -
                    FloatyLogger.error(e)
         | 
| 174 176 | 
             
                  end
         | 
| 177 | 
            +
                rescue StandardError => e
         | 
| 178 | 
            +
                  FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
         | 
| 179 | 
            +
                  FloatyLogger.error(e)
         | 
| 175 180 | 
             
                end
         | 
| 176 181 | 
             
                result
         | 
| 177 182 | 
             
              end
         | 
| @@ -187,16 +192,14 @@ class Utils | |
| 187 192 |  | 
| 188 193 | 
             
                  width = pools.keys.map(&:length).max
         | 
| 189 194 | 
             
                  pools.each do |name, pool|
         | 
| 190 | 
            -
                     | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
                     | 
| 198 | 
            -
                      FloatyLogger.error "#{name.ljust(width)} #{e.red}"
         | 
| 199 | 
            -
                    end
         | 
| 195 | 
            +
                    max = pool['max']
         | 
| 196 | 
            +
                    ready = pool['ready']
         | 
| 197 | 
            +
                    pending = pool['pending']
         | 
| 198 | 
            +
                    missing = max - ready - pending
         | 
| 199 | 
            +
                    char = 'o'
         | 
| 200 | 
            +
                    puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
         | 
| 201 | 
            +
                  rescue StandardError => e
         | 
| 202 | 
            +
                    FloatyLogger.error "#{name.ljust(width)} #{e.red}"
         | 
| 200 203 | 
             
                  end
         | 
| 201 204 | 
             
                  puts message.colorize(status_response['status']['ok'] ? :default : :red)
         | 
| 202 205 | 
             
                when 'NonstandardPooler'
         | 
| @@ -206,16 +209,14 @@ class Utils | |
| 206 209 |  | 
| 207 210 | 
             
                  width = pools.keys.map(&:length).max
         | 
| 208 211 | 
             
                  pools.each do |name, pool|
         | 
| 209 | 
            -
                     | 
| 210 | 
            -
             | 
| 211 | 
            -
             | 
| 212 | 
            -
             | 
| 213 | 
            -
             | 
| 214 | 
            -
             | 
| 215 | 
            -
             | 
| 216 | 
            -
                     | 
| 217 | 
            -
                      FloatyLogger.error "#{name.ljust(width)} #{e.red}"
         | 
| 218 | 
            -
                    end
         | 
| 212 | 
            +
                    max = pool['total_hosts']
         | 
| 213 | 
            +
                    ready = pool['available_hosts']
         | 
| 214 | 
            +
                    pending = pool['pending'] || 0 # not available for nspooler
         | 
| 215 | 
            +
                    missing = max - ready - pending
         | 
| 216 | 
            +
                    char = 'o'
         | 
| 217 | 
            +
                    puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
         | 
| 218 | 
            +
                  rescue StandardError => e
         | 
| 219 | 
            +
                    FloatyLogger.error "#{name.ljust(width)} #{e.red}"
         | 
| 219 220 | 
             
                  end
         | 
| 220 221 | 
             
                when 'ABS'
         | 
| 221 222 | 
             
                  FloatyLogger.error 'ABS Not OK' unless status_response
         | 
| @@ -251,10 +252,11 @@ class Utils | |
| 251 252 | 
             
              def self.get_service_config(config, options)
         | 
| 252 253 | 
             
                # The top-level url, user, and token values in the config file are treated as defaults
         | 
| 253 254 | 
             
                service_config = {
         | 
| 254 | 
            -
                  'url' | 
| 255 | 
            -
                  'user' | 
| 255 | 
            +
                  'url' => config['url'],
         | 
| 256 | 
            +
                  'user' => config['user'],
         | 
| 256 257 | 
             
                  'token' => config['token'],
         | 
| 257 | 
            -
                  ' | 
| 258 | 
            +
                  'vmpooler_fallback' => config['vmpooler_fallback'],
         | 
| 259 | 
            +
                  'type' => config['type'] || 'vmpooler'
         | 
| 258 260 | 
             
                }
         | 
| 259 261 |  | 
| 260 262 | 
             
                if config['services']
         | 
| @@ -265,7 +267,10 @@ class Utils | |
| 265 267 | 
             
                    service_config.merge! values
         | 
| 266 268 | 
             
                  else
         | 
| 267 269 | 
             
                    # If the user provided a service name at the command line, use that service if posible, or fail
         | 
| 268 | 
            -
                     | 
| 270 | 
            +
                    unless config['services'][options.service]
         | 
| 271 | 
            +
                      raise ArgumentError,
         | 
| 272 | 
            +
                            "Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml"
         | 
| 273 | 
            +
                    end
         | 
| 269 274 |  | 
| 270 275 | 
             
                    # If the service is configured but some values are missing, use the top-level defaults to fill them in
         | 
| 271 276 | 
             
                    service_config.merge! config['services'][options.service]
         | 
| @@ -289,22 +294,22 @@ class Utils | |
| 289 294 | 
             
                config = Conf.read_config
         | 
| 290 295 | 
             
                # The top-level url, user, and token values in the config file are treated as defaults
         | 
| 291 296 | 
             
                service_config = {
         | 
| 292 | 
            -
             | 
| 293 | 
            -
             | 
| 294 | 
            -
             | 
| 295 | 
            -
             | 
| 297 | 
            +
                  'url' => config['url'],
         | 
| 298 | 
            +
                  'user' => config['user'],
         | 
| 299 | 
            +
                  'token' => config['token'],
         | 
| 300 | 
            +
                  'type' => 'vmpooler'
         | 
| 296 301 | 
             
                }
         | 
| 297 302 |  | 
| 298 303 | 
             
                # at a minimum, the url needs to be configured
         | 
| 299 304 | 
             
                if config['services'] && config['services'][vmpooler_fallback] && config['services'][vmpooler_fallback]['url']
         | 
| 300 305 | 
             
                  # If the service is configured but some values are missing, use the top-level defaults to fill them in
         | 
| 301 306 | 
             
                  service_config.merge! config['services'][vmpooler_fallback]
         | 
| 307 | 
            +
                elsif vmpooler_fallback.nil?
         | 
| 308 | 
            +
                  raise ArgumentError,
         | 
| 309 | 
            +
                        "The abs service should have a key named 'vmpooler_fallback' in ~/.vmfloaty.yml with a value that points to a vmpooler service name use this format:\nservices:\n  myabs:\n    url: 'http://abs.com'\n    user: 'superman'\n    token: 'kryptonite'\n    vmpooler_fallback: 'myvmpooler'\n  myvmpooler:\n    url: 'http://vmpooler.com'\n    user: 'superman'\n    token: 'kryptonite'"
         | 
| 302 310 | 
             
                else
         | 
| 303 | 
            -
                   | 
| 304 | 
            -
             | 
| 305 | 
            -
                  else
         | 
| 306 | 
            -
                    raise ArgumentError, "Could not find a configured service named '#{vmpooler_fallback}' in ~/.vmfloaty.yml use this format:\nservices:\n  #{vmpooler_fallback}:\n    url: 'http://vmpooler.com'\n    user: 'superman'\n    token: 'kryptonite'"
         | 
| 307 | 
            -
                  end
         | 
| 311 | 
            +
                  raise ArgumentError,
         | 
| 312 | 
            +
                        "Could not find a configured service named '#{vmpooler_fallback}' in ~/.vmfloaty.yml use this format:\nservices:\n  #{vmpooler_fallback}:\n    url: 'http://vmpooler.com'\n    user: 'superman'\n    token: 'kryptonite'"
         | 
| 308 313 | 
             
                end
         | 
| 309 314 |  | 
| 310 315 | 
             
                service_config
         |