Изменение настроек программ с сохранением персональных параметров

    Предыстория


    В одной медицинской организации внедряли решения на базе PACS-серверов Orthanc и DICOM-клиента Radiant. В ходе настройки выяснили, что каждый DICOM-клиент должен быть описан в PACS-серверах следующим образом:

    • Имя клиента
    • AE-имя (должно быть уникально)
    • TCP-порт, который автоматически открывается на стороне клиента и принимает DICOM-обследования от PACS-сервера (т.е. сервер как бы толкает их в сторону клиента – инициируя соединение первым)
    • IP-адрес

    После настройки Radiant клиентов получили следующую информацию к размышлению – у каждого клиента настройка ПО с указанными выше параметрами приводила к заполнению файла pacs.xml, который располагался в профиле пользователя (путь: %APPDATA%\RadiantViewer\pacs.xml). При этом конфиг одного клиента от другого отличался минимум двумя параметрами (AE-имя у всех разное, а порт в основном одинаковый, кроме терминальных клиентов, работающих на одном и том же сервере – там порты тоже приходилось назначать разными).

    Пример файла pacs.xml по ссылке:

    Примерно полгода все было хорошо, система заработала…и тут до нас дошли «подводные камни»:

    • Нам нужно ввести в строй несколько новых PACS-серверов, которые подменят старые (где стало заканчиваться место на дисках). PACS сервера в виртуальных машинах, но речь не об этом;
    • Нам нужно как-то централизованно изменить уникальные конфигурации (двумя отличающимися параметрами) на 200 машинах (их количество регулярно увеличивалось);
    • Учитывая темпы роста объемов обследований, решение нужно не разовое, а тиражируемое и регулярное (например, 1 раз в 3-5 месяцев).

    Решение ниже.

    Выбор инструментария для решения задачи


    Вначале были попытки найти какое-то решение, которое на стороне клиента изменяло файл pacs.xml, и вносило в него изменения в список PACS-серверов, не трогая настройки AE-имени и TCP-порта. Windows клиенты на тот момент были на базе как Windows XP, так и Windows 7 – поэтому были попытки написать что-то такое на базе VBScript. Но увы – осилить такую задачу не получилось, ввиду полного отсутствия опыта написания чего-либо сложного и комплексного на этом языке. Попытки же найти и переписать также не увенчались успехом (тут надо отметить, что в голове уже был другой план, поэтому я не долбился с VBScript больше 3-4 часов).

    В итоге я остановился на следующем решении:

    • Собрать групповой политикой все файлы pacs.xml в одном месте на каком ни будь сервере в сетевом ресурсе;
    • Изменить файлы скопом (опыт решения таких задач уже был – с использованием Perl);
    • Также с помощью групповых политик обновить настройки клиентов.

    Сбор файлов с помощью групповой политики


    Самая простая часть – при входе клиента в свой профиль он со своими правами выполняет некий .bat файл, в котором прописано:

    echo off
    If exist %APPDATA%\RadiantViewer\pacs.xml copy %APPDATA%\RadiantViewer\pacs.xml \\srv.test.local\pconfigs$\pacs-%COMPUTERNAME%-%USERNAME%.xml
    

    Таким образом на сервере в скрытом ресурсе будут накапливаться файлы pacs.xml, в имени которых есть информация с какого компьютера и с какого пользователя был скопирован данный конфиг.

    Самое сложное было – дождаться, когда у всех пользователей отработает данная политика.

    Изменение конфигураций с помощью Perl скрипта


    Нам потребуется Active Perl под Windows от компании ActiveState, а также модуль XML::Writer, который можно установить с помощью команды ppm install XML-Writer.

    Сам же скрипт получился довольно простой:

    use XML::Writer;
     
    # Открываем папку с отчетами, обрабатываем ссписок (удаляем лишнее):
    	$report_dir = "C:\\Perl64\\WORK\\PACS-xml3\\";
    	opendir(DIR, "$report_dir") or die "Не могу открыть папку с отчетами!";
    	@report_files = readdir DIR;
    	shift (@report_files); # удаляем точку из элементов массива (.)
    	shift (@report_files); # удаляем две точки из элементов массива (..)
    #	print "@report_files";
    	closedir(DIR);
     
    # Начинаем обрабатывать файлы - по одному за раз. Нужно считать параметр AET и номер порта в переменные.
    foreach $analiz_file (@report_files) 
    {
    	$full_path_to_file="C:\\Perl64\\WORK\\PACS-xml3\\".$analiz_file;
    	open (INFO, $full_path_to_file);
     
    	while ($line = <INFO>)
    	{
    		# Переменные $aet и $port содержат уникальные данные для каждого XML файла:
    		my ($other1, $aet, $other2, $port, $other3) = split /\"/, $line, 5;
    		# Если встречается строка listener - то мы дошли до нужной строчки и можно формировать новый XML:
    		if ($other1 =~ 'listener')
    			{
    				# Формируем новый XML c нужными полями и данными:
    				my $writer = XML::Writer->new(OUTPUT => 'self', DATA_MODE => 1, DATA_INDENT => 2, );
    				$writer->xmlDecl('utf-8');
    				$writer->startTag('pacs');
    				$writer->startTag('listener', ae => $aet, port => $port);
    				$writer->endTag();
    				$writer->startTag('hosts');
    				$writer->startTag('host', name => 'MRT', ae => 'ORTHANC', ip => 'XX.YY.214.17', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
    				$writer->endTag();
    				$writer->startTag('host', name => 'KT', ae => 'ORTHANC2', ip => 'XX.YY.215.253', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
    				$writer->endTag();
    				$writer->startTag('host', name => 'R', ae => 'ORTHANC3', ip => 'XX.YY.215.252', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
    				$writer->endTag();
    				$writer->startTag('host', name => 'KT-20180501-20180831', ae => 'ORTHANC4', ip => 'XX.YY.215.251', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
    				$writer->endTag();
    				$writer->startTag('host', name => 'KT-20180901-20181130', ae => 'ORTHANC5', ip => 'XX.YY.215.250', ts => '1.2.840.10008.1.2.1', port => '4242', maxassoc => '1', allpres => '0', search => '1', protocol => '1', searchcharset => '', wildcards => '3', carets => '0');
    				$writer->endTag();
    				$writer->endTag('hosts');
    				$writer->startTag('presets');
    				$writer->endTag();
    				$writer->startTag('lastsearch', dt => '4', mfid => '1048592');
    				$writer->endTag();
    				$writer->endTag('pacs');
     
    				# Помещаем готовый XML в переменную:
    				my $xml = $writer->end();
    				# Подготавливаем файл для перезаписи:
    				$rewritexml = $full_path_to_file;
    				# Переписываем XML файлы новыми данными:
    				open (NEWXML, ">$rewritexml");
    				print NEWXML $xml;
    				close (NEWXML);				
    			}
    	}
     
    }
    

    Принцип его работы:

    • Открываем каталог, в котором у нас собраны конфигурации pacs.xml от клиентов и помещаем список файлов в массив скаляров (@report_files);
    • В цикле обрабатываем по одному файлу и считываем его построчно;
    • С помощью split дробим каждую строку на 5 частей, используя кавычки как разделитель;
    • Находим строку с словом listener и помещаем в две переменные уникальные для каждого файла данные (AE-имя клиента и номер TCP-порта);
    • После этого просто формируем новый XML-файл, вписываем в него уникальные параметры и далее вставляем нужное количество PACS-серверов с их параметрами – т.е. то, ради чего все затевалось)
    • Переписываем новый XML-файл поверх старого.

    Надо отметить, что на самом деле я использую данный скрипт не полностью автоматически – по сути я копирую собираемые конфиги в отдельный каталог и потом запуская скрипт изменяю их всем скопом. Далее выборочная проверка – и конфиги можно разливать обратно по машинам.

    Распространение измененных pacs.xml файлов по клиентам


    Самое простое, что пришло в голову – внести изменения в уже работающий .bat файл, который собирает конфигурации с клиентов и добавить строку:

    If exist %APPDATA%\RadiantViewer\pacs.xml copy /Y \\srv.test.local\pconfigsnew$\pacs-%COMPUTERNAME%-%USERNAME%.xml %APPDATA%\RadiantViewer\pacs.xml
    

    Итоговый .bat файл выглядит так:

    @echo off
    If exist %APPDATA%\RadiantViewer\pacs.xml copy %APPDATA%\RadiantViewer\pacs.xml \\srv.test.local\pconfigs$\pacs-%COMPUTERNAME%-%USERNAME%.xml
    If exist %APPDATA%\RadiantViewer\pacs.xml copy /Y \\srv.test.local\pconfigsnew$\pacs-%COMPUTERNAME%-%USERNAME%.xml %APPDATA%\RadiantViewer\pacs.xml
    

    Заключение


    Такое вот «наколеночное» решение. Опробовали его уже два раза (в сентябре 2018 и в феврале 2019), пока полет нормальный. Конечно обновляет не 100% клиентов, но близко к этому значению — остальных доделываем удаленно. Скрипт по ссылке.
    Поделиться публикацией

    Комментарии 7

      0

      А почему Perl а не естественный для win powershell?

        0
        Потому что уже был готовый скрипт для решения другой задачи.
        Идея переписать на PowerShell стоит в планах — как нибудь займусь.
        +2
        Сейчас все 2 перловика Хабра проснутся и закидают вас тапками за отсутствие strict, warnings, ООП подхода в работе с файлами и другие сомнительные практики…

        А вообще здорово, что Perl продолжает get shit done вне зависимости от того жив он или нет.
          0
          Аж целых два!? ))))))
          Ну да — у меня есть подборка Perl-скриптов, которые писал сам когда то для различных задач админских — работают и по сей день.
          В принципе какие то вещи действительно лучше написать (или переписать на PowerShell) — но тут просто вспомнилось, что есть уже скриптец, который чуть чуть переписать ))))
          +1
          Orthanc до сих пор не умеет настраивать несколько хранилищ, шел 2019 год. Печально. Либо самому писать storage plugin.
          А вот Conquest server это мог еще с лохматых годов.
          Inobitec DICOM Viewer хранит настройки в реестре пользователя, в такой ситуации проще.

            0
            Чет не нашел на их сайте стоимость этого клиента. Только табличку сравнения Free, Lite и Pro версий.
              0
              Lite-версия примерно как Radiant стоила.
              А зачем вам Radiant на 200 рабочих мест, там прям все рентгенологи? Обычным врачам же хватает или web-интерфейса, или бесплатного K-pacs. Причем некоторым K-Pacs нравится даже больше, чем фирменный софт от одной широкоизвестной в узких кругах питерской конторы рентген-оборудования.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое