75 lines
2.7 KiB
Elixir
75 lines
2.7 KiB
Elixir
|
defmodule HAHandler.DRBD do
|
||
|
@supervisor HAHandler.DRBD.Supervisor
|
||
|
|
||
|
# There might be >1 resources configured in DRBD!
|
||
|
@default_resource_id "1"
|
||
|
|
||
|
# We don't support DRBD 9 for the time being, as /proc/drbd does not have a
|
||
|
# stable API.
|
||
|
@supported_drbd_major_version "8"
|
||
|
|
||
|
# Parsing of /proc/drbd, assuming DRBD 8. Splitting the regexes helps humans
|
||
|
# wrapping their head around what's going on. And yes, it's fragile: we need
|
||
|
# drbd 9 to get a JSON interface to `drbdadm status`.
|
||
|
@drbd_proc_cmd "cat /proc/drbd"
|
||
|
@block_regex ~r/(?<version_block>(.|\n)*)\n(?<resource_block>\n\s(.|\n)*)/
|
||
|
@version_regex ~r/version: (?<full>(?<major>\d+)\.(?<intermediate>\d+)\.(?<minor>\d))/
|
||
|
@resource_split_regex ~r{(\n\s(\d+)\:\s)}
|
||
|
@id_extraction_regex ~r/\n\s(?<id>\d+)\:\s/
|
||
|
@data_extraction_regex ~r/cs:(?<cs>(\w|\/)+)\sro:(?<ro>(\w|\/)+)\sds:(?<ds>(\w|\/)+)\s/
|
||
|
|
||
|
def get_instances() do
|
||
|
watchers = Supervisor.which_children(@supervisor)
|
||
|
|
||
|
for {hostname, pid, _type, _modules} <- watchers do
|
||
|
{hostname, pid}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def get_stats() do
|
||
|
get_instances()
|
||
|
|> Enum.map(fn instance -> get_state(instance) end)
|
||
|
end
|
||
|
|
||
|
def get_state({hostname, pid}) do
|
||
|
case GenServer.call(pid, {:execute, @drbd_proc_cmd}) do
|
||
|
{:ok, raw, 0} ->
|
||
|
case Regex.named_captures(@block_regex, raw) do
|
||
|
%{"version_block" => version_block, "resource_block" => resource_block} ->
|
||
|
version = Regex.named_captures(@version_regex, version_block)
|
||
|
|
||
|
if Map.get(version, "major") != @supported_drbd_major_version do
|
||
|
{:error, "unsupported DRBD version #{inspect(version)}"}
|
||
|
else
|
||
|
resources = Regex.split( @resource_split_regex, resource_block,
|
||
|
[include_captures: true, trim: true])
|
||
|
|> Enum.chunk_every(2)
|
||
|
|> Enum.map(fn [raw_id, raw_data] ->
|
||
|
%{}
|
||
|
|> Map.merge(Regex.named_captures(@id_extraction_regex, raw_id))
|
||
|
|> Map.merge(Regex.named_captures(@data_extraction_regex, raw_data))
|
||
|
end)
|
||
|
|
||
|
default_resource = resources
|
||
|
|> Enum.filter(fn r -> r["id"] == @default_resource_id end)
|
||
|
|> Enum.at(0)
|
||
|
|
||
|
%{
|
||
|
hostname: hostname,
|
||
|
version: Map.get(version, "full"),
|
||
|
mode: Map.get(default_resource, "ro"),
|
||
|
status: Map.get(default_resource, "ds"),
|
||
|
data: resources
|
||
|
}
|
||
|
end
|
||
|
_ ->
|
||
|
{:error, "could not parse /proc/drbd"}
|
||
|
end
|
||
|
{:ok, _, posix_err} ->
|
||
|
{:error, posix_err}
|
||
|
{:error, _err} = reply ->
|
||
|
reply
|
||
|
end
|
||
|
end
|
||
|
end
|