blob: d5139f55dedae0c1337ff84011c0dea087d6f2ef [file] [log] [blame]
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +02001/* Asterisk's AMI interface functions in TTCN-3
2 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
3 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12/*
13 * https://docs.asterisk.org/Configuration/Interfaces/Asterisk-Manager-Interface-AMI/AMI-v2-Specification/
14 */
15module AMI_Functions {
16
17import from Misc_Helpers all;
18import from TELNETasp_PortType all;
19import from Osmocom_Types all;
20import from TCCConversion_Functions all;
21import from Socket_API_Definitions all;
22
23modulepar {
24 float mp_ami_prompt_timeout := 10.0;
25}
26
27const charstring AMI_FIELD_ACTION := "Action";
28const charstring AMI_FIELD_USERNAME := "Username";
29const charstring AMI_FIELD_SECRET := "Secret";
30const charstring AMI_FIELD_RESPONSE := "Response";
31
32type record AMI_Field {
33 charstring key,
34 charstring val
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +020035} with {
36 encode "TEXT"
37 variant "SEPARATOR(': ', ':\s+')"
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020038};
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +020039
40type set of AMI_Field AMI_Msg with {
41 encode "TEXT"
42 variant "SEPARATOR('\r\n', '(\r\n)|[\n]')"
43 variant "END('\r\n', '(\r\n)|[\n]')"
44};
45
46external function enc_AMI_Msg(in AMI_Msg msg) return charstring
47 with { extension "prototype(convert) encode(TEXT)" }
48external function dec_AMI_Msg(in charstring stream) return AMI_Msg
49 with { extension "prototype(convert) decode(TEXT)" }
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020050
51template (value) AMI_Field
52ts_AMI_Field(template (value) charstring key,
53 template (value) charstring val) := {
54 key := key,
55 val := val
56};
57
58template (present) AMI_Field
59tr_AMI_Field(template (present) charstring key := ?,
60 template (present) charstring val := ?) := {
61 key := key,
62 val := val
63};
64
65/*
66 * Field Templates:
67 */
68
69template (value) AMI_Field
70ts_AMI_Field_Action(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION, val);
71template (value) AMI_Field
72ts_AMI_Field_Username(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_USERNAME, val);
73template (value) AMI_Field
74ts_AMI_Field_Secret(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_SECRET, val);
75
76template (present) AMI_Field
77tr_AMI_Field_Action(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_ACTION, val);
78template (present) AMI_Field
79tr_AMI_Field_Username(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_USERNAME, val);
80template (present) AMI_Field
81tr_AMI_Field_Secret(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_SECRET, val);
82template (present) AMI_Field
83tr_AMI_Field_Response(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_RESPONSE, val);
84
85
86template (present) AMI_Field
87tr_AMI_Field_ResponseSuccess := tr_AMI_Field(AMI_FIELD_RESPONSE, "Success");
88
89
90/*
91 * Message Templates:
92 */
93
94template (value) AMI_Msg
95ts_AMI_Action_Login(charstring username, charstring secret) := {
96 ts_AMI_Field_Action("Login"),
97 ts_AMI_Field_Username(username),
98 ts_AMI_Field_Secret(secret)
99};
100
101template (present) AMI_Msg
102tr_AMI_Action_Login(template(present) charstring username := ?,
103 template(present) charstring secret := ?) := superset(
104 tr_AMI_Field_Action("Login"),
105 tr_AMI_Field_Username(username),
106 tr_AMI_Field_Secret(secret)
107);
108
109template (present) AMI_Msg
110tr_AMI_Response_Success := superset(
111 tr_AMI_Field_ResponseSuccess
112);
113
114/*
115 * Functions:
116 */
117
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200118private function f_ami_wait_for_prompt_str(TELNETasp_PT pt, charstring log_label := "(?)")
119return charstring {
120 var charstring rx, buf := "";
121 var integer fd;
122 timer T;
123
124 T.start(mp_ami_prompt_timeout);
125 alt {
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +0200126 [] pt.receive(pattern "\n") { buf := buf & "\n" };
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200127 [] pt.receive(charstring:?) -> value rx { buf := buf & rx; repeat };
128 [] pt.receive(integer:?) -> value fd {
129 if (fd == -1) {
130 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
131 "AMI Telnet Connection Failure: " & log_label);
132 } else {
133 repeat; /* telnet connection succeeded */
134 }
135 }
136 [] T.timeout {
137 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
138 "AMI Timeout for prompt: " & log_label);
139 };
140 }
141 T.stop;
142 return buf;
143}
144
145function f_ami_wait_for_prompt(TELNETasp_PT pt, charstring log_label := "(?)") return AMI_Msg {
146 var charstring buf := f_ami_wait_for_prompt_str(pt, log_label);
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +0200147 var AMI_Msg msg := dec_AMI_Msg(buf);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200148 return msg;
149}
150
151/* send a AMI command and obtain response until prompt is received */
152private function f_ami_transceive_ret_str(TELNETasp_PT pt, charstring tx) return charstring {
153 pt.send(tx);
154 return f_ami_wait_for_prompt_str(pt, tx);
155}
156
157function f_ami_transceive_ret(TELNETasp_PT pt, template (value) AMI_Msg tx_msg) return AMI_Msg {
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +0200158 var charstring tx_txt := enc_AMI_Msg(valueof(tx_msg));
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200159 var charstring resp_txt := f_ami_transceive_ret_str(pt, tx_txt);
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +0200160 return dec_AMI_Msg(resp_txt);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200161}
162
163function f_ami_transceive_match(TELNETasp_PT pt,
164 template (value) AMI_Msg tx_msg,
165 template (present) AMI_Msg exp_ret := ?) {
166 var AMI_Msg ret := f_ami_transceive_ret(pt, tx_msg);
167 if (not match(ret, exp_ret)) {
168 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
169 log2str("Non-matching AMI response: ", ret, " vs exp: ", exp_ret));
170 }
171}
172
173function f_ami_transceive_match_response_success(TELNETasp_PT pt,
174 template (value) AMI_Msg tx_msg) {
175 f_ami_transceive_match(pt, tx_msg, tr_AMI_Response_Success);
176}
177
178function f_ami_action_login(TELNETasp_PT pt, charstring username, charstring secret) {
179 f_ami_transceive_match_response_success(pt, ts_AMI_Action_Login(username, secret));
180}
181
182}