GPOA (GPO Applier for Linux) поддерживает систему плагинов для расширения функциональности применения групповых политик. Плагины позволяют добавлять поддержку новых типов политик и системных настроек без изменения основного кода.
plugin- Абстрактный базовый класс с финальными методамиapply()иapply_user()FrontendPlugin- Упрощенный класс для плагинов с поддержкой логирования
plugin_manager- Загружает и выполняет плагины из директорий:/usr/lib/gpupdate/plugins/- системные плагиныgpoa/frontend_plugins/- плагины разработки
#!/usr/bin/env python3
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2025 BaseALT Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
from gpoa.plugin.plugin_base import FrontendPlugin
class ExampleApplier(FrontendPlugin):
"""
Пример простого плагина с логированием и работой с реестром.
"""
# Домен для переводов
domain = 'example_applier'
def __init__(self, dict_dconf_db, username=None, fs_file_cache=None):
"""
Инициализация плагина.
Args:
dict_dconf_db (dict): Словарь с данными из реестра
username (str): Имя пользователя
fs_file_cache: Кэш файловой системы
"""
super().__init__(dict_dconf_db, username, fs_file_cache)
# Инициализация системы логирования
self._init_plugin_log(
message_dict={
'i': { # Информационные сообщения
1: "Example Applier initialized",
2: "Configuration applied successfully"
},
'w': { # Предупреждения
10: "No configuration found in registry"
},
'e': { # Ошибки
20: "Failed to apply configuration"
}
},
domain="example_applier"
)
def run(self):
"""
Основной метод выполнения плагина.
Returns:
bool: True если успешно, False при ошибке
"""
try:
self.log("I1") # Плагин инициализирован
# Получение данных из реестра
self.config = self.get_dict_registry('Software/BaseALT/Policies/Example')
if not self.config:
self.log("W10") # Конфигурация не найдена в реестре
return True
# Логирование данных из реестра
self.log("I2") # Конфигурация успешно применена
return True
except Exception as e:
self.log("E20", {"error": str(e)})
return False
def create_machine_applier(dict_dconf_db, username=None, fs_file_cache=None):
"""
Фабричная функция для создания экземпляра плагина для машинного контекста.
Args:
dict_dconf_db (dict): Словарь с данными из реестра
username (str): Имя пользователя
fs_file_cache: Кэш файловой системы
Returns:
ExampleApplier: Экземпляр плагина
"""
return ExampleApplier(dict_dconf_db, username, fs_file_cache)
def create_user_applier(dict_dconf_db, username=None, fs_file_cache=None):
"""
Фабричная функция для создания экземпляра плагина для пользовательского контекста.
Args:
dict_dconf_db (dict): Словарь с данными из реестра
username (str): Имя пользователя
fs_file_cache: Кэш файловой системы
Returns:
ExampleApplier: Экземпляр плагина
"""
return ExampleApplier(dict_dconf_db, username, fs_file_cache)Плагины используют систему логирования с кодами сообщений:
self._init_plugin_log(
message_dict={
'i': { # Информационные сообщения
1: "Example Applier initialized",
2: "Configuration applied successfully"
},
'w': { # Предупреждения
10: "No configuration found in registry"
},
'e': { # Ошибки
20: "Failed to apply configuration"
}
},
domain="example_applier"
)Доступ к данным из реестра через метод get_dict_registry():
self.config = self.get_dict_registry('Software/BaseALT/Policies/Example')Использование зарегистрированных кодов сообщений:
self.log("I1") # Простое сообщение
self.log("E20", {"error": str(e)}) # Сообщение с даннымиПлагины должны предоставлять фабричные функции:
create_machine_applier()- для машинного контекстаcreate_user_applier()- для пользовательского контекста
GPOA поддерживает автоматическую локализацию сообщений плагинов. Система использует стандарт GNU gettext.
gpoa/locale/
├── ru/
│ └── LC_MESSAGES/
│ ├── gpoa.mo
│ └── gpoa.po
└── en/
└── LC_MESSAGES/
├── gpoa.mo
└── gpoa.po
- Определение домена переводов:
class MyPlugin(FrontendPlugin):
domain = 'my_plugin' # Домен для файлов перевода- Инициализация логгера с поддержкой переводов:
self._init_plugin_log(
message_dict={
'i': {
1: "Plugin initialized",
2: "Configuration applied successfully"
},
'e': {
10: "Configuration error"
}
},
domain="my_plugin" # Домен для поиска файлов перевода
)- Использование в коде:
# Сообщения автоматически переводятся при логировании
self.log("I1") # Будет показано на языке системы- Извлечение строк для перевода:
# Извлечь строки из кода плагина
xgettext -d my_plugin -o my_plugin.po my_plugin.py- Создание файла перевода:
# my_plugin.po
msgid "Plugin initialized"
msgstr "Плагин инициализирован"
msgid "Configuration applied successfully"
msgstr "Конфигурация успешно применена"- Компиляция переводов:
# Скомпилировать .po в .mo
msgfmt my_plugin.po -o my_plugin.mo
# Разместить в правильной директории
mkdir -p /usr/share/locale/ru/LC_MESSAGES/
cp my_plugin.mo /usr/share/locale/ru/LC_MESSAGES/- Используйте полные предложения - не разбивайте строки на части
- Избегайте конкатенации строк - это затрудняет перевод
- Указывайте контекст - добавляйте комментарии для переводчиков
- Тестируйте переводы - проверяйте отображение на разных языках
- Обновляйте переводы - при изменении сообщений обновляйте файлы .po
my_plugin/
├── my_plugin.py # Основной код плагина
├── locale/
│ ├── ru/
│ │ └── LC_MESSAGES/
│ │ ├── my_plugin.mo
│ │ └── my_plugin.po
│ └── en/
│ └── LC_MESSAGES/
│ ├── my_plugin.mo
│ └── my_plugin.po
└── README.md
__init__(dict_dconf_db, username=None, fs_file_cache=None)- инициализацияrun()- основной метод выполнения (абстрактный)apply()- выполнение с текущими привилегиями (финальный)apply_user(username)- выполнение с привилегиями пользователя (финальный)get_dict_registry(prefix='')- получение данных из реестра_init_plugin_log(message_dict=None, locale_dir=None, domain=None)- инициализация логгераlog(message_code, data=None)- логирование с кодами сообщений
Коды сообщений:
- I - Информационные сообщения
- W - Предупреждения
- E - Ошибки
- D - Отладочные сообщения
- F - Фатальные ошибки
dict_dconf_db- словарь данных из реестраusername- имя пользователя (для пользовательского контекста)fs_file_cache- кэш файловой системы для работы с файлами
- Выполняется с правами root
- Применяет системные настройки
- Использует фабричную функцию
create_machine_applier()
- Выполняется с правами указанного пользователя
- Применяет пользовательские настройки
- Использует фабричную функцию
create_user_applier()
- Безопасность: Всегда валидируйте входные данные
- Идемпотентность: Повторное выполнение должно давать тот же результат
- Логирование: Используйте коды сообщений для всех операций
- Обработка ошибок: Плагин не должен "падать" при ошибках
- Транзакционность: Изменения должны быть атомарными
- Переводы: Поддерживайте локализацию сообщений