main app done
This commit is contained in:
parent
43e8325890
commit
1c6bdddbdc
@ -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")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user