Current section
Files
Jump to
Current section
Files
lib/cmdb-profiles/cmdb_profiles.ex
defmodule CA.PRO do
@moduledoc """
Функції доступу до інвентарів судів для КСЗІ (cmdb-profile).
Призначений для простого й ідіоматичного пошуку категорій та конкретних серійних номерів (інвентарних номерів)
по всіх профілях без використання макросів.
"""
@inventory_modules [
{CA.HW, :erp, :hw},
{CA.Sys, :erp, :sys}
]
@process_modules [
{CA.Proc, :erp}
]
@data_modules [
{CA.Data, :erp}
]
# --- API ДЛЯ КАТЕГОРІЙ ІНВЕНТАРЯ (HW / SYS) ---
@doc """
Отримати всі категорії інвентаря по всіх судах та профілях.
Додає метадані: `:court`, `:inventory_type`, `:category_key`.
"""
def list_categories do
# 1. Base Inventory Modules (HW / Sys)
inventory_items =
for {mod, court, type} <- @inventory_modules,
{category_key, items} <- mod.inventory(),
item <- items do
item
|> Map.put(:court, court)
|> Map.put(:inventory_type, type)
|> Map.put(:category_key, category_key)
end
# 2. Net Modules (zones)
net_items =
for {mod, court} <- [
{CA.Net, :erp}
],
{category_key, zones} <- mod.zones(),
zone <- zones do
zone
|> Map.put(:court, court)
|> Map.put(:inventory_type, :net)
|> Map.put(:category_key, category_key)
end
# 3. Risk Modules (taxonomy)
risk_items =
for {mod, court} <- [
{CA.Risk, :erp}
],
{category_key, risks} <- mod.taxonomy(),
risk <- risks do
risk
|> Map.put(:court, court)
|> Map.put(:inventory_type, :risk)
|> Map.put(:category_key, category_key)
end
# 4. Data Modules (classification)
data_items =
for {mod, court} <- [
{CA.Data, :erp}
],
{category_key, classifications} <- mod.classification(),
data <- classifications do
data
|> Map.put(:court, court)
|> Map.put(:inventory_type, :data)
|> Map.put(:category_key, category_key)
end
# 5. ABAC Modules (roles)
abac_items =
for {mod, court} <- [
{CA.ABAC, :erp}
],
{category_key, roles} <- mod.roles(),
role <- roles do
role
|> Map.put(:court, court)
|> Map.put(:inventory_type, :abac)
|> Map.put(:category_key, category_key)
end
# 6. Process Modules (processes)
process_items =
for {mod, court} <- @process_modules,
{category_key, processes} <- mod.processes(),
process <- processes do
process
|> Map.put(:court, court)
|> Map.put(:inventory_type, :process)
|> Map.put(:category_key, category_key)
end
inventory_items ++ net_items ++ risk_items ++ data_items ++ abac_items ++ process_items
end
@doc """
Отримати конкретну категорію за її ID (наприклад, "HW-SRV-01" або "SYS-OS-01").
"""
def get_category(category_id) do
list_categories()
|> Enum.find(fn cat -> cat.id == category_id end)
end
@doc """
Пошук категорій за довільними фільтрами (map, keyword list або функція-предикат).
"""
def search_categories(filters) do
list_categories()
|> Enum.filter(&matches_filters?(&1, filters))
end
# --- API ДЛЯ КОНКРЕТНИХ ЕКЗЕМПЛЯРІВ (INSTANCES) ---
@doc """
Отримати всі конкретні екземпляри обладнання/активів (instances) по всіх категоріях.
Додає метадані: `:court`, `:inventory_type`, `:category_id`, `:category_key`.
"""
def list_instances do
for cat <- list_categories(),
instance <- Map.get(cat, :instances, []) do
instance
|> Map.put(:court, cat.court)
|> Map.put(:inventory_type, cat.inventory_type)
|> Map.put(:category_id, cat.id)
|> Map.put(:category_key, cat.category_key)
end
end
@doc """
Отримати конкретний екземпляр за його ID (наприклад, "COD-SRV-01") або
інвентарним/серійним номером (наприклад, "47850001", "101890577").
"""
def get_instance(id_or_serial) do
list_instances()
|> Enum.find(fn inst ->
inst.id == id_or_serial or Map.get(inst, :inventory_num) == id_or_serial
end)
end
@doc """
Пошук екземплярів за довільними фільтрами (map, keyword list або функція-предикат).
"""
def search_instances(filters) do
list_instances()
|> Enum.filter(&matches_filters?(&1, filters))
end
# --- API ДЛЯ БІЗНЕС-ПРОЦЕСІВ ---
@doc """
Отримати всі процеси по всіх судах.
"""
def list_processes do
for {mod, court} <- @process_modules,
{process_key, processes} <- mod.processes(),
process <- processes do
process
|> Map.put(:court, court)
|> Map.put(:process_key, process_key)
end
end
@doc """
Отримати процес за його ID (наприклад, "P-DOC").
"""
def get_process(process_id) do
list_processes()
|> Enum.find(fn proc -> proc.id == process_id end)
end
@doc """
Пошук процесів за фільтрами.
"""
def search_processes(filters) do
list_processes()
|> Enum.filter(&matches_filters?(&1, filters))
end
# --- API ДЛЯ КЛАСИФІКАЦІЇ ДАНИХ ---
@doc """
Отримати всі класифікації даних по всіх судах.
"""
def list_classifications do
for {mod, court} <- @data_modules,
{class_key, classifications} <- mod.classification(),
class <- classifications do
class
|> Map.put(:court, court)
|> Map.put(:classification_key, class_key)
end
end
@doc """
Отримати класифікацію за її ID (наприклад, "D-PUB").
"""
def get_classification(class_id) do
list_classifications()
|> Enum.find(fn class -> class.id == class_id end)
end
@doc """
Пошук класифікацій за фільтрами.
"""
def search_classifications(filters) do
list_classifications()
|> Enum.filter(&matches_filters?(&1, filters))
end
# --- ЗРУЧНИЙ ВИСВІТЛЕННЯ ДЛЯ IEx (З АВТОШИРИНОЮ ТЕРМІНАЛУ) ---
@doc """
Отримати всі категорії для COURT або COD у вигляді Erlang-кортежів,
що автоналаштовують свою ширину під ширину терміналу IEx для зручного читання.
"""
def categories(court_str) do
court = parse_court(court_str)
width = terminal_width()
# overhead for print formatting: `{"", "", []}` -> ~14 chars
overhead = 14
for cat <- search_categories(court: court) do
id = cat.id
controls_str = Enum.join(cat.controls, ",")
name_width = max(10, width - String.length(id) - String.length(controls_str) - overhead)
name = format_string(cat.name, name_width)
{id, name, cat.controls}
end
end
@doc """
Отримати фізичний HW-інвентар (instances) для COURT, COD або ERP.
Показує лише апаратні екземпляри з полями: id, inventory_num, model, location, status.
"""
def inventory(court_str) do
court = parse_court(court_str)
width = terminal_width()
# overhead for print formatting: `{"", "", "", "", ""}` -> ~18 chars
overhead = 18
hw_mod = case court do
:erp -> CA.HW
end
for {_key, groups} <- hw_mod.inventory(),
group <- groups,
inst <- Map.get(group, :instances, []) do
id = inst.id
inv_num = Map.get(inst, :inventory_num, "")
model_raw = Map.get(inst, :model, Map.get(inst, :name, ""))
used_width = String.length(id) + String.length(inv_num) + overhead
model_width = max(15, width - used_width)
model = format_string(model_raw, model_width)
{id, inv_num, model}
end
end
@doc """
Отримати всі ризики для COURT або COD у вигляді Erlang-кортежів.
"""
def risk(court_str) do
court = parse_court(court_str)
width = terminal_width()
risk_mod = case court do
:erp -> CA.Risk
end
# overhead for print formatting: `{"", "", []}` -> ~14 chars
overhead = 14
for {_key, groups} <- risk_mod.taxonomy(),
group <- groups,
inst <- Map.get(group, :instances, []) do
id = inst.id
controls_str = Enum.join(Map.get(inst, :controls, []), ",")
name_raw = Map.get(inst, :name, "")
used_width = String.length(id) + String.length(controls_str) + overhead
name_width = max(10, width - used_width)
name = format_string(name_raw, name_width)
{id, name, Map.get(inst, :controls, [])}
end
end
@doc """
Отримати системний софт (SYS інвентар) для COURT або COD у вигляді Erlang-кортежів.
"""
def sys(court_str) do
court = parse_court(court_str)
width = terminal_width()
# overhead for print formatting: `{"", "", []}` -> ~14 chars
overhead = 14
sys_mod = case court do
:erp -> CA.Sys
end
for {_key, groups} <- sys_mod.inventory(),
group <- groups,
inst <- Map.get(group, :instances, []) do
id = inst.id
controls_str = Enum.join(Map.get(inst, :controls, []), ",")
version_suffix =
case Map.get(inst, :version) do
nil -> ""
"" -> ""
"N/A" -> ""
version -> " (" <> version <> ")"
end
name_raw = Map.get(inst, :name, "") <> version_suffix
used_width = String.length(id) + String.length(controls_str) + overhead
name_width = max(10, width - used_width)
name = format_string(name_raw, name_width)
{id, name, Map.get(inst, :controls, [])}
end
end
@doc """
Отримати мережеві інстанси (NET) для COURT або COD у вигляді Erlang-кортежів.
Розгортає :instances всередині кожної зони (аналогічно до inventory/1).
"""
def net(court_str) do
court = parse_court(court_str)
width = terminal_width()
# overhead: {id, name, subnet} -> ~16 chars
overhead = 16
net_mod = case court do
:erp -> CA.Net
end
for {_key, zones} <- net_mod.zones(),
zone <- zones,
inst <- Map.get(zone, :instances, []) do
id = inst.id
subnet = Map.get(inst, :subnet, "")
name_raw = Map.get(inst, :name, "")
used_width = String.length(id) + String.length(subnet) + overhead
name_width = max(10, width - used_width)
name = format_string(name_raw, name_width)
{id, name, subnet}
end
end
@doc """
Отримати бізнес-процеси (PROC) для COURT або COD — розгортає :instances кожної групи.
"""
def proc(court_str) do
court = parse_court(court_str)
width = terminal_width()
overhead = 16
proc_mod = case court do
:erp -> CA.Proc
end
for {_key, groups} <- proc_mod.processes(),
group <- groups,
inst <- Map.get(group, :instances, []) do
id = inst.id
owner = Map.get(inst, :owner, "")
name_raw = Map.get(inst, :name, "")
used_width = String.length(id) + String.length(owner) + overhead
name_width = max(10, width - used_width)
name = format_string(name_raw, name_width)
{id, name, owner}
end
end
@doc """
Отримати класифікацію даних (DATA) для COURT або COD — розгортає :instances кожної групи.
"""
def data(court_str) do
court = parse_court(court_str)
width = terminal_width()
overhead = 16
data_mod = case court do
:erp -> CA.Data
end
for {_key, groups} <- data_mod.classification(),
group <- groups,
inst <- Map.get(group, :instances, []) do
id = inst.id
storage = Map.get(inst, :storage, "")
name_raw = Map.get(inst, :name, "")
used_width = String.length(id) + String.length(storage) + overhead
name_width = max(10, width - used_width)
name = format_string(name_raw, name_width)
{id, name, storage}
end
end
@doc """
Отримати ролі доступу (ROLES) для COURT або COD — розгортає :instances кожної групи.
"""
def roles(court_str) do
court = parse_court(court_str)
width = terminal_width()
overhead = 16
abac_mod = case court do
:erp -> CA.ABAC
end
for {_key, groups} <- abac_mod.roles(),
group <- groups,
inst <- Map.get(group, :instances, []) do
id = inst.id
users_str = Map.get(inst, :users, []) |> Enum.join(", ")
name_raw = Map.get(inst, :name, "")
used_width = String.length(id) + String.length(users_str) + overhead
name_width = max(10, width - used_width)
name = format_string(name_raw, name_width)
{id, name, Map.get(inst, :users, [])}
end
end
# --- ВНУТРІШНІ ДОПОМІЖНІ ФУНКЦІЇ ФОРМАТУВАННЯ ---
defp parse_court(court) when court in [:cod, :vyshhorod, :erp], do: court
defp parse_court(court) when is_binary(court) do
case String.downcase(court) do
"cod" -> :cod
"court" -> :vyshhorod
"vyshhorod" -> :vyshhorod
"erp" -> :erp
_ -> raise ArgumentError, "Unknown court: #{inspect(court)}. Expected 'COD', 'COURT' or 'ERP'"
end
end
def terminal_width do
width = case :io.columns() do
{:ok, w} when w > 0 -> w + 50
_ -> 200
end
if Process.whereis(IEx.Config) do
IEx.configure(
inspect: [
limit: :infinity,
printable_limit: :infinity,
width: width
],
colors: [
eval_result: [:cyan, :bright],
eval_info: [:yellow],
eval_error: [:red, :bright]
]
)
end
width
end
defp format_string(str, max_len) do
str = String.replace(str, "\n", " ") |> String.trim()
if String.length(str) > max_len do
len = max(0, max_len - 3)
String.slice(str, 0, len) <> "..."
else
str
end
end
# --- ДОПОМІЖНІ ФУНКЦІЇ ФІЛЬТРАЦІЇ ---
# Фільтрація за допомогою анонімної функції-предиката
defp matches_filters?(item, filters) when is_function(filters, 1) do
filters.(item)
end
# Фільтрація за списком пар ключ-значення (Map або Keyword list)
defp matches_filters?(item, filters) when is_list(filters) or is_map(filters) do
Enum.all?(filters, fn {key, val} ->
case Map.fetch(item, key) do
{:ok, item_val} -> match_value?(item_val, val)
_ -> false
end
end)
end
defp match_value?(item_val, val) when is_list(item_val) do
if is_list(val) do
Enum.all?(val, &(&1 in item_val))
else
val in item_val
end
end
defp match_value?(item_val, val), do: item_val == val
end