В процессе трудовой деятельности пришлось столкнуться с одной задачей. Есть некоторый продукт, работающий под Windows и состоящий из нескольких компонент — ядра, протокола, моста и канала доставки. Часть компонент написана на JAVA, часть на C++. Каналов доставки может быть от одного до двадцати. Каждый компонент использует свою БД (в качестве СУБД выступает MS SQL Server). Для компонент, написанных на JAVA, структура БД создается автоматически, для компонент, написанных на C++ структуру нужно создавать вручную SQL скриптом. Каждый компонент должен работать как сервис в системе. Все это добро должно быть тщательно протестировано. Для каждого теста необходимо пересоздавать БД и системные сервисы.
Проделав это дело один раз, я задумался, а как бы этот процесс можно было максимально автоматизировать (действия не сложные, но рутинные). Решение должно быть простым и не требующим установки какого-либо софта. Поэтому сразу же выбор пал на древний, но проверенный временем файл сценариев на встроенном командном языке — Bat. Заодно проверим, на что bat скрипты еще способны.
Все наши действия с помощью скрипта постараемся логировать. Для этого задаем имя лог-файла:
@set LOGFILE="log.%DATE%_%TIME:~0,2%-%TIME:~3,2%-%TIME:~6,2%.txt"
Получим на выходе файл подобный этому: log.14.02.2012_10-41-42.txt
Однако, если вы любитель работать с самого утра или глубокой ночью, то может получиться и так: log.14.02.2012_ 1-03-28.txt
С радостью рассмотрю ваши предложения, как можно это исправить.
Задаем выбор действия в батнике, которое будет ему передаваться в качестве первого параметра
@rem INSTALL или REMOVEset ACTION=%1
Задаем строку для подключения к БД:
@rem Формат: либо просто IP@rem Если не указаны ни параметр -U, ни параметр -P, то программа sqlcmd пытается подключиться с помощью режима проверки подлинности Microsoft Windows. @rem При этом используется учетная запись пользователя Windows, который запустил программу sqlcmd.set url_db="(local)"
Далее для каждого компонента задаем специфические параметры. Нужно ли работать с компонентом, или нет (y или n) будем задавать из параметров
@rem Заполняем эти параметры, y - если будем создавать глобальную БД для ядра@set WorkWhisKernel=%2@set KernelDbName=Kernel@set KernelUserName=Kernel@set KernelUserPassw=Kernel@rem Модель восстановления для базы. Доступны значения FULL | BULK_LOGGED | SIMPLE@set KernelRecovery_mode=SIMPLE
@rem Заполняем эти параметры, y - если будем создавать БД для канала@set WorkWhisChannel=%3@set ChannelDbName=Channel@set ChannelDbCount=10@set ChannelUserName=Channel@set ChannelUserPassw=Channel@rem Модель восстановления для базы. Доступны значения FULL | BULK_LOGGED | SIMPLE@set ChannelRecovery_mode=SIMPLE@rem Использвать знак "_" для разделения имя БД и ее номера. Например test_1 или test1@set Use_underline=y@rem Версия базы данных для Channel@set ChannelVersion=mssql.Channel_5.4.3.2
@rem Заполняем эти параметры, y - если будем создавать БД для Bridge@set WorkWhisBridge=%4@set BridgeDbName=Bridge@set BridgeUserName=Bridge@set BridgeUserPassw=Bridge@rem Модель восстановления для базы. Доступны значения FULL | BULK_LOGGED | SIMPLE@set BridgeRecovery_mode=SIMPLE@rem Версия базы данных для Bridge@set BridgeVersion=mssql.Bridge_1.1.3
Далее, в зависимости от типа действия с базой (INSTALL или REMOVE), а также от того, нужно ли работать с этим компонентом будем выполнять определенные действия.
Для удобства, действия на БД вынесены в отдельные sql скрипты. Работать будем через утилиту sqlcmd. Если на вашей машине не установлен MSSQL Server, то sqlcmd можно скачать и использовать в рамках дистрибутива SQL Server Express Utility.
if "%WorkWhisI_k%" == "y" (if "%ACTION%" == "INSTALL" (@rem Создаем глобальную БД для Kernel и пользователя и ее
sqlcmd -S %url_db% -i Kernel.sql >> %LOGFILE%)if "%ACTION%" == "REMOVE" (@rem Удаляем БД informer_kernel и ее пользователя
sqlcmd -S %url_db% -i KernelDel.sql >> %LOGFILE%))
Как вы можете видеть, тут все очень просто. Однако есть и небольшая проблема, а именно как передать имена БД, пользователя и т.д. в SQL скрипт, вызываемый через sqlcmd?
На самом деле все очень просто:
--Создаем логин для БДCREATE LOGIN $(KernelUserName)
WITH PASSWORD = '$(KernelUserPassw)', CHECK_POLICY=OFF;
GO
--Создаем БДUSE master;CREATE DATABASE $(KernelDbName);
GO
--Устанавливаем режим восстановления БДALTER DATABASE $(KernelDbName) SET RECOVERY $(KernelRecovery_mode)
GO
--Создаем пользователя для БДUSE $(KernelDbName);CREATE USER $(KernelUserName) FOR LOGIN $(KernelUserName)
WITH DEFAULT_SCHEMA = dbo;
GO
--Назначаем роль для пользователяUSE $(KernelDbName);
EXEC sp_addrolemember 'db_owner', '$(KernelUserName)'
GO
Похожие строчки будут и для компонента Bridge. А вот для компонент Channel у нас будет, допустим, десять. Поэтому добавим простой цикл:
for /L %%i in (1,1,%ChannelDbCount%) do (if "%Use_underline%" == "y" (@set ChannelDbNameCounter=%ChannelDbName%_%%i) else (@set ChannelDbNameCounter=%ChannelDbName%%%i)%ChannelDbNameCounter%@rem Создаем БД cp_service
sqlcmd -S %url_db% -i Channel.sql >> %LOGFILE%@rem Создаем структуру БД для cp_service
sqlcmd -S %url_db% -i %ChannelVersion%.sql >> %LOGFILE%)
Далее устанавливаем сервисы наших компонент в системе (приведу пример только для компонент Channel):
@rem Количество Channels
SET ChannelDbCount=10@rem Тип запуска
SET run_type=demand@rem Зависимости от сервисов (separated by / (forward slash))
SET depend=MSSQLSERVER
for /L %%i in (1,1,%ChannelDbCount%) do (@rem устанавливаем сервис Channel. Для установки компонента как сервиса используется его внутреняя реализация
ruby Channel%%i.exe --install@rem для сервиса устанавливаем 3 перезагрузки с интервалом в 1 мин. если возникла фатальная ошибка
sc failure Channel%%i reset= 240 actions= restart/60000/restart/60000/restart/600000@rem тип запуска сервиса и зависимости
sc config Channel%%i start= %run_type% depend= %depend%)
Запускаем установленные сервисы:for /L %%i in (1,1,%ChannelDbCount%) do (
sc start Channel%%i)
Код для остановки и удаления сервисов приводить не буду, т.к. он идентичен.
Теперь скомпонуем все, о чем мы писали ранее.
Запуск скрипта развертывания продукта с параметрами install.bat y y y
@rem Запускаем наш главный скрипт с параметрами на создание баз
DbManage.bat INSTALL %1 %2 %3
@rem Устанавливаем сервисы
InstallServices.bat
@rem Запускаем все сервисы
StartServices.bat
Запуск скрипта удаления продукта с параметрами install.bat y y y
@rem Останавливаем сервисы, если они запущены
StopServices.bat
@rem Делаем пазу для того, чтобы все сервисы успели остановиться@rem Данные метод может не работать в не серверной Windows
timeout /t 10
@rem Удаляем старые сервисы
RemoveServices.bat
@rem Запускаем главный скрипт с параметрами на удаление
start DbManage.bat REMOVE %1 %2 %3
В итоге мы получили выполнение всех желаемых действий, о которых написано в начале данного опуса с минимальным количеством усилий, а именно запуском обычного батника с несколькими параметрами.
Реализация конечно не идеальна, но свои функции выполняет. Старые добрые «батники» еще на что-то способны, хотя пора переходить на PoverShell. Однако и там есть свои сложности, в частности с сертификатами и подписанием скриптов.
В общем, как говорится, идеального решения не существует.