Сценарии
Сценарии
Теперь, когда мы разобрались с базой данных, самое время написать сценарии для выполнения периодических или каждодневных действий, необходимых при системном администрировании. Эти сценарии построены на низкоуровневой библиотеке компонентов (Account.pm), которую мы создали, объединив в один файл все только что написанные подпрограммы. Такая подпрограмма позволяет убедиться, что все необходимые модули загружены:
sub InitAccountf
use XML: :Writer;
Srecord = { fields => [login, fullname,id,type,password]};
$addqueue = "addqueue"; tt имя файла очереди добавления
Sdelqueue = "delqueue"; ft имя файла очереди удаления
$maindata = "accountdb"; tt имя основной базы данных
ft учетных записей
if ($"0 eq "MSWin32"){
require Win32::Lanman;
require Win32::Perms;
require File::Path;
ft местоположение файлов учетных записей
Saccountdir = "\\\\server\\accountsystem\\";
ft списки рассылки
$maillists = "$accountdir\\maillists\\";
ft корневой каталог домашних каталогов
$homeNTdirs = "\\\\nomeserver\\home";
ft имя подпрограммы, добавляющей учетные записи
Saccountadd = "CreateNTAccount";
ft имя подпрограммы, удаляющей учетные записи
Saccountdel = "DeleteNTAccount": }
else {
require Expect;
и местоположение файлов учетных записей
$accountdir = "/usr/accountsystem/";
в списки рассылки
Smaillists = "Saccountdir/maillists/";
tt местоположение команды useradd
Suseraddex = ",/usr/sbin/useradd";
tt местоположение команды userdel
Suserdelex = "/usr/sbin/userdel";
tt местоположение команды passwd
Spasswdex = "/bin/passwd";
tt корневой каталог домашних каталогов
ShomeUnixdirs = "/home";
tt прототип домашнего каталога
$skeldir = "/home/skel";
ft командный интерпретатор по умолчанию
Sdefshell = "/bin/zsh";
ft имя подпрограммы, добавляющей учетные записи
Saccountadd = "CreateUnixAccount";
tt имя подпрограммы, удаляющей учетные записи
Saccountdel = "DeleteUnixAccount";
}
}
Рассмотрим сценарий, обрабатывающий очередь добавления:
use Account;
use XML;:Simple;
SlnitAccount;
считываем низкоуровневые подпрограммы
&ReadAddQueue;
tt считываем и анализируем очередь добавления
&ProcessAddQueue;
tt пытаемся создать все учетные записи
&DisposeAddQueue;
ft записываем учетную запись либо в основную
tt базу данных, либо обратно в очередь, если
tt возникли какие-то проблемы
tt считываем очередь добавления в структуру данных $queue
sub ReadAddQueue{
open(ADD,Saccountdir.Saddqueue) or
die "Unable to open ".Saccountdir.$addqueue.":$!\n";
read(ADD, Squeuecontents, -s ADD);
close(ADD);
Squeue = XMLin("<queue>".Squeuecontents."</queue>",
keyattr => ["login"]);
ft обходим в цикле структуру данных, пытаясь создать учетную
запись для каждого запроса (т. е. для каждого ключа)
sub ProcessAddQueue{
foreach my Slogin (keys %{$queue->{account}})
{
sub InitAccountf
use XML: :Writer;
Srecord = { fields => [login, fullname,id,type,password]};
$addqueue = "addqueue"; tt имя файла очереди добавления
Sdelqueue = "delqueue"; ft имя файла очереди удаления
$maindata = "accountdb"; tt имя основной базы данных
ft учетных записей
if ($"0 eq "MSWin32"){
require Win32::Lanman;
require Win32::Perms;
require File::Path;
ft местоположение файлов учетных записей
Saccountdir = "\\\\server\\accountsystem\\";
ft списки рассылки
$maillists = "$accountdir\\maillists\\";
ft корневой каталог домашних каталогов
$homeNTdirs = "\\\\nomeserver\\home";
ft имя подпрограммы, добавляющей учетные записи
Saccountadd = "CreateNTAccount";
ft имя подпрограммы, удаляющей учетные записи
Saccountdel = "DeleteNTAccount": }
else {
require Expect;
и местоположение файлов учетных записей
$accountdir = "/usr/accountsystem/";
в списки рассылки
Smaillists = "Saccountdir/maillists/";
tt местоположение команды useradd
Suseraddex = ",/usr/sbin/useradd";
tt местоположение команды userdel
Suserdelex = "/usr/sbin/userdel";
tt местоположение команды passwd
Spasswdex = "/bin/passwd";
tt корневой каталог домашних каталогов
ShomeUnixdirs = "/home";
tt прототип домашнего каталога
$skeldir = "/home/skel";
ft командный интерпретатор по умолчанию
Sdefshell = "/bin/zsh";
ft имя подпрограммы, добавляющей учетные записи
Saccountadd = "CreateUnixAccount";
tt имя подпрограммы, удаляющей учетные записи
Saccountdel = "DeleteUnixAccount";
}
}
Рассмотрим сценарий, обрабатывающий очередь добавления:
use Account;
use XML;:Simple;
SlnitAccount;
считываем низкоуровневые подпрограммы
&ReadAddQueue;
считываем и анализируем очередь добавления
&ProcessAddQueue;
пытаемся создать все учетные записи
&DisposeAddQueue;
записываем учетную запись либо в основную
tt базу данных, либо обратно в очередь, если
tt возникли какие-то проблемы
tt считываем очередь добавления в структуру данных $queue
sub ReadAddQueue{
open(ADD,Saccountdir.Saddqueue) or
die "Unable to open ".Saccountdir.$addqueue.":$!\n";
read(ADD, Squeuecontents, -s ADD);
close(ADD);
Squeue = XMLin("<queue>".Squeuecontents."</queue>",
keyattr => ["login"]);
ft обходим в цикле структуру данных, пытаясь создать учетную
запись для каждого запроса (т. е. для каждого ключа)
sub ProcessAddQueue{
foreach my Slogin (keys %{$queue->{account}}){
Sresult = &$accountadd($login,
$queue->{account}->{$login});
if (!$result){
$queue->{account}->{$login}{status} = "created";
}
else {
$queue->{account}->{$login}{status} =
"error:$result";
}
}
}
ft теперь снова обходим структуру данных. Каждую учетную запись
# со статусом "created," добавляем в основную базу данных. Все
и остальные записываем обратно в файл очереди, перезаписывая
tt все его содержимое.
sub DisposeAddQueue{
foreach my Slogin (keys %{$queue->{account}}){
if ($queue->{account}->{$login}{status} eq "created")!
$queue->{account}->{$login}{login} = Slogin;
$queue->{account}->{$login}{creation_date} = time;
&AppendAccountXML($accountdir.$maindata,
$queue->{account}->{$login});
delete $queue->{account}->{$login};
next;
}
}
# To, что осталось сейчас в Squeue, - это учетные записи,
# которые невозможно создать
# перезаписываем файл очереди
open(ADD,">".$accountdir.$addqueue) or
die "Unable to open ".$accountdir.$addqueue.":$!\n";
П если есть учетные записи, которые не были созданы,
# записываем их
if (scalar keys %{$queue->{account}}){
print ADD XMLout(&TransformForWrite($queue),
rootname => undef);
}
close(ADD);
}
Сценарий, обрабатывающий очередь удаления, очень похож:
use Account;
use XML::Simple;
SlnitAccount;
# считываем низкоуровневые подпрограммы
&ReadDelOueue;
# считываем и анализируем очередь удаления
&ProcessDelQueue;
пытаемся удалить все учетные записи
&DisposeDelQueue;
удаляем учетную запись либо из основной
базы данных, либо записываем ее обратно в
в очередь, если возникли какие-то проблемы
и считываем очередь удаления в структуру данных $queue
sub ReadDelQueue{
open(DEL,Saccountdir.Sdelqueue) or
die "Unable to open ${accountdir}${delqueue}:$!\n";
read(OEL, Squeuecontents, -s DEL);
close(DEL);
Squeue = XMLin("<queue>".$queuecontents."</queue>",
keyattr => ["login"]);
}
# обходим в цикле структуру данных, пытаясь удалить учетную
# запись при каждом запросе (т, е. для каждого ключа)
sub ProcessDelQueue{
foreach my Slogin (keys %{$queue->{account}}){
Sresult = &$accountdel($login,
$queue->{account}->{$login});
if (!$result){
Squeue->{account}->{$login}{status} = "deleted";
}
else {
$queue->{account}->{$login}{status} =
"error:$result";
}
>
}
# считываем основную базу данных и затем вновь обходим в цикле
структуру Squeue. Для каждой учетной записи со статусом
"deleted," изменяем информацию в основной базе данных. Затем
записываем в базу данных. Все, что нельзя удалить, помещаем
# обратно в файл очереди удаления. Файл перезаписывается,
sub DisposeDelQueue{
&ReadMainDatabase;
foreach my Slogin (keys %{$queue->{account}}){
if ($queue->{account}->{Slogin}{status} eq "deleted"){
unless (exists $maindb->{account}->{$login}){
warn "Could not find Slogin in $maindata\n";
next;
}
$maindb->{account}->{$login}{status} = "deleted";
$maindb->{account}->{$login}{deletion_date} = time;
delete $queue->{account}->{$login};
next;
}
&WriteMainDatabase;
# все, что сейчас осталось в Sqjjeue, - это учетные записи,
# которые нельзя удалить
open(DEL,">".$accountdir.$delqueue) or die "Unable to open ".
$accountdir.$delqueue.":$!\n";
if (scalar keys %{$queue->{account}}){
print DEL XMLout(&TransformForWrite($queue), rootname => undef);
}
close(DEL); }
sub ReadMainDatabase{
open(MAIN,$accountdir.$maindata) or
die "Unable to open ".$accountdir.$maindata.":$!\n";
read (MAIN, $dbcontents, -s MAIN);
close(MAIN); $maindb = XMLin("<maindb>".Sdbcontents. "</maindb>",
keyattr => ["login"]); }
sub WriteMainDatabase{
# замечание: было бы «гораздо безопаснее* записывать данные
# сначала во временный файл и только если они были записаны
# успешно, записывать их окончательно open(MAIN,">".
$accountdir.Smaindata) or
die "Unable to open ".$accountdir.$maindata.":$!\n";
print MAIN XMLout(&TransformForWrite($maindb),
rootname => undef); close(MAIN); }
Можно написать еще множество сценариев. Например, мы могли бы применять сценарии, осуществляющие экспорт данных и проверку согласованности. В частности, совпадает ли домашний каталог пользователя с типом учетной записи из основной базы данных? Входит ли пользователь в нужную группу? Нам не хватит места, чтобы рассмотреть весь спектр таких программ, поэтому завершим этот раздел небольшим примером экспортирования данных. Речь уже шла о том, что хотелось бы завести отдельные списки рассылки для пользователей различного типа. В следующем примере из основной базы данных считываются данные и создается набор файлов, содержащих имена пользователей (по одному файлу для каждого типа пользователей):
use Account; И только чтобы найти файлы use XML::Simple:
&InitAccount;
SReadMainDatabase:
&WriteFiles:
open(MAIN,Saccountdir.Smaindata) or
die "Unauie to open ".Saccountdir.$maindata "-$'\r";
read (MAIN, Sdbcontents. -s MAIN);
ciose(MAIN): Smaindb = XMLin("<maindb>
".Sdbcontents." /maincm>",
keyattr -> [""]):
}
обходим в цикле списки, собираем списки учетных записей
определенного типа и сохраняем им в хэше списков. Затем
записываем содержимое каждого ключа в отдельный файл.
sub WriteFiles {
foreach my Saccount (@{$niaindb->{account}}){
next if $account->{status}
eq "deleted"; push(@{$types{$account->{type}}},
$account->{login}); }
foreach $type (keys %types){
open(OUT,">".Smalllists.Stype) or die "Unable to write to
".Saccountdir.$maillists.$type.": $!\n";
print OUT ]0in("\n",sort @{$types{$type}})."\n"; close(OUT);
}
}
Если посмотреть в каталог списков рассылки, то можно увидеть:
> dir
faculty staff
Каждый из этих файлов содержит соответствующий список учетных записей пользователей.