pbxd is a web service that provides an API to an Avaya PBX.
It logs in to the SAT terminal of the PBX, then runs commands on the PBX and returns the results to the client.
The number of gunicorn workers determines how many simultaneous logins are made to the PBX system.
Environment variables:
PBX_COMMAND_TIMEOUT=300
PBXD_CONF=pbxd_conf/pbxd_uw01_conf.json
Secrets are loaded from a JSON config file like this:
{
"connection_command": "/usr/bin/ssh -o 'StrictHostKeyChecking no' -o 'ServerAliveInterval -p 5022 -l username
ip_address_of_pbx",
"pbx_username": "username",
"pbx_password": "password"
}
Restricting access to authorized users must be done by a proxy server like Nginx. Typically this will require X.509 certificates or host IP addresses.
There are some python packages required for pbxd in the setup.py file and it is good to keep them updated. pip-tools is used here to update the requirements.txt file and help ensure predictable builds.
deactivate; rm -rf venv
python3 -m venv venv && . venv/bin/activate
pip install pip-tools
pip-compile
python3 -m venv venv && . venv/bin/activate
pip install -r requirements.txt
pip install -e .
APPLICATION_ROOT=/uw01 \
PBX_COMMAND_TIMEOUT=300 \
PBXD_CONF=pbxd_conf/pbxd_uw01_conf.json \
gunicorn "pbxd.app:load()" -b localhost:8000 --access-logfile - --log-level INFO --reload
docker-compose build
docker-compose up --abort-on-container-exit
Clients use pbxd to run a command on the PBX and receive the results.
The PBX need to know what terminal type to use:
ossi
is the terminal type that returns fields with unique IDs. You can find the IDs by doing a display command and use them in a change command.vt220
is the terminal type that returns screen snapshots. You only use this for display commands.
The command
can be any command that the PBX understands. Typically these start
with the word display
, change
, status
, or monitor
.
With the ossi
terminal type you can also provide a list of fields
that are just
objects with OSSI field IDs and any value you want them changed to.
To clear a field you often have to set the value to a single space character.
One way to identify field IDs is to run a "display" command and then compare it to the output from the same command in the vt220 terminal.
The v3 API uses JSON. The JSON object has the following keys:
termtype
string: ossi or vt220command
string: the command to run on the pbxfields
array: the list of field IDs and values- field values should be a string, either an empty string '' or the value that the field will be changed to.
debug
boolean: true or false to indicate if you want the raw PBX OSSI response.
Examples:
curl -X POST -H "Content-Type: application/json" \
-d '{"termtype": "ossi","command": "display time"}' \
http://localhost:8000/uw01/v3/
curl -X POST -H "Content-Type: application/json" \
-d '{"termtype": "ossi",
"command": "display station 12345",
"fields": {"8003ff00": "12345 Test", "8005ff00": ""}}' \
http://localhost:8000/uw01/v3/
curl -X POST -H "Content-Type: application/json" \
-d '{"termtype": "ossi",
"command": "list extension count 10",
"fields": {"0001ff00": "", "0002ff00": ""}}' \
http://localhost:8000/uw01/v3/
The v3 API will return a JSON object with one or more of these keys:
- ossi_objects: array with each OSSI object returned by the PBX
- screens: an array containing the vt220 screens
- error: a string with any error message from the PBX
- debug: an array with each raw line from the PBX OSSI response
The v2 API uses XML.
The <command>
element has 3 attributes:
- pbxName: the name of the pbx that should match the name in the URL
- cmdType: ossi or vt220
- cmd: the command to run on the pbx
The <command>
element may have multiple child <field>
elements. If the field has a text node then that text will be used for change commands.
<field>
elements have 1 attribute:
- fid: the OSSI field id
Examples:
curl -d 'request=<command pbxName="uw01" cmdType="ossi" cmd="display time"/>' \
http://localhost:8000/uw01/v2/
curl -d 'request=<command pbxName="uw01" cmdType="ossi" cmd="display station 12345">
<field fid="8005ff00"/>
<field fid="8003ff00">12345 Test</field>
</command>' \
http://localhost:8000/uw01/v2/
curl -d 'request=<command pbxName="uw01" cmdType="ossi" cmd="list extension count 10">
<field fid="0001ff00"/>
<field fid="0002ff00"/>
</command>' \
http://localhost:8000/uw01/v2/
The v2 API will return a XML document with the <command>
element and one or more of these child elements:
<ossi_object>
<screen>
<error>
You can import pbxd.pbx.definity in your own python code if you want to have more control or don't need the pbxd web service.
python3 -m venv venv && . venv/bin/activate
pip install -r requirements.txt
pip install -e .
Example script:
import pbxd.pbx.definity as definity
import json
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG,
format='%(asctime)s %(levelname)-8s [%(filename)s:%(lineno)d] - %(message)s'
)
with open('pbxd_conf/pbxd_uw01_conf.json') as json_file:
config = json.load(json_file)
pbx = definity.Terminal(
config['connection_command'],
config['pbx_username'],
config['pbx_password']
)
pbx.connect()
fields = {'0003ff00': ''}
result = pbx.ossi_command('display time', fields) # returns {"ossi_objects": [{"0003ff00": "2020"}]}
ossi = result['ossi_objects'][0]
print('The PBX says the year is {}'.format(ossi.get('0003ff00')))
vt220 = pbx.vt220_command('display time')
print('\n'.join(vt220['screens']))
pbx.disconnect()
The development of pbxd originated with a Perl module developed to support scripts for automating management of the Lucent G3r PBX in the early 2000's. As more scripts were written and run there was duplicate code and multiple scripts accessing the PBX simultaneously. There were also more PBX systems at remote sites. Pbxd was created to share connections to a PBX, and enable multiple clients like web browsers to get bits of information from the PBX.
Written in Perl as a CGI script it worked well enough for many years but given time everything changes. It is still a useful tool and the Avaya Communication Manager still supports the OSSI protocol so pbxd has been rewritten in Python with a JSON API.
You can see a description of the original version here.