Perl для системного администрирования

       

Методы для изменения элементов в Net LDAP



Таблица 6.6. Методы для изменения элементов в Net::LDAP



Параметр Действие
add => {Sattrname => Sattrvalue} Добавляет указанный элемент с заданным значением.
add => {Sattrname => [$attrvalue1, $attrvalue2. . . ]} Добавляет указанный атрибут с заданным набором значений.
delete => {Sattrname => Sattrvalue} I Удаляет указанный атрибут с заданным значением.
delete => {Sattrname => []} delete => [Sattrnamel, $attrname2. . . ]

replace => {Sattrname => Sattrvalue}

Удаляет атрибут или набор атрибутов независимо от их значений.

Действует, как add, только заменяет текущее значение указанного атрибута. Если Sattrvalue является ссылкой на пустой анонимный список ([]), метод становится синонимом для приведенной выше операции удаления.

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

Можно объединять несколько таких параметров в одном и том же вызове modify, но это представляет собой потенциальную проблему. Когда modify вызывается с набором параметров, например, так:

$c->modify($dn, replace => {'!' => "Medfora"},

add => {'1' =N "Boston"). add => {'1 => "Cambridge"});

нет никаких гарантий, что указанные операции добавления будут выполняться после замены. Если необходимо, чтобы операции выполнялись в определенном порядке, можно применять синтаксис, подобный только что рассмотренному. Вместо использования набора дискретных параметров можно передать единственный массив, содержащий очередь команд. Вот как это работает: Tiod:fy() принимает параметр changes, значение которого- список. Данный список считается набором пар. Первая половина пары - это операция, которую необходимо выполнить, вторая половина - ссылка на анонимный массив, содержащий данные для этой операции. Например, если мы хотим гарантировать, что операции из предыдущего фрагмента кода выполнятся в нужном порядке, то можем написать:

$c->Tiodify($dn. changes =>

[ replace -;" ['!' => "Kedfrr'd"].

add --> ['!' => "Boston"],

add =>['!' => "Caubndge"]

]);

Внимательно посмотрите на пунктуацию: она отличается от других параметров, которые приводились раньше.

Учитывая информацию, передаваемую функции modify(), можно переписать для Net: : ЮАР предыдущую программу, меняющую Бостон на Индиану:

use Net::LDAP;

$server = $ARGV[0];

Sport = getservbynameC'ldap", "tcp") |j "389";

Sbasedn = "dc=ccs,dc=hogwarts,c!c=edu";

$scope = "sub";

Srootdn = "c!i=Manager, ou=Syste'ns, dc=ccs, dc-hogwarrs, do-edu".

$pw = "secret",

$c = new Net::LDAP($server, port => Sport) or

die "Невозможно соединиться с сервером Ssorver

$«>'\n": $c->bind(dn --> S'ootrin. password => $pw) or

die "Ошибка при соедимении; $@\n";

Ssearchob] = $c->search(base => Soasedn, fiiiei => "(l-Bosion)".

scope => $scope, attrs -s [''}.

typeso.il у => 1): dio "Ошибка поиска: ". Ssear-chonj->er! or

if (SsearchobJ){

(Sentries = $searcnopj->entries;

ОГ ( aPI't ' .OS ) {

Собираем все вместе

Теперь, когда нам известны все основные LDAP-функции, напишем несколько небольших сценариев для системного администрирования. Мы импортируем базу данных машин из главы 5 «Службы имен TCP/IP» на сервер LDAP и затем сгенерируем некую полезную информацию, основываясь на LDAP-запросах. Вот пара выдержек из этого простого файла (для того только, чтобы напомнить вам формат):

name: shimmer

address: 192.168.1.11

aliases: shim shimmy shimmydoodles

owner: David Davis

department: software

building: main

room: 909

manufacturer: Sun

model: UltraGO

name: bendir address: 192.168.1,3 aliases:

ben bendoodles owner: Cindy Coltrane department:

IT building: west room: 143

manufacturer: Apple model: 7500/100

Первое, что нужно сделать, приготовить сервер каталогов для приема этих данных. Мы будем использовать нестандартные атрибуты, так что придется обновить схему сервера. Различные серверы выполняют это по-разному. Например, сервер каталогов Netscape имеет симпатичную графическую консоль Directory Server Console для подобных изменений. Другие серверы требуют внесения изменений в текстовые конфигурационные файлы. Работая с OpenLDAP, можно использовать нечто подобное в файле, включенном основным конфигурационным файлом для определения собственных пользовательских классов объектов для машины:

objectclass machine requires-:

,-• п ullOrtS

a-:a;;es building, room.

manufacturer, model

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

Продолжающиеся строки

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

Разделители элементов

Между элементами в базе данных в качестве разделителя используется симпатичная последовательность -=-. Два разделителя строк (т. е. пустая строка) должны находиться между элементами LDIF, так что нужно будет удалить эту последовательность из вводимых данных.

Разделители атрибутов

В настоящее время в наших данных есть только один атрибут с несколькими значениями: aliases (псевдонимы). LDIF обрабатывает многозначные атрибуты, перечисляя каждое значение на отдельной строке. Если встретится несколько атрибутов, то понадобится специальный код, печатающий для каждого значения отдельную строку. Если бы не эта особенность, программа, преобразующая наш формат в LDIF, представляла бы собой одну строку кода на Perl.

Но даже и с этими ловушками программа преобразования на удивление проста:

Sdatafile = "database";

Srecordsep = "-=-\n";

Ssuffix = "ou=data, ou=systems, dc=ccs. dc=hogwarts. dc=edu";

Sobjectclass = «EOC;

objectclass: top

objectclass: machine

EOC

open(DATA, Sdatafile) or aie "Невозможно открыть Sdataf ile: $''.n";

Модули Perl не работают с зги», даже если в специфики-;'!'

while (<DATA>) {

ft выводим заголовок для каждого элемента if (/name:\s-(.-)/){

print "arr сп=$1, $suffix\n":

print Soijjoctclass;

print "en: $1\n":

next: I tt обрабатываем многозначный атрибут aliases

if (s/~aliases:\s*//){

@aliases = split:

foreach $name (@aliases){ print "aliases: $name\n";

}

next; }

ft обрабатываем конец разделителя записей if ($_ eq $recordsep){

print "\n";

next; }

ft в противном случае просто печатаем найденный атрибут print;

close(OATA);

Если выполнить эту программу, то она выведет файл LDIF,

выглядящий примерно так:

dn: cn=shimmer, ou=data, ou=systems, dc=ccs, dc=hogwarts. dc=edu

objectclass: top

objectclass: machine

en: shimmer

address: 192.168.1.11

aliases: shim

aliases: shimmy

aliases: shimmydoodles

owner: David Davis

department: software

building: main

room: 909

manufacturer: Sun

model: UltraGO

dn: cn=bendir, ou=data. Ob=systems, ac=ccs, dc=hogwarts, dc=edu

objectclass: top

objectclass: machine

en: bendir

address: 192.168. 1.3

aliases: ben aliases: bendoodles owner: Cindy Colt rant; department:

building1 west room: 143

manufacturer: Apple model: 7500/100

Имея этот LDIF-файл, можно применять одну из программ, распространяемых с сервером, для загрузки этих данных на сервер. Например, Idif2ldbm, входящий в состав обоих серверов OpenLDAP и Netscape Directory Server, считывает LDIF-файл и напрямую импортирует его в формат сервера каталогов, избавляя от необходимости проходить через LDAP. Хотя эта программа используется только при неработающем сервере, она может обеспечить самый быстрый способ загрузки большого количества данных. Если нельзя остановить сервер, можно применить только что написанную на Perl программу для чтения LDIF-файлов и передать подобный файл на LDAP-сервер.

Добавим еще один способ: вот программа, в которой пропускается промежуточный шаг создания LDIF-файла, и наши данные напрямую импортируются на LDAP-сервер:

use Net::LDAP;

use Net::LDAP::Entry;

Sdatafile = "database";

Srecordsep = "-=-":

Sserver = $ARGV[0];

Sport = getservbynameC'ldap","tcp") || "389";

Ssuffix = "ou=data, ou=systems, dc=ccs, dc=hogwarts, dc=edu";

$rootdn = "cn=Manager. o=University of Michigan, c=US";

$pw = "secret";

$c = new Net::LDAP($server.port => Sport) or

die "Невозможно соединиться с сервером Sserver: $@\n";

$c->bind(dn => Srootdn. password => Spw) or die "Ошибка при соединен;',

open(DATA,Sdatafile) or die "Невозможно открыть $datafile:$!\n";

while (<DATA>) {

chomp;

в

начале новой записи создаем

if (,/"nare:\S'(. •)/'){

$d^="cn=$1. Ssbffix":

Sentry = new fiet: : LDAP 'E:"t'\.

$еп:г^,-аса( с *' . $i)

ne:xt i

$entry-vad<l( aliases'. [splitO]):

next }

$entry->add( "objectciass". [ "тор", "riacii'ie" ]):

$entry->dn($dn);

$res = $c->add($entry);

warn "Ошибка добавления для "

undef Sentry;

next; }

ft добавляем все остальные атрибуты

$entry->add(split(':\s*')); # считаем, что у атрибута только одно значение >

close(DATA); $c->unbind();

После того как данные были импортированы на сервер, можно приступить к довольно любопытным вещам. В следующих примерах мы будем поочередно обращаться к двум LDAP-модулям. Для краткости в каждом примере не будет повторяться заголовок, в котором устанавливаются конфигурационные переменные, и код для соединения с сервером.

Так что же можно сделать с данными, расположенными на сервере LDAP? Можно на лету создать файл узлов:

use Mozilla::LDAP;

Sentry = $c->search($basedn,'one','(objectclass=machirie) ,0.

'en','address','aliases'); die "Ошибка поиска:". $c->getErrorString()."\n" if $c->getErrorCode( ;.

if (SentryM

print "#\n\U host file - GENERATED BY $0\n

tt

DO NOT EDIT BY HAND!\n«\n"; while(Sentry)!

print $entry->{adflress}[0]."\t". $ег!!-у->{сп}[0]," ".

jdir(' ' .?{$e^rry->{a:iasesM V " i": $entry = Sc-'-nextEntr1, (): }; ) $o->close();

Вот что получается:

host file - GENERATED BY Idap2host.s

В DO NOT EDIT BY HAND

192.168.1.11 shimmer shim shimmy sriimmydoodles

192.168.1,3 bendir ben bendoodles

192.168.1.12 Sulawesi sula su-lee 192.168.1.55

sander sandy mickey mickeydoo

Можно найти имена всех машин, произведенных Apple:

use Net::LDAP;

Ssearchobj = $csearch(base => Sbasedn,

filter => "(manufacturer=Apple)", scope => 'one', attrs => ['en']);

die "Ошибка поиска: ".$searchobj->error()."\n" if ($searchobj->code());

if ($searchobj){

for ($searchobj->entries){ print $_->get('en'),"\n";

$c->unbind();

Вот и результат:

bendir Sulawesi

Можно сгенерировать список владельцев машин:

use Mozilla::LDAP;

Sentry = $c~>search($basedn,'one','(objectclass=machine)',0,

'en','owner'): die "Ошибка поиска:". $c->getErrorString()."\n" if $c->getErrorCode():

if ($entry){

while($entry){

push(@{$owners{Sentry->{owner}[0]}}. $er,try->{cn}[0]); Sentry = $c->nextEntry();

};

$c->close():

for (sort ke>s %owners){

ADSI (Интерфейсы служб активных каталогов) 241

Получилось так:

Alex Rollins: sadder

Cindy Coltrane: oenciir

David Davis: sfurrmer

Ellen Monk: Sulawesi

Заодно можно проверить, является ли владельцем машины пользователь с текущим идентификатором (псевдоаутентификация):

use Mozilla::LDAP::Conn; use Sys::Hostname;

$user = (getpwuid($<))[6];

Shostname = hostname;

$hostname =" s/"([".]+)\..«/$1/; # удаляем имя домена из имени узла

Sentry = $c->search("cn=$hostname,$suffix",'base',"(owner=$user)",1,''):

if ($entry){

print "Владелец ($user) зарегистрирован на машине $hostname.\n"; } else {

print "Suser не является владельцем этой машины ($hostname)\n."; } $c->close();

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



Содержание раздела