manageiq-appliance_console 5.3.2 → 6.1.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/.codeclimate.yml +24 -25
- data/.rspec_ci +2 -0
- data/.rubocop.yml +3 -3
- data/.rubocop_cc.yml +3 -4
- data/.rubocop_local.yml +1 -1
- data/.travis.yml +4 -3
- data/Gemfile +1 -3
- data/README.md +1 -2
- data/Rakefile +20 -1
- data/bin/appliance_console +44 -7
- data/lib/manageiq-appliance_console.rb +2 -5
- data/lib/manageiq/appliance_console/certificate_authority.rb +1 -1
- data/lib/manageiq/appliance_console/cli.rb +66 -1
- data/lib/manageiq/appliance_console/database_configuration.rb +2 -1
- data/lib/manageiq/appliance_console/database_replication.rb +1 -1
- data/lib/manageiq/appliance_console/database_replication_standby.rb +1 -1
- data/lib/manageiq/appliance_console/internal_database_configuration.rb +1 -1
- data/lib/manageiq/appliance_console/logfile_configuration.rb +2 -2
- data/lib/manageiq/appliance_console/message_configuration.rb +199 -0
- data/lib/manageiq/appliance_console/message_configuration_client.rb +96 -0
- data/lib/manageiq/appliance_console/message_configuration_server.rb +319 -0
- data/lib/manageiq/appliance_console/oidc_authentication.rb +43 -4
- data/lib/manageiq/appliance_console/postgres_admin.rb +325 -0
- data/lib/manageiq/appliance_console/utilities.rb +45 -1
- data/lib/manageiq/appliance_console/version.rb +1 -1
- data/locales/appliance/en.yml +3 -3
- data/locales/container/en.yml +3 -3
- data/manageiq-appliance_console.gemspec +6 -2
- metadata +83 -12
@@ -1,3 +1,6 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "uri"
|
3
|
+
|
1
4
|
module ManageIQ
|
2
5
|
module ApplianceConsole
|
3
6
|
class OIDCAuthentication
|
@@ -5,6 +8,10 @@ module ManageIQ
|
|
5
8
|
|
6
9
|
attr_accessor :host, :options
|
7
10
|
|
11
|
+
URL_SUFFIX = /\/\.well-known\/openid-configuration$/.freeze
|
12
|
+
INTROSPECT_SUFFIX = "/protocol/openid-connect/token/introspect".freeze
|
13
|
+
INTROSPECT_ENDPOINT_ERROR = "Unable to derive the OpenID-Connect Client Introspection Endpoint. Use --oidc-introspection-endpoint".freeze
|
14
|
+
|
8
15
|
def initialize(options)
|
9
16
|
@options = options
|
10
17
|
end
|
@@ -12,6 +19,7 @@ module ManageIQ
|
|
12
19
|
def configure(host)
|
13
20
|
@host = host
|
14
21
|
validate_oidc_options
|
22
|
+
derive_introspection_endpoint
|
15
23
|
|
16
24
|
say("Configuring OpenID-Connect Authentication for https://#{host} ...")
|
17
25
|
copy_apache_oidc_configfiles
|
@@ -52,10 +60,18 @@ module ManageIQ
|
|
52
60
|
debug_msg("Copying Apache OpenID-Connect Config files ...")
|
53
61
|
copy_template(HTTPD_CONFIG_DIRECTORY, "manageiq-remote-user-openidc.conf")
|
54
62
|
copy_template(HTTPD_CONFIG_DIRECTORY, "manageiq-external-auth-openidc.conf.erb",
|
55
|
-
:miq_appliance
|
56
|
-
:oidc_provider_metadata_url
|
57
|
-
:oidc_client_id
|
58
|
-
:oidc_client_secret
|
63
|
+
:miq_appliance => host,
|
64
|
+
:oidc_provider_metadata_url => options[:oidc_url],
|
65
|
+
:oidc_client_id => options[:oidc_client_id],
|
66
|
+
:oidc_client_secret => options[:oidc_client_secret],
|
67
|
+
:oidc_introspection_endpoint => options[:oidc_introspection_endpoint])
|
68
|
+
|
69
|
+
if options[:oidc_insecure]
|
70
|
+
File.open("#{HTTPD_CONFIG_DIRECTORY}/manageiq-external-auth-openidc.conf", "a") do |f|
|
71
|
+
f.write("\nOIDCSSLValidateServer Off\n")
|
72
|
+
f.write("OIDCOAuthSSLValidateServer Off\n")
|
73
|
+
end
|
74
|
+
end
|
59
75
|
end
|
60
76
|
|
61
77
|
def remove_apache_oidc_configfiles
|
@@ -76,6 +92,29 @@ module ManageIQ
|
|
76
92
|
raise "Must specify the OpenID-Connect Client Secret via --oidc-client-secret" if options[:oidc_client_secret].blank?
|
77
93
|
end
|
78
94
|
|
95
|
+
def derive_introspection_endpoint
|
96
|
+
return if options[:oidc_introspection_endpoint].present?
|
97
|
+
|
98
|
+
options[:oidc_introspection_endpoint] = fetch_introspection_endpoint
|
99
|
+
raise INTROSPECT_ENDPOINT_ERROR if options[:oidc_introspection_endpoint].blank?
|
100
|
+
end
|
101
|
+
|
102
|
+
def fetch_introspection_endpoint
|
103
|
+
uri = URI.parse(options[:oidc_url])
|
104
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
105
|
+
http.use_ssl = (uri.scheme == "https")
|
106
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if options[:oidc_insecure]
|
107
|
+
|
108
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
109
|
+
request.basic_auth(options[:oidc_client_id], options[:oidc_client_secret])
|
110
|
+
response = http.request(request)
|
111
|
+
|
112
|
+
JSON.parse(response.body)["introspection_endpoint"]
|
113
|
+
rescue => err
|
114
|
+
say("Failed to fetch introspection endpoint - #{err}")
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
79
118
|
# Appliance Settings
|
80
119
|
|
81
120
|
def configure_auth_settings_oidc
|
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'awesome_spawn'
|
2
|
+
require 'pathname'
|
3
|
+
require 'linux_admin'
|
4
|
+
|
5
|
+
module ManageIQ
|
6
|
+
module ApplianceConsole
|
7
|
+
class PostgresAdmin
|
8
|
+
def self.data_directory
|
9
|
+
Pathname.new(ENV.fetch("APPLIANCE_PG_DATA"))
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.mount_point
|
13
|
+
Pathname.new(ENV.fetch("APPLIANCE_PG_MOUNT_POINT"))
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.template_directory
|
17
|
+
Pathname.new(ENV.fetch("APPLIANCE_TEMPLATE_DIRECTORY"))
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.service_name
|
21
|
+
ENV.fetch("APPLIANCE_PG_SERVICE")
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.package_name
|
25
|
+
ENV.fetch('APPLIANCE_PG_PACKAGE_NAME')
|
26
|
+
end
|
27
|
+
|
28
|
+
# Unprivileged user to run postgresql
|
29
|
+
def self.user
|
30
|
+
"postgres".freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.group
|
34
|
+
user
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.logical_volume_name
|
38
|
+
"lv_pg".freeze
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.volume_group_name
|
42
|
+
"vg_data".freeze
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.database_disk_filesystem
|
46
|
+
"xfs".freeze
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.initialized?
|
50
|
+
!Dir[data_directory.join("*")].empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.service_running?
|
54
|
+
LinuxAdmin::Service.new(service_name).running?
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.local_server_in_recovery?
|
58
|
+
data_directory.join("recovery.conf").exist?
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.local_server_status
|
62
|
+
if service_running?
|
63
|
+
"running (#{local_server_in_recovery? ? "standby" : "primary"})"
|
64
|
+
elsif initialized?
|
65
|
+
"initialized and stopped"
|
66
|
+
else
|
67
|
+
"not initialized"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.logical_volume_path
|
72
|
+
Pathname.new("/dev").join(volume_group_name, logical_volume_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.database_size(opts)
|
76
|
+
result = run_command("psql", opts, :command => "SELECT pg_database_size('#{opts[:dbname]}');")
|
77
|
+
result.match(/^\s+([0-9]+)\n/)[1].to_i
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.prep_data_directory
|
81
|
+
# initdb will fail if the database directory is not empty or not owned by the PostgresAdmin.user
|
82
|
+
FileUtils.mkdir(PostgresAdmin.data_directory) unless Dir.exist?(PostgresAdmin.data_directory)
|
83
|
+
FileUtils.chown_R(PostgresAdmin.user, PostgresAdmin.group, PostgresAdmin.data_directory)
|
84
|
+
FileUtils.rm_rf(PostgresAdmin.data_directory.children.map(&:to_s))
|
85
|
+
end
|
86
|
+
|
87
|
+
PG_DUMP_MAGIC = "PGDMP".force_encoding(Encoding::BINARY).freeze
|
88
|
+
def self.pg_dump_file?(file)
|
89
|
+
File.open(file, "rb") { |f| f.readpartial(5) } == PG_DUMP_MAGIC
|
90
|
+
end
|
91
|
+
|
92
|
+
BASE_BACKUP_MAGIC = "\037\213".force_encoding(Encoding::BINARY).freeze # just the first 2 bits of gzip magic
|
93
|
+
def self.base_backup_file?(file)
|
94
|
+
File.open(file, "rb") { |f| f.readpartial(2) } == BASE_BACKUP_MAGIC
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.backup(opts)
|
98
|
+
backup_pg_compress(opts)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.restore(opts)
|
102
|
+
file = opts[:local_file]
|
103
|
+
backup_type = opts.delete(:backup_type)
|
104
|
+
|
105
|
+
case
|
106
|
+
when backup_type == :pgdump then restore_pg_dump(opts)
|
107
|
+
when backup_type == :basebackup then restore_pg_basebackup(file)
|
108
|
+
when pg_dump_file?(file) then restore_pg_dump(opts)
|
109
|
+
when base_backup_file?(file) then restore_pg_basebackup(file)
|
110
|
+
else
|
111
|
+
raise "#{file} is not a database backup"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.restore_pg_basebackup(file)
|
116
|
+
pg_service = LinuxAdmin::Service.new(service_name)
|
117
|
+
|
118
|
+
pg_service.stop
|
119
|
+
prep_data_directory
|
120
|
+
|
121
|
+
require 'rubygems/package'
|
122
|
+
|
123
|
+
# Using a Gem::Package instance for the #extract_tar_gz method, so we don't
|
124
|
+
# have to re-write all of that logic. Mostly making use of
|
125
|
+
# `Gem::Package::TarReader` + `Zlib::GzipReader` that is already part of
|
126
|
+
# rubygems/stdlib and integrated there.
|
127
|
+
unpacker = Gem::Package.new("obviously_not_a_gem")
|
128
|
+
File.open(file, IO::RDONLY | IO::NONBLOCK) do |backup_file|
|
129
|
+
unpacker.extract_tar_gz(backup_file, data_directory.to_s)
|
130
|
+
end
|
131
|
+
|
132
|
+
FileUtils.chown_R(PostgresAdmin.user, PostgresAdmin.group, PostgresAdmin.data_directory)
|
133
|
+
|
134
|
+
pg_service.start
|
135
|
+
file
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.backup_pg_dump(opts)
|
139
|
+
opts = opts.dup
|
140
|
+
dbname = opts.delete(:dbname)
|
141
|
+
|
142
|
+
args = combine_command_args(opts, :format => "c", :file => opts[:local_file], nil => dbname)
|
143
|
+
args = handle_multi_value_pg_dump_args!(opts, args)
|
144
|
+
|
145
|
+
run_command_with_logging("pg_dump", opts, args)
|
146
|
+
opts[:local_file]
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.backup_pg_compress(opts)
|
150
|
+
opts = opts.dup
|
151
|
+
|
152
|
+
# discard dbname as pg_basebackup does not connect to a specific database
|
153
|
+
opts.delete(:dbname)
|
154
|
+
|
155
|
+
path = Pathname.new(opts.delete(:local_file))
|
156
|
+
FileUtils.mkdir_p(path.dirname)
|
157
|
+
|
158
|
+
# Build commandline from AwesomeSpawn
|
159
|
+
args = {:z => nil, :format => "t", :wal_method => "fetch", :pgdata => "-"}
|
160
|
+
cmd = AwesomeSpawn.build_command_line("pg_basebackup", combine_command_args(opts, args))
|
161
|
+
logger.info("MIQ(#{name}.#{__method__}) Running command... #{cmd}")
|
162
|
+
|
163
|
+
# Run command in a separate thread
|
164
|
+
read, write = IO.pipe
|
165
|
+
error_path = Dir::Tmpname.create("") { |tmpname| tmpname }
|
166
|
+
process_thread = Process.detach(Kernel.spawn(pg_env(opts), cmd, :out => write, :err => error_path))
|
167
|
+
stream_reader = Thread.new { IO.copy_stream(read, path) } # Copy output to path
|
168
|
+
write.close
|
169
|
+
|
170
|
+
# Wait for them to finish
|
171
|
+
process_status = process_thread.value
|
172
|
+
stream_reader.join
|
173
|
+
read.close
|
174
|
+
|
175
|
+
handle_error(cmd, process_status.exitstatus, error_path)
|
176
|
+
path.to_s
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.recreate_db(opts)
|
180
|
+
dbname = opts[:dbname]
|
181
|
+
opts = opts.merge(:dbname => 'postgres')
|
182
|
+
run_command("psql", opts, :command => "DROP DATABASE IF EXISTS #{dbname}")
|
183
|
+
run_command("psql", opts, :command => "CREATE DATABASE #{dbname} WITH OWNER = #{opts[:username] || 'root'} ENCODING = 'UTF8'")
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.restore_pg_dump(opts)
|
187
|
+
recreate_db(opts)
|
188
|
+
args = { :verbose => nil, :exit_on_error => nil }
|
189
|
+
|
190
|
+
if File.pipe?(opts[:local_file])
|
191
|
+
cmd_args = combine_command_args(opts, args)
|
192
|
+
cmd = AwesomeSpawn.build_command_line("pg_restore", cmd_args)
|
193
|
+
error_path = Dir::Tmpname.create("") { |tmpname| tmpname }
|
194
|
+
spawn_args = { :err => error_path, :in => [opts[:local_file].to_s, "rb"] }
|
195
|
+
|
196
|
+
logger.info("MIQ(#{name}.#{__method__}) Running command... #{cmd}")
|
197
|
+
process_thread = Process.detach(Kernel.spawn(pg_env(opts), cmd, spawn_args))
|
198
|
+
process_status = process_thread.value
|
199
|
+
|
200
|
+
handle_error(cmd, process_status.exitstatus, error_path)
|
201
|
+
else
|
202
|
+
args[nil] = opts[:local_file]
|
203
|
+
run_command("pg_restore", opts, args)
|
204
|
+
end
|
205
|
+
opts[:local_file]
|
206
|
+
end
|
207
|
+
|
208
|
+
GC_DEFAULTS = {
|
209
|
+
:analyze => false,
|
210
|
+
:full => false,
|
211
|
+
:verbose => false,
|
212
|
+
:table => nil,
|
213
|
+
:dbname => nil,
|
214
|
+
:username => nil,
|
215
|
+
:reindex => false
|
216
|
+
}
|
217
|
+
|
218
|
+
GC_AGGRESSIVE_DEFAULTS = {
|
219
|
+
:analyze => true,
|
220
|
+
:full => true,
|
221
|
+
:verbose => false,
|
222
|
+
:table => nil,
|
223
|
+
:dbname => nil,
|
224
|
+
:username => nil,
|
225
|
+
:reindex => true
|
226
|
+
}
|
227
|
+
|
228
|
+
def self.gc(options = {})
|
229
|
+
options = (options[:aggressive] ? GC_AGGRESSIVE_DEFAULTS : GC_DEFAULTS).merge(options)
|
230
|
+
|
231
|
+
result = vacuum(options)
|
232
|
+
logger.info("MIQ(#{name}.#{__method__}) Output... #{result}") if result.to_s.length > 0
|
233
|
+
|
234
|
+
if options[:reindex]
|
235
|
+
result = reindex(options)
|
236
|
+
logger.info("MIQ(#{name}.#{__method__}) Output... #{result}") if result.to_s.length > 0
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.vacuum(opts)
|
241
|
+
# TODO: Add a real exception here
|
242
|
+
raise "Vacuum requires database" unless opts[:dbname]
|
243
|
+
|
244
|
+
args = {}
|
245
|
+
args[:analyze] = nil if opts[:analyze]
|
246
|
+
args[:full] = nil if opts[:full]
|
247
|
+
args[:verbose] = nil if opts[:verbose]
|
248
|
+
args[:table] = opts[:table] if opts[:table]
|
249
|
+
run_command("vacuumdb", opts, args)
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.reindex(opts)
|
253
|
+
args = {}
|
254
|
+
args[:table] = opts[:table] if opts[:table]
|
255
|
+
run_command("reindexdb", opts, args)
|
256
|
+
end
|
257
|
+
|
258
|
+
def self.run_command(cmd_str, opts, args)
|
259
|
+
run_command_with_logging(cmd_str, opts, combine_command_args(opts, args))
|
260
|
+
end
|
261
|
+
|
262
|
+
def self.run_command_with_logging(cmd_str, opts, params = {})
|
263
|
+
logger.info("MIQ(#{name}.#{__method__}) Running command... #{AwesomeSpawn.build_command_line(cmd_str, params)}")
|
264
|
+
AwesomeSpawn.run!(cmd_str, :params => params, :env => pg_env(opts)).output
|
265
|
+
end
|
266
|
+
|
267
|
+
class << self
|
268
|
+
# Temporary alias due to manageiq core stubbing this method
|
269
|
+
alias runcmd_with_logging run_command_with_logging
|
270
|
+
end
|
271
|
+
|
272
|
+
private_class_method def self.combine_command_args(opts, args)
|
273
|
+
default_args = {:no_password => nil}
|
274
|
+
default_args[:dbname] = opts[:dbname] if opts[:dbname]
|
275
|
+
default_args[:username] = opts[:username] if opts[:username]
|
276
|
+
default_args[:host] = opts[:hostname] if opts[:hostname]
|
277
|
+
default_args[:port] = opts[:port] if opts[:port]
|
278
|
+
default_args.merge(args)
|
279
|
+
end
|
280
|
+
|
281
|
+
private_class_method def self.logger
|
282
|
+
ManageIQ::ApplianceConsole.logger
|
283
|
+
end
|
284
|
+
|
285
|
+
private_class_method def self.pg_env(opts)
|
286
|
+
{
|
287
|
+
"PGUSER" => opts[:username],
|
288
|
+
"PGPASSWORD" => opts[:password]
|
289
|
+
}.delete_blanks
|
290
|
+
end
|
291
|
+
# rubocop:disable Style/SymbolArray
|
292
|
+
PG_DUMP_MULTI_VALUE_ARGS = [
|
293
|
+
:t, :table, :T, :exclude_table, :"exclude-table", :exclude_table_data, :"exclude-table-data",
|
294
|
+
:n, :schema, :N, :exclude_schema, :"exclude-schema"
|
295
|
+
].freeze
|
296
|
+
# rubocop:enable Style/SymbolArray
|
297
|
+
#
|
298
|
+
# NOTE: Potentially mutates opts hash (args becomes new array and not
|
299
|
+
# mutated by this method)
|
300
|
+
private_class_method def self.handle_multi_value_pg_dump_args!(opts, args)
|
301
|
+
if opts.keys.any? { |key| PG_DUMP_MULTI_VALUE_ARGS.include?(key) }
|
302
|
+
args = args.to_a
|
303
|
+
PG_DUMP_MULTI_VALUE_ARGS.each do |table_key|
|
304
|
+
next unless opts.key?(table_key)
|
305
|
+
table_val = opts.delete(table_key)
|
306
|
+
args += Array.wrap(table_val).map! { |v| [table_key, v] }
|
307
|
+
end
|
308
|
+
end
|
309
|
+
args
|
310
|
+
end
|
311
|
+
|
312
|
+
private_class_method def self.handle_error(cmd, exit_status, error_path)
|
313
|
+
if exit_status != 0
|
314
|
+
result = AwesomeSpawn::CommandResult.new(cmd, "", File.read(error_path), exit_status)
|
315
|
+
message = AwesomeSpawn::CommandResultError.default_message(cmd, exit_status)
|
316
|
+
logger.error("AwesomeSpawn: #{message}")
|
317
|
+
logger.error("AwesomeSpawn: #{result.error}")
|
318
|
+
raise AwesomeSpawn::CommandResultError.new(message, result)
|
319
|
+
end
|
320
|
+
ensure
|
321
|
+
File.delete(error_path) if File.exist?(error_path)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# TODO: add appropriate requires instead of depending on appliance_console.rb.
|
2
2
|
# TODO: Further refactor these unrelated methods.
|
3
|
-
require "
|
3
|
+
require "manageiq/appliance_console/postgres_admin"
|
4
4
|
require "awesome_spawn"
|
5
5
|
|
6
6
|
module ManageIQ
|
@@ -62,6 +62,50 @@ module ApplianceConsole
|
|
62
62
|
say(" " + h + ': ' + (Net::Ping::External.new(h).ping ? 'Success!' : 'Failure, Check network settings and IP address or hostname provided.'))
|
63
63
|
end
|
64
64
|
end
|
65
|
+
|
66
|
+
def self.disk_usage(file = nil)
|
67
|
+
file_arg = file
|
68
|
+
file_arg = "-l" if file.nil? || file == ""
|
69
|
+
|
70
|
+
unless file_arg == "-l" || File.exist?(file)
|
71
|
+
raise "file #{file} does not exist"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Collect bytes
|
75
|
+
result = AwesomeSpawn.run!("df", :params => ["-T", "-P", file_arg]).output.lines.each_with_object([]) do |line, array|
|
76
|
+
lArray = line.strip.split(" ")
|
77
|
+
next if lArray.length != 7
|
78
|
+
fsname, type, total, used, free, used_percentage, mount_point = lArray
|
79
|
+
next unless total =~ /[0-9]+/
|
80
|
+
next if array.detect { |hh| hh[:filesystem] == fsname }
|
81
|
+
|
82
|
+
array << {
|
83
|
+
:filesystem => fsname,
|
84
|
+
:type => type,
|
85
|
+
:total_bytes => total.to_i * 1024,
|
86
|
+
:used_bytes => used.to_i * 1024,
|
87
|
+
:available_bytes => free.to_i * 1024,
|
88
|
+
:used_bytes_percent => used_percentage.chomp("%").to_i,
|
89
|
+
:mount_point => mount_point,
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
# Collect inodes
|
94
|
+
AwesomeSpawn.run!("df", :params => ["-T", "-P", "-i", file_arg]).output.lines.each do |line|
|
95
|
+
lArray = line.strip.split(" ")
|
96
|
+
next if lArray.length != 7
|
97
|
+
fsname, _type, total, used, free, used_percentage, _mount_point = lArray
|
98
|
+
next unless total =~ /[0-9]+/
|
99
|
+
h = result.detect { |hh| hh[:filesystem] == fsname }
|
100
|
+
next if h.nil?
|
101
|
+
|
102
|
+
h[:total_inodes] = total.to_i
|
103
|
+
h[:used_inodes] = used.to_i
|
104
|
+
h[:available_inodes] = free.to_i
|
105
|
+
h[:used_inodes_percent] = used_percentage.chomp("%").to_i
|
106
|
+
end
|
107
|
+
result
|
108
|
+
end
|
65
109
|
end
|
66
110
|
end
|
67
111
|
end
|