vmfloaty 0.7.9 → 0.8.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.
@@ -1,27 +1,62 @@
1
-
2
1
  require 'vmfloaty/pooler'
2
+ require 'vmfloaty/nonstandard_pooler'
3
3
 
4
4
  class Utils
5
5
  # TODO: Takes the json response body from an HTTP GET
6
6
  # request and "pretty prints" it
7
- def self.format_hosts(hostname_hash)
8
- host_hash = {}
9
-
10
- hostname_hash.delete("ok")
11
- domain = hostname_hash["domain"]
12
- hostname_hash.each do |type, hosts|
13
- if type != "domain"
14
- if hosts["hostname"].kind_of?(Array)
15
- hosts["hostname"].map!{|host| host + "." + domain }
7
+ def self.format_hosts(response_body)
8
+ # vmpooler response body example when `floaty get` arguments are `ubuntu-1610-x86_64=2 centos-7-x86_64`:
9
+ # {
10
+ # "ok": true,
11
+ # "domain": "delivery.mycompany.net",
12
+ # "ubuntu-1610-x86_64": {
13
+ # "hostname": ["gdoy8q3nckuob0i", "ctnktsd0u11p9tm"]
14
+ # },
15
+ # "centos-7-x86_64": {
16
+ # "hostname": "dlgietfmgeegry2"
17
+ # }
18
+ # }
19
+
20
+ # nonstandard pooler response body example when `floaty get` arguments are `solaris-11-sparc=2 ubuntu-16.04-power8`:
21
+ # {
22
+ # "ok": true,
23
+ # "solaris-10-sparc": {
24
+ # "hostname": ["sol10-10.delivery.mycompany.net", "sol10-11.delivery.mycompany.net"]
25
+ # },
26
+ # "ubuntu-16.04-power8": {
27
+ # "hostname": "power8-ubuntu1604-6.delivery.mycompany.net"
28
+ # }
29
+ # }
30
+
31
+ unless response_body.delete('ok')
32
+ raise ArgumentError, "Bad GET response passed to format_hosts: #{response_body.to_json}"
33
+ end
34
+
35
+ hostnames = []
36
+
37
+ # vmpooler reports the domain separately from the hostname
38
+ domain = response_body.delete('domain')
39
+
40
+ if domain
41
+ # vmpooler output
42
+ response_body.each do |os, hosts|
43
+ if hosts['hostname'].kind_of?(Array)
44
+ hosts['hostname'].map!{ |host| hostnames << host + "." + domain + " (#{os})"}
16
45
  else
17
- hosts["hostname"] = hosts["hostname"] + "." + domain
46
+ hostnames << hosts["hostname"] + ".#{domain} (#{os})"
47
+ end
48
+ end
49
+ else
50
+ response_body.each do |os, hosts|
51
+ if hosts['hostname'].kind_of?(Array)
52
+ hosts['hostname'].map!{ |host| hostnames << host + " (#{os})" }
53
+ else
54
+ hostnames << hosts['hostname'] + " (#{os})"
18
55
  end
19
-
20
- host_hash[type] = hosts["hostname"]
21
56
  end
22
57
  end
23
58
 
24
- host_hash.to_json
59
+ hostnames.map { |hostname| puts "- #{hostname}" }
25
60
  end
26
61
 
27
62
  def self.generate_os_hash(os_args)
@@ -46,79 +81,134 @@ class Utils
46
81
  os_types
47
82
  end
48
83
 
49
- def self.get_vm_info(hosts, verbose, url)
50
- vms = {}
51
- hosts.each do |host|
52
- vm_info = Pooler.query(verbose, url, host)
53
- if vm_info['ok']
54
- vms[host] = {}
55
- vms[host]['domain'] = vm_info[host]['domain']
56
- vms[host]['template'] = vm_info[host]['template']
57
- vms[host]['lifetime'] = vm_info[host]['lifetime']
58
- vms[host]['running'] = vm_info[host]['running']
59
- vms[host]['tags'] = vm_info[host]['tags']
84
+ def self.pretty_print_hosts(verbose, service, hostnames = [])
85
+ hostnames = [hostnames] unless hostnames.is_a? Array
86
+ hostnames.each do |hostname|
87
+ begin
88
+ response = service.query(verbose, hostname)
89
+ host_data = response[hostname]
90
+
91
+ case service.type
92
+ when 'Pooler'
93
+ tag_pairs = []
94
+ unless host_data['tags'].nil?
95
+ tag_pairs = host_data['tags'].map {|key, value| "#{key}: #{value}"}
96
+ end
97
+ duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
98
+ metadata = [host_data['template'], duration, *tag_pairs]
99
+ puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(", ")})"
100
+ when 'NonstandardPooler'
101
+ line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
102
+ line += ", #{host_data['hours_left_on_reservation']}h remaining"
103
+ unless host_data['reserved_for_reason'].empty?
104
+ line += ", reason: #{host_data['reserved_for_reason']}"
105
+ end
106
+ line += ')'
107
+ puts line
108
+ else
109
+ raise "Invalid service type #{service.type}"
110
+ end
111
+ rescue => e
112
+ STDERR.puts("Something went wrong while trying to gather information on #{hostname}:")
113
+ STDERR.puts(e)
60
114
  end
61
115
  end
62
- vms
63
116
  end
64
117
 
65
- def self.prettyprint_hosts(hosts, verbose, url)
66
- puts "Running VMs:"
67
- vm_info = get_vm_info(hosts, verbose, url)
68
- vm_info.each do |vm,info|
69
- domain = info['domain']
70
- template = info['template']
71
- lifetime = info['lifetime']
72
- running = info['running']
73
- tags = info['tags'] || {}
74
-
75
- tag_pairs = tags.map {|key,value| "#{key}: #{value}" }
76
- duration = "#{running}/#{lifetime} hours"
77
- metadata = [template, duration, *tag_pairs]
78
-
79
- puts "- #{vm}.#{domain} (#{metadata.join(", ")})"
118
+ def self.pretty_print_status(verbose, service)
119
+ status_response = service.status(verbose)
120
+
121
+ case service.type
122
+ when 'Pooler'
123
+ message = status_response['status']['message']
124
+ pools = status_response['pools']
125
+ pools.select! {|_, pool| pool['ready'] < pool['max']} unless verbose
126
+
127
+ width = pools.keys.map(&:length).max
128
+ pools.each do |name, pool|
129
+ begin
130
+ max = pool['max']
131
+ ready = pool['ready']
132
+ pending = pool['pending']
133
+ missing = max - ready - pending
134
+ char = 'o'
135
+ puts "#{name.ljust(width)} #{(char*ready).green}#{(char*pending).yellow}#{(char*missing).red}"
136
+ rescue => e
137
+ puts "#{name.ljust(width)} #{e.red}"
138
+ end
139
+ end
140
+ puts message.colorize(status_response['status']['ok'] ? :default : :red)
141
+ when 'NonstandardPooler'
142
+ pools = status_response
143
+ pools.delete 'ok'
144
+ pools.select! {|_, pool| pool['available_hosts'] < pool['total_hosts']} unless verbose
145
+
146
+ width = pools.keys.map(&:length).max
147
+ pools.each do |name, pool|
148
+ begin
149
+ max = pool['total_hosts']
150
+ ready = pool['available_hosts']
151
+ pending = pool['pending'] || 0 # not available for nspooler
152
+ missing = max - ready - pending
153
+ char = 'o'
154
+ puts "#{name.ljust(width)} #{(char*ready).green}#{(char*pending).yellow}#{(char*missing).red}"
155
+ rescue => e
156
+ puts "#{name.ljust(width)} #{e.red}"
157
+ end
158
+ end
159
+ else
160
+ raise "Invalid service type #{service.type}"
80
161
  end
81
162
  end
82
163
 
83
- def self.get_all_token_vms(verbose, url, token)
84
- # get vms with token
85
- status = Auth.token_status(verbose, url, token)
86
-
87
- vms = status[token]['vms']
88
- if vms.nil?
89
- raise "You have no running vms"
90
- end
164
+ # Adapted from ActiveSupport
165
+ def self.strip_heredoc(str)
166
+ min_indent = str.scan(/^[ \t]*(?=\S)/).min
167
+ min_indent_size = min_indent.nil? ? 0 : min_indent.size
91
168
 
92
- running_vms = vms['running']
93
- running_vms
169
+ str.gsub(/^[ \t]{#{min_indent_size}}/, '')
94
170
  end
95
171
 
96
- def self.prettyprint_status(status, message, pools, verbose)
97
- pools.select! {|name,pool| pool['ready'] < pool['max']} if ! verbose
172
+ def self.get_service_object(type = '')
173
+ nspooler_strings = ['ns', 'nspooler', 'nonstandard', 'nonstandard_pooler']
174
+ if nspooler_strings.include? type.downcase
175
+ NonstandardPooler
176
+ else
177
+ Pooler
178
+ end
179
+ end
98
180
 
99
- width = pools.keys.map(&:length).max
100
- pools.each do |name,pool|
101
- begin
102
- max = pool['max']
103
- ready = pool['ready']
104
- pending = pool['pending']
105
- missing = max - ready - pending
106
- char = 'o'
107
- puts "#{name.ljust(width)} #{(char*ready).green}#{(char*pending).yellow}#{(char*missing).red}"
108
- rescue => e
109
- puts "#{name.ljust(width)} #{e.red}"
181
+ def self.get_service_config(config, options)
182
+ # The top-level url, user, and token values in the config file are treated as defaults
183
+ service_config = {
184
+ 'url' => config['url'],
185
+ 'user' => config['user'],
186
+ 'token' => config['token'],
187
+ 'type' => config['type'] || 'vmpooler'
188
+ }
189
+
190
+ if config['services']
191
+ if options.service.nil?
192
+ # If the user did not specify a service name at the command line, but configured services do exist,
193
+ # use the first configured service in the list by default.
194
+ _, values = config['services'].first
195
+ service_config.merge! values
196
+ else
197
+ # If the user provided a service name at the command line, use that service if posible, or fail
198
+ if config['services'][options.service]
199
+ # If the service is configured but some values are missing, use the top-level defaults to fill them in
200
+ service_config.merge! config['services'][options.service]
201
+ else
202
+ raise ArgumentError, "Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml"
203
+ end
110
204
  end
111
205
  end
112
206
 
113
- puts
114
- puts message.colorize(status['status']['ok'] ? :default : :red)
115
- end
207
+ # Prioritize an explicitly specified url, user, or token if the user provided one
208
+ service_config['url'] = options.url unless options.url.nil?
209
+ service_config['token'] = options.token unless options.token.nil?
210
+ service_config['user'] = options.user unless options.user.nil?
116
211
 
117
- # Adapted from ActiveSupport
118
- def self.strip_heredoc(str)
119
- min_indent = str.scan(/^[ \t]*(?=\S)/).min
120
- min_indent_size = min_indent.nil? ? 0 : min_indent.size
121
-
122
- str.gsub(/^[ \t]{#{min_indent_size}}/, '')
212
+ service_config
123
213
  end
124
214
  end
@@ -1,3 +1,3 @@
1
1
  class Vmfloaty
2
- VERSION = '0.7.9'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end