blob: cfdc67a1f3b8569e18e824e47ba2fd20bbc3bf92 [file] [log] [blame]
# TODO(hmtuan): add type annotation.
import requests
import time
class ImsApiConnector():
"""A wrapper class for Keysight Ims API Connector.
Keysight provided an API connector application
which is a HTTP server running on the same host
as Keysight IMS server simulator and client simulator.
It allows IMS simulator/app to be controlled via HTTP request.
Attributes:
api_connector_ip: ip of http server.
api_connector_port: port of http server.
ims_app: type of ims app (client/server).
api_token: an arbitrary and unique token-string
to identify the link between API connector
and ims app.
log: logger object.
"""
def __init__(self, api_connector_ip,
api_connector_port, ims_app,
api_token, ims_app_ip,
ims_app_port, log):
# api connector info
self.api_connector_ip = api_connector_ip
self.api_connector_port = api_connector_port
# ims app info
self.ims_app = ims_app
self.api_token = api_token
self.ims_app_ip = ims_app_ip
self.ims_app_port = ims_app_port
self.log = log
# construct base url
self.base_url = 'http://{addr}:{port}/ims/api/{app}s/{api_token}'.format(
addr = self.api_connector_ip,
port = self.api_connector_port,
app = self.ims_app,
api_token = self.api_token
)
def get_base_url(self):
return self.base_url
def create_ims_app_link(self):
"""Create link between Keysight API Connector to ims app."""
self.log.info('Create ims app link: token:ip:port')
self.log.info('Creating ims_{app} link: {token}:{target_ip}:{target_port}'.format(
app = self.ims_app,
token = self.api_token,
target_ip = self.ims_app_ip,
target_port= self.ims_app_port)
)
request_data = {
"targetIpAddress": self.ims_app_ip,
"targetWcfPort": self.ims_app_port
}
self.log.debug(f'Payload to create ims app link: {request_data}')
r = requests.post(url = self.get_base_url(), json = request_data)
self.log.info('HTTP request sent:')
self.log.info('-> method: ' + str(r.request.method))
self.log.info('-> url: ' + str(r.url))
self.log.info('-> status_code: ' + str(r.status_code))
return (r.status_code == 201)
def remove_ims_app_link(self):
"""Remove link between Keysight API Connector to ims app."""
self.log.info('Remove ims_{app} link: {token}'.format(
app = self.ims_app,
token = self.api_token)
)
r = requests.delete(url = self.get_base_url())
self.log.info('-> method: ' + str(r.request.method))
self.log.info('-> url: ' + str(r.url))
self.log.info('-> status_code: ' + str(r.status_code))
return (r.status_code == 200)
def get_ims_app_property(self, property_name):
"""Get property value of IMS app.
Attributes:
property_name: name of property to get value.
"""
self.log.info('Getting ims app property: ' + property_name)
request_url = self.get_base_url() + '/get_property'
request_params = {"propertyName": property_name}
r = requests.get(url = request_url, params = request_params)
self.log.info('-> method: ' + str(r.request.method))
self.log.info('-> url: ' + str(r.url))
self.log.info('-> status_code: ' + str(r.status_code))
try:
res_json = r.json()
except:
res_json = {'propertyValue': None }
prop_value = res_json['propertyValue']
return prop_value
def set_ims_app_property(self, property_name, property_value):
"""Set property value of IMS app.
Attributes:
property_name: name of property to set value.
property_value: value to be set.
"""
self.log.info('Setting ims property: ' + property_name + ' = ' + str(property_value))
request_url = self.get_base_url() + '/set_property'
data = {
'propertyName': property_name,
'propertyValue': property_value
}
r = requests.post(url = request_url, json = data)
self.log.info('-> method: ' + str(r.request.method))
self.log.info('-> url: ' + str(r.url))
self.log.info('-> status_code: ' + str(r.status_code))
return (r.status_code == 200)
def ims_api_call_method(self, method_name, method_args=None):
"""
Attributes:
method_name: a name of method from Keysight API in string.
method_args: a python-array contains
arguments for the called API method.
Returns:
a tuple of (STATUS_BOOL, FUNC_RET_VAL),
if STATUS_BOOL is false, FUNC_RET_VAL is questionable/undefined,
if STATUS_BOOL is true, FUNC_RET_VAL will be the API function return value
or FUNC_RET_VAL is None if the called method return nothing.
"""
self.log.info('Calling ims method: ' + method_name)
if (method_args == None):
method_args = []
elif (type(method_args) != list):
method_args = [method_args]
data = {
'methodName': method_name,
'arguments': method_args
}
request_url = self.get_base_url() + '/call_method'
r = requests.post(url = request_url, json = data)
ret_val = None
if ( ('Content-Type' in r.headers.keys()) and r.headers['Content-Type'] == 'application/json'):
# TODO(hmtuan): try json.loads() instead
response_body = r.json()
if ((response_body != None) and ('returnValue' in response_body.keys())) :
ret_val = response_body['returnValue']
self.log.info('-> method: ' + str(r.request.method))
self.log.info('-> url: ' + str(r.url))
self.log.info('-> status_code: ' + str(r.status_code))
self.log.info('-> ret_val: ' + str(ret_val))
return (r.status_code == 200), ret_val
def _is_line_idle(self, call_line_number):
is_line_idle_prop = self.get_ims_app_property(
f'IVoip.CallLineParams({call_line_number}).SessionState')
return is_line_idle_prop == 'Idle'
def _is_ims_client_app_registered(self):
is_registered_prop = self.get_ims_app_property('IComponentControl.IsRegistered')
return is_registered_prop == 'True'
def initiate_call(self, callee_number, call_line_idx=0):
"""Dial to callee_number.
Attributes:
callee_number: number to be dialed to.
"""
# create IMS-Client API link
ret_val = self.create_ims_app_link()
if not ret_val:
raise RuntimeError('Fail to create link to IMS app.')
# check if IMS-Client is registered, and if not, request client to perform Registration
self.log.info('Ensuring client registered.')
is_registered = self._is_ims_client_app_registered()
if not is_registered:
self.log.info('Client not currently registered - registering.')
self.ims_api_call_method('ISipConnection.Register()')
is_registered = self._is_ims_client_app_registered()
if not is_registered:
raise RuntimeError('Failed to register IMS-client to IMS-server.')
# switch to call-line #1 (idx = 0)
self.log.info('Switching to call-line #1.')
self.set_ims_app_property('IVoip.SelectedCallLine', call_line_idx)
# check whether the call-line #1 is ready for dialling
is_line1_idle = self._is_line_idle(call_line_idx)
if not is_line1_idle:
raise RuntimeError('Call-line not is not in indle state.')
# entering callee number for call-line #1
self.log.info(f'Enter callee number: {callee_number}.')
self.set_ims_app_property('IVoip.CallLineParams(0).CallLocation', callee_number)
# dial entered callee number
self.log.info('Dialling call.')
self.ims_api_call_method('IVoip.Dial()')
time.sleep(5)
# check if dial success (not idle)
is_line1_idle = self._is_line_idle(call_line_idx)
if is_line1_idle:
raise RuntimeError('Fail to dial.')