The need to keep certain information ordered is fairly common
across eBox modules. It is also common to provide reordering functions.
An example of this are firewall rules, which need to be applied in
a given order, and the user has to be able to change their order.
The EBox::Order class solves just this
problem.
The idea is to give a directory to each item you want to
keep ordered. Following the firewall example, we could have a
rules/ directory and, below it, one directory
per rule. Each rule would get a unique identifier, and that
would be its directory name below rules/.
A rule with id r3561 would be stored in
rules/r3561, and below that directory we would
store keys with each one of the rule's parameters. This is the most
natural way of organizing items such as firewall rules, and it is with
this organization that EBox::Order is designed
to work.
The ordering mechanism adds one field to the
items being ordered. Not surprisingly it is called
order. To use the ordering API you have
to create an EBox::Order instance. Its
constructor takes to arguments: the instance of the module that owns
the items being ordered, and the base directory where the items are
stored.
EBox::Order implements these
operations:
highestReturns the highest order key of
all the items.
lowestReturns the lowest order key of
all the items.
nextnGiven a number, returns the
order key for the next item.
prevnGiven a number, returns the
order key for the previous
item.
getReturns the identifier for the item whose
order key equals a given
number.
swapIt finds the items whose order
keys match two given numbers and swaps their values.
listReturns a reference to an array that holds the identifiers of all items, ordered from lowest to highest order.
Example 3.6. Ordering firewall rules
Let's see how the firewall module uses
EBox::Order to keep its forwarding rules
ordered. The _fwdRulesOrder returns the
EBox::Order instance for the firewall
rules:
sub _fwdRulesOrder
{
my $self = shift;
return new EBox::Order($self, "fwdrules");
}fwdrules is the directory that
holds all the rules. Another private helper function is
_fwdRuleNumer, it returns the order number
for a given rule identifier:
sub _fwdRuleNumber # (rule)
{
my ($self, $rule) = @_;
return $self->get_int("fwdrules/$rule/order");
}New rules are appended at the end of the list, so we find the
highest order number and add one to it, this code is part of the
addFwdRule method:
my $order = $self->_lastFwdRule() + 1;
$self->set_string("fwdrules/$id/name", $id);
$self->set_string("fwdrules/$id/action", $action);
$self->set_bool("fwdrules/$id/active", 1);
$self->set_int("fwdrules/$id/order", $order);_lastFwdRule is a trivial wrapper that
returns the highest ordered number:
sub _lastFwdRule
{
my $self = shift;
my $order = $self->_fwdRulesOrder();
defined($order) or return 0;
return $order->highest;
}Finally there are two methods to allow rule
reordering, they are FwdRuleUp and
FwdRuleDown (only the first one is shown
here since they are almost identical):
sub FwdRuleUp # (rule)
{
my ($self, $rule) = @_;
my $order = $self->_fwdRulesOrder();
defined($order) or return;
my $num = $self->_fwdRuleNumber($rule);
if ($num == 0) {
return;
}
my $prev = $order->prevn($num);
$order->swap($num, $prev);
}