Записи с темой: powershell (список заголовков)
16:21 

Regex Lookahead in action

We rise up for the things we believe in over and over again

Уже давно в своей аудиотеке я стараюсь придерживаться определенного формата имен файлов:
01 - Song-name.extension
Именно так - двухзначный номер в альбоме, пробел, тире, пробел, имя, точка, расширение. И откровенно бесит, когда попадаются треки с именами другого формата. Их приходится править. Обычно спасает Mass Rename в каком-нибудь файловом менеджере, но тут ситуация возникла несколько иного рода:
01.Song-Name.extension
Две долбаных точки. Казалось бы - элементарная замена, но она побьет и разделитель имени и расширения. Пришлось впервые на практике применить механизм Lookahead (в данном случае - Negative) из регулярок:

Вот это (?!mp3) в переводе на русский язык означает "включать в выборку то, за чем НЕ следует "mp3".

@музыка: Rasmus Faber - Lombardo

@темы: Scripting, PowerShell

11:03 

Windows DHCP Failover

We rise up for the things we believe in over and over again

Все знают, что в Win2012 сервис DHCP обладает собственным средством обеспечения отказоустойчивости - Failover. Это не кластер, это всего лишь пара серверов, обменивающихся информацией об одних и тех же диапазонах. Сдохнет один - второй возьмет на себя его обязанности.

Штука эта работает, хотя и с нареканиями. Но так уж вышло, что на одной из площадок работает она совсем уж через пень-колоду. И рвануло буквально вчера.

Нужно было по уже старому скрипту добавить новую запись о резервировании IP-адреса для нового устройства. Поначалу все штатно - проверить, что MAC нового устройства ни на каких других DHCP серверах не числится, а если числится - удалить. Затем выбрать из целевого диапазона первый попавшийся свободный адрес, и затем сделать новую запись. И на моменте создания записи получаем ошибку DHCP 20013.

Припыли. Этот код ошибки означает, что у нас бааааааальшие проблемы с JET-базой, в которой DHCP все и хранит. Начинаем разбираться.

База работает. DHCP нормально отдает новые адреса в аренду. В принципе, даже руками создавать записи можно. Но работать руками при наличии автоматизированного средства - фу. Пригляделся к ошибке по-внимательнее. Оказалось, что проблем даже не одна, а целых две. Первая - это сама ошибка 20013, которая не дает записывать в базу. Вторая еще интереснее - целевой адрес выбирается не тот, что ожидалось. Консоль DHCP показывает, что свободным адресом в текущем диапазоне является х.х.х.222, но скрипт почему-то выбирает х.х.х.234. Почему?

А покажи-ка мне все доступные записи о резервировании? Причем именно так, как это делает скрипт:

200 и 250 - это границы, в которых резервируются записи. Этот вариант и показывает, что ВСЕ адреса в диапазоне 200-233 заняты, и свободным, действительно, является аж 234. Консоль же упрямо продолжает твердить, что тот же 222 - свободен, вот прям сейчас можно туда кого-нибудь записать.

Ок, а что нам покажет вот такая запись:

То есть попытаться вытащить из базы все записи о зарезервированных адресах в конкретно заданном диапазоне. Каково же было мое удивление, когда я увидел, что результат этой команды в точности совпадает с той информацией, что нам выдает консоль DHCP! Ясно, в базе данных из-за совершенно криво работающего Failover и его репликации остались просроченные элементы, которые видны при довольно специфичных условиях.

Как лечить? Методов много, но суть одна и та же - новая база. Отличие лишь в том, как и откуда переносить туда данные. Сразу понятно, что любой бекап отпадает, потому что так будет перенесен весь мусор. На ум пришел старый добрый netsh. Проверяем:

netsh dhcp server и далее по тексту.

Утилита показывает ровно то, что нужно - актуальные записи о резервированных адресах. Отлично:
netsh dhcp server export d:\dhcpdb all

После чего раскатываем это дело на пустом DHCP сервере. Беда. Несмотря на то, что в режиме просмотра netsh не показывает мусор, тем не менее, он его так же бережно переносит в бекап и, следовательно, на новый сервер.

Руками переносить все резервированные адреса жуть, как не хочется, их довольно много. Начинаем прикидывать:
- объекты переноса - опции сервера, области, исключения в этих самых областях, опции областей, резервированные адреса, опции адресов. Ну и само собой, настройки DHCP-сервера в целом. Много объектов, да, без автоматизации никак.
- мы можем получить актуальные данные путем запроса всех резервированных адресов в конкретном скопе. Так почему бы всю эту полученную коллекцию и не перенести на свежий DHCP?
Что ж, поехали. Сидя на нашем сервере DHCP-test с развернутым мусорным бекапом и имея резервный пустой сервер с именем DHCP-test2 с поднятой на нем ролью DHCP (база пуста), выполняем:

На выходе получаем свежий DHCP-сервер с нужной информацией и отсутствием в базе всякого мусора. Прогон кода, выбирающего первый свободный адрес, это подтверждает.
Примечательно, что на соседней площадке ровно такой же DHCP-failover работает как часы. Впрочем, там и MS Exchange DAG никогда проблем не вызывала, но это уже другая история. Но похоже, что моя площадка реально проклята, баги собираем полными горстями :)

@музыка: Dhamika - At night I fly

@темы: PowerShell, Scripting

15:33 

NPS

We rise up for the things we believe in over and over again

Этого нет ни в одном официальном документе. Нарваться на это поведение можно только "наступив на грабли". При выполнении командлета Export-NPSConfiguration в целевой файл НЕ БУДУТ записаны никакие определенные на сервере шаблоны: ни шаблоны RADIUS-клиентов, ни шаблоны ключей, ни-че-го. Что полностью противоречит описанию командлета, в котором указано, что он выполняет экспорт всей конфигурации.

Но и это еще не все. При экспорте в файл будут перенесены ключи устройств (shared secrets), причем, открытым текстом. Это нормально, это ожидаемо. Неожиданностью стало, что даже в активной конфигурации, которая вот прямо сейчас крутится на сервере, ключи в устройствах забиты тоже открытым текстом.
Но и это еще не все! Если устройства в конфигурации заданы с ключами на основе шаблона ключей, то записаны эти ключи все равно будут явно, а не в виде ссылки на шаблон. И это заставляет меня очень сильно печалиться. Потому что следствием этого является полная неспособность командлета Set-NPSRADIUSClient задать новому устройству ключ на основе шаблона. Я могу получить объект шаблона ключа, но не могу передать его в устройство. Точнее, могу, но записан ключ будет все равно в явном виде.

И это все, черт побери, совершенно иррационально.

@темы: Этот безумный мир, Security, PowerShell

18:31 

Switch

We rise up for the things we believe in over and over again

Хорошую штуку узнал сегодня про Switch(). Раньше для проверки значений я использовал этот оператор сугубо простым методом:


А оказалось, что можно, например, и вот так:

И еще много чего этот оператор позволяет, вплоть до регулярок!

@музыка: Australis - Truth of faith

@темы: PowerShell, Scripting

10:01 

Xpath #2 - все еще боль...

We rise up for the things we believe in over and over again

... но, как оказалось, очень быстрая. Настолько, что придется к ней привыкнуть, несмотря на вырвиглазный синтаксис. Берем простой код, где в памяти крутится XML-файл на два с половинкой мегабайта (нужно выбрать одну единственную ноду), оформляем его дважды, используя разные подходы, и обрамляем это дело в командлет measure-command {}, который и покажет разницу. Код вот такой:


Разница - больше, чем сто раз в пользу XPath. 9 мс против 1202 мс. СТО РАЗ, КАРЛ!!!

За подобное увеличение скорости работы я готов простить даже то убожество, в каком приходится записывать регистронезависимый поиск в атрибутах ноды. Да-да, я вот про это самое contains бла-бла-бла. А все потому, что Select-XML регистрозависим целиком и полностью. Как в области структуры XML, которую указываем в XPath (имена нод/атрибутов), так и в области значений атрибутов.

Короче, грядет перепись пары-тройки модулей...

@музыка: Dance with the Dead - Skeletons in the Attic

@темы: PowerShell

11:27 

Printer security

We rise up for the things we believe in over and over again

Казалось бы, ситуация с принтерами уже давно отлажена. Ну иногда переезжают они, но какого-то прям ада в этом уже нет. Скрипты работают на полную мощность, снимая изрядную часть нагрузки с сотрудников по отслеживанию и актуализации данных на инфраструктурных серверах. Но идилия не вечна.

Повадились сотрудники печатать на чужих принтерах. Как именно - я не понимаю, учитывая, что сами они принтеры не подключают (я даже не в курсе, знают ли они, как это вообще делается), все выполняется автоматом через политики. И высокому начальству это стало не по нраву.

В общем, спустили задачу. Из всех принтеров в настройках безопасности нужно убрать запись Everyone - Allow Print, а вместо нее подставить группу, в которую входят сотрудники, имеющие право на этом принтере печатать. Группы эти уже давно есть, хоть что-то не придется в авральном порядке создавать.

То есть имеем ситуацию: есть принтер prn-hp1235. В недрах AD есть группа с таким же именем, в ней сидит кучка пользователей, которым этот принтер через политики и подключается. Порядок действий:
- Открыть настройки безопасности принтера,
- вынести оттуда запись с группой Everyone,
- добавить группу prn-hp1235, отметив галкой разрешение Allow Print и только его,
- закрыть настройки принтера.

И так, порядка 600 штук. Весело, правда? :) Вот и я думаю, что весело. Начнем ковыряться.

В свойствах принтера есть такой параметр как PermissionSDDL. Собственно, это и есть запись того, кто и что может делать с объектом, просто записано это в формате SDDL - не самая удобная вещь для понимания. Выглядит она вот так:

Да-да, именно так. И вот эту строку (а это именно одна строка) надо как-то распарсить, изменить, измененное утрамбовать обратно в такую же строку и поменять свойства принтера.

Отправной точкой в исследованиях стала вот эта статья, за которую автору огромное спасибо. Для разбора SDDL-строки она использует объект класса Security.AccessControl.CommonSecurityDescriptor - туда заносится SDDL, там формируется нормальный список объектов и их прав, там же меняется состав этих объектов, а затем при промощи метода GetSDDLForm() формируется новая строка SDDL, которая и заносится в свойства принтера.

В теории все просто. На практике... На практике получаем, что на выходе в разрешениях принтера теряются сущности CREATOR OWNER и APPLICATION PACKAGES. И если с пакетами еще куда ни шло, то потеря CREATOR OWNER может привести к невозможности печати (в сети есть упоминания таких случаев). Что же делать? Искать, на каком этапе теряется информация об этих объектах. Выяснилось, что исчезают они как раз в момент парсинга SDDL в объект CommonSecurityDescriptor. Не умеет он, видимо, с ними работать (хотя почему?).

Пришлось искать замену. И она нашлась в лице System.Security.AccessControl.RawSecurityDescriptor. Конечно, работать с Raw немного муторнее, чем с Common (формирование записи с группой сотрудников с нуля доставило), но результат получился тот, что и заказывали.

Итак, исходная информация:
- имя принт-сервера: ps1
- маска имен принтеров: либо prn*, либо mfd* (multifunctional device)
- SID группы Everyone: S-1-1-0 (константа, не меняется нигде и никогда)

Поехали :)


Пожалуй, за всю мою практику еще не приходилось настолько плотно работать с DACL. Хорошо, что хотя бы SACL тут не участвовал :)

@музыка: Земля Ветров - Город под пеплом

@темы: Scripting, PowerShell

16:39 

Windows Print Services and Locales

We rise up for the things we believe in over and over again

Что имеем? Имеем несколько уже давно развернутых принт-серверов, на них кучу установленных при помощи уже давно написанного набора скриптов принтеров. И появился у нас еще один принт-сервер, на котором тот же самый набор скриптов выдает странный результат - все принтеры по-умолчанию печатают на бумаге формата Letter, а не общепринятого А4. Какого черта?

Оказалось, что тот новый принт-сервер развернули не из образа, как обычно, а поставили с нуля из ISO-файла, и забыли при этом сменить локаль. А локаль, как оказалась, отвечает не только за шрифты, кодировки, но еще и за настройки печати, и именно это стало откровением.

Ок, поменяли локаль, перезагрузили принт-сервер. А принтерам уже пофиг, их настройки как были, так и остались, хотя новые принтеры создаются с правильным типом бумаги. Что же делать? Там больше 60 объектов, проходить по всем им и править руками - проще удавиться. Не для того к Powershell привыкал:

@музыка: Alex M.O.R.P.H., Heatbeat - Amistad

@темы: Scripting, PowerShell

22:16 

Powershell - Resize Image #2

We rise up for the things we believe in over and over again

Давняя история, начало которой аж вот тут: Клац!

Посмотрев на код, решил, что жестко заданный прямо в теле скрипта путь к обрабатываемым файлам - не есть хорошо. Попутно пришла в голову мысль, а чего бы не разнообразить варианты работы? Теперь скрипт умеет делать следующее:

- обрабатывать пофайлово (параметр Identity). Скормил ему прямо имя файла, он его и обработал. Скормил через запятую пару-тройку файлов - обработает и их, например, d:\1.jpg,D:\2.jpg.
- обрабатывать пакетно целые каталоги (параметры Path и Mask). В этом случае используется имя каталога, в котором лежат нужные нам картинки, и маски расширений. Маски можно указывать через запятую, например, *.jpg,*.png
- задавать произвольные размеры картинкам (параметры Width и Height). Указываем прямо нужный размер, и он будет применен ко всем выбранным файлам.
- задавать размер с учетом соотношения длин большей и меньшей стороны (параметр Size). Указываем нужный нам размер большей стороны. Меньшая будет вычислена автоматически. Какая сторона больше - определяется также автоматически для каждого обрабатываемого изображения.

Использовать одновременно параметры Identity и Path/Mask нельзя. Хотя, возможно, я это в дальнейшем поменяю.
Параметры Path и Mask работают только в паре, по-отдельности - нельзя.

Результирующие файлы будут лежать там же, где и исходные, в имя будет добавлен суффикс _new.

@музыка: Gandalf - Titapatawa

@темы: PowerShell, Scripting

16:21 

Powershell + Yandex.Music #5 - Final

We rise up for the things we believe in over and over again

Ну, поглядев на заголовок, подумал, что хочется верить, что это финал :)

Отличия от прошлой версии:
- добавил забытый(sic!) блок очистки временного каталога
- убрал работу с cookies (бесполезны)
- отказался от простой конструкции $source=@(), явно указав, что это динамический массив. Как следствие - System.StringSplitOptions больше не нужны в таком адовом количестве.
- раскидал комментарии.

Для тех, кто все же заинтересовался, как это работает, пояснение ниже.

Вся конструкция для вытаскивания музыки с сервиса Yandex.Music состоит из трех частей: этого скрипта, программки Youtube-DL, которая является "ломовой лошадью" и еще одной программки ID3.exe. С ее помощью мы развешиваем теги к стащенным песням.

В качестве исходных данных скрипт использует ссылку/-и на песни и/или альбомы на самой Яндекс.Музыке. И берет он их даже не из файла или веб-страницы, а из буфера обмена. Пример 1 - в буфере у нас лежит вот такая строка:

Это выкачает одну песенку.

Пример 2 - в буфере лежит подобная ссылка:

Будет стащен весь альбом.

Пример 3 - в буфер загнали больше, чем одну строку:

Будет стащена сначала песня по первой ссылке, затем альбом по второй.

Как "готовить"?

В блоке переменных нужно прописать три пути: путь к папке, где будет храниться скачанная музыка, путь к файлу Youtube-dl.exe, путь к файлу ID3.exe. Естественно, эти файлы у нас уже должны быть. На этом подготока закочена. Копируем в буфер обмена интересующую нас ссылку или группу ссылок и выполняем скрипт.

Текст финальной версии - ниже.

@музыка: Sergey Eybog - Afterword

@темы: Scripting, PowerShell

10:29 

Powershell + Yandex.Music #4 - Full Batch

We rise up for the things we believe in over and over again

Подумалось тут - а чего бы не обучить свой скриптик таскать и альбомы, и треки разом? Собрал в буфере обмена целый список того, что нужно, надавил кнопочку "Download", а умная машинка сама поймет, что от нее хотят.

Теперь скрипт работает так. Скармливаем ему через буфер обмена данные. Первое, что будет сделано - попытка разбить эти данные на строки. Одна строка там будет на выходе или несколько - неважно, они будут потом поочередно обработаны и распознаны - ведет ссылка в этой строке на трек, или же она ведет на альбом. Если на трек, в список загрузки будет добавлена сама эта ссылка, если же на альбом - как и в предыдущей версии - скрипт стащит ссылки на треки этого альбома и уже их добавит в список загрузки.

Ну а в финале - пробежка по списку загрузки и, собственно, скачка нужного.

На удивление часто пришлось играться с System.StringSplitOptions::RemoveEmptyLines, как-то нервно Powershell со строками работает.

@музыка: Sergey Eybog - Get to know me better (Как перестать гореть???)

@настроение: клац-клац-клац

@темы: Scripting, PowerShell

01:35 

Powershell + Yandex.Music #3 - Album Download

We rise up for the things we believe in over and over again

OST "Бесконечного лета" стал последней каплей. Я давно уже собирался обучить мой скрипт загрузки треков с Яндекс.Музыки работе с целыми альбомами, но все духу не хватало. Хотя дел там не так уж и много. Но пройти мимо этой жемчужины я физически не смог, БЛ просто обязан поселиться на моем телефоне целиком.

Так что теперь скрипт смотрит на то, какую ссылку ему скормили и определяет - ссылка это на трек или же на целый альбом:
- если трек - с ним все просто, качать и положить в папку с музыкой, навесив ID3 теги;
- если альбом - сначала получить список всех песенок, в него входящих, а потом циклом загрузить их по-одиночке, так же навесив теги.

Похоже, что музыкой БЛ я буду болеть еще очень и очень долго. Похоже, она стала именно тем, что я давно для себя искал...

@музыка: Sergey Eybog - Went Fishing, Caught A Girl (я ржу с названия этой композиции)

@настроение: после открытия хорошего рута Лены - уже лучше

@темы: Scripting, PowerShell

09:34 

XPath - боль

We rise up for the things we believe in over and over again

Впервые довелось воспользоваться XPath-фильтром при работе с логами Windows. Это кусок боли, причем с двух сторон. Сначала взвыл, поглядев на структуру сообщений об удалении файлов с ресурсов севрера. Обычно же как:

C этим все просто и понятно. А вот фигвам! Имеем вот такой шаблон:

Энто ж как же, вашу мать, извиняюсь, понимать? (с) бессмертный Филатов.

Повторюсь, с простыми нодами все хорошо, они скриптуются на ура. С нодами при поддержке аттрибутов тоже особых проблем нет. Но вот ноды с аттрибутами и значениями. Каким синтаксисом их утрамбовывать? Как оказалось, в Xpath можно использовать два варианта - через квадратные скобки и через слэши. Второй вариант мне ближе, так что пишем:

Это выберет сообщения, в которых есть указанная нода с указанным аттрибутом и нужным значением этой ноды.

Выбирать можно и по аттрибутам:

Проверяется только значение attribute1, без значения самой ноды, если такого, например, нет вовсе.

Само собой, логические операторы тоже поддерживаются:


Вроде бы и ничего особо сложного, но только когда хоть малость привыкнешь. А до тех пор - bite the bullet, как говорится. А я еще, помнится, на вычисляемые поля в select-object ругался. Тоже долго с тамошнему синтаксису привыкал, со всеми скобочками, точками с запятой, кавычками. Удобная штука, эти вычисляемые поля, не спорю, но прописывать их запаришься.

В общем, основное правило при работе с XPath - вложенные ноды - через слэш, аттрибут рассматриваемой ноды - в квадратные скобки через comm-at - @.

@музыка: David Arkenstone - Ah*Nee*Mah Project - The White Feather

@настроение: привидение выспалось, привидение довольно...

@темы: PowerShell, Scripting, Этот безумный мир

16:02 

DPM - Available Tapes #3 - Case Closed

We rise up for the things we believe in over and over again

В топку. В топку тот скрипт, который мне дался таким потом и кровью. А все почему - потому что с точки зрения управления лентами чере Powershell DPM еще та дрянь. Простой пример: лента с одной (всего одной) точкой восстановления, для которой назначено больше чем одна Recovery Goal (ну то есть для данной точки восстановления есть бекап бекапа - вторая копия этой информации). Powershell для этой ленты говорит: Recovery Point status - Expired. Открываем содержимое той же самой ленты в GUI-консоли - бааа, да эта лента сдохнет только через пару недель. И самое паскудное - верная информация все же в консоли.

Короче, как я уже и сказал - в топку. Придется использовать встроенный в сам DPM отчет о ротации лент. Не так удобно, как мой скрипт, но данным можно будет верить, это важнее.

@музыка: Iron maiden - Heaven can wait

@настроение: не самое безоблачное, столько сил впустую

@темы: Scripting, PowerShell, DPM

09:02 

Powershell Functions

We rise up for the things we believe in over and over again

На самом деле такие моменты надо собирать, запоминать и ценить. Они как раз и показывают, что не напрасно занимаешься тем, чем занимаешься.

Есть у нас на работе один сервер, на котором с завидной регулярностью падает одна служба. Имя ее несущественно, главное то, что она таки должна работать. Информацию об этом ее паршивом поведении я уже давно передал людям, которые за нее отвечают. Решения пока нет, так что приходится ее банально поднимать руками.

Каждый раз заходить для этого на сервер - лениво. Даже воспользоваться mmc-оснастокй Services - тоже лениво. А самая главная засада в том, что даже если работать через любимый Powershell - имеющаяся там встроенная команда Start-Service не принимает в качестве параметра имя удаленного сервера, она работает только с локальной системой. Потому была написана простенькая функция, которая вызывается вообще без параметров и запускает на жестко забитом сервере жестко заданную по имени службу, используя механизм Invoke-Command. Примерно так:

И она работала себе, жрать не просила.

В какой-то момент я понял, что неплохо бы было расширить ее с таким расчетом, чтобы запускать любую службу на любом сервере, куда можно дотянуться при помощи PS. Удобно же, закинул ее в личный модуль, повесил какой-нибудь короткий алиас, и все. Пара нажатых кнопок, пара имен. И не надо ждать, пока прогрузится либо полностью весь сеанс, либо оснастка. Консоль же PS у меня запущена всегда, это удобно.

Получилось нечто вот такое. Решил, что пора бы осваивать Advanced Functions и встроенный в них man. Заодно обучил функцию работать не только с именами служб, но и с их отображаемыми подписями, так что службу идентифицировать можно разными способами.

И показал ее своему коллеге. Посмотрел он на нее, после чего в чат выдал вот такое:
- Эээ... Get-service -ComputerName %SERVERNAME% -Name %SERVICENAME% | Start-Service

После чего настал мой черед выдать:
- @#$...

И дружно ржем. Потому что весь мой велосипед реально умещается в одну строку кода, если отказаться от проверки DisplayName.

И самая мякотка - того коллегу на Powershell именно я и подсадил где-то год назад :)

@музыка: Nicholas Gunn - Flight over North Rim

@настроение: клац-клац-клац

@темы: PowerShell, Scripting

22:50 

PS Function Parameters

We rise up for the things we believe in over and over again

Так, чтобы не забывать. Эдакая рыба при объявлении параметров в функциях PS:

Параметр -> Атрибуты параметра -> валидация параметра -> тип+имя параметра.
Пример:

Нет, все же формат объявления параметров - одна из тех немногих вещей, которые мне в PS категорически не по нраву. Но что поделать, we take all they can give, right? (c) Dante

@музыка: W.A.S.P. - Sex Drive (да, да, самая музыка на ночь, а завтра с утра на работу)

@настроение: но все же можно было как-то проще сделать эти несчастные параметры...

@темы: PowerShell

18:33 

Powershell - Runtime

We rise up for the things we believe in over and over again

Запись, скорее, из разряда "не забыть проверить":

Одна и та же операция в двух разных подходах. Первый вариант ощутимо быстрее. Вероятно, это может помочь с оптимизацией давешнего скрипта по подсчету доступных лент DPM. На текущий момент он может выполняться аж 20-40 минут, в зависимости от задумчивости каждого конкрентного сервера. Это много, слишком много.
Все же хорошо иногда свои же собственные записи перечитывать. Иногда сознание что-то цепляет оттуда и прямо таки кричит: "Видишь??? А теперь сравни вот с этим."

@музыка: Iron Savior - Crazy

@темы: PowerShell

17:25 

Guest VM provisioning

We rise up for the things we believe in over and over again

Очень хорошая статья на тему автоматической начальной настройки гостевой ОС:
Specialize Windows Server Hyper-V guest OS automatically - Клац!
Интересна она тем, что применить изложенный метод можно не только к Hyper-V, но и к любому гипервизору, способному работать с файловой системой оффлайн-ВМ. Потому как на одном из этапов нужно будет подложить в гостевую ВМ файл с параметрами машины.

Попробовав выполнить все описанное в VMWorkstation, могу сказать - работает. Если требуется поднять сразу пачку ВМ - все будет значительно быстрее, чем вбивать параметры сети и имени машины в домене руками. Хотя в моем случае нашлась пара подводных камней.

Первое. Засада с файлом ответов unattend.xml. В статье говорится, что ветку Autologon необходимо прописывать в этап конфигурации oobeSystem. В моем случае это не так, пришлось записать его в этап Specialize, иначе не работает.

Второе. В скрипте, где и выполняется настройка сети, между блоком работы с сетевым адаптером и блоком ввода в домен пришлось ввести задержку на несколько секунд. Иначе нарываемся на неприятность - сеть ВМ не успевает инициализироваться с новым адресом, а система уже пытается найти домен и присоединиться к нему. И ожидаемо валится.

@музыка: Nigel Stansord - Deep Space

@настроение: клац-клац-клац

@темы: Virtualization, VMWare, PowerShell, Hyper-V

10:02 

Check DNS addresses

We rise up for the things we believe in over and over again

Частенько бывает необходимо проверить сразу десяток-другой имен узлов в DNS: есть они там, нет их там, если есть - под какими адресами значатся. Каждый раз вбивать nslookup %hostname% руками - утомляет безумно. Потому набросал простенькую конструкцию:

Если узел в DNS есть - будет выведено его имя и его адрес. Если узла нет, или в процессе просмотра вылезла какая-нибудь ошибка - будет выведено только имя.

@музыка: Silence

@настроение: клац-клац-клац

@темы: Scripting, PowerShell

02:32 

DPM - Available Tapes #2 - In depth

We rise up for the things we believe in over and over again

Попробую собрать в кучу все свои мысли по поводу давешнего скрипта подсчета доступных лент на серверах Data Protection Manager.
Итак, что у нас имеется. Имеется жутко тормозная консоль, которая представляет в сводной таблице сведения о доступных лентах в неудобном формате: Free Tapes и Expired Tapes. Штука в том, что Free Tapes отражает общее количество доступных лент, включая просроченные (expired). И в то же время если докапываться до подробностей - сам же DPM различает эти сущности - Free и Expired. Для именно свободных лент целый пул есть, который так и называется - Free. В скрипте он фигурирует, там, где подсчитываются пустые ленты. Вот этот формат вывода информации тоже хотелось бы поменять на более вменяемый - X доступных лент, из которых Y пустых и Z просроченных.

Собственно, с подсчетом пустых лент сложностей никаких. Взять все ленты, которые есть в данный момент в библиотеке, да выбрать из них те, что принадлежат к пулу Free. Невеликого ума задача. С просроченными дело обстоит куда сложнее (ну и интереснее, теперь я уже могу это сказать).

Что такое просроченная лента? Это кассета, на которой истекло время хранения всех бекапов. Отдельного признака Tape Expired (или чего-то подобного) у объекта Tape не нашлось, стало быть, придется опрашивать непосредственно ленты. Ну, не сами ленты, конечно же, а сведения о них в базе данных DPM. ОК, выбираем все ленты, не принадлежащие к пулу Free, и относящиеся к типу "Архивная лента" (иначе в выборку попадут чистящие кассеты, а это нам не нужно). А потом в цикле просматриваем их содержимое:

Get-RecoveryPoint -Tape $Tape

С этого момента начался мой персональный ад. Когда-то давно использовался найденный на просторах интернета скриптик, который и занимался подсчетом лент, как свободных, так и просроченных. Да вот беда, неделю назад нашлось расхождение между тем, что показывает этот скрипт, и тем, что показывает сам DPM. А именно - консоль показывает ленту как просроченную, а скрипт утверждает, что на ней еще есть "живые" бекапы, и затирать ленту нельзя. Более того, лента "сдохнет" только через месяц, который, к слову, уже почти истек. Вот эту чертовщину и нужно решить.

Хорошо, давай смотреть, что у нас на такой проблемной ленте лежит. Там лежат такие же бекапы, что и везде, они прекрасно видны через Get-RecoveryPoint. We need to go deeper... Берем один из таких бекапов (они называются точками восстановления в терминах DPM) и изучаем его под микроскопом:

Get-RecoveryPointLocation -RecoveryPoint $RP

Команда выдает нам тучу объектов, которые technet называет RecoveryPointLocation, в скрипте они обозваны как RSL- RecoverySourceLocation (и не спрашивайте, почему так, я сам не до конца разобрался, что меня заставило именно так их назвать). Как я понял - это карта размещения данных в точке восстановления на кассете. И уже с вот этими объектами связаны временные метки, когда точка восстановления была сделана, когда она сдохнет, там даже отдельное поле есть - Validity. Собственно, прошлый скрипт именно на это поле и смотрел, но делал это как-то странно: он смотрел только на первую такую запись. Если она просрочена - помечал всю точку восстановления как сдохшую. И шел себе дальше.

Казалось бы, все просто - исправив пару строк пройтись в цикле по всем этим записям, и если не найдется ни одного вхождения Validity = Valid, точка восстановления должна быть признана сдохшей. Как только попадется хоть одна запись вида Validity = Valid, точку восстановления помечаем как живую и прекращаем ее обработку, перейдя к следующей. Вот тут-то расхождение в показаниях и раскрылось: на ленте куча RSL, для которых поле Validity = Valid. Но консоль же эту ленту так же упорно видит как просроченную. И вот-вот ее затрет, лишив возможности разобраться окончательно.

В попытках рассмотреть под микроскопом уже сами RSL я себе чуть не сжег остатки мозга и глаз. Везде сплошные IDшники и ничего более, буквы и цифры чуть ли не снились. Подключил коллегу, который те бекапы и настраивал. Смотрим вместе с ним, видим поле Generation. Далее диалог двух уже основательно окосевших от обилия буквенно-цифровой информации людей:

- Стоп. Поколения, это что еще за хрень?
- Ыы, там отцы и сыновья. А может ли это быть бекапом бекапа?
- Хм. Может. У нас же настроено копирование с ленты на ленту. Давай сравним поколения и временные метки.


Эх, счастье было так возможно, так близко. Но нет, для разных RSL на той ленте были "живыми" и сыновья, и отцы. Нашлись все варианты комбинаций Father/Son, Valid/Expired. То есть так просто по полю Generation тоже не отфильтруешь. Но ведь консоль это как-то делает! Да и мысль о "бекапах бекапов" покоя все не дает.

$RSL | Get-member

Покажи мне, что еще есть у этих записей. Та же куча IDшников, но в свете обсуждения копий лент глаз резанул параметр MediaMapList. Это уже интереснее. Берем одну RSL и заглядываем в нее. А там... А там ни что иное, как список ID лент, на которых лежат копии этой самой точки восстановления. Вот оно! Картина вырисовывается такая:
- берется лента, на ней читаются точки восстановления.
- берется точка восстановления, читаются все объекты RSL.
- если во всех объектах RSL в поле Validity стоит Expired - проблемы нет, лента просрочена.
- если в какой-то RSL в поле Validity видим Valid, нужно определить, что именно еще не умерло: запись на этой самой ленте, или информация на совершенно другой ленте (тот самый бекап бекапа).
- смотрим на параметр MediaMapList и получаем оттуда ID всех лент, задействованных для этой RSL.
- если среди всего там имеется ID текущей ленты, "живая" информация лежит на именно этой ленте, и значит, всю ленту помечать как просроченную нельзя. Если же среди найденного нет ID текущей ленты, "живая" информация находится на других лентах, а значит, точку восстановления на текущей ленте можно пометить как просроченную.
Вот именно последних двух шагов ранняя версия скрипта и не делала. И именно этим обусловлены расхождения в результатах. Щелкание кнопок, дописывание соответствующего кода, тестовый прогон на нескольких "подозрительных" лентах - результаты что в скрипте, что в консоли - идентичны. Прогон на нескольких серверах - все прекрасно.

Одно плохо: если на лентах много точек восстановления - собирается эта информация безумно долго, намного дольше, чем если открыть консоль и все же дождаться, пока она прогрузится и соизволит показать все требуемые данные. Как побороть такую неторопливость - пока еще не придумал.

@музыка: Земля Ветров - Моя земля (для разнообразия)

@темы: PowerShell, DPM

16:42 

DPM - Available Tapes

We rise up for the things we believe in over and over again

Когда-нибудь я распишу этот скрипт, и то, что он делает, очень подробно. Но не сейчас. Сейчас лишь ограничусь тем, что он собирает данные по доступным лентам в ленточных накопителях. Доступные - значит свободные для записи. В эту категорию попадают как пустые ленты, так и те, на которых истекло время хранения всех точек восстановления. Этот скрипт, вроде бы и небольшой, сожрал мне мозг начисто. Но он работает. Медленно, но работает, причем не вступает в противоречие с консолью DPM, а именно это меня и бесило больше всего.

Но как бы то ни было - задача выполнена.

@музыка: Nigel Stansord - Deep Space

@настроение: Бобер - выдыхай!

@темы: DPM, PowerShell

Записная книжка

главная