renamed to lxc-exporter. os-release, apt update / upgradable.
This commit is contained in:
parent
d623efde64
commit
e225cc96db
|
@ -1,12 +1,12 @@
|
||||||
require 'rack'
|
require 'rack'
|
||||||
require_relative 'pvect-exporter'
|
require_relative 'lxc-exporter'
|
||||||
require 'prometheus/client/formats/text'
|
require 'prometheus/client/formats/text'
|
||||||
|
|
||||||
run lambda {|env|
|
run lambda {|env|
|
||||||
req = Rack::Request.new env
|
req = Rack::Request.new env
|
||||||
case req.path
|
case req.path
|
||||||
when "/metrics"
|
when "/metrics"
|
||||||
collector = PveCtCollector.new
|
collector = LxcCollector.new
|
||||||
[200, {"Content-Type" => "text/plain"}, [Prometheus::Client::Formats::Text.marshal( collector.collect)]]
|
[200, {"Content-Type" => "text/plain"}, [Prometheus::Client::Formats::Text.marshal( collector.collect)]]
|
||||||
else
|
else
|
||||||
[404, {"Content-Type" => "text/plain"}, ["Not found\nYou want to try /metrics?\n"]]
|
[404, {"Content-Type" => "text/plain"}, ["Not found\nYou want to try /metrics?\n"]]
|
||||||
|
|
183
pvect-exporter.rb → lxc-exporter.rb
Normal file → Executable file
183
pvect-exporter.rb → lxc-exporter.rb
Normal file → Executable file
|
@ -26,7 +26,6 @@ end
|
||||||
class File
|
class File
|
||||||
module Statfs
|
module Statfs
|
||||||
Magics = {}
|
Magics = {}
|
||||||
#=begin
|
|
||||||
Line = <<~EOF
|
Line = <<~EOF
|
||||||
ADFS 0xadf5
|
ADFS 0xadf5
|
||||||
AFFS 0xadff
|
AFFS 0xadff
|
||||||
|
@ -121,7 +120,6 @@ class File
|
||||||
end
|
end
|
||||||
Magics[v] = n.downcase.to_sym
|
Magics[v] = n.downcase.to_sym
|
||||||
end
|
end
|
||||||
#=end
|
|
||||||
|
|
||||||
extend Fiddle::Importer
|
extend Fiddle::Importer
|
||||||
dlload Fiddle.dlopen( nil)
|
dlload Fiddle.dlopen( nil)
|
||||||
|
@ -164,17 +162,12 @@ class File
|
||||||
end
|
end
|
||||||
|
|
||||||
class NS
|
class NS
|
||||||
#extend FFI::Library
|
|
||||||
#ffi_lib 'c'
|
|
||||||
#attach_function :setns_without_exception_handling, :setns, %i[int int], :int
|
|
||||||
extend Fiddle::Importer
|
extend Fiddle::Importer
|
||||||
dlload Fiddle.dlopen( nil)
|
dlload Fiddle.dlopen( nil)
|
||||||
SetNS_Function = Fiddle::Function.new handler['setns'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT
|
SetNS_Function = Fiddle::Function.new handler['setns'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT
|
||||||
|
|
||||||
class <<self
|
class <<self
|
||||||
def setns fd, t
|
def setns fd, t
|
||||||
#r = setns_without_exception_handling fd.to_i, t.to_i
|
|
||||||
#SystemCallError.new "setns(#{fd.to_i})", FFI::LastError.error if -1 == r
|
|
||||||
r = SetNS_Function.call fd.to_i, t.to_i
|
r = SetNS_Function.call fd.to_i, t.to_i
|
||||||
SystemCallError.new "setns(#{fd.to_i})", Fiddle.last_error if -1 == r
|
SystemCallError.new "setns(#{fd.to_i})", Fiddle.last_error if -1 == r
|
||||||
r
|
r
|
||||||
|
@ -247,71 +240,144 @@ class MountInfo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin
|
class OsRelease
|
||||||
LxcStartExe = Pathname.new "/usr/bin/lxc-start"
|
Path = Pathname.new '/etc/os-release'
|
||||||
def get_running_ct_pids &block
|
class <<self
|
||||||
return to_enum( __method__) unless block_given?
|
def parse txt
|
||||||
|
r =
|
||||||
cts = []
|
txt.each_line.
|
||||||
Pathname.new( "/proc").each_child do |fn|
|
map do |l|
|
||||||
next if /\D/ =~ fn.basename.to_s
|
k, v =
|
||||||
exepath = fn + 'exe'
|
*case l.chomp
|
||||||
next unless exepath.exist? and LxcStartExe == exepath.readlink
|
when /^([^=]+)="(.*)"$/ then [$1, $2]
|
||||||
pid = fn.basename.to_s.to_i
|
when /^([^=]+)=(.*)$/ then [$1,$2]
|
||||||
cts.push pid
|
else next
|
||||||
|
end
|
||||||
|
[ k.downcase.to_sym, v.sub( /^"/, '').sub( /"$/, '') ]
|
||||||
|
end.
|
||||||
|
compact.
|
||||||
|
to_h
|
||||||
|
new r
|
||||||
end
|
end
|
||||||
|
|
||||||
Pathname.new( "/proc").each_child do |fn|
|
def read() parse Path.read if Path.exist? end
|
||||||
next if /\D/ =~ fn.basename.to_s
|
end
|
||||||
statuspath = fn + 'status'
|
|
||||||
ppid = statuspath.read.match( /^PPid:\s+(\d+)$/)[1].to_i
|
def initialize( h) @h = h.freeze end
|
||||||
next unless cts.include? ppid
|
def to_h() @h.dup end
|
||||||
yield fn.basename.to_s.to_i
|
def []( n) @h[n] end
|
||||||
|
|
||||||
|
def missing_method n, *a
|
||||||
|
if a.empty? and @h.has_key?( n)
|
||||||
|
@h[n]
|
||||||
|
else
|
||||||
|
super n, *a
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
pids = get_running_ct_pids
|
|
||||||
=end
|
|
||||||
|
|
||||||
class PveCtCollector
|
class LxcCollector
|
||||||
def fetch
|
def statfs ct, mis
|
||||||
LXC.list_containers.map {|n|LXC::Container.new n}.select( &:running?).each do |ct|
|
|
||||||
pid = ct.init_pid
|
|
||||||
mis = MountInfo.of pid
|
|
||||||
NS.change pid, :pid, :mnt do
|
|
||||||
mis.each do |mi|
|
mis.each do |mi|
|
||||||
next if %w[devpts devtmpfs fuse.lxcfs].include? mi.type
|
next if %w[devpts devtmpfs fuse.lxcfs].include? mi.type
|
||||||
statfs = File::Statfs.new mi.mp
|
statfs = File::Statfs.new mi.mp
|
||||||
yield ct, mi.mp, statfs
|
yield ct, mi.mp, statfs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize registry = nil
|
def initialize registry = nil
|
||||||
@prometheus ||= Prometheus::Client::Registry.new
|
@prometheus ||= Prometheus::Client::Registry.new
|
||||||
@info = Prometheus::Client::Gauge.new :pvect_filesystem_info, labels: %i[id mountpoint name fstype], docstring: 'CT Info'
|
@info = Prometheus::Client::Gauge.new :lxc_filesystem_info,
|
||||||
@totalb = Prometheus::Client::Gauge.new :pvect_filesystem_total_bytes, labels: %i[id mountpoint], docstring: 'Total bytes in filesystem'
|
labels: %i[id mountpoint name fstype], docstring: 'CT Info'
|
||||||
@freeb = Prometheus::Client::Gauge.new :pvect_filesystem_free_bytes, labels: %i[id mountpoint], docstring: 'Free bytes in filesystem'
|
@totalb = Prometheus::Client::Gauge.new :lxc_filesystem_total_bytes,
|
||||||
@availb = Prometheus::Client::Gauge.new :pvect_filesystem_available_bytes, labels: %i[id mountpoint], docstring: 'Free bytes available to unprivileged user'
|
labels: %i[id mountpoint], docstring: 'Total bytes in filesystem'
|
||||||
@totalf = Prometheus::Client::Gauge.new :pvect_filesystem_total_files, labels: %i[id mountpoint], docstring: 'Total files in filesystem'
|
@freeb = Prometheus::Client::Gauge.new :lxc_filesystem_free_bytes,
|
||||||
@freef = Prometheus::Client::Gauge.new :pvect_filesystem_free_files, labels: %i[id mountpoint], docstring: 'Free files in filesystem'
|
labels: %i[id mountpoint], docstring: 'Free bytes in filesystem'
|
||||||
[@info, @totalb, @freeb, @availb, @totalf, @freef].each {|g| @prometheus.register g }
|
@availb = Prometheus::Client::Gauge.new :lxc_filesystem_available_bytes,
|
||||||
|
labels: %i[id mountpoint], docstring: 'Free bytes available to unprivileged user'
|
||||||
|
@totalf = Prometheus::Client::Gauge.new :lxc_filesystem_total_files,
|
||||||
|
labels: %i[id mountpoint], docstring: 'Total files in filesystem'
|
||||||
|
@freef = Prometheus::Client::Gauge.new :lxc_filesystem_free_files,
|
||||||
|
labels: %i[id mountpoint], docstring: 'Free files in filesystem'
|
||||||
|
@os_release = Prometheus::Client::Gauge.new :lxc_os_release_info,
|
||||||
|
labels: %i[id name os_id os_name os_pretty_name os_version_codename],
|
||||||
|
docstring: 'OS release info'
|
||||||
|
@os_version = Prometheus::Client::Gauge.new :lxc_os_version_id,
|
||||||
|
labels: %i[id], docstring: 'OS release (distribution) version'
|
||||||
|
@pkgs_upgradable = Prometheus::Client::Gauge.new :lxc_packages_upgradable,
|
||||||
|
labels: %i[id], docstring: 'Count of upgradable packages'
|
||||||
|
@pkgs_last_update = Prometheus::Client::Gauge.new :lxc_packages_last_update,
|
||||||
|
labels: %i[id], docstring: 'Last successfull source update'
|
||||||
|
[
|
||||||
|
@info, @totalb, @freeb, @availb, @totalf, @freef,
|
||||||
|
@os_release, @os_version,
|
||||||
|
@pkgs_upgradable, @pkgs_last_update
|
||||||
|
].each {|g| @prometheus.register g }
|
||||||
|
end
|
||||||
|
|
||||||
|
def running_containers &exe
|
||||||
|
return to_enum __method__ unless block_given?
|
||||||
|
LXC.
|
||||||
|
list_containers.
|
||||||
|
map {|n| LXC::Container.new n }.
|
||||||
|
select( &:running?).
|
||||||
|
each &exe
|
||||||
|
end
|
||||||
|
|
||||||
|
AptLastUpdateFile = Pathname.new '/var/cache/apt/pkgcache.bin'
|
||||||
|
|
||||||
|
def forked wr
|
||||||
|
running_containers do |ct|
|
||||||
|
pid = ct.init_pid
|
||||||
|
mis = MountInfo.of pid
|
||||||
|
NS.change pid, :pid, :mnt do
|
||||||
|
name = ct.config_item( 'lxc.uts.name')
|
||||||
|
statfs ct, mis do |ct, mp, st|
|
||||||
|
next unless st.fstypename and 0 < st.blocks
|
||||||
|
st = st.to_h.merge mp: mp, id: ct.name, name: name
|
||||||
|
wr.puts [:mountpoint, st].to_json
|
||||||
|
end
|
||||||
|
osr = OsRelease.read
|
||||||
|
if osr
|
||||||
|
wr.puts [:os_release, {
|
||||||
|
id: ct.name, name: name,
|
||||||
|
os_id: osr[:id],
|
||||||
|
os_name: osr[:name],
|
||||||
|
os_pretty_name: osr[:pretty_name],
|
||||||
|
os_version_id: osr[:version_id].to_i,
|
||||||
|
os_version_codename: osr[:version_codename]
|
||||||
|
}].to_json
|
||||||
|
case osr[:id].to_sym
|
||||||
|
when :debian
|
||||||
|
upgradable = nil
|
||||||
|
pid =
|
||||||
|
IO.popen %w[apt list --upgradable], err: "/dev/null" do |apt|
|
||||||
|
upgradable = apt.readlines.length - 1
|
||||||
|
end
|
||||||
|
wr.puts [:pkgs, {
|
||||||
|
id: ct.name, name: name, upgradable: upgradable,
|
||||||
|
last_update: AptLastUpdateFile.stat.mtime.to_f,
|
||||||
|
}].to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
wr.close
|
||||||
|
exit 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def collect
|
def collect
|
||||||
rd, wr = IO.pipe
|
rd, wr = IO.pipe
|
||||||
pid = fork do
|
pid = fork do
|
||||||
rd.close
|
rd.close
|
||||||
fetch do |ct, mp, st|
|
forked wr
|
||||||
next unless st.fstypename and 0 < st.blocks
|
|
||||||
wr.puts st.to_h.merge( mp: mp, id: ct.name, name: ct.config_item( 'lxc.uts.name')).to_json
|
|
||||||
end
|
|
||||||
wr.close
|
|
||||||
exit 0
|
|
||||||
end
|
end
|
||||||
wr.close
|
wr.close
|
||||||
rd.each_line do |l|
|
rd.each_line do |l|
|
||||||
st = OpenStruct.new JSON.parse( l)
|
l = JSON.parse l, symbolize_names: true
|
||||||
|
case l[0].to_sym
|
||||||
|
when :mountpoint
|
||||||
|
st = OpenStruct.new l[1]
|
||||||
labels = {id: st.id, mountpoint: st.mp}
|
labels = {id: st.id, mountpoint: st.mp}
|
||||||
@info.set 1, labels: labels.merge( name: st.name, fstype: st.fstypename)
|
@info.set 1, labels: labels.merge( name: st.name, fstype: st.fstypename)
|
||||||
@totalb.set st.bsize*st.blocks, labels: labels
|
@totalb.set st.bsize*st.blocks, labels: labels
|
||||||
|
@ -319,9 +385,28 @@ class PveCtCollector
|
||||||
@availb.set st.bsize*st.bavail, labels: labels
|
@availb.set st.bsize*st.bavail, labels: labels
|
||||||
@totalf.set st.files, labels: labels
|
@totalf.set st.files, labels: labels
|
||||||
@freef.set st.ffree, labels: labels
|
@freef.set st.ffree, labels: labels
|
||||||
|
when :os_release
|
||||||
|
osr = OpenStruct.new l[1]
|
||||||
|
@os_release.set 1, labels: { id: osr.id, name: osr.name,
|
||||||
|
os_id: osr.os_id,
|
||||||
|
os_name: osr.os_name,
|
||||||
|
os_pretty_name: osr.os_pretty_name,
|
||||||
|
os_version_codename: osr.os_version_codename
|
||||||
|
}
|
||||||
|
@os_version.set osr.os_version_id, labels: {id: osr.id}
|
||||||
|
when :pkgs
|
||||||
|
as = OpenStruct.new l[1]
|
||||||
|
@pkgs_last_update.set as.last_update, labels: { id: as.id }
|
||||||
|
@pkgs_upgradable.set as.upgradable, labels: { id: as.id }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rd.close
|
rd.close
|
||||||
Process.wait pid
|
Process.wait pid
|
||||||
@prometheus
|
@prometheus
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if Pathname.new( __FILE__).expand_path == Pathname.new( $0).expand_path
|
||||||
|
require 'prometheus/client/formats/text'
|
||||||
|
puts Prometheus::Client::Formats::Text.marshal( LxcCollector.new.collect)
|
||||||
|
end
|
Loading…
Reference in a new issue