finish doc 1.0
This commit is contained in:
parent
de2c2ab21b
commit
67fa4e1e6d
@ -1,6 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
'''
|
||||
## Connection manager
|
||||
|
||||
conn is a connection manager that allows you to store nodes to connect them fast and password free.
|
||||
|
||||
### Features
|
||||
- You can generate profiles and reference them from nodes using @profilename
|
||||
so you dont need to edit multiple nodes when changing password or other
|
||||
information.
|
||||
- Nodes can be stored on @folder or @subfolder@folder to organize your
|
||||
devices. Then can be referenced using node@subfolder@folder or node@folder
|
||||
- Much more!
|
||||
|
||||
### Usage
|
||||
```
|
||||
usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder]
|
||||
conn {profile,move,mv,copy,cp,list,ls,bulk,config} ...
|
||||
@ -27,7 +39,7 @@ Commands:
|
||||
config Manage app config
|
||||
```
|
||||
|
||||
#### Manage profiles
|
||||
### Manage profiles
|
||||
```
|
||||
usage: conn profile [-h] (--add | --del | --mod | --show) profile
|
||||
|
||||
@ -42,7 +54,7 @@ options:
|
||||
--show Show profile
|
||||
```
|
||||
|
||||
#### Examples
|
||||
### Examples
|
||||
```
|
||||
conn profile --add office-user
|
||||
conn --add @office
|
||||
@ -53,15 +65,68 @@ options:
|
||||
conn pc@office
|
||||
conn server
|
||||
```
|
||||
|
||||
## Automation module
|
||||
the automation module
|
||||
|
||||
### Standalone module
|
||||
```
|
||||
import conn
|
||||
router = conn.node("unique name","ip/hostname", user="username", password="pass")
|
||||
router.run(["term len 0","show run"])
|
||||
print(router.output)
|
||||
hasip = router.test("show ip int brief","1.1.1.1")
|
||||
if hasip:
|
||||
print("Router has ip 1.1.1.1")
|
||||
else:
|
||||
print("router don't has ip 1.1.1.1")
|
||||
```
|
||||
|
||||
### Using manager configuration
|
||||
```
|
||||
import conn
|
||||
conf = conn.configfile()
|
||||
device = conf.getitem("server@office")
|
||||
server = conn.node("unique name", **device, config=conf)
|
||||
result = server.run(["cd /", "ls -la"])
|
||||
print(result)
|
||||
```
|
||||
### Running parallel tasks
|
||||
```
|
||||
import conn
|
||||
conf = conn.configfile()
|
||||
#You can get the nodes from the config from a folder and fitlering in it
|
||||
nodes = conf.getitem("@office", ["router1", "router2", "router3"])
|
||||
#You can also get each node individually:
|
||||
nodes = {}
|
||||
nodes["router1"] = conf.getitem("router1@office")
|
||||
nodes["router2"] = conf.getitem("router2@office")
|
||||
nodes["router10"] = conf.getitem("router10@datacenter")
|
||||
#Also, you can create the nodes manually:
|
||||
nodes = {}
|
||||
nodes["router1"] = {"host": "1.1.1.1", "user": "username", "password": "pass1"}
|
||||
nodes["router2"] = {"host": "1.1.1.2", "user": "username", "password": "pass2"}
|
||||
nodes["router3"] = {"host": "1.1.1.2", "user": "username", "password": "pass3"}
|
||||
#Finally you run some tasks on the nodes
|
||||
mynodes = conn.nodes(nodes, config = conf)
|
||||
result = mynodes.test(["show ip int br"], "1.1.1.2")
|
||||
for i in result:
|
||||
print("---" + i + "---")
|
||||
print(result[i])
|
||||
print()
|
||||
# Or for one specific node
|
||||
mynodes.router1.run(["term len 0". "show run"], folder = "/home/user/logs")
|
||||
```
|
||||
|
||||
'''
|
||||
from .core import node,nodes
|
||||
from .configfile import configfile
|
||||
from .connapp import connapp
|
||||
from pkg_resources import get_distribution
|
||||
|
||||
__all__ = ["node", "nodes", "configfile"]
|
||||
__all__ = ["node", "nodes", "configfile", "connapp"]
|
||||
__version__ = "2.0.10"
|
||||
__author__ = "Federico Luzzi"
|
||||
__pdoc__ = {
|
||||
'core': False,
|
||||
'connapp': False,
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
from connapp import connapp
|
||||
from configfile import configfile
|
||||
from core import node
|
||||
from conn import *
|
||||
|
||||
def main():
|
||||
conf = configfile()
|
||||
connapp(conf, node)
|
||||
connapp(conf)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
@ -10,39 +10,75 @@ from pathlib import Path
|
||||
#functions and classes
|
||||
|
||||
class configfile:
|
||||
''' This class generates a configfile object. Containts a dictionary storing, config, nodes and profiles, normaly used by connection manager.
|
||||
|
||||
def __init__(self, conf = None, *, key = None):
|
||||
### Attributes:
|
||||
|
||||
- file (str): Path/file to config file.
|
||||
|
||||
- key (str): Path/file to RSA key file.
|
||||
|
||||
- config (dict): Dictionary containing information of connection
|
||||
manager configuration.
|
||||
|
||||
- connections (dict): Dictionary containing all the nodes added to
|
||||
connection manager.
|
||||
|
||||
- profiles (dict): Dictionary containing all the profiles added to
|
||||
connection manager.
|
||||
|
||||
- privatekey (obj): Object containing the private key to encrypt
|
||||
passwords.
|
||||
|
||||
- publickey (obj): Object containing the public key to decrypt
|
||||
passwords.
|
||||
'''
|
||||
|
||||
def __init__(self, conf = None, key = None):
|
||||
'''
|
||||
|
||||
### Optional Parameters:
|
||||
|
||||
- conf (str): Path/file to config file. If left empty default
|
||||
path is ~/.config/conn/config.json
|
||||
|
||||
- key (str): Path/file to RSA key file. If left empty default
|
||||
path is ~/.config/conn/.osk
|
||||
|
||||
'''
|
||||
home = os.path.expanduser("~")
|
||||
self.defaultdir = home + '/.config/conn'
|
||||
self.defaultfile = self.defaultdir + '/config.json'
|
||||
self.defaultkey = self.defaultdir + '/.osk'
|
||||
Path(self.defaultdir).mkdir(parents=True, exist_ok=True)
|
||||
defaultdir = home + '/.config/conn'
|
||||
defaultfile = defaultdir + '/config.json'
|
||||
defaultkey = defaultdir + '/.osk'
|
||||
Path(defaultdir).mkdir(parents=True, exist_ok=True)
|
||||
if conf == None:
|
||||
self.file = self.defaultfile
|
||||
self.file = defaultfile
|
||||
else:
|
||||
self.file = conf
|
||||
if key == None:
|
||||
self.key = self.defaultkey
|
||||
self.key = defaultkey
|
||||
else:
|
||||
self.key = key
|
||||
if os.path.exists(self.file):
|
||||
config = self.loadconfig(self.file)
|
||||
config = self._loadconfig(self.file)
|
||||
else:
|
||||
config = self.createconfig(self.file)
|
||||
config = self._createconfig(self.file)
|
||||
self.config = config["config"]
|
||||
self.connections = config["connections"]
|
||||
self.profiles = config["profiles"]
|
||||
if not os.path.exists(self.key):
|
||||
self.createkey(self.key)
|
||||
self._createkey(self.key)
|
||||
self.privatekey = RSA.import_key(open(self.key).read())
|
||||
self.publickey = self.privatekey.publickey()
|
||||
|
||||
|
||||
def loadconfig(self, conf):
|
||||
def _loadconfig(self, conf):
|
||||
#Loads config file
|
||||
jsonconf = open(conf)
|
||||
return json.load(jsonconf)
|
||||
|
||||
def createconfig(self, conf):
|
||||
def _createconfig(self, conf):
|
||||
#Create config file
|
||||
defaultconfig = {'config': {'case': False, 'idletime': 30}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"" }}}
|
||||
if not os.path.exists(conf):
|
||||
with open(conf, "w") as f:
|
||||
@ -52,7 +88,8 @@ class configfile:
|
||||
jsonconf = open(conf)
|
||||
return json.load(jsonconf)
|
||||
|
||||
def saveconfig(self, conf):
|
||||
def _saveconfig(self, conf):
|
||||
#Save config file
|
||||
newconfig = {"config":{}, "connections": {}, "profiles": {}}
|
||||
newconfig["config"] = self.config
|
||||
newconfig["connections"] = self.connections
|
||||
@ -61,7 +98,8 @@ class configfile:
|
||||
json.dump(newconfig, f, indent = 4)
|
||||
f.close()
|
||||
|
||||
def createkey(self, keyfile):
|
||||
def _createkey(self, keyfile):
|
||||
#Create key file
|
||||
key = RSA.generate(2048)
|
||||
with open(keyfile,'wb') as f:
|
||||
f.write(key.export_key('PEM'))
|
||||
@ -69,6 +107,7 @@ class configfile:
|
||||
os.chmod(keyfile, 0o600)
|
||||
|
||||
def _explode_unique(self, unique):
|
||||
#Divide unique name into folder, subfolder and id
|
||||
uniques = unique.split("@")
|
||||
if not unique.startswith("@"):
|
||||
result = {"id": uniques[0]}
|
||||
@ -88,6 +127,26 @@ class configfile:
|
||||
return result
|
||||
|
||||
def getitem(self, unique, keys = None):
|
||||
'''
|
||||
Get an node or a group of nodes from configfile which can be passed to node/nodes class
|
||||
|
||||
### Parameters:
|
||||
|
||||
- unique (str): Unique name of the node or folder in config using
|
||||
connection manager style: node[@subfolder][@folder]
|
||||
or [@subfolder]@folder
|
||||
|
||||
### Optional Parameters:
|
||||
|
||||
- keys (list): In case you pass a folder as unique, you can filter
|
||||
nodes inside the folder passing a list.
|
||||
|
||||
### Returns:
|
||||
|
||||
dict: Dictionary containing information of node or multiple dictionaries
|
||||
of multiple nodes.
|
||||
|
||||
'''
|
||||
uniques = self._explode_unique(unique)
|
||||
if unique.startswith("@"):
|
||||
if uniques.keys() >= {"folder", "subfolder"}:
|
||||
@ -116,6 +175,7 @@ class configfile:
|
||||
return newnode
|
||||
|
||||
def _connections_add(self,*, id, host, folder='', subfolder='', options='', logs='', password='', port='', protocol='', user='', type = "connection" ):
|
||||
#Add connection from config
|
||||
if folder == '':
|
||||
self.connections[id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "type": type}
|
||||
elif folder != '' and subfolder == '':
|
||||
@ -125,6 +185,7 @@ class configfile:
|
||||
|
||||
|
||||
def _connections_del(self,*, id, folder='', subfolder=''):
|
||||
#Delete connection from config
|
||||
if folder == '':
|
||||
del self.connections[id]
|
||||
elif folder != '' and subfolder == '':
|
||||
@ -133,6 +194,7 @@ class configfile:
|
||||
del self.connections[folder][subfolder][id]
|
||||
|
||||
def _folder_add(self,*, folder, subfolder = ''):
|
||||
#Add Folder from config
|
||||
if subfolder == '':
|
||||
if folder not in self.connections:
|
||||
self.connections[folder] = {"type": "folder"}
|
||||
@ -141,6 +203,7 @@ class configfile:
|
||||
self.connections[folder][subfolder] = {"type": "subfolder"}
|
||||
|
||||
def _folder_del(self,*, folder, subfolder=''):
|
||||
#Delete folder from config
|
||||
if subfolder == '':
|
||||
del self.connections[folder]
|
||||
else:
|
||||
@ -148,8 +211,10 @@ class configfile:
|
||||
|
||||
|
||||
def _profiles_add(self,*, id, host = '', options='', logs='', password='', port='', protocol='', user='' ):
|
||||
#Add profile from config
|
||||
self.profiles[id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user}
|
||||
|
||||
|
||||
def _profiles_del(self,*, id ):
|
||||
#Delete profile from config
|
||||
del self.profiles[id]
|
||||
|
@ -9,15 +9,25 @@ import argparse
|
||||
import sys
|
||||
import inquirer
|
||||
import json
|
||||
from conn import *
|
||||
from .core import node
|
||||
|
||||
|
||||
#functions and classes
|
||||
|
||||
class connapp:
|
||||
# Class that starts the connection manager app.
|
||||
def __init__(self, config, node):
|
||||
#Define the parser for the arguments
|
||||
''' This class starts the connection manager app. It's normally used by connection manager but you can use it on a script to run the connection manager your way and use a different configfile and key.
|
||||
'''
|
||||
|
||||
def __init__(self, config):
|
||||
'''
|
||||
|
||||
### Parameters:
|
||||
|
||||
- config (obj): Object generated with configfile class, it contains
|
||||
the nodes configuration and the methods to manage
|
||||
the config file.
|
||||
|
||||
'''
|
||||
self.node = node
|
||||
self.config = config
|
||||
self.nodes = self._getallnodes()
|
||||
@ -134,7 +144,7 @@ class connapp:
|
||||
self.config._folder_del(**uniques)
|
||||
else:
|
||||
self.config._connections_del(**uniques)
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("{} deleted succesfully".format(matches[0]))
|
||||
elif args.action == "add":
|
||||
if args.data == None:
|
||||
@ -166,7 +176,7 @@ class connapp:
|
||||
print("Folder {} not found".format(uniques["folder"]))
|
||||
exit(2)
|
||||
self.config._folder_add(**uniques)
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("{} added succesfully".format(args.data))
|
||||
|
||||
if type == "node":
|
||||
@ -187,7 +197,7 @@ class connapp:
|
||||
if newnode == False:
|
||||
exit(7)
|
||||
self.config._connections_add(**newnode)
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("{} added succesfully".format(args.data))
|
||||
elif args.action == "show":
|
||||
if args.data == None:
|
||||
@ -227,7 +237,7 @@ class connapp:
|
||||
return
|
||||
else:
|
||||
self.config._connections_add(**updatenode)
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("{} edited succesfully".format(args.data))
|
||||
|
||||
|
||||
@ -252,7 +262,7 @@ class connapp:
|
||||
confirm = inquirer.prompt(question)
|
||||
if confirm["delete"]:
|
||||
self.config._profiles_del(id = matches[0])
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("{} deleted succesfully".format(matches[0]))
|
||||
elif args.action == "show":
|
||||
matches = list(filter(lambda k: k == args.data[0], self.profiles))
|
||||
@ -276,7 +286,7 @@ class connapp:
|
||||
if newprofile == False:
|
||||
exit(7)
|
||||
self.config._profiles_add(**newprofile)
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("{} added succesfully".format(args.data[0]))
|
||||
elif args.action == "mod":
|
||||
matches = list(filter(lambda k: k == args.data[0], self.profiles))
|
||||
@ -297,7 +307,7 @@ class connapp:
|
||||
return
|
||||
else:
|
||||
self.config._profiles_add(**updateprofile)
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("{} edited succesfully".format(args.data[0]))
|
||||
|
||||
def _func_others(self, args):
|
||||
@ -331,7 +341,7 @@ class connapp:
|
||||
self.config._connections_add(**newnode)
|
||||
if args.command == "move":
|
||||
self.config._connections_del(**olduniques)
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
if args.command == "move":
|
||||
print("{} moved succesfully to {}".format(args.data[0],args.data[1]))
|
||||
if args.command == "cp":
|
||||
@ -375,7 +385,7 @@ class connapp:
|
||||
self.config._connections_add(**newnode)
|
||||
self.nodes = self._getallnodes()
|
||||
if count > 0:
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("Succesfully added {} nodes".format(count))
|
||||
else:
|
||||
print("0 nodes added")
|
||||
@ -392,7 +402,7 @@ class connapp:
|
||||
if args.data[0] < 0:
|
||||
args.data[0] = 0
|
||||
self.config.config[args.command] = args.data[0]
|
||||
self.config.saveconfig(self.config.file)
|
||||
self.config._saveconfig(self.config.file)
|
||||
print("Config saved")
|
||||
|
||||
def _choose(self, list, name, action):
|
||||
@ -759,7 +769,23 @@ complete -o nosort -F _conn conn
|
||||
return nodes
|
||||
|
||||
def encrypt(self, password, keyfile=None):
|
||||
#Encrypt password using keyfile
|
||||
'''
|
||||
Encrypts password using RSA keyfile
|
||||
|
||||
### Parameters:
|
||||
|
||||
- password (str): Plaintext password to encrypt.
|
||||
|
||||
### Optional Parameters:
|
||||
|
||||
- keyfile (str): Path/file to keyfile. Default is config keyfile.
|
||||
|
||||
|
||||
### Returns:
|
||||
|
||||
str: Encrypted password.
|
||||
|
||||
'''
|
||||
if keyfile is None:
|
||||
keyfile = self.config.key
|
||||
key = RSA.import_key(open(keyfile).read())
|
||||
@ -768,9 +794,3 @@ complete -o nosort -F _conn conn
|
||||
password = encryptor.encrypt(password.encode("utf-8"))
|
||||
return str(password)
|
||||
|
||||
def main():
|
||||
conf = configfile()
|
||||
connapp(conf, node)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
1212
docs/conn/index.html
1212
docs/conn/index.html
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user