En este momento ya deberiamos haber adquirido un alto grado de conocimiento sobre el servicio con el que vamos a trabajar, y haber decidido qué vamos y qué no vamos a permitir realizar al futuro usuario del mismo. Además ya tenemos la plantilla básica sobre la que empezar a escribir nuestro módulo. Ahora debemos definir la API, ver que métodos vamos a necesitar para que el módulo pueda obtener y establecer los parámetros de configuración elegidos, asi como el manejo típico sobre el demonio: arranque, parada, reinicio, etc...
El backend de nuestro módulo va a estar en la clase
EBox::NTP. Esta clase va a heredar de
EBox::GConfModule y contendrá todos los métodos que
forman el API del NTP. Este es su constructor:
Ejemplo 7.1. Constructor de EBox::NTP
sub _create
{
my $class = shift;
my $self = $class->SUPER::_create(name => 'ntp',
domain => 'ebox-ntp',
@_);
bless($self, $class);
return $self;
}Tras el estudio realizado en la sección anterior podemos definir
los siguientes métodos en EBox::NTP, recordemos
que todo método privado de la clase ha de empezar por el carácter de
subrayado (_metodo):
setService
service
setSynchronized
synchronized
setServers
servers
setNewData
setNewTimeZone
_restartAllServices
setServiceEste método recibe un parámetro que establecerá la activación o no del servicio de sincronización horaria de eBox, con él permitiremos que un cliente de nuestra red pueda sincronizar su fecha y hora con eBox.
Veamos mas a fondo cómo está implementado este método:
Ejemplo 7.2. Activación del servicio de sincronización
sub setService
{
my ($self, $active) = @_;
if ($active xor $self->service) {
$self->set_bool('active', $active);
}
} En primer lugar tomamos el valor de
$active que recibimos como parámetro
del métod y por otro lado gracias a la llamada a
$self->service obtenemos el estado
del servicio de sincronización. En caso de que estos valores
sean diferentes pasamos a actualizar el valor del parámetro
active de la configuración del módulo en
gconf, mediante la llamada $self->setbool('active',
$active).
serviceEn este caso, el método nos retornará el estado del
servicio de sincronización horaria. La implementación de este
método es trivial, tan solo retornamos el valor del parámetro
active de la configuración del módulo
almacenada en gconf.
Ejemplo 7.3. Obtención el estado del servicio NTP
sub service
{
my $self = shift;
return $self->getbool('active');
} setSynchronizedEste método recibe un parámetro que permitirá o no
la posibilidad de que eBox sincronice su fecha y hora a
través de servidores externos NTP. Como vemos en el código
que tenemos a continuación es practicamente similar a la
implementación del método setService
salvo que esta vez almacenamos el valor del parámetro
synchronized en la configuración de
gconf.
Ejemplo 7.4. Establecer el estado de la sincronización externa
sub setSynchronized # (synchronized)
{
my ($self, $synchronized) = @_;
if ($synchronized xor $self->synchronized) {
$self->set_bool('synchronized', $synchronized);
}
} synchronizedÉste método retorna el valor del parámetro
synchronized de la configuración
almacenada en gconf. Su implementación es muy sencilla y similar
a la del método service.
Ejemplo 7.5. Obtener el estado de la sincronización externa
sub synchronized
{
my $self = shift;
return $self->getbool('synchronized');
} setServersGracias a este método, vamos a poder almacenar en la
configuración de gconf los servidores de NTP externos que
el usuario introduzca en caso de que active el servicio de
sincronización externa. La implementación de este método
tiene en cuenta por un lado si ha introducido una dirección
ip o un nombre dns para poder comprobar su sintaxis. En caso
de que sea correcta, lo almacenará en gconf mediante el
método set_string. Por otro lado
también se ha de tener en cuenta que no vamos a permitir
introducir un servidor secundario de ntp si antes no se ha
introducido un primario, e igual para un tercero. Veamos
parte[1]de la implementación de
este método:
Ejemplo 7.6. Establecer servidores ntp externos
sub setServers # (server1, server2, server3)
{
my ($self, $s1, $s2, $s3) = @_;
if ($s1 =~ /^(\d{1,3}\.){3}\d{1,3}$/) {
checkIP($s1, __("primary server IP address"));
$self->set_string('server1', $s1);
} else {
checkDomainName($s1, __("primary server name "));
$self->set_string('server1', $s1);
}
if (defined($s2) and ($s2 ne "")) {
if ($s2 =~ /^(\d{1,3}\.){3}\d{1,3}$/) {
checkIP($s2, __("secondary server IP address"));
$self->set_string('server2', $s2);
} else {
...
serversÉste método nos retorna un array con los servidores ntp externos que tengamos almacenados en gconf. Su implementación es como sigue:
Ejemplo 7.7. Obtener la lista de servidores ntp externos
sub servers
{
my $self = shift;
my @servers;
@servers = ($self->get_string('server1'),
$self->get_string('server2'),
$self->get_string('server3'));
return @servers;
} setNewDateCon este método vamos a ofrecer la posibilidad de cambiar manualmente la fecha y hora del sistema. Veamos su implementación:
Ejemplo 7.8. Establecer una nueva fecha y hora en el sistema
sub setNewDate # (day, month, year, hour, minute, second)
{
my ($self, $day, $month, $year, $hour, $min, $sec) = @_;
my $newdate = "$year-$month-$day $hour:$min:$sec";
my $command = "/bin/date --set \"$newdate\"";
root($command);
$self->_restartAllServices;
} Recibimos como parámetros todos y cada uno de los datos necesarios para establecer la fecha y hora el sistema: día, mes, año, hora, minutos y segundos.
Formamos el comando completo en la variable
$command y realizamos una llamada al
método root($command) con el comando
formado. Este comando ha de ser llamado como root mediante sudo
y es por eso que no lo ejecutamos directamente.
Tras un cambio de hora en el sistema es necesario
reiniciar ciertos servicios como módulos de eBox y logs del
sistemar; es por ello que realizamos la llamada al método
_restartAllServices del que veremos su
implementación mas adelante.
setNewTimeZoneAl igual que podemos modificar la fecha y hora del sistema, también vamos a poder cambiar la zona horaria, para ello nos implementamos éste método de la siguiente manera:
Ejemplo 7.9. Establecer una nueva zona horaria
sub setNewTimeZone # (continent, country)
{
my ($self, $continent, $country) = @_;
my $command = "ln -s /usr/share/zoneinfo/$continent/$country" .
" /etc/localtime";
$self->set_string('continent', $continent);
$self->set_string('country', $country);
root("rm /etc/localtime");
root($command);
$self->_restartAllServices;
} Vamos a recibir dos parámetros: el país y el
continente. Con ellos vamos a rehacer el enlace simbólico
de /etc/localtime para que apunte al
nuevo país que se nos pasa como parámetro. Por ejemplo
si como parámetros tenemos Dakar
como país, y Africa como continente,
/etc/localtime deberá apuntar a
/usr/share/zoneinfo/Africa/Dakar.
También almacenaremos ambos parámetros en la
configuración de gconf con las llamadas al método
set_string. Tras haber realizado las
llamadas a comandos del sistema para establecer la nueva zona
horaria, pasamos a reiniciar ciertos módulos eBox y sistemas de
logs para que se arranquen en un estado consistente y no trabajen
con una desviación temporal.
_restartAllServicesHay ciertas operaciones que realizan métodos del módulo que pueden dejar el sistema inconsistente, porque modifican la fecha y hora. Para que tanto módulos eBox como logs no se enfrenten a una diferencia temporal, vamos a implementar este método que reiniciara los sistemas de logs y módulos eBox que ahora se lanzarán con la nueva fecha y hora del sistema. Veamos como está implementado:
Ejemplo 7.10. Reiniciar módulos y servicios del sistema
sub _restartAllServices
{
my $self = shift;
my $global = EBox::Global->getInstance();
my @names = grep(!/^network$/, @{$global->modNames});
@names = grep(!/^firewall$/, @names);
my $log = $global->logger;
my $failed = "";
$log->info("Restarting all modules");
foreach my $name (@names) {
my $mod = $global->modInstance($name);
try {
$mod->restartService();
} catch EBox::Exceptions::Internal with {
$failed .= "$name ";
};
}
if ($failed ne "") {
throw EBox::Exceptions::Internal("The following modules ".
"failed while being restarted, their state is ".
"unknown: $failed");
}
$log->info("Restarting system logs");
try {
root("/etc/init.d/sysklogd restart");
root("/etc/init.d/klogd restart");
root("/etc/init.d/cron restart");
} catch EBox::Exceptions::Internal with {
};
} Primero obtenemos una instacia de la clase
EBox::Global con la que obtendremos
la lista de módulos instalados en eBox. Vamos a reiniciar
todos salvo los módulos network y
firewall, capturando las posibles
excepciones que se produzcan al reiniciar cada módulo. Tras ello
pasaremos a reiniciar manualmente los sistemas de log: sysklogd,
klogd y cron, que como requiere privilegios de root se realiza a
través de la función root.
[1] La implementación completa de este método asi como del módulo completo se puede encontrar en el repositorio de subversion