main app done
This commit is contained in:
		| @@ -4,6 +4,7 @@ import yaml | |||||||
| import os | import os | ||||||
| import re | import re | ||||||
| from Crypto.PublicKey import RSA | from Crypto.PublicKey import RSA | ||||||
|  | from pathlib import Path | ||||||
|  |  | ||||||
|  |  | ||||||
| #functions and classes | #functions and classes | ||||||
| @@ -15,8 +16,8 @@ class configfile: | |||||||
|         self.defaultdir = home + '/.config/conn' |         self.defaultdir = home + '/.config/conn' | ||||||
|         self.defaultfile = self.defaultdir + '/config.yaml' |         self.defaultfile = self.defaultdir + '/config.yaml' | ||||||
|         self.defaultkey = self.defaultdir + '/.osk' |         self.defaultkey = self.defaultdir + '/.osk' | ||||||
|  |         Path(self.defaultdir).mkdir(parents=True, exist_ok=True) | ||||||
|         if conf == None: |         if conf == None: | ||||||
|             self.dir = self.defaultdir |  | ||||||
|             self.file = self.defaultfile |             self.file = self.defaultfile | ||||||
|         else: |         else: | ||||||
|             self.file = conf |             self.file = conf | ||||||
| @@ -42,11 +43,12 @@ class configfile: | |||||||
|         return yaml.load(ymlconf.read(), Loader=yaml.CLoader) |         return yaml.load(ymlconf.read(), Loader=yaml.CLoader) | ||||||
|  |  | ||||||
|     def createconfig(self, conf): |     def createconfig(self, conf): | ||||||
|         defaultconfig = {'config': {'case': False, 'frun': False, 'idletime': 30}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"" }}} |         defaultconfig = {'config': {'case': False, 'idletime': 30}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"" }}} | ||||||
|         if not os.path.exists(conf): |         if not os.path.exists(conf): | ||||||
|             with open(conf, "w") as f: |             with open(conf, "w") as f: | ||||||
|                 yaml.dump(defaultconfig, f, explicit_start=True, Dumper=yaml.CDumper) |                 yaml.dump(defaultconfig, f, explicit_start=True, Dumper=yaml.CDumper) | ||||||
|                 f.close() |                 f.close() | ||||||
|  |                 os.chmod(conf, 0o600) | ||||||
|         ymlconf = open(conf) |         ymlconf = open(conf) | ||||||
|         return yaml.load(ymlconf.read(), Loader=yaml.CLoader) |         return yaml.load(ymlconf.read(), Loader=yaml.CLoader) | ||||||
|  |  | ||||||
| @@ -64,6 +66,7 @@ class configfile: | |||||||
|         with open(keyfile,'wb') as f: |         with open(keyfile,'wb') as f: | ||||||
|             f.write(key.export_key('PEM')) |             f.write(key.export_key('PEM')) | ||||||
|             f.close() |             f.close() | ||||||
|  |             os.chmod(keyfile, 0o600) | ||||||
|  |  | ||||||
|     def _explode_unique(self, unique): |     def _explode_unique(self, unique): | ||||||
|         uniques = unique.split("@") |         uniques = unique.split("@") | ||||||
| @@ -84,6 +87,23 @@ class configfile: | |||||||
|             return False |             return False | ||||||
|         return result |         return result | ||||||
|  |  | ||||||
|  |     def getitem(self, unique): | ||||||
|  |             uniques = self._explode_unique(unique) | ||||||
|  |             if unique.startswith("@"): | ||||||
|  |                 if uniques.keys() >= {"folder", "subfolder"}: | ||||||
|  |                     folder = self.connections[uniques["folder"]][uniques["subfolder"]] | ||||||
|  |                 else: | ||||||
|  |                     folder = self.connections[uniques["folder"]] | ||||||
|  |                 return folder | ||||||
|  |             else: | ||||||
|  |                 if uniques.keys() >= {"folder", "subfolder"}: | ||||||
|  |                     node = self.connections[uniques["folder"]][uniques["subfolder"]][uniques["id"]] | ||||||
|  |                 elif "folder" in uniques.keys(): | ||||||
|  |                     node = self.connections[uniques["folder"]][uniques["id"]] | ||||||
|  |                 else: | ||||||
|  |                     node = self.connections[uniques["id"]] | ||||||
|  |                 return node | ||||||
|  |  | ||||||
|     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" ): | ||||||
|         if folder == '': |         if folder == '': | ||||||
|             self.connections[id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "type": type} |             self.connections[id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "type": type} | ||||||
|   | |||||||
							
								
								
									
										231
									
								
								conn/connapp.py
									
									
									
									
									
								
							
							
						
						
									
										231
									
								
								conn/connapp.py
									
									
									
									
									
								
							| @@ -20,6 +20,7 @@ class connapp: | |||||||
|         self.nodes = self._getallnodes() |         self.nodes = self._getallnodes() | ||||||
|         self.folders = self._getallfolders() |         self.folders = self._getallfolders() | ||||||
|         self.profiles = list(self.config.profiles.keys()) |         self.profiles = list(self.config.profiles.keys()) | ||||||
|  |         self.case = self.config.config["case"] | ||||||
|         #DEFAULTPARSER |         #DEFAULTPARSER | ||||||
|         defaultparser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter) |         defaultparser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter) | ||||||
|         subparsers = defaultparser.add_subparsers(title="Commands") |         subparsers = defaultparser.add_subparsers(title="Commands") | ||||||
| @@ -27,19 +28,20 @@ class connapp: | |||||||
|         nodeparser = subparsers.add_parser("node", help=self._help("node"),formatter_class=argparse.RawTextHelpFormatter)  |         nodeparser = subparsers.add_parser("node", help=self._help("node"),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]", 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]", 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") | ||||||
|         nodecrud.add_argument("--show", dest="action", action="store_const", help="Show node[@subfolder][@folder]", const="show", default="connect") |         nodecrud.add_argument("--show", dest="action", action="store_const", help="Show node[@subfolder][@folder]", const="show", default="connect") | ||||||
|  |         nodecrud.add_argument("--debug", "-d", dest="action", action="store_const", help="Display all conections steps", const="debug", default="connect") | ||||||
|         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", default="connect") |         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", default="connect") |         profilecrud.add_argument("--del", "--rm", dest="action", action="store_const", help="Delete profile", const="del") | ||||||
|         profilecrud.add_argument("--mod", "--edit", dest="action", action="store_const", help="Modify profile", const="mod", default="connect") |         profilecrud.add_argument("--mod", "--edit", dest="action", action="store_const", help="Modify profile", const="mod") | ||||||
|         profilecrud.add_argument("--show", dest="action", action="store_const", help="Show profile", const="show", default="connect") |         profilecrud.add_argument("--show", dest="action", action="store_const", help="Show profile", const="show") | ||||||
|         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")  | ||||||
| @@ -57,8 +59,13 @@ class connapp: | |||||||
|         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 = 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("--keepalive", dest="idletime", nargs=1, action=self.store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT") | ||||||
|  |         configparser.set_defaults(func=self._func_others) | ||||||
|         #Set default subparser and tune arguments |         #Set default subparser and tune arguments | ||||||
|         commands = ["node", "-h", "--help", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list"] |         commands = ["node", "-h", "--help", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "config"] | ||||||
|         profilecmds = ["--add", "--del", "--rm", "--mod", "--edit", "--show"] |         profilecmds = ["--add", "--del", "--rm", "--mod", "--edit", "--show"] | ||||||
|         if len(sys.argv) >= 3 and sys.argv[2] == "profile" and sys.argv[1] in profilecmds: |         if len(sys.argv) >= 3 and sys.argv[2] == "profile" and sys.argv[1] in profilecmds: | ||||||
|             sys.argv[2] = sys.argv[1] |             sys.argv[2] = sys.argv[1] | ||||||
| @@ -68,8 +75,16 @@ class connapp: | |||||||
|         args = defaultparser.parse_args() |         args = defaultparser.parse_args() | ||||||
|         args.func(args) |         args.func(args) | ||||||
|  |  | ||||||
|  |     class store_type(argparse.Action): | ||||||
|  |         def __call__(self, parser, args, values, option_string=None): | ||||||
|  |             setattr(args, "data", values) | ||||||
|  |             delattr(args,self.dest) | ||||||
|  |             setattr(args, "command", self.dest) | ||||||
|  |  | ||||||
|     def _func_node(self, args): |     def _func_node(self, args): | ||||||
|         if args.action == "connect": |         if not self.case and args.data != None: | ||||||
|  |             args.data = args.data.lower() | ||||||
|  |         if args.action == "connect" or args.action == "debug": | ||||||
|             if args.data == None: |             if args.data == None: | ||||||
|                 matches = self.nodes |                 matches = self.nodes | ||||||
|             else: |             else: | ||||||
| @@ -78,26 +93,29 @@ class connapp: | |||||||
|                 else: |                 else: | ||||||
|                     matches = list(filter(lambda k: k.startswith(args.data), self.nodes)) |                     matches = list(filter(lambda k: k.startswith(args.data), self.nodes)) | ||||||
|             if len(matches) == 0: |             if len(matches) == 0: | ||||||
|                 print("ERROR NO MACHEA NI FOLDER NI NODE") |                 print("{} not found".format(args.data)) | ||||||
|                 return |                 exit(1) | ||||||
|             elif len(matches) > 1: |             elif len(matches) > 1: | ||||||
|                 matches[0] = self._choose(matches,"node", "connect") |                 matches[0] = self._choose(matches,"node", "connect") | ||||||
|             if matches[0] == None: |             if matches[0] == None: | ||||||
|                 return |                 exit(6) | ||||||
|             node = self._get_item(matches[0]) |             node = self.config.getitem(matches[0]) | ||||||
|             node = self.node(matches[0],**node, config = self.config) |             node = self.node(matches[0],**node, config = self.config) | ||||||
|  |             if args.action == "debug": | ||||||
|  |                 node.interact(debug = True) | ||||||
|  |             else: | ||||||
|                 node.interact() |                 node.interact() | ||||||
|         elif args.action == "del": |         elif args.action == "del": | ||||||
|             if args.data == None: |             if args.data == None: | ||||||
|                 print("MISSING ARGUMENT NODE") |                 print("Missing argument node") | ||||||
|                 return |                 exit(2) | ||||||
|             elif args.data.startswith("@"): |             elif args.data.startswith("@"): | ||||||
|                 matches = list(filter(lambda k: k == args.data, self.folders)) |                 matches = list(filter(lambda k: k == args.data, self.folders)) | ||||||
|             else: |             else: | ||||||
|                 matches = list(filter(lambda k: k == args.data, self.nodes)) |                 matches = list(filter(lambda k: k == args.data, self.nodes)) | ||||||
|             if len(matches) == 0: |             if len(matches) == 0: | ||||||
|                 print("ERROR NO MACHEO NI FOLDER NI NODE") |                 print("{} not found".format(args.data)) | ||||||
|                 return |                 exit(1) | ||||||
|             question = [inquirer.Confirm("delete", message="Are you sure you want to delete {}?".format(matches[0]))] |             question = [inquirer.Confirm("delete", message="Are you sure you want to delete {}?".format(matches[0]))] | ||||||
|             confirm = inquirer.prompt(question) |             confirm = inquirer.prompt(question) | ||||||
|             if confirm["delete"]: |             if confirm["delete"]: | ||||||
| @@ -110,28 +128,33 @@ class connapp: | |||||||
|                 print("{} deleted succesfully".format(matches[0])) |                 print("{} deleted succesfully".format(matches[0])) | ||||||
|         elif args.action == "add": |         elif args.action == "add": | ||||||
|             if args.data == None: |             if args.data == None: | ||||||
|                 print("MISSING ARGUMENT NODE") |                 print("Missing argument node") | ||||||
|                 return |                 exit(2) | ||||||
|             elif args.data.startswith("@"): |             elif args.data.startswith("@"): | ||||||
|                 type = "folder" |                 type = "folder" | ||||||
|                 matches = list(filter(lambda k: k == args.data, self.folders)) |                 matches = list(filter(lambda k: k == args.data, self.folders)) | ||||||
|  |                 reversematches = list(filter(lambda k: "@" + k == args.data, self.nodes)) | ||||||
|             else: |             else: | ||||||
|                 type = "node" |                 type = "node" | ||||||
|                 matches = list(filter(lambda k: k == args.data, self.nodes)) |                 matches = list(filter(lambda k: k == args.data, self.nodes)) | ||||||
|  |                 reversematches = list(filter(lambda k: k == "@" + args.data, self.folders)) | ||||||
|             if len(matches) > 0: |             if len(matches) > 0: | ||||||
|                 print(matches[0] + " ALLREADY EXIST") |                 print("{} already exist".format(matches[0])) | ||||||
|                 return |                 exit(3) | ||||||
|  |             if len(reversematches) > 0: | ||||||
|  |                 print("{} already exist".format(reversematches[0])) | ||||||
|  |                 exit(3) | ||||||
|             else: |             else: | ||||||
|                 if type == "folder": |                 if type == "folder": | ||||||
|                     uniques = self.config._explode_unique(args.data) |                     uniques = self.config._explode_unique(args.data) | ||||||
|                     if uniques == False: |                     if uniques == False: | ||||||
|                         print("Invalid folder {}".format(args.data)) |                         print("Invalid folder {}".format(args.data)) | ||||||
|                         return |                         exit(4) | ||||||
|                     if "subfolder" in uniques.keys(): |                     if "subfolder" in uniques.keys(): | ||||||
|                         parent = "@" + uniques["folder"] |                         parent = "@" + uniques["folder"] | ||||||
|                         if parent not in self.folders: |                         if parent not in self.folders: | ||||||
|                             print("FOLDER {} DONT EXIST".format(uniques["folder"])) |                             print("Folder {} not found".format(uniques["folder"])) | ||||||
|                             return |                             exit(1) | ||||||
|                     self.config._folder_add(**uniques) |                     self.config._folder_add(**uniques) | ||||||
|                     self.config.saveconfig(self.config.file) |                     self.config.saveconfig(self.config.file) | ||||||
|                     print("{} added succesfully".format(args.data)) |                     print("{} added succesfully".format(args.data)) | ||||||
| @@ -140,48 +163,48 @@ class connapp: | |||||||
|                     nodefolder = args.data.partition("@") |                     nodefolder = args.data.partition("@") | ||||||
|                     nodefolder = "@" + nodefolder[2] |                     nodefolder = "@" + nodefolder[2] | ||||||
|                     if nodefolder not in self.folders and nodefolder != "@": |                     if nodefolder not in self.folders and nodefolder != "@": | ||||||
|                         print(nodefolder + " DONT EXIST") |                         print(nodefolder + " not found") | ||||||
|                         return |                         exit(1) | ||||||
|                     uniques = self.config._explode_unique(args.data) |                     uniques = self.config._explode_unique(args.data) | ||||||
|                     if uniques == False: |                     if uniques == False: | ||||||
|                         print("Invalid node {}".format(args.data)) |                         print("Invalid node {}".format(args.data)) | ||||||
|                         return False |                         exit(4) | ||||||
|                     print("You can use the configured setting in a profile using @profilename.") |                     print("You can use the configured setting in a profile using @profilename.") | ||||||
|                     print("You can also leave empty any value except hostname/IP.") |                     print("You can also leave empty any value except hostname/IP.") | ||||||
|                     print("You can pass 1 or more passwords using comma separated @profiles") |                     print("You can pass 1 or more passwords using comma separated @profiles") | ||||||
|                     print("You can use this variables on logging file name: ${id} ${unique} ${host} ${port} ${user} ${protocol}") |                     print("You can use this variables on logging file name: ${id} ${unique} ${host} ${port} ${user} ${protocol}") | ||||||
|                     newnode = self._questions_nodes(args.data, uniques) |                     newnode = self._questions_nodes(args.data, uniques) | ||||||
|                     if newnode == False: |                     if newnode == False: | ||||||
|                         return |                         exit(6) | ||||||
|                     self.config._connections_add(**newnode) |                     self.config._connections_add(**newnode) | ||||||
|                     self.config.saveconfig(self.config.file) |                     self.config.saveconfig(self.config.file) | ||||||
|                     print("{} added succesfully".format(args.data)) |                     print("{} added succesfully".format(args.data)) | ||||||
|         elif args.action == "show": |         elif args.action == "show": | ||||||
|             if args.data == None: |             if args.data == None: | ||||||
|                 print("MISSING ARGUMENT NODE") |                 print("Missing argument node") | ||||||
|                 return |                 exit(2) | ||||||
|             matches = list(filter(lambda k: k == args.data, self.nodes)) |             matches = list(filter(lambda k: k == args.data, self.nodes)) | ||||||
|             if len(matches) == 0: |             if len(matches) == 0: | ||||||
|                 print("ERROR NO MACHEO NODE") |                 print("{} not found".format(args.data)) | ||||||
|                 return |                 exit(1) | ||||||
|             node = self._get_item(matches[0]) |             node = self.config.getitem(matches[0]) | ||||||
|             print(yaml.dump(node, Dumper=yaml.CDumper)) |             print(yaml.dump(node, Dumper=yaml.CDumper)) | ||||||
|         elif args.action == "mod": |         elif args.action == "mod": | ||||||
|             if args.data == None: |             if args.data == None: | ||||||
|                 print("MISSING ARGUMENT NODE") |                 print("Missing argument node") | ||||||
|                 return |                 exit(2) | ||||||
|             matches = list(filter(lambda k: k == args.data, self.nodes)) |             matches = list(filter(lambda k: k == args.data, self.nodes)) | ||||||
|             if len(matches) == 0: |             if len(matches) == 0: | ||||||
|                 print("ERROR NO MACHEO NODE") |                 print("{} not found".format(args.data)) | ||||||
|                 return |                 exit(1) | ||||||
|             node = self._get_item(matches[0]) |             node = self.config.getitem(matches[0]) | ||||||
|             edits = self._questions_edit() |             edits = self._questions_edit() | ||||||
|             if edits == None: |             if edits == None: | ||||||
|                 return |                 exit(6) | ||||||
|             uniques = self.config._explode_unique(args.data) |             uniques = self.config._explode_unique(args.data) | ||||||
|             updatenode = self._questions_nodes(args.data, uniques, edit=edits) |             updatenode = self._questions_nodes(args.data, uniques, edit=edits) | ||||||
|             if not updatenode: |             if not updatenode: | ||||||
|                 return |                 exit(6) | ||||||
|             uniques.update(node) |             uniques.update(node) | ||||||
|             if sorted(updatenode.items()) == sorted(uniques.items()): |             if sorted(updatenode.items()) == sorted(uniques.items()): | ||||||
|                 print("Nothing to do here") |                 print("Nothing to do here") | ||||||
| @@ -193,14 +216,16 @@ class connapp: | |||||||
|  |  | ||||||
|  |  | ||||||
|     def _func_profile(self, args): |     def _func_profile(self, args): | ||||||
|  |         if not self.case: | ||||||
|  |             args.data[0] = args.data[0].lower() | ||||||
|         if args.action == "del": |         if args.action == "del": | ||||||
|             matches = list(filter(lambda k: k == args.data[0], self.profiles)) |             matches = list(filter(lambda k: k == args.data[0], self.profiles)) | ||||||
|             if len(matches) == 0: |             if len(matches) == 0: | ||||||
|                 print("ERROR NO MACHEO PROFILE") |                 print("{} not found".format(args.data[0])) | ||||||
|                 return |                 exit(1) | ||||||
|             if matches[0] == "default": |             if matches[0] == "default": | ||||||
|                 print("CANT DELETE DEFAULT PROFILE") |                 print("Can't delete default profile") | ||||||
|                 return |                 exit(5) | ||||||
|             question = [inquirer.Confirm("delete", message="Are you sure you want to delete {}?".format(matches[0]))] |             question = [inquirer.Confirm("delete", message="Are you sure you want to delete {}?".format(matches[0]))] | ||||||
|             confirm = inquirer.prompt(question) |             confirm = inquirer.prompt(question) | ||||||
|             if confirm["delete"]: |             if confirm["delete"]: | ||||||
| @@ -210,35 +235,35 @@ class connapp: | |||||||
|         elif args.action == "show": |         elif args.action == "show": | ||||||
|             matches = list(filter(lambda k: k == args.data[0], self.profiles)) |             matches = list(filter(lambda k: k == args.data[0], self.profiles)) | ||||||
|             if len(matches) == 0: |             if len(matches) == 0: | ||||||
|                 print("ERROR NO MACHEO PROFILE") |                 print("{} not found".format(args.data[0])) | ||||||
|                 return |                 exit(1) | ||||||
|             profile = self.config.profiles[matches[0]] |             profile = self.config.profiles[matches[0]] | ||||||
|             print(yaml.dump(profile, Dumper=yaml.CDumper)) |             print(yaml.dump(profile, Dumper=yaml.CDumper)) | ||||||
|         elif args.action == "add": |         elif args.action == "add": | ||||||
|             matches = list(filter(lambda k: k == args.data[0], self.profiles)) |             matches = list(filter(lambda k: k == args.data[0], self.profiles)) | ||||||
|             if len(matches) > 0: |             if len(matches) > 0: | ||||||
|                 print("Profile {} Already exist".format(matches[0])) |                 print("Profile {} Already exist".format(matches[0])) | ||||||
|                 return |                 exit(3) | ||||||
|             newprofile = self._questions_profiles(args.data[0]) |             newprofile = self._questions_profiles(args.data[0]) | ||||||
|             if newprofile == False: |             if newprofile == False: | ||||||
|                 return |                 exit(6) | ||||||
|             self.config._profiles_add(**newprofile) |             self.config._profiles_add(**newprofile) | ||||||
|             self.config.saveconfig(self.config.file) |             self.config.saveconfig(self.config.file) | ||||||
|             print("{} added succesfully".format(args.data[0])) |             print("{} added succesfully".format(args.data[0])) | ||||||
|         elif args.action == "mod": |         elif args.action == "mod": | ||||||
|             matches = list(filter(lambda k: k == args.data[0], self.profiles)) |             matches = list(filter(lambda k: k == args.data[0], self.profiles)) | ||||||
|             if len(matches) == 0: |             if len(matches) == 0: | ||||||
|                 print("ERROR NO MACHEO PROFILE") |                 print("{} not found".format(args.data[0])) | ||||||
|                 return |                 exit(1) | ||||||
|             profile = self.config.profiles[matches[0]] |             profile = self.config.profiles[matches[0]] | ||||||
|             oldprofile = {"id": matches[0]} |             oldprofile = {"id": matches[0]} | ||||||
|             oldprofile.update(profile) |             oldprofile.update(profile) | ||||||
|             edits = self._questions_edit() |             edits = self._questions_edit() | ||||||
|             if edits == None: |             if edits == None: | ||||||
|                 return |                 exit(6) | ||||||
|             updateprofile = self._questions_profiles(matches[0], edit=edits) |             updateprofile = self._questions_profiles(matches[0], edit=edits) | ||||||
|             if not updateprofile: |             if not updateprofile: | ||||||
|                 return |                 exit(6) | ||||||
|             if sorted(updateprofile.items()) == sorted(oldprofile.items()): |             if sorted(updateprofile.items()) == sorted(oldprofile.items()): | ||||||
|                 print("Nothing to do here") |                 print("Nothing to do here") | ||||||
|                 return |                 return | ||||||
| @@ -251,25 +276,28 @@ class connapp: | |||||||
|         if args.command == "ls": |         if args.command == "ls": | ||||||
|             print(*getattr(self, args.data), sep="\n") |             print(*getattr(self, args.data), sep="\n") | ||||||
|         elif args.command == "move" or args.command == "cp": |         elif args.command == "move" or args.command == "cp": | ||||||
|  |             if not self.case: | ||||||
|  |                 args.data[0] = args.data[0].lower() | ||||||
|  |                 args.data[1] = args.data[1].lower() | ||||||
|             source = list(filter(lambda k: k == args.data[0], self.nodes)) |             source = list(filter(lambda k: k == args.data[0], self.nodes)) | ||||||
|             dest = list(filter(lambda k: k == args.data[1], self.nodes)) |             dest = list(filter(lambda k: k == args.data[1], self.nodes)) | ||||||
|             if len(source) != 1: |             if len(source) != 1: | ||||||
|                 print("ERROR NO MACHEO NODE {}".format(args.data[0])) |                 print("{} not found".format(args.data[0])) | ||||||
|                 return |                 exit(1) | ||||||
|             if len(dest) > 0: |             if len(dest) > 0: | ||||||
|                 print("{} ALREADY EXIST".format(args.data[1])) |                 print("Node {} Already exist".format(args.data[1])) | ||||||
|                 return |                 exit(3) | ||||||
|             nodefolder = args.data[1].partition("@") |             nodefolder = args.data[1].partition("@") | ||||||
|             nodefolder = "@" + nodefolder[2] |             nodefolder = "@" + nodefolder[2] | ||||||
|             if nodefolder not in self.folders and nodefolder != "@": |             if nodefolder not in self.folders and nodefolder != "@": | ||||||
|                 print(nodefolder + " DONT EXIST") |                 print("{} not found".format(nodefolder)) | ||||||
|                 return |                 exit(1) | ||||||
|             olduniques = self.config._explode_unique(args.data[0]) |             olduniques = self.config._explode_unique(args.data[0]) | ||||||
|             newuniques = self.config._explode_unique(args.data[1]) |             newuniques = self.config._explode_unique(args.data[1]) | ||||||
|             if newuniques == False: |             if newuniques == False: | ||||||
|                 print("Invalid node {}".format(args.data[1])) |                 print("Invalid node {}".format(args.data[1])) | ||||||
|                 return False |                 exit(4) | ||||||
|             node = self._get_item(source[0]) |             node = self.config.getitem(source[0]) | ||||||
|             newnode = {**newuniques, **node} |             newnode = {**newuniques, **node} | ||||||
|             self.config._connections_add(**newnode) |             self.config._connections_add(**newnode) | ||||||
|             if args.command == "move": |             if args.command == "move": | ||||||
| @@ -280,11 +308,60 @@ class connapp: | |||||||
|             if args.command == "cp": |             if args.command == "cp": | ||||||
|                 print("{} copied succesfully to {}".format(args.data[0],args.data[1])) |                 print("{} copied succesfully to {}".format(args.data[0],args.data[1])) | ||||||
|         elif args.command == "bulk": |         elif args.command == "bulk": | ||||||
|             test = self._questions_bulk() |             newnodes = self._questions_bulk() | ||||||
|             print(test) |             if newnodes == False: | ||||||
|  |                 exit(6) | ||||||
|  |             if not self.case: | ||||||
|  |                 newnodes["location"] = newnodes["location"].lower() | ||||||
|  |                 newnodes["ids"] = newnodes["ids"].lower() | ||||||
|  |             ids = newnodes["ids"].split(",") | ||||||
|  |             hosts = newnodes["host"].split(",") | ||||||
|  |             count = 0 | ||||||
|  |             for n in ids: | ||||||
|  |                 unique = n + newnodes["location"] | ||||||
|  |                 matches = list(filter(lambda k: k == unique, self.nodes)) | ||||||
|  |                 reversematches = list(filter(lambda k: k == "@" + unique, self.folders)) | ||||||
|  |                 if len(matches) > 0: | ||||||
|  |                     print("Node {} already exist, ignoring it".format(unique)) | ||||||
|  |                     continue | ||||||
|  |                 if len(reversematches) > 0: | ||||||
|  |                     print("Folder with name {} already exist, ignoring it".format(unique)) | ||||||
|  |                     continue | ||||||
|  |                 newnode = {"id": n} | ||||||
|  |                 if newnodes["location"] != "": | ||||||
|  |                     location = self.config._explode_unique(newnodes["location"]) | ||||||
|  |                     newnode.update(location) | ||||||
|  |                 if len(hosts) > 1: | ||||||
|  |                     index = ids.index(n) | ||||||
|  |                     newnode["host"] = hosts[index] | ||||||
|                 else: |                 else: | ||||||
|             print(args.command) |                     newnode["host"] = hosts[0] | ||||||
|             print(vars(args)) |                 newnode["protocol"] = newnodes["protocol"] | ||||||
|  |                 newnode["port"] = newnodes["port"] | ||||||
|  |                 newnode["options"] = newnodes["options"] | ||||||
|  |                 newnode["logs"] = newnodes["logs"] | ||||||
|  |                 newnode["user"] = newnodes["user"] | ||||||
|  |                 newnode["password"] = newnodes["password"] | ||||||
|  |                 count +=1 | ||||||
|  |                 self.config._connections_add(**newnode) | ||||||
|  |                 self.nodes = self._getallnodes() | ||||||
|  |             if count > 0: | ||||||
|  |                 self.config.saveconfig(self.config.file) | ||||||
|  |                 print("Succesfully added {} nodes".format(count)) | ||||||
|  |             else: | ||||||
|  |                 print("0 nodes added") | ||||||
|  |         else: | ||||||
|  |             if args.command == "case": | ||||||
|  |                 if args.data[0] == "true": | ||||||
|  |                     args.data[0] = True | ||||||
|  |                 elif args.data[0] == "false": | ||||||
|  |                     args.data[0] = False | ||||||
|  |             if args.command == "idletime": | ||||||
|  |                 if args.data[0] < 0: | ||||||
|  |                     args.data[0] = 0 | ||||||
|  |             self.config.config[args.command] = args.data[0] | ||||||
|  |             self.config.saveconfig(self.config.file) | ||||||
|  |             print("Config saved") | ||||||
|  |  | ||||||
|     def _choose(self, list, name, action): |     def _choose(self, list, name, action): | ||||||
|         questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list)] |         questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list)] | ||||||
| @@ -362,7 +439,8 @@ class connapp: | |||||||
|         return True |         return True | ||||||
|  |  | ||||||
|     def _bulk_folder_validation(self, answers, current): |     def _bulk_folder_validation(self, answers, current): | ||||||
|  |         if not self.case: | ||||||
|  |             current = current.lower() | ||||||
|         matches = list(filter(lambda k: k == current, self.folders)) |         matches = list(filter(lambda k: k == current, self.folders)) | ||||||
|         if current != "" and len(matches) == 0: |         if current != "" and len(matches) == 0: | ||||||
|             raise inquirer.errors.ValidationError("", reason="Location {} don't exist".format(current)) |             raise inquirer.errors.ValidationError("", reason="Location {} don't exist".format(current)) | ||||||
| @@ -394,7 +472,7 @@ class connapp: | |||||||
|  |  | ||||||
|     def _questions_nodes(self, unique, uniques = None, edit = None): |     def _questions_nodes(self, unique, uniques = None, edit = None): | ||||||
|         try: |         try: | ||||||
|             defaults = self._get_item(unique) |             defaults = self.config.getitem(unique) | ||||||
|         except: |         except: | ||||||
|             defaults = { "host":"", "protocol":"", "port":"", "user":"", "options":"", "logs":"" } |             defaults = { "host":"", "protocol":"", "port":"", "user":"", "options":"", "logs":"" } | ||||||
|         node = {} |         node = {} | ||||||
| @@ -448,23 +526,6 @@ class connapp: | |||||||
|         result["type"] = "connection" |         result["type"] = "connection" | ||||||
|         return result |         return result | ||||||
|  |  | ||||||
|     def _get_item(self, unique): |  | ||||||
|             uniques = self.config._explode_unique(unique) |  | ||||||
|             if unique.startswith("@"): |  | ||||||
|                 if uniques.keys() >= {"folder", "subfolder"}: |  | ||||||
|                     folder = self.config.connections[uniques["folder"]][uniques["subfolder"]] |  | ||||||
|                 else: |  | ||||||
|                     folder = self.config.connections[uniques["folder"]] |  | ||||||
|                 return folder |  | ||||||
|             else: |  | ||||||
|                 if uniques.keys() >= {"folder", "subfolder"}: |  | ||||||
|                     node = self.config.connections[uniques["folder"]][uniques["subfolder"]][uniques["id"]] |  | ||||||
|                 elif "folder" in uniques.keys(): |  | ||||||
|                     node = self.config.connections[uniques["folder"]][uniques["id"]] |  | ||||||
|                 else: |  | ||||||
|                     node = self.config.connections[uniques["id"]] |  | ||||||
|                 return node |  | ||||||
|  |  | ||||||
|     def _questions_profiles(self, unique, edit = None): |     def _questions_profiles(self, unique, edit = None): | ||||||
|         try: |         try: | ||||||
|             defaults = self.config.profiles[unique] |             defaults = self.config.profiles[unique] | ||||||
| @@ -550,12 +611,6 @@ class connapp: | |||||||
|             raise argparse.ArgumentTypeError |             raise argparse.ArgumentTypeError | ||||||
|         return arg_value |         return arg_value | ||||||
|  |  | ||||||
|     class store_type(argparse.Action): |  | ||||||
|         def __call__(self, parser, args, values, option_string=None): |  | ||||||
|             setattr(args, "data", values) |  | ||||||
|             delattr(args,self.dest) |  | ||||||
|             setattr(args, "command", self.dest) |  | ||||||
|  |  | ||||||
|     def _help(self, type): |     def _help(self, type): | ||||||
|         if type == "node": |         if type == "node": | ||||||
|             return "node[@subfolder][@folder]\nConnect to specific node or show all matching nodes\n[@subfolder][@folder]\nShow all available connections globaly or in specified path" |             return "node[@subfolder][@folder]\nConnect to specific node or show all matching nodes\n[@subfolder][@folder]\nShow all available connections globaly or in specified path" | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								conn/core.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								conn/core.py
									
									
									
									
									
								
							| @@ -45,11 +45,10 @@ class node: | |||||||
|             self.password = [password] |             self.password = [password] | ||||||
|  |  | ||||||
|     def __passtx(self, passwords, *, keyfile=None): |     def __passtx(self, passwords, *, keyfile=None): | ||||||
|         keyfile = self.key |  | ||||||
|         dpass = [] |         dpass = [] | ||||||
|         if keyfile is None: |         if keyfile is None: | ||||||
|             keyfile = self.key |             keyfile = self.key | ||||||
|         else: |         if keyfile is not None: | ||||||
|             key = RSA.import_key(open(keyfile).read()) |             key = RSA.import_key(open(keyfile).read()) | ||||||
|             decryptor = PKCS1_OAEP.new(key) |             decryptor = PKCS1_OAEP.new(key) | ||||||
|         for passwd in passwords: |         for passwd in passwords: | ||||||
| @@ -60,8 +59,7 @@ class node: | |||||||
|                     decrypted = decryptor.decrypt(ast.literal_eval(str(passwd))).decode("utf-8") |                     decrypted = decryptor.decrypt(ast.literal_eval(str(passwd))).decode("utf-8") | ||||||
|                     dpass.append(decrypted) |                     dpass.append(decrypted) | ||||||
|                 except: |                 except: | ||||||
|                     print("Missing or wrong key") |                     raise ValueError("Missing or corrupted key") | ||||||
|                     exit(1) |  | ||||||
|         return dpass |         return dpass | ||||||
|  |  | ||||||
|      |      | ||||||
| @@ -108,15 +106,18 @@ class node: | |||||||
|         connect = self._connect(debug = debug) |         connect = self._connect(debug = debug) | ||||||
|         if connect == True: |         if connect == True: | ||||||
|             print("Connected to " + self.unique + " at " + self.host + (":" if self.port != '' else '') + self.port + " via: " + self.protocol) |             print("Connected to " + self.unique + " at " + self.host + (":" if self.port != '' else '') + self.port + " via: " + self.protocol) | ||||||
|             if debug: |             if 'logfile' in dir(self): | ||||||
|                 self.child.logfile_read = None |  | ||||||
|             elif 'logfile' in dir(self): |  | ||||||
|                 self.child.logfile_read = open(self.logfile, "wb") |                 self.child.logfile_read = open(self.logfile, "wb") | ||||||
|  |             elif debug: | ||||||
|  |                 self.child.logfile_read = None | ||||||
|             if 'missingtext' in dir(self): |             if 'missingtext' in dir(self): | ||||||
|                 print(self.child.after.decode(), end='') |                 print(self.child.after.decode(), end='') | ||||||
|             self.child.interact() |             self.child.interact() | ||||||
|             if "logfile" in dir(self) and not debug: |             if "logfile" in dir(self) and not debug: | ||||||
|                 self._logclean(self.logfile) |                 self._logclean(self.logfile) | ||||||
|  |         else: | ||||||
|  |             print(connect) | ||||||
|  |             exit(7) | ||||||
|  |  | ||||||
|     def run(self, commands,*, folder = '', prompt = '>$|#$|\$.$', stdout = False): |     def run(self, commands,*, folder = '', prompt = '>$|#$|\$.$', stdout = False): | ||||||
|         connect = self._connect() |         connect = self._connect() | ||||||
| @@ -135,10 +136,9 @@ class node: | |||||||
|                 output = output + self.child.before.decode() + self.child.after.decode() |                 output = output + self.child.before.decode() + self.child.after.decode() | ||||||
|             self.child.expect(prompt) |             self.child.expect(prompt) | ||||||
|             output = output + self.child.before.decode() + self.child.after.decode() |             output = output + self.child.before.decode() + self.child.after.decode() | ||||||
|             if folder == '': |  | ||||||
|             if stdout == True: |             if stdout == True: | ||||||
|                 print(output) |                 print(output) | ||||||
|             else: |             if folder != '': | ||||||
|                 with open(folder + "/" + self.unique, "w") as f: |                 with open(folder + "/" + self.unique, "w") as f: | ||||||
|                     f.write(output) |                     f.write(output) | ||||||
|                     f.close() |                     f.close() | ||||||
| @@ -146,8 +146,6 @@ class node: | |||||||
|             self.output = output |             self.output = output | ||||||
|             return output |             return output | ||||||
|  |  | ||||||
|              |  | ||||||
|  |  | ||||||
|     def _connect(self, debug = False): |     def _connect(self, debug = False): | ||||||
|         if self.protocol == "ssh": |         if self.protocol == "ssh": | ||||||
|             cmd = "ssh" |             cmd = "ssh" | ||||||
| @@ -182,8 +180,7 @@ class node: | |||||||
|                 passwords = [] |                 passwords = [] | ||||||
|             expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:', '>$|#$|\$.$', 'suspend', pexpect.EOF, "No route to host"] |             expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:', '>$|#$|\$.$', 'suspend', pexpect.EOF, "No route to host"] | ||||||
|         else: |         else: | ||||||
|             print("Invalid protocol: " + self.protocol) |             raise ValueError("Invalid protocol: " + self.protocol) | ||||||
|             return |  | ||||||
|         child = pexpect.spawn(cmd) |         child = pexpect.spawn(cmd) | ||||||
|         if debug: |         if debug: | ||||||
|             child.logfile_read = sys.stdout.buffer |             child.logfile_read = sys.stdout.buffer | ||||||
| @@ -206,9 +203,8 @@ class node: | |||||||
|                                 self.missingtext = True |                                 self.missingtext = True | ||||||
|                                 break |                                 break | ||||||
|                     case 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12: |                     case 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12: | ||||||
|                         print("Connection failed code:" + str(results)) |  | ||||||
|                         child.close() |                         child.close() | ||||||
|                         return |                         return "Connection failed code:" + str(results) | ||||||
|                     case 8: |                     case 8: | ||||||
|                         if len(passwords) > 0: |                         if len(passwords) > 0: | ||||||
|                             child.sendline(passwords[i]) |                             child.sendline(passwords[i]) | ||||||
|   | |||||||
| @@ -1,21 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
| #Imports |  | ||||||
| import os |  | ||||||
| from Crypto.PublicKey import RSA |  | ||||||
| from Crypto.Cipher import PKCS1_OAEP |  | ||||||
| import ast |  | ||||||
|  |  | ||||||
| #functions and classes |  | ||||||
|  |  | ||||||
| def encrypt(password, keyfile=None): |  | ||||||
|     if keyfile is None: |  | ||||||
|         home = os.path.expanduser("~") |  | ||||||
|         keyfile = home + '/.config/conn/.osk' |  | ||||||
|     key = RSA.import_key(open(keyfile).read()) |  | ||||||
|     publickey = key.publickey() |  | ||||||
|     encryptor = PKCS1_OAEP.new(publickey) |  | ||||||
|     password = encryptor.encrypt(password.encode("utf-8")) |  | ||||||
|     return password |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								test.py
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								test.py
									
									
									
									
									
								
							| @@ -19,15 +19,15 @@ conf = conn.configfile("test.yaml") | |||||||
| # conf.saveconfig("test.yaml") | # conf.saveconfig("test.yaml") | ||||||
| # *** | # *** | ||||||
| # test = conn.node("test", "10.21.96.45") | # test = conn.node("test", "10.21.96.45") | ||||||
| # xr=conn.node("xr@home", **conf.connections["home"]["xr"], config=conf) | # xr=conn.node("xr@home", **conf.getitem("xr@home"), config=conf) | ||||||
| # ios=conn.node("ios@home", **conf.connections["home"]["ios"], config=conf) | # ios=conn.node("ios@home", **conf.connections["home"]["ios"], config=conf) | ||||||
| # norman = conn.node("norman@home", **conf.connections["home"]["norman"], config=conf) | # norman = conn.node("norman@home", **conf.connections["home"]["norman"], config=conf) | ||||||
| # eve = conn.node("eve@home", **conf.connections["home"]["eve"], config=conf) | # eve = conn.node("eve@home", **conf.connections["home"]["eve"], config=conf) | ||||||
| # router228 = conn.node("router228@bbva", **conf.connections["bbva"]["router228"], config=conf) | # router228 = conn.node("router228@bbva", **conf.connections["bbva"]["router228"], config=conf) | ||||||
| # router228.interact() | # router228.interact() | ||||||
| # router228.run(["term len 0","show ip int br"]) | # router228.run(["term len 0","show ip int br"]) | ||||||
| # xroutput = xr.run(["show ip bgp", "show ip bgp summ"], folder="test") | # xroutput = xr.run("show run") | ||||||
| # ios.run("show run") | # ios.run("show run", folder=".",stdout=True) | ||||||
| # norman.run(["ls -la", "pwd"]) | # norman.run(["ls -la", "pwd"]) | ||||||
| # test = eve.run(["ls -la", "pwd"]) | # test = eve.run(["ls -la", "pwd"]) | ||||||
| # print(norman.output) | # print(norman.output) | ||||||
| @@ -36,3 +36,10 @@ conf = conn.configfile("test.yaml") | |||||||
| # test.interact() | # test.interact() | ||||||
| # *** | # *** | ||||||
| conn.connapp(conf, conn.node) | conn.connapp(conf, conn.node) | ||||||
|  | # *** | ||||||
|  | # list = ["xr@home","ios@home","router228@bbva","router142@bbva"] | ||||||
|  | # for i in list: | ||||||
|  |     # data = conf.getitem(i) | ||||||
|  |     # routeri = conn.node(i,**data,config=conf) | ||||||
|  |     # routeri.run(["term len 0","show run"], folder="test") | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user