Chapter 8. How to create a module

Table of Contents

8.1. Long way to create a small module
8.1.1. Studying the NTP service and its features
8.1.2. Create a new module from the module template.
8.1.3. Defining and implementing the API
8.1.4. Creating CGIs and templates
8.1.5. Showing the menu and the Summary page
8.1.6. Generating config files and managing the NTP server
8.1.7. Setting up proper firewall rules
8.1.8. Conclusion
8.2. Short way to create a small module
8.2.1. Define data model
8.2.2. Define module web GUI using Composites
8.2.3. Publishing model and composite from this module
8.2.4. Common tasks
8.2.4.1. Configuring Apache2 service
8.2.4.2. Manage Apache2 daemon execution
8.2.4.3. Web service inclusion in eBox menu and summary
8.2.4.4. Establish rules through services and firewall modules to use Web service in eBox
8.2.5. Expose module API
8.2.6. Conclusion

8.1. Long way to create a small module

Once you've seen the eBox internals, we are going to apply all the knowledge explained in the previous chapters creating a module from scratch. The first thing to do is choose a network service that you want to integrate into eBox. In our case we've chosen the Network Time Protocol server because it is one of the most simple service that exist in eBox. Our module will provide eBox these features:

  • Date and time synchronisation with an external server.

  • Let clients synchronise their time and date with eBox.

And these are the steps we are going to follow in the development for the module:

  • Study the NTP server and its features.

  • Create a new module from the module template.

  • Define and implement an API to manage its configuration.

  • Develop CGIs and mason templates.

  • Make our module show up in the eBox menu and in the summary page.

  • Generate the configuration files and control the daemon execution.

  • Establish custom rules in the firewall that let our module work.

8.1.1. Studying the NTP service and its features

We've decided to develop this module using the ntp server from www.ntp.org. Debian includes packages for the server so we are going to use two of them: ntp-server to provide the time synchronisation service to clients and ntpdate to make synchronisation queries to external servers.

After choosing the software we have to study how it works, its configuration files and the configurable parameters within them. With all this information we will be able to choose what degree of control the user of our module will have. We'll try to strike a balance between flexibility in the configuration and ease of use.

For the module we are developing we only need to tune some of the parameters contained in the /etc/ntp.conf file, which holds the configuration for the ntp daemon. Of all the possible configuration options contained in that file, we are only interested in two:

  • The list of ntp servers we are going to use to synchronise our date and time. This can be achieved by adding lines like the following as many times as necessary to the configuration file:

      server ntp_server_ip 
              
  • The ability to act as an NTP server for clients in our network. This is done using the server attribute, as seen above, with an special SIP address:

      server 127.127.1.0  

The NTP server has more configuration options, but we have decided to leave them with sane default values that are transparent to the user. We will concentrate on those features that will be generally most interesting to our users.

We will also add the possibility for the user to manually change the system time and date and the time zone, in case there is no possibility to synchronise the time with external NTP servers.

Time and date modification is easily done using the /bin/date command.

Time zone configuration is easy too. /etc/localtime is a symbolic link that points to a file named after the location we are in. All possible timezones are stored under the /usr/share/zoneinfo/ directory. Each location is stored inside its continent, so if our time zone is Madrid/Europe, then /etc/localtimemust point to /usr/share/zoneinfo/Europe/Madrid. The operation of modifying the time zone is as simple as changing the /etc/localtime symbolic link.

8.1.2. Create a new module from the module template.

As we have seen above, eBox modules have a complex directory structure that can be tedious to set up. Because of that, a module template is provided inside the tools directory in the base ebox module. This template provides a basic skeleton for an eBox module and can be cloned to create a new one. Note that you should replace the string modulename with your module name.

eBox uses autoconf and automake for module configuration and installation. The files autogen.sh, configure.ac and Makefile.am contain the basic autotools configuration for a standard module and are also included in the template. The template also provides a m4/ebox.m4 file, used by modules to detect the current eBox installation paths.

The Autoconf and Automake manuals are the best autotools reference.

8.1.3. Defining and implementing the API

At this point we should have gained a good degree of familiarity with the network service we are going to work with. We should also know what features will be exposed to our future users. In addition we have a template to use as a base for our module. The next step is to define the API for our module's backend. We must define which methods will be needed to let the rest of the modules read and write all the configuration options and to manage the daemon: start, stop, etc...

The backend of our module is going to be in the EBox::NTP class. It will inherit from EBox::GConfModule and it will contain all the methods that conform the NTP API. This is its constructor:

Example 8.1. EBox::NTP constructor

sub _create 
{
    my $class = shift;
    my $self = $class->SUPER::_create(name => 'ntp', 
                                      domain => 'ebox-ntp',
                                      @_);
    bless($self, $class);
    return $self;
}
        

After the analysis performed in the previous section we can define the following methods in EBox::NTP(remember that we use a leading underscore for private method names):

  • setService

  • service

  • setSynchronized

  • synchronized

  • setServers

  • servers

  • setNewData

  • setNewTimeZone

  • _restartAllServices

setService

This method gets a boolean argument that enables or disables the NTP service, which will allows clients in the local network synchronise their time and date with eBox.

Here is its implementation:

Example 8.2. Enabling the NTP server

sub setService 
{
    my ($self, $active) = @_;
    if ($active xor $self->service) {
        $self->set_bool('active', $active);
    }
} 
              

First we read the active argument and then we use the service method to find out if the server is currently enabled. Only if the new value is different than the old one we go on to set the active gconf key, by calling set_bool.

service

This method returns whether the NTP server is currently enabled or not. Its implementation is trivial, all we need is to fetch the active gconf key and return it:

Example 8.3. Reading the state of the NTP server

sub service 
{
    my $self = shift;
    return $self->get_bool('active');
}  
              

setSynchronized

This method receives a boolean parameter that decides whether eBox will synchronise its date and time with external NTP servers. As you can see, its implementation is similar to setService, we just use the synchronized gconf key instead:

Example 8.4. Enabling the external NTP synchronisation

sub setSynchronized # (synchronized)
{
    my ($self, $synchronized) = @_;

    if ($synchronized xor $self->synchronized) {
        $self->set_bool('synchronized', $synchronized);
    }
}  

synchronized

This method returns the value of the synchronized gconf key. Its implementation is quite simple and similar to service.

Example 8.5. Fetching the configuration for external synchronization

sub synchronized 
{ 
    my $self = shift;
    return $self->getbool('synchronized');
} 
              

setServers

This allows us to store the names of the ntp servers we will use to synchronise our time and date in the gconf database. Its implementation checks whether an SIP address or a domain name were introduced and checks the syntax of the value accordingly. If the syntax is correct the servers will be stored in the gconf database calling the set_string method. In addition to that, we won't let the user set a secondary server if no primary server has been set, and the same for the third server. Let's see part [1]of its implementation:

Example 8.6. Setting the external NTP servers

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

It returns an array that contains the NTP external servers stored in gconf:

Example 8.7. Getting the list of external NTP servers

sub servers 
{
    my $self = shift;
    my @servers;
    @servers = ($self->get_string('server1'),
    $self->get_string('server2'),
    $self->get_string('server3'));

    return @servers;
}  
              

setNewDate

This method changes the system's date and time. This is its implementation:

Example 8.8. Setting a new system time and date

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;
    
}  
              

We receive as arguments every piece of data necessary to set the time and date in the system: day, month, year, hour, minutes and seconds.

We build the complete command in the command variable and a call is made to the root function that executes it. This command must be run as root using sudo, that's why we don't run it directly.

When the system time changes, some system services and eBox modules need to be restarted. This is implemented in the _restartAllServices method seen in Example 8.10.

setNewTimeZone

Along with the time and date, the time zone may be changed too. For this purpose we implemented this method:

Example 8.9. Setting a new time zone

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;
} 
              

It gets two arguments: continent and country. We use them to redo the symbolic link in /etc/localtime so that it points to the new time zone. For example, if the two arguments are Africa and Africa, /etc/localtime will be changed so that it points to /usr/share/zoneinfo/Africa/Dakar.

We store both arguments in gconf too, making to calls to set_string. When the time zone has been changed some eBox modules and the system log services are restarted so that they don't run with the wrong time.

_restartAllServices

Operations that change the system's time and date may leave some parts of the system in an inconsistent state. To avoid having eBox modules and system log services with a time shift we'll write a method that will restart all those services so that they get the new time and date. Here it goes:

Example 8.10. Restarting eBox modules and system services

 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 {
    };
} 
              

First we get an EBox::Global instance that will build instances of every eBox module. We restart all modules except network and firewall, catching any exception that may be thrown while restarting them. Then we manually restart the system daemons: sysklogd, klogd and crond. Doing this requires root privileges so we invoke the root function.

8.1.4. Creating CGIs and templates

After designing and implementing the API, it is time to create the layer that will interact with it: CGIs and mason templates. As you saw in Section 8.1.1, this module is going to answer NTP queries for clients in the local network. The API that enables and disables this service was implemented in Section 8.1.3. We will now create two CGIs and a mason template that will use these methods to give the user an interface for this feature:

The two CGIs are EBox::CGI::NTP::Index and EBox::CGI::NTP::Enable, the template is called ntp/index.mas.

Our first CGI is EBox::CGI::NTP::Index. It inherits from EBox::CGI::ClientBase and implements a constructor that sets the title for our page and the name of the template associated to this CGI. This is the constructor:

Example 8.11. Constructor for EBox::CGI::NTP::Index

package EBox::CGI::NTP::Index;

use strict;
use warnings;

use base 'EBox::CGI::ClientBase';

use EBox::Global;
use EBox::Gettext;

sub new {
    my $class = shift;
    my $self = $class->SUPER::new('title'    => NTP,
                                  'template' => 'ntp/index.mas', @_);
                                  $self->{domain} = "ebox-ntp";
    bless($self, $class);
    return $self;
} 
        

A noteworthy detail is the fact that the title string in this CGI is not translatable, and thus we haven't followed the instructions in Section 2.4.1 for i18n. If the title was translatable we would have followed those instructions.

We are now ready to implement the _process method, which reads the current configuration for the NTP server and feeds it to the mason template.

Example 8.12. Feeding the configuration of the NTP server to the mason template

sub _process
{
    my $self = shift;
    my $ntp = EBox::Global->modInstance('ntp');

    my @array = ();
    my $active = 'no';

    if ($ntp->service()) {
        $active = 'yes';
    }

    push (@array, 'active'     => $active);
    $self->{params} = \@array;
    }
        

You can see that the first thing we do is create an instance of our module (ntp) using EBox::Global. We use this instance to invoke the service method, which returns the configuration of the server and we pass it to the mason template placing it in the param attribute of the CGI. If the mason template needed more arguments we would just add them to the array variable.

This CGI we just implemented, along with its mason template, shows the current configuration for the NTP server. We will now implement the CGI that will receive a new configuration from the user and will tell the backend to change it.

Example 8.13. CGI to enable and disable the NTP server

package EBox::CGI::NTP::Enable;

use strict;
use warnings;

use base 'EBox::CGI::ClientBase';

use EBox::Global;
use EBox::Gettext;

sub new 
{
    my $class = shift;
    my $self = $class->SUPER::new('title' => 'NTP', @_);
    $self->{redirect} = "NTP/Index";
    $self->{domain} = "ebox-ntp";
    bless($self, $class);

    return $self;
}

sub _process
{
    my $self = shift;
    my $ntp= EBox::Global->modInstance('ntp');

    $self->_requireParam('active', __('module status'));
    $ntp->setService(($self->param('active') eq 'yes'));
}

1;  

Its implementation is quite simple, only a couple of details are noteworthy.

First, the constructor sets the redirect attribute to NTP/Index. This will make the browser invoke that CGI after setting the configuration so that the value shown to the user is refreshed.

Finally, we use the setService method, and we pass it a boolean argument that results from the reading of the active parameter from the HTTP request. HTTP parameters are fetched by invoking the param method from the parent class.

Next we are going to create the mason template that will display the configuration and the form to change it. This is an special case, since most eBox modules will need to enable/disable network services. A common way of doing this is provided by the enable.mas mason template, which is part of the basic framework.

It is very easy to use it. We create a template that receives an argument with the current configuration of the server. As explained in Section 5.2 we can include an external template by writing:

<& template.mas, @arguments &>

. We are going to include enable.mas and we give it two arguments, the title and the current configuration of the NTP server. It looks like this:

Example 8.14. Mason template for enabling the NTP server

<%args> $active </%args>
<%init> use EBox::Gettext; </%init>

<div class='ntpnew'>
  <br />
  <& enable.mas, title => __('Enable the local NTP server'),
     active => $active &>
</div>        
        

Using enable.mas requires that the CGI that enables or disables the service be called Enable, since that is hard-coded into enable.mas. If you take another look at the CGI we implemented for this purpose, you'll see that its classname is EBox::CGI::NTP::Enable, just as required.

We are now missing the CGIs that change the time zone, the time and date, and set the external NTP servers. These CGIs and templates are quite simple, it you want to see their source code you can check the subversion repository, their code will not be shown here, we'll limit ourselves to a quick overview of the files involved and their relationships.

Two CGIs handle the time zone changing feature:

  • Timezone uses a mason template (timezone.mas) to display the current timezone configuration.

  • ChangeTimeZone receives the new timezone from the web browser and invokes the SetNewTimeZone.

There is one more detail about the TimeZone CGI. It sends the current country and continent to the mason template, but it also sends a list with all possible continents and a hash that links each continents with the list of countries it contains. All this information is read from the /usr/share/zoneinfo/zone.tab file.

Finally, our module provides two ways to establish the system's time and date: manually and synchronizing with external NTP servers. Each of these two methods excludes the other one (the user can only use one of the two methods). The Datetime CGI displays the information about the current time and date and the configuration of the external NTP servers stored in gconf. Two CGIs let the user change the settings shown in Datetime: Synch enables the synchronization against external NTP servers and ChangeDate changes the time and date manually.

We just said that only one of the two methods may be used at the same time. This is enforced by the mason templates, and we are going to see how it's done. The datetime.mas template gets its arguments from the Datetime CGI, it includes the template synch.mas which lets the user choose whether he wants to set the time manually or use external NTP servers. Then, depending on the current configuration it loads the NTP server selection template (servers.mas) or the manual time and date configuration template (date.mas).

Example 8.15. datetime.mas template

<& /ntp/synch.mas, title => __('Synchronize with external NTP servers'),
   synchronized => $synchronized &>

% if ($synchronized eq 'yes') {
    <& /ntp/servers.mas, title => __('External NTP servers'), servers => \@servers
    &>
% }

% if ($synchronized eq 'no') {
    <& /ntp/date.mas, title => __('Change Date and Time'), date => \@date &>
% }
        

synch.mas, server.mas and date.mas just display the information that datetime.mas sends them as arguments.

8.1.5. Showing the menu and the Summary page

We are already in the final steps in the development of our module. Now we are going to add a new section to the eBox menu that will let the user access the user interface of our module and a section to the summary page with information about the ntp module.

Adding a new section to the menu is as simple as implementing the menu method in our EBox::NTP class. This method gets an instance of EBox::Menu::Root to which we will add a new NTP section with several items: “NTP Server”, “Date/Time” and “Time zone”. Here is the menu method:

Example 8.16. Adding entries to the eBox menu

sub menu
{
    my ($self, $root) = @_;
    my $folder = new EBox::Menu::Folder('name' => 'NTP',
                                        'text' => __('NTP'));

    $folder->add(new EBox::Menu::Item('url' => 'NTP/Index',
                                      'text' => __('NTP server')));
    $folder->add(new EBox::Menu::Item('url' => 'NTP/Datetime',
                                      'text' => __('Date/time')));
    $folder->add(new EBox::Menu::Item('url' => 'NTP/Timezone',
                                      'text' => __('Time zone')));
    $root->add($folder);
}
        

As you saw in Section 5.3, a new section is created with an instance of EBox::Menu::Folder, which needs to be given a name.

The we add to it instances of EBox::Menu::Item which needs a name and a URL (as we explained in Section 5.1, we only need to specify “NTP/Index”, not the whole path).

The NTP module is not going to have its own section in the summary page as there is not much information to display. We'll just add an entry in the status table at top of that page. For that we are going to implement the statusSummary method in EBox::NTP:

Example 8.17. statusSummary in EBox::NTP

sub statusSummary
{
    my $self = shift;
    return new EBox::Summary::Status('ntp', __('NTP local server'),
                                     $self->isRunning, $self->service);
}
        

8.1.6. Generating config files and managing the NTP server

Let's see how you can use mason templates to generate the config file for the NTP server. In Section 8.1.1 we saw that the NTP server reads all its configuration from the /etc/ntp.conf file. We'll use the same system we use to generate HTML for this file.

This part of the module belongs in the backend, so all the methods needed to implemented will be placed in EBox::NTP.

We have created a private method called _setNTPConf which will be invoked every time we need to generate the configuration file. Here it is:

Example 8.18.  Generating the /etc/ntp.conf configuration file

sub _setNTPConf
{
    my $self = shift;
    my @array = ();
    my @servers = $self->servers;
    my $synch = 'no';
    my $active = 'no';

    ($self->synchronized) and $synch = 'yes';
    ($self->service) and $active = 'yes';

    push(@array, 'active'   => $active);
    push(@array, 'synchronized'  => $synch);
    push(@array, 'servers'  => \@servers);
    
    $self->writeConfFile(NTPCONFFILE, "ntp/ntp.conf.mas", \@array);
}
        

It is very simple, we just add all the arguments for the mason template to the array variable. Our arguments are:

active

It tells the template whether we are going to offer the NTP service for clients in our network or not.

synchronized

It tells the template whether we are going to synchronize our system's time with external NTP servers.

servers

It's an array with the list of external NTP servers.

After building the array with the arguments we call writeConfFile, which generates the configuration file with proper permissions and needs these arguments:

  • The absolute path to the configuration file. In our case it is the NTPCONFFILE constant, which is defined in the beginning of our module:

    use constant NTPCONFFILE => "/etc/ntp.conf";
  • The path to the mason template that generates the file.

  • An array with the arguments for the template.

And now let's see the template. We decided to leave some of the values in the configuration file with fixed values, other values are dynamic and are generated based on the arguments received:

Example 8.19. Template to generate /etc/ntp.conf

<%args>
$active
$synchronized
@servers
</%args>
# /etc/ntp.conf, configuration for ntpd
# Generated by EBox

driftfile /var/lib/ntp/ntp.drift
statsdir /var/log/ntpstats/

% if ($synchronized eq 'yes') {
%       if ($servers[0]) {
server <% $servers[0] %>
%       }
%       if ($servers[1]) {
server <% $servers[1] %>
%       }
%       if ($servers[2]) {
server <% $servers[2] %>
%       }
% }
% if ($active eq 'yes') {
server 127.127.1.0
% }
fudge 127.127.1.0 stratum 13

restrict default kod notrap nomodify nopeer noquery

restrict 127.0.0.1 nomodify
        

The only thing left is the code to manage the ntp daemon. We have to implement several methods, the first is an abstract method defined in EBox::Module: _regenConfig. It is invoked when services are restarted or when a new configuration for a module is saved. It has to generate the configuration file, using the already seen _setNTPConf method. Let's see it:

Example 8.20. _regenConfig method

sub _regenConfig
{
    my $self = shift;

    $self->_setNTPConf;
    $self->_doDaemon();
    }
        

Besides invoking _setNTPConf, it needs to restart the daemon, it does so by calling a private method: _doDaemon. Together with methods _stopService, and isRunning, _doDaemon performs the management of the daemon. Let's see them one by one:

Example 8.21. NTP daemon management method

sub _doDaemon
        {
           my $self = shift;
           my $logger = EBox::Global->logger;

           if (($self->service or $self->synchronized) and $self->isRunning) {
                 EBox::Service::manage('ntpd','stop');
                 sleep 2;
                 if ($self->synchronized) {
                     my $exserver = $self->get_string('server1');
                     try {
                         root("/usr/sbin/ntpdate $exserver");
                     } catch EBox::Exceptions::Internal with {
                         $logger->info("Error, ntpdate could" .
                                       " not be started.");
                     };
                 }
                 EBox::Service::manage('ntpd','start');
            } elsif ($self->service or $self->synchronized) {
                 if ($self->synchronized) {
                    my $exserver = $self->get_string('server1');
                    try {
                       root("/usr/sbin/ntpdate $exserver");
                    } catch EBox::Exceptions::Internal with {
                       $logger->info("Error ntpdate could" .
                                     " not be started.");
                    };
                 }
                 EBox::Service::manage('ntpd','start');
            } elsif ($self->isRunning) {
                 EBox::Service::manage('ntpd','stop');
                 if ($self->synchronized) { 
                     EBox::Service::manage('ntpd','start');
                 }
           }
        }

This method is invoked:

  • To launch the server if it was stopped.

  • To restart the server when it is running.

  • To stop the server.

Depending on which case we are in it calls EBox::Service::manage telling it which action we want to perform: start or stop. If the system date is to be synchronised with external servers we should try to make a manual query with the /usr/sbin/ntpdate before starting the daemon. This is a recommended practice before launching the ntp daemon.

Let's see how EBox::Service::manage works, it starts, stops or restarts a service directly depending on its argument. It uses runit application to manage automatically daemon by its supervision. This allows us to check the daemon status, for how long and restart the service every time is goes down.

In order to configure a runit service, it is required to edit a shell script with the service name, in our case ntpd, indicating how the service is run in foreground mode. This file must place in tools/runit/ directory.

Example 8.22. tools/runit/ntpd file

#!/bin/sh
exec 2>&1
exec /usr/sbin/ntpd -n -g

        

There only two methods left for you to see. _stopService is defined as an abstract method by EBox::Module, it just stops the service. isRunning tells us whether the ntp daemon is currently running, it does so by checking its process id (PID).

Example 8.23. _stopService method.

sub _stopService
{
    my $self = shift;

    if ($self->isRunning) {
        $self->_daemon('stop');
    }
} 
        

Example 8.24. Telling whether the NTP daemon is running or not.

sub isRunning
{
   my $self = shift;
   return $self->pidFileRunning(PIDFILE);
} 
        

8.1.7. Setting up proper firewall rules

The last step in creating this module is to let the firewall know about our needs, so that the ntp service works fine. If our date is set by querying external servers we will need to make UDP connections on port 123 to them. We also need to let clients connect to our 123 UDP port if we are going to be an NTP server. We created a private method called _configureFirewall that takes care of all this stuff. Here it is:

Example 8.25. Firewall configuration

sub _configureFirewall
{
    my $self = shift;
    my $fw = EBox::Global->modInstance('firewall');

    if ($self->synchronized) {
        $fw->addOutputRule('udp', 123);
    } else {
        $fw->removeOutputRule('udp', 123);
    }

    if ($self->service and (!defined($fw->service('ntp')))) {
        $fw->addService('ntp', 'udp', 123, 0);
        $fw->setObjectService('_global', 'ntp', 'allow');
    } elsif ( !($self->service) and defined($fw->service('ntp')) ) {
        $fw->removeService('ntp');
    }
}
        

The eBox firewall module simplifies this job providing methods that let us add new rules to the firewall. First of all we need an instance of the firewall module, as usual we use EBox::Global to get it. Then we add the output rule if we need to connect to external servers or remove it if we do not need to and it had been previously added. The methods for this are addOutputRule and removeOutputRule.

For our NTP server, we need to register our service with the firewall by calling addService and then we'll allow it by default by calling setObjectService. If the NTP server feature is disabled we remove the ntp service from the firewall by calling removeService.

8.1.8. Conclusion

We are done, we just created an eBox module form scratch. We've gone through every step needed and seen what issues to watch out for. Now it is time to try it out and do some tests to check that it works as we expect. We hope this guide has been helpful and encourages you to contribute to making it a better platform with new modules and features.



[1] The complete implementation can be found in the subversion repository