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/(?(.|\n)*)\n(?\n\s(.|\n)*)/ @version_regex ~r/version: (?(?\d+)\.(?\d+)\.(?\d))/ @resource_split_regex ~r{(\n\s(\d+)\:\s)} @id_extraction_regex ~r/\n\s(?\d+)\:\s/ @data_extraction_regex ~r/cs:(?(\w|\/)+)\sro:(?(\w|\/)+)\sds:(?(\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