├── README.md
└── dialer.pl
/README.md:
--------------------------------------------------------------------------------
1 | FreeSWITCH call generator for performance tests
2 | ===============================================
3 |
4 | This is a simple dialer that connects to FreeSWITCH via event socket and
5 | originates calls at a given interval.
6 |
7 | The script subsututes the question mark signs (?) with random digits in
8 | caller ID and the destination number.
9 |
10 | There are two ways to specify the call destination:
11 |
12 | 1. Loopback endpoint
13 | --------------------
14 |
15 | If used with `--content` and `--dest` options, the dialer originates the
16 | calls into the specified context via loopback endpoint. For example, the
17 | following contexts will forward all calls to some remote servers:
18 |
19 | ```
20 |
21 |
22 |
23 |
24 |
25 |
26 |
28 |
29 |
30 |
31 |
32 | ```
33 |
34 | Keep in mind that the default limit of sessions per second in FreeSWITCH
35 | is 30 (`sessions-per-second` parameter in
36 | `autoload_configs/switch.conf.xml`). Because of the loopback endpoint,
37 | each call occupies 3 channels, and this results in 10 calls per second
38 | maximum.
39 |
40 | For example, the following command would start 100 calls, and each call
41 | would last 10 minutes:
42 |
43 | ```
44 | perl /opt/freeswitch-perf-dialer/dialer.pl \
45 | --ncalls=100 --cps=9 --duration=600 --context=dialer01 \
46 | --dest='01234?????'
47 | ```
48 |
49 | 2. Endpoint string
50 | ------------------
51 |
52 | You can specify explicitly the endpoint string. In case of SIP
53 | endpoints, that will instruct FreeSWITCH to use a specified SIP profile
54 | and gateway for example:
55 |
56 | ```
57 | perl /opt/freeswitch-perf-dialer/dialer.pl --cid='+3333???????' \
58 | --endpoint='sofia/external/+777???????@10.250.250.23' --cps=5 --forever
59 |
60 |
61 | perl /opt/freeswitch-perf-dialer/dialer.pl --cid='+3333???????' \
62 | --endpoint='sofia/gateway/voxbeam/+777???????' --cps=5 --forever
63 | ```
64 |
65 | Transferring instead of playback
66 | --------------------------------
67 |
68 | The `--exec` option allows you to send the call to a dialplan context
69 | instead of playing back the media. The following example sends the call
70 | after originating to default dialplan context, with destination number
71 | 12345678 and caller ID 87654321:
72 |
73 | ```
74 | perl /opt/freeswitch-perf-dialer/dialer.pl --cid='12345678' \
75 | --endpoint='sofia/gateway/voxbeam/87654321' --cps=5 --forever \
76 | --exec='12345678 XML default 87654321 87654321'
77 | ```
78 |
79 | The general syntax of the exec string is as follows:
80 |
81 | ```
82 | XML
83 | ```
84 |
85 |
86 | Installing on Debian 8
87 | ----------------------
88 |
89 | ```
90 | apt-get install -y curl git
91 |
92 | cat >/etc/apt/sources.list.d/freeswitch.list <
140 |
141 | This software is distributed under the MIT license.
--------------------------------------------------------------------------------
/dialer.pl:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015 Stanislav Sinyagin
2 |
3 | # Permission is hereby granted, free of charge, to any person obtaining
4 | # a copy of this software and associated documentation files (the
5 | # "Software"), to deal in the Software without restriction, including
6 | # without limitation the rights to use, copy, modify, merge, publish,
7 | # distribute, sublicense, and/or sell copies of the Software, and to
8 | # permit persons to whom the Software is furnished to do so, subject to
9 | # the following conditions:
10 |
11 | # The above copyright notice and this permission notice shall be
12 | # included in all copies or substantial portions of the Software.
13 |
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 | use strict;
23 | use warnings;
24 | use Getopt::Long;
25 | use Time::HiRes;
26 | use POSIX;
27 | use ESL;
28 |
29 | $| = 1;
30 |
31 | my $fs_host = '127.0.0.1';
32 | my $fs_port = 8021;
33 | my $fs_password = 'ClueCon';
34 |
35 | my $callerid = '12126647665';
36 | my $dest = '13115552368';
37 | my $playback = 'local_stream://moh';
38 | my $context = 'public';
39 | my $endpoint;
40 | my $exec;
41 |
42 | my $duration = 60;
43 | my $ncalls = 10;
44 | my $forever;
45 | my $interval;
46 | my $cps;
47 | my $rnd_interval;
48 | my $rnd_cps;
49 |
50 |
51 | my $help_needed;
52 |
53 | my $ok = GetOptions
54 | (
55 | 'fs_host=s' => \$fs_host,
56 | 'fs_port=s' => \$fs_port,
57 | 'fs_password=s' => \$fs_password,
58 | 'cid=s' => \$callerid,
59 | 'dest=s' => \$dest,
60 | 'context=s' => \$context,
61 | 'endpoint=s' => \$endpoint,
62 | 'duration=i' => \$duration,
63 | 'ncalls=i' => \$ncalls,
64 | 'forever' => \$forever,
65 | 'cps=f' => \$cps,
66 | 'interval=f' => \$interval,
67 | 'rc=f' => \$rnd_cps,
68 | 'ri=f' => \$rnd_interval,
69 | 'play=s' => \$playback,
70 | 'exec=s' => \$exec,
71 | 'help' => \$help_needed,
72 | );
73 |
74 |
75 | if( not $ok or $help_needed or scalar(@ARGV) > 0 )
76 | {
77 | print STDERR "Usage: $0 --cps=10 [options...]\n",
78 | "Options:\n",
79 | " --fs_host=HOST \[$fs_host\] FreeSWITCH host\n",
80 | " --fs_port=PORT \[$fs_port\] FreeSWITCH ESL port\n",
81 | " --fs_password=PW \[$fs_password\] FreeSWITCH ESL password\n",
82 | " --cid=NUMBER \[$callerid\] caller ID\n",
83 | " --dest=NUMBER \[$dest\] destination number\n",
84 | " --context=NAME \[$context\] FreeSWITCH context name\n",
85 | " --endpoint=STRING destination endpoint\n",
86 | " --duration=N \[$duration\] call duration in seconds\n",
87 | " --ncalls=N \[$ncalls\] total number of calls\n",
88 | " --forever run endlessly and ignore ncalls\n",
89 | " --cps=F rate in calls per second\n",
90 | " --interval=F interval between calls in seconds (CPS\*\*-1)\n",
91 | " --rc=F random factor in CPS (should be higher than CPS*2)\n",
92 | " --ri=F random factor in interval (should be less than interval/2)\n",
93 | " --play=STRING \[$playback\] playback argument\n",
94 | " --exec=STRING application to execute instead of playback\n",
95 | " --help this help message\n",
96 | "\n",
97 | "If endpoint is specified, --dest and --context are ignored.\n",
98 | "Otherwise, the call is sent to the loopback endpoint with the specified\n",
99 | "context and destination number\n",
100 | "The question mark characters (?) in numbers are replaced with random digits\n",
101 | "\n",
102 | ;
103 | exit 1;
104 | }
105 |
106 | if( defined($cps) and defined($interval) )
107 | {
108 | print STDERR "Only one of CPS and interval must be defined\n";
109 | exit 1;
110 | }
111 |
112 | if( not defined($cps) and not defined($interval) )
113 | {
114 | print STDERR "Either CPS or interval must be defined\n";
115 | exit 1;
116 | }
117 |
118 | if( defined($rnd_cps) )
119 | {
120 | if( not defined($cps) )
121 | {
122 | print STDERR "--rc can only be defined together with --cps\n";
123 | exit 1;
124 | }
125 | elsif( $rnd_cps < $cps * 2 )
126 | {
127 | print STDERR "--rc shoudl be higher than CPS*2\n";
128 | exit 1;
129 | }
130 | }
131 |
132 | if( defined($rnd_interval) )
133 | {
134 | if( not defined($interval) )
135 | {
136 | print STDERR "--ri can only be defined together with --interval\n";
137 | exit 1;
138 | }
139 | elsif( $rnd_interval > $interval / 2 )
140 | {
141 | print STDERR "--ri should be lower than interval/2\n";
142 | exit 1;
143 | }
144 | }
145 |
146 |
147 |
148 | my $originate_string =
149 | 'originate ' .
150 | '{ignore_early_media=true,' .
151 | 'origination_uuid=%s,' .
152 | 'originate_timeout=60,' .
153 | 'origination_caller_id_number=' . $callerid . ',' .
154 | 'origination_caller_id_name=dialer_pl}';
155 |
156 | if( defined($endpoint) )
157 | {
158 | $originate_string .= $endpoint;
159 | }
160 | else
161 | {
162 | $originate_string .= 'loopback/' . $dest . '/' . $context;
163 | }
164 |
165 | if( defined($exec) )
166 | {
167 | $originate_string .= ' ' . $exec;
168 | }
169 | else
170 | {
171 | $originate_string .= ' ' . '&playback(' . $playback . ')';
172 | }
173 |
174 |
175 | my $esl = new ESL::ESLconnection($fs_host,
176 | sprintf('%d', $fs_port),
177 | $fs_password);
178 |
179 | $esl->connected() or die("Cannot connect to FreeSWITCH");
180 |
181 | if( not defined($interval) )
182 | {
183 | $interval = 1.0/$cps;
184 | }
185 |
186 | if( defined($rnd_cps) )
187 | {
188 | $rnd_interval = 1.0/$rnd_cps;
189 | }
190 |
191 |
192 | my $nc = 0;
193 | my $start = Time::HiRes::time();
194 |
195 | while( $forever or $nc < $ncalls )
196 | {
197 | my $next_time = $start + $nc * $interval;
198 |
199 | if( defined($rnd_interval) )
200 | {
201 | $next_time += rand($rnd_interval);
202 | }
203 |
204 | # Replace "?" with random digits
205 | my $orig_str = $originate_string;
206 | while( $orig_str =~ /\?/o )
207 | {
208 | my $random_digit = sprintf('%d', rand(10));
209 | $orig_str =~ s/\?/$random_digit/;
210 | }
211 |
212 | my $now = Time::HiRes::time();
213 | if( $next_time > $now )
214 | {
215 | Time::HiRes::sleep($next_time - $now);
216 | }
217 |
218 | my $uuid = $esl->api('create_uuid')->getBody();
219 | my ($time_epoch, $time_hires) = Time::HiRes::gettimeofday();
220 | $esl->bgapi(sprintf($orig_str, $uuid));
221 | $esl->bgapi(sprintf('sched_hangup +%d %s', $duration, $uuid));
222 |
223 | printf("%.6d: %s.%.6d\n",
224 | $nc,
225 | POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($time_epoch)),
226 | $time_hires);
227 | $nc++;
228 | }
229 |
--------------------------------------------------------------------------------