$ 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ервера лицензирования** предусмотрены следущие графики:
* Использование активированных лицензий

## Правила обнаружения
Для отслеживания использования клиентских лицензий, выдаваемых сервером лицензирования реализованы следующие "разрезы":
* Если сервер лицензирования входит в состав нескольких кластеров 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