Обновление серверов, миграция, вкупе с тенденциями консолидации - все это создает критическую нагрузку на системных администраторов. От того, насколько эффективно администратор способен собрать информацию о текущей конфигурации серверов, зависит успех проекта, насколько гладко и без потерь, возмущений и эксцессов пройдет перенос функций с сервера на сервер. Если вы тщательно документируете все изменения конфигурации, задача кажется достаточно простой и ясной. Если же документирование прихрамывает, к тому же вы унаследовали множество старых серверов, документация по которым утеряна, то вам предстоит большая исследовательская работа. При подготовке проекта по миграции серверного хозяйства для одного из своих клиентов, я столкнулся с проблемой документирования принтерных очередей на серверах печати, работающих под управлением Windows 2000 Server. Нам нужен был простой надежный способ сохранить информацию о существующих настройках, чтобы затем воссоздать эти конфигурации на новых серверах Windows 2003.

Методы переноса настроек принтеров

Утилита Printer Migrator (printmig.exe), входящая в состав Microsoft Windows 2000 Server Resource Kit, обеспечивает простой перенос принтеров с одного сервера на другой. Несложный пользовательский интерфейс позволяет создать принтеры на новом сервере и даже автоматически позволяет загрузить необходимые драйверы принтеров. Все работает прекрасно до тех пор, пока требуется выполнить миграцию принтеров на новый сервер, работающий под управлением той же операционной системы, что и старый сервер, или, по крайней мере, обеспечена прямая совместимость драйверов принтеровх старой и новой серверной операционной системы. К сожалению, в моем случае некоторые принтеры моего клиента требовали использования уникальных драйверов, различающихся для Windows 2000 и Windows 2003. Кроме того, оказалось, что Printer Migrator просто зависает при попытке переноса большого количества принтеров. В результате обсуждения было принято решение выполнить миграцию вручную, то есть вручную создать и настроить все принтеры на новом сервере. После того, как будет завершен этот этап, нам все равно необходимо перенести необходимую информацию в таблицу, чтобы при создании принтеров нам не пришлось метаться между старым и новым серверами.

Как получить информацию о принтерах

Нам необходима следующая информация: имя принтера, описание, название драйвера, имя порта (в данном случае IP-адрес порта TCP/IP), имя очереди, местоположение и комментарий. Эта информация была получена из раздела реестра HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlPrintPrinters. При использовании редактора реестра или команды Reg.exe для экспорта этого раздела реестра, мы получили бы гораздо больше информации в неудобном для работы формате.

Альтернатива заключается в использовании командного интерпретатора Windows, мы же решили пойти по третьему пути - воспользоваться Windows Management Instrumentation (WMI) для получения необходимой информации от провайдера Win32_Printer и сохранения полученной информации в удобном для обработке табличном формате, который можно использовать в Microsoft Excel. Это решение в итоге дает полную информацию, необходимую для выполнения миграции принтеров, а разработанный сценарий может быть повторно использован для документирования других серверов печати в случае возникновения необходимости.

Как работает сценарий

Представленный в листинге 1 сценарий PrinterInfo.vbs начинается с разбора двух обязательных параметров запуска, а именно, имени компьютера, о котором мы собираемся составить отчет, и имени файла результата. Если пользователь не указал параметры запуска, сценарий выводит справочную информацию и завершает работу. Этим действиям соответствует фрагмент А.

Затем сценарий с помощью службы WMI выполняет подключение к указанному пользователем компьютеру, и, при успехе, обращается к провайдеру Win32_Printer этого компьютера. Результатом выполнения данной операции является коллекция, содержащая все установленные на компьютере принтеры, для дальнейшего использования данной коллекции используется переменная colPrinters, см. фрагмент В. Предполагается, что запустивший сценарий пользователь обладает достаточными полномочиями для выполнения запроса к выбранному компьютеру. Это предположение находит выражение в параметре inpersonateLevel, которому присвоено значение impersonate.

Следующим шагом идет создание выходного файла, для этого используется объект Scripting.FileSystemObject. Происходит вызов метода OpenTextFile объекта FSO, которому передается имя файла, полученное из командной строки. Если файл с таким именем уже существует, он будет перезаписан, если же такого файла нет, вызов этого метода создает новый текстовый файл. Затем в файл записывается заголовок, содержащий имена столбцов данных, которые будут записаны в файл. Для получения данных об установленных на компьютере принтерах из коллекции colPrinters используется цикл For Each … Next. В теле цикла для каждого из перечисленных в коллекции принтера выполняется считывание атрибутов имени, описания, порта, имени совместного доступа, драйвера, местоположения и комментарии (соответственно, поля name, description, portname, sharename, drivername, location, и comment). Из собранных сведений формируется строка отчета, которая записывается в текстовый файл, см. фрагмент С. Помимо перечисленных сведений, в строку отчета добавляется также признак, предоставлен ли общий доступ к данному принтеру (свойство Shared). После завершения формирования отчета выполняется закрытие выходного текстового файла. Полученный отчет удобно просматривать в Microsoft Excel.

Как заставить сценарий работать

Сценарий может запускаться на серверах и рабочих станциях Windows 2003/XP/2000, а в качестве целевых серверов печати могут быть указаны компьютеры с Windows XP и более поздними версиями. Это ограничение обусловлено тем, что Windows 2000 не поддерживает свойства Network, Shared и Comment для объектов класса Win32_Printer. Сценарий зависит от доступности службы WMI на компьютере, на котором вы собираетесь запускать сценарий. По умолчании провайдер WMI установлен на всех трех перечисленных платформах. Сценарий работает нормально если пользователь обладает достаточными привилегиями для выполнения обращения к коллекции Win32_Printers. Для запуска сценария может использоваться как cscript, так и wscript. При запуске используется следующий синтаксис:

printerinfo.vbs computername outputfile

Например, для получения отчета printers.tsv о принтерах, обслуживаемых принт-сервером WIN2K3PRT01, следует выполнить команду

printerinfo.vbs WIN2K3PRT01 printers.tsv

Полезный инструмент в арсенал администратора

Даже если вы не планируете перенос серверов печати в ближайшее время, этот сценарий все равно стоит иметь под рукой, хотя бы чтобы документировать существующие серверы печати. В зависимости от ваших текущих потребностей вам может потребоваться более или менее подробная информация о конфигурациях. Полный список свойств провайдера WMI Win32_Printer можно найти на страницах MSDN по адресу http://msdn.microsoft.com/library/default.asp?url=/ library/en-us/wmisdk/wmi/win32_printer.asp.


Листинг 1. Сценарий PrinterInfo.vbs
'=== PrinterInfo.vbs
Option Explicit
On Error Resume Next
'=== Константы, используемые при вызове OpenTextFile
Const ForWriting = 2
Const ForAppending = 8
'=== Получение параметров командной строки
Dim oArgs
Set oArgs = Wscript.Arguments
'=== НАЧАЛО ФРАГМЕНТА A
If oArgs.Count < 2 Then
	WScript.Echo vbCrLf & _
		     "Syntax: printerinfo 
 " & vbCrLf
	WScript.Quit 1
End If
'=== КОНЕЦ ФРАГМЕНТА A
Dim strComputer, outfile
strComputer = oArgs(0)
outfile = oArgs(1)
Dim objSWbemServices, colPrinters, printer
'=== НАЧАЛО ФРАГМЕНТА B
Set objSWbemServices = _
	GetObject("winmgmts:{impersonationLevel=impersonate}"
 & strComputer & "
ootcimv2")
If Err.Number <> 0 Then
	WSCript.Echo "Unable to connect to " & strComputer
 & "!"
	WScript.Quit 1
End If
Set colPrinters = _
	objSWbemServices.ExecQuery("Select * from Win32_Printer
 Where Network ='False'")
'=== КОНЕЦ ФРАГМЕНТА B
'=== Открываем файл outputfile для записи
Dim fs,fsOut
Set fs = CreateObject("Scripting.FileSystemObject")
Set fsOut = fs.OpenTextFile(outfile, ForWriting, True)
'=== Выводим в файл заголовок отчета
Dim strHeader
strHeader = "Name" & vbTab & _
	    "Description" & vbTab & _
	    "Port" & vbTab & _
	    "Share" & vbTab & _
	    "Driver" & vbTab & _
	    "Location" & vbTab & _
	    "Comment" 
fsOut.WriteLine strHeader
'=== НАЧАЛО ФРАГМЕНТА C
For each printer in ColPrinters
	Dim strPrtInfo
	strPrtinfo = printer.name & vbTab & _
		     printer.Description & vbTab & _
		     printer.PortName & vbTab 
	If printer.Shared = True Then
		     strPrtInfo = strPrtInfo & printer.ShareName 
	Else
		     strPrtInfo = strPrtInfo & "Not Shared"
	End If
	strPrtInfo = strPrtInfo & vbTab & _
		     printer.DriverName & vbTab & _
		     printer.Location & vbTab & _
		     printer.Comment
	fsOut.WriteLine strPrtInfo
Next
'=== КОНЕЦ ФРАГМЕНТА C
fsOut.Close
Set fsOut = Nothing
WScript.Echo "Script Complete!"