Repository: slothfk/1c_zabbix_template_ce Branch: master Commit: 63a961bb0b0b Files: 60 Total size: 227.8 KB Directory structure: gitextract_y6e7qtdo/ ├── 1c_central_server.xml ├── 1c_license_files.xml ├── 1c_license_server.xml ├── 1c_work_server.xml ├── LICENSE ├── README.md ├── _config.yml ├── configs/ │ ├── logcfg.xml │ ├── userparameter_1c-cs.conf │ ├── userparameter_1c-lf.conf │ ├── userparameter_1c-ls.conf │ ├── userparameter_1c-ws.conf │ └── zabbix.sudoers ├── docs/ │ ├── central_server.md │ ├── install.md │ ├── license_files.md │ ├── license_server.md │ ├── windows.md │ └── work_server.md ├── playbooks/ │ ├── install.yml │ ├── inventory.sample │ ├── roles/ │ │ ├── general/ │ │ │ ├── .travis.yml │ │ │ ├── README.md │ │ │ ├── defaults/ │ │ │ │ └── main.yml │ │ │ ├── handlers/ │ │ │ │ └── main.yml │ │ │ ├── tasks/ │ │ │ │ └── main.yml │ │ │ ├── tests/ │ │ │ │ ├── inventory │ │ │ │ └── test.yml │ │ │ └── vars/ │ │ │ └── main.yml │ │ ├── srv1c_cs/ │ │ │ ├── .travis.yml │ │ │ ├── README.md │ │ │ ├── defaults/ │ │ │ │ └── main.yml │ │ │ ├── handlers/ │ │ │ │ └── main.yml │ │ │ ├── tasks/ │ │ │ │ └── main.yml │ │ │ ├── tests/ │ │ │ │ ├── inventory │ │ │ │ └── test.yml │ │ │ └── vars/ │ │ │ └── main.yml │ │ ├── srv1c_ls/ │ │ │ ├── .travis.yml │ │ │ ├── README.md │ │ │ ├── defaults/ │ │ │ │ └── main.yml │ │ │ ├── handlers/ │ │ │ │ └── main.yml │ │ │ ├── tasks/ │ │ │ │ └── main.yml │ │ │ ├── tests/ │ │ │ │ ├── inventory │ │ │ │ └── test.yml │ │ │ └── vars/ │ │ │ └── main.yml │ │ └── srv1c_ws/ │ │ ├── .travis.yml │ │ ├── README.md │ │ ├── defaults/ │ │ │ └── main.yml │ │ ├── handlers/ │ │ │ └── main.yml │ │ ├── tasks/ │ │ │ └── main.yml │ │ ├── tests/ │ │ │ ├── inventory │ │ │ └── test.yml │ │ └── vars/ │ │ └── main.yml │ └── tasks/ │ ├── install-Debian.yml │ └── install-RedHat.yml └── scripts/ ├── 1c_central_server.sh ├── 1c_common_module.sh ├── 1c_license_files.sh ├── 1c_license_server.sh └── 1c_work_server.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: 1c_central_server.xml ================================================ 4.4 2023-07-13T05:09:00Z Templates/Applications Templates/Kaminsoft [1С/Центральный сервер] Максимальная длительность текущего вызова 199C0D Template App 1C Enterprise Central Server 1c.cs.sessions.cld 1 F63100 Template App 1C Enterprise Central Server 1c.cs.sessions.bgd 2 2774A4 Template App 1C Enterprise Central Server 1c.cs.sessions.wsd 3 F7941D Template App 1C Enterprise Central Server 1c.cs.sessions.hsd [1С/Центральный сервер] Текущие сеансы GRADIENT_LINE 199C0D Template App 1C Enterprise Central Server 1c.cs.sessions.total 1 GRADIENT_LINE F63100 Template App 1C Enterprise Central Server 1c.cs.sessions.bg 2 GRADIENT_LINE 2774A4 Template App 1C Enterprise Central Server 1c.cs.sessions.hb 3 GRADIENT_LINE F7941D Template App 1C Enterprise Central Server 1c.cs.sessions.ws 4 GRADIENT_LINE FC6EA3 Template App 1C Enterprise Central Server 1c.cs.sessions.http 5 GRADIENT_LINE 6C59DC Template App 1C Enterprise Central Server 1c.cs.sessions.as Service state 0 Down 1 Up ================================================ FILE: 1c_license_files.xml ================================================ 4.4 2023-06-09T08:59:00Z Templates/Kaminsoft ================================================ FILE: 1c_license_server.xml ================================================ 4.4 2023-06-15T18:45:43Z Templates/Applications Templates/Kaminsoft {Template App 1C Enterprise License Server:1c.ls.license.used.last()}>={Template App 1C Enterprise License Server:1c.licenses.clients.last()} [1С/Лицензии] Использованы все доступные клиентские лицензии HIGH YES {Template App 1C Enterprise License Server:1c.ls.license.used.last()}/{Template App 1C Enterprise License Server:1c.licenses.clients.last()}>{$LIC_UTIL_LIMIT} [1С/Лицензии] Количество использованных лицензий близко к максимальному AVERAGE YES [1С/Лицензии] Использованы все доступные клиентские лицензии {Template App 1C Enterprise License Server:1c.ls.license.used.last()}>={Template App 1C Enterprise License Server:1c.licenses.clients.last()} {Template App 1C Enterprise License Server:1c.ls.license.client.last()}/{Template App 1C Enterprise License Server:1c.ls.license.total.last()}>0.5 [1С/Лицензии] Количество лицензий с клиентских компьютеров > 50% DISABLED WARNING YES [1С/Лицензии] Использование активированных лицензий 300 FIXED GRADIENT_LINE 1B5E20 Template App 1C Enterprise License Server 1c.ls.license.used 1 FFFFFF Template App 1C Enterprise License Server 1c.ls.license.uniq 2 FC6EA3 Template App 1C Enterprise License Server 1c.ls.license.client 3 6C59DC Template App 1C Enterprise License Server 1c.ls.license.webclient 4 F7941D Template App 1C Enterprise License Server 1c.ls.license.total 5 FF0000 Template App 1C Enterprise License Server 1c.licenses.clients ================================================ FILE: 1c_work_server.xml ================================================ 4.4 2023-07-07T07:48:53Z Templates/Applications Templates/Kaminsoft {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}>{Template App 1C Enterprise Work Server:1c.ws.rmngr.count.last()}>0 or {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}>{Template App 1C Enterprise Work Server:1c.ws.rphost.count.last()}>0 [1С/Агент сервера] Аномальное число процессов AVERAGE [1С/Менеджер кластера] Нет запущенных процессов {Template App 1C Enterprise Work Server:1c.ws.rmngr.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}>0 [1С/Рабочий процесс] Нет запущенных процессов {Template App 1C Enterprise Work Server:1c.ws.rphost.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}>0 {Template App 1C Enterprise Work Server:1c.ws.ragent.excps.last()}/{Template App 1C Enterprise Work Server:1c.ws.ragent.count.avg(3600)}>{$EXCP_THRESHOLD} [1С/Агент сервера] Количество исключений свыше {$EXCP_THRESHOLD} AVERAGE {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}=0 and ({Template App 1C Enterprise Work Server:1c.ws.rmngr.count.last()}>0 or {Template App 1C Enterprise Work Server:1c.ws.rphost.count.last()}>0) [1С/Агент сервера] Нет запущенных процессов HIGH {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.rmngr.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.rphost.count.last()}=0 [1С/Кластер] Отсутствуют все процессы кластера HIGH {Template App 1C Enterprise Work Server:1c.ws.rmngr.excps.last()}/{Template App 1C Enterprise Work Server:1c.ws.rmngr.count.avg(3600)}>{$EXCP_THRESHOLD} [1С/Менеджер кластера] Количество исключений свыше {$EXCP_THRESHOLD} AVERAGE {Template App 1C Enterprise Work Server:1c.ws.rmngr.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}>0 [1С/Менеджер кластера] Нет запущенных процессов HIGH [1С/Кластер] Отсутствуют все процессы кластера {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.rmngr.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.rphost.count.last()}=0 {Template App 1C Enterprise Work Server:1c.ws.rphost.excps.last()}/{Template App 1C Enterprise Work Server:1c.ws.rphost.count.avg(3600)}>{$EXCP_THRESHOLD} [1С/Рабочий процесс] Количество исключений свыше {$EXCP_THRESHOLD} AVERAGE {Template App 1C Enterprise Work Server:1c.ws.rphost.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}>0 [1С/Рабочий процесс] Нет запущенных процессов HIGH [1С/Кластер] Отсутствуют все процессы кластера {Template App 1C Enterprise Work Server:1c.ws.ragent.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.rmngr.count.last()}=0 and {Template App 1C Enterprise Work Server:1c.ws.rphost.count.last()}=0 {Template App 1C Enterprise Work Server:1c.ws.rphost.memory.last()}/{Template App 1C Enterprise Work Server:1c.ws.ram.last()}>{$RPHOST_MAX_MEM} [1С/Рабочий процесс] Превышено пороговое значение используемой памяти AVERAGE {Template App 1C Enterprise Work Server:1c.ws.rphost.count.avg(5m)}={Template App 1C Enterprise Work Server:1c.ws.rphost.count.last()} and {Template App 1C Enterprise Work Server:1c.ws.rphost.change.count(5m,1)}>=3 [1С/Рабочий процесс] Частые изменения в списке процессов HIGH {Template App 1C Enterprise Work Server:1c.ws.rmngr.memory.last()}>={Template App 1C Enterprise Work Server:1c.ws.rphost.memory.max(90)} [1С/Рабочий сервер] Объем памяти rmngr превышает объем rphost HIGH [1С/Рабочий сервер] Объем используемой памяти FF4000 Template App 1C Enterprise Work Server 1c.ws.ram 1 FC6EA3 Template App 1C Enterprise Work Server 1c.ws.ragent.memory 2 C7A72D Template App 1C Enterprise Work Server 1c.ws.rmngr.memory 3 199C0D Template App 1C Enterprise Work Server 1c.ws.rphost.memory 4 6C59DC RIGHT Template App 1C Enterprise Work Server 1c.ws.ragent.count 5 BA2A5D RIGHT Template App 1C Enterprise Work Server 1c.ws.rmngr.count 6 0040FF RIGHT Template App 1C Enterprise Work Server 1c.ws.rphost.count 7 F7941D RIGHT Template App 1C Enterprise Work Server 1c.ws.rphost.change [1С/Рабочий сервер] Объем памяти процессов STACKED ITEM Template App 1C Enterprise Work Server 1c.ws.ram 199C0D RIGHT Template App 1C Enterprise Work Server 1c.ws.ram 1 2774A4 Template App 1C Enterprise Work Server 1c.ws.rphost.memory 2 F7941D Template App 1C Enterprise Work Server 1c.ws.rmngr.memory 3 F63100 Template App 1C Enterprise Work Server 1c.ws.ragent.memory [1С/Рабочий сервер] Ошибки процессов 199C0D Template App 1C Enterprise Work Server 1c.ws.ragent.excps 1 F63100 Template App 1C Enterprise Work Server 1c.ws.rmngr.excps 2 2774A4 Template App 1C Enterprise Work Server 1c.ws.rphost.excps [1С/Рабочий сервер] Процессы сервера NO F63100 Template App 1C Enterprise Work Server 1c.ws.ragent.count 1 BOLD_LINE 2774A4 Template App 1C Enterprise Work Server 1c.ws.rmngr.count 2 199C0D Template App 1C Enterprise Work Server 1c.ws.rphost.count 3 BOLD_LINE F7941D RIGHT Template App 1C Enterprise Work Server 1c.ws.perfs[{$RAS_PORTS},{$RAS_TIMEOUT},{$RAS_USER},{$RAS_PASS}] 4 0040FF Template App 1C Enterprise Work Server 1c.ws.rphost.change [1С/Рабочий процесс] Перемены в процессах 0 изменений нет 1 произошло изменение ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Alexey Y. Fedotov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![CodeFactor](https://www.codefactor.io/repository/github/slothfk/1c_zabbix_template_ce/badge)](https://www.codefactor.io/repository/github/slothfk/1c_zabbix_template_ce) # 1c_zabbix_template_ce (Community Edition) Шаблон (конфигурационные файлы агента и вспомогательные скрипты) для мониторинга с помощью Zabbix (версии 4.4) серверов 1С Предприятия, работающих как под управлением операционной системы GNU/Linux так и под MS Windows. ## Основная идея проекта * Расследование инцидентов, возникающих в работе сервера 1С Предприятия, с минимальной задержкой от момента их возникновения * Хранение минимального объема информации, нобходимого для расследования ## Выбранная архитектура Шаблон разбит на несколько составляющих по функциональному назначению: * Шаблон для мониторинга [рабочего сервера](./docs/work_server.md) 1С Предприятия * Шаблон для мониторинга [сервера лицензирования](./docs/license_server.md) * Шаблон для мониторинга [центрального сервера](./docs/central_server.md) * Шаблон для мониторинга [файлов программных лицензий](./docs/license_files.md) Неявно подразумевается, что на сервере (объекте мониторинга) запущенна **только одна служба сервера 1С Предприятия** (агент сервера 1С Предприятия). Однако шаблон позволяет собирать информацию одновременно с разных сервисов **RAS** (для чего в макросе **{$RAS_PORTS}** необходимо указывать порты, используемых **RAS** через запятую) ## Установка и обновление Описание процесса установки можно найти [здесь](./docs/install.md). Специфика использования скриптов на платформе MS Windows описана [здесь](./docs/windows.md) Т.к. шаблон находится в состоянии активной разработки (добавляются новые функции, корректируется имеющийся функционал), то может возникнуть ситуация, когда на работающей системе необходимо привести уже развернутый шаблон к актуальному состоянию. К сожалению, на текущий момент универсальной инструкции, позволяющей перейти к актуальной версии, и при этом не потерять накопленные данные, **нет**! ## Статьи о данном шаблоне (с картинками) * [Мониторинг количества использованных программных лицензий, выданных выделенным сервером лицензирования](https://infostart.ru/public/1157013/) * [Использование Zabbix для сбора информации о серверных вызовах и управляемых блокировках с сервера 1С Предприятия, работающего на платформе GNU/Linux](https://infostart.ru/public/1120500/) * [Мониторинг количества использованных клиентских лицензий, выданных сервером 1С Предприятия, работающим на платформе GNU/Linux](https://infostart.ru/public/1114020/) ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-modernist ================================================ FILE: configs/logcfg.xml ================================================ ================================================ FILE: configs/userparameter_1c-cs.conf ================================================ # # Мониторинг 1С Предприятия 8.3 (центральный сервер) # # (c) 2019-2023, Алексей Ю. Федотов # # Email: fedotov@kaminsoft.ru # UserParameter=1c.cs.sessions[*],/var/lib/zabbix/scripts/1c_central_server.sh sessions $1 $2 $3 $4 UserParameter=1c.cs.ib.restrictions[*],/var/lib/zabbix/scripts/1c_central_server.sh ib_restrict UserParameter=1c.cs.clusters.discovery[*],/var/lib/zabbix/scripts/1c_central_server.sh clusters $1 $2 $3 $4 UserParameter=1c.cs.infobases.discovery[*],/var/lib/zabbix/scripts/1c_central_server.sh infobases $1 $2 $3 $4 $5 UserParameter=1c.cs.ras.discovery[*],/usr/bin/echo "$1" | awk -F, 'BEGIN { print "{\"data\":[" } { for (i=1;i<=NF;i++) { print "{\"{#RAS_PORT}\":\""$i"\"}"(i$ ls -al /var/log/1C/zabbix/ итого 24 drwxr-xr-x 6 usr1cv8 grp1cv8 4096 Feb 25 12:29 . drwxr-xr-x 4 root root 4096 Feb 25 12:29 .. drwxr-xr-x 2 usr1cv8 grp1cv8 4096 Aug 27 2019 calls drwxr-xr-x 2 usr1cv8 grp1cv8 4096 Feb 25 12:29 excps drwxr-xr-x 2 usr1cv8 grp1cv8 4096 Aug 27 2019 locks drwxr-xr-x 2 zabbix grp1cv8 4096 Aug 27 2019 problem_log Добавить пользователя **zabbix** в группу **grp1cv8**, чтобы скрипт, запущенный **zabbix-agent** мог прочитать файлы технологического журнала
$ sudo usermod -a -G grp1cv8 zabbix
Помимо этого, на центральном сервере кластера должен быть запущен сервис **RAS**, для запуска которого можно воспользоваться юнитом **systemd**, взятым [здесь](https://github.com/slothfk/1c_systemd/blob/master/srv1cv8-ras.service) ## Установка с помощью ansible Если в рабочем окружении используется **ansible**, то для развертывания шаблона на сервера с **CentOS** можно воспользоваться сценарием **playbooks/install.yml**. Для этого требуется создать файл **inventory** по примеру **playbooks/inventory.sample**, добавив в соответствующие группы (**srv1c_cs**, **srv1c_ls** и **srv1c_ws**) "нужные" сервера ## Общие замечания Не зависимо от способа развертывания, для включения сбора технологического журнала 1С, необходимого для целей мониторинга, следует из файла **configs/logcfg.xml** перенести секции **log** в файл **logcfg.xml** рабочего сервера 1С Предприятия (или просто скопировать его в каталог /opt/1C/v8.3/тип_архитектуры/conf/, если сбор ТЖ ранее не использовался). **ВАЖНО:** В шаблоне в основном используеются элементы данных, получающие данные от активного агента **Zabbix**. В связи с этим необходимо в настройках агента **Zabbix** (в файле конфигурации abbix_agentd.conf скорректировать строку
ServerActive=127.0.0.1
указав в ней **ip**-адрес или доменное имя вашего сервера **Zabbix**. Вместе с тем параметр Hostname= должен иметь такое же значение какое имеет этот узел мониторинга на сервере **Zabbix**! В противном случае, **Zabbix** сервер не сможет корректно обработать поступающие к нему данные! **ВАЖНО:** Для корректной работы скриптов на сервере 1С Предприятия должны быть установлены следующие программы: zabbix-sender и zabbix-get. Так же должен быть запущен RAS на центральном сервере кластера. **ВАЖНО:** В случае многосерверного кластера 1С Предприятия, для корректной работы механизма сохранения файлов "проблемного" технологического журнала, необходимо на всех серверах, входящих в кластер, в настройке **Server** агента zabbix указать все сервера, входящие в кластер. Например, для кластера из двух серверов (**server_a** и **server_b**) на обоих серверах данная настройка агента zabbix должна выглядеть следующим образом:
Server=server_zabbix,server_a,server_b
**ВАЖНО:** В случае использования шаблона на **Debian**-like операционных системах, необходимо заменить, установленный в системе по-умолчанию **mawk**, на **gawk**, так как в скриптах [сервера лицензирования](./license_server.md) используются специфичные для **gawk** "конструкции" (подробнее см. #69) [Назад](../README.md) ================================================ FILE: docs/license_files.md ================================================ # Файлы программных лицензий Для мониторинга **файлов программных лицензий** на наблюдаемом сервере необходимо наличие утилиты **license-tools** (см. на ИТС, например https://its.1c.ru/db/v8323doc#bookmark:adm:TI000000679) В шаблоне реализован сбор следующих показателей: * Число серверных лицензий; * Число клиентских лицензий. **ВАЖНО:** Шаблон используется в составе шаблона [сервера лицензирования](./license_server.md), но так же может быть использован самостоятельно (например на [рабочих серверах](./work_server.md) для отслеживания состояния серверных лицензий)! ## Правила обнаружения Шаблон имеет правило обнаружения файлов программных лицензий, которое формирует список лицензий на основе выдачи `ring license list` По каждому обнаруженному файлу лицензии собираются следующие показатели: * Тип лицензи (серверная или клиентская, для клиентской указыватеся номинал) * Срок действия лицензии * Результат проверки лицензии командой `ring license validate` **ВАЖНО:** Первые два элемента данных собираются на основе информации, содержащейся в файле лицензии (так было не всегда, посему может работать некорректно) Соответственно по каждому обнаруженному файлу лицензии есть следующие триггеры: * Файл не прошел проверку * Срок действия лицензии истек [Назад](../README.md) ================================================ FILE: docs/license_server.md ================================================ # Сервер лицензирования Шаблон **сервера лицензирования** является зависимым от шаблона [файлов программных лицензий](./license_files.md) Для мониторинга роли **Сервера лицензирования** реализован сбор следующих показателей: * Общее количество сеансов на серверах кластера, в котором участвует данный сервер лицензирования; * Количество файлов клиентских лицензий, активированных на данном сервере (только клиентские лицензии ПРОФ и КОРП); * Количество сеансов лицензируемых клиентскими (программными) лицензиями, активированными на данном сервере; * Количество сеансов лицензируемых клиентскими лицензиями с пользовательских компьютеров; * Количество сеансов использующих веб-клиент; * Количество использованных лицензий (выданных данным сервером); * Проверка на отключение от кластеров, в которые включен сервер лицензирования. **СОВЕТ:** Для надежного получения значений по сеансам необходимо на сервере лицензирования увеличить таймаут zabbix-агента в зависимости от количества кластеров, в состав которых входит сервер лицензирования, количества информационных баз и общего количества сеансов. Рекомендуемое значение - **10 сек**. ## Макросы для сервера лицензирования В шаблоне сервера лицензирования есть следующие макросы: * **{$RAS_PORTS}** - порт(ы) сервера RAS, для кластера(ов), в котором(ых) данный сервер является центральным (в случае несколькихх значений, они указываются через запятую); * **{$RAS_TIMEOUT}** - максимальное время ожидание ответа сервиса RAS, указанное в секундах; * **{$RAS_USER}** - имя пользователя (администратора кластера), необходимое для получения данных от сервиса RAS; * **{$RAS_PASS}** - пароль пользователя (администратора кластера); * **{$LIC_UTIL_LIMIT}** - значение отношения количества использованных лицензий к количеству сеансов, лицензируемых клиентскими лицензиями, активированными на данном сервере, по превышении которого срабатывает триггер с предупреждением о скором исчерпании имеющихся лицензий. ## Тригеры Для отслеживания критичных изменений показателей роли **Сервера лицензирования** созданы триггеры, срабатывающие при следующих событиях: * Количество лицензий, выданных данным сервером лицензирования, превышает пороговое значение, регулируемое макросом **{$LIC_UTIL_LIMIT}**. Уровень важности - **Предупреждение**; * Использованы все активированные лицензии, т.е. количество сеансов с лицензией, выданной данным сервером лицензирования, равно количеству клиентских лицензий активированных на данном сервере. Уровень важности - **Высокая**; * При работе с кластером 1С Предприятия используются локальные пользовательские лицензии (по-умолчанию деактивирован). Уровень важности - **Информация**; * Количество локальных пользовательских лицензий превышает 50% (по-умолчанию деактивирован). Уровень важности - **Предупреждение**; * Отключение от кластера, в состав которого включен сервер лицензирования. Уровень важности - **Высокая**. ## Графики Для визуализации показателей роли **Cервера лицензирования** предусмотрены следущие графики: * Использование активированных лицензий
![screen](./imgs/1c_ls_lic_util.gif) ## Правила обнаружения Для отслеживания использования клиентских лицензий, выдаваемых сервером лицензирования реализованы следующие "разрезы": * Если сервер лицензирования входит в состав нескольких кластеров 1С Предприятия и требуется сбор данных в разрезе кластров, необходимо активировать соответствующее **правило обнаружения** (по-умолчнию деактивировано). После этого собираемые показатели и графики станут доступными в разрезе кластеров, в которых принимает участие данный сервер лицензирования; * Если требуется сбор данных в разрезе информационных баз, необходимо активировать соответствующее **правило обнаружения** (по-умолчнию деактивировано). После этого собираемые показатели и графики станут доступными в разрезе всех информационных баз всех кластеров, в состав которых включен данный сервер лицензирования. ## Комплексные экраны В шаблон сервера лицезирования включены два комплексных экрана: * Использование активированных лицензий по кластерам; * Использование активированных лицензий по информационным базам. Данные экраны строятся автоматически и включают общий график по утилизации лицензий, а так же все графики в контексте выбранного "разреза" [Назад](../README.md) ================================================ FILE: docs/windows.md ================================================ # Что делать, если у меня MS Windows? Не смотря на то, что изначально шаблон создавался для платформы GNU/Linux, в файлы скриптов были внесены необходимымые изменения, которые позволяют использовать их на платформе MS Windows. ## Что необходимо сделать? * Установить на сервер, который требуется мониторить, **Git Bash** - https://gitforwindows.org/ * Поместить файлы **userparameter_1c-*.conf** из каталога **configs** в каталог **C:\Program Files\Zabbix Agent\zabbix_agentd.conf.d** (или в какой другой, куда установлен **zabbix-agent**) * Поместить все файлы скриптов из каталога **scripts** в каталог, к примеру, **C:\Program Files\Zabbix Agent\scripts** * Внести правки в файлы **userparameter_1c-*.conf** в части строки вызова скриптов, заменив в каждой строке вызов срипта по аналогии с
/var/lib/zabbix/scripts/1c_central_server.sh
на следующее
"C:\Program Files\Git\bin\bash.exe" "C:\Program Files\Zabbix Agent\scripts\1c_central_server.sh"
обязательно заключая пути в кавычки! *ВАЖНО:* Указывать имя каталога в макросе *{$LOG_DIR}* требуется в виде
/c/Logs
вместо привычных
C:\Logs
Имена каталогов чувствительны к регистру! Настроить запуск сервера **RAS**, воспользовавшись для этого, например, информацией из статьи https://infostart.ru/1c/articles/810752/ Все остальные настройки, касающиеся агента и сервера **Zabbix** делаются ка написано [здесь](./install.md) [Назад](../README.md) ================================================ FILE: docs/work_server.md ================================================ # Рабочий сервер Для мониторинга роли **Рабочего сервера** реализован сбор следующих показателей: * Количество процессов ragent; * Суммарный объем памяти всех процессов ragent; * Количество исключений (событий EXCP) возникших в процессах ragent; * Количество процессов rmngr; * Суммарный объем памяти всех процессов rmngr; * Количество исключений (событий EXCP) возникших в процессах rmngr; * Количество процессов rphost; * Суммарный объем памяти всех процессов rphost; * Количество исключений (событий EXCP) возникших в процессах rphost; * Изменение в списке рабочих процессов; * Режим отладки сервера; * Количество событий об управляемых блокировках - TLOCK (по-умолчанию деактивирован); * Количество таймаутов на управляемых блокировках (по-умолчанию деактивирован); * Количество взаимоблокировок на управляемых блокировках (по-умолчанию деактивирован); * Суммарное время ожидания на управляемых блокировках (по-умолчанию деактивирован); * ТОП серверных вызовов по суммарной длительности (по-умолчанию деактивирован); * ТОП серверных вызовов по суммарному процессорному времени (по-умолчанию деактивирован); * ТОП серверных вызовов по суммарному вводу-выводу (по-умолчанию деактивирован); * ТОП серверных вызовов по средней длительности (по-умолчанию деактивирован); * ТОП серверных вызовов по суммарному количеству (по-умолчанию деактивирован); * ТОП серверных вызовов по максимальной памяти за вызов (по-умолчанию деактивирован); * ТОП "ленивых" (с большой длительностью и малым процессорным временем) серверных вызовов (по-умолчанию деактивирован); * Объем оперативной памяти сервера. ## Макросы для рабочего сервера В шаблоне рабочего сервера есть следующие макросы: * **{$LOG_DIR}** - каталог для хранения файлов технологического журнала; * **{$MAX_LOCK_WAIT}** - пороговое значение суммарного времени ожидания на управляемых блокировках за прошедший час, по превышении которого срабатывает триггер, указанное в секундах; * **{$TOP_LIST_SIZE}** - количество выдаваемых (сохраняемых) записей в агригированных выборках по серверным вызовам; * **{$RAS_PORTS}** - порт(ы) сервера RAS, для кластера(ов), в котором(ых) данный сервер является центральным (в случае несколькихх значений, они указываются через запятую); * **{$RAS_TIMEOUT}** - максимальное время ожидание ответа сервиса RAS, указанное в секундах; * **{$RAS_USER}** - имя пользователя (администратора кластера), необходимое для получения данных от сервиса RAS; * **{$RAS_PASS}** - пароль пользователя (администратора кластера); * **{$RPHOST_MAX_MEM}** - пороговое значение суммарного объема памяти, занимаемого процессами rphost, по превышении которого срабатывает триггер; * **{$EXCP_THRESHOLD}** - пороговое количество исключений на один процесс, по превышении которого срабатывает триггер. ## Триггеры Для отслеживания критичных изменений показателей роли **Рабочего сервера** созданы триггеры, срабатывающие при следующих событиях: * Отсутствуют процессы ragent. Уровень важности - **Высокая**; * Отсутствуют процессы rmngr. Уровень важности - **Высокая**; * Отсутствуют процессы rphost. Уровень важности - **Высокая**; * В списке процессов rphost происходят частые измеенния. Уровень важности - **Высокая**; * Превышено пороговое значение объема памяти, занимаемого процессами rphost (регулируется макросом {$RPHOST_MAX_MEM}). Уровень важности - **Средняя**; * Обнаружены таймауты на управляемых блокировках 1С. Уровень важности - **Высокая**; * Обнаружены взаимоблокировки на управляемых блокировках 1С. Уровень важности - **Высокая**; * Превышено пороговое значение ожидания на управляемых блокировках 1С, регулируемое макросом {$MAX_LOCK_WAIT}. Уровень важности - **Высокая**; * Включен режим отладки. Уровень важности - **Средняя**; * Количество исключений по процессам ragent превышает {$EXCP_THRESHOLD}. Уровень важности - **Средняя**; * Количество исключений по процессам rmngr превышает {$EXCP_THRESHOLD}. Уровень важности - **Средняя**; * Количество исключений по процессам rphost превышает {$EXCP_THRESHOLD}. Уровень важности - **Средняя**; * Отсутствуют события TLOCK. Уровень важности - **Информация**. ## Графики Для визуализации показателей роли **Рабочего сервера** предусмотрены следущие графики: * Количество ошибок по процессам ragent, rmngr и rphost; * Объем используемой памяти процессами сервера 1С Предприятия в разрезе ragent, rmngr и rphost. [Назад](../README.md) ================================================ FILE: playbooks/install.yml ================================================ --- - hosts: srv1c become: yes tasks: - name: Check zabbix agent installation shell: "ZABBIX_COMMAND=$(pgrep -a zabbix_agent) && awk -F= '/^Include=\\/etc\\// {print $2}' $(echo $ZABBIX_COMMAND | sed -nr 's/.*-c ([^ ]+).*/\\1/p') | xargs dirname" register: 'zabbix_agent_current' - name: set_fact: zabbix_configs: '{{ zabbix_agent_current.stdout | default("/etc/zabbix/zabbix_agent2.d") }}' - name: Include role tasks by host groups include_role: name: '{{ item }}' when: 'item == "general" or inventory_hostname in groups[item]' loop: - 'general' - 'srv1c_cs' - 'srv1c_ls' - 'srv1c_ws' - hosts: hosts_to_restart gather_facts: no become: yes tasks: - name: Restart zabbix agent service: name: 'zabbix-agent{% if zabbix_configs | regex_search("2") %}2{% endif %}' state: restarted ================================================ FILE: playbooks/inventory.sample ================================================ # Общая группа серверов 1С Предприятия [srv1c:children] srv1c_cs srv1c_ls srv1c_ws # Группа центральных серверов 1С Предприятия [srv1c_cs] localhost # Группа серверов лицензирования 1С Предприятия [srv1c_ls] localhost # Группа рабочих серверов 1С Предприятия [srv1c_ws] localhost ================================================ FILE: playbooks/roles/general/.travis.yml ================================================ --- language: python python: "2.7" # Use the new container infrastructure sudo: false # Install ansible addons: apt: packages: - python-pip install: # Install ansible - pip install ansible # Check ansible version - ansible --version # Create ansible.cfg with correct roles_path - printf '[defaults]\nroles_path=../' >ansible.cfg script: # Basic role syntax check - ansible-playbook tests/test.yml -i tests/inventory --syntax-check notifications: webhooks: https://galaxy.ansible.com/api/v1/notifications/ ================================================ FILE: playbooks/roles/general/README.md ================================================ Role Name ========= A brief description of the role goes here. Requirements ------------ Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. Role Variables -------------- A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. Dependencies ------------ A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. Example Playbook ---------------- Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: - hosts: servers roles: - { role: username.rolename, x: 42 } License ------- BSD Author Information ------------------ An optional section for the role authors to include contact information, or a website (HTML is not allowed). ================================================ FILE: playbooks/roles/general/defaults/main.yml ================================================ --- # defaults file for general ================================================ FILE: playbooks/roles/general/handlers/main.yml ================================================ --- # handlers file for general ================================================ FILE: playbooks/roles/general/tasks/main.yml ================================================ --- # tasks file for general - name: Make directory for template scripts file: path: /var/lib/zabbix/scripts state: directory owner: zabbix group: zabbix mode: 0755 - name: Copy common module script copy: src: 1c_common_module.sh dest: /var/lib/zabbix/scripts/ owner: zabbix group: zabbix mode: 0755 ================================================ FILE: playbooks/roles/general/tests/inventory ================================================ localhost ================================================ FILE: playbooks/roles/general/tests/test.yml ================================================ --- - hosts: localhost remote_user: root roles: - general ================================================ FILE: playbooks/roles/general/vars/main.yml ================================================ --- # vars file for general ================================================ FILE: playbooks/roles/srv1c_cs/.travis.yml ================================================ --- language: python python: "2.7" # Use the new container infrastructure sudo: false # Install ansible addons: apt: packages: - python-pip install: # Install ansible - pip install ansible # Check ansible version - ansible --version # Create ansible.cfg with correct roles_path - printf '[defaults]\nroles_path=../' >ansible.cfg script: # Basic role syntax check - ansible-playbook tests/test.yml -i tests/inventory --syntax-check notifications: webhooks: https://galaxy.ansible.com/api/v1/notifications/ ================================================ FILE: playbooks/roles/srv1c_cs/README.md ================================================ Role Name ========= A brief description of the role goes here. Requirements ------------ Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. Role Variables -------------- A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. Dependencies ------------ A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. Example Playbook ---------------- Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: - hosts: servers roles: - { role: username.rolename, x: 42 } License ------- BSD Author Information ------------------ An optional section for the role authors to include contact information, or a website (HTML is not allowed). ================================================ FILE: playbooks/roles/srv1c_cs/defaults/main.yml ================================================ --- # defaults file for 1c_cs ================================================ FILE: playbooks/roles/srv1c_cs/handlers/main.yml ================================================ --- # handlers file for 1c_cs - name: Add host to group for restart zabbix agent group_by: key: hosts_to_restart ================================================ FILE: playbooks/roles/srv1c_cs/tasks/main.yml ================================================ --- # tasks file for 1c_cs - name: Copy sudoers file for zabbix user copy: src: zabbix.sudoers dest: /etc/sudoers.d/zabbix owner: root group: root mode: 0440 - name: Copy userparameter for central server copy: src: userparameter_1c-cs.conf dest: '{{ zabbix_configs }}' owner: root group: root mode: 0644 notify: - Add host to group for restart zabbix agent - name: Copy main script for central server copy: src: 1c_central_server.sh dest: /var/lib/zabbix/scripts/ owner: zabbix group: zabbix mode: 0755 ================================================ FILE: playbooks/roles/srv1c_cs/tests/inventory ================================================ localhost ================================================ FILE: playbooks/roles/srv1c_cs/tests/test.yml ================================================ --- - hosts: localhost remote_user: root roles: - 1c_cs ================================================ FILE: playbooks/roles/srv1c_cs/vars/main.yml ================================================ --- # vars file for 1c_cs ================================================ FILE: playbooks/roles/srv1c_ls/.travis.yml ================================================ --- language: python python: "2.7" # Use the new container infrastructure sudo: false # Install ansible addons: apt: packages: - python-pip install: # Install ansible - pip install ansible # Check ansible version - ansible --version # Create ansible.cfg with correct roles_path - printf '[defaults]\nroles_path=../' >ansible.cfg script: # Basic role syntax check - ansible-playbook tests/test.yml -i tests/inventory --syntax-check notifications: webhooks: https://galaxy.ansible.com/api/v1/notifications/ ================================================ FILE: playbooks/roles/srv1c_ls/README.md ================================================ Role Name ========= A brief description of the role goes here. Requirements ------------ Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. Role Variables -------------- A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. Dependencies ------------ A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. Example Playbook ---------------- Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: - hosts: servers roles: - { role: username.rolename, x: 42 } License ------- BSD Author Information ------------------ An optional section for the role authors to include contact information, or a website (HTML is not allowed). ================================================ FILE: playbooks/roles/srv1c_ls/defaults/main.yml ================================================ --- # defaults file for 1c_ls ================================================ FILE: playbooks/roles/srv1c_ls/handlers/main.yml ================================================ --- # handlers file for 1c_ls - name: Add host to group for restart zabbix agent group_by: key: hosts_to_restart ================================================ FILE: playbooks/roles/srv1c_ls/tasks/main.yml ================================================ --- # tasks file for 1c_ls #- name: Install required packages - include: "install-{{ ansible_os_family }}.yml" - name: Copy zabbix userparameter files for license server copy: src: '{{ item }}' dest: '{{ zabbix_configs }}' owner: root group: root mode: 0644 with_items: - userparameter_1c-ls.conf - userparameter_1c-lf.conf notify: - Add host to group for restart zabbix agent - name: Copy scripts for license server copy: src: '{{ item }}' dest: /var/lib/zabbix/scripts/ owner: zabbix group: zabbix mode: 0755 with_items: - 1c_license_server.sh - 1c_license_files.sh ================================================ FILE: playbooks/roles/srv1c_ls/tests/inventory ================================================ localhost ================================================ FILE: playbooks/roles/srv1c_ls/tests/test.yml ================================================ --- - hosts: localhost remote_user: root roles: - 1c_ls ================================================ FILE: playbooks/roles/srv1c_ls/vars/main.yml ================================================ --- # vars file for 1c_ls packages: - zabbix-sender ================================================ FILE: playbooks/roles/srv1c_ws/.travis.yml ================================================ --- language: python python: "2.7" # Use the new container infrastructure sudo: false # Install ansible addons: apt: packages: - python-pip install: # Install ansible - pip install ansible # Check ansible version - ansible --version # Create ansible.cfg with correct roles_path - printf '[defaults]\nroles_path=../' >ansible.cfg script: # Basic role syntax check - ansible-playbook tests/test.yml -i tests/inventory --syntax-check notifications: webhooks: https://galaxy.ansible.com/api/v1/notifications/ ================================================ FILE: playbooks/roles/srv1c_ws/README.md ================================================ Role Name ========= A brief description of the role goes here. Requirements ------------ Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. Role Variables -------------- A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. Dependencies ------------ A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. Example Playbook ---------------- Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: - hosts: servers roles: - { role: username.rolename, x: 42 } License ------- BSD Author Information ------------------ An optional section for the role authors to include contact information, or a website (HTML is not allowed). ================================================ FILE: playbooks/roles/srv1c_ws/defaults/main.yml ================================================ --- # defaults file for 1c_ws ================================================ FILE: playbooks/roles/srv1c_ws/handlers/main.yml ================================================ --- # handlers file for 1c_ws - name: Add host to group for restart zabbix agent group_by: key: hosts_to_restart ================================================ FILE: playbooks/roles/srv1c_ws/tasks/main.yml ================================================ --- # tasks file for 1c_ws #- name: Install required packages - include: "install-{{ ansible_os_family }}.yml" - name: Copy zabbix userparameter files for work server copy: src: '{{ item }}' dest: '{{ zabbix_configs }}' owner: root group: root mode: 0644 with_items: - userparameter_1c-ws.conf - userparameter_1c-lf.conf notify: - Add host to group for restart zabbix agent - name: Copy scripts for work server copy: src: '{{ item }}' dest: /var/lib/zabbix/scripts/ owner: zabbix group: zabbix mode: 0755 with_items: - 1c_work_server.sh - 1c_license_files.sh - name: Make directory for 1C tech log file: name: /var/log/1C owner: usr1cv8 group: grp1cv8 state: directory mode: 0750 - name: Make directory for 1C tech log file: name: /var/log/1C/zabbix owner: usr1cv8 group: grp1cv8 state: directory mode: 0750 - name: Make directory for problem log file: name: /var/log/1C/zabbix/problem_log owner: zabbix group: grp1cv8 state: directory mode: 0750 - name: Attach zabbix user to 1C group user: name: zabbix groups: grp1cv8 append: yes ================================================ FILE: playbooks/roles/srv1c_ws/tests/inventory ================================================ localhost ================================================ FILE: playbooks/roles/srv1c_ws/tests/test.yml ================================================ --- - hosts: localhost remote_user: root roles: - 1c_ws ================================================ FILE: playbooks/roles/srv1c_ws/vars/main.yml ================================================ --- # vars file for 1c_ws packages: - gawk ================================================ FILE: playbooks/tasks/install-Debian.yml ================================================ - name: Install required packages apt: name: '{{ packages }}' state: 'latest' ================================================ FILE: playbooks/tasks/install-RedHat.yml ================================================ - name: Install required packages yum: name: '{{ packages }}' state: 'latest' ================================================ FILE: scripts/1c_central_server.sh ================================================ #!/bin/bash # # Мониторинг 1С Предприятия 8.3 (центральный сервер) # # (c) 2020-2023, Алексей Ю. Федотов # # Email: fedotov@kaminsoft.ru # WORK_DIR=$(dirname "${0}" | sed -r 's/\\/\//g; s/^(.{1}):/\/\1/') # Включить опцию extglob если отключена (используется в 1c_common_module.sh) shopt -q extglob || shopt -s extglob source "${WORK_DIR}/1c_common_module.sh" 2>/dev/null || { echo "ОШИБКА: Не найден файл 1c_common_module.sh!" ; exit 1; } function get_clusters_sessions { CLSTR_LIST=${1##*#} for CURR_CLSTR in ${CLSTR_LIST//;/ }; do get_sessions_list "${1%#*}" "${CURR_CLSTR%%,*}" | ( if [[ -s ${IB_CACHE} ]]; then awk -v cluster="CL#${CURR_CLSTR%%,*}" -v OFS=':' -F':' \ 'FNR==NR{ if ( $0 ~ "^"substr(cluster,4) ) { split($0, ib_uuid, " "); ss["IB#"ib_uuid[2]]=0; }; next } BEGIN { ss[cluster]=0; } { if ( $0 ~ "^FMT#") { split($0,a,"#|:"); for (i in a) { f[a[i]]=i-1 } } else if ( length(f) > 0 ) { if ( $f["app-id"] ~ /(cl|wc)/ ) { app_id="cl" } else { app_id = $f["app-id"] } ib_mark="IB#"$f["infobase"]; ss[cluster]++; ss[ib_mark]++; if ( app_id != "cl" ) { sc[app_id,cluster]++; sc[app_id,ib_mark]++ } if ( $f["hibernate"] == "yes" ) { sc["hb",cluster]++; sc["hb",ib_mark]++ } if ( $f["duration-current"] != 0) { as[cluster]++; as[ib_mark]++; if ( asd[app_id,cluster] < $f["duration-current"] ) { asd[app_id,cluster]=$f["duration-current"]; asd[app_id,ib_mark]=$f["duration-current"]; if ( app_id == "cl" ) { asu[cluster]=$f["user-name"]" ("$f["session-id"]")"; asu[ib_mark]=$f["user-name"]" ("$f["session-id"]")" } } else if ( asd[app_id,ib_mark] < $f["duration-current"] ) { asd[app_id,ib_mark]=$f["duration-current"]; if ( app_id == "cl" ) { asu[ib_mark]=$f["user-name"]" ("$f["session-id"]")"; } } } } } END { for (i in ss) { print i,ss[i]?ss[i]:0,sc["bg",i]?sc["bg",i]:0,sc["hb",i]?sc["hb",i]:0, sc["ws",i]?sc["ws",i]:0,sc["hs",i]?sc["hs",i]:0,as[i]?as[i]:0, asd["cl",i]?asd["cl",i]:0,asd["bg",i]?asd["bg",i]:0, asd["ws",i]?asd["ws",i]:0,asd["hs",i]?asd["hs",i]:0,asu[i] } }' "${IB_CACHE}" - else awk -v cluster="CL#${CURR_CLSTR%%,*}" -v OFS=':' -F':' \ 'BEGIN { ss[cluster]=0; } { if ( $0 ~ "^FMT#") { split($0,a,"#|:"); for (i in a) { f[a[i]]=i-1 } } else if ( length(f) > 0 ) { if ( $f["app-id"] ~ /(cl|wc)/ ) { app_id="cl" } else { app_id = $f["app-id"] } ss[cluster]++; if ( app_id != "cl" ) { sc[app_id,cluster]++ } if ( $f["hibernate"] == "yes" ) { sc["hb",cluster]++ } if ( $f["duration-current"] != 0) { as[cluster]++; if ( asd[app_id,cluster] < $f["duration-current"] ) { asd[app_id,cluster]=$f["duration-current"]; if ( app_id == "cl" ) { asu[cluster]=$f["user-name"]" ("$f["session-id"]")"; } } } } } END { for (i in ss) { print i,ss[i]?ss[i]:0,sc["bg",i]?sc["bg",i]:0,sc["hb",i]?sc["hb",i]:0, sc["ws",i]?sc["ws",i]:0,sc["hs",i]?sc["hs",i]:0,as[i]?as[i]:0, asd["cl",i]?asd["cl",i]:0,asd["bg",i]?asd["bg",i]:0, asd["ws",i]?asd["ws",i]:0,asd["hs",i]?asd["hs",i]:0,asu[i] } }' fi ) done } function get_session_amounts { check_clusters_cache ( execute_tasks get_clusters_sessions $( pop_clusters_list self ) ) | \ awk -F: -v OFS=':' '{ print $0; if ($1 !~ /^IB/) { sc["all"]+=$2; sc["bg"]+=$3; sc["hb"]+=$4; sc["ws"]+=$5; sc["hs"]+=$6; sc["as"]+=$7; if ( asd["cl"] < $8 ) { asd["cl"]=$8; } if ( asd["bg"] < $9 ) { asd["bg"]=$9; } if ( asd["ws"] < $10 ) { asd["ws"]=$10; } if ( asd["hs"] < $11 ) { asd["hs"]=$11; } } } END { print "summary",sc["all"]?sc["all"]:0,sc["bg"]?sc["bg"]:0,sc["hb"]?sc["hb"]:0,\ sc["ws"]?sc["ws"]:0,sc["hs"]?sc["hs"]:0,sc["as"]?sc["as"]:0,asd["cl"]?asd["cl"]:0,\ asd["bg"]?asd["bg"]:0,asd["ws"]?asd["ws"]:0,asd["hs"]?asd["hs"]:0 }' | sed 's// /g' } function get_infobases_restrictions { if [[ -z ${IS_WINDOWS} ]]; then COMMAND_PREFIX=( sudo -u "${USR1CV8}" ) else COMMAND_PREFIX=() fi get_server_directory | xargs -I server_directory "${COMMAND_PREFIX[@]}" find server_directory -maxdepth 2 -name 1CV8Clst.lst -exec grep DBMS -A1 {} + | perl -pe 's/([^}],)\r?\n/\1/' | perl -pe 's/.*{(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}),.+{([01]),([0-9]+),([0-9]+),.+},([01]),.*/IB#\1,\2,\3,\4,\5/' | awk -v current_date="$(date +%Y%m%d%H%M%S)" -F, '{ if ( $2 == "1" && $3 < current_date && $4 > current_date ) { sl=1 } else { sl=0 }; print $1","sl","$5}' } case ${1} in sessions) shift; make_ras_params "${@}"; get_session_amounts ;; infobases) shift 2; make_ras_params "${@}"; get_infobases_list self;; clusters) shift; make_ras_params "${@}"; get_clusters_list self ;; ib_restrict) get_infobases_restrictions ;; *) error "${ERROR_UNKNOWN_MODE}" ;; esac ================================================ FILE: scripts/1c_common_module.sh ================================================ #!/bin/bash # # Мониторинг 1С Предприятия 8.3 (общие переменные и функции) # # (c) 2019-2023, Алексей Ю. Федотов # # Email: fedotov@kaminsoft.ru # # Вывести сообщение об ошибке переданное в аргументе и выйти с кодом 1 function error { echo "ОШИБКА: ${1}" >&2 ; exit 1 } # Системный пользователь для запуска сервера 1С Предприятия export USR1CV8="usr1cv8" # Тип операционной системы GNU/Linux или MS Windows [[ "$(uname -s)" != "Linux" ]] && { IS_WINDOWS=1; export IS_WINDOWS; } # Имя сервера, используемое в кластере 1С Предприятия [[ -z ${IS_WINDOWS} ]] && HOSTNAME=$(hostname -s) || HOSTNAME=$(hostname) export HOSTNAME # Добавление пути к бинарным файлам 1С Предприятия в переменную PATH RAC_PATH="$( find /opt/1C/ /opt/1cv8/ /c/Program\ Files*/1cv8/ -regextype awk -regex ".*/rac([.]exe)?" -name "rac*" -print -quit 2>/dev/null )" if [[ -n ${RAC_PATH} ]]; then export PATH="${PATH}:${RAC_PATH%/*}" else error "Не найдена платформа 1С Предприятия!" fi # Проверить инициализацию переменной TMPDIR [[ -z ${TMPDIR} ]] && export TMPDIR="/tmp" # Файл списка кластеров export CLSTR_CACHE="${TMPDIR}/1c_clusters_cache" # Параметры взаимодействия с сервисом RAS RAS_PORTS="1545" RAS_TIMEOUT="1.5" RAS_AUTH="" # Максимальное число параллельных потоков (половина от числа ядер ЦПУ) MAX_THREADS=$(( $(nproc) / 2 )) && [[ ${MAX_THREADS} -eq 0 ]] && MAX_THREADS=1 # Общие для всех скриптов тексты ошибок export ERROR_UNKNOWN_MODE="Неизвестный режим работы скрипта!" export ERROR_UNKNOWN_PARAM="Неизвестный параметр для данного режима работы скрипта!" # Файл списка информационных баз export IB_CACHE=${TMPDIR}/1c_infobase_cache # Вывести разделительную строку длинной ${1} из символов ${2} # По-умолчанию раделительная строка - это 80 символов "-" function put_brack_line { [[ -n ${1} ]] && LIMIT=${1} || LIMIT=80 [[ -n ${2} ]] && CHAR=${2} || CHAR="-" printf "%*s\n" "${LIMIT}" "${CHAR}" | sed "s/ /${CHAR}/g" } # Выполнить серию команд ${1} с параметром, являющимся элементом массива ${@}, следующим за ${1} function execute_tasks { [[ ${#@} -le 1 ]] && exit # Если список задач пуст, то выходим TASK_CMD=${1} shift export -f "${TASK_CMD?}" echo "${@}" | xargs -d' ' -P${MAX_THREADS} -I task_args bash -c "${TASK_CMD} \${@}" _ task_args } # Проверить наличие ring license и вернуть путь до ring function check_ring_license { [[ -z ${IS_WINDOWS} ]] && RING_CONF="/etc/1C/1CE/ring-commands.cfg" || RING_CONF="/C/ProgramData/1C/1CE/ring-commands.cfg" [[ ! -f ${RING_CONF} ]] && error "Не установлена утилита ring!" LIC_TOOL=$(grep license-tools "${RING_CONF}" | sed -r 's/.+file: //; s/\\/\//g; s/^(.{1}):/\/\1/') [[ -z ${LIC_TOOL} ]] && error "Не установлена утилита license-tools!" ls "${LIC_TOOL%\/*\/*}"/*ring*/ring* } # Установить параметры взаимодействия с сервисом RAS # Метод устанавливает значения, указанные в параметрах: # * ${1} - номер порта RAS # * ${2} - максимальное время ожидания ответа RAS # * ${3} - пользователь администратор кластрера # * ${4} - пароль пользователя администратора кластера function make_ras_params { [[ -n ${1} ]] && RAS_PORTS=${1} [[ -n ${2} ]] && RAS_TIMEOUT=${2} [[ -n ${3} ]] && RAS_AUTH="--cluster-user=${3}" [[ -n ${4} ]] && RAS_AUTH+=" --cluster-pwd=${4}" export RAS_PORTS RAS_TIMEOUT RAS_AUTH } # Сохранить список UUID кластеров во временный файл function push_clusters_uuid { CURR_CLSTR=$( timeout -s HUP "${RAS_TIMEOUT}" rac cluster list "${1%%:*}:${RAS_PORT}" 2>/dev/null | awk -v FS=' +: +' '/^($|cluster|name|port)/ { if ($1){ print $2 } else {print "==="} }' | awk -v FS='\n' -v RS='={3}\n' -v OFS=',' -v ORS=';' '$1=$1' ) [[ -n ${CURR_CLSTR} ]] && echo "${1%%:*}:${RAS_PORT}#${CURR_CLSTR}" | sed 's/,;/;/g' } # Сохранить список кластеров во временный файл function push_clusters_list { if echo ${$} 2>/dev/null > "${CACHE_FILENAME}.lock"; then trap 'rm -f "${CACHE_FILENAME}.lock"; exit ${?}' INT TERM EXIT execute_tasks push_clusters_uuid "${@}" | if [[ -z ${IS_WINDOWS} ]]; then cat; else iconv -f CP866 -t UTF-8; fi >| "${CACHE_FILENAME}" rm -f "${CACHE_FILENAME}.lock" fi } # Вывести список кластеров из временных файлов: # - если в первом параметре указано self, то выводится только список кластеров текущего сервера function pop_clusters_list { find "${TMPDIR}" -maxdepth 1 -regextype awk -regex ".*_(${RAS_PORTS//,/|})" -name "$( basename "${CLSTR_CACHE}" )_*" | grep -qv "^$" || error "Не найдено ни одного файла списка кластеров!" # ВАЖНО: Для этого блока включается extglob (возможно имеет смысл переделать) if [[ -n ${1} && ${1} == "self" ]]; then grep -i "^${HOSTNAME}" "${CLSTR_CACHE}_"?(${RAS_PORTS//,/|}) | sed -re 's/^([/][^:]+:)?//' else cat "${CLSTR_CACHE}_"?(${RAS_PORTS//,/|}) fi | sed 's/ //g; s/"//g' } # Вывести список кластеров в формате json: # - если в первом параметре указано self, то выводится только список кластеров текущего сервера function get_clusters_list { pop_clusters_list "${1}" | awk -F, -v RS=";\n?" -v ORS="" 'BEGIN {print "{\"data\":[" } { sub(".*#",""); print (NR!=1?",":"")"{\"{#CLSTR_UUID}\":\""$1"\",\"{#CLSTR_NAME}\":\""$3"\"}" } END {ORS="\n"; print "]}" }' | sed 's// /g' } # Проверить актуальность файла списка кластеров function check_clusters_cache { # Получим список менеджеров кластеров, в которых участвует данный сервер, следующего вида: # <имя_сервера>:<номер_порта_0>[|<номер_порта_1>[|..<номер_порта_N>]] readarray -t RMNGR_LIST < <( if [ -z "${IS_WINDOWS}" ]; then pgrep -ax rphost; else wmic path win32_process where "caption like 'rphost%'" get CommandLine /format:csv | grep rphost; fi | sed -r 's/.*-regport ([^ ]+).*/\0|\1/; s/.*-reghost ([^ ]+).*\|/\1:/' | sort -u | awk -F: '{ if ( clstr_list[$1]== "" ) { clstr_list[$1]=$2 } \ else { clstr_list[$1]=clstr_list[$1]"|"$2 } } \ END { for ( i in clstr_list ) { print i":"clstr_list[i]} }' ) # Проверка необходимости обновления временного файла: # - если временный файл не существует # - если количество строк во временном файле отличается от количества элементов # списка менеджеров кластеров # - если временный файл старше 1 часа set -o noclobber for RAS_PORT in ${RAS_PORTS//,/ }; do CACHE_FILENAME="${CLSTR_CACHE}_${RAS_PORT}" export RAS_PORT CACHE_FILENAME if [[ -e ${CACHE_FILENAME} ]]; then if [[ ${1} == "lost" ]]; then cp "${CACHE_FILENAME}" "${CACHE_FILENAME}.${$}" && trap 'rm -f "${CACHE_FILENAME}.${$}"; exit ${?}' INT TERM EXIT for CURR_RMNGR in "${RMNGR_LIST[@]}"; do CURR_LOST=$( grep "^${CURR_RMNGR%:*}" "${CACHE_FILENAME}.${$}" | \ sed -re "s/[^:^;]+,(${CURR_RMNGR#*:}),[^;]+;//" ) sed -i -re "s/^${CURR_RMNGR%:*}.*$/${CURR_LOST}/; /[^:]+:$/d" "${CACHE_FILENAME}.${$}" done grep -v "^$" "${CACHE_FILENAME}.${$}" || [[ ${#RMNGR_LIST[@]} -ne $(grep -vc "^$" "${CACHE_FILENAME}") ]] && push_clusters_list "${RMNGR_LIST[@]}" rm -f "${CACHE_FILENAME}.${$}" &>/dev/null elif [[ ${#RMNGR_LIST[@]} -ne $(grep -vc "^$" "${CACHE_FILENAME}") || $(date -r "${CACHE_FILENAME}" "+%s") -lt $(date -d "last hour" "+%s") ]]; then push_clusters_list "${RMNGR_LIST[@]}" fi else push_clusters_list "${RMNGR_LIST[@]}" fi done } # Доступная производительность процессов рабочих серверов # Выводит список: # <имя_хоста>:<средняя_производительность> function get_processes_perfomance { CLSTR_LIST=${1#*#} for CURR_CLSTR in ${CLSTR_LIST//;/ }; do timeout -s HUP "${RAS_TIMEOUT}" rac process list "--cluster=${CURR_CLSTR%%,*}" \ ${RAS_AUTH} "${1%#*}" 2>/dev/null | \ awk '/^(host|available-perfomance|$)/' | perl -pe "s/.*: ([^.]+).*\n/\1:/" | \ awk -F: '{ apc[$1]+=1; aps[$1]+=$2 } END { for (i in apc) { print i":"aps[i]/apc[i] } }' done } # Получить каталоги серверов 1С function get_server_directory { if [ -z "${IS_WINDOWS}" ]; then pgrep -a ragent | sed -r 's/.*-d ([^ ]+).*/\1/' else wmic path win32_process where "caption like 'ragent.exe'" get commandline /format:csv | \ awk -F, '/ragent/ { print $2 }' | sed -re 's/.*-d "([^"]+).*/\1/; s/^/\//; s/\\/\//g; s/://' fi | sed -re 's/(.*)[/]$/\1/' | sort -u | grep -v "^$" #TODO: Возможно имеет смысл задавать значение SRV1CV8_DATA в случае если его не удалось определить из строки запуска # т.е. в списке значений будет пресутствовать пустая строка # - для Linux, пустую строку можно заменить значением полученным в результате работы похжей команды # $(awk -v uid="^$(awk '/Uid/ {print $2}' /proc/"$(pgrep ragent)"/status 2>/dev/null)$" -F: \ # '$3 ~ uid {print $6}' /etc/passwd)/.1cv8/1C/1cv8" # ВАЖНО: однако следует учитывать имено pid процесса ragent-а у которого не указан каталог сервера! # - для Windows, остуствие указанного каталога сервера возможно толькопри ручном запуске ragent } # Cписок информационных баз в виде json + файл кэша (идентификаторы: кластер, информационная база) # Список кластеров берется из файла кэша кластеров 1c_clusters_cache # - если в первом параметре указано self, то выводится только список информационных баз текущего сервера function get_infobases_list { cat /dev/null > "${IB_CACHE}" readarray -t SERVERS_LIST < <( pop_clusters_list "${1}" ) BASE_INFO='{"data":[ ' MAX_THREADS=0 BASE_INFO+="$( execute_tasks get_clusters_infobases "${SERVERS_LIST[@]}" )" echo "${BASE_INFO%, } ]}" | sed 's// /g' } # Список информационных баз кластеров, указанного в ${1} сервера 1С, в формате json # В параметр ${1} передается строка вида: # [<имя_сервера>:]<идентификатор_кластера>,<порт_rmngr>,<имя_кластера>;[<идентификатор_кластера>,<порт_rmngr>,<имя_кластера>;[...]] # где # <имя_сервера> - необязательный параметр, указывает адрес сервера 1С (не указывается, если требуется получить список с текущего сервера) # <идентификатор_кластера> - идентификатор (UUID) кластера # <порт_rmngr> - порт на котором работает процесс rmngr данного кластера # <имя_кластера> - имя кластера # комбинация <идентификатор_кластера>,<порт_rmngr>,<имя_кластера>; может встречаться в строке столько раз, # сколько имеется кластеров на указанном (<имя_сервера>) сервере 1С Предприятия function get_clusters_infobases { RMNGR_HOST=${1%%#*} CLUSTERS_LIST=${1#*#} for CURRENT_CLUSTER in ${CLUSTERS_LIST//;/ }; do readarray -t BASE_LIST < <( timeout -s HUP "${RAS_TIMEOUT}" rac infobase summary list \ --cluster "${CURRENT_CLUSTER%%,*}" ${RAS_AUTH} "${RMNGR_HOST}" 2>/dev/null | if [[ -z ${IS_WINDOWS} ]]; then cat; else iconv -f CP866 -t UTF-8; fi | awk -v FS=' +: +' '/^(infobase|name|)(\s|$)/ { if ( $2 ) { print $2 } else { print "===" } }' | awk -v FS='\n' -v RS='={3}\n' -v OFS='|' '$1=$1' | sed 's/|$//' ) for CURRENT_BASE in "${BASE_LIST[@]}"; do echo "{ \"{#CLSTR_UUID}\":\"${CURRENT_CLUSTER%%,*}\",\"{#CLSTR_NAME}\":\"${CURRENT_CLUSTER##*,}\",\"{#IB_UUID}\":\"${CURRENT_BASE%%|*}\",\"{#IB_NAME}\":\"${CURRENT_BASE#*|}\" }, " echo "${CURRENT_CLUSTER%%,*} ${CURRENT_BASE%%|*}" >> "${IB_CACHE}" done done } # Список сеансов кластера, указанного в параметрах: # - ${1} - имя сервера 1С # - ${2} - UUID кластера # - ${3} - необязательный, если принимает значение "license", то выводится информация # только о сеансах, потребляющих клиентскую лицензию function get_sessions_list { SERVER_NAME=${1} CLUSTER_UUID=${2} SESSION_FORMAT="session:session-id:infobase:user-name:app-id:hibernate:duration-current:data-separation" LICENSE_FORMAT="session:full-name:rmngr-address" timeout -s HUP "${RAS_TIMEOUT}" rac session list --cluster="${CLUSTER_UUID}" \ ${RAS_AUTH} "${SERVER_NAME}" 2>/dev/null | if [[ -z ${IS_WINDOWS} ]]; then cat; else iconv -f CP866 -t UTF-8; fi | awk -v FS=' +: +' -v format=${SESSION_FORMAT} \ 'BEGIN { print "FMT#"format"\n" } ( $0 ~ "^("gensub(":","|","g",format)"|)($| )" ) { if ( $1 == "app-id" ) { switch ($2) { case "WebClient": print "wc"; break; case /1CV8/: print "cl"; break; case "BackgroundJob": print "bg"; break; case "WSConnection": print "ws"; break; case "HTTPServiceConnection": print "hs"; break; default: print $2; } } else { if ($1 == "user-name" && $2 == "" ) { print "empty" } else { print $2; } } }' | awk -F':' -v RS='' -v OFS=':' '$1=$1' | ( if [[ ${3} != "license" ]]; then cat; else awk -F':' -v OFS=":" -v format="${LICENSE_FORMAT#*:}" 'FNR==NR{licenses[$1]=gensub("^[^:]+(:|$)","","g",$0); next} ($1 in licenses || $0 ~ "^FMT#") { if ( $0 ~ "^FMT#" ) { print $0,format } else { print $0,licenses[$1] } }' \ <( timeout -s HUP "${RAS_TIMEOUT}" rac session list --licenses --cluster="${CLUSTER_UUID}" \ ${RAS_AUTH} "${SERVER_NAME}" 2>/dev/null | awk -v FS=' +: +' -v format="${LICENSE_FORMAT}" '( $0 ~ "^("gensub(":","|","g",format)"|)($| )" ) { if ( $1 == "full-name" ) { value=gensub("[^\"].*/", "", "g", $2) } else { value=$2 }; print value||(!$1)?value:"n/a" }' | awk -v RS='' -v OFS=':' '$1=$1' ) - ; fi ) } export -f get_sessions_list ================================================ FILE: scripts/1c_license_files.sh ================================================ #!/bin/bash # # Мониторинг 1С Предприятия 8.3 (файлы программных лицензий) # # (c) 2023, Алексей Ю. Федотов # # Email: fedotov@kaminsoft.ru # WORK_DIR=$(dirname "${0}" | sed -r 's/\\/\//g; s/^(.{1}):/\/\1/') # Включить опцию extglob если отключена (используется в 1c_common_module.sh) shopt -q extglob || shopt -s extglob source "${WORK_DIR}/1c_common_module.sh" 2>/dev/null || { echo "ОШИБКА: Не найден файл 1c_common_module.sh!" ; exit 1; } # Проверяет соответствие оборудования на компьютере оборудованию, # которое было зафиксировано в момент активации лицензии # - в качестве превого параметра указывается путь до утилиты ring # - в качестве второго параметра указывается имя лицензии function license_check { [[ -z "${1}" ]] && error "${ERROR_UNKNOWN_PARAM}" RESULT=$("${1}" license validate --name "${2}" --send-statistics false 2>/dev/null) && echo "Ok" || echo "${RESULT}" } # Возвращает сводную информацию по файлам лицензий # ВАЖНО: Не использует для этих целей утилиту ring, поскольку: # - утилита ring имеет длительный отклик и высокую нагрузку на ЦПУ при большом количестве файлов лицензий # - утилита ring не выводит информацию о сроке действия лицензии для срочных лицензий (актуально для версии 0.15.0-2) # ВАЖНО: Анализируется информация, содержащаяся в файле лицензии, что было не всегда, поэтому в случае # файла "старого формата" функция может возвращать "пустой результат" function get_licenses_info { ( find /var/1C/licenses/ "${ALLUSERSPROFILE}/Application\ Data/1C/licenses" "${ALLUSERSPROFILE}/1C/licenses" -maxdepth 1 -name "*.lic" \ -exec awk '/^(Номер продукта|Product code|Срок действия|Valid till)/ { str[FILENAME]=str[FILENAME]?str[FILENAME]":"gensub("^[^:]+[:] *","","g",$0):FILENAME":"gensub("^[^:]+[:] *","","g",$0) } END { for (i in str) { print str[i] }}' {} \; 2>/dev/null ) | sed -re 's/\r//g' | xargs -I license_file basename license_file | sed -re 's/(0{7}10{3}1)5/\10/; s/(0{7}[10]0)10{3}/\10500/; s/:[0-9]{9}0*/:/; s/^([^:]+[:][^:]*[:])([0-9]{2})[.]([0-9]{2})[.]([0-9]{4}).*/\1\4\3\2/' | awk -F: -v OFS=':' '{ if ($2) { client+=$2 } else {server++ }; print $1,$2?$2:"s",gensub("^[^:]+:[^:]*:","","g",$0) } END { print "summary",server?server:0,client?client:0 }' } # Возвращает список файлов программных лицензий в формате json # - в качестве первого параметра указывается путь до утилиты ring function get_license_list { [[ -z ${1} ]] && error "${ERROR_UNKNOWN_PARAM}" "${1}" license list --send-statistics false 2>/dev/null | sed -re '/^$/d; s/([0-9]+)[-]([0-9]+)[^"]+"([0-9]+[.]lic)".*/\1 \2 \3/' | awk -F' ' -v ORS='' 'BEGIN { print "{\"data\":[" } { print (NR!=1?",":"")"{ \"{#NUMBER}\":\""$2"\",\"{#PIN}\":\""$1"\",\"{#FILE}\":\""$3"\" }" } END { print "]}" }' } case ${1} in check) license_check "$(check_ring_license)" "${2}";; info) get_licenses_info ;; list) get_license_list "$(check_ring_license)" ;; *) error "${ERROR_UNKNOWN_MODE}" ;; esac ================================================ FILE: scripts/1c_license_server.sh ================================================ #!/bin/bash # # Мониторинг 1С Предприятия 8.3 (сервер лицензирования) # # (c) 2019-2023, Алексей Ю. Федотов # # Email: fedotov@kaminsoft.ru # WORK_DIR=$(dirname "${0}" | sed -r 's/\\/\//g; s/^(.{1}):/\/\1/') # Включить опцию extglob если отключена (используется в 1c_common_module.sh) shopt -q extglob || shopt -s extglob source "${WORK_DIR}/1c_common_module.sh" 2>/dev/null || { echo "ОШИБКА: Не найден файл 1c_common_module.sh!" ; exit 1; } function get_license_counts { CLSTR_LIST=${1##*#} for CURR_CLSTR in ${CLSTR_LIST//;/ }; do # Выводим первую строку (строка формата), после чего формируем список уникальных сеансов для испключения # повторного учета выданной лицензии в случае УО > 1 (см. #86) ( { read -r ; echo "$REPLY" ; sort -u; } < <( get_sessions_list "${1%#*}" "${CURR_CLSTR%%,*}" license ) ) | if [[ -s ${IB_CACHE} ]]; then awk -F':' -v OFS=':' -v hostname="${HOSTNAME,,}" -v cluster="CL#${CURR_CLSTR%%,*}" \ 'FNR==NR{ if ($0 ~ "^"substr(cluster,4)) { split($0, ib_uuid, " "); sc["IB#"ib_uuid[2]]=0 }; next} BEGIN { sc[cluster]=0 } { print; if ( $0 ~ "^FMT#") { split($0,a,"#|:"); for (i in a) { f[a[i]]=i-1 } } else if ( length(f) > 0 ) { ib_mark="IB#"$f["infobase"]; sc[cluster]++; sc[ib_mark]++; uc[cluster][$f["user-name"]]; uc[ib_mark][$f["user-name"]]; if ( index(tolower($f["rmngr-address"]), hostname) > 0 ) { hc[cluster]++; hc[ib_mark]++; } if ($f["app-id"] == "wc") { wc[cluster]++; wc[ib_mark]++; } if ($f["rmngr-address"] == "n/a") { cc[cluster]++; cc[ib_mark]++; } } } END { for (i in sc) { print i,hc[i]?hc[i]:0,length(uc[i]),sc[i]?sc[i]:0,cc[i]?cc[i]:0,wc[i]?wc[i]:0 } }' "${IB_CACHE}" - else awk -F':' -v OFS=':' -v hostname="${HOSTNAME,,}" -v cluster="CL#${CURR_CLSTR%%,*}" \ 'BEGIN { sc[cluster]=0 } { print; if ( $0 ~ "^FMT#") { split($0,a,"#|:"); for (i in a) { f[a[i]]=i-1 } } else if ( length(f) > 0 ) { sc[cluster]++; uc[cluster][$f["user-name"]]; if ( index(tolower($f["rmngr-address"]), hostname) > 0 ) { hc[cluster]++ } if ($f["app-id"] == "wc") { wc[cluster]++ } if ($f["rmngr-address"] == "n/a") { cc[cluster]++ } } } END { for (i in sc) { print i,hc[i]?hc[i]:0,length(uc[i]),sc[i]?sc[i]:0,cc[i]?cc[i]:0,wc[i]?wc[i]:0 } }' fi done } function used_license { export MAX_THREADS=0 # Отключаем ограничение по количеству параллельно выполняемых задач check_clusters_cache readarray LIC_LIST < <( find /var/1C/licenses "${ALLUSERSPROFILE}/Application\ Data/1C/licenses" "${ALLUSERSPROFILE}/1C/licenses" \ -maxdepth 1 -name "*.lic" -exec basename {} \; 2>/dev/null ) ( execute_tasks get_license_counts $( pop_clusters_list ) ) | \ awk -F: -v OFS=':' -v hostname="${HOSTNAME,,}" -v lic_list="${LIC_LIST[*]}" '{ switch ($0) { case /^FMT#/: split($0,a,"#|:"); for (i in a) { f[a[i]]=i-1 }; break case /^(IB|CL)#/: print; break default: sc++; uc[$f["user-name"]]; if ( index(tolower($f["rmngr-address"]), hostname) > 0 ) { hc++; lic[gensub("\"","","g",$f["full-name"])]++; } if ($f["app-id"] == "wc") { wc++ } if ($f["rmngr-address"] == "n/a") { cc++ } } } END { split(lic_list, lic_files, " "); for ( i in lic_files ) { print "LIC#"lic_files[i],lic[lic_files[i]]?lic[lic_files[i]]:0 } print "summary",hc?hc:0,length(uc),sc?sc:0,cc?cc:0,wc?wc:0 }' } # Проверяет наличие процессов сервера 1С для кластера по указанным параметрам: # - ${1} - должен принимать заничение rmngr или rphost # - ${2} - UUID кластера, к которому подключен сервер лицензирования function check_cluster_process { RAC_PARAM=$(echo "${1}" | sed 's/rmngr/manager/; s/rphost/process/') PROCESS_UUID=$(timeout -s HUP "${RAS_TIMEOUT}" rac "${RAC_PARAM}" list --cluster "${2}" ${RAS_AUTH} \ "$( awk -F# -v cluster="${2}" '$0 ~ cluster { print $1 }' "${CLSTR_CACHE}_"?(${RAS_PORTS//,/|}) )" 2>/dev/null | awk -v FS=" +: *" -v filter="${RAC_PARAM}" '( $0 ~ "^("filter"|host|)( |$)" ) { print $2}' | awk -v RS='' -v OFS=':' '$1=$1' | awk -F":" -v hostname="${HOSTNAME,,}" '( tolower($0) ~ ":"hostname ) { print $1 }') if [[ -z ${PROCESS_UUID} ]]; then echo 0 else if [[ -z ${IS_WINDOWS} ]]; then pgrep "${1}" -a else wmic path win32_process where "caption like '${1}.exe'" get commandline /format:csv fi | awk "/${PROCESS_UUID//$'\n'/|}|${2}/ { count++ } END { print count?count:0 }" fi } case ${1} in process) CLSTR_UUID=${2}; CHECK_MODE=${3} ; shift 2 ;;& used|infobases|clusters|process) shift; make_ras_params "${@}" ;;& used) used_license ;; infobases) get_infobases_list;; clusters) get_clusters_list ;; process) check_cluster_process "${CHECK_MODE}" "${CLSTR_UUID}" ;; *) error "${ERROR_UNKNOWN_MODE}" ;; esac ================================================ FILE: scripts/1c_work_server.sh ================================================ #!/bin/bash # # Мониторинг 1С Предприятия 8.3 (рабочий сервер) # # (c) 2019-2023, Алексей Ю. Федотов # # Email: fedotov@kaminsoft.ru # WORK_DIR=$(dirname "${0}" | sed -r 's/\\/\//g; s/^(.{1}):/\/\1/') # Включить опцию extglob если отключена (используется в 1c_common_module.sh) shopt -q extglob || shopt -s extglob source "${WORK_DIR}/1c_common_module.sh" 2>/dev/null || { echo "ОШИБКА: Не найден файл 1c_common_module.sh!" ; exit 1; } # Коды завершения процедуры архивирования файлов технологического журнала export DUMP_CODE_0=0 # Архивированение файлов ТЖ выполнено успешно export DUMP_CODE_1=1 # Файл архива ТЖ уже существует export DUMP_CODE_2=2 # При архивировании файлов ТЖ возникли ошибки export DUMP_CODE_3=3 # Не удалось выполнить архивирование ТЖ на удаленом сервере function check_log_dir { [[ ! -d "${1}/zabbix/${2}" ]] && error "Неверно задан каталог технологического журнала!" } function get_calls_info { MODE=${1} [[ -n ${2} ]] && TOP_LIMIT=${2} || TOP_LIMIT=25 case ${MODE} in count) echo "Кол-во | Длит-ть,с | СрДл-ть,мс | Контекст";; cpu) echo "Процессор,с (%) | Длит-ть,с | Кол-во | СрДл-ть,мс | Контекст";; duration) echo "Длительность,с (%) | Кол-во | СрДл-ть,мс | Процессор | Контекст";; lazy) echo "Длит-ть,с | Кол-во | СрДл-ть,мс | Процессор | Контекст";; dur_avg) echo "СрДл-ть,с | Длит-ть,с | Кол-во | Процессор | Контекст";; memory) echo "Память,МБ | СрДл-ть,мс | СрПр-ор,мс | Кол-во | Контекст";; iobytes) echo "Объем IO,МБ | Длит-ть,с | Процессор | Кол-во | Контекст";; *) error "${ERROR_UNKNOWN_PARAM}" ;; esac put_brack_line cat "${LOG_DIR}"/rphost_*/"${LOG_FILE}.log" 2>/dev/null | awk "/CALL,.*(Context|Module)/" | \ sed -re 's/,Module=(.*),Method=/,Context=ОбщийМодуль.ФоновыйВызов : ОбщийМодуль.\1.Модуль./' | \ sed -re "s/[0-9]+:[0-9]+.[0-9]+-//; s/,Method=[^,]+//; s/,[a-zA-Z:]+=/,/g" | \ awk -F, -v mode="${MODE}" '{ if ($4) {count[$4"->"$5]+=1; durations[$4"->"$5]+=$1; \ cpus[$4"->"$5]+=$9; iobytes[$4"->"$5]+=$7+$8; duration[$4]+=$1; cpu[$4]+=$9; \ if ( mempeak[$4"->"$5] < $6 ) { mempeak[$4"->"$5]=$6; } } } \ END { for ( i in count ) { \ if ( mode == "count" ) { printf "%6d | %9.2f | %10.2f | %s\n", count[i], \ durations[i]/1000000, durations[i]/count[i]/1000, i } \ else if ( mode == "cpu" ) { printf "%8.2f (%4.1f) | %9.2f | %6d | %10.2f | %s\n", \ cpus[i]/1000000, cpus[i]/cpu[substr(i,0,index(i,"->")-1)]*100, durations[i]/1000000, count[i], durations[i]/count[i]/1000, i } \ else if ( mode == "lazy" ) { printf "%f@%9.2f | %6d | %10.2f | %9.2f | %s\n", \ durations[i]/cpus[i], durations[i]/1000000, count[i], durations[i]/count[i]/1000, cpus[i]/1000000, i } \ else if ( mode == "dur_avg" ) { printf "%9.2f | %9.2f | %6d | %9.2f | %s\n", \ durations[i]/count[i]/1000000, durations[i]/1000000, count[i], cpus[i]/1000000, i } \ else if ( mode == "duration" ) { printf "%11.2f (%4.1f) | %6d | %10.2f | %9.2f | %s\n", \ durations[i]/1000000, durations[i]/duration[substr(i,0,index(i, "->")-1)]*100, count[i], durations[i]/count[i]/1000, cpus[i]/1000000, i } \ else if ( mode == "memory" ) { printf "%9.2f | %10.2f | %10.2f | %6d | %s\n", \ mempeak[i]/1024/1024, durations[i]/count[i]/1000, cpus[i]/count[i]/1000, count[i], i } \ else if ( mode == "iobytes" ) { printf "%11.2f | %9.2f | %9.2f | %6d | %s\n", \ iobytes[i]/1024/1024, durations[i]/1000000, cpus[i]/1000000, count[i], i } \ } }' | \ sort -rn | head -n "${TOP_LIMIT}" | awk -v mode="${MODE}" -F"@" '{ if ( mode == "lazy" ) { print $2 } else { print $0 } }' } function get_locks_info { STORE_PERIOD=30 # Срок хранения архивов ТЖ, содержащих информацию о проблемах - 30 дней WAIT_LIMIT=${1} function save_logs { if [[ $(echo "${1}" | grep -ic "${HOSTNAME}") -ne 0 ]]; then DUMP_RESULT=$(dump_logs "${LOG_DIR}" "${LOG_FILE}") else DUMP_RESULT=$(zabbix_get -s "${1}" -k 1c.ws.dump_logs["${LOG_DIR}","${LOG_FILE}"] 2>/dev/null) [[ -z ${DUMP_RESULT} || ${DUMP_RESULT} -eq ${DUMP_CODE_2} ]] && DUMP_RESULT=${DUMP_CODE_3} fi [[ ${DUMP_RESULT} -gt 1 ]] && DUMP_TEXT="ОШИБКА: не удалось сохранить файлы технологического журнала!" || DUMP_TEXT="Файлы технологического журнала сохранены (${LOG_DIR%/*}/problem_log/${LOG_DIR##*/}-${LOG_FILE}.tgz)" [[ -n ${DUMP_RESULT} ]] && echo "[${1} (${DUMP_RESULT})] ${DUMP_TEXT}" && unset DUMP_RESULT } echo "lock: $(cat "${LOG_DIR}"/rphost_*/"${LOG_FILE}.log" 2>/dev/null | grep -c ',TLOCK,')" readarray -t RESULT < <(cat "${LOG_DIR}"/rphost_*/"${LOG_FILE}.log" 2>/dev/null | awk "/(TDEADLOCK|TTIMEOUT|TLOCK.*,WaitConnections=[0-9]+)/" | sed -re "s/[0-9]{2}:[0-9]{2}.[0-9]{6}-//; s/,[a-zA-Z\:]+=/,/g" | awk -F"," -v lts="${WAIT_LIMIT}" 'BEGIN {dl=0; to=0; lw=0} { if ($2 == "TDEADLOCK") {dl+=1} else if ($2 == "TTIMEOUT") { to+=1 } else { lw+=$1; lws[$4"->"$6]+=$1; } } END { print "timeout: "to""; print "deadlock: "dl""; print "wait: "lw/1000000""; if ( lw > 0 ) { print "Ожидания на блокировках (установлен порог "lts" сек):"; for ( i in lws ) { print "> "i" - "lws[i]/1000000" сек." } } }') echo "${RESULT[@]}" | perl -pe 's/\s?/\n/g' if [[ "${RESULT[1]%<*}" != 0 || "${RESULT[3]%<*}" != 0 || $( awk -v value="${RESULT[5]%<*}" -v limit="${WAIT_LIMIT}" 'BEGIN { print ( value > limit ) }' ) == 1 ]]; then shift; make_ras_params "${@}" check_clusters_cache for CURRENT_HOST in $( pop_clusters_list ); do CLSTR_LIST=${CURRENT_HOST#*#} for CURR_CLSTR in ${CLSTR_LIST//;/ }; do SRV_LIST+=( $(timeout -s HUP "${RAS_TIMEOUT}" rac server list --cluster="${CURR_CLSTR%,*}" \ ${RAS_AUTH} "${CURRENT_HOST%#*}" 2>/dev/null| grep agent-host | sort -u | \ sed -r "s/.*: (.*)$/\1/; s/\"//g") ) done done fi export -f dump_logs execute_tasks save_logs $(echo "${SRV_LIST[@]}" | perl -pe 's/ /\n/g' | sort -u) find "${LOG_DIR%/*}/problem_log/" -mtime +${STORE_PERIOD} -name "*.tgz" -delete 2>/dev/null } function get_excps_info { for PROCESS in "${PROCESS_NAMES[@]}"; do EXCP_COUNT=$(cat "${LOG_DIR}"/"${PROCESS}"_*/"${LOG_FILE}.log" 2>/dev/null | grep -c ",EXCP,") echo "${PROCESS}: $([[ -n ${EXCP_COUNT} ]] && echo "${EXCP_COUNT}" || echo 0)" done } function get_memory_counts { RPHOST_PID_HASH="${TMPDIR}/1c_rphost_pid_hash" PROC_REGEX="$(echo "${PROCESS_NAMES[*]}" | sed 's/ /|/g')" if [[ -z "${IS_WINDOWS}" ]]; then PROC_PIDS="$( pgrep -d, "${PROC_REGEX}" )" ps -hwwp "${PROC_PIDS:-1}" -o comm,pid,rss,cmd -k pid | sed -re 's/^([^ ]+) +([0-9]+) +([0-9]+) +/\1,\2,\3,/' else wmic path win32_process where "caption like 'ragent%' or caption like 'rmngr%' or caption like 'rphost%'" \ get caption,processid,workingsetsize,commandline /format:csv 2>/dev/null | sed -re 's/^[^,]+,([^,]+),([^,]+),([^,]+),(.*)/\1,\3,\4,\2/' fi | awk -F, -v proc_regex="${PROC_REGEX}" -v mem_in_kb="${IS_WINDOWS:-1024}" -v pid_hash="$( cat "${RPHOST_PID_HASH}" 2>/dev/null )" \ 'BEGIN { split(proc_regex, proc_names, "|"); proc_regex+="[^,]*,[0-9]+,[0-9]+" } $0 ~ proc_regex { proc_name=gensub(/[.].+/,"","g",$1) proc_pids[proc_name][$2] proc[proc_name,"memory"]+=$3 } END { for ( i in proc_names ) { proc_flag=""; pid_list=""; proc_name=proc_names[i] switch (proc_name) { case "ragent": if ($4 ~ /(\/|-)debug(\s|$)/ ) proc_flag=1; else proc_flag=0 break case "rphost": if ( length(proc_pids[proc_name]) > 0 ) { for (j in proc_pids[proc_name]) pid_list=pid_list?pid_list","j:j } hash_command="echo "pid_list" | md5sum | sed \"s/ .*//\"" (hash_command | getline new_hash) > 0 close(hash_command) if ( pid_hash == new_hash ) { proc_flag=0 } else { proc_flag=1 } print new_hash > "'"${RPHOST_PID_HASH}"'" break } print proc_name":",length(proc_pids[proc_name]),proc[proc_name,"memory"]*mem_in_kb,proc_flag } }' } # Архивирование файлов ТЖ с именем ${2} из каталога ${1} в problem_log function dump_logs { # TODO: Проверка наличия каталога problem_log и возможности записи в него if [[ -f ${1%/*}/problem_log/${1##*/}-${2}.tgz ]]; then DUMP_RESULT=${DUMP_CODE_1} else cd "${1}" 2>/dev/null && tar czf "../problem_log/${1##*/}-${2}.tgz" ./rphost_*/"${2}".log && \ DUMP_RESULT=${DUMP_CODE_0} || DUMP_RESULT=${DUMP_CODE_2} fi echo "${DUMP_RESULT}" } function get_physical_memory { if [[ -z ${IS_WINDOWS} ]]; then free -b | grep -m1 "^[^ ]" | awk '{ print $2 }' else wmic computersystem get totalphysicalmemory /format:csv | awk -F, '/[0-9]+/ {print $2}' fi } function get_available_perfomance { check_clusters_cache ( execute_tasks get_processes_perfomance $( pop_clusters_list ) ) | grep -i "${HOSTNAME}" | awk -F: '{ apc+=1; aps+=$2 } END { if ( apc > 0) { print aps/apc } else { print "0" } }' } case ${1} in calls | locks | excps) check_log_dir "${2}" "${1}"; export LOG_FILE=$(date --date="last hour" "+%y%m%d%H"); export LOG_DIR="${2%/}/zabbix/${1}" ;;& excps|memory) PROCESS_NAMES=(ragent rmngr rphost) ;;& calls) shift 2; get_calls_info "${@}" ;; locks) shift 2; get_locks_info "${@}" ;; excps) shift 2; get_excps_info "${@}" ;; memory) get_memory_counts ;; ram) get_physical_memory ;; dump_logs) shift; dump_logs "${@}" ;; perfomance) shift; make_ras_params "${@}"; get_available_perfomance ;; *) error "${ERROR_UNKNOWN_MODE}" ;; esac