Page tree

This example written in Python allows users to manage VoipNow accounts and fetch call statistics.

How To Install It

To be able to use the SystemAPI Python Tool, ensure that you have the following resources: 

System Requirements for Linux

System Requirements for Microsoft Windows

Setup

The code is available on GitHub

Download here. Do not hesitate to contribute in order to make this example better - we welcome Pull Requests!

Linux

STEP 1: Install GCC and Python and download fpconst. Then unzip the fpconst archive, open the extracted folder, and run the following commands:

python setup.py build
python setup.py install

To make sure fpconst has been properly installed, type the following in your Python command-line:

import fpconst

If you're not getting any error messages, it means fpconst is correctly installed.

STEP 2: Download PyXML, then unzip the archive, open the extracted folder, and run the following commands:

python setup.py build
python setup.py install

To make sure PyXML has been properly installed, type the following in your Python command-line:

import xml

If you're not getting any error messages, it means that PyXML is correctly installed.

STEP 3: Download SOAPpy, then unzip the archive, open the extracted folder, and run the following commands:

python setup.py build
python setup.py install

To make sure SOAPpy has been properly installed, type the following in your Python command-line:

import SOAPpy

If you're not getting any error messages, it means SOAPpy is correctly installed.

If during installation you're prompted the following error:

File ".../SOAPpy/Client.py", line 46
from _future_ import nested_scopes
SyntaxError: from _future_ imports must occur at the beginning of the file 

You need to open your temporary folder SOAPpy\Client.py and copy/paste the "from _future_ imports" line to be the first import. The Client.py is just a temporary folder used by the SOAPpy engine, so ignore it.

Please note that the same error will occur with other files as well during installation. Therefore, it is recommended that you repeat this step whenever necessary.

STEP 4: Download ZSI, then unzip the archive, open the extracted folder, and run the following commands:

python setup.py build
python setup.py install

To make sure ZSI has been properly installed, type the following in your Python command-line:

import ZSI

If you're not getting any error messages, it means ZSI is correctly installed.

STEP 5: At this point, you can start writing applications for your VoipNow server using the SOAPpy web services toolkit.

Microsoft Windows

STEP 1: Install Visual Studio 2008 Express and Python, and download fpconst. Then unzip the fpconst archive, open the extracted folder, and run the following commands:

python setup.py build
python setup.py install

STEP 2: Download PyXML and run the installer:

There are different PyXML installers for various Python versions, so make sure you download the appropriate one, otherwise the installer will not work. If you're not sure what to do, please visit the links in the README file.

STEP 3: Download SOAPpy, then unzip the archive, open the extracted folder, and run the following commands:

python setup.py build
python setup.py install

If during installation you're prompted the following error:

File ".../SOAPpy/Client.py", line 46
from _future_ import nested_scopes
SyntaxError: from _future_ imports must occur at the beginning of the file 

You must open your temporary folder SOAPpy\Client.py and copy/paste the "from _future_ imports" line to be the first import.

Please note that the same error will occur with other files as well during installation. Therefore, it is recommended that you repeat this step whenever necessary.

STEP 4: Download ZSI, then unzip the archive, open the extracted folder, and run the following commands:

python setup.py build
python setup.py install

STEP 5: At this point, you can start writing applications for your VoipNow server using the SOAPpy web services toolkit.

When writing applications, you may be prompted the following error:

Traceback (most recent call last):
File "pythonDemo.py", line 61, in ?
server._callWithBody(body1)
File "/usr/lib/python2.4/site-packages/SOAPpy/Client.py", line 436, in _callWithBody
return self.__call(None, body, {})
File "/usr/lib/python2.4/site-packages/SOAPpy/Client.py", line 395, in __call
p, attrs = parseSOAPRPC(r, attrs = 1)
File "/usr/lib/python2.4/site-packages/SOAPpy/Parser.py", line 1049, in parseSOAPRPC
t = _parseSOAP(xml_str, rules = rules)
File "/usr/lib/python2.4/site-packages/SOAPpy/Parser.py", line 1029, in _parseSOAP
parser.parse(inpsrc)
File "/usr/lib/python2.4/site-packages/_xmlplus/sax/expatreader.py", line 109, in parse
xmlreader.IncrementalParser.parse(self, source)
File "/usr/lib/python2.4/site-packages/_xmlplus/sax/xmlreader.py", line 123, in parse
self.feed(buffer)
File "/usr/lib/python2.4/site-packages/_xmlplus/sax/expatreader.py", line 216, in feed
self._parser.Parse(data, isFinal)
File "/usr/lib/python2.4/site-packages/_xmlplus/sax/expatreader.py", line 363, in end_element_ns
self._cont_handler.endElementNS(pair, None)
File "/usr/lib/python2.4/site-packages/SOAPpy/Parser.py", line 235, in endElementNS
kind = (self._prem[kind[:i]], kind[i + 1:])
KeyError: u'http'

Whenever this happens, download the latest patch for the Parser.py file from the SOAPpy library. The patch is available here.

How To Use It

Authentication

STEP 1: Create a headerType object.

header1 = SOAPpy.Types.headerType()

STEP 2: Fill in the user credentials. VoipNow uses the OAuth protocol, where the user must provide his access token. The old legacy authentication and OAuth with consumer key and consumer secret are deprecated.

The code should look something like this:

# Authentication data
ACCESS_TOKEN = sys.argv[2]

# Start constructing the Header
soapHeader = SOAPpy.Types.headerType()

#XML template for sending the authentication data
authenticationTemplate = """ <ns1:accessToken>%s</ns1:accessToken>"""
  				
# filling in the template above with our authentication data				
credentials = authenticationTemplate % (ACCESS_TOKEN)

soapHeader.userCredentials = SOAPpy.Types.headerType(credentials)

Examples

Add a Service Provider

In DemoAddServiceProvider.py, you have an example on how to add a new service provider. The script also fetches the list of charging plans you can choose from and use for the new service provider.

For more information on how to get the access token, please check the access management documentation.

Adding Other Account Types

If you want to add other account types (organizations, users or extensions), simply tweak the previous Service Provider example using the recommendations below.  

Most important, you will need the ID of a parent account. Depending on the account type you want to add, this ID can be:

  • The ID of the service provider that owns the organization (when adding a new organization)
  • The ID of the organization that owns the user (when adding a new user) 
  • The ID of the user that owns the extension (when adding a new extension)

Therefore, the list of appropriate accounts will be fetched and a random one will be chosen as the parent of the new account:

# We need a parent service provider for the new organization, so we make a request to
# fetch all the service providers.

# Construct the Body
soapBody = SOAPpy.Types.untypedType("")
soapBody._name = "ns2:GetServiceProviders" 

# Set namespaces
soapBody._setAttr("xmlns:ns2", "http://4psa.com/ServiceProviderMessages.xsd/" + version)

# Set endpoint
endpoint = "https://" + str(sys.argv[1]) + "/soap2/sp_agent.php"

# Set soapaction
soapaction = "http://4psa.com/ServiceProvider/" + version + ":getServiceProvidersIn"

# Set service connection
server = SOAPProxy(endpoint, noroot = 1, soapaction = soapaction, header = soapHeader)

#run our soap request
try:
	serviceProviders = server._callWithBody(soapBody)
except SOAPpy.Types.faultType, error:
	# Catch exception, for situations when the service providers could not be fetched
	print "Error " + error[0] + ": " + error[1]
	sys.exit(1)

# Get the id of a random service provider
serviceProviderID = None
if len(serviceProviders) != 0:
	randServiceProvider = serviceProviders[random.randint(0, len(serviceProviders) - 1)]
	if hasattr(randServiceProvider, 'ID'):
		serviceProviderID = randServiceProvider.ID
		if hasattr(randServiceProvider, 'name'):
			print "Using parent service provider " + randServiceProvider.name + "."
		else:
			print "Using parent service provider with id " + serviceProviderID + "."

if serviceProviderID == None:
	print "No service providers found on the server. Can not add an organization."
	sys.exit(1)

You will also need to make adjustments to the parameters of the new request so that they match the new type of account:

# Organization data
name = "OrgPython" + str(random.randint(1, 1000))
login = "OrgPython" + str(random.randint(1, 1000))
firstname = "FirstnamePython" + str(random.randint(1, 1000))
lastname = "LastnamePython" + str(random.randint(1, 1000))
email = "Email" + str(random.randint(1, 1000)) + "@example.com"
password = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10))
country = "us"
parentID = serviceProviderID
company = "test_company"

# Filling in the template above with the organization data
if chargingPlanID != None:
	body = bodyTemplate % (name, login, firstname, lastname, email, password, country, parentID, company, chargingPlanID)
else:
	body = bodyTemplate % (name, login, firstname, lastname, email, password, country, parentID, company)
soapBody = SOAPpy.Types.untypedType(body)
soapBody._name = "ns2:AddOrganization" 

# Set namespaces
soapBody._setAttr("xmlns:ns2", "http://4psa.com/OrganizationMessages.xsd/" + version)
soapBody._setAttr("xmlns:ns3", "http://4psa.com/OrganizationData.xsd/" + version)

# Set endpoint
endpoint = "https://" + str(sys.argv[1]) + "/soap2/organization_agent.php"

# Set soapaction
soapaction = "http://4psa.com/Organization/" + version + ":addOrganizationIn"

Adding a user or an extension is very similar. You only need to adjust the namespaces, endpoint, and soapaction variables. Also, for extension accounts, you should use the label field.

CallCosts

The DEMO version also provides a CallCosts request. Here is the code.

#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))
  • No labels

Except where otherwise noted, content in this space is licensed under a Creative Commons Attribution 4.0 International.