Skip to content

Commit

Permalink
Update csclient.py
Browse files Browse the repository at this point in the history
  • Loading branch information
phate999 authored Nov 1, 2024
1 parent 6f59a62 commit 7a77e87
Showing 1 changed file with 91 additions and 13 deletions.
104 changes: 91 additions & 13 deletions iperf3/csclient.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
NCOS communication module for SDK applications.
Copyright (c) 2018 Cradlepoint, Inc. <www.cradlepoint.com>. All rights reserved.
Copyright (c) 2022 Cradlepoint, Inc. <www.cradlepoint.com>. All rights reserved.
This file contains confidential information of CradlePoint, Inc. and your use of
this file is subject to the CradlePoint Software License Agreement distributed with
Expand Down Expand Up @@ -62,8 +62,9 @@ def __new__(cls, *na, **kwna):

def __init__(self, app_name, init=False):
self.app_name = app_name
self.ncos = '/var/mnt/sdk' in os.getcwd() # Running in NCOS
handlers = [logging.StreamHandler()]
if sys.platform == 'linux2':
if 'linux' in sys.platform:
handlers.append(logging.handlers.SysLogHandler(address='/dev/log'))
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s: %(message)s', datefmt='%b %d %H:%M:%S',
handlers=handlers)
Expand Down Expand Up @@ -91,7 +92,7 @@ def get(self, base, query='', tree=0):
A dictionary containing the response (i.e. {"success": True, "data:": {}}
"""
if sys.platform == 'linux2':
if 'linux' in sys.platform:
cmd = "get\n{}\n{}\n{}\n".format(base, query, tree)
return self._dispatch(cmd).get('data')
else:
Expand All @@ -110,6 +111,33 @@ def get(self, base, query='', tree=0):

return json.loads(response.text).get('data')

def decrypt(self, base, query='', tree=0):
"""
Constructs and sends a decrypt/get request to retrieve specified data from a device.
The behavior of this method is contextual:
- If the app is installed on (and executed from) a device, it directly queries the router tree to retrieve the
specified data.
- If the app running remotely from a computer it calls the HTTP GET method to retrieve the specified data.
Args:
base: String representing a path to a resource on a router tree,
(i.e. '/config/system/logging/level').
value: Not required.
query: Not required.
tree: Not required.
Returns:
A dictionary containing the response (i.e. {"success": True, "data:": {}}
"""
if 'linux' in sys.platform:
cmd = "decrypt\n{}\n{}\n{}\n".format(base, query, tree)
return self._dispatch(cmd).get('data')
else:
# Running in a computer and can't actually send the alert.
print('Decrypt is only available when running the app in NCOS.')

def put(self, base, value='', query='', tree=0):
"""
Constructs and sends a put request to update or add specified data to the device router tree.
Expand All @@ -132,7 +160,7 @@ def put(self, base, value='', query='', tree=0):
A dictionary containing the response (i.e. {"success": True, "data:": {}}
"""
value = json.dumps(value)
if sys.platform == 'linux2':
if 'linux' in sys.platform:
cmd = "put\n{}\n{}\n{}\n{}\n".format(base, query, tree, value)
return self._dispatch(cmd)
else:
Expand Down Expand Up @@ -174,7 +202,7 @@ def post(self, base, value='', query=''):
A dictionary containing the response (i.e. {"success": True, "data:": {}}
"""
value = json.dumps(value)
if sys.platform == 'linux2':
if 'linux' in sys.platform:
cmd = f"post\n{base}\n{query}\n{value}\n"
return self._dispatch(cmd)
else:
Expand All @@ -195,6 +223,50 @@ def post(self, base, value='', query=''):

return json.loads(response.text)

def patch(self, value):
"""
Constructs and sends a patch request to update or add specified data to the device router tree.
The behavior of this method is contextual:
- If the app is installed on(and executed from) a device, it directly updates or adds the specified data to
the router tree.
- If the app running remotely from a computer it calls the HTTP PUT method to update or add the specified
data.
Args:
value: list containing dict of add/changes, and list of removals: [{add},[remove]]
Returns:
A dictionary containing the response (i.e. {"success": True, "data:": {}}
"""

if 'linux' in sys.platform:
if value[0].get("config"):
adds = value[0]
else:
adds = {"config": value[0]}
adds = json.dumps(adds)
removals = json.dumps(value[1])
cmd = f"patch\n{adds}\n{removals}\n"
return self._dispatch(cmd)
else:
# Running in a computer so use http to send the put to the device.
import requests
device_ip, username, password = self._get_device_access_info()
device_api = 'http://{}/api/'.format(device_ip)

try:
response = requests.patch(device_api,
headers={"Content-Type": "application/x-www-form-urlencoded"},
auth=self._get_auth(device_ip, username, password),
data={"data": '{}'.format(json.dumps(value))})
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError):
print("Timeout: device at {} did not respond.".format(device_ip))
return None

return json.loads(response.text)

def delete(self, base, query=''):
"""
Constructs and sends a delete request to delete specified data to the device router tree.
Expand All @@ -214,7 +286,7 @@ def delete(self, base, query=''):
Returns:
A dictionary containing the response (i.e. {"success": True, "data:": {}}
"""
if sys.platform == 'linux2':
if 'linux' in sys.platform:
cmd = "delete\n{}\n{}\n".format(base, query)
return self._dispatch(cmd)
else:
Expand All @@ -227,7 +299,7 @@ def delete(self, base, query=''):
response = requests.delete(device_api,
headers={"Content-Type": "application/x-www-form-urlencoded"},
auth=self._get_auth(device_ip, username, password),
data={"data": '{}'.format(value)})
data={"data": '{}'.format(base)})
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError):
print("Timeout: device at {} did not respond.".format(device_ip))
Expand All @@ -249,7 +321,7 @@ def alert(self, value=''):
Success: None
Failure: An error
"""
if sys.platform == 'linux2':
if 'linux' in sys.platform:
cmd = "alert\n{}\n{}\n".format(self.app_name, value)
return self._dispatch(cmd)
else:
Expand All @@ -267,12 +339,18 @@ def log(self, value=''):
Returns:
None
"""
if sys.platform == 'linux2':
if self.ncos:
# Running in NCOS so write to the logger
self.logger.info(value)
elif 'linux' in sys.platform:
# Running in Linux (container?) so write to stdout
with open('/dev/stdout', 'w') as log:
log.write(f'{self.app_name}: {value}\n')
else:
# Running in a computer so just use print for the log.
print(value)


def _get_auth(self, device_ip, username, password):
# This is only needed when the app is running in a computer.
# Returns the proper HTTP Auth for the global username and password.
Expand Down Expand Up @@ -306,7 +384,7 @@ def _get_device_access_info():
device_username = ''
device_password = ''

if sys.platform != 'linux2':
if 'linux' not in sys.platform:
import os
import configparser

Expand Down Expand Up @@ -356,7 +434,7 @@ def _dispatch(self, cmd):
# ignore the command error, continue on to next command
errmsg = "dispatch failed with exception={} err={}".format(type(err), str(err))
if errmsg is not None:
self.log(self.app_name, errmsg)
self.log(errmsg)
pass
return result

Expand Down Expand Up @@ -408,7 +486,7 @@ def _receive(self, sock):
# ignore the command error, continue on to next command
errmsg = "_receive failed with exception={} err={}".format(type(err), str(err))
if errmsg is not None:
self.log(self.app_name, errmsg)
self.log(errmsg)
return result


Expand Down Expand Up @@ -443,7 +521,7 @@ def start(self):
def stop(self):
if not self.running:
return
self.log(f"Stopping {self.app_name}")
self.log(f"Stopping")
for k in list(self.registry.keys()):
self.unregister(k)
self.event_sock.close()
Expand Down

0 comments on commit 7a77e87

Please sign in to comment.