2022-04-03 09:38:00 -03:00
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1, minimum-scale=1" />
< meta name = "generator" content = "pdoc 0.10.0" />
< title > conn API documentation</ title >
2022-04-03 12:00:35 -03:00
< meta name = "description" content = "Connection manager
```
usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder]
conn …" />
2022-04-03 09:38:00 -03:00
< link rel = "preload stylesheet" as = "style" href = "https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity = "sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin >
< link rel = "preload stylesheet" as = "style" href = "https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity = "sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin >
2022-04-03 10:26:08 -03:00
< link rel = "stylesheet preload" as = "style" href = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/fundation.min.css" crossorigin >
2022-04-03 09:38:00 -03:00
< style >: root { --highlight-color : #fe9 }. flex { display : flex !important } body { line-height : 1.5 em }# content { padding : 20 px }# sidebar { padding : 30 px ; overflow : hidden }# sidebar > * : last-child { margin-bottom : 2 cm }. http-server-breadcrumbs { font-size : 130 % ; margin : 0 0 15 px 0 }# footer { font-size : .75 em ; padding : 5 px 30 px ; border-top : 1 px solid #ddd ; text-align : right }# footer p { margin : 0 0 0 1 em ; display : inline-block }# footer p : last-child { margin-right : 30 px } h1 , h2 , h3 , h4 , h5 { font-weight : 300 } h1 { font-size : 2.5 em ; line-height : 1.1 em } h2 { font-size : 1.75 em ; margin : 1 em 0 .50 em 0 } h3 { font-size : 1.4 em ; margin : 25 px 0 10 px 0 } h4 { margin : 0 ; font-size : 105 % } h1 : target , h2 : target , h3 : target , h4 : target , h5 : target , h6 : target { background : var ( -- highlight - color ); padding : .2 em 0 } a { color : #058 ; text-decoration : none ; transition : color .3 s ease-in-out } a : hover { color : #e82 }. title code { font-weight : bold } h2 [ id ^= "header-" ] { margin-top : 2 em }. ident { color : #900 } pre code { background : #f8f8f8 ; font-size : .8 em ; line-height : 1.4 em } code { background : #f2f2f1 ; padding : 1 px 4 px ; overflow-wrap : break-word } h1 code { background : transparent } pre { background : #f8f8f8 ; border : 0 ; border-top : 1 px solid #ccc ; border-bottom : 1 px solid #ccc ; margin : 1 em 0 ; padding : 1 ex }# http-server-module-list { display : flex ; flex-flow : column }# http-server-module-list div { display : flex }# http-server-module-list dt { min-width : 10 % }# http-server-module-list p { margin-top : 0 }. toc ul , # index { list-style-type : none ; margin : 0 ; padding : 0 }# index code { background : transparent }# index h3 { border-bottom : 1 px solid #ddd }# index ul { padding : 0 }# index h4 { margin-top : .6 em ; font-weight : bold }@ media ( min-width : 200ex ) {# index . two-column { column-count : 2 }}@ media ( min-width : 300ex ) {# index . two-column { column-count : 3 }} dl { margin-bottom : 2 em } dl dl : last-child { margin-bottom : 4 em } dd { margin : 0 0 1 em 3 em }# header-classes + dl > dd { margin-bottom : 3 em } dd dd { margin-left : 2 em } dd p { margin : 10 px 0 }. name { background : #eee ; font-weight : bold ; font-size : .85 em ; padding : 5 px 10 px ; display : inline-block ; min-width : 40 % }. name : hover { background : #e0e0e0 } dt : target . name { background : var ( -- highlight - color )}. name > span : first-child { white-space : nowrap }. name . class > span : nth-child ( 2 ) { margin-left : .4 em }. inherited { color : #999 ; border-left : 5 px solid #eee ; padding-left : 1 em }. inheritance em { font-style : normal ; font-weight : bold }. desc h2 { font-weight : 400 ; font-size : 1.25 em }. desc h3 { font-size : 1 em }. desc dt code { background : inherit }. source summary , . git-link-div { color : #666 ; text-align : right ; font-weight : 400 ; font-size : .8 em ; text-transform : uppercase }. source summary > * { white-space : nowrap ; cursor : pointer }. git-link { color : inherit ; margin-left : 1 em }. source pre { max-height : 500 px ; overflow : auto ; margin : 0 }. source pre code { font-size : 12 px ; overflow : visible }. hlist { list-style : none }. hlist li { display : inline }. hlist li : after { content : ',\2002' }. hlist li : last-child : after { content : none }. hlist . hlist { display : inline ; padding-left : 1 em } img { max-width : 100 % } td { padding : 0 .5 em }. admonition { padding : .1 em .5 em ; margin-bottom : 1 em }. admonition-title { font-weight : bold }. admonition . note , . admonition . info , . admonition . important { background : #aef }. admonition . todo , . admonition . versionadded , . admonition . tip , . admonition . hint { background : #dfd }. admonition . warning , . admonition . versionchanged , . admonition . deprecated { background : #fd4 }. admonition . error , . admonition . danger , . admonition . caution { background : lightpink }</ style >
< style media = "screen and (min-width: 700px)" >@ media screen and ( min-width : 700px ) {# sidebar { width : 30 % ; height : 100 vh ; overflow : auto ; position : sticky ; top : 0 }# content { width : 70 % ; max-width : 100 ch ; padding : 3 em 4 em ; border-left : 1 px solid #ddd } pre code { font-size : 1 em }. item . name { font-size : 1 em } main { display : flex ; flex-direction : row-reverse ; justify-content : flex-end }. toc ul ul , # index ul { padding-left : 1.5 em }. toc > ul > li { margin-top : .5 em }}</ style >
< style media = "print" >@ media print {# sidebar h1 { page-break-before : always }. source { display : none }}@ media print { * { background : transparent !important ; color : #000 !important ; box-shadow : none !important ; text-shadow : none !important } a [ href ] : after { content : " (" attr ( href ) ")" ; font-size : 90 % } a [ href ][ title ] : after { content : none } abbr [ title ] : after { content : " (" attr ( title ) ")" }. ir a : after , a [ href ^= "javascript:" ] : after , a [ href ^= "#" ] : after { content : "" } pre , blockquote { border : 1 px solid #999 ; page-break-inside : avoid } thead { display : table-header-group } tr , img { page-break-inside : avoid } img { max-width : 100 % !important }@ page { margin : 0 . 5cm } p , h2 , h3 { orphans : 3 ; widows : 3 } h1 , h2 , h3 , h4 , h5 , h6 { page-break-after : avoid }}</ style >
2022-04-03 10:26:08 -03:00
< script defer src = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity = "sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin ></ script >
< script > window . addEventListener ( 'DOMContentLoaded' , () => hljs . initHighlighting ())</ script >
2022-04-03 09:38:00 -03:00
</ head >
< body >
< main >
< article id = "content" >
< header >
< h1 class = "title" > Package < code > conn</ code ></ h1 >
</ header >
< section id = "section-intro" >
2022-04-03 12:00:35 -03:00
< h2 id = "connection-manager" > Connection manager</ h2 >
< pre >< code > usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder]
conn {profile,move,mv,copy,cp,list,ls,bulk,config} ...
positional arguments:
node|folder node[@subfolder][@folder]
Connect to specific node or show all matching nodes
[@subfolder][@folder]
Show all available connections globaly or in specified path
Options:
-h, --help show this help message and exit
--add Add new node[@subfolder][@folder] or [@subfolder]@folder
--del, --rm Delete node[@subfolder][@folder] or [@subfolder]@folder
--mod, --edit Modify node[@subfolder][@folder]
--show Show node[@subfolder][@folder]
--debug, -d Display all conections steps
Commands:
profile Manage profiles
move (mv) Move node
copy (cp) Copy node
list (ls) List profiles, nodes or folders
bulk Add nodes in bulk
config Manage app config
</ code ></ pre >
< h4 id = "manage-profiles" > Manage profiles</ h4 >
< pre >< code > usage: conn profile [-h] (--add | --del | --mod | --show) profile
positional arguments:
profile Name of profile to manage
options:
-h, --help show this help message and exit
--add Add new profile
--del, --rm Delete profile
--mod, --edit Modify profile
--show Show profile
</ code ></ pre >
< h4 id = "examples" > Examples</ h4 >
< pre >< code > conn profile --add office-user
conn --add @office
conn --add @datacenter@office
conn --add server@datacenter@office
conn --add pc@office
conn --show server@datacenter@office
conn pc@office
conn server
</ code ></ pre >
2022-04-03 09:38:00 -03:00
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > #!/usr/bin/env python3
'''
2022-04-03 12:00:35 -03:00
## Connection manager
```
usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder]
conn {profile,move,mv,copy,cp,list,ls,bulk,config} ...
positional arguments:
node|folder node[@subfolder][@folder]
Connect to specific node or show all matching nodes
[@subfolder][@folder]
Show all available connections globaly or in specified path
Options:
-h, --help show this help message and exit
--add Add new node[@subfolder][@folder] or [@subfolder]@folder
--del, --rm Delete node[@subfolder][@folder] or [@subfolder]@folder
--mod, --edit Modify node[@subfolder][@folder]
--show Show node[@subfolder][@folder]
--debug, -d Display all conections steps
Commands:
profile Manage profiles
move (mv) Move node
copy (cp) Copy node
list (ls) List profiles, nodes or folders
bulk Add nodes in bulk
config Manage app config
```
#### Manage profiles
```
usage: conn profile [-h] (--add | --del | --mod | --show) profile
positional arguments:
profile Name of profile to manage
options:
-h, --help show this help message and exit
--add Add new profile
--del, --rm Delete profile
--mod, --edit Modify profile
--show Show profile
```
#### Examples
```
conn profile --add office-user
conn --add @office
conn --add @datacenter@office
conn --add server@datacenter@office
conn --add pc@office
conn --show server@datacenter@office
conn pc@office
conn server
```
2022-04-03 09:38:00 -03:00
'''
from .core import node,nodes
from .configfile import configfile
from .connapp import connapp
from pkg_resources import get_distribution
__all__ = [" node" , " nodes" , " configfile" ]
__version__ = " 2.0.10"
__author__ = " Federico Luzzi"
__pdoc__ = {
' core' : False,
' connapp' : False,
}</ code ></ pre >
</ details >
</ section >
< section >
</ section >
< section >
</ section >
< section >
</ section >
< section >
< h2 class = "section-title" id = "header-classes" > Classes</ h2 >
< dl >
< dt id = "conn.configfile" >< code class = "flex name class" >
< span > class < span class = "ident" > configfile</ span ></ span >
< span > (</ span >< span > conf=None, *, key=None)</ span >
</ code ></ dt >
< dd >
< div class = "desc" ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > class configfile:
def __init__(self, conf = None, *, key = None):
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)
if conf == None:
self.file = self.defaultfile
else:
self.file = conf
if key == None:
self.key = self.defaultkey
else:
self.key = key
if os.path.exists(self.file):
config = self.loadconfig(self.file)
else:
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.privatekey = RSA.import_key(open(self.key).read())
self.publickey = self.privatekey.publickey()
def loadconfig(self, conf):
jsonconf = open(conf)
return json.load(jsonconf)
def createconfig(self, conf):
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:
json.dump(defaultconfig, f, indent = 4)
f.close()
os.chmod(conf, 0o600)
jsonconf = open(conf)
return json.load(jsonconf)
def saveconfig(self, conf):
newconfig = {" config" :{}, " connections" : {}, " profiles" : {}}
newconfig[" config" ] = self.config
newconfig[" connections" ] = self.connections
newconfig[" profiles" ] = self.profiles
with open(conf, " w" ) as f:
json.dump(newconfig, f, indent = 4)
f.close()
def createkey(self, keyfile):
key = RSA.generate(2048)
with open(keyfile,' wb' ) as f:
f.write(key.export_key(' PEM' ))
f.close()
os.chmod(keyfile, 0o600)
def _explode_unique(self, unique):
uniques = unique.split(" @" )
if not unique.startswith(" @" ):
result = {" id" : uniques[0]}
else:
result = {}
if len(uniques) == 2:
result[" folder" ] = uniques[1]
if result[" folder" ] == "" :
return False
elif len(uniques) == 3:
result[" folder" ] = uniques[2]
result[" subfolder" ] = uniques[1]
if result[" folder" ] == "" or result[" subfolder" ] == "" :
return False
elif len(uniques) > 3:
return False
return result
def getitem(self, unique, keys = None):
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" ]]
newfolder = folder.copy()
newfolder.pop(" type" )
for node in newfolder.keys():
if " type" in newfolder[node].keys():
newfolder[node].pop(" type" )
if keys == None:
return newfolder
else:
f_newfolder = dict((k, newfolder[k]) for k in keys)
return f_newfolder
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" ]]
newnode = node.copy()
newnode.pop(" type" )
return newnode
def _connections_add(self,*, id, host, folder='' , subfolder='' , options='' , logs='' , password='' , port='' , protocol='' , user='' , type = " connection" ):
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 == '' :
self.connections[folder][id] = {" host" : host, " options" : options, " logs" : logs, " password" : password, " port" : port, " protocol" : protocol, " user" : user, " type" : type}
elif folder != '' and subfolder != '' :
self.connections[folder][subfolder][id] = {" host" : host, " options" : options, " logs" : logs, " password" : password, " port" : port, " protocol" : protocol, " user" : user, " type" : type}
def _connections_del(self,*, id, folder='' , subfolder='' ):
if folder == '' :
del self.connections[id]
elif folder != '' and subfolder == '' :
del self.connections[folder][id]
elif folder != '' and subfolder != '' :
del self.connections[folder][subfolder][id]
def _folder_add(self,*, folder, subfolder = '' ):
if subfolder == '' :
if folder not in self.connections:
self.connections[folder] = {" type" : " folder" }
else:
if subfolder not in self.connections[folder]:
self.connections[folder][subfolder] = {" type" : " subfolder" }
def _folder_del(self,*, folder, subfolder='' ):
if subfolder == '' :
del self.connections[folder]
else:
del self.connections[folder][subfolder]
def _profiles_add(self,*, id, host = '' , options='' , logs='' , password='' , port='' , protocol='' , user='' ):
self.profiles[id] = {" host" : host, " options" : options, " logs" : logs, " password" : password, " port" : port, " protocol" : protocol, " user" : user}
def _profiles_del(self,*, id ):
del self.profiles[id]</ code ></ pre >
</ details >
< h3 > Methods</ h3 >
< dl >
< dt id = "conn.configfile.createconfig" >< code class = "name flex" >
< span > def < span class = "ident" > createconfig</ span ></ span > (< span > self, conf)</ span >
</ code ></ dt >
< dd >
< div class = "desc" ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def createconfig(self, conf):
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:
json.dump(defaultconfig, f, indent = 4)
f.close()
os.chmod(conf, 0o600)
jsonconf = open(conf)
return json.load(jsonconf)</ code ></ pre >
</ details >
</ dd >
< dt id = "conn.configfile.createkey" >< code class = "name flex" >
< span > def < span class = "ident" > createkey</ span ></ span > (< span > self, keyfile)</ span >
</ code ></ dt >
< dd >
< div class = "desc" ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def createkey(self, keyfile):
key = RSA.generate(2048)
with open(keyfile,' wb' ) as f:
f.write(key.export_key(' PEM' ))
f.close()
os.chmod(keyfile, 0o600)</ code ></ pre >
</ details >
</ dd >
< dt id = "conn.configfile.getitem" >< code class = "name flex" >
< span > def < span class = "ident" > getitem</ span ></ span > (< span > self, unique, keys=None)</ span >
</ code ></ dt >
< dd >
< div class = "desc" ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def getitem(self, unique, keys = None):
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" ]]
newfolder = folder.copy()
newfolder.pop(" type" )
for node in newfolder.keys():
if " type" in newfolder[node].keys():
newfolder[node].pop(" type" )
if keys == None:
return newfolder
else:
f_newfolder = dict((k, newfolder[k]) for k in keys)
return f_newfolder
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" ]]
newnode = node.copy()
newnode.pop(" type" )
return newnode</ code ></ pre >
</ details >
</ dd >
< dt id = "conn.configfile.loadconfig" >< code class = "name flex" >
< span > def < span class = "ident" > loadconfig</ span ></ span > (< span > self, conf)</ span >
</ code ></ dt >
< dd >
< div class = "desc" ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def loadconfig(self, conf):
jsonconf = open(conf)
return json.load(jsonconf)</ code ></ pre >
</ details >
</ dd >
< dt id = "conn.configfile.saveconfig" >< code class = "name flex" >
< span > def < span class = "ident" > saveconfig</ span ></ span > (< span > self, conf)</ span >
</ code ></ dt >
< dd >
< div class = "desc" ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def saveconfig(self, conf):
newconfig = {" config" :{}, " connections" : {}, " profiles" : {}}
newconfig[" config" ] = self.config
newconfig[" connections" ] = self.connections
newconfig[" profiles" ] = self.profiles
with open(conf, " w" ) as f:
json.dump(newconfig, f, indent = 4)
f.close()</ code ></ pre >
</ details >
</ dd >
</ dl >
</ dd >
< dt id = "conn.node" >< code class = "flex name class" >
< span > class < span class = "ident" > node</ span ></ span >
< span > (</ span >< span > unique, host, options='', logs='', password='', port='', protocol='', user='', config='')</ span >
</ code ></ dt >
< dd >
< div class = "desc" >< p > This class generates a node object. Containts all the information and methods to connect and interact with a device using ssh or telnet.</ p >
2022-04-03 10:26:08 -03:00
< h3 id = "attributes" > Attributes:</ h3 >
< pre >< code > - output (str): Output of the commands you ran with run or test
method.
- result(bool): True if expected value is found after running
the commands using test method.
2022-04-03 09:38:00 -03:00
</ code ></ pre >
2022-04-03 10:26:08 -03:00
< h3 id = "parameters" > Parameters:</ h3 >
< pre >< code > - unique (str): Unique name to assign to the node.
- host (str): IP address or hostname of the node.
2022-04-03 09:38:00 -03:00
</ code ></ pre >
2022-04-03 10:26:08 -03:00
< h3 id = "optional-parameters" > Optional Parameters:</ h3 >
< pre >< code > - options (str): Additional options to pass the ssh/telnet for
connection.
- logs (str): Path/file for storing the logs. You can use
${unique},${host}, ${port}, ${user}, ${protocol}
as variables.
- password (str): Encrypted or plaintext password.
- port (str): Port to connect to node, default 22 for ssh and 23
for telnet.
- protocol (str): Select ssh or telnet. Default is ssh.
- user (str): Username to of the node.
- config (obj): Pass the object created with class configfile with
key for decryption and extra configuration if you
are using connection manager.
2022-04-03 09:38:00 -03:00
</ code ></ pre ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > class node:
''' This class generates a node object. Containts all the information and methods to connect and interact with a device using ssh or telnet.
2022-04-03 10:26:08 -03:00
### Attributes:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- output (str): Output of the commands you ran with run or test
method.
- result(bool): True if expected value is found after running
the commands using test method.
2022-04-03 09:38:00 -03:00
'''
def __init__(self, unique, host, options='' , logs='' , password='' , port='' , protocol='' , user='' , config='' ):
'''
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- unique (str): Unique name to assign to the node.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- host (str): IP address or hostname of the node.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
### Optional Parameters:
- options (str): Additional options to pass the ssh/telnet for
connection.
- logs (str): Path/file for storing the logs. You can use
${unique},${host}, ${port}, ${user}, ${protocol}
as variables.
- password (str): Encrypted or plaintext password.
- port (str): Port to connect to node, default 22 for ssh and 23
for telnet.
- protocol (str): Select ssh or telnet. Default is ssh.
- user (str): Username to of the node.
- config (obj): Pass the object created with class configfile with
key for decryption and extra configuration if you
are using connection manager.
2022-04-03 09:38:00 -03:00
'''
if config == '' :
self.idletime = 0
self.key = None
else:
self.idletime = config.config[" idletime" ]
self.key = config.key
self.unique = unique
attr = {" host" : host, " logs" : logs, " options" :options, " port" : port, " protocol" : protocol, " user" : user}
for key in attr:
profile = re.search(" ^@(.*)" , attr[key])
if profile and config != '' :
setattr(self,key,config.profiles[profile.group(1)][key])
elif attr[key] == '' and key == " protocol" :
try:
setattr(self,key,config.profiles[" default" ][key])
except:
setattr(self,key," ssh" )
else:
setattr(self,key,attr[key])
if isinstance(password,list):
self.password = []
for i, s in enumerate(password):
profile = re.search(" ^@(.*)" , password[i])
if profile and config != '' :
self.password.append(config.profiles[profile.group(1)][" password" ])
else:
self.password = [password]
def __passtx(self, passwords, *, keyfile=None):
# decrypts passwords, used by other methdos.
dpass = []
if keyfile is None:
keyfile = self.key
if keyfile is not None:
key = RSA.import_key(open(keyfile).read())
decryptor = PKCS1_OAEP.new(key)
for passwd in passwords:
if not re.match(' ^b[\" \' ].+[\" \' ]$' , passwd):
dpass.append(passwd)
else:
try:
decrypted = decryptor.decrypt(ast.literal_eval(passwd)).decode(" utf-8" )
dpass.append(decrypted)
except:
raise ValueError(" Missing or corrupted key" )
return dpass
def _logfile(self, logfile = None):
# translate logs variables and generate logs path.
if logfile == None:
logfile = self.logs
logfile = logfile.replace(" ${unique}" , self.unique)
logfile = logfile.replace(" ${host}" , self.host)
logfile = logfile.replace(" ${port}" , self.port)
logfile = logfile.replace(" ${user}" , self.user)
logfile = logfile.replace(" ${protocol}" , self.protocol)
now = datetime.datetime.now()
dateconf = re.search(r' \$\{date \' (.*)\' }' , logfile)
if dateconf:
logfile = re.sub(r' \$\{date (.*)}' ,now.strftime(dateconf.group(1)), logfile)
return logfile
def _logclean(self, logfile, var = False):
#Remove special ascii characters and other stuff from logfile.
if var == False:
t = open(logfile, " r" ).read()
else:
t = logfile
t = t.replace(" \n" ,"" ,1).replace(" \a" ,"" )
t = t.replace(' \n\n' , ' \n' )
t = re.sub(r' .\[K' , '' , t)
while True:
tb = re.sub(' .\b' , '' , t, count=1)
if len(t) == len(tb):
break
t = tb
ansi_escape = re.compile(r' \x1B(?:[@-Z\\-_]|\[[0-?]*[ -/ ]*[@-~])' )
t = ansi_escape.sub('' , t)
if var == False:
d = open(logfile, " w" )
d.write(t)
d.close()
return
else:
return t
def interact(self, debug = False):
'''
Allow user to interact with the node directly, mostly used by connection manager.
2022-04-03 10:26:08 -03:00
### Optional Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- debug (bool): If True, display all the connecting information
before interact. Default False.
2022-04-03 09:38:00 -03:00
'''
connect = self._connect(debug = debug)
if connect == True:
size = re.search(' columns=([0-9]+).*lines=([0-9]+)' ,str(os.get_terminal_size()))
self.child.setwinsize(int(size.group(2)),int(size.group(1)))
print(" Connected to " + self.unique + " at " + self.host + (" :" if self.port != '' else '' ) + self.port + " via: " + self.protocol)
if ' logfile' in dir(self):
self.child.logfile_read = open(self.logfile, " wb" )
elif debug:
self.child.logfile_read = None
if ' missingtext' in dir(self):
print(self.child.after.decode(), end='' )
self.child.interact()
if " logfile" in dir(self) and not debug:
self._logclean(self.logfile)
else:
print(connect)
exit(1)
def run(self, commands,*, folder = '' , prompt = r'> $|#$|\$$|> .$|#.$|\$.$' , stdout = False):
'''
Run a command or list of commands on the node and return the output.
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- commands (str/list): Commands to run on the node. Should be
str or a list of str.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
### Optional Named Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- folder (str): Path where output log should be stored, leave
empty to disable logging.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses ">" or EOF while
routers use ">" or " #" . The default value should
work for most nodes. Change it if your connection
need some special symbol.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- stdout (bool):Set True to send the command output to stdout.
default False.
### Returns:
str: Output of the commands you ran on the node.
2022-04-03 09:38:00 -03:00
'''
connect = self._connect()
if connect == True:
expects = [prompt, pexpect.EOF]
output = ''
if isinstance(commands, list):
for c in commands:
result = self.child.expect(expects)
self.child.sendline(c)
if result == 0:
output = output + self.child.before.decode() + self.child.after.decode()
if result == 1:
output = output + self.child.before.decode()
else:
result = self.child.expect(expects)
self.child.sendline(commands)
if result == 0:
output = output + self.child.before.decode() + self.child.after.decode()
if result == 1:
output = output + self.child.before.decode()
result = self.child.expect(expects)
if result == 0:
output = output + self.child.before.decode() + self.child.after.decode()
if result == 1:
output = output + self.child.before.decode()
self.child.close()
output = output.lstrip()
if stdout == True:
print(output)
if folder != '' :
with open(folder + " /" + self.unique, " w" ) as f:
f.write(output)
f.close()
self._logclean(folder + " /" + self.unique)
self.output = output
return output
else:
self.output = connect
return connect
def test(self, commands, expected, *, prompt = r'> $|#$|\$$|> .$|#.$|\$.$' ):
'''
Run a command or list of commands on the node, then check if expected value appears on the output after the last command.
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- commands (str/list): Commands to run on the node. Should be
str or list of str.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- expected (str) : Expected text to appear after running
all the commands on the node.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
### Optional Named Parameters:
- prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses ">" or EOF while
routers use ">" or " #" . The default value should
work for most nodes. Change it if your connection
need some special symbol.
2022-04-03 09:38:00 -03:00
### Returns:
2022-04-03 10:26:08 -03:00
bool: true if expected value is found after running the commands
false if prompt is found before.
2022-04-03 09:38:00 -03:00
'''
connect = self._connect()
if connect == True:
expects = [prompt, pexpect.EOF]
output = ''
if isinstance(commands, list):
for c in commands:
result = self.child.expect(expects)
self.child.sendline(c)
if result == 0:
output = output + self.child.before.decode() + self.child.after.decode()
if result == 1:
output = output + self.child.before.decode()
else:
self.child.expect(expects)
self.child.sendline(commands)
output = output + self.child.before.decode() + self.child.after.decode()
expects = [expected, prompt, pexpect.EOF]
results = self.child.expect(expects)
if results == 0:
self.child.close()
self.result = True
output = output + self.child.before.decode() + self.child.after.decode()
output = output.lstrip()
self.output = output
return True
if results in [1, 2]:
self.child.close()
self.result = False
if results == 1:
output = output + self.child.before.decode() + self.child.after.decode()
elif results == 2:
output = output + self.child.before.decode()
output = output.lstrip()
self.output = output
return False
else:
self.result = None
self.output = connect
return connect
def _connect(self, debug = False):
# Method to connect to the node, it parse all the information, create the ssh/telnet command and login to the node.
if self.protocol == " ssh" :
cmd = " ssh"
if self.idletime > 0:
cmd = cmd + " -o ServerAliveInterval=" + str(self.idletime)
if self.user == '' :
cmd = cmd + " -t {}" .format(self.host)
else:
cmd = cmd + " -t {}" .format(" @" .join([self.user,self.host]))
if self.port != '' :
cmd = cmd + " -p " + self.port
if self.options != '' :
cmd = cmd + " " + self.options
if self.logs != '' :
self.logfile = self._logfile()
if self.password[0] != '' :
passwords = self.__passtx(self.password)
else:
passwords = []
expects = [' yes/no' , ' refused' , ' supported' , ' cipher' , ' sage' , ' timeout' , ' unavailable' , ' closed' , ' [p|P]assword:|[u|U]sername:' , r'> $|#$|\$$|> .$|#.$|\$.$' , ' suspend' , pexpect.EOF, " No route to host" , " resolve hostname" , " no matching host key" ]
elif self.protocol == " telnet" :
cmd = " telnet " + self.host
if self.port != '' :
cmd = cmd + " " + self.port
if self.options != '' :
cmd = cmd + " " + self.options
if self.logs != '' :
self.logfile = self._logfile()
if self.password[0] != '' :
passwords = self.__passtx(self.password)
else:
passwords = []
expects = [' [u|U]sername:' , ' refused' , ' supported' , ' cipher' , ' sage' , ' timeout' , ' unavailable' , ' closed' , ' [p|P]assword:' , r'> $|#$|\$$|> .$|#.$|\$.$' , ' suspend' , pexpect.EOF, " No route to host" , " resolve hostname" , " no matching host key" ]
else:
raise ValueError(" Invalid protocol: " + self.protocol)
child = pexpect.spawn(cmd)
if debug:
child.logfile_read = sys.stdout.buffer
if len(passwords) > 0:
loops = len(passwords)
else:
loops = 1
endloop = False
for i in range(0, loops):
while True:
results = child.expect(expects)
if results == 0:
if self.protocol == " ssh" :
child.sendline(' yes' )
elif self.protocol == " telnet" :
if self.user != '' :
child.sendline(self.user)
else:
self.missingtext = True
break
if results in [1, 2, 3, 4, 5, 6, 7, 12, 13, 14]:
child.close()
return " Connection failed code:" + str(results)
if results == 8:
if len(passwords) > 0:
child.sendline(passwords[i])
else:
self.missingtext = True
break
if results in [9, 11]:
endloop = True
child.sendline()
break
if results == 10:
child.sendline(" \r" )
sleep(2)
if endloop:
break
child.readline(0)
self.child = child
return True</ code ></ pre >
</ details >
< h3 > Methods</ h3 >
< dl >
< dt id = "conn.node.interact" >< code class = "name flex" >
< span > def < span class = "ident" > interact</ span ></ span > (< span > self, debug=False)</ span >
</ code ></ dt >
< dd >
< div class = "desc" >< p > Allow user to interact with the node directly, mostly used by connection manager.</ p >
2022-04-03 10:26:08 -03:00
< h3 id = "optional-parameters" > Optional Parameters:</ h3 >
< pre >< code > - debug (bool): If True, display all the connecting information
before interact. Default False.
2022-04-03 09:38:00 -03:00
</ code ></ pre ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def interact(self, debug = False):
'''
Allow user to interact with the node directly, mostly used by connection manager.
2022-04-03 10:26:08 -03:00
### Optional Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- debug (bool): If True, display all the connecting information
before interact. Default False.
2022-04-03 09:38:00 -03:00
'''
connect = self._connect(debug = debug)
if connect == True:
size = re.search(' columns=([0-9]+).*lines=([0-9]+)' ,str(os.get_terminal_size()))
self.child.setwinsize(int(size.group(2)),int(size.group(1)))
print(" Connected to " + self.unique + " at " + self.host + (" :" if self.port != '' else '' ) + self.port + " via: " + self.protocol)
if ' logfile' in dir(self):
self.child.logfile_read = open(self.logfile, " wb" )
elif debug:
self.child.logfile_read = None
if ' missingtext' in dir(self):
print(self.child.after.decode(), end='' )
self.child.interact()
if " logfile" in dir(self) and not debug:
self._logclean(self.logfile)
else:
print(connect)
exit(1)</ code ></ pre >
</ details >
</ dd >
< dt id = "conn.node.run" >< code class = "name flex" >
< span > def < span class = "ident" > run</ span ></ span > (< span > self, commands, *, folder='', prompt='> $|#$|\\$$|> .$|#.$|\\$.$' , stdout=False)</ span >
</ code ></ dt >
< dd >
< div class = "desc" >< p > Run a command or list of commands on the node and return the output.</ p >
2022-04-03 10:26:08 -03:00
< h3 id = "parameters" > Parameters:</ h3 >
< pre >< code > - commands (str/list): Commands to run on the node. Should be
str or a list of str.
2022-04-03 09:38:00 -03:00
</ code ></ pre >
2022-04-03 10:26:08 -03:00
< h3 id = "optional-named-parameters" > Optional Named Parameters:</ h3 >
< pre >< code > - folder (str): Path where output log should be stored, leave
empty to disable logging.
- prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses "> " or EOF while
routers use "> " or "#". The default value should
work for most nodes. Change it if your connection
need some special symbol.
- stdout (bool):Set True to send the command output to stdout.
default False.
2022-04-03 09:38:00 -03:00
</ code ></ pre >
2022-04-03 10:26:08 -03:00
< h3 id = "returns" > Returns:</ h3 >
< pre >< code > str: Output of the commands you ran on the node.
2022-04-03 09:38:00 -03:00
</ code ></ pre ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def run(self, commands,*, folder = '' , prompt = r'> $|#$|\$$|> .$|#.$|\$.$' , stdout = False):
'''
Run a command or list of commands on the node and return the output.
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- commands (str/list): Commands to run on the node. Should be
str or a list of str.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
### Optional Named Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- folder (str): Path where output log should be stored, leave
empty to disable logging.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses ">" or EOF while
routers use ">" or " #" . The default value should
work for most nodes. Change it if your connection
need some special symbol.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- stdout (bool):Set True to send the command output to stdout.
default False.
### Returns:
str: Output of the commands you ran on the node.
2022-04-03 09:38:00 -03:00
'''
connect = self._connect()
if connect == True:
expects = [prompt, pexpect.EOF]
output = ''
if isinstance(commands, list):
for c in commands:
result = self.child.expect(expects)
self.child.sendline(c)
if result == 0:
output = output + self.child.before.decode() + self.child.after.decode()
if result == 1:
output = output + self.child.before.decode()
else:
result = self.child.expect(expects)
self.child.sendline(commands)
if result == 0:
output = output + self.child.before.decode() + self.child.after.decode()
if result == 1:
output = output + self.child.before.decode()
result = self.child.expect(expects)
if result == 0:
output = output + self.child.before.decode() + self.child.after.decode()
if result == 1:
output = output + self.child.before.decode()
self.child.close()
output = output.lstrip()
if stdout == True:
print(output)
if folder != '' :
with open(folder + " /" + self.unique, " w" ) as f:
f.write(output)
f.close()
self._logclean(folder + " /" + self.unique)
self.output = output
return output
else:
self.output = connect
return connect</ code ></ pre >
</ details >
</ dd >
< dt id = "conn.node.test" >< code class = "name flex" >
< span > def < span class = "ident" > test</ span ></ span > (< span > self, commands, expected, *, prompt='> $|#$|\\$$|> .$|#.$|\\$.$' )</ span >
</ code ></ dt >
< dd >
< div class = "desc" >< p > Run a command or list of commands on the node, then check if expected value appears on the output after the last command.</ p >
2022-04-03 10:26:08 -03:00
< h3 id = "parameters" > Parameters:</ h3 >
< pre >< code > - commands (str/list): Commands to run on the node. Should be
str or list of str.
- expected (str) : Expected text to appear after running
all the commands on the node.
2022-04-03 09:38:00 -03:00
</ code ></ pre >
2022-04-03 10:26:08 -03:00
< h3 id = "optional-named-parameters" > Optional Named Parameters:</ h3 >
< pre >< code > - prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses "> " or EOF while
routers use "> " or "#". The default value should
work for most nodes. Change it if your connection
need some special symbol.
2022-04-03 09:38:00 -03:00
</ code ></ pre >
< h3 id = "returns" > Returns:</ h3 >
2022-04-03 10:26:08 -03:00
< pre >< code > bool: true if expected value is found after running the commands
false if prompt is found before.
2022-04-03 09:38:00 -03:00
</ code ></ pre ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def test(self, commands, expected, *, prompt = r'> $|#$|\$$|> .$|#.$|\$.$' ):
'''
Run a command or list of commands on the node, then check if expected value appears on the output after the last command.
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- commands (str/list): Commands to run on the node. Should be
str or list of str.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
- expected (str) : Expected text to appear after running
all the commands on the node.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
### Optional Named Parameters:
- prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses ">" or EOF while
routers use ">" or " #" . The default value should
work for most nodes. Change it if your connection
need some special symbol.
2022-04-03 09:38:00 -03:00
### Returns:
2022-04-03 10:26:08 -03:00
bool: true if expected value is found after running the commands
false if prompt is found before.
2022-04-03 09:38:00 -03:00
'''
connect = self._connect()
if connect == True:
expects = [prompt, pexpect.EOF]
output = ''
if isinstance(commands, list):
for c in commands:
result = self.child.expect(expects)
self.child.sendline(c)
if result == 0:
output = output + self.child.before.decode() + self.child.after.decode()
if result == 1:
output = output + self.child.before.decode()
else:
self.child.expect(expects)
self.child.sendline(commands)
output = output + self.child.before.decode() + self.child.after.decode()
expects = [expected, prompt, pexpect.EOF]
results = self.child.expect(expects)
if results == 0:
self.child.close()
self.result = True
output = output + self.child.before.decode() + self.child.after.decode()
output = output.lstrip()
self.output = output
return True
if results in [1, 2]:
self.child.close()
self.result = False
if results == 1:
output = output + self.child.before.decode() + self.child.after.decode()
elif results == 2:
output = output + self.child.before.decode()
output = output.lstrip()
self.output = output
return False
else:
self.result = None
self.output = connect
return connect</ code ></ pre >
</ details >
</ dd >
</ dl >
</ dd >
< dt id = "conn.nodes" >< code class = "flex name class" >
< span > class < span class = "ident" > nodes</ span ></ span >
< span > (</ span >< span > nodes: dict, config='')</ span >
</ code ></ dt >
< dd >
< div class = "desc" >< p > This class generates a nodes object. Contains a list of node class objects and methods to run multiple tasks on nodes simultaneously.</ p >
< h3 id = "attributes" > Attributes:</ h3 >
< pre >< code > - nodelist (list): List of node class objects passed to the init
function.
- output (dict): Dictionary formed by nodes unique as keys,
output of the commands you ran on the node as
value. Created after running methods run or test.
- result (dict): Dictionary formed by nodes unique as keys, value
is True if expected value is found after running
the commands, False if prompt is found before.
Created after running method test.
- < unique> (obj): For each item in nodelist, there is an attribute
generated with the node unique.
</ code ></ pre >
< h3 id = "parameters" > Parameters:</ h3 >
< pre >< code > - nodes (dict): Dictionary formed by node information:
Keys: Unique name for each node.
Mandatory Subkeys: host(str).
Optional Subkeys: options(str), logs(str), password(str),
port(str), protocol(str), user(str).
For reference on subkeys check node class.
</ code ></ pre >
2022-04-03 10:26:08 -03:00
< h3 id = "optional-parameters" > Optional Parameters:</ h3 >
2022-04-03 09:38:00 -03:00
< pre >< code > - config (obj): Pass the object created with class configfile with key
for decryption and extra configuration if you are using
connection manager.
</ code ></ pre ></ div >
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > class nodes:
''' This class generates a nodes object. Contains a list of node class objects and methods to run multiple tasks on nodes simultaneously.
### Attributes:
- nodelist (list): List of node class objects passed to the init
function.
- output (dict): Dictionary formed by nodes unique as keys,
output of the commands you ran on the node as
value. Created after running methods run or test.
- result (dict): Dictionary formed by nodes unique as keys, value
is True if expected value is found after running
the commands, False if prompt is found before.
Created after running method test.
- < unique> (obj): For each item in nodelist, there is an attribute
generated with the node unique.
'''
def __init__(self, nodes: dict, config = '' ):
'''
### Parameters:
- nodes (dict): Dictionary formed by node information:
Keys: Unique name for each node.
Mandatory Subkeys: host(str).
Optional Subkeys: options(str), logs(str), password(str),
port(str), protocol(str), user(str).
For reference on subkeys check node class.
2022-04-03 10:26:08 -03:00
### Optional Parameters:
2022-04-03 09:38:00 -03:00
- config (obj): Pass the object created with class configfile with key
for decryption and extra configuration if you are using
connection manager.
'''
self.nodelist = []
self.config = config
for n in nodes:
this = node(n, **nodes[n], config = config)
self.nodelist.append(this)
setattr(self,n,this)
def _splitlist(self, lst, n):
#split a list in lists of n members.
for i in range(0, len(lst), n):
yield lst[i:i + n]
def run(self, commands,*, folder = None, prompt = None, stdout = None, parallel = 10):
'''
Run a command or list of commands on all the nodes in nodelist.
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
commands (str/list): Commands to run on the node. Should be str or
list of str.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
### Optional Named Parameters:
folder (str): Path where output log should be stored, leave empty
to disable logging.
prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses ">" or EOF while routers
use ">" or " #" . The default value should work for
most nodes. Change it if your connection need some
special symbol.
stdout (bool): Set True to send the command output to stdout.
Default False.
parallel (int): Number of nodes to run the commands simultaneously.
Default is 10, if there are more nodes that this
value, nodes are groups in groups with max this
number of members.
###Returns:
dict: Dictionary formed by nodes unique as keys, Output of the
commands you ran on the node as value.
2022-04-03 09:38:00 -03:00
'''
args = {}
args[" commands" ] = commands
if folder != None:
args[" folder" ] = folder
if prompt != None:
args[" prompt" ] = prompt
if stdout != None:
args[" stdout" ] = stdout
output = {}
tasks = []
for n in self.nodelist:
tasks.append(threading.Thread(target=n.run, kwargs=args))
taskslist = list(self._splitlist(tasks, parallel))
for t in taskslist:
for i in t:
i.start()
for i in t:
i.join()
for i in self.nodelist:
output[i.unique] = i.output
self.output = output
return output
def test(self, commands, expected, *, prompt = None, parallel = 10):
'''
Run a command or list of commands on all the nodes in nodelist, then check if expected value appears on the output after the last command.
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
commands (str/list): Commands to run on the node. Should be str or
list of str.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
expected (str) : Expected text to appear after running all the
commands on the node.
### Optional Named Parameters:
prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses ">" or EOF while
routers use ">" or " #" . The default value should
work for most nodes. Change it if your connection
need some special symbol.
### Returns:
dict: Dictionary formed by nodes unique as keys, value is True if
expected value is found after running the commands, False
if prompt is found before.
2022-04-03 09:38:00 -03:00
'''
args = {}
args[" commands" ] = commands
args[" expected" ] = expected
if prompt != None:
args[" prompt" ] = prompt
output = {}
result = {}
tasks = []
for n in self.nodelist:
tasks.append(threading.Thread(target=n.test, kwargs=args))
taskslist = list(self._splitlist(tasks, parallel))
for t in taskslist:
for i in t:
i.start()
for i in t:
i.join()
for i in self.nodelist:
result[i.unique] = i.result
output[i.unique] = i.output
self.output = output
self.result = result
return result</ code ></ pre >
</ details >
< h3 > Methods</ h3 >
< dl >
< dt id = "conn.nodes.run" >< code class = "name flex" >
< span > def < span class = "ident" > run</ span ></ span > (< span > self, commands, *, folder=None, prompt=None, stdout=None, parallel=10)</ span >
</ code ></ dt >
< dd >
< div class = "desc" >< p > Run a command or list of commands on all the nodes in nodelist.</ p >
2022-04-03 10:26:08 -03:00
< h3 id = "parameters" > Parameters:</ h3 >
< pre >< code > commands (str/list): Commands to run on the node. Should be str or
list of str.
</ code ></ pre >
< h3 id = "optional-named-parameters" > Optional Named Parameters:</ h3 >
< pre >< code > folder (str): Path where output log should be stored, leave empty
to disable logging.
prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses "> " or EOF while routers
use "> " or "#". The default value should work for
most nodes. Change it if your connection need some
special symbol.
stdout (bool): Set True to send the command output to stdout.
Default False.
parallel (int): Number of nodes to run the commands simultaneously.
Default is 10, if there are more nodes that this
value, nodes are groups in groups with max this
number of members.
</ code ></ pre >
< h3 id = "returns" > Returns:</ h3 >
< pre >< code > dict: Dictionary formed by nodes unique as keys, Output of the
commands you ran on the node as value.
</ code ></ pre ></ div >
2022-04-03 09:38:00 -03:00
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def run(self, commands,*, folder = None, prompt = None, stdout = None, parallel = 10):
'''
Run a command or list of commands on all the nodes in nodelist.
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
commands (str/list): Commands to run on the node. Should be str or
list of str.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
### Optional Named Parameters:
folder (str): Path where output log should be stored, leave empty
to disable logging.
prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses ">" or EOF while routers
use ">" or " #" . The default value should work for
most nodes. Change it if your connection need some
special symbol.
stdout (bool): Set True to send the command output to stdout.
Default False.
parallel (int): Number of nodes to run the commands simultaneously.
Default is 10, if there are more nodes that this
value, nodes are groups in groups with max this
number of members.
###Returns:
dict: Dictionary formed by nodes unique as keys, Output of the
commands you ran on the node as value.
2022-04-03 09:38:00 -03:00
'''
args = {}
args[" commands" ] = commands
if folder != None:
args[" folder" ] = folder
if prompt != None:
args[" prompt" ] = prompt
if stdout != None:
args[" stdout" ] = stdout
output = {}
tasks = []
for n in self.nodelist:
tasks.append(threading.Thread(target=n.run, kwargs=args))
taskslist = list(self._splitlist(tasks, parallel))
for t in taskslist:
for i in t:
i.start()
for i in t:
i.join()
for i in self.nodelist:
output[i.unique] = i.output
self.output = output
return output</ code ></ pre >
</ details >
</ dd >
< dt id = "conn.nodes.test" >< code class = "name flex" >
< span > def < span class = "ident" > test</ span ></ span > (< span > self, commands, expected, *, prompt=None, parallel=10)</ span >
</ code ></ dt >
< dd >
< div class = "desc" >< p > Run a command or list of commands on all the nodes in nodelist, then check if expected value appears on the output after the last command.</ p >
2022-04-03 10:26:08 -03:00
< h3 id = "parameters" > Parameters:</ h3 >
< pre >< code > commands (str/list): Commands to run on the node. Should be str or
list of str.
expected (str) : Expected text to appear after running all the
commands on the node.
</ code ></ pre >
< h3 id = "optional-named-parameters" > Optional Named Parameters:</ h3 >
< pre >< code > prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses "> " or EOF while
routers use "> " or "#". The default value should
work for most nodes. Change it if your connection
need some special symbol.
</ code ></ pre >
< h3 id = "returns" > Returns:</ h3 >
< pre >< code > dict: Dictionary formed by nodes unique as keys, value is True if
expected value is found after running the commands, False
if prompt is found before.
</ code ></ pre ></ div >
2022-04-03 09:38:00 -03:00
< details class = "source" >
< summary >
< span > Expand source code</ span >
</ summary >
< pre >< code class = "python" > def test(self, commands, expected, *, prompt = None, parallel = 10):
'''
Run a command or list of commands on all the nodes in nodelist, then check if expected value appears on the output after the last command.
2022-04-03 10:26:08 -03:00
### Parameters:
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
commands (str/list): Commands to run on the node. Should be str or
list of str.
2022-04-03 09:38:00 -03:00
2022-04-03 10:26:08 -03:00
expected (str) : Expected text to appear after running all the
commands on the node.
### Optional Named Parameters:
prompt (str): Prompt to be expected after a command is finished
running. Usually linux uses ">" or EOF while
routers use ">" or " #" . The default value should
work for most nodes. Change it if your connection
need some special symbol.
### Returns:
dict: Dictionary formed by nodes unique as keys, value is True if
expected value is found after running the commands, False
if prompt is found before.
2022-04-03 09:38:00 -03:00
'''
args = {}
args[" commands" ] = commands
args[" expected" ] = expected
if prompt != None:
args[" prompt" ] = prompt
output = {}
result = {}
tasks = []
for n in self.nodelist:
tasks.append(threading.Thread(target=n.test, kwargs=args))
taskslist = list(self._splitlist(tasks, parallel))
for t in taskslist:
for i in t:
i.start()
for i in t:
i.join()
for i in self.nodelist:
result[i.unique] = i.result
output[i.unique] = i.output
self.output = output
self.result = result
return result</ code ></ pre >
</ details >
</ dd >
</ dl >
</ dd >
</ dl >
</ section >
</ article >
< nav id = "sidebar" >
< h1 > Index</ h1 >
< div class = "toc" >
2022-04-03 12:00:35 -03:00
< ul >
< li >< a href = "#connection-manager" > Connection manager</ a >< ul >
< li >< a href = "#manage-profiles" > Manage profiles</ a ></ li >
< li >< a href = "#examples" > Examples</ a ></ li >
</ ul >
</ li >
</ ul >
2022-04-03 09:38:00 -03:00
</ div >
< ul id = "index" >
< li >< h3 >< a href = "#header-classes" > Classes</ a ></ h3 >
< ul >
< li >
< h4 >< code >< a title = "conn.configfile" href = "#conn.configfile" > configfile</ a ></ code ></ h4 >
< ul class = "" >
< li >< code >< a title = "conn.configfile.createconfig" href = "#conn.configfile.createconfig" > createconfig</ a ></ code ></ li >
< li >< code >< a title = "conn.configfile.createkey" href = "#conn.configfile.createkey" > createkey</ a ></ code ></ li >
< li >< code >< a title = "conn.configfile.getitem" href = "#conn.configfile.getitem" > getitem</ a ></ code ></ li >
< li >< code >< a title = "conn.configfile.loadconfig" href = "#conn.configfile.loadconfig" > loadconfig</ a ></ code ></ li >
< li >< code >< a title = "conn.configfile.saveconfig" href = "#conn.configfile.saveconfig" > saveconfig</ a ></ code ></ li >
</ ul >
</ li >
< li >
< h4 >< code >< a title = "conn.node" href = "#conn.node" > node</ a ></ code ></ h4 >
< ul class = "" >
< li >< code >< a title = "conn.node.interact" href = "#conn.node.interact" > interact</ a ></ code ></ li >
< li >< code >< a title = "conn.node.run" href = "#conn.node.run" > run</ a ></ code ></ li >
< li >< code >< a title = "conn.node.test" href = "#conn.node.test" > test</ a ></ code ></ li >
</ ul >
</ li >
< li >
< h4 >< code >< a title = "conn.nodes" href = "#conn.nodes" > nodes</ a ></ code ></ h4 >
< ul class = "" >
< li >< code >< a title = "conn.nodes.run" href = "#conn.nodes.run" > run</ a ></ code ></ li >
< li >< code >< a title = "conn.nodes.test" href = "#conn.nodes.test" > test</ a ></ code ></ li >
</ ul >
</ li >
</ ul >
</ li >
</ ul >
</ nav >
</ main >
< footer id = "footer" >
< p > Generated by < a href = "https://pdoc3.github.io/pdoc" title = "pdoc: Python API documentation generator" >< cite > pdoc</ cite > 0.10.0</ a > .</ p >
</ footer >
</ body >
</ html >