Рецепты¶
Готовые куски кода под частые задачи. Все примеры предполагают, что сессия уже создана:
import raclib as rc
from raclib import errors
session = rc.Session(
rc.Client("/opt/1cv8/x86_64/8.3.24.1548/rac"),
host="cluster.example.com",
timeout=30,
)
cluster = rc.Cluster.list(session)[0]["cluster"]
Вывести все базы с числом сеансов¶
summary = rc.Infobase.Summary.list(session, cluster)
summary.sort(key=lambda item: int(item.get("session-count", 0)), reverse=True)
for item in summary:
print(f'{item["name"]:<30} сеансов: {item.get("session-count", 0)}')
Завершить сеансы конкретного пользователя¶
user = "ivanov"
for item in rc.UserSession.list(session, cluster):
if item.get("user-name") != user:
continue
rc.UserSession.terminate(
session,
cluster,
item["session"],
error_message="Ваш сеанс завершён администратором",
)
print("завершён сеанс", item["session"])
Найти виновника блокировок¶
locks = rc.Lock.list(session, cluster)
# Группируем блокировки по сеансу, который их удерживает.
by_session: dict[str, list[dict]] = {}
for lock in locks:
by_session.setdefault(str(lock["session"]), []).append(lock)
for user_session, items in sorted(by_session.items(), key=lambda kv: -len(kv[1])):
print(f"сеанс {user_session}: блокировок {len(items)}")
Разорвать все соединения базы¶
Полезно перед выводом базы на обслуживание.
infobase_id = rc.Infobase.info(session, cluster, name="TestIB")["infobase"]
for server in rc.Server.list(session, cluster):
for process in rc.Process.list(session, cluster, server["server"]):
connections = rc.Connection.list(
session, cluster, process=process["process"], infobase=infobase_id
)
for connection in connections:
rc.Connection.disconnect(
session, cluster, process["process"], connection["connection"]
)
print("разорвано соединение", connection["connection"])
Вывести базу на обслуживание¶
Последовательность, которая не даст пользователям «зайти посередине»: сначала запрещаем новые сеансы, затем завершаем существующие.
infobase_id = rc.Infobase.info(session, cluster, name="TestIB")["infobase"]
# 1. Запретить новые сеансы и регламентные задания
rc.Infobase.update(
session,
cluster,
infobase_id,
sessions_deny=True,
scheduled_jobs_deny=True,
descr="Обслуживание до 04:00",
)
# 2. Завершить текущие сеансы
for item in rc.UserSession.list(session, cluster, infobase=infobase_id):
rc.UserSession.terminate(
session, cluster, item["session"], error_message="База на обслуживании"
)
# 3. ... работы ...
# 4. Вернуть доступ
rc.Infobase.update(
session, cluster, infobase_id, sessions_deny=False, scheduled_jobs_deny=False
)
Настроить счётчик производительности¶
rc.Counter.update(
session,
cluster,
name="cpu-time",
collection_time="0",
group="process",
filter_type="processor",
duration=True,
cpu_time=True,
memory=True,
descr="Базовые метрики процессов",
)
for value in rc.Counter.values(session, cluster, "cpu-time"):
print(value)
Включённые метрики передаются как True, выключенные — как False,
неизменяемые — как None. В RAC это analyze / not-analyze.
Ограничить ресурсы проблемной базы¶
rc.Limit.update(
session,
cluster,
name="memory-per-process",
action="restart", # или "ignore"
memory=2_000_000, # порог в единицах счётчика (КБ)
error_message="Превышен лимит памяти, процесс перезапущен",
descr="Защита от утечек памяти",
)
Профиль безопасности с доступом к каталогу¶
Профиль безопасности ограничивает доступ к внешним ресурсам для кода в безопасном режиме.
rc.Profile.update(
session,
cluster,
name="safe-profile",
descr="Профиль для внешних обработок",
config="deny",
priv="deny",
crypto="deny",
)
# Разрешить чтение из одного каталога
rc.Profile.ACL.Directory.update(
session,
cluster,
name="safe-profile",
alias="reports",
physicalPath="/srv/1c/reports",
allowedRead=True,
allowedWrite=False,
)
Правила есть и для других типов ресурсов: Profile.ACL.COM,
Profile.ACL.Addin (внешние компоненты), Profile.ACL.Module,
Profile.ACL.App, Profile.ACL.Inet (интернет-ресурсы). Структура вызовов у
них одинаковая: list, update, remove.
Собрать отчёт по всем кластерам¶
import json
report = []
for cluster_item in rc.Cluster.list(session):
cluster_id = cluster_item["cluster"]
report.append(
{
"cluster": cluster_item["name"],
"servers": len(rc.Server.list(session, cluster_id)),
"infobases": len(rc.Infobase.Summary.list(session, cluster_id)),
"sessions": len(rc.UserSession.list(session, cluster_id)),
}
)
print(json.dumps(report, ensure_ascii=False, indent=2))
Устойчивый скрипт: одна ошибка не срывает обход¶
import sys
results = {}
for cluster_item in rc.Cluster.list(session):
cluster_id = cluster_item["cluster"]
try:
results[cluster_id] = rc.Infobase.Summary.list(session, cluster_id)
except errors.RACTimeoutError:
print(f"кластер {cluster_id}: таймаут", file=sys.stderr)
except errors.ClusterNotFoundError:
print(f"кластер {cluster_id}: не найден", file=sys.stderr)
except errors.UnknownError as exc:
print(f"кластер {cluster_id}: ошибка RAC: {exc}", file=sys.stderr)
Разобрать вывод в dataclass¶
Если хочется работать с объектами, а не словарями:
from dataclasses import dataclass
@dataclass
class ClusterRow:
cluster: str
name: str
host: str
port: int
expiration_timeout: int
lifetime_limit: int
security_level: int
output = session.exec("cluster list")
clusters = output.to_list_of_dataclass(ClusterRow)
print(clusters[0].name)
Все поля вывода должны быть объявлены
to_dataclass передаёт в конструктор все ключи из вывода RAC. Если
в датаклассе не объявить какое-то поле, будет TypeError. Для устойчивости
к версиям 1С объявляйте поля с значениями по умолчанию или оставайтесь на
словарях.