Skip to content
КомандаУправлениеСеансами.os 30 KiB
Newer Older

///////////////////////////////////////////////////////////////////////////////////////////////////
// Прикладной интерфейс

Перем мНастройки;
Перем Лог;
Перем мИдентификаторКластера;
Перем мИдентификаторБазы;

Процедура ЗарегистрироватьКоманду(Знач ИмяКоманды, Знач Парсер) Экспорт
	
	ОписаниеКоманды = Парсер.ОписаниеКоманды(ИмяКоманды, "Управление сеансами информационной базы");
	Парсер.ДобавитьПозиционныйПараметрКоманды(ОписаниеКоманды, "Действие", "lock|unlock|kill");
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "-ras", "Сетевой адрес RAS, по умолчанию localhost:1545");
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "-rac", "Команда запуска RAC, по умолчанию находим в каталоге установки 1с");
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, "-db", "Имя информационной базы");
	
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-db-user",
		"Пользователь информационной базы");

	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-db-pwd",
		"Пароль пользователя информационной базы");
		
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-cluster-admin",
		"Администратор кластера");

	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-cluster-pwd",
		"Пароль администратора кластера");
	
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-v8version",
		"Маска версии платформы 1С");
	
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-lockmessage",
		"Сообщение блокировки");
		
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-lockuccode",
		"Ключ разрешения запуска");
	
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-lockstart",
		"Время старта блокировки пользователей, время указываем как '2040-12-31T23:59:59'");
	
	Парсер.ДобавитьИменованныйПараметрКоманды(ОписаниеКоманды, 
		"-lockstartat",
		"Время старта блокировки через n сек");
	
	Парсер.ДобавитьКоманду(ОписаниеКоманды);
	
КонецПроцедуры

Функция ВыполнитьКоманду(Знач ПараметрыКоманды) Экспорт

	ПрочитатьПараметры(ПараметрыКоманды);
	
	Если Не ПараметрыВведеныКорректно() Тогда
		Возврат МенеджерКомандПриложения.РезультатыКоманд().НеверныеПараметры;
	КонецЕсли;
	
	Если мНастройки.Действие = "lock" Тогда
		УстановитьСтатусБлокировкиСеансов(Истина);
	ИначеЕсли мНастройки.Действие = "unlock" Тогда
		УстановитьСтатусБлокировкиСеансов(Ложь);
	ИначеЕсли мНастройки.Действие = "kill" Тогда
		УдалитьВсеСеансыИСоединенияБазы();
	Иначе
		Лог.Ошибка("Неизвестное действие: " + мНастройки.Действие);
		Возврат МенеджерКомандПриложения.РезультатыКоманд().НеверныеПараметры;
	КонецЕсли;

	Возврат МенеджерКомандПриложения.РезультатыКоманд().Успех;
КонецФункции

Процедура ПрочитатьПараметры(Знач ПараметрыКоманды)
	мНастройки = Новый Структура;
	
	Для Каждого КЗ Из ПараметрыКоманды Цикл
		Лог.Отладка(КЗ.Ключ + " = " + КЗ.Значение);
	КонецЦикла;
	
	мНастройки.Вставить("АдресСервераАдминистрирования", ПараметрыКоманды["-ras"]);
	мНастройки.Вставить("ПутьКлиентаАдминистрирования", ПараметрыКоманды["-rac"]);
	мНастройки.Вставить("ИмяБазыДанных", ПараметрыКоманды["-db"]);
	мНастройки.Вставить("АдминистраторИБ", ПараметрыКоманды["-db-user"]);
	мНастройки.Вставить("ПарольАдминистратораИБ", ПараметрыКоманды["-db-pwd"]);
	мНастройки.Вставить("АдминистраторКластера", ПараметрыКоманды["-cluster-admin"]);
	мНастройки.Вставить("ПарольАдминистратораКластера", ПараметрыКоманды["-cluster-pwd"]);
	мНастройки.Вставить("ИспользуемаяВерсияПлатформы", ПараметрыКоманды["-v8version"]);
	мНастройки.Вставить("КлючРазрешенияЗапуска", ПараметрыКоманды["-lockuccode"]);
	мНастройки.Вставить("СообщениеОблокировке", ПараметрыКоманды["-lockmessage"]);
	мНастройки.Вставить("ВремяСтратаБлокировки", ПараметрыКоманды["-lockstart"]);
	мНастройки.Вставить("ВремяСтратаБлокировкиЧерез", ПараметрыКоманды["-lockstartat"]);
	
	
	мНастройки.Вставить("Действие", ПараметрыКоманды["Действие"]);
	
	//Получим путь к платформе если вдруг не установленна
	мНастройки.ПутьКлиентаАдминистрирования = ПолучитьПутьКRAC(мНастройки.ПутьКлиентаАдминистрирования, мНастройки.ИспользуемаяВерсияПлатформы);
	Если ПустаяСтрока(мНастройки.АдресСервераАдминистрирования) Тогда
		мНастройки.АдресСервераАдминистрирования = "localhost:1545";
	КонецЕсли;
КонецПроцедуры

Функция ПараметрыВведеныКорректно()
	
	Успех = Истина;
	
	Если Не ЗначениеЗаполнено(мНастройки.АдресСервераАдминистрирования) Тогда
		Лог.Ошибка("Не указан сервер администрирования");
		Успех = Ложь;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(мНастройки.ПутьКлиентаАдминистрирования) Тогда
		Лог.Ошибка("Не указан клиент администрирования");
		Успех = Ложь;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(мНастройки.ИмяБазыДанных) Тогда
		Лог.Ошибка("Не указано имя базы данных");
		Успех = Ложь;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(мНастройки.Действие) Тогда
		Лог.Ошибка("Не указано действие lock/unlock");
		Успех = Ложь;
	КонецЕсли;
	
	Возврат Успех;
	
КонецФункции

/////////////////////////////////////////////////////////////////////////////////
// Взаимодействие с кластером

Процедура УдалитьВсеСеансыИСоединенияБазы()
	УстановитьСтатусБлокировкиСеансов(Истина);
	ОтключитьСуществующиеСеансы();
	Приостановить(500);
	Сеансы = ПолучитьСписокСеансов();
	Если Сеансы.Количество() Тогда
		Лог.Информация("Пауза перед отключением соединений");
		Приостановить(10000);
		ОтключитьСоединенияСРабочимиПроцессами();
		
КонецПроцедуры

Процедура УстановитьСтатусБлокировкиСеансов(Знач Блокировать)
	
	КлючиАвторизацииВБазе = КлючиАвторизацииВБазе();
	
	ИдентификаторКластера = ИдентификаторКластера();
	ИдентификаторБазы = ИдентификаторБазы();
	
	Если Блокировать Тогда
		КлючРазрешенияЗапускаПоУмолчанию = ИдентификаторБазы;
	Иначе
		КлючРазрешенияЗапускаПоУмолчанию = "";
	КонецЕсли;
	КлючРазрешенияЗапуска = ?(ПустаяСтрока(мНастройки.КлючРазрешенияЗапуска), КлючРазрешенияЗапускаПоУмолчанию, мНастройки.КлючРазрешенияЗапуска);

	ВремяБлокировки = мНастройки.ВремяСтратаБлокировки;
	Если ПустаяСтрока(ВремяБлокировки) И Не ПустаяСтрока(мНастройки.ВремяСтратаБлокировкиЧерез) Тогда
		Секунды = 0;
		Попытка
			Секунды = Число(мНастройки.ВремяСтратаБлокировкиЧерез);
		Исключение
		КонецПопытки;
		
		ВремяБлокировки = Формат(ТекущаяДата()+Секунды,"ДФ='yyyy-MM-ddTHH:mm:ss'");
	КомандаВыполнения = СтрокаЗапускаКлиента() + СтрШаблон("infobase update --infobase=""%3""%4 --cluster=""%1""%2 --sessions-deny=%5 --denied-message=""%6"" --denied-from=""%8"" --permission-code=""%7""",
		ИдентификаторКластера,
		КлючиАвторизацииВКластере(),
		ИдентификаторБазы,
		КлючиАвторизацииВБазе,
		?(Блокировать, "on", "off"), 
		мНастройки.СообщениеОблокировке, 
		КлючРазрешенияЗапуска, 
		ВремяБлокировки) + " "+мНастройки.АдресСервераАдминистрирования;
		
	ЗапуститьПроцесс(КомандаВыполнения);
	
	Лог.Информация("Сеансы " + ?(Блокировать, "запрещены", "разрешены"));
	
Функция КлючиАвторизацииВБазе()
	КлючиАвторизацииВБазе = "";
	Если ЗначениеЗаполнено(мНастройки.АдминистраторИБ) Тогда
		КлючиАвторизацииВБазе = КлючиАвторизацииВБазе + СтрШаблон(" --infobase-user=""%1""", мНастройки.АдминистраторИБ);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(мНастройки.ПарольАдминистратораИБ) Тогда
		КлючиАвторизацииВБазе = КлючиАвторизацииВБазе + СтрШаблон(" --infobase-pwd=""%1""", мНастройки.ПарольАдминистратораИБ);
	КонецЕсли;
	
	Возврат КлючиАвторизацииВБазе;
	
КонецФункции


Функция ИдентификаторКластера()

	Если мИдентификаторКластера = Неопределено Тогда
		Лог.Информация("Получаю список кластеров");
		
	   КомандаВыполнения = СтрокаЗапускаКлиента() + "cluster list" + " "+мНастройки.АдресСервераАдминистрирования;
	   
	   СписокКластеров = ЗапуститьПроцесс(КомандаВыполнения);
	   
	   УИДКластера = Сред(СписокКластеров,(Найти(СписокКластеров,":")+1),Найти(СписокКластеров,"host")-Найти(СписокКластеров,":")-1);	
	   мИдентификаторКластера = СокрЛП(СтрЗаменить(УИДКластера,Символы.ПС,""));
		
	КонецЕсли;
	
	Если ПустаяСтрока(мИдентификаторКластера) Тогда
		ВызватьИсключение "Кластер серверов отсутствует";
	КонецЕсли;
	
	Возврат мИдентификаторКластера;
Функция ИдентификаторБазы()
	Если мИдентификаторБазы = Неопределено Тогда
		мИдентификаторБазы = НайтиБазуВКластере();
	КонецЕсли;
	
	Возврат мИдентификаторБазы;
Функция НайтиБазуВКластере()
	
	КомандаВыполнения = СтрокаЗапускаКлиента() + СтрШаблон("infobase summary list --cluster=""%1""%2",
		ИдентификаторКластера(), 
		КлючиАвторизацииВКластере()) + " "+мНастройки.АдресСервераАдминистрирования;

	Лог.Информация("Получаю список баз кластера");
	
	СписокБазВКластере = СокрЛП(ЗапуститьПроцесс(КомандаВыполнения));    
	Лог.Отладка(СписокБазВКластере);
	ЧислоСтрок = СтрЧислоСтрок(СписокБазВКластере);
	НайденаБазаВКластере = Ложь;
	Для К = 1 По ЧислоСтрок Цикл
		
		СтрокаРазбора = СтрПолучитьСтроку(СписокБазВКластере,К);   
		ПозицияРазделителя = Найти(СтрокаРазбора,":");
		Если Найти(СтрокаРазбора,"infobase")>0 Тогда						
			УИДИБ =  СокрЛП(Сред(СтрокаРазбора,ПозицияРазделителя+1));	
		ИначеЕсли Найти(СтрокаРазбора,"name")>0 Тогда 
			 ИмяБазы = СокрЛП(Сред(СтрокаРазбора,ПозицияРазделителя+1));
			 Если Нрег(ИмяБазы) = НРег(мНастройки.ИмяБазыДанных) Тогда
				Лог.Информация("Получен УИД базы");
				НайденаБазаВКластере = Истина;
				Прервать;
			 КонецЕсли;
		КонецЕсли;
		
	КонецЦикла;
	Если Не НайденаБазаВКластере Тогда
		ВызватьИсключение "База "+мНастройки.ИмяБазыДанных +" не найдена в кластере";
Функция КлючиАвторизацииВКластере()
	КомандаВыполнения = "";
	Если ЗначениеЗаполнено(мНастройки.АдминистраторКластера) Тогда
		КомандаВыполнения = КомандаВыполнения + СтрШаблон(" --cluster-user=""%1""", мНастройки.АдминистраторКластера);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(мНастройки.ПарольАдминистратораКластера) Тогда
		КомандаВыполнения = КомандаВыполнения + СтрШаблон(" --cluster-pwd=""%1""", мНастройки.ПарольАдминистратораКластера);
	КонецЕсли;
	Возврат КомандаВыполнения;
Функция СтрокаЗапускаКлиента()
	Перем ПутьКлиентаАдминистрирования;
	Если ЭтоWindows Тогда 
		ПутьКлиентаАдминистрирования = ЗапускПриложений.ОбернутьВКавычки(мНастройки.ПутьКлиентаАдминистрирования);
	Иначе
		ПутьКлиентаАдминистрирования = мНастройки.ПутьКлиентаАдминистрирования;
	КонецЕсли;
	
	Возврат  ПутьКлиентаАдминистрирования + " ";
	
Функция ЗапуститьПроцесс(Знач СтрокаВыполнения)
	Перем ПаузаОжиданияЧтенияБуфера;
	
	ПаузаОжиданияЧтенияБуфера = 20;
	Лог.Отладка(СтрокаВыполнения);
	Процесс = СоздатьПроцесс(СтрокаВыполнения,,Истина);
	Процесс.Запустить();
	Текст = Новый ТекстовыйДокумент;

	Пока Истина Цикл

		ВывестиДанныеПроцесса(Процесс, Текст);

		Если Процесс.Завершен Тогда
			Процесс.ОжидатьЗавершения(); // финальный сброс буферов
			ВывестиДанныеПроцесса(Процесс, Текст);

		Приостановить(ПаузаОжиданияЧтенияБуфера);

	Если Процесс.КодВозврата = 0 Тогда
		Возврат Текст.ПолучитьТекст();
	Иначе
		ВызватьИсключение "Сообщение от RAS/RAC 
		|" + Текст.ПолучитьТекст();
Процедура ВывестиДанныеПроцесса(Знач Процесс, Знач Приемник)
	Вывод = Процесс.ПотокВывода.Прочитать();
	Ошибки = Процесс.ПотокОшибок.Прочитать();
	Если СтрДлина(Строка(Вывод)) > 0 Тогда
		Приемник.ДобавитьСтроку(Вывод);
	КонецЕсли;
	Если СтрДлина(Строка(Ошибки)) > 0 Тогда
		Приемник.ДобавитьСтроку(Ошибки);
	КонецЕсли;
КонецПроцедуры

Процедура ОтключитьСуществующиеСеансы()

	Лог.Информация("Отключаю существующие сеансы");
	
	СеансыБазы = ПолучитьСписокСеансов();
	Для Каждого Сеанс Из СеансыБазы Цикл
		Попытка
			ОтключитьСеанс(Сеанс);
		Исключение
			Лог.Ошибка(ОписаниеОшибки());
		КонецПопытки;
КонецПроцедуры

Функция ПолучитьСписокСеансов()
	
	ТаблицаСеансов = Новый ТаблицаЗначений;
	ТаблицаСеансов.Колонки.Добавить("Идентификатор");
	ТаблицаСеансов.Колонки.Добавить("Приложение");
	ТаблицаСеансов.Колонки.Добавить("Пользователь");
	ТаблицаСеансов.Колонки.Добавить("НомерСеанса");
	
	КомандаЗапуска = СтрокаЗапускаКлиента() + СтрШаблон("session list --cluster=""%1""%2 --infobase=""%3""",
		ИдентификаторКластера(), 
		КлючиАвторизацииВКластере(),
		ИдентификаторБазы()) + " " + мНастройки.АдресСервераАдминистрирования;
	
	СписокСеансовИБ = ЗапуститьПроцесс(КомандаЗапуска);	
	
	Данные = РазобратьПоток(СписокСеансовИБ);
	
	Для Каждого Элемент Из Данные Цикл
		
		ТекСтрока = ТаблицаСеансов.Добавить();
		ТекСтрока.Идентификатор = Элемент["session"];
		ТекСтрока.Пользователь  = Элемент["user-name"];
		ТекСтрока.Приложение    = Элемент["app-id"];
		ТекСтрока.НомерСеанса   = Элемент["session-id"];

	
	Возврат ТаблицаСеансов;
	
КонецФункции

Процедура ОтключитьСеанс(Знач Сеанс)

	СтрокаВыполнения = СтрокаЗапускаКлиента() + СтрШаблон("session terminate --cluster=""%1""%2 --session=""%3""",
		ИдентификаторКластера(),
		КлючиАвторизацииВКластере(),
		Сеанс.Идентификатор) + " " +мНастройки.АдресСервераАдминистрирования;
	
	Лог.Информация(СтрШаблон("Отключаю сеанс: %1 [%2] (%3)", Сеанс.НомерСеанса, Сеанс.Пользователь, Сеанс.Приложение));
	
Функция ОтключитьСоединенияСРабочимиПроцессами()
	
	Процессы = ПолучитьСписокРабочихПроцессов();
	
	Для Каждого РабочийПроцесс Из Процессы Цикл
		Если РабочийПроцесс["running"] = "yes" Тогда
			
			СписокСоединений = ПолучитьСоединенияРабочегоПроцесса(РабочийПроцесс);
			Для Каждого Соединение Из СписокСоединений Цикл
				
				Попытка
					РазорватьСоединениеСПроцессом(РабочийПроцесс, Соединение);
				Исключение
					Лог.Ошибка(ОписаниеОшибки());
				КонецПопытки;
				
			КонецЦикла;
			
		КонецЕсли;
	КонецЦикла;
КонецФункции

Функция ПолучитьСписокРабочихПроцессов()
	
	КомандаЗапускаПроцессы = СтрокаЗапускаКлиента() + СтрШаблон("process list --cluster=""%1""%2",
		ИдентификаторКластера(), 
		КлючиАвторизацииВКластере()) + " "+мНастройки.АдресСервераАдминистрирования;
	Лог.Информация("Получаю список рабочих процессов...");
	СписокПроцессов = ВыполнитьКоманду(КомандаЗапускаПроцессы);
	
	Возврат РазобратьПоток(СписокПроцессов);
	
КонецФункции

Функция ПолучитьСоединенияРабочегоПроцесса(Знач РабочийПроцесс)
	
	КомандаЗапускаСоединения = СтрокаЗапускаКлиента() + СтрШаблон("connection list --cluster=""%1""%2 --infobase=%3%4 --process=%5",
				ИдентификаторКластера(), 
				КлючиАвторизацииВКластере(),
				ИдентификаторБазы(),
				КлючиАвторизацииВБазе(),
				РабочийПроцесс["process"]) + " "+мНастройки.АдресСервераАдминистрирования;
				
	Лог.Информация("Получаю список соединений...");
	Возврат РазобратьПоток(ВыполнитьКоманду(КомандаЗапускаСоединения));
	
КонецФункции

Функция РазорватьСоединениеСПроцессом(Знач РабочийПроцесс, Знач Соединение)
	
	КомандаРазрывСоединения = СтрокаЗапускаКлиента() + СтрШаблон("connection disconnect --cluster=""%1""%2 --infobase=%3%4 --process=%5 --connection=%6",
						ИдентификаторКластера(), 
						КлючиАвторизацииВКластере(),
						ИдентификаторБазы(),
						КлючиАвторизацииВБазе(),
						РабочийПроцесс["process"],
						Соединение["connection"])+ " "+мНастройки.АдресСервераАдминистрирования;
	
	Сообщение = СтрШаблон("Отключаю соединение %1 [%2] (%3)",
					Соединение["conn-id"],
					Соединение["app-id"],
					Соединение["user-name"]);
					
	Лог.Информация(Сообщение);
	
	Возврат ВыполнитьКоманду(КомандаРазрывСоединения);
	
КонецФункции

Функция РазобратьПоток(Знач Поток) Экспорт
	
	ТД = Новый ТекстовыйДокумент;
	ТД.УстановитьТекст(Поток);
	
	СписокОбъектов = Новый Массив;
	ТекущийОбъект = Неопределено;
	
	Для Сч = 1 По ТД.КоличествоСтрок() Цикл
		
		Текст = ТД.ПолучитьСтроку(Сч);
		Если ПустаяСтрока(Текст) или ТекущийОбъект = Неопределено Тогда
			Если ТекущийОбъект <> Неопределено и ТекущийОбъект.Количество() = 0 Тогда
				Продолжить; // очередная пустая строка подряд
			КонецЕсли;
			 
			ТекущийОбъект = Новый Соответствие;
			СписокОбъектов.Добавить(ТекущийОбъект);
		КонецЕсли;
		
		СтрокаРазбораИмя      = "";
		СтрокаРазбораЗначение = "";
		Если РазобратьНаКлючИЗначение(Текст, СтрокаРазбораИмя, СтрокаРазбораЗначение) Тогда
			ТекущийОбъект[СтрокаРазбораИмя] = СтрокаРазбораЗначение;
		КонецЕсли;
		
	КонецЦикла;
	
	Если ТекущийОбъект <> Неопределено и ТекущийОбъект.Количество() = 0 Тогда
		СписокОбъектов.Удалить(СписокОбъектов.ВГраница());
	КонецЕсли; 
	
	Возврат СписокОбъектов;
	
КонецФункции

Функция ПолучитьПутьКRAC(ТекущийПуть, Знач ВерсияПлатформы="")
	
	Если НЕ ПустаяСтрока(ТекущийПуть) Тогда 
		ФайлУтилиты = Новый Файл(ТекущийПуть);
		Если ФайлУтилиты.Существует() Тогда 
			Лог.Отладка("Текущая версия rac "+ФайлУтилиты.ПолноеИмя);
			Возврат ФайлУтилиты.ПолноеИмя;
		КонецЕсли;
	КонецЕсли;
	
	Если ПустаяСтрока(ВерсияПлатформы) Тогда 
		ВерсияПлатформы="8.3";
	КонецЕсли;
	
	Конфигуратор = Новый УправлениеКонфигуратором;
	ПутьКПлатформе = Конфигуратор.ПолучитьПутьКВерсииПлатформы(ВерсияПлатформы);
	Лог.Отладка("Используемый путь для поиска rac "+ПутьКПлатформе);
	КаталогУстановки = Новый Файл(ПутьКПлатформе);
	Лог.Отладка(КаталогУстановки.Путь);
	
	ИмяФайла = ?(ЭтоWindows, "rac.exe", "rac");
	
	ФайлУтилиты = Новый Файл(ОбъединитьПути(Строка(КаталогУстановки.Путь), ИмяФайла));
	Если ФайлУтилиты.Существует() Тогда 
		Лог.Отладка("Текущая версия rac "+ФайлУтилиты.ПолноеИмя);
		Возврат ФайлУтилиты.ПолноеИмя;
	КонецЕсли;
	
	Возврат ТекущийПуть;

КонецФункции

Функция РазобратьНаКлючИЗначение(Знач СтрокаРазбора, Ключ, Значение)
	
	ПозицияРазделителя = Найти(СтрокаРазбора,":");
	Если ПозицияРазделителя = 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Ключ     = СокрЛП(Лев(СтрокаРазбора,ПозицияРазделителя-1));
	Значение = СокрЛП(Сред(СтрокаРазбора,ПозицияРазделителя+1));
	
	Возврат Истина;
	
КонецФункции

/////////////////////////////////////////////////////////////////////////////////
СистемнаяИнформация = Новый СистемнаяИнформация;
ЭтоWindows = Найти(НРег(СистемнаяИнформация.ВерсияОС), "windows") > 0;
Лог = Логирование.ПолучитьЛог("vanessa.app.deployka");