diff --git a/connpy/__main__.py b/connpy/__main__.py index 4d6f390..5924061 100644 --- a/connpy/__main__.py +++ b/connpy/__main__.py @@ -4,7 +4,8 @@ from connpy import * def main(): conf = configfile() - connapp(conf) + app = connapp(conf) + app.start() if __name__ == '__main__': sys.exit(main()) diff --git a/connpy/_version.py b/connpy/_version.py index 0873e16..3ead992 100644 --- a/connpy/_version.py +++ b/connpy/_version.py @@ -1,2 +1,2 @@ -__version__ = "2.1.0" +__version__ = "2.1.1" diff --git a/connpy/connapp.py b/connpy/connapp.py index 9beacc4..6250e48 100755 --- a/connpy/connapp.py +++ b/connpy/connapp.py @@ -44,6 +44,17 @@ class connapp: self.fzf = self.config.config["fzf"] except: self.fzf = False + + + def start(self,argv = sys.argv[1:]): + ''' + + ### Parameters: + + - argv (list): List of arguments to pass to the app. + Default: sys.argv[1:] + + ''' #DEFAULTPARSER defaultparser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter) subparsers = defaultparser.add_subparsers(title="Commands") @@ -99,13 +110,13 @@ class connapp: #Manage sys arguments commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "run", "config"] profilecmds = ["--add", "-a", "--del", "--rm", "-r", "--mod", "--edit", "-e", "--show", "-s"] - if len(sys.argv) >= 3 and sys.argv[2] == "profile" and sys.argv[1] in profilecmds: - sys.argv[2] = sys.argv[1] - sys.argv[1] = "profile" - if len(sys.argv) < 2 or sys.argv[1] not in commands: - sys.argv.insert(1,"node") - args = defaultparser.parse_args() - args.func(args) + if len(argv) >= 2 and argv[1] == "profile" and argv[0] in profilecmds: + argv[1] = argv[0] + argv[0] = "profile" + if len(argv) < 1 or argv[0] not in commands: + argv.insert(0,"node") + args = defaultparser.parse_args(argv) + return args.func(args) class _store_type(argparse.Action): #Custom store type for cli app. @@ -252,6 +263,7 @@ class connapp: if not updatenode: exit(7) uniques.update(node) + uniques["type"] = "connection" if sorted(updatenode.items()) == sorted(uniques.items()): print("Nothing to do here") return @@ -546,9 +558,14 @@ class connapp: try: myexpected = args["expected"].format(**args["vars"][i]) except: - myexpected = args["expected"] + try: + myexpected = args["expected"].format(**args["vars"]["__global__"]) + except: + myexpected = args["expected"] print(" TEST for '{}' --> ".format(myexpected) + str(nodes.result[i]).upper()) if stdout: + if nodes.status[i] == 0: + print(" " + "-" * (len(myexpected) + 16 + len(str(nodes.result[i])))) for line in nodes.output[i].splitlines(): print(" " + line) else: diff --git a/connpy/core.py b/connpy/core.py index 5bd4fd9..6c95355 100755 --- a/connpy/core.py +++ b/connpy/core.py @@ -10,6 +10,7 @@ from time import sleep import datetime import sys import threading +from pathlib import Path from copy import deepcopy #functions and classes @@ -142,6 +143,7 @@ class node: t = tb ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/ ]*[@-~])') t = ansi_escape.sub('', t) + t = t.lstrip(" \n\r") if var == False: d = open(logfile, "w") d.write(t) @@ -218,9 +220,11 @@ class node: ''' connect = self._connect(timeout = timeout) + now = datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S') if connect == True: expects = [prompt, pexpect.EOF, pexpect.TIMEOUT] output = '' + status = '' if not isinstance(commands, list): commands = [commands] for c in commands: @@ -233,33 +237,41 @@ class node: if result == 1: output = output + self.child.before.decode() if result == 2: - self.output = output + self.child.before.decode() - self.status = 2 - return self.output - result = self.child.expect(expects, timeout = timeout) - if result == 0: - output = output + self.child.before.decode() + self.child.after.decode() - if result == 1: - output = output + self.child.before.decode() - if result == 2: - self.output = output + self.child.before.decode() - self.status = 2 - return self.output + output = output + self.child.before.decode() + status = 2 + break + if not status == 2: + result = self.child.expect(expects, timeout = timeout) + if result == 0: + output = output + self.child.before.decode() + self.child.after.decode() + if result == 1: + output = output + self.child.before.decode() + if result == 2: + output = output + self.child.before.decode() + status = 2 self.child.close() - output = output.lstrip() + output = self._logclean(output, True) if stdout == True: print(output) if folder != '': - with open(folder + "/" + self.unique, "w") as f: + with open(folder + "/" + self.unique + "_" + now + ".txt", "w") as f: f.write(output) f.close() - self._logclean(folder + "/" + self.unique) self.output = output - self.status = 0 + if status == 2: + self.status = 2 + else: + self.status = 0 return output else: self.output = connect self.status = 1 + if stdout == True: + print(connect) + if folder != '': + with open(folder + "/" + self.unique + "_" + now + ".txt", "w") as f: + f.write(connect) + f.close() return connect def test(self, commands, expected, vars = None,*, prompt = r'>$|#$|\$$|>.$|#.$|\$.$', timeout = 10): @@ -317,8 +329,9 @@ class node: if result == 1: output = output + self.child.before.decode() if result == 2: + output = output + self.child.before.decode() self.result = None - self.output = output + self.child.before.decode() + self.output = self._logclean(output, True) self.status = 2 return self.output if vars is not None: @@ -329,7 +342,7 @@ class node: if results == 0: self.result = True output = output + self.child.before.decode() + self.child.after.decode() - output = output.lstrip() + output = self._logclean(output, True) self.output = output self.status = 0 return True @@ -339,14 +352,14 @@ class node: output = output + self.child.before.decode() + self.child.after.decode() elif results == 2: output = output + self.child.before.decode() - output = output.lstrip() + output = self._logclean(output, True) self.output = output self.status = 0 return False if results == 3: self.result = None output = output + self.child.before.decode() - output = output.lstrip() + output = self._logclean(output, True) self.output = output self.status = 2 return output @@ -544,6 +557,7 @@ class nodes: args["commands"] = commands if folder != None: args["folder"] = folder + Path(folder).mkdir(parents=True, exist_ok=True) if prompt != None: args["prompt"] = prompt if stdout != None: diff --git a/docs/connpy/index.html b/docs/connpy/index.html index 5c02b7b..804bc38 100644 --- a/docs/connpy/index.html +++ b/docs/connpy/index.html @@ -704,6 +704,17 @@ __pdoc__ = { self.fzf = self.config.config["fzf"] except: self.fzf = False + + + def start(self,argv = sys.argv[1:]): + ''' + + ### Parameters: + + - argv (list): List of arguments to pass to the app. + Default: sys.argv[1:] + + ''' #DEFAULTPARSER defaultparser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter) subparsers = defaultparser.add_subparsers(title="Commands") @@ -759,13 +770,13 @@ __pdoc__ = { #Manage sys arguments commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "run", "config"] profilecmds = ["--add", "-a", "--del", "--rm", "-r", "--mod", "--edit", "-e", "--show", "-s"] - if len(sys.argv) >= 3 and sys.argv[2] == "profile" and sys.argv[1] in profilecmds: - sys.argv[2] = sys.argv[1] - sys.argv[1] = "profile" - if len(sys.argv) < 2 or sys.argv[1] not in commands: - sys.argv.insert(1,"node") - args = defaultparser.parse_args() - args.func(args) + if len(argv) >= 2 and argv[1] == "profile" and argv[0] in profilecmds: + argv[1] = argv[0] + argv[0] = "profile" + if len(argv) < 1 or argv[0] not in commands: + argv.insert(0,"node") + args = defaultparser.parse_args(argv) + return args.func(args) class _store_type(argparse.Action): #Custom store type for cli app. @@ -912,6 +923,7 @@ __pdoc__ = { if not updatenode: exit(7) uniques.update(node) + uniques["type"] = "connection" if sorted(updatenode.items()) == sorted(uniques.items()): print("Nothing to do here") return @@ -1206,9 +1218,14 @@ __pdoc__ = { try: myexpected = args["expected"].format(**args["vars"][i]) except: - myexpected = args["expected"] + try: + myexpected = args["expected"].format(**args["vars"]["__global__"]) + except: + myexpected = args["expected"] print(" TEST for '{}' --> ".format(myexpected) + str(nodes.result[i]).upper()) if stdout: + if nodes.status[i] == 0: + print(" " + "-" * (len(myexpected) + 16 + len(str(nodes.result[i])))) for line in nodes.output[i].splitlines(): print(" " + line) else: @@ -1710,6 +1727,91 @@ tasks: return str(password) +
+def start(self, argv=['-f', '-o', 'docs', '-c', "hljs_style='greyscale'", 'connpy', '--html'])
+
- argv (list): List of arguments to pass to the app.
+ Default: sys.argv[1:]
+
def start(self,argv = sys.argv[1:]):
+ '''
+
+ ### Parameters:
+
+ - argv (list): List of arguments to pass to the app.
+ Default: sys.argv[1:]
+
+ '''
+ #DEFAULTPARSER
+ defaultparser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter)
+ subparsers = defaultparser.add_subparsers(title="Commands")
+ #NODEPARSER
+ 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()
+ 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("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect")
+ nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect")
+ nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect")
+ nodecrud.add_argument("-e","--mod", "--edit", dest="action", action="store_const", help="Modify node[@subfolder][@folder]", const="mod", default="connect")
+ nodecrud.add_argument("-s","--show", dest="action", action="store_const", help="Show node[@subfolder][@folder]", const="show", default="connect")
+ nodecrud.add_argument("-d","--debug", dest="action", action="store_const", help="Display all conections steps", const="debug", default="connect")
+ nodeparser.set_defaults(func=self._func_node)
+ #PROFILEPARSER
+ 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")
+ profilecrud = profileparser.add_mutually_exclusive_group(required=True)
+ profilecrud.add_argument("-a", "--add", dest="action", action="store_const", help="Add new profile", const="add")
+ profilecrud.add_argument("-r", "--del", "--rm", dest="action", action="store_const", help="Delete profile", const="del")
+ profilecrud.add_argument("-e", "--mod", "--edit", dest="action", action="store_const", help="Modify profile", const="mod")
+ profilecrud.add_argument("-s", "--show", dest="action", action="store_const", help="Show profile", const="show")
+ profileparser.set_defaults(func=self._func_profile)
+ #MOVEPARSER
+ 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.set_defaults(func=self._func_others)
+ #COPYPARSER
+ 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.set_defaults(func=self._func_others)
+ #LISTPARSER
+ 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.set_defaults(func=self._func_others)
+ #BULKPARSER
+ 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.set_defaults(func=self._func_others)
+ #RUNPARSER
+ runparser = subparsers.add_parser("run", help="Run scripts or commands on nodes", formatter_class=argparse.RawTextHelpFormatter)
+ runparser.add_argument("run", nargs='+', action=self._store_type, help=self._help("run"), default="run", type=self._type_node)
+ runparser.add_argument("-g","--generate", dest="action", action="store_const", help="Generate yaml file template", const="generate", default="run")
+ runparser.set_defaults(func=self._func_run)
+ #CONFIGPARSER
+ configparser = subparsers.add_parser("config", help="Manage app config")
+ configcrud = configparser.add_mutually_exclusive_group(required=True)
+ configcrud.add_argument("--allow-uppercase", dest="case", nargs=1, action=self._store_type, help="Allow case sensitive names", choices=["true","false"])
+ configcrud.add_argument("--fzf", dest="fzf", nargs=1, action=self._store_type, help="Use fzf for lists", choices=["true","false"])
+ configcrud.add_argument("--keepalive", dest="idletime", nargs=1, action=self._store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT")
+ configcrud.add_argument("--completion", dest="completion", nargs=1, choices=["bash","zsh"], action=self._store_type, help="Get terminal completion configuration for conn")
+ configparser.set_defaults(func=self._func_others)
+ #Manage sys arguments
+ commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "run", "config"]
+ profilecmds = ["--add", "-a", "--del", "--rm", "-r", "--mod", "--edit", "-e", "--show", "-s"]
+ if len(argv) >= 2 and argv[1] == "profile" and argv[0] in profilecmds:
+ argv[1] = argv[0]
+ argv[0] = "profile"
+ if len(argv) < 1 or argv[0] not in commands:
+ argv.insert(0,"node")
+ args = defaultparser.parse_args(argv)
+ return args.func(args)
+
@@ -1887,6 +1989,7 @@ tasks:
t = tb
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/ ]*[@-~])')
t = ansi_escape.sub('', t)
+ t = t.lstrip(" \n\r")
if var == False:
d = open(logfile, "w")
d.write(t)
@@ -1963,9 +2066,11 @@ tasks:
'''
connect = self._connect(timeout = timeout)
+ now = datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')
if connect == True:
expects = [prompt, pexpect.EOF, pexpect.TIMEOUT]
output = ''
+ status = ''
if not isinstance(commands, list):
commands = [commands]
for c in commands:
@@ -1978,33 +2083,41 @@ tasks:
if result == 1:
output = output + self.child.before.decode()
if result == 2:
- self.output = output + self.child.before.decode()
- self.status = 2
- return self.output
- result = self.child.expect(expects, timeout = timeout)
- if result == 0:
- output = output + self.child.before.decode() + self.child.after.decode()
- if result == 1:
- output = output + self.child.before.decode()
- if result == 2:
- self.output = output + self.child.before.decode()
- self.status = 2
- return self.output
+ output = output + self.child.before.decode()
+ status = 2
+ break
+ if not status == 2:
+ result = self.child.expect(expects, timeout = timeout)
+ if result == 0:
+ output = output + self.child.before.decode() + self.child.after.decode()
+ if result == 1:
+ output = output + self.child.before.decode()
+ if result == 2:
+ output = output + self.child.before.decode()
+ status = 2
self.child.close()
- output = output.lstrip()
+ output = self._logclean(output, True)
if stdout == True:
print(output)
if folder != '':
- with open(folder + "/" + self.unique, "w") as f:
+ with open(folder + "/" + self.unique + "_" + now + ".txt", "w") as f:
f.write(output)
f.close()
- self._logclean(folder + "/" + self.unique)
self.output = output
- self.status = 0
+ if status == 2:
+ self.status = 2
+ else:
+ self.status = 0
return output
else:
self.output = connect
self.status = 1
+ if stdout == True:
+ print(connect)
+ if folder != '':
+ with open(folder + "/" + self.unique + "_" + now + ".txt", "w") as f:
+ f.write(connect)
+ f.close()
return connect
def test(self, commands, expected, vars = None,*, prompt = r'>$|#$|\$$|>.$|#.$|\$.$', timeout = 10):
@@ -2062,8 +2175,9 @@ tasks:
if result == 1:
output = output + self.child.before.decode()
if result == 2:
+ output = output + self.child.before.decode()
self.result = None
- self.output = output + self.child.before.decode()
+ self.output = self._logclean(output, True)
self.status = 2
return self.output
if vars is not None:
@@ -2074,7 +2188,7 @@ tasks:
if results == 0:
self.result = True
output = output + self.child.before.decode() + self.child.after.decode()
- output = output.lstrip()
+ output = self._logclean(output, True)
self.output = output
self.status = 0
return True
@@ -2084,14 +2198,14 @@ tasks:
output = output + self.child.before.decode() + self.child.after.decode()
elif results == 2:
output = output + self.child.before.decode()
- output = output.lstrip()
+ output = self._logclean(output, True)
self.output = output
self.status = 0
return False
if results == 3:
self.result = None
output = output + self.child.before.decode()
- output = output.lstrip()
+ output = self._logclean(output, True)
self.output = output
self.status = 2
return output
@@ -2304,9 +2418,11 @@ tasks:
'''
connect = self._connect(timeout = timeout)
+ now = datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')
if connect == True:
expects = [prompt, pexpect.EOF, pexpect.TIMEOUT]
output = ''
+ status = ''
if not isinstance(commands, list):
commands = [commands]
for c in commands:
@@ -2319,33 +2435,41 @@ tasks:
if result == 1:
output = output + self.child.before.decode()
if result == 2:
- self.output = output + self.child.before.decode()
- self.status = 2
- return self.output
- result = self.child.expect(expects, timeout = timeout)
- if result == 0:
- output = output + self.child.before.decode() + self.child.after.decode()
- if result == 1:
- output = output + self.child.before.decode()
- if result == 2:
- self.output = output + self.child.before.decode()
- self.status = 2
- return self.output
+ output = output + self.child.before.decode()
+ status = 2
+ break
+ if not status == 2:
+ result = self.child.expect(expects, timeout = timeout)
+ if result == 0:
+ output = output + self.child.before.decode() + self.child.after.decode()
+ if result == 1:
+ output = output + self.child.before.decode()
+ if result == 2:
+ output = output + self.child.before.decode()
+ status = 2
self.child.close()
- output = output.lstrip()
+ output = self._logclean(output, True)
if stdout == True:
print(output)
if folder != '':
- with open(folder + "/" + self.unique, "w") as f:
+ with open(folder + "/" + self.unique + "_" + now + ".txt", "w") as f:
f.write(output)
f.close()
- self._logclean(folder + "/" + self.unique)
self.output = output
- self.status = 0
+ if status == 2:
+ self.status = 2
+ else:
+ self.status = 0
return output
else:
self.output = connect
self.status = 1
+ if stdout == True:
+ print(connect)
+ if folder != '':
+ with open(folder + "/" + self.unique + "_" + now + ".txt", "w") as f:
+ f.write(connect)
+ f.close()
return connect
@@ -2444,8 +2568,9 @@ tasks:
if result == 1:
output = output + self.child.before.decode()
if result == 2:
+ output = output + self.child.before.decode()
self.result = None
- self.output = output + self.child.before.decode()
+ self.output = self._logclean(output, True)
self.status = 2
return self.output
if vars is not None:
@@ -2456,7 +2581,7 @@ tasks:
if results == 0:
self.result = True
output = output + self.child.before.decode() + self.child.after.decode()
- output = output.lstrip()
+ output = self._logclean(output, True)
self.output = output
self.status = 0
return True
@@ -2466,14 +2591,14 @@ tasks:
output = output + self.child.before.decode() + self.child.after.decode()
elif results == 2:
output = output + self.child.before.decode()
- output = output.lstrip()
+ output = self._logclean(output, True)
self.output = output
self.status = 0
return False
if results == 3:
self.result = None
output = output + self.child.before.decode()
- output = output.lstrip()
+ output = self._logclean(output, True)
self.output = output
self.status = 2
return output
@@ -2639,6 +2764,7 @@ tasks:
args["commands"] = commands
if folder != None:
args["folder"] = folder
+ Path(folder).mkdir(parents=True, exist_ok=True)
if prompt != None:
args["prompt"] = prompt
if stdout != None:
@@ -2852,6 +2978,7 @@ tasks:
args["commands"] = commands
if folder != None:
args["folder"] = folder
+ Path(folder).mkdir(parents=True, exist_ok=True)
if prompt != None:
args["prompt"] = prompt
if stdout != None:
@@ -3049,6 +3176,7 @@ tasks:
connapp