Further rejiggering of classes

This commit is contained in:
Maff 2019-05-21 20:02:25 +01:00
parent 740bb07a64
commit 0bb7fb94cd
1 changed files with 275 additions and 276 deletions

View File

@ -1,276 +1,275 @@
# Environment var definitions # Environment var definitions
ENV_URI='TCX_SITE_URI' ENV_URI='TCX_SITE_URI'
ENV_AUTH_USER='TCX_API_AUTH_USERNAME' ENV_AUTH_USER='TCX_API_AUTH_USERNAME'
ENV_AUTH_PASS='TCX_API_AUTH_PASSWORD' ENV_AUTH_PASS='TCX_API_AUTH_PASSWORD'
class APIError(Exception): class APIError(Exception):
pass pass
class ValidationError(Exception): class ValidationError(Exception):
pass pass
class Request: class Request:
from requests import ConnectionError, exceptions from requests import ConnectionError, exceptions
def __init__(self, uri, verify): def __init__(self, uri, verify):
from requests import Session from requests import Session
self.uri=uri self.uri=uri
self.base="%s/api/{}" % uri self.base="%s/api/{}" % uri
self.last=None self.last=None
self.sess=Session() self.sess=Session()
self.sess.verify=verify self.sess.verify=verify
def get(self, api, params=None, expect=200): def get(self, api, params=None, expect=200):
try: try:
self.last=resp=self.sess.get(url=self.base.format(api), params=params) self.last=resp=self.sess.get(url=self.base.format(api), params=params)
assert (resp.status_code==expect) assert (resp.status_code==expect)
except AssertionError: except AssertionError:
raise APIError("Assertion error when handling API response; response code was %s, but expected response was %s" % (resp.status_code, expect)) raise APIError("Assertion error when handling API response; response code was %s, but expected response was %s" % (resp.status_code, expect))
except self.exceptions.SSLError as e: except self.exceptions.SSLError as e:
raise APIError("TLS error returned when communicating; use tls_verify=False or check leaf certs: %s" % str(e)) raise APIError("TLS error returned when communicating; use tls_verify=False or check leaf certs: %s" % str(e))
except self.exceptions.BaseHTTPError as e: except self.exceptions.BaseHTTPError as e:
raise APIError("HTTP error raised when communicating: %s" % str(e)) raise APIError("HTTP error raised when communicating: %s" % str(e))
except ConnectionError as e: except ConnectionError as e:
raise APIError("ConnectionError raised when communicating: %s" % str(e)) raise APIError("ConnectionError raised when communicating: %s" % str(e))
return resp return resp
def post(self, api, params, expect=200): def post(self, api, params, expect=200):
try: try:
self.last=resp=self.sess.post(url=self.base.format(api), json=params) self.last=resp=self.sess.post(url=self.base.format(api), json=params)
assert (resp.status_code==expect) assert (resp.status_code==expect)
except AssertionError: except AssertionError:
raise APIError("Assertion error when handling API response; response code was %s, but expected response was %s" % (resp.status_code, expect)) raise APIError("Assertion error when handling API response; response code was %s, but expected response was %s" % (resp.status_code, expect))
except self.exceptions.SSLError as e: except self.exceptions.SSLError as e:
raise APIError("TLS error returned when communicating; use tls_verify=False or check leaf certs: %s" % str(e)) raise APIError("TLS error returned when communicating; use tls_verify=False or check leaf certs: %s" % str(e))
except self.exceptions.BaseHTTPError as e: except self.exceptions.BaseHTTPError as e:
raise APIError("HTTP error raised when communicating: %s" % str(e)) raise APIError("HTTP error raised when communicating: %s" % str(e))
except ConnectionError as e: except ConnectionError as e:
raise APIError("ConnectionError raised when communicating: %s" % str(e)) raise APIError("ConnectionError raised when communicating: %s" % str(e))
except Exception as e: except Exception as e:
raise APIError("Other exception raised during API call: %s" % str(e)) raise APIError("Other exception raised during API call: %s" % str(e))
return resp return resp
class BasicObject(object): class BasicObject(object):
pass pass
class ReadOnlyObject(object): class APIObject(object):
def __init__(self, parent, api): def __init__(self, parent, api):
self.tcx=parent self.tcx=parent
self.api=api self.api=api
def refresh(self, params={}): class ReadOnlyObject(APIObject):
self._result=self.tcx.rq.get(self.api, params=params) def refresh(self, params={}):
self.active=self._result.json() self._result=self.tcx.rq.get(self.api, params=params)
class TransactionalObject(object): self.active=self._result.json()
def __init__(self, parent, api, save='edit/save', discard='edit/cancel'): class TransactionalObject(APIObject):
self.tcx=parent def __init__(self, parent, api, save='edit/save', discard='edit/cancel'):
self.api=api super().__init__(parent, api)
self.save=save self.save=save
self.discard=discard self.discard=discard
def create(self, params): def create(self, params):
self._result=self.tcx.rq.post(self.api, params=params) self._result=self.tcx.rq.post(self.api, params=params)
self._session=self._result.json()['Id'] self._session=self._result.json()['Id']
self.active=self._result.json()['ActiveObject'] self.active=self._result.json()['ActiveObject']
def submit(self, params): def submit(self, params):
raise ValidationError("NotImplemented error") raise ValidationError("NotImplemented error")
def cancel(self): def cancel(self):
self.tcx.rq.post(self.discard, params={ self.tcx.rq.post(self.discard, params={
'Id':self._session}) 'Id':self._session})
class Call(ReadOnlyObject): class Py3CX:
def __init__(self, tcx, callid): class __Call(ReadOnlyObject):
super().__init__(tcx, 'activeCalls') def __init__(self, tcx, callid):
self.params=callid super().__init__(tcx, 'activeCalls')
def refresh(self): self.params=callid
self.refresh() def refresh(self):
self.timestamp=self._result.headers.get('date') self.refresh()
res=list(filter(lambda cid: cid['Id'] == self.params, self._result.json()['list'])) self.timestamp=self._result.headers.get('date')
assert (len(res)>0), "No call found in currently-active call list for ID %s" % self.params res=list(filter(lambda cid: cid['Id'] == self.params, self._result.json()['list']))
assert(len(res)==1), "More than one active call found for ID %s" % self.params assert (len(res)>0), "No call found in currently-active call list for ID %s" % self.params
res=res[0] assert(len(res)==1), "More than one active call found for ID %s" % self.params
self.id=res['Id'] res=res[0]
self.caller=res['Caller'] self.id=res['Id']
self.callee=res['Callee'] self.caller=res['Caller']
self.state=res['Status'] self.callee=res['Callee']
self.duration=res['Duration'] self.state=res['Status']
self.since=res['LastChangeStatus'] self.duration=res['Duration']
def hangup(self): self.since=res['LastChangeStatus']
self.tcx.rq.post('activeCalls/drop', params={ def hangup(self):
'Id': self.params}) self.tcx.rq.post('activeCalls/drop', params={
self.params=None 'Id': self.params})
self.params=None
class User(TransactionalObject): class __User(TransactionalObject):
def __init__(self, parent, params): def __init__(self, parent, params):
super().__init__(parent, 'ExtensionList/set') super().__init__(parent, 'ExtensionList/set')
self.params=params self.params=params
self.load() self.load()
def load(self): def load(self):
self.create(params={ self.create(params={
'Id':self.params}) 'Id':self.params})
parms=self.active parms=self.active
self.id=parms['Id'] self.id=parms['Id']
self.enabled=not parms['Disabled']['_value'] self.enabled=not parms['Disabled']['_value']
self.sip_id=parms['SIPId']['_value'] self.sip_id=parms['SIPId']['_value']
#RecordCallsOption - enum #RecordCallsOption - enum
self.callrecs=parms['RecordCalls']['selected'] self.callrecs=parms['RecordCalls']['selected']
self.sip_host=parms['MyPhoneLocalInterface']['selected'] self.sip_host=parms['MyPhoneLocalInterface']['selected']
self.sip_host_addrs=parms['MyPhoneLocalInterface']['possibleValues'] self.sip_host_addrs=parms['MyPhoneLocalInterface']['possibleValues']
self.sip_authid=parms['AuthId']['_value'] self.sip_authid=parms['AuthId']['_value']
self.sip_authpw=parms['AuthPassword']['_value'] self.sip_authpw=parms['AuthPassword']['_value']
self.provision_uri=parms['MyPhoneProvLink']['_value'] self.provision_uri=parms['MyPhoneProvLink']['_value']
self.webpw=parms['AccessPassword']['_value'] self.webpw=parms['AccessPassword']['_value']
self.extension=Extension(self.tcx, self.params) self.extension=Py3CX.__Extension(self.tcx, self.params)
self.cancel() self.cancel()
class Extension(ReadOnlyObject): class __Extension(ReadOnlyObject):
def __init__(self, parent, params, populate=True): def __init__(self, parent, params, populate=True):
super().__init__(parent, api='ExtensionList') super().__init__(parent, api='ExtensionList')
self.params=params self.params=params
if populate: if populate:
self.load() self.load()
def load(self): def load(self):
self.refresh(params=self.params) self.refresh(params=self.params)
res=self._result res=self._result
self.timestamp=res.headers.get('date') self.timestamp=res.headers.get('date')
res=list(filter(lambda ext: ext['Number'] == self.params, res.json()['list'])) res=list(filter(lambda ext: ext['Number'] == self.params, res.json()['list']))
assert (len(res)>0), "No extension found for: %s" % self.params assert (len(res)>0), "No extension found for: %s" % self.params
assert (len(res)==1), "More than one extension found for %s" % self.params assert (len(res)==1), "More than one extension found for %s" % self.params
res=res[0] res=res[0]
self.number=res['Number'] self.number=res['Number']
self.firstname=res['FirstName'] self.firstname=res['FirstName']
self.surname=res['LastName'] self.surname=res['LastName']
self.name="%s %s" % (self.firstname, self.surname) self.name="%s %s" % (self.firstname, self.surname)
self.dnd=res['DND'] self.dnd=res['DND']
self.status=res['CurrentProfile'] self.status=res['CurrentProfile']
self.mail=res['Email'] self.mail=res['Email']
self.cli=res['OutboundCallerId'] self.cli=res['OutboundCallerId']
self.mobile=res['MobileNumber'] self.mobile=res['MobileNumber']
self.online=res['IsRegistered'] self.online=res['IsRegistered']
class PhoneSystem: class __PhoneSystem:
class System(object): class System(object):
class License(object): class License(object):
pass pass
class Status(object): class Status(object):
pass pass
def __init__(self, tcx): def __init__(self, tcx):
self.tcx=tcx self.tcx=tcx
self.extlist=None self.extlist=None
def refresh_sysstat(self): def refresh_sysstat(self):
sysstat=self.tcx.rq.get('SystemStatus').json() sysstat=self.tcx.rq.get('SystemStatus').json()
self.Status.exts_online=sysstat['ExtensionsRegistered'] self.Status.exts_online=sysstat['ExtensionsRegistered']
self.Status.trunks_online=sysstat['TrunksRegistered'] self.Status.trunks_online=sysstat['TrunksRegistered']
self.Status.calls_active=sysstat['CallsActive'] self.Status.calls_active=sysstat['CallsActive']
self.Status.diskbytes_free=sysstat['FreeDiskSpace'] self.Status.diskbytes_free=sysstat['FreeDiskSpace']
self.Status.membytes_free=sysstat['FreePhysicalMemory'] self.Status.membytes_free=sysstat['FreePhysicalMemory']
self.Status.banned_ips=sysstat['BlacklistedIpCount'] self.Status.banned_ips=sysstat['BlacklistedIpCount']
self.Status.last_backup=sysstat['LastBackupDateTime'] self.Status.last_backup=sysstat['LastBackupDateTime']
self.Status.sysexts_online=not sysstat['HasUnregisteredSystemExtensions'] self.Status.sysexts_online=not sysstat['HasUnregisteredSystemExtensions']
self.Status.services_online=not sysstat['HasNotRunningServices'] self.Status.services_online=not sysstat['HasNotRunningServices']
self.System.routes_out=sysstat['OutboundRules'] self.System.routes_out=sysstat['OutboundRules']
self.System.fqdn=sysstat['FQDN'] self.System.fqdn=sysstat['FQDN']
self.System.webconf=sysstat['WebMeetingFQDN'] self.System.webconf=sysstat['WebMeetingFQDN']
self.System.version=sysstat['Version'] self.System.version=sysstat['Version']
self.System.diskbytes_total=sysstat['TotalDiskSpace'] self.System.diskbytes_total=sysstat['TotalDiskSpace']
self.System.membytes_total=sysstat['TotalPhysicalMemory'] self.System.membytes_total=sysstat['TotalPhysicalMemory']
self.System.exts_total=sysstat['ExtensionsTotal'] self.System.exts_total=sysstat['ExtensionsTotal']
self.System.trunks_total=sysstat['TrunksTotal'] self.System.trunks_total=sysstat['TrunksTotal']
self.System.firebase_confed=sysstat['OwnPush'] self.System.firebase_confed=sysstat['OwnPush']
self.System.backups_enabled=sysstat['BackupScheduled'] self.System.backups_enabled=sysstat['BackupScheduled']
self.System.License.activated=sysstat['Activated'] self.System.License.activated=sysstat['Activated']
self.System.License.calls=sysstat['MaxSimCalls'] self.System.License.calls=sysstat['MaxSimCalls']
self.System.License.meetingcalls=sysstat['MaxSimMeetingParticipants'] self.System.License.meetingcalls=sysstat['MaxSimMeetingParticipants']
self.System.License.supported=sysstat['Support'] self.System.License.supported=sysstat['Support']
self.System.License.expire=sysstat['ExpirationDate'] self.System.License.expire=sysstat['ExpirationDate']
self.System.License.maintexpire=sysstat['MaintenanceExpiresAt'] self.System.License.maintexpire=sysstat['MaintenanceExpiresAt']
self.System.License.reseller=sysstat['ResellerName'] self.System.License.reseller=sysstat['ResellerName']
self.System.License.key=sysstat['LicenseKey'] self.System.License.key=sysstat['LicenseKey']
self.System.License.sku=sysstat['ProductCode'] self.System.License.sku=sysstat['ProductCode']
self.System.License.spla=sysstat['IsSpla'] self.System.License.spla=sysstat['IsSpla']
def refresh_addl(self): def refresh_addl(self):
addl=self.tcx.rq.get('SystemStatus/AdditionalStatus').json() addl=self.tcx.rq.get('SystemStatus/AdditionalStatus').json()
self.Status.callrecs_bytes=addl['RecordingUsedSpace'] self.Status.callrecs_bytes=addl['RecordingUsedSpace']
self.System.callrecs_enabled=not addl['RecordingStopped'] self.System.callrecs_enabled=not addl['RecordingStopped']
self.System.callrecs_quota=addl['RecordingQuota'] self.System.callrecs_quota=addl['RecordingQuota']
def refresh_inroute(self): def refresh_inroute(self):
inroute=self.tcx.rq.get('InboundRulesList').json()['list'] inroute=self.tcx.rq.get('InboundRulesList').json()['list']
self.System.routes_in=len(inroute) self.System.routes_in=len(inroute)
def refresh_hists(self): def refresh_hists(self):
hists=self.tcx.rq.post('SystemStatus/getDbInformation', params={}).json() hists=self.tcx.rq.post('SystemStatus/getDbInformation', params={}).json()
self.System.calls_total=hists['CallHistoryCount'] self.System.calls_total=hists['CallHistoryCount']
self.System.chats_total=hists['ChatMessagesCount'] self.System.chats_total=hists['ChatMessagesCount']
def refresh_certified(self): def refresh_certified(self):
certstat=self.tcx.rq.get('SystemStatus/GetSingleStatus').json() certstat=self.tcx.rq.get('SystemStatus/GetSingleStatus').json()
self.System.License.firewallsupported=certstat['Health']['Firewall'] self.System.License.firewallsupported=certstat['Health']['Firewall']
self.System.License.trunkssupported=certstat['Health']['Trunks'] self.System.License.trunkssupported=certstat['Health']['Trunks']
self.System.License.phonessupported=certstat['Health']['Phones'] self.System.License.phonessupported=certstat['Health']['Phones']
def refresh(self): def refresh(self):
self.refresh_addl() self.refresh_addl()
self.refresh_certified() self.refresh_certified()
self.refresh_hists() self.refresh_hists()
self.refresh_inroute() self.refresh_inroute()
self.refresh_sysstat() self.refresh_sysstat()
@property @property
def extensionlist(self): def extensionlist(self):
if self.extlist is None: if self.extlist is None:
self.extlist=ReadOnlyObject(self.tcx, api='ExtensionList') self.extlist=ReadOnlyObject(self.tcx, api='ExtensionList')
self.extlist.refresh() self.extlist.refresh()
res=self.extlist._result.json()['list'] res=self.extlist._result.json()['list']
assert (len(res)>0), "No extensions found" assert (len(res)>0), "No extensions found"
ret=[] ret=[]
for extension in res: for extension in res:
this=Extension(self.tcx, params=extension['Number'], populate=False) this=Py3CX.__Extension(self.tcx, params=extension['Number'], populate=False)
this.firstname=extension['FirstName'] this.firstname=extension['FirstName']
this.surname=extension['LastName'] this.surname=extension['LastName']
this.name="%s %s" % (this.firstname, this.surname) this.name="%s %s" % (this.firstname, this.surname)
this.dnd=extension['DND'] this.dnd=extension['DND']
this.status=extension['CurrentProfile'] this.status=extension['CurrentProfile']
this.mail=extension['Email'] this.mail=extension['Email']
this.cli=extension['OutboundCallerId'] this.cli=extension['OutboundCallerId']
this.mobile=extension['MobileNumber'] this.mobile=extension['MobileNumber']
this.online=extension['IsRegistered'] this.online=extension['IsRegistered']
ret.append(this) ret.append(this)
return ret return ret
@property @property
def callslist(self): def callslist(self):
if self.calllist is None: if self.calllist is None:
self.calllist=ReadOnlyObject(self.tcx, api='activeCalls') self.calllist=ReadOnlyObject(self.tcx, api='activeCalls')
self.calllist.refresh() self.calllist.refresh()
res=self.calllist._result.json()['list'] res=self.calllist._result.json()['list']
if len(res)==0: return [] if len(res)==0: return []
ret=[] ret=[]
for call in res: for call in res:
this=Call(self.tcx, call['Id']) this=Py3CX.__Call(self.tcx, call['Id'])
this.caller=call['Caller'] this.caller=call['Caller']
this.callee=call['Callee'] this.callee=call['Callee']
this.state=call['Status'] this.state=call['Status']
this.duration=res['Duration'] this.duration=res['Duration']
this.since=res['LastChangeStatus'] this.since=res['LastChangeStatus']
ret.append(this) ret.append(this)
return ret return ret
def __init__(self, uri=None, tls_verify=True):
class Py3CX: from os import getenv
def __init__(self, uri=None, tls_verify=True): self.uri=uri if uri is not None and uri.startswith('http') else getenv(ENV_URI, None)
from os import getenv assert (self.uri is not None and self.uri.startswith('http')), "Please provide URI"
self.uri=uri if uri is not None and uri.startswith('http') else getenv(ENV_URI, None) self.uname=getenv(ENV_AUTH_USER, None)
assert (self.uri is not None and self.uri.startswith('http')), "Please provide URI" self.passw=getenv(ENV_AUTH_PASS, None)
self.uname=getenv(ENV_AUTH_USER, None) self.rq=Request(uri=self.uri, verify=tls_verify)
self.passw=getenv(ENV_AUTH_PASS, None) self.__tcxsystem=None
self.rq=Request(uri=self.uri, verify=tls_verify) def authenticate(self, username=None, password=None):
def authenticate(self, username=None, password=None): if username is not None and password is not None:
if username is not None and password is not None: self.uname=username
self.uname=username self.passw=password
self.passw=password assert (self.uname is not None and self.passw is not None), "Authentication information needed"
assert (self.uname is not None and self.passw is not None), "Authentication information needed" rs=self.rq.post('login', params={
rs=self.rq.post('login', params={ 'Username': self.uname,
'Username': self.uname, 'Password': self.passw})
'Password': self.passw}) self.rq.sess.headers.update({'x-xsrf-token':rs.cookies['XSRF-TOKEN']})
self.rq.sess.headers.update({'x-xsrf-token':rs.cookies['XSRF-TOKEN']}) @property
@property def authenticated(self):
def authenticated(self): try:
try: self.rq.get('CurrentUser')
self.rq.get('CurrentUser') except APIError:
except APIError: return False
return False return True
return True @property
@property def System(self):
def System(self): assert self.authenticated, "Py3CX not authenticated yet!"
assert self.authenticated, "Py3CX not authenticated yet!" if self.__tcxsystem is None:
if self.tcxsystem is None: self.__tcxsystem=__PhoneSystem(self)
self.tcxsystem=PhoneSystem(self) return self.__tcxsystem
return self.tcxsystem