Introduzione ad Asterisk AEL
by admin on Aug.26, 2010, under Asterisk, Telefonia, VoIP
AEL è un acronimo che sta per Asterisk Extensions Language. Il suo scopo è quello di rendere la stesura dei dialplans più semplice rispetto a quanto avviene normalmente utilizzando la tradizionale sintassi di extensions.conf.
La sintassi standard è falsamente user friendly, rendendo difficoltoso redigere dialplans complessi, ed, molto spesso, ancora più difficoltoso interpretarli.
AEL invece rende la scrittura di un dialplan una attività più semplice e rapida, specialmente nel caso in cui sistemista Asterisk provenga dal mondo IT, dato che la sua sintassi lo rende simile ad uno dei tanti linguaggi di programmazione con cui avrà probabilmente avuto familiarità.
AEL è infatti concepito per fornire un linguaggio di programmazione per la scrittura di un dialplan per Asterisk.
Il parser AEL (pbx_ael.so) è totalmente separato dal modulo che effettua il parsing di extensions.conf (pbx_config.so).
Per usare AEL, è necessario solamente che il modulo pbx_ael.so venga caricato da Asterisk. Ciò avviene automaticamente impostando ‘autoload=yes’ in /etc/asterisk/modules.conf.
Una volta che il modulo è caricato, ricerca il file “extensions.ael” in /etc/asterisk/.
Extensions.conf ed extensions.ael possono operare in sinergia.
Alcuni utenti preferiscono ad esempio mantenere extensions.conf per le features configurate nella sezione ‘general’.
Ecco alcune indicazioni che spero possano riuscire ad invogliare qualcuno ad iniziare a scrivere in AEL.
Contesti
Un contesto (context) in AEL rappresenta un set di extensions esattamente nella stessa maniera in cui lo fa in extensions.conf.
context default {
};
NOTA: La parentesi graffa aperta deve essere usata come nello schema precedente. Spostarla sulla linea seguente può avere conseguenze imprevedibili!
Se il file extensions.ael non esistesse nella directory /etc/asterisk, basta crearlo assegnandone la opportuna proprietà all’utente con cui viene eseguito Asterisk. Può essere reso normalmente leggibile per tutti gli altri utenti.
# touch /etc/asterisk/extensions.ael
# chown asterisk:asterisk /etc/asterisk/extensions.ael
Per iniziare, se considerassimo, in extensions.conf, un context tipo il seguente:
[context-stupido]
exten => 333,1,Wait(3)
exten => 333,n,Answer()
exten => 333,n,Playback(benvenuto)
exten => 333,n,MusicOnHold()
exten => h,1,NoCDR()
exten => h,n,Hangup()
Potremmo facilmente dedurne che la ripetizione di “exten => 333,…” è un po’ noiosa e sarebbe auspicabile non fosse necessaria. E che la sua presenza aggiunge inoltre una certa complessità visiva al codice, anche se si tratta di codice molto semplice.
La stessa cosa in AEL potrebbe venire espressa così:
context context-stupido => {
111 => {
Wait(3)
Answer();
Playback(benvenuto)
MusicOnHold();
};
h => {
NoCDR();
Hangup();
};
};
Il codice AEL si presenta meglio e risulta più chiaro e leggibile rispetto alla versione standard. Forse non così tanto come in questo esempio così banale, ma se il dialplan dovesse ingrossarsi, ciò diventerebbe immediatamente evidente.
In AEL un context viene definito come un blocco di codice, esattamente come in buona parte dei più comuni linguaggi di programmazione, compreso cioè fra parentesi graffe, costituito a sua volta da un insieme di blocchi relativi ad extensions. E sempre come nei più comuni linguaggi di programmazione, ciascuna linea (statement) termina con un segno di “punto e virgola” (semi-colon).
Nel context di esempio la extension 333, dopo aver atteso 3 secondi prima che la controparte ottenga risposta, emette un messaggio di benvenuto e successivamente attiva la applicazione MusicOnHold, mentre la extension h termina la chiamata assicurandosi che i relativi dettagli non vengano registrati (NoCDR).
Includes
I contexts possono venire inclusi in altri contesti. Tutti i contexts inclusi sono elencati in un singolo blocco.
context default {
includes {
locali;
extraurbane;
internazionali;
};
};
Dopo aver modificato il file extensions.ael, occorre impartire, nella CLI di Asterisk il comando “ael reload” per rendere operative le modifiche (conformemente ad uno stile comune a tanti altri moduli).
Variabili
Le variabili in Asterisk non sono tipizzate, pertanto, per definire una variabile, basta specificarla assegnandole un valore.
Le variabili globali vengono impostate nel loro specifico blocco.
globals {
CONSOLE=Console/dsp;
TRUNK=Zap/g2;
};
Le variabili (locali) possono essere impostate nelle extensions.
context foo {
555 => {
x=5;
y=quiquoqua;
quoziente=10/2
NoOp(x vale ${x} ed y vale ${y} mentre quoziente vale ${quoziente} !);
};
};
MACRO
Come la scrittura dei contexts, la scrittura delle MACRO è altrettanto semplice.
Le MACRO sono un po’ come le functioni o i metodi e vengono definite tramite la keyword “macro”, seguita da eventuali argomenti racchiusi tra parentesi tonde.
macro musica-di-attesa () {
MusicOnHold();
};
equivale alla MACRO nel tipico stile di extensions.conf:
[macro-musica-di-attesa]
exten => s,1,MusicOnHold()
Gli argumenti passati alla MACRO vengono quindi riferiti dallo stesso nome.
macro std-exten( ext , dev ) {
Dial(${dev}/${ext},20);
switch(${DIALSTATUS) {
case BUSY:
Voicemail(b${ext});
break;
default:
Voicemail(u${ext});
};
};
Una MACRO viene invocata in AEL tramite un simbolo di “ampersand” (&) seguito dal nome della MACRO e gli argomenti tra parentesi tonde, o parentesi tonde vuote in assenza di argomenti.
Ad esempio, se combinassimo il context con la MACRO visti entrambi in precedenza, la cosa apparirebbe così:
context context-stupido => {
111 => {
Wait(3)
Answer();
Playback(benvenuto)
&musica-di-attesa();
};
h => {
NoCDR();
Hangup();
};
};
macro musica-di-attesa () {
MusicOnHold();
};
Lo statement catch nelle MACRO
È spesso motivo di confusione tra i neo utenti di AEL il modo di usare extensions speciali come h, t o i in una MACRO.
Lo si fa utilizzando lo statement catch come nell’esempio seguente (un blocco catch può venire specificato per catturare le estensions speciali.
macro std-exten( ext , dev ) {
Dial(${dev}/${ext},20);
switch(${DIALSTATUS) {
case BUSY:
Voicemail(b${ext});
break;
default:
Voicemail(u${ext});
};
catch a {
VoiceMailMain(${ext});
return;
};
};
Ricordiamo che in questo caso la extension a (abort) verrebbe raggiunta premendo * (asterisco).
macro musica-di-attesa () {
MusicOnHold();
catch h {
NoCDR();
Hangup();
};
};
Labels e GoTo
Anche l’uso delle labels in AEL è più intuitivo.
Una label la si specifica facendola seguire da un simbolo di due punti (colon), equindi usando lo statement goto per saltare ad essa. Ciò risulta valido sia per i contexts che per le MACRO.
Si consideri il seguente esempio in sintassi standard:
[esempio-label]
exten => 111,1,Set(COUNT=0)
exten => 111,n(incremento),Set(COUNT=$[${COUNT}+1])
exten => 111,n,GotoIf($["${COUNT}">"5"]?4:5)
exten => 111,n,Goto(termine)
exten => 111,n,SayDigits(${COUNT})
exten => 111,n,Goto(incremento)
exten => 111,n(termine),Hangup()
exten => h,1,NoCDR()
exten => h,n,Hangup()
Si tratta evidentemente di un contatore, che incrementa una variabile fino a 5 e quindi salta alla extension h terminando la chiamata. Ha due labels: incremento e termine.
Appare un po’ confuso, e in particolare la ripetizione di “exten => 111,n” la si sospetta inutile.
Lo stesso codice in AEL potrebbe essere espresso come segue:
context esempio-label {
111 => {
Set(COUNT=0);
incremento:
Set(COUNT=$[${COUNT}+1]);
if("${COUNT}">"5") {
goto termine;
};
SayDigits(${COUNT});
goto incremento;
termine:
Hangup();
};
h => {
NoCDR();
Hangup();
};
};
Certamente ora appare meglio organizzato, facile da leggere e da comprendere. In altri termini, più professionale.
Lo statement “goto label” segue lo stesso comportamento della applicazione Goto(), ad eccezione del fatto che l’ultimo valore deve consistere in una label. De la label non esistesse, si otterrebbe un errore di run-time. Se invece la label esiste, ma in una differente extension, occorre specificare il nome della extension name oltre alla label nel goto; se la label si trovasse in un context differente, occorrerebbe specificare context|extension|label.
context esempio-goto {
s => {
inizio:
NoOp(Loop Infinito!);
Wait(1);
goto inizio; // va alla label nella stessa extension
};
3 => {
goto s|inizio; // va alla label in una extension differente
};
4 => {
goto esempio-goto|s|inizio; // va alla label in una extension differente dello stesso context
};
};
context esempio-goto2 {
s => {
fine:
goto esempio-goto|s|inizio; // va alla label in un context differente
};
};
Strutture condizionali
In precedenza si è fatto uso dello statement if, ancora qualcosa che viene usato in tutti i linguaggi di programmazione.
Si tenga conto che avendo una causola “else”, occorre necessariamente mettere delle parentesi graffe a delimitare la porzione non-else dello statement if.
Analogamente allo statement “if”, AEL supporta lo statements “switch”.
context conditional {
_8XXX => {
Dial(SIP/${EXTEN});
if ("${DIALSTATUS}" = "BUSY") {
Voicemail(${EXTEN}|b);
} else {
Voicemail(${EXTEN}|u);
}
};
_777X => {
switch (${EXTEN}) {
pattern N11:
NoOp(You called a N11 number-- ${EXTEN});
break;
case 7771:
NoOp(You called 7771!);
break;
case 7772:
NoOp(You called 7772!);
break;
case 7773:
NoOp(You called 7773!);
// fall thru-
default:
NoOp(In the default clause!);
};
};
};
Loops
AEL possiede una propria implementazione di strutture iterative, come i loops “for” e “while” .
context cicli {
1 => {
for (x=0; ${x} < 3; x=${x} + 1) {
Verbose(x vale ${x} !);
}
}
2 => {
y=10;
while (${y} >= 0) {
Verbose(y vale ${y} !);
y=${y}-1;
}
}
}
AEL v.2
AEL v.2, che è la versione più recente di AEL, ha esteso il linguaggio all’uso di una sintassi più flessibile (lo strano vincolo del posizionamento della parentesi graffa aperta sembrerebbe non avere più senso), fornendo inoltre messaggi di errore più comprensibili, ed alcune funzionalità fin qui mancanti.
AEL2 offre differenti modi per esprimere la stessa cosa. E così, una serie di scelte può venire annidata così:
ifTime(*|*|20-25|dec)
{
Playback(auguri/natale);
}
else
{
ifTime(*|*|31|dec)
{
Playback(auguri/fineanno);
}
else
{
ifTime(*|*|1|jan)
{
Playback(auguri/nuovoanno);
}
else
{
ifTime(*|*|14|feb)
{
Playback(auguri/sanvalentino);
else {Playback(benvenuti); };
}
}
}
}
Oppure espressa in forma equivalente così:
ifTime(*|*|20-25|dec) {
Playback(auguri/natale);
}
else ifTime(*|*|31|dec) {
Playback(auguri/fineanno);
}
else ifTime(*|*|1|jan) {
Playback(auguri/nuovoanno);
}
else ifTime(*|*|14|feb) {
Playback(auguri/sanvalentino);
}
else {
Playback(benvenuti);
}
Jump
Per richiamare una diversa extension (oppure, in uno statement switch, un altro “case”), si usa lo statement “jump”:
context incoming {
s => {
NoOp(Qui viene gestita la chiamata);
};
0437945876 => jump s; // vengono accettate le chiamate PSTN destinate a tale numero
};
context home-test {
999 => jump s@incoming; // 999 serve a simulare una chiamata in ingresso
};
Considerazioni finali
Per concludere questa breve incursione nel mondo di AEL, occorre ammettere che è piuttosto strano che esso non sia stato accolto ed adottato dalla comunità Asterisk come meriterebbe, e che la maggior parte dei membri della stessa continuino a combattere con la sintassi standard per scrivere dialplans anche complessi, compito che diventa progressivamente più arduo se il dialplan cresce.
Apprendere AEL è molto semplice e coloro che conoscessero, oltre alla sintassi standard, anche un qualsiasi linguaggio di programmazione, potrebbero passare tranquillamente ad AEL in una manciata di ore, acquisendo in cambio notevoli vantaggi.
Come ultima indicazione, non bisognerebbe inserire nuovo codice in /etc/extensions.ael senza prima averlo testato con il tool aelparse.
Il tool aelparse viene compilato a partire dal relativo codice presente nella directory utils/ dei sorgenti di Asterisk.
Si tratta di un eseguibile stand-alone che prevede le opzioni -d, -w, -q, e -n.
- -d: Usando tale opzione, anziché leggere il file extensions.ael2 o extensions.ael da /etc/asterisk (o dalla directory deputata a contenere i files di configurazione) aelparse esamina la directory corrente. Ciò risulta molto utile per poter testare il dialplan senza compromettere l’ambiente di produzione.
- -w: Convert il file dialplan di input (extensions.ael2) nel formato extensions.conf, ed archivia tale file nella directory corrente col nome di “extensions.conf.aeldump”.
A cosa dovrebbe mai servire? Be, potreste prendere tale file ed utilizzarlo su una versione 1.2 o precedente di Asterisk, pertanto senza dover più programmare dialplans nel formato extensions.conf, anche continuando ad utilizzare vecchie versioni.
- -n: Non visualizza le chiamate di funzione necessarie a costruire le extensions in asterisk.
- -q: (implica -n). Visualizza solamente messaggi di warning ed errore.

