Diagnostics and tools
Uniloader is meant to help automate a number of little tasks that pertain to administering and running a QueueMetrics system.
Diagnostics: AMI connection test
Uniloader lets you test an AMI port from the command line. It will also check that the queuemetrics context is present on the system, and will make sure that the AMI user has the required "originate" privilege.
$ ./uniloader test ami -? NAME: uniloader test ami - Tests an AMI connection USAGE: uniloader test ami [command options] [arguments...] DESCRIPTION: This command test an AMI connection. It checks that the `queuemetrics` context is present and its functions are present. It checks originates to `10@queuemetrics` and prints available queues. OPTIONS: --host "127.0.0.1" Your Asterisk server --port "5038" The AMI port on Asterisk --login The AMI user as defined in manager.conf --secret The AMI secret [$AMISECRET] --testChannel "Local/10@queuemetrics" The channel to use when testing originates. --testExtCtxt "10@queuemetrics" The ext@ctxt to use when testing originates.
In order to use it, you can call it like:
$ uniloader test ami --login admin --secret amp111
It will print out a comprehensive report, like:
Testing AMI connection to 10.10.5.27:5038 - Username 'admin' secret '******' AMI Connected: Asterisk Call Manager/1.3 N. Meaning ext@queuemetrics Present? -_---------------------------------------------------------------------------- 1 Dummy extension 10 Ok 2 Chanspy – inbound calls 11 Ok 3 Sets a call status 12 Ok 4 Chanspy – outbound calls 14 Ok 5 Add call feature 16 Ok 6 Remove call feature 17 Ok 7 Agent pause 22 Ok 8 Agent unpause 23 Ok 9 AddMember 25 Ok 10 RemoveMember 26 Ok 11 Custom dial 28 Ok ◉ 12 Send SMS to SIP device 29 MISSING 13 Soft hangup of call in queue 30 Ok 14 Redirect call in queue 31 Ok 15 Agent pause with hotdesking 32 Ok 16 Agent unpause with hotdesking 33 Ok 17 Addmember with hotdesking 35 Ok 18 Removemeber with hotdesking 37 Ok Known Queues Completed Abandoned -_------------------------------------------------------------- - 300 10 5 - 301 0 0 - default 0 0 - 400 0 0 Originate on 10@queuemetrics worked.
This shows:
-
The version of AMI in use
-
Whether all default queuemetrics extensions are present, and which ones are missing
-
The queues configured in Asterisk, and their current usage statistics
-
Whether the user has "originate" privileges
If connection is possible, it returns with a status code of zero; if not possible, or wrong credentials are used, it returns with an error code so that you can script it.
Originating custom channels
It is possible to use have Uniloader originate arbitrary channels on the PBX by telling it the channel and the extension and context to connect.
$ uniloader amitest --login admin --secret 123 --testChannel SIP/701 --testExtCtxt 706@from-internal
In the example above, first channel 'SIP/701' is brought up, and then it is connected to extension '706' in context 'from-internal'.
Diagnostics: Test upload link
If you want to make sure that your upload credentials to a server (either HTTP/S or SQL) are working, you can test them by using:
$ ./uniloader test upload -? NAME: uniloader test upload - Tests a data upload connection USAGE: uniloader test upload [command options] [arguments...] OPTIONS: --uri, -u The connection URI. Valid URIs start with file:, mysql:, http:, https: --login, -l "webqloader" The login for your connection --pass, -p "qloader" The password for your connection [$UPASSWD] --token, -t In MySQL mode, the partition. In HTTP/S mode, usually blank or server-id --timeout "10" The time-out to wait for (in seconds) on errors.
If the command runs and succeeds, it will print out the current high water mark for the back-end and return with a status of zero. If there is any error, or the format of the connection is invalid, it will return with a status different than zero.
For example, this command tests a local database that contains data:
$ uniloader testupload --uri "mysql:tcp(127.0.0.1:3306)/queuemetrics?allowOldPasswords=1" \ --login queuemetrics --pass javadude --token P001 Testing upload credentials. 2017/07/27 14:28:14 Error: no db object 2017/07/27 14:28:14 Assert: DB Connection works 2017/07/27 14:28:14 [,P001] Driver error: retrying in 200 ms
High Water Mark is 1472824811 [2016-09-02 16:00:11 +0200 CEST] Connection OK
As back-ends keep on retrying for errors automatically, the tool waits for a missed answer within 'timeout' seconds before giving up and marking the connection as invalid. |
If the driver supports it, the test also prints:
-
The complete version of the remote QueueMetrics server
-
The time it took to estabilish a connection
-
The current time, according to the server
-
The current time, according to Uniloader
For example:
Server version : 23.09.8 / 141 - 2024.01.16-12:11 - ec6180d2ef6@rel_23_09_m Connection time: 158 ms Server time : Fri Apr 05 19:27:22 CEST 2024 Uniloader time : Fri Apr 05 19:27:22 CEST 2024
If there is a difference of more than one second (no matter the time zone) between your QM server and Uniloader, you are in for some anomalies in real-time displays. Make sure that both servers are aligned using NTP - as QueueMetrics Live servers are. |
Diagnostics: Test connection to Mysql/MariaDB database
This tool will check that you have a working connection to your Mysql or MariaDB database, and that the credentials you use are correct.
NAME: uniloader test mysql - Tests a MySQL/MariaDB connection USAGE: uniloader test mysql [command options] [arguments...] DESCRIPTION: This command tests connection credentials. It just connects to the database and runs an empty query to confirm everything is working as expected. OPTIONS: --dburi "tcp(127.0.0.1:3306)/queuemetrics?allowOldPasswords=1" The database to connect to --login "root" A database user --pwd A database password
An example run:
uniloader test mysql --dburi 10.10.5.27/somedb --login user --pwd pass
Will print:
2019/04/02 09:04:13 Testing MySQL connection to 'user:pass@tcp(10.10.5.27:3306)/somedb?allowOldPasswords=1' 2019/04/02 09:04:13 -- Connection took 54.183µs 2019/04/02 09:04:13 -- Query took 21.387871ms 2019/04/02 09:04:13 Local time on database is: 2019-04-02 09:04:13
Diagnostics: Test connection to Postgres database
This tool will check that you have a working connection to your Postgres database, and that the credentials you use are correct.
$ uniloader test postgres -? NAME: uniloader test postgres - Tests a Postgres connection USAGE: uniloader test postgres [command options] [arguments...] DESCRIPTION: This command tests a Postgres connection. It just connects to the database and runs an empty query to confirm everything is working as expected. OPTIONS: --ps-uri "localhost/fusionpbx" A Postgres database to connect to --ps-login "fusionpbx" A database user --ps-pwd A database password [$FUSIONPWD]
Example run:
uniloader test postgres --ps-uri 10.10.5.182/fusionpbx --ps-login fusionpbx --ps-pwd ""
Will print:
2019/02/22 09:59:32 Testing Postgres connection to 'postgres://fusionpbx:@10.10.5.182/fusionpbx' 2019/02/22 09:59:32 -- Connection took 298.534µs 2019/02/22 09:59:32 -- Query took 21.780999ms 2019/02/22 09:59:32 Local time on database is: 2019-02-22T00:50:58.357799+01:00
If you know that the database credentials are correct and you get a connection error
that says unknown authentication response: 10 , it
is likely that your database is using SCRAM-SHA-256 authentication. This is supported in Uniloader
from version 23.09.3 onwards - in case, update Uniloader and try again.
|
Diagnostics: Test Freeswitch’s ESL port
This tool tries connecting to Freeswitch’s ESL port and tries displaying queues and agents defined.
$ uniloader test fsw-esl -? NAME: uniloader test fsw-esl - Test Freeswitch's ESL port USAGE: uniloader test fsw-esl [command options] [arguments...] DESCRIPTION: This command tries to connect to FreeSwitch's Event Socket. OPTIONS: --host "127.0.0.1" Your FreeSwitch server --port "8021" The ESL port on FreeSwitch --auth "ClueCon" The ESL auth secret [$AUTH]
For example:
uniloader test fsw-esl --host 127.0.0.1 --port 8021 --auth ClueCon
Will display a successful dialog:
2019/02/22 10:01:37 Testing Freeswitch connection to '127.0.0.1:8021' with auth token 'ClueCon' 2019/02/22 10:03:29 <ESL: Content-Type: auth/request 2019/02/22 10:03:29 <ESL: 2019/02/22 10:03:29 ======= Attempting log in 2019/02/22 10:03:29 >ESL auth ClueCon 2019/02/22 10:03:29 <ESL: Content-Type: command/reply 2019/02/22 10:03:29 <ESL: Reply-Text: +OK accepted 2019/02/22 10:03:29 <ESL: 2019/02/22 10:03:29 ======= Login OK 2019/02/22 10:03:29 ======= Showing queues in mod_callcenter 2019/02/22 10:03:29 >ESL api callcenter_config queue list 2019/02/22 10:03:29 <ESL: Content-Type: api/response 2019/02/22 10:03:29 <ESL: Content-Length: 543 2019/02/22 10:03:29 <ESL: 2019/02/22 10:03:29 <ESL: name|strategy|moh_sound|time_base_score|tier_rules_apply|tier_rule_wait_second|tier_rule_wait_multiply_level|tier_rule_no_agent_no_wait|discard_abandoned_after|abandoned_resume_allowed|max_wait_time|max_wait_time_with_no_agent|max_wait_time_with_no_agent_time_reached|record_template|calls_answered|calls_abandoned|ring_progressively_delay|skip_agents_with_external_calls|agent_no_answer_status 2019/02/22 10:03:29 <ESL: 75082016-6394-4738-b896-b9121c060612|longest-idle-agent|local_stream://default|system|false|30|true|true|900|false|0|90|5||1|10|0|true|On Break 2019/02/22 10:03:29 <ESL: +OK 2019/02/22 10:03:29 ======= Showing agents defined in mod_callcenter 2019/02/22 10:03:29 >ESL api callcenter_config agent list 2019/02/22 10:03:29 <ESL: Content-Type: api/response 2019/02/22 10:03:29 <ESL: Content-Length: 464 2019/02/22 10:03:29 <ESL: 2019/02/22 10:03:29 <ESL: name|system|uuid|type|contact|status|state|max_no_answer|wrap_up_time|reject_delay_time|busy_delay_time|no_answer_delay_time|last_bridge_start|last_bridge_end|last_offered_call|last_status_change|no_answer_count|calls_answered|talk_time|ready_time|external_calls_count 2019/02/22 10:03:29 <ESL: 739a4112-d755-4977-bf2b-d2b9037babd0|single_box||callback|{call_timeout=15}user/200@10.10.5.182|Available|Waiting|0|10|90|90|30|1550762836|1550762841|1550763105|1550762723|0|1|5|1550763195|0 2019/02/22 10:03:29 <ESL: +OK 2019/02/22 10:03:29 ======= Logging off 2019/02/22 10:03:29 >ESL exit 2019/02/22 10:03:29 <ESL: Content-Type: command/reply 2019/02/22 10:03:29 <ESL: Reply-Text: +OK bye 2019/02/22 10:03:29 <ESL:
Diagnostics: Test data download from Enswitch
This tool will check that you can download data from your Enswitch PBX, as one of its tenants, as QueueMetrics Live would do.
Example run:
uniloader test enswitch --uri https://www.my.pbx --login qm@cli --pwd ab1234 --single-tenant 1234
Note that you always need to pass the numeric Customer ID in the single-tenant
parameter.
Will print:
2021/03/31 19:08:07 Testing Enswitch connection to 'esw/https://www.my.pbx qm@cli ab1234 123' 2021/03/31 19:08:09 Downloaded 6 rows in 1748 ms 2021/03/31 19:08:09 Showing last 10 records: 2021/03/31 19:08:09 # 1: {1617156858|1617156848.656|q1|NONE|ENTERQUEUE||0447xxx} 2021/03/31 19:08:09 # 2: {1617156859|1617156848.656|q1|NONE|ABANDON|1|1|1} 2021/03/31 19:08:09 # 3: {1617166397|1617166387.152|q1|NONE|ENTERQUEUE||0427xxx} 2021/03/31 19:08:09 # 4: {1617166412|1617166387.152|q1|Agent/621|RINGNOANSWER|15000} 2021/03/31 19:08:09 # 5: {1617166425|1617166387.152|q1|Agent/621|CONNECT|28|Local/621@enswitch-local/n|8000} 2021/03/31 19:08:09 # 6: {1617166440|1617166387.152|q1|Agent/621|COMPLETEAGENT|28|15|1}
You can check credentials and download queue/agent configuration with the sub-command pbxinfo enswitch. |
Diagnostics: Test an AudioVault server
It is possible to run a query on an AudioVault server as-if QueueMetrics would do it. See AudioVault for more details on how to enable it.
This test is useful to:
-
detect connectivity issues: if you use a proxy in front of AudioVault for HTTPS, you may want to make sure that it is working correctly
-
demonstrate slow search performance
-
print the correct configuration to be entered on QueueMetrics to enable AudioVault
To set up an AudioVault server, our suggested operational sequence is the following:
-
find a unique-of a call that you want to find from one of the recordings you have on disk
-
run
uniloader av find
to make sure that the search path is set up correctly and that your call-id can be found -
run
uniloader av serve
to expose the service on a local port -
run
uniloader test audiovault
pointing to the local HTTP URL to make sure that the service is configured correctly -
set up an HTTPS proxy
-
run
uniloader test audiovault
pointing to the public HTTPS URL to make sure that everything is still working with the proxy in front
To run it, just use:
uniloader test audiovault --public-url http://127.0.0.1:4012 --token Sup3rZ3brA -c 443652.1
Note that there is no need to pass a timestamp if the call-id has the format "12345678.12" like is the default for Asterisk systems.
And this will output:
Connecting to server on http://127.0.0.1:4012/search/ with token 'Sup3rZ3brA' Searching for UID '443652.1' around timestamp '443652' ... took 4.553417ms ========= RESULTS FOUND: 1 #| Type| Size| File ---|-----| --------|-------------------------------------- 1| MP3| 66821| call-443652.1.mp3
It will also print out working QueueMetrics configuration.properties
lines that will use the same configuration:
audio.server=it.loway.app.queuemetrics.callListen.listeners.JsonListener audio.jsonlistener.url=http://127.0.0.1:4012/search/ audio.jsonlistener.method=POST audio.jsonlistener.searchtoken=Sup3rZ3brA audio.jsonlistener.verbose=false audio.html5player=true
Diagnostics: Direct QueueMetrics database access
It is possible to use Uniloader to directly access a QueueMetrics queue_log
database to perform low-level operations.
Database contents
It is possible to print the contents of a database:
$ uniloader qmdb --dburi "localhost/queuemetrics" --login queuemetrics --pwd javadude info # Partition # Entries From date To date Time span 1 P001 51129261 2018.09.03 07:09 2019.09.03 09:09 1y 0d 1h
This displays the contents of each partition.
Exporting a partition
You can export a partition to a text file in queue_log
format; this can be useful for backup purposes
or to upload it again to a different instance or a different partition:
$ uniloader qmdb --dburi "localhost/qm" --login qm --pwd 123 \ export --partition P001 --filename mylog.txt Exporting partition: P001 (from: 0 to: 999999999999) to 'mylog.txt' in batches of 50000 rows Finding zones: ........................................ Source zones: 27 -> Packed zones: 5 (Efficiency: 80%) Processing zone 1 of 5: 0% done Processing zone 2 of 5: 20% done Processing zone 3 of 5: 40% done Processing zone 4 of 5: 60% done Processing zone 5 of 5: 80% done Success: Written 154599 lines to 'mylog.txt'
When this runs, the contents of the queue_log
table are split into "zones" that have a maximum of
rows as defined in the batchsize
parameter. The larger the batchsize
, the more memory Uniloader uses,
but of course the operation is quicker. As a rule of thumb, you can expect each 1000 rows to take ~700k
of memory.
Exporting and reimporting
A common case for exporting data is to re-import it somewhere else or to a different partition within the same database.
This can be easily achieved:
uniloader qmdb --dburi "localhost/queuemetrics" --login qm --pwd 234 \ export --partition P001 --filename mylog.txt uniloader -s mylog.txt upload --uri "mysql:otherserver/queuemetrics" \ --login qm --pass 123 --token P002
Deduplicating data
If you happen to have duplicate data on a partition (eg. because the loader was run multiple times in parallel), you can easily detect it and, if needed, clean it up.
If the process is called with the --write
flags, a deduplication will be performed; if not, as
default, it will only inspect the partition.
Make a backup of the database before you attempt a clean-up. Data is deleted for good and is not recoverable. |
To perform its task, dedupe performs a series of steps:
-
The contents of the
queue_log
table is split into a number of "zones", where each zone has a maximum ofbatchsize
rows (default 500k). -
Each zone is separately checked for duplicates
-
Only zones that actually have duplicate data are de-duplicated
You can run it like:
$ uniloader qmdb --dburi "localhost/qm" --login qm --pwd 1234 dedupe --write Deduplicating P001 (0-999999999999) with batches of 30000 rows - write: true Finding zones: ................................................................... Source zones: 51 -> Packed zones: 12 (Efficiency: 75%) Scanned zone 1 of 12 - 8% done - Dupes found: 0 Scanned zone 2 of 12 - 16% done - Dupes found: 0 Scanned zone 3 of 12 - 25% done - Dupes found: 19338 Scanned zone 4 of 12 - 33% done - Dupes found: 28094 Scanned zone 5 of 12 - 41% done - Dupes found: 20226 Scanned zone 6 of 12 - 50% done - Dupes found: 20606 Scanned zone 7 of 12 - 58% done - Dupes found: 10438 Scanned zone 8 of 12 - 66% done - Dupes found: 20126 Scanned zone 9 of 12 - 75% done - Dupes found: 21046 Scanned zone 10 of 12 - 83% done - Dupes found: 25302 Scanned zone 11 of 12 - 91% done - Dupes found: 14790 Scanned zone 12 of 12 - 100% done - Dupes found: 27438 -- Total duplicates found: 207404 Cleaned zone 1 of 10 - 10% - Removed: 19338 Cleaned zone 2 of 10 - 20% - Removed: 28094 Cleaned zone 3 of 10 - 30% - Removed: 20226 Cleaned zone 4 of 10 - 40% - Removed: 20606 Cleaned zone 5 of 10 - 50% - Removed: 10438 Cleaned zone 6 of 10 - 60% - Removed: 20126 Cleaned zone 7 of 10 - 70% - Removed: 21046 Cleaned zone 8 of 10 - 80% - Removed: 25302 Cleaned zone 9 of 10 - 90% - Removed: 14790 Cleaned zone 10 of 10 - 100% - Removed: 27438 -- Total duplicates removed: 207404
As duplicate rows may be loaded in memory, make sure that Uniloader has enough RAM to run in (it’s ~700K every 1000 records). It is possible to set e.g. --batchsize 30000
to specify a maximum size for each zone.
As the actual deduplication is performed by the database, it will create large temporary files during this process. Make sure that thee database server has plenty of disk space available. Using a smaller batch size helps if you see the process failing.
The process can be run safely on a live system that is uploading data, though it will severely affect database performance; so we suggest running it off-hours. We also suggest running a database optimization afterwards to compact the database if a lot of deletions were performed.
After deleting data, you need to clean the caches of QueueMetrics or restart it. |
Creating private clones of reports
Sometimes, you want users to have private copies of "scratchpad" reports. This can be easily done from the main interface of QM, but to explain to all users how to make a private copy of a report can be burdensome.
We suggest instead that:
-
You create "template" reports, and make them public but protect them with an editing key that users do not have;
-
Using this command, you create a private copy of the report for each user, and set its editing key to ''.
This way, each user will have its own scratch copy of said reports, and shared changes won’t trigger optilock exceptions (or just unwanted modifications).
To do this, we imagine that:
-
the report you want to share exists and is named "A". Its name must be unique in the system.
-
you want each user to have a private copy called "A (pvt)", so we need to add a suffix "(Pvt)"
-
the users you want to give it to have their logins named named
alice
andbob
in QM
uniloader qmdb --login queueumetrics --pwd javadude \ clonereport --sourceTitle 'A' --destSuffix '(pvt)' --toUsers alice,bob --editingKey ''
This command will make sure that everything is in order; users and reports exist, and users do not have a report with the same name as the one that will be cloned.
To actually make changes, just add the '--forReal' parameter.
uniloader qmdb --login queueumetrics --pwd javadude \ clonereport --sourceTitle 'A' --destSuffix '(pvt)' --toUsers alice,bob --editingKey '' --forReal
To leave keys unchanged, just set them to "=". That is the default. Any other value will be used to overwrite the key itself, so '' means "no key". |
A sucessful output may look like:
2023/10/13 16:16:30 Will attempt cloning report 'A' for users [alice bob] 2023/10/13 16:16:30 Report #1 'A' for user demoadmin (Administrator) has 14 screens and 101 items (VK '' EK 'XXX') 2023/10/13 16:16:30 Starting to copy reports 2023/10/13 16:16:30 Cloning for alice... 2023/10/13 16:16:30 Done 2023/10/13 16:16:30 Report #4734 'A (pvt)' for user alice (Alice Cooper) has 14 screens and 101 items (VK '' EK '') 2023/10/13 16:16:30 Cloning for bob... 2023/10/13 16:16:30 Done 2023/10/13 16:16:30 Report #4850 'A (pvt)' for user bob (Bob Moog) has 14 screens and 101 items (VK '' EK '') 2023/10/13 16:16:30 Reports cloned
The values VK
are the visibility key for that report and EK
the editing key, respectively.
Before you run such transactions, you may want to make a backup of the QM database. |
Diagnostics: Regexp tester
Writing regular expressions to be used in Uniloader is sometimes unwieldy; the syntax is arcane and it is not always clear which expressions will match.
Uniloader lets you test a Golang regular expression from the command line. It will make sure that the syntax is correct and will check it against a number of cases you supply, printing out their average execution time so you can optimize it.
$ uniloader test regexp -? NAME: uniloader test regexp - Tests a Go Regular Expression (RegExp) USAGE: uniloader test regexp [command options] [arguments...] DESCRIPTION: This command test a Regular Expresion. It makes sure that the RegExp is correct, shows how it behaves against a number of inputs you provide and tries measuring its performance. A complete reference guide on Go RegExps is available at https://pkg.go.dev/regexp/syntax OPTIONS: --regexp value, -r value The regular expression you want to test (default: "^.*")
In order to use it, you can call it like:
$ uniloader test regexp -r '^(?i)sip/|zap/' SIP/1234 PJSIP/4576 SIP/trunk-1234
And it will print out a table where you can see which items are a positive match (the ones with someting under the column "Matches?") versus the ones that are skipped.
Testing Regexp '(?i)^sip/|^zap/' - compilation took 24.542µs | # | TARGET | MATCHES? | TIME | +---+----------------+----------+--------+ | 1 | SIP/1234 | [SIP/] | 15.8µs | | 2 | PJSIP/4576 | - | 10.1µs | | 3 | SIP/trunk-1234 | [SIP/] | 11.8µs | Average regexp execution time (across all cases): 12.6µs
The elapsed time is not meant as an exact duration (though it is the average of a number
of repeats) but as a guide to optimize your regexp - for example you can see significant changes
by adding or removing the ^
that is used to anchor the regexp to the beginning of a line.
Some examples you can start from:
-
(?i)sip/|zap/
: matches anything that starts withsip/
orzap/
in a case-insensitive way -
^(?i)pjsip/[34]..($|-)
: matches any string likepjsip/3XX
orpjsip/4XX
, optionally followed by a dash or just ending there. So it won’t matchpjsip/523
orpjsip/3120
, but will matchpjsip/321-1234
A complete reference guide on Go RegExps is available at https://pkg.go.dev/regexp/syntax
To improve matching efficiency and reduce CPU usage, whenever posibile you should "anchor" the regexp to the beginning of the line by using the caret symbol. Regular expressions are very powerful, but, as you know, with great power comes great responsibility and higher CPU load. |