vmfloaty 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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