change yaml to json for speed. Add completion
This commit is contained in:
parent
9f3cb6f6d9
commit
8f13b0b2bf
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#Imports
|
#Imports
|
||||||
import yaml
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
@ -14,7 +14,7 @@ class configfile:
|
|||||||
def __init__(self, conf = None, *, key = None):
|
def __init__(self, conf = None, *, key = None):
|
||||||
home = os.path.expanduser("~")
|
home = os.path.expanduser("~")
|
||||||
self.defaultdir = home + '/.config/conn'
|
self.defaultdir = home + '/.config/conn'
|
||||||
self.defaultfile = self.defaultdir + '/config.yaml'
|
self.defaultfile = self.defaultdir + '/config.json'
|
||||||
self.defaultkey = self.defaultdir + '/.osk'
|
self.defaultkey = self.defaultdir + '/.osk'
|
||||||
Path(self.defaultdir).mkdir(parents=True, exist_ok=True)
|
Path(self.defaultdir).mkdir(parents=True, exist_ok=True)
|
||||||
if conf == None:
|
if conf == None:
|
||||||
@ -39,18 +39,18 @@ class configfile:
|
|||||||
|
|
||||||
|
|
||||||
def loadconfig(self, conf):
|
def loadconfig(self, conf):
|
||||||
ymlconf = open(conf)
|
jsonconf = open(conf)
|
||||||
return yaml.load(ymlconf.read(), Loader=yaml.CLoader)
|
return json.load(jsonconf)
|
||||||
|
|
||||||
def createconfig(self, conf):
|
def createconfig(self, conf):
|
||||||
defaultconfig = {'config': {'case': 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)
|
json.dump(defaultconfig, f, indent = 4)
|
||||||
f.close()
|
f.close()
|
||||||
os.chmod(conf, 0o600)
|
os.chmod(conf, 0o600)
|
||||||
ymlconf = open(conf)
|
jsonconf = open(conf)
|
||||||
return yaml.load(ymlconf.read(), Loader=yaml.CLoader)
|
return json.load(jsonconf)
|
||||||
|
|
||||||
def saveconfig(self, conf):
|
def saveconfig(self, conf):
|
||||||
newconfig = {"config":{}, "connections": {}, "profiles": {}}
|
newconfig = {"config":{}, "connections": {}, "profiles": {}}
|
||||||
@ -58,7 +58,7 @@ class configfile:
|
|||||||
newconfig["connections"] = self.connections
|
newconfig["connections"] = self.connections
|
||||||
newconfig["profiles"] = self.profiles
|
newconfig["profiles"] = self.profiles
|
||||||
with open(conf, "w") as f:
|
with open(conf, "w") as f:
|
||||||
yaml.dump(newconfig, f, explicit_start=True, Dumper=yaml.CDumper)
|
json.dump(newconfig, f, indent = 4)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def createkey(self, keyfile):
|
def createkey(self, keyfile):
|
||||||
|
@ -8,7 +8,7 @@ import ast
|
|||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
import inquirer
|
import inquirer
|
||||||
import yaml
|
import json
|
||||||
|
|
||||||
#functions and classes
|
#functions and classes
|
||||||
|
|
||||||
@ -63,6 +63,7 @@ class connapp:
|
|||||||
configparser = subparsers.add_parser("config", help="Manage app config")
|
configparser = subparsers.add_parser("config", help="Manage app config")
|
||||||
configparser.add_argument("--allow-uppercase", dest="case", nargs=1, action=self.store_type, help="Allow case sensitive names", choices=["true","false"])
|
configparser.add_argument("--allow-uppercase", dest="case", nargs=1, action=self.store_type, help="Allow case sensitive names", choices=["true","false"])
|
||||||
configparser.add_argument("--keepalive", dest="idletime", nargs=1, action=self.store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT")
|
configparser.add_argument("--keepalive", dest="idletime", nargs=1, action=self.store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT")
|
||||||
|
configparser.add_argument("--completion", dest="completion", nargs=0, action=self.store_type, help="Get bash completion configuration for conn")
|
||||||
configparser.set_defaults(func=self._func_others)
|
configparser.set_defaults(func=self._func_others)
|
||||||
#Set default subparser and tune arguments
|
#Set default subparser and tune arguments
|
||||||
commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "config"]
|
commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "config"]
|
||||||
@ -188,7 +189,13 @@ class connapp:
|
|||||||
print("{} not found".format(args.data))
|
print("{} not found".format(args.data))
|
||||||
exit(2)
|
exit(2)
|
||||||
node = self.config.getitem(matches[0])
|
node = self.config.getitem(matches[0])
|
||||||
print(yaml.dump(node, Dumper=yaml.CDumper))
|
for k, v in node.items():
|
||||||
|
if isinstance(v, str):
|
||||||
|
print(k + ": " + v)
|
||||||
|
else:
|
||||||
|
print(k + ":")
|
||||||
|
for i in v:
|
||||||
|
print(" - " + i)
|
||||||
elif args.action == "mod":
|
elif args.action == "mod":
|
||||||
if args.data == None:
|
if args.data == None:
|
||||||
print("Missing argument node")
|
print("Missing argument node")
|
||||||
@ -243,7 +250,13 @@ class connapp:
|
|||||||
print("{} not found".format(args.data[0]))
|
print("{} not found".format(args.data[0]))
|
||||||
exit(2)
|
exit(2)
|
||||||
profile = self.config.profiles[matches[0]]
|
profile = self.config.profiles[matches[0]]
|
||||||
print(yaml.dump(profile, Dumper=yaml.CDumper))
|
for k, v in profile.items():
|
||||||
|
if isinstance(v, str):
|
||||||
|
print(k + ": " + v)
|
||||||
|
else:
|
||||||
|
print(k + ":")
|
||||||
|
for i in v:
|
||||||
|
print(" - " + i)
|
||||||
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:
|
||||||
@ -356,17 +369,20 @@ class connapp:
|
|||||||
else:
|
else:
|
||||||
print("0 nodes added")
|
print("0 nodes added")
|
||||||
else:
|
else:
|
||||||
if args.command == "case":
|
if args.command == "completion":
|
||||||
if args.data[0] == "true":
|
print(self._help("completion"))
|
||||||
args.data[0] = True
|
else:
|
||||||
elif args.data[0] == "false":
|
if args.command == "case":
|
||||||
args.data[0] = False
|
if args.data[0] == "true":
|
||||||
if args.command == "idletime":
|
args.data[0] = True
|
||||||
if args.data[0] < 0:
|
elif args.data[0] == "false":
|
||||||
args.data[0] = 0
|
args.data[0] = False
|
||||||
self.config.config[args.command] = args.data[0]
|
if args.command == "idletime":
|
||||||
self.config.saveconfig(self.config.file)
|
if args.data[0] < 0:
|
||||||
print("Config saved")
|
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, carousel=True)]
|
questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list, carousel=True)]
|
||||||
@ -627,6 +643,53 @@ class connapp:
|
|||||||
return "conn [-h] [--add | --del | --mod | --show | --debug] [node|folder]\n conn {profile,move,mv,copy,cp,list,ls,bulk,config} ..."
|
return "conn [-h] [--add | --del | --mod | --show | --debug] [node|folder]\n conn {profile,move,mv,copy,cp,list,ls,bulk,config} ..."
|
||||||
if type == "end":
|
if type == "end":
|
||||||
return "Commands:\n profile Manage profiles\n move (mv) Move node\n copy (cp) Copy node\n list (ls) List profiles, nodes or folders\n bulk Add nodes in bulk\n config Manage app config"
|
return "Commands:\n profile Manage profiles\n move (mv) Move node\n copy (cp) Copy node\n list (ls) List profiles, nodes or folders\n bulk Add nodes in bulk\n config Manage app config"
|
||||||
|
if type == "completion":
|
||||||
|
return '''
|
||||||
|
#Here starts bash completion for conn
|
||||||
|
#You need jq installed in order to use this
|
||||||
|
_conn()
|
||||||
|
{
|
||||||
|
|
||||||
|
DATADIR=$HOME/.config/conn
|
||||||
|
mapfile -t connections < <(jq -r ' .["connections"] | paths as $path | select(getpath($path) == "connection") | $path | [map(select(. != "type"))[-1,-2,-3]] | map(select(. !=null)) | join("@")' $DATADIR/config.json)
|
||||||
|
mapfile -t folders < <(jq -r ' .["connections"] | paths as $path | select(getpath($path) == "folder" or getpath($path) == "subfolder") | $path | [map(select(. != "type"))[-1,-2]] | map(select(. !=null)) | join("@")' $DATADIR/config.json)
|
||||||
|
mapfile -t profiles < <(jq -r '.["profiles"] | keys[]' $DATADIR/config.json)
|
||||||
|
if [ "${#COMP_WORDS[@]}" = "2" ]; then
|
||||||
|
strings="--add --del --rm --edit --mod mv --show ls cp profile bulk config --help"
|
||||||
|
strings="$strings ${connections[@]} ${folders[@]/#/@}"
|
||||||
|
COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[1]}"))
|
||||||
|
fi
|
||||||
|
if [ "${#COMP_WORDS[@]}" = "3" ]; then
|
||||||
|
strings=""
|
||||||
|
if [ "${COMP_WORDS[1]}" = "profile" ]; then strings="--add --rm --del --edit --mod --show --help"; fi
|
||||||
|
if [ "${COMP_WORDS[1]}" = "config" ]; then strings="--allow-uppercase --keepalive --completion --help"; fi
|
||||||
|
if [[ "${COMP_WORDS[1]}" =~ ^--mod|--edit|--show|--add|--rm|--del$ ]]; then strings="profile"; fi
|
||||||
|
if [[ "${COMP_WORDS[1]}" =~ ^list|ls$ ]]; then strings="profiles nodes folders"; fi
|
||||||
|
if [[ "${COMP_WORDS[1]}" =~ ^bulk|mv|move|cp|copy$$ ]]; then strings="--help"; fi
|
||||||
|
if [[ "${COMP_WORDS[1]}" =~ ^--rm|--del$ ]]; then strings="$strings ${folders[@]/#/@}"; fi
|
||||||
|
if [[ "${COMP_WORDS[1]}" =~ ^--rm|--del|--mod|--edit|mv|move|cp|copy|--show$ ]]; then
|
||||||
|
strings="$strings ${connections[@]}"
|
||||||
|
fi
|
||||||
|
COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[2]}"))
|
||||||
|
fi
|
||||||
|
if [ "${#COMP_WORDS[@]}" = "4" ]; then
|
||||||
|
strings=""
|
||||||
|
if [ "${COMP_WORDS[1]}" = "profile" ]; then
|
||||||
|
if [[ "${COMP_WORDS[2]}" =~ ^--rm|--del|--mod|--edit|--show$ ]] ; then
|
||||||
|
strings="$strings ${profiles[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "${COMP_WORDS[2]}" = "profile" ]; then
|
||||||
|
if [[ "${COMP_WORDS[1]}" =~ ^--rm|--remove|--del|--mod|--edit|--show$ ]] ; then
|
||||||
|
strings="$strings ${profiles[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[3]}"))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
complete -o nosort -F _conn conn
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
def _getallnodes(self):
|
def _getallnodes(self):
|
||||||
nodes = []
|
nodes = []
|
||||||
@ -672,4 +735,4 @@ class connapp:
|
|||||||
publickey = key.publickey()
|
publickey = key.publickey()
|
||||||
encryptor = PKCS1_OAEP.new(publickey)
|
encryptor = PKCS1_OAEP.new(publickey)
|
||||||
password = encryptor.encrypt(password.encode("utf-8"))
|
password = encryptor.encrypt(password.encode("utf-8"))
|
||||||
return password
|
return str(password)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#Imports
|
#Imports
|
||||||
import yaml
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import pexpect
|
import pexpect
|
||||||
@ -52,11 +51,11 @@ class node:
|
|||||||
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:
|
||||||
if isinstance(passwd, str):
|
if not re.match('^b[\"\'].+[\"\']$', passwd):
|
||||||
dpass.append(passwd)
|
dpass.append(passwd)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
decrypted = decryptor.decrypt(ast.literal_eval(str(passwd))).decode("utf-8")
|
decrypted = decryptor.decrypt(ast.literal_eval(passwd)).decode("utf-8")
|
||||||
dpass.append(decrypted)
|
dpass.append(decrypted)
|
||||||
except:
|
except:
|
||||||
raise ValueError("Missing or corrupted key")
|
raise ValueError("Missing or corrupted key")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
inquirer~=2.9.2
|
inquirer~=2.9.2
|
||||||
pexpect~=4.8.0
|
pexpect~=4.8.0
|
||||||
pycryptodome~=3.14.1
|
pycryptodome~=3.14.1
|
||||||
PyYAML~=6.0
|
setuptools~=59.4.0
|
||||||
|
2
setup.py
2
setup.py
@ -18,7 +18,7 @@ setup(
|
|||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
url="https://github.com/fluzzi/connpy",
|
url="https://github.com/fluzzi/connpy",
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
install_requires=["inquirer","pexpect","pycryptodome","PyYAML"], # add any additional packages that
|
install_requires=["inquirer","pexpect","pycryptodome"], # add any additional packages that
|
||||||
keywords=['networking', 'automation', 'ssh', 'telnet', 'connection manager'],
|
keywords=['networking', 'automation', 'ssh', 'telnet', 'connection manager'],
|
||||||
classifiers= [
|
classifiers= [
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 4 - Beta",
|
||||||
|
Loading…
Reference in New Issue
Block a user