Introducing Ebox
1.1.�Intended audience
This guide is written for developers interested in writing new modules with new features for eBox, and for those who need to change or extend the base eBox framework.
eBox is written in perl and is mostly object-oriented. It is assumed that readers know how to write object oriented programs in perl. Some features of the language or the libraries used in eBox may be discussed, but no attempt has been made to document those aspects throughly.
Some experience writing object oriented applications and using design patterns in any language should be useful, but you should be fine just with the basic concepts of object oriented programming.
An effort has been made to provide examples for all aspects of eBox development explained in this guide. Most of them come straight from existing and working modules. Furthermore, a complete module is developed step by step in Chapter�8. It's a real module so that chapter should cover all the parts of a complete and working module.
eBox is a platform for the development and deployment of security and work-group related services on a local network. It is configured through a web interface that integrates all services in a consistent and easy to use way. Its goal is to be usable by non-experts.
eBox is meant to be installed on a dedicated machine, all configuration tasks are performed through the eBox web interface. This means that the configuration of the underlying services is one-way: eBox modules generate configuration files, overwriting system files in some cases (although that tends to be avoided if possible) and manual changes to those files are not detected by eBox. This simplifies the implementation and usage of the package but has the disadvantage that developers need to be careful if they use their own system for testing purposes.
eBox design is modular, new modules providing new services and features can be developed independently from its core package. eBox simplifies the deployment of new modules and the updates of existing ones with a software management module, which is also independent from the eBox base package.
The system is based on Linux and has been developed on top of Debian, no support is provided for other Linux distributions as there are some "debianisms" in some of the modules. Porting to other Linux distributions should be quite easy, and porting to other Unix like operating systems such as OpenBSD would take a little more work but it should still be doable, and worth it.
eBox is based on a few software packages, which are used for several purposes:
- Linux 2.6
eBox relies on some features provided by the kernel, some are available only on the 2.6 series. Among these features are netfilter (for the firewall), 802.1q (for VLANs) and ipsec.
- perl and mod_perl
All eBox modules are implemented in perl, the web interface runs under mod_perl for performance reasons.
- mason
mason is a templating system for perl, it is used to generate HTML for the web based user interface and to generate configuration files.
- apache
The web interface is served by apache, we usually use the apache-perl Debian package.
- gconf
eBox stores its configuration using gconf2 and its perl library bindings.
- sudo
Apache runs as an unprivileged user, all eBox modules use sudo to execute commands that need to be run as root.
The base eBox package provides a development framework for new modules. By using this framework modules automatically get features like configuration backups and reversion of changes in the configuration before they are saved. The features available to module developers will be explained in detail in later chapters.
A few conventions are used throughout this guide regarding several concepts:
Booleans.�Whenever we need to talk about a boolean value passed to a method or returned by it, we will refer to a true value as true and to a false value as undef or false.
Perl modules and eBox modules.�There may be some confusion in some parts of the guide about the term module as the term may refer to an eBox module or a perl module. An eBox module is the complete set of classes (and perl modules) and other files that provide a self-contained functionality for eBox and that may be installed or uninstalled independently from the rest of eBox. A perl module is basically a perl source file.
Whenever there is no possibility of confusion, we'll just write module, otherwise we'll write explicitly perl module since this meaning will be the one used less frequently.
The typical eBox module handles the configuration of a daemon, possibly integrated with other eBox modules. The developer decides in what ways should the user be able to configure the daemon, this ways do not necessarily map directly to the daemon configuration options on a one-to-one relationship. The developer may pick a sane default value for most of the options and hide them from the user, showing him just the ones that he feels are important. Even further, an option changed by the user through the web interface may cause configuration changes in several real configuration options or even on several eBox modules. The main goal is to have an user interface as simple, easy to use, and integrated as possible, while providing the user with a rich set of features.
However, there may be modules that do not handle the configuration of a network service. An example is the 'sysinfo' module in the base system, it just gathers system information to be shown in the Summary page and provides a few menu entries for features that do not belong to any module in particular. The module parent class defines several abstract methods that real modules are free to leave unimplemented. Thus, a module may just provide info in the Summary, add new menu items, handle a network service or all of the above.
The normal, and most interesting, case is the module described in the first paragraph. Such a module has three parts.
It defines and implements an API that will let the GUI, other modules or plain perl scripts configure the daemon it is going to handle.
The second is the GUI, which is a set of CGIs that show the current configuration to the user and let him change it, these CGIs use the API defined earlier to fetch the configuration info and make changes to it.
The third part of the module is usually quite small, it translates all the configuration information stored in GConf into firewall rules, configuration files and commands that make the network service behave as the user expects. It also takes care of starting, stopping and restarting the service when needed.
This separation between the GUI and the backend opens the possibility for other means of changing the configuration. One such means is through perl scripts, these is useful when making packages for a distribution, the package maintainer can write a simple script to import the current system configuration into eBox, or set up some default values. Another use of the API is for other modules, the firewall module is the most used case, almost all modules need tell the firewall to open some port for them. In the future a wrapper may be written around these APIs to publish them through web-services, this would make eBox configurable programmatically over the network.
Besides these three parts, the module has some other minor parts, like its piece of the summary page in the web interface, menu entries, dependency declarations, backups of configuration pieces not stored in GConf, etc.
That's all there is to it, creating a module is as simple as following these steps:
Decide what daemon your module is going to handle and learn how it works and how to set it up.
Plan what options you are going to expose to the user through the web interface, and how they may interact with other eBox modules
Define and implement the API that will let the GUI manipulate the necessary configuration options. The main class in your module should inherit from
EBox::GConfModule, this class wraps the GConf API and transparently implements some useful features that all eBox modules need to have.Create the CGIs and HTML templates that will let the user interact with the module. CGIs should inherit from the
EBox::CGI::Baseclass which, again, provides some features transparently to all its children.Write the code needed to make the daemon work, possibly generating a configuration file and setting up a firewall rule using the
EBox::Firewallclass. Configuration files are generated almost trivially with mason, which is the template system also used to generate the HTML pages for the GUI.
The size and complexity of a module depends directly on the complexity of the service involved and the amount of configuration items exposed to the user. The necessary work to make a small eBox module is minimal, take the DNSCache module as an example, its CGIs add up to 49 lines of code and the module itself is 134 lines long.
The directory structure of an eBox module may look quite complex for a newcomer. A usual eBox module directory should look like this:
AUTHORS configure.ac INSTALL Makefile.am README stubs/
autogen.sh COPYING NEWS schemas/ tools/ ChangeLog
debian/ m4/ po/ src/ www/ migration/
The more important directories are src/, schemas/, www/, stubs/ and migration/.
The src/ directory contains the source code for the module. Inside this directory two directories can be found: EBox and templates. The EBox directory contains the Perl source code files, including a CGI subdirectory with the web frontend for the module. The templates directory is used to store the Mason templates, which will be used to generate the HTML output.
The schemas/ directory contains gconf schemas, used to define the configuration schemas for modules, and optionally provide default values for some options.
The www/ directory contains images and stylesheets that will be used in the web frontend.
The stubs/ is used to store Mason templates to generate configuration files for the module services.
The migration/ stores the migration perl scripts to upgrade from an eBox module version to a newer one.
Bear Section�1.4 in mind, a new way to develop eBox modules have been developed. It relies on Model-View-Controller design pattern, which an architectural pattern used in large software projects to decouple highly data (model) and user interface (view) so that developer can change the data architecture wihout changing the user interface and vice versa. A new component is added, the controller, which manages data access and business logic acting as an intermediate between them.
Thus with our new architecture the eBox MVC conception will be the following:
- Model
Our model is defined as a perl module which is a subclass of
EBox::Model::DataTableor one of the subclasses.- View
The already defined Mason templates are used to show the information to the user.
- Controller
The CGIs which are mapped from the Mason templates to do actions. There are some default actions already implemented.
In Chapter�7, there is a large explanation about how it works internally. Below we are only going to introduce the main components which an eBox developer requires to know to build up an eBox MVC's module.
A basic MVC based module should have, at least, these three main parts:
A series of data models which represent the information you want to store to configure your desired service.
Manage your service daemon (if any) accessing to the data model, add menu entry, summary and make the data contained in the model(s) available to the eBox system.
Expose the desired API to other eBox modules, perl scripts and SOAP interface.
A model is just a table concept, that is, an array of rows which have a defined fixed definition, the table description. This table description contains a series of EBox::Types which are instances of EBox::Types::Abstract, these eBox types try to include the required representation for eBox configuration parameters, a brief list could be:
- Basic types
Basic types include are:
Boolean,Int,Text,Password,Select(an enumeration) orLink, which describe a type that stores a simple link reference.- Network related types
These comprise
IPAddrwhich represents an IP address,MACAddrwhich is a Ethernet address,Servicewhich matches with port and protocol such as/etc/servicesdoes andPortRangewhich describes a single port number, a port range or any port.- Composite types
Currently, just a
Uniontype has been developed which represents a type is used from a defined list of options. If none of options are suitable, aUnion::Textmay be used with this purpose.
The data storage is managed automatically by the model itself. A model stores its information under a GConf directory which must set explicitly at the model creation. However, you can set dynamically which becomes the model instance in a submodel. The submodels are model instances whose GConf directory is set dynamically, not only at the runtime. Its usage is intimately with EBox::Types::HasMany, which describes a relationship between a field from a model with another model indicating that field contains the whole data from another model.
Apart from the table description, it is required to set several attributes such as the name, the printable name, if it is ordered, etc... Some model definition examples may help to understand the subject:
Example�1.1.�Object model definition
sub _table
{
my @tableHead =
(
new EBox::Types::Text
(
'fieldName' => 'name',
'printableName' => __('Name'),
'size' => '12',
'unique' => 1,
'editable' => 1
),
new EBox::Types::HasMany
(
'fieldName' => 'members',
'printableName' => __('Members'),
'foreignModel' => 'MemberTable',
'view' => '/ebox/Object/View/MemberTable',
'backView' => '/ebox/Object/View/MemberTable',
'size' => '1',
)
);
my $dataTable =
{
'tableName' => 'ObjectTable',
'printableTableName' => __('Objects'),
'automaticRemove' => 1,
'modelDomain' => 'Objects',
'defaultActions' => ['add', 'del', 'editField' ],
'tableDescription' => \@tableHead,
'class' => 'dataTable',
'help' => __('Objects'),
'printableRowName' => __('object'),
};
return $dataTable;
}
As it is shown, a model is used to describe an eBox object, which has a name and a group of members which are described in Example�1.2:
Example�1.2.�Object member model definition
sub _table
{
my @tableHead =
(
new EBox::Types::Text
(
'fieldName' => 'name',
'printableName' => __('Name'),
'size' => '12',
'unique' => 1,
'editable' => 1
),
new EBox::Types::IPAddr
(
'fieldName' => 'ipaddr',
'printableName' => __('IP Address'),
'editable' => 1,
),
new EBox::Types::MACAddr
(
'fieldName' => 'macaddr',
'printableName' => __('MAC Address'),
'editable' => 1,
'optional' => 1
),
);
my $dataTable =
{
'tableName' => 'MemberTable',
'printableTableName' => __('Members'),
'automaticRemove' => 1,
'defaultActions' => ['add', 'del', 'editField' ],
'modelDomain' => 'Objects',
'tableDescription' => \@tableHead,
'class' => 'dataTable',
'help' => __('Objects'),
'printableRowName' => __('member'),
};
return $dataTable;
}
As you may see, every field has some attributes as well as the model itself. Just note the actions are the allowed actions to apply to that model. For instance, you can add, delete rows, edit fields... Several attributes are allowed for every type and model, if they are not explicitly set at the model definition, they are treated as false value. That is, if it is a boolean type or empty string if the type is an string.
The best part of it comes from forms. Typical eBox scenarios contain a set of tables and forms. So the information that a form may show can be stored in a data model as described above. However, it has some significant differences. A form data model looks like a table data model with just one row which is always shown to be edited. A form model definition is similar to a table data model one as Example�1.3 depicts:
Example�1.3.�Jabber dispatcherconfiguration form data model definition
sub _table
{
my @tableDesc =
(
new EBox::Types::Text(
fieldName => 'server',
printableName => __('Jabber server name'),
size => 12,
editable => 1,
),
new EBox::Types::Int(
fieldName => 'port',
printableName => __('Port'),
size => 6,
editable => 1,
defaultValue => 5222,
),
new EBox::Types::Text(
fieldName => 'user',
printableName => __('Jabber user name'),
size => 12,
editable => 1,
),
new EBox::Types::Password(
fieldName => 'password',
printableName => __('User password'),
size => 12,
editable => 1,
minLength => 4,
maxLength => 25,
),
new EBox::Types::Boolean(
fieldName => 'subscribe',
printableName => __('Subscribe'),
editable => 1,
),
new EBox::Types::Text(
fieldName => 'adminJID',
printableName => __('Administrator Jabber Identifier'),
size => 12,
editable => 1,
),
);
my $dataForm = {
tableName => 'JabberDispatcherForm',
printableTableName => __('Configure Jabber dispatcher'),
modelDomain => 'Events',
tableDescription => \@tableDesc,
class => 'dataForm',
help => __('In order to configure the Jabber event dispatcher ' .
'is required to be registered at the chosen Jabber ' .
'server or check subscribe to do register. The administrator ' .
'identifier should follow the pattern: user@domain[/resource]'),
};
return $dataForm;
}
As you may notice, fields at a form can contain default values. This is done due to have default eBox configuration working. For example, you may want to enable a service to a specific port whose value does not modify the service if the default value is set.
Once the definition is set you may want to validate the user input. Types do some validation, for instance, IPAddr checks if the parameters contain a valid IP. However, there are cases where the developer requires to do extra validation. To achieve so, there are two methods to override validateRow and validateTypedRow.
Furthermore, actions have callbacks to called after an action have been finished. Therefore add action has addedRowNotify, edit updatedRowNotify and so on. This allows you to manage the updated information to do something different from just storing updating information at GConf database.
This section explains how the data model is shown at eBox user interface. If you just want to show a data model in eBox, a new menu entry is required. Its URL must have this pattern: ${modelDomain}/View/${tableName}. The modelDomain and tableName are attributes which have been already set at the data model definition. A table model will show up an empty table and a HTML form will appear if a form model is defined, they are named as their viewers.
You may wish to combine the several models in the same page. Here where the model composites play its role. The model composites are containers which stores two kinds of components: models and composites, thus you can create complex composites to find the more usable and adapted user interface for your needs. The current available layouts are:
- top-bottom
The components are shown in order from top to the bottom.
- tabbed
The component viewers are displayed in a tabbed way. That is, each component is shown when the tab name is clicked
Define a model composite is even simpler than a model one as it depicts in this example:
Example�1.4.�A sample model composite definition
sub _description
{
my $description =
{
components => [
'EnableForm',
'ConfigurationComposite',
],
layout => 'top-bottom',
name => 'GeneralComposite',
printableName => __('Events'),
compositeDomain => 'Events',
help => __('Events module may help you to make eBox ' .
'inform you about events that happen at eBox ' .
'in some different ways'),
};
return $description;
}
The components are referenced using its name, tableName at models and name at composites. In order to display a composite based page, it is required to change the menu entry. In this case, the URL to call must follow this pattern ${compositeDomain}/Composite/${name}. That's all you need to display your models together in a single page.
Just get the magic done, it is required to publish the models and composites. To achieve so, the eBox modules, which are subclasses of EBox::GConfModule, must inherit from EBox::Model::ModelProvider and EBox::Model::CompositeProvider interfaces, implementing its respectively methods models and composites, which returns an array reference of model and composite instances that lives within this eBox module realm. An example could be the next one:
Example�1.5.�Model and composite publishing example
sub models {
my ($self) = @_;
return [
$self->configureLogModel(),
$self->forcePurgeModel(),
];
}
sub composites
{
my ($self) = @_;
return [
$self->_configureLogComposite(),
];
}
Once the models are published and ready to be displayed, they actually have to do something to manage its service. Current data model API is quite experimental in the way it is constantly evolving to satisfy the client needs. The main components that use data model are migration scripts and eBox module itself to obtain the required data to configure the service.
The main method to retrieve the data model is rows which returns an array reference which contains the rows from that data model. You can filter the result using a filter string or get the result from a defined page, since data model allows filtering and paging. Each array component contains the return value from row which is a hash reference containing the following key - value pairs:
- id
Each row has an identifier which allows distinguishing from the others inside the data model. You can use it as well as indexer calling the
rowmethod.- order
A data model can be ordered or not. If so, the rows are ordered in the same order they have been added. Furthermore, you might tailor the order overriding
_tailoredOrderto give rows in the desired order.- values
Array reference which contains eBox types, which were explained previously.
- valueHash
Hash reference indexed by field name whose values are eBox types as values does.
- plainValueHash
For convenience, some methods have been added to help accessing the methods. This one returns a hash reference containing field name - value pairs.
- printableValueHash
Similar to the previous one, it returns a hash reference containing field name - printable value pairs.
The rows may be quite complex. An dumped from a result could clarify the subject:
Example�1.6.�Rows dumped result
[
{
'order' => undef,
'plainValueHash' => {
'members' => {
...
},
'name' => 'RRHH',
'id' => 'x6666'
},
'valueHash' => {
'members' => bless( {
...
}, 'EBox::Types::HasMany' ),
'name' => bless( {
'unique' => 1,
'value' => 'RRHH',
'printableName' => 'Name',
'model' => $VAR1->[0]{'valueHash'}{'members'}{'model'},
'HTMLSetter' => '/ajax/setter/textSetter.mas',
'optional' => 0,
'size' => '12',
'row' => $VAR1->[0],
'editable' => 1,
'type' => 'text',
'HTMLViewer' => '/ajax/viewer/textViewer.mas',
'fieldName' => 'name'
}, 'EBox::Types::Text' )
},
'values' => [
$VAR1->[0]{'valueHash'}{'name'},
$VAR1->[0]{'valueHash'}{'members'}
],
'id' => 'x6666',
'printableValueHash' => {
'members' => {
'model' => 'MemberTable',
'values' => [
{
'macaddr' => undef,
'name' => 'Claudia',
'id' => 'memb44',
'ipaddr' => '192.168.1.92/32'
}
],
'directory' => 'objectTable/keys/x6666/members'
},
'name' => 'RRHH',
'id' => 'x6666'
},
'readOnly' => 0
},
];
With the ease of use in mind, form data model allows accessing directly to the value, printable value attributes or the whole type using as method name the field name. For instance, at jabber dispatcher configuration form data model, you can access to the value, printable value and the instanced type itself of user property calling userValue, userPrintableValue and userType respectively.
Nevertheless, you may search for rows which match a fixed criterion, this implies the match is done against the field value. Four methods make the work for you, find, findAll, findValue and findValueAll. They are divided in two groups, the ones which returns the valueHash and the ones which returns the printableValueHash and has the 'Value' word in their names. Moreover, if the method ends with 'All' words, returns not just the first row which matchs but also the possible remainder rows which match the criterion as well. Taking the previous data model a search with $model->find( 'name' => 'RHHH' ), the dumped return value is:
{
'members' => {
'model' => 'MemberTable',
'values' => [
{
'macaddr' => undef,
'name' => 'Claudia',
'id' => 'memb44',
'ipaddr' => '192.168.1.92/32'
}
],
'directory' => 'objectTable/keys/x6666/members'
},
'name' => 'RRHH',
'id' => 'x6666'
}
Final part of developing with MVC framework is expose the desired API to other eBox modules, perl scripts and SOAP clients. Two main options are probable, just disclose the instanced data model allowing full access to the model using in the way we used above or integrate some helper methods to access to the most important features which clients use most. The latter is likely the chosen if the data model rises in its complexity. A module which consists of several data models connected with each other indicates that a simple API may help the module's clients.
As you may see, the MVC framework is powerful scheme to develop in eBox. However, it is not so impressive yet. It is still under development and some optimization as well as lack of some features. For instance, an eBox type to manage file uploading from the user, automatic getter and setter methods,...
dikutip dari ebox-platform.com


Post a Comment