documentation full transformation

This commit is contained in:
fluzzi 2022-04-02 23:25:53 -03:00
parent b5d6894865
commit 27212b1009
11 changed files with 517 additions and 40 deletions

View File

@ -1,10 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
'''
'''
from .core import node,nodes from .core import node,nodes
from .configfile import configfile from .configfile import configfile
from .connapp import connapp from .connapp import connapp
from pkg_resources import get_distribution from pkg_resources import get_distribution
__all__ = ["node", "nodes", "configfile", "connapp"] __all__ = ["node", "nodes", "configfile"]
__version__ = "2.0.9" __version__ = "2.0.10"
__author__ = "Federico Luzzi" __author__ = "Federico Luzzi"
__pdoc__ = {
'core': False,
'connapp': False,
}

View File

@ -1,10 +0,0 @@
#!/usr/bin/env python3
import sys
from conn import *
def main():
conf = configfile()
connapp(conf, node)
if __name__ == '__main__':
sys.exit(main())

View File

@ -96,6 +96,9 @@ class configfile:
folder = self.connections[uniques["folder"]] folder = self.connections[uniques["folder"]]
newfolder = folder.copy() newfolder = folder.copy()
newfolder.pop("type") newfolder.pop("type")
for node in newfolder.keys():
if "type" in newfolder[node].keys():
newfolder[node].pop("type")
if keys == None: if keys == None:
return newfolder return newfolder
else: else:
@ -109,6 +112,7 @@ class configfile:
else: else:
node = self.connections[uniques["id"]] node = self.connections[uniques["id"]]
newnode = node.copy() newnode = node.copy()
newnode.pop("type")
return newnode return newnode
def _connections_add(self,*, id, host, folder='', subfolder='', options='', logs='', password='', port='', protocol='', user='', type = "connection" ): def _connections_add(self,*, id, host, folder='', subfolder='', options='', logs='', password='', port='', protocol='', user='', type = "connection" ):

View File

@ -9,11 +9,12 @@ import argparse
import sys import sys
import inquirer import inquirer
import json import json
from conn import *
#functions and classes #functions and classes
class connapp: class connapp:
def __init__(self, config, node): def __init__(self, config, node):
self.node = node self.node = node
self.config = config self.config = config
@ -27,7 +28,7 @@ class connapp:
#NODEPARSER #NODEPARSER
nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), formatter_class=argparse.RawTextHelpFormatter) nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), formatter_class=argparse.RawTextHelpFormatter)
nodecrud = nodeparser.add_mutually_exclusive_group() nodecrud = nodeparser.add_mutually_exclusive_group()
nodeparser.add_argument("node", metavar="node|folder", nargs='?', default=None, action=self.store_type, type=self._type_node, help=self._help("node")) nodeparser.add_argument("node", metavar="node|folder", nargs='?', default=None, action=self._store_type, type=self._type_node, help=self._help("node"))
nodecrud.add_argument("--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect") nodecrud.add_argument("--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect")
nodecrud.add_argument("--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect") nodecrud.add_argument("--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect")
nodecrud.add_argument("--mod", "--edit", dest="action", action="store_const", help="Modify node[@subfolder][@folder]", const="mod", default="connect") nodecrud.add_argument("--mod", "--edit", dest="action", action="store_const", help="Modify node[@subfolder][@folder]", const="mod", default="connect")
@ -36,7 +37,7 @@ class connapp:
nodeparser.set_defaults(func=self._func_node) nodeparser.set_defaults(func=self._func_node)
#PROFILEPARSER #PROFILEPARSER
profileparser = subparsers.add_parser("profile", help="Manage profiles") profileparser = subparsers.add_parser("profile", help="Manage profiles")
profileparser.add_argument("profile", nargs=1, action=self.store_type, type=self._type_profile, help="Name of profile to manage") profileparser.add_argument("profile", nargs=1, action=self._store_type, type=self._type_profile, help="Name of profile to manage")
profilecrud = profileparser.add_mutually_exclusive_group(required=True) profilecrud = profileparser.add_mutually_exclusive_group(required=True)
profilecrud.add_argument("--add", dest="action", action="store_const", help="Add new profile", const="add") profilecrud.add_argument("--add", dest="action", action="store_const", help="Add new profile", const="add")
profilecrud.add_argument("--del", "--rm", dest="action", action="store_const", help="Delete profile", const="del") profilecrud.add_argument("--del", "--rm", dest="action", action="store_const", help="Delete profile", const="del")
@ -45,25 +46,25 @@ class connapp:
profileparser.set_defaults(func=self._func_profile) profileparser.set_defaults(func=self._func_profile)
#MOVEPARSER #MOVEPARSER
moveparser = subparsers.add_parser("move", aliases=["mv"], help="Move node") moveparser = subparsers.add_parser("move", aliases=["mv"], help="Move node")
moveparser.add_argument("move", nargs=2, action=self.store_type, help="Move node[@subfolder][@folder] dest_node[@subfolder][@folder]", default="move", type=self._type_node) moveparser.add_argument("move", nargs=2, action=self._store_type, help="Move node[@subfolder][@folder] dest_node[@subfolder][@folder]", default="move", type=self._type_node)
moveparser.set_defaults(func=self._func_others) moveparser.set_defaults(func=self._func_others)
#COPYPARSER #COPYPARSER
copyparser = subparsers.add_parser("copy", aliases=["cp"], help="Copy node") copyparser = subparsers.add_parser("copy", aliases=["cp"], help="Copy node")
copyparser.add_argument("cp", nargs=2, action=self.store_type, help="Copy node[@subfolder][@folder] new_node[@subfolder][@folder]", default="cp", type=self._type_node) copyparser.add_argument("cp", nargs=2, action=self._store_type, help="Copy node[@subfolder][@folder] new_node[@subfolder][@folder]", default="cp", type=self._type_node)
copyparser.set_defaults(func=self._func_others) copyparser.set_defaults(func=self._func_others)
#LISTPARSER #LISTPARSER
lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders") lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders")
lsparser.add_argument("ls", action=self.store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False) lsparser.add_argument("ls", action=self._store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False)
lsparser.set_defaults(func=self._func_others) lsparser.set_defaults(func=self._func_others)
#BULKPARSER #BULKPARSER
bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk") bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk")
bulkparser.add_argument("bulk", const="bulk", nargs=0, action=self.store_type, help="Add nodes in bulk") bulkparser.add_argument("bulk", const="bulk", nargs=0, action=self._store_type, help="Add nodes in bulk")
bulkparser.set_defaults(func=self._func_others) bulkparser.set_defaults(func=self._func_others)
#CONFIGPARSER #CONFIGPARSER
configparser = subparsers.add_parser("config", help="Manage app config") configparser = subparsers.add_parser("config", help="Manage app config")
configparser.add_argument("--allow-uppercase", dest="case", nargs=1, action=self.store_type, help="Allow case sensitive names", choices=["true","false"]) configparser.add_argument("--allow-uppercase", dest="case", nargs=1, action=self._store_type, help="Allow case sensitive names", choices=["true","false"])
configparser.add_argument("--keepalive", dest="idletime", nargs=1, action=self.store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT") configparser.add_argument("--keepalive", dest="idletime", nargs=1, action=self._store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT")
configparser.add_argument("--completion", dest="completion", nargs=0, action=self.store_type, help="Get bash completion configuration for conn") configparser.add_argument("--completion", dest="completion", nargs=0, action=self._store_type, help="Get bash completion configuration for conn")
configparser.set_defaults(func=self._func_others) configparser.set_defaults(func=self._func_others)
#Set default subparser and tune arguments #Set default subparser and tune arguments
commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "config"] commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "config"]
@ -76,7 +77,7 @@ class connapp:
args = defaultparser.parse_args() args = defaultparser.parse_args()
args.func(args) args.func(args)
class store_type(argparse.Action): class _store_type(argparse.Action):
def __call__(self, parser, args, values, option_string=None): def __call__(self, parser, args, values, option_string=None):
setattr(args, "data", values) setattr(args, "data", values)
delattr(args,self.dest) delattr(args,self.dest)
@ -740,3 +741,10 @@ complete -o nosort -F _conn conn
encryptor = PKCS1_OAEP.new(publickey) encryptor = PKCS1_OAEP.new(publickey)
password = encryptor.encrypt(password.encode("utf-8")) password = encryptor.encrypt(password.encode("utf-8"))
return str(password) return str(password)
def main():
conf = configfile()
connapp(conf, node)
if __name__ == '__main__':
sys.exit(main())

View File

@ -14,7 +14,40 @@ import threading
#functions and classes #functions and classes
class node: class node:
def __init__(self, unique, host, options='', logs='', password='', port='', protocol='', type='', user='', config=''): ''' This class generates a node object. Containts all the information and methods to connect and interact with a device using ssh or telnet.
Attributes:
- output (str) -- Output of the commands you ran with run or test
-- method.
- result(bool) -- True if expected value is found after running
-- the commands using test method.
'''
def __init__(self, unique, host, options='', logs='', password='', port='', protocol='', user='', config=''):
'''
Parameters:
- unique (str) -- Unique name to assign to the node.
- host (str) -- IP address or hostname of the node.
Optional Parameters:
- options (str) -- Additional options to pass the ssh/telnet for
-- connection.
- logs (str) -- Path/file for storing the logs. You can use
-- ${unique},${host}, ${port}, ${user}, ${protocol}
-- as variables.
- password (str) -- Encrypted or plaintext password.
- port (str) -- Port to connect to node, default 22 for ssh and 23
-- for telnet.
- protocol (str) -- Select ssh or telnet. Default is ssh.
- user (str) -- Username to of the node.
- config (obj) -- Pass the object created with class configfile with
-- key for decryption and extra configuration if you
-- are using connection manager.
'''
if config == '': if config == '':
self.idletime = 0 self.idletime = 0
self.key = None self.key = None
@ -22,7 +55,6 @@ class node:
self.idletime = config.config["idletime"] self.idletime = config.config["idletime"]
self.key = config.key self.key = config.key
self.unique = unique self.unique = unique
self.id = self.unique.split("@")[0]
attr = {"host": host, "logs": logs, "options":options, "port": port, "protocol": protocol, "user": user} attr = {"host": host, "logs": logs, "options":options, "port": port, "protocol": protocol, "user": user}
for key in attr: for key in attr:
profile = re.search("^@(.*)", attr[key]) profile = re.search("^@(.*)", attr[key])
@ -45,6 +77,7 @@ class node:
self.password = [password] self.password = [password]
def __passtx(self, passwords, *, keyfile=None): def __passtx(self, passwords, *, keyfile=None):
# decrypts passwords, used by other methdos.
dpass = [] dpass = []
if keyfile is None: if keyfile is None:
keyfile = self.key keyfile = self.key
@ -65,9 +98,9 @@ class node:
def _logfile(self, logfile = None): def _logfile(self, logfile = None):
# translate logs variables and generate logs path.
if logfile == None: if logfile == None:
logfile = self.logs logfile = self.logs
logfile = logfile.replace("${id}", self.id)
logfile = logfile.replace("${unique}", self.unique) logfile = logfile.replace("${unique}", self.unique)
logfile = logfile.replace("${host}", self.host) logfile = logfile.replace("${host}", self.host)
logfile = logfile.replace("${port}", self.port) logfile = logfile.replace("${port}", self.port)
@ -80,13 +113,14 @@ class node:
return logfile return logfile
def _logclean(self, logfile, var = False): def _logclean(self, logfile, var = False):
#Remove special ascii characters and other stuff from logfile.
if var == False: if var == False:
t = open(logfile, "r").read() t = open(logfile, "r").read()
else: else:
t = logfile t = logfile
t = t.replace("\n","",1).replace("\a","") t = t.replace("\n","",1).replace("\a","")
t = t.replace('\n\n', '\n') t = t.replace('\n\n', '\n')
t = re.sub('.\[K', '', t) t = re.sub(r'.\[K', '', t)
while True: while True:
tb = re.sub('.\b', '', t, count=1) tb = re.sub('.\b', '', t, count=1)
if len(t) == len(tb): if len(t) == len(tb):
@ -103,6 +137,14 @@ class node:
return t return t
def interact(self, debug = False): def interact(self, debug = False):
'''
Allow user to interact with the node directly, mostly used by connection manager.
Optional Parameters:
- debug (bool) -- If True, display all the connecting information
-- before interact. Default False.
'''
connect = self._connect(debug = debug) connect = self._connect(debug = debug)
if connect == True: if connect == True:
size = re.search('columns=([0-9]+).*lines=([0-9]+)',str(os.get_terminal_size())) size = re.search('columns=([0-9]+).*lines=([0-9]+)',str(os.get_terminal_size()))
@ -121,7 +163,32 @@ class node:
print(connect) print(connect)
exit(1) exit(1)
def run(self, commands,*, folder = '', prompt = '>$|#$|\$$|>.$|#.$|\$.$', stdout = False): def run(self, commands,*, folder = '', prompt = r'>$|#$|\$$|>.$|#.$|\$.$', stdout = False):
'''
Run a command or list of commands on the node and return the output.
Parameters:
- commands (str/list) -- Commands to run on the node. Should be
-- str or a list of str.
Optional Named Parameters:
- folder (str) -- Path where output log should be stored, leave
-- empty to disable logging.
- prompt (str) -- Prompt to be expected after a command is finished
-- running. Usually linux uses ">" or EOF while
-- routers use ">" or "#". The default value should
-- work for most nodes. Change it if your connection
-- need some special symbol.
- stdout (bool) -- Set True to send the command output to stdout.
-- default False.
Returns:
str -> Output of the commands you ran on the node.
'''
connect = self._connect() connect = self._connect()
if connect == True: if connect == True:
expects = [prompt, pexpect.EOF] expects = [prompt, pexpect.EOF]
@ -161,7 +228,31 @@ class node:
self.output = connect self.output = connect
return connect return connect
def test(self, commands, expected, *, prompt = '>$|#$|\$$|>.$|#.$|\$.$'): def test(self, commands, expected, *, prompt = r'>$|#$|\$$|>.$|#.$|\$.$'):
'''
Run a command or list of commands on the node, then check if expected value appears on the output after the last command.
Parameters:
- commands (str/list) -- Commands to run on the node. Should be
-- str or list of str.
- expected (str) -- Expected text to appear after running
-- all the commands on the node.
Optional Named Parameters:
- prompt (str) -- Prompt to be expected after a command is finished
-- running. Usually linux uses ">" or EOF while
-- routers use ">" or "#". The default value should
-- work for most nodes. Change it if your connection
-- need some special symbol.
Returns:
bool -> true if expected value is found after running the commands
false if prompt is found before.
'''
connect = self._connect() connect = self._connect()
if connect == True: if connect == True:
expects = [prompt, pexpect.EOF] expects = [prompt, pexpect.EOF]
@ -203,6 +294,7 @@ class node:
return connect return connect
def _connect(self, debug = False): def _connect(self, debug = False):
# Method to connect to the node, it parse all the information, create the ssh/telnet command and login to the node.
if self.protocol == "ssh": if self.protocol == "ssh":
cmd = "ssh" cmd = "ssh"
if self.idletime > 0: if self.idletime > 0:
@ -221,7 +313,7 @@ class node:
passwords = self.__passtx(self.password) passwords = self.__passtx(self.password)
else: else:
passwords = [] passwords = []
expects = ['yes/no', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:|[u|U]sername:', '>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, "No route to host", "resolve hostname"] expects = ['yes/no', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:|[u|U]sername:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, "No route to host", "resolve hostname", "no matching host key"]
elif self.protocol == "telnet": elif self.protocol == "telnet":
cmd = "telnet " + self.host cmd = "telnet " + self.host
if self.port != '': if self.port != '':
@ -234,7 +326,7 @@ class node:
passwords = self.__passtx(self.password) passwords = self.__passtx(self.password)
else: else:
passwords = [] passwords = []
expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:', '>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, "No route to host", "resolve hostname"] expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, "No route to host", "resolve hostname", "no matching host key"]
else: else:
raise ValueError("Invalid protocol: " + self.protocol) raise ValueError("Invalid protocol: " + self.protocol)
child = pexpect.spawn(cmd) child = pexpect.spawn(cmd)
@ -257,7 +349,7 @@ class node:
else: else:
self.missingtext = True self.missingtext = True
break break
if results in [1, 2, 3, 4, 5, 6, 7, 12, 13]: if results in [1, 2, 3, 4, 5, 6, 7, 12, 13, 14]:
child.close() child.close()
return "Connection failed code:" + str(results) return "Connection failed code:" + str(results)
if results == 8: if results == 8:
@ -280,7 +372,43 @@ class node:
return True return True
class nodes: class nodes:
''' This class generates a nodes object. Contains a list of node class objects and methods to run multiple tasks on nodes simultaneously.
### Attributes:
- nodelist (list): List of node class objects passed to the init
function.
- output (dict): Dictionary formed by nodes unique as keys,
output of the commands you ran on the node as
value. Created after running methods run or test.
- result (dict): Dictionary formed by nodes unique as keys, value
is True if expected value is found after running
the commands, False if prompt is found before.
Created after running method test.
- <unique> (obj): For each item in nodelist, there is an attribute
generated with the node unique.
'''
def __init__(self, nodes: dict, config = ''): def __init__(self, nodes: dict, config = ''):
'''
### Parameters:
- nodes (dict): Dictionary formed by node information:
Keys: Unique name for each node.
Mandatory Subkeys: host(str).
Optional Subkeys: options(str), logs(str), password(str),
port(str), protocol(str), user(str).
For reference on subkeys check node class.
Optional Parameters:
- config (obj): Pass the object created with class configfile with key
for decryption and extra configuration if you are using
connection manager.
'''
self.nodelist = [] self.nodelist = []
self.config = config self.config = config
for n in nodes: for n in nodes:
@ -289,12 +417,29 @@ class nodes:
setattr(self,n,this) setattr(self,n,this)
def splitlist(self, lst, n): def _splitlist(self, lst, n):
#split a list in lists of n members.
for i in range(0, len(lst), n): for i in range(0, len(lst), n):
yield lst[i:i + n] yield lst[i:i + n]
def run(self, commands,*, folder = None, prompt = None, stdout = None, parallel = 10): def run(self, commands,*, folder = None, prompt = None, stdout = None, parallel = 10):
'''
Run a command or list of commands on all the nodes in nodelist.
Parameters:
commands (str/list): Commands to run on the node. Should be a str or a list of str.
Optional Named Parameters:
folder (str): Path where output log should be stored, leave empty to disable logging.
prompt (str): Prompt to be expected after a command is finished running. Usually linux uses ">" or EOF while routers use ">" or "#". The default value should work for most nodes. Change it if your connection need some special symbol.
stdout (bool): Set True to send the command output to stdout. default False.
parallel (int): Number of nodes to run the commands simultaneously. Default is 10, if there are more nodes that this value, nodes are groups in groups with max this number of members.
Returns:
dict: Dictionary formed by nodes unique as keys, Output of the commands you ran on the node as value.
'''
args = {} args = {}
args["commands"] = commands args["commands"] = commands
if folder != None: if folder != None:
@ -307,18 +452,32 @@ class nodes:
tasks = [] tasks = []
for n in self.nodelist: for n in self.nodelist:
tasks.append(threading.Thread(target=n.run, kwargs=args)) tasks.append(threading.Thread(target=n.run, kwargs=args))
taskslist = list(self.splitlist(tasks, parallel)) taskslist = list(self._splitlist(tasks, parallel))
for t in taskslist: for t in taskslist:
for i in t: for i in t:
i.start() i.start()
for i in t: for i in t:
i.join() i.join()
for i in self.nodelist: for i in self.nodelist:
output[i.id] = i.output output[i.unique] = i.output
self.output = output self.output = output
return output return output
def test(self, commands, expected, *, prompt = None, parallel = 10): def test(self, commands, expected, *, prompt = None, parallel = 10):
'''
Run a command or list of commands on all the nodes in nodelist, then check if expected value appears on the output after the last command.
Parameters:
commands (str/list): Commands to run on the node. Should be a str or a list of str.
commands (str): Expected text to appear after running all the commands on the node.
Optional Named Parameters:
prompt (str): Prompt to be expected after a command is finished running. Usually linux uses ">" or EOF while routers use ">" or "#". The default value should work for most nodes. Change it if your connection need some special symbol.
Returns:
dict: Dictionary formed by nodes unique as keys, value is True if expected value is found after running the commands, False if prompt is found before.
'''
args = {} args = {}
args["commands"] = commands args["commands"] = commands
args["expected"] = expected args["expected"] = expected
@ -329,15 +488,15 @@ class nodes:
tasks = [] tasks = []
for n in self.nodelist: for n in self.nodelist:
tasks.append(threading.Thread(target=n.test, kwargs=args)) tasks.append(threading.Thread(target=n.test, kwargs=args))
taskslist = list(self.splitlist(tasks, parallel)) taskslist = list(self._splitlist(tasks, parallel))
for t in taskslist: for t in taskslist:
for i in t: for i in t:
i.start() i.start()
for i in t: for i in t:
i.join() i.join()
for i in self.nodelist: for i in self.nodelist:
result[i.id] = i.result result[i.unique] = i.result
output[i.id] = i.output output[i.unique] = i.output
self.output = output self.output = output
self.result = result self.result = result
return result return result

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

54
docs/conf.py Normal file
View File

@ -0,0 +1,54 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'conn'
copyright = '2022, Federico Luzzi'
author = 'Federico Luzzi'
# The full version, including alpha/beta/rc tags
release = '2.0.10'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['myst_parser']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

180
docs/doc.md Normal file
View File

@ -0,0 +1,180 @@
Module conn
===========
Classes
-------
`configfile(conf=None, *, key=None)`
:
### Methods
`createconfig(self, conf)`
:
`createkey(self, keyfile)`
:
`getitem(self, unique, keys=None)`
:
`loadconfig(self, conf)`
:
`saveconfig(self, conf)`
:
`node(unique, host, options='', logs='', password='', port='', protocol='', user='', config='')`
: This class generates a node object. Containts all the information and methods to connect and interact with a device using ssh or telnet.
Attributes:
- output (str) -- Output of the commands you ran with run or test
-- method.
- result(bool) -- True if expected value is found after running
-- the commands using test method.
Parameters:
- unique (str) -- Unique name to assign to the node.
- host (str) -- IP address or hostname of the node.
Optional Parameters:
- options (str) -- Additional options to pass the ssh/telnet for
-- connection.
- logs (str) -- Path/file for storing the logs. You can use
-- ${unique},${host}, ${port}, ${user}, ${protocol}
-- as variables.
- password (str) -- Encrypted or plaintext password.
- port (str) -- Port to connect to node, default 22 for ssh and 23
-- for telnet.
- protocol (str) -- Select ssh or telnet. Default is ssh.
- user (str) -- Username to of the node.
- config (obj) -- Pass the object created with class configfile with
-- key for decryption and extra configuration if you
-- are using connection manager.
### Methods
`interact(self, debug=False)`
: Allow user to interact with the node directly, mostly used by connection manager.
Optional Parameters:
- debug (bool) -- If True, display all the connecting information
-- before interact. Default False.
`run(self, commands, *, folder='', prompt='>$|#$|\\$$|>.$|#.$|\\$.$', stdout=False)`
: Run a command or list of commands on the node and return the output.
Parameters:
- commands (str/list) -- Commands to run on the node. Should be
-- str or a list of str.
Optional Named Parameters:
- folder (str) -- Path where output log should be stored, leave
-- empty to disable logging.
- prompt (str) -- Prompt to be expected after a command is finished
-- running. Usually linux uses ">" or EOF while
-- routers use ">" or "#". The default value should
-- work for most nodes. Change it if your connection
-- need some special symbol.
- stdout (bool) -- Set True to send the command output to stdout.
-- default False.
Returns:
str -> Output of the commands you ran on the node.
`test(self, commands, expected, *, prompt='>$|#$|\\$$|>.$|#.$|\\$.$')`
: Run a command or list of commands on the node, then check if expected value appears on the output after the last command.
Parameters:
- commands (str/list) -- Commands to run on the node. Should be
-- str or list of str.
- expected (str) -- Expected text to appear after running
-- all the commands on the node.
Optional Named Parameters:
- prompt (str) -- Prompt to be expected after a command is finished
-- running. Usually linux uses ">" or EOF while
-- routers use ">" or "#". The default value should
-- work for most nodes. Change it if your connection
-- need some special symbol.
Returns:
bool -> true if expected value is found after running the commands
false if prompt is found before.
`nodes(nodes: dict, config='')`
: This class generates a nodes object. Contains a list of node class objects and methods to run multiple tasks on nodes simultaneously.
### Attributes:
- nodelist (list): List of node class objects passed to the init
function.
- output (dict): Dictionary formed by nodes unique as keys, output of the commands you ran on the node as
value. Created after running methods run or test.
- result (dict): Dictionary formed by nodes unique as keys, value
is True if expected value is found after running
the commands, False if prompt is found before.
Created after running method test.
- <unique> (obj): For each item in nodelist, there is an attribute
generated with the node unique.
### Parameters:
- nodes (dict): Dictionary formed by node information:
Keys: Unique name for each node.
Mandatory Subkeys: host(str).
Optional Subkeys: options(str), logs(str), password(str),
port(str), protocol(str), user(str).
For reference on subkeys check node class.
Optional Parameters:
- config (obj): Pass the object created with class configfile with key
for decryption and extra configuration if you are using
connection manager.
### Methods
`run(self, commands, *, folder=None, prompt=None, stdout=None, parallel=10)`
: Run a command or list of commands on all the nodes in nodelist.
Parameters:
commands (str/list): Commands to run on the node. Should be a str or a list of str.
Optional Named Parameters:
folder (str): Path where output log should be stored, leave empty to disable logging.
prompt (str): Prompt to be expected after a command is finished running. Usually linux uses ">" or EOF while routers use ">" or "#". The default value should work for most nodes. Change it if your connection need some special symbol.
stdout (bool): Set True to send the command output to stdout. default False.
parallel (int): Number of nodes to run the commands simultaneously. Default is 10, if there are more nodes that this value, nodes are groups in groups with max this number of members.
Returns:
dict: Dictionary formed by nodes unique as keys, Output of the commands you ran on the node as value.
`test(self, commands, expected, *, prompt=None, parallel=10)`
: Run a command or list of commands on all the nodes in nodelist, then check if expected value appears on the output after the last command.
Parameters:
commands (str/list): Commands to run on the node. Should be a str or a list of str.
commands (str): Expected text to appear after running all the commands on the node.
Optional Named Parameters:
prompt (str): Prompt to be expected after a command is finished running. Usually linux uses ">" or EOF while routers use ">" or "#". The default value should work for most nodes. Change it if your connection need some special symbol.
Returns:
dict: Dictionary formed by nodes unique as keys, value is True if expected value is found after running the commands, False if prompt is found before.

22
docs/index.rst Normal file
View File

@ -0,0 +1,22 @@
.. conn documentation master file, created by
sphinx-quickstart on Sat Apr 2 23:19:12 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to conn's documentation!
================================
.. toctree::
doc.md
:maxdepth: 2
:caption: Contents:
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

35
docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View File

@ -27,4 +27,4 @@ install_requires =
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =
conn = conn.app:main conn = conn.connapp:main