Skip to content

Commit

Permalink
modify cli_sample to use control tree method
Browse files Browse the repository at this point in the history
  • Loading branch information
dapplegatecp committed Nov 10, 2023
1 parent 6dae02f commit bb8a7ac
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 34 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ The Application Developmers Guide is the best document to read first.
- **cp_shell**
- Web interface for running linux shell commands.
- **cli_sample**
- Includes cppxssh module that enables SSH access to local CLI to send commands and return output.
- Includes csterm module that enables access to local CLI to send commands and return output.
- **ipverify_custom_action**
- Create a custom action in a function to be called when an IPverify test status changes.
- **cpu_usage**
Expand Down
41 changes: 10 additions & 31 deletions cli_sample/cli_sample.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
# cli - execute CLI command and return output

from csclient import EventingCSClient


def cli(username, password, cmd):
"""
:param username: string
username for SSH login
:param password: string
user password for SSH login
:param cmd: string
CLI command to execute
Example: "sms 2081234567 'hello world' int1"
Example: "arpdump"
:return: string
Text output returned from CLI command
"""
import cppxssh
ssh_tunnel = cppxssh.cppxssh()
ssh_tunnel.login('localhost', username, password, auto_prompt_reset=False)
ssh_tunnel.PROMPT = '\[[0-9A-Za-z_]+@.+\]\$ '
ssh_tunnel.sendline(cmd)
ssh_tunnel.prompt()
output = ssh_tunnel.before.decode()
del ssh_tunnel
try:
# try to remove the command echo
return output.split(cmd + '\r\n')[1]
except IndexError:
# for some reason we failed to remove so return original
return output
import time

from csclient import EventingCSClient
from csterm import CSTerm

cp = EventingCSClient('cli_sample')
ct = CSTerm(cp)
cp.log('Starting...')
cp.log('Output:\n' + cli('admin', '11', 'arpdump'))
while True:
# use ct.exec followed by CLI command to execute
# Example: "sms 2081234567 'hello world' int1"
# Example: "arpdump"
cp.log('Output:\n' + ct.exec('arpdump'))
time.sleep(10)
83 changes: 83 additions & 0 deletions cli_sample/csterm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import random
import time
import re


class CSTerm:
INTERVAL = 0.3 # how often to poll for output, faster can sometimes miss output

def __init__(self, csclient, timeout=10, soft_timeout=5, user=None):
"""
:param csclient: csclient.EventingCSClient
csclient object to use for communication
:param timeout: int
absolute maximum number of seconds to wait for output (default 10)
:param soft_timeout: int
number of seconds to wait for output before sending interrupt (default 5)
"""
self.c = csclient
self.timeout = timeout
self.soft_timeout = soft_timeout
self.s_id = "term-%s" % random.randint(100000000, 999999999)
self.user = user

def exec(self, cmds, clean=True):
"""
:param cmds: list or string
list of commands to execute or single command to execute
:param clean: bool
if True, remove terminal escape sequences from output
:return: string
Text output returned from CLI command
"""
cmds = cmds if isinstance(cmds, list) else [cmds]
cmds = [c + '\n' for c in cmds]
timeout = self.timeout * (1 / self.INTERVAL)
soft_timeout = self.soft_timeout * (1 / self.INTERVAL)

k = iter(cmds)
kp = next(k)
r = ''
while timeout > 0:

self.c.put("/control/csterm/%s" % self.s_id, self._k(kp))
rp = self.c.get("/control/csterm/%s" % self.s_id)

r += rp['k']
# the output generally will end with a prompt and no newline, so we can check for that
# after we've sent all our commands
if kp == "" and not r.endswith('\n'):
break

kp = next(k, None) or ""
timeout-=1
time.sleep(self.INTERVAL)
if timeout < soft_timeout:
self.c.put("/control/csterm/%s" % self.s_id, self._k('\x03'))
rp = self.c.get("/control/csterm/%s" % self.s_id)
r += rp['k']
soft_timeout = 0

# remove the prompt and any terminal escape sequences
if clean:
r = re.sub(r'(?:\x1B[@-Z\\-_]|[\x80-\x9A\x9C-\x9F]|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~])','', r)
r = r.split('\n')
prompt = r[-1]
r = [l for l in r if not l.startswith(prompt)]
r = "\n".join(r)

return r

def _k(self, v):
r = {"k": v}
if self.user:
r["u"] = self.user
return r

if __name__ == '__main__':
import sys
from csclient import EventingCSClient

c = EventingCSClient('cli_sample')
ct = CSTerm(c, user="admin")
print(ct.exec(sys.argv[1:]))
1 change: 1 addition & 0 deletions cli_sample/package.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ reboot = true
auto_start = true
version_major = 1
version_minor = 0
version_patch = 1
36 changes: 34 additions & 2 deletions cli_sample/readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cli_sample

Application Version
===================
1.0
1.0.1


NCOS Devices Supported
Expand All @@ -20,7 +20,7 @@ None

Application Purpose
===================
Includes cppxssh module that enables SSH access to local CLI to send commands and return output. Example sends "arpdump".
Includes a csterm.py library that uses csclient control tree access to local CLI to send commands and return output. Example sends "arpdump".

Expected Output
===============
Expand All @@ -32,4 +32,36 @@ Expected Output
ethernet primarylan1 STALE 14:b1:c8:01:59:09 fe80::1cd1:9ffa:135:3ed4
ethernet primarylan1 STALE 14:b1:c8:01:59:09 fe80::18d5:408e:d760:2e39

Notes
====
csterm.py is a useful utility for interacting with the NCOS CLI. The usage is straight forward:
To run a single command:
c = EventingCSClient('cli_sample')
ct = CSTerm(c)
ct.exec("arpdump")

Multiple commands can be run by passing a list of commands:
ct.exec(["clients", "arpdump"])

An instance of CSTerm invokes a single CLI session, similar to SSHing into the device. Besides
sending a list of commands into exec, you can execute exec multiple times to send multiple commands

ct.exec("clients")
ct.exec("wan")
ct.exec("arpdump")

Intuitively, it's then possible to automate a workflow. Here's an example using NCOS ssh client
to SSH into a local machine and run a command:

ct.exec(["ssh user@host", # ssh into host
"yes", # respond 'yes' to accept host key
"password", # respond with password
"cd workflow", # change directory
"ls", # list files
"exit"]) # exit ssh session to return back to NCOS cli

You can adjust some of the timers for CSTerm:
ct = CSTerm(c, timeout=10, soft_timeout=5)
Timeout is the absolute timeout when running a command, soft_timeout is the timeout for sending
a "ctrl+c" to the console to terminate the running command. The default values are
10 and 5 seconds respectively.

0 comments on commit bb8a7ac

Please sign in to comment.