Fix:
- Fix not allowing to use some regex symbols when passing arguments. - Fix AI requests timing out when list of nodes is big. - Fix error when forwarding connpy run commands to a file Features: - Improve AI response time changing list of devices to list of OS, reducing the lenght of request. - Update GPT model to last one. - Add filtering option to list command, Also format can be passed to format the output as needed. - Allow to use regular expresions to match nodes in: run command (using yaml file or directly), remove command. - When there is a connectivity error, now it shows the error number plus the protocol error.
This commit is contained in:
parent
d96910092b
commit
00905575fc
@ -7,9 +7,10 @@ tasks:
|
|||||||
nodes: #List of nodes to work on. Mandatory
|
nodes: #List of nodes to work on. Mandatory
|
||||||
- 'router1@office' #You can add specific nodes
|
- 'router1@office' #You can add specific nodes
|
||||||
- '@aws' #entire folders or subfolders
|
- '@aws' #entire folders or subfolders
|
||||||
- '@office': #or filter inside a folder or subfolder
|
- '@office': #filter inside a folder or subfolder
|
||||||
- 'router2'
|
- 'router2'
|
||||||
- 'router7'
|
- 'router7'
|
||||||
|
- 'router[0-9]' # Or use regular expressions
|
||||||
|
|
||||||
commands: #List of commands to send, use {name} to pass variables
|
commands: #List of commands to send, use {name} to pass variables
|
||||||
- 'term len 0'
|
- 'term len 0'
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
__version__ = "3.5.0"
|
__version__ = "3.6.0"
|
||||||
|
|
||||||
|
40
connpy/ai.py
40
connpy/ai.py
@ -66,7 +66,7 @@ class ai:
|
|||||||
try:
|
try:
|
||||||
self.model = self.config.config["openai"]["model"]
|
self.model = self.config.config["openai"]["model"]
|
||||||
except:
|
except:
|
||||||
self.model = "gpt-3.5-turbo-0613"
|
self.model = "gpt-3.5-turbo"
|
||||||
self.temp = temp
|
self.temp = temp
|
||||||
self.__prompt = {}
|
self.__prompt = {}
|
||||||
self.__prompt["original_system"] = """
|
self.__prompt["original_system"] = """
|
||||||
@ -125,7 +125,7 @@ Categorize the user's request based on the operation they want to perform on the
|
|||||||
"""
|
"""
|
||||||
self.__prompt["original_function"]["parameters"]["required"] = ["type", "filter"]
|
self.__prompt["original_function"]["parameters"]["required"] = ["type", "filter"]
|
||||||
self.__prompt["command_system"] = """
|
self.__prompt["command_system"] = """
|
||||||
For each device listed below, provide the command(s) needed to perform the specified action, depending on the device OS (e.g., Cisco IOSXR router, Linux server).
|
For each OS listed below, provide the command(s) needed to perform the specified action, depending on the device OS (e.g., Cisco IOSXR router, Linux server).
|
||||||
The application knows how to connect to devices via SSH, so you only need to provide the command(s) to run after connecting.
|
The application knows how to connect to devices via SSH, so you only need to provide the command(s) to run after connecting.
|
||||||
If the commands needed are not for the specific OS type, just send an empty list (e.g., []).
|
If the commands needed are not for the specific OS type, just send an empty list (e.g., []).
|
||||||
Note: Preserving the integrity of user-provided commands is of utmost importance. If a user has provided a specific command to run, include that command exactly as it was given, even if it's not recognized or understood. Under no circumstances should you modify or alter user-provided commands.
|
Note: Preserving the integrity of user-provided commands is of utmost importance. If a user has provided a specific command to run, include that command exactly as it was given, even if it's not recognized or understood. Under no circumstances should you modify or alter user-provided commands.
|
||||||
@ -133,14 +133,14 @@ Categorize the user's request based on the operation they want to perform on the
|
|||||||
self.__prompt["command_user"]= """
|
self.__prompt["command_user"]= """
|
||||||
input: show me the full configuration for all this devices:
|
input: show me the full configuration for all this devices:
|
||||||
|
|
||||||
Devices:
|
OS:
|
||||||
router1: cisco ios
|
cisco ios:
|
||||||
"""
|
"""
|
||||||
self.__prompt["command_assistant"] = {"name": "get_commands", "arguments": "{\n \"router1\": \"show running-configuration\"\n}"}
|
self.__prompt["command_assistant"] = {"name": "get_commands", "arguments": "{\n \"cisco ios\": \"show running-configuration\"\n}"}
|
||||||
self.__prompt["command_function"] = {}
|
self.__prompt["command_function"] = {}
|
||||||
self.__prompt["command_function"]["name"] = "get_commands"
|
self.__prompt["command_function"]["name"] = "get_commands"
|
||||||
self.__prompt["command_function"]["descriptions"] = """
|
self.__prompt["command_function"]["descriptions"] = """
|
||||||
For each device listed below, provide the command(s) needed to perform the specified action, depending on the device OS (e.g., Cisco IOSXR router, Linux server).
|
For each OS listed below, provide the command(s) needed to perform the specified action, depending on the device OS (e.g., Cisco IOSXR router, Linux server).
|
||||||
The application knows how to connect to devices via SSH, so you only need to provide the command(s) to run after connecting.
|
The application knows how to connect to devices via SSH, so you only need to provide the command(s) to run after connecting.
|
||||||
If the commands needed are not for the specific OS type, just send an empty list (e.g., []).
|
If the commands needed are not for the specific OS type, just send an empty list (e.g., []).
|
||||||
"""
|
"""
|
||||||
@ -201,16 +201,16 @@ Categorize the user's request based on the operation they want to perform on the
|
|||||||
myfunction = False
|
myfunction = False
|
||||||
return myfunction
|
return myfunction
|
||||||
|
|
||||||
def _clean_command_response(self, raw_response):
|
def _clean_command_response(self, raw_response, node_list):
|
||||||
#Parse response for command request to openAI GPT.
|
#Parse response for command request to openAI GPT.
|
||||||
info_dict = {}
|
info_dict = {}
|
||||||
info_dict["commands"] = []
|
info_dict["commands"] = []
|
||||||
info_dict["variables"] = {}
|
info_dict["variables"] = {}
|
||||||
info_dict["variables"]["__global__"] = {}
|
info_dict["variables"]["__global__"] = {}
|
||||||
for key, value in raw_response.items():
|
for key, value in node_list.items():
|
||||||
key = key.strip()
|
|
||||||
newvalue = {}
|
newvalue = {}
|
||||||
for i,e in enumerate(value, start=1):
|
commands = raw_response[value]
|
||||||
|
for i,e in enumerate(commands, start=1):
|
||||||
newvalue[f"command{i}"] = e
|
newvalue[f"command{i}"] = e
|
||||||
if f"{{command{i}}}" not in info_dict["commands"]:
|
if f"{{command{i}}}" not in info_dict["commands"]:
|
||||||
info_dict["commands"].append(f"{{command{i}}}")
|
info_dict["commands"].append(f"{{command{i}}}")
|
||||||
@ -222,20 +222,22 @@ Categorize the user's request based on the operation they want to perform on the
|
|||||||
#Send the request for commands for each device to openAI GPT.
|
#Send the request for commands for each device to openAI GPT.
|
||||||
output_list = []
|
output_list = []
|
||||||
command_function = deepcopy(self.__prompt["command_function"])
|
command_function = deepcopy(self.__prompt["command_function"])
|
||||||
|
node_list = {}
|
||||||
for key, value in nodes.items():
|
for key, value in nodes.items():
|
||||||
tags = value.get('tags', {})
|
tags = value.get('tags', {})
|
||||||
try:
|
try:
|
||||||
if os_value := tags.get('os'):
|
if os_value := tags.get('os'):
|
||||||
output_list.append(f"{key}: {os_value}")
|
node_list[key] = os_value
|
||||||
command_function["parameters"]["properties"][key] = {}
|
output_list.append(f"{os_value}")
|
||||||
command_function["parameters"]["properties"][key]["type"] = "array"
|
command_function["parameters"]["properties"][os_value] = {}
|
||||||
command_function["parameters"]["properties"][key]["description"] = f"OS: {os_value}"
|
command_function["parameters"]["properties"][os_value]["type"] = "array"
|
||||||
command_function["parameters"]["properties"][key]["items"] = {}
|
command_function["parameters"]["properties"][os_value]["description"] = f"OS: {os_value}"
|
||||||
command_function["parameters"]["properties"][key]["items"]["type"] = "string"
|
command_function["parameters"]["properties"][os_value]["items"] = {}
|
||||||
|
command_function["parameters"]["properties"][os_value]["items"]["type"] = "string"
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
output_str = "\n".join(output_list)
|
output_str = "\n".join(list(set(output_list)))
|
||||||
command_input = f"input: {user_input}\n\nDevices:\n{output_str}"
|
command_input = f"input: {user_input}\n\nOS:\n{output_str}"
|
||||||
message = []
|
message = []
|
||||||
message.append({"role": "system", "content": dedent(self.__prompt["command_system"]).strip()})
|
message.append({"role": "system", "content": dedent(self.__prompt["command_system"]).strip()})
|
||||||
message.append({"role": "user", "content": dedent(self.__prompt["command_user"]).strip()})
|
message.append({"role": "user", "content": dedent(self.__prompt["command_user"]).strip()})
|
||||||
@ -252,7 +254,7 @@ Categorize the user's request based on the operation they want to perform on the
|
|||||||
output = {}
|
output = {}
|
||||||
result = response["choices"][0]["message"].to_dict()
|
result = response["choices"][0]["message"].to_dict()
|
||||||
json_result = json.loads(result["function_call"]["arguments"])
|
json_result = json.loads(result["function_call"]["arguments"])
|
||||||
output["response"] = self._clean_command_response(json_result)
|
output["response"] = self._clean_command_response(json_result, node_list)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def _get_filter(self, user_input, chat_history = None):
|
def _get_filter(self, user_input, chat_history = None):
|
||||||
|
@ -72,7 +72,7 @@ class connapp:
|
|||||||
#NODEPARSER
|
#NODEPARSER
|
||||||
nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), formatter_class=argparse.RawTextHelpFormatter)
|
nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), 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, help=self._help("node"))
|
||||||
nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect")
|
nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect")
|
||||||
nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect")
|
nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect")
|
||||||
nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect")
|
nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect")
|
||||||
@ -100,6 +100,8 @@ class connapp:
|
|||||||
#LISTPARSER
|
#LISTPARSER
|
||||||
lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders")
|
lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders")
|
||||||
lsparser.add_argument("ls", action=self._store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False)
|
lsparser.add_argument("ls", action=self._store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False)
|
||||||
|
lsparser.add_argument("--filter", nargs=1, help="Filter results")
|
||||||
|
lsparser.add_argument("--format", nargs=1, help="Format of the output of nodes using {name}, {NAME}, {location}, {LOCATION}, {host} and {HOST}")
|
||||||
lsparser.set_defaults(func=self._func_others)
|
lsparser.set_defaults(func=self._func_others)
|
||||||
#BULKPARSER
|
#BULKPARSER
|
||||||
bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk")
|
bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk")
|
||||||
@ -206,24 +208,31 @@ class connapp:
|
|||||||
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 = self.config._getallnodes(args.data)
|
||||||
if len(matches) == 0:
|
if len(matches) == 0:
|
||||||
print("{} not found".format(args.data))
|
print("{} not found".format(args.data))
|
||||||
exit(2)
|
exit(2)
|
||||||
question = [inquirer.Confirm("delete", message="Are you sure you want to delete {}?".format(matches[0]))]
|
print("Removing: {}".format(matches))
|
||||||
|
question = [inquirer.Confirm("delete", message="Are you sure you want to continue?")]
|
||||||
confirm = inquirer.prompt(question)
|
confirm = inquirer.prompt(question)
|
||||||
if confirm == None:
|
if confirm == None:
|
||||||
exit(7)
|
exit(7)
|
||||||
if confirm["delete"]:
|
if confirm["delete"]:
|
||||||
uniques = self.config._explode_unique(matches[0])
|
|
||||||
if args.data.startswith("@"):
|
if args.data.startswith("@"):
|
||||||
|
uniques = self.config._explode_unique(matches[0])
|
||||||
self.config._folder_del(**uniques)
|
self.config._folder_del(**uniques)
|
||||||
else:
|
else:
|
||||||
self.config._connections_del(**uniques)
|
for node in matches:
|
||||||
|
nodeuniques = self.config._explode_unique(node)
|
||||||
|
self.config._connections_del(**nodeuniques)
|
||||||
self.config._saveconfig(self.config.file)
|
self.config._saveconfig(self.config.file)
|
||||||
|
if len(matches) == 1:
|
||||||
print("{} deleted succesfully".format(matches[0]))
|
print("{} deleted succesfully".format(matches[0]))
|
||||||
|
else:
|
||||||
|
print(f"{len(matches)} nodes deleted succesfully")
|
||||||
|
|
||||||
def _add(self, args):
|
def _add(self, args):
|
||||||
|
args.data = self._type_node(args.data)
|
||||||
if args.data == None:
|
if args.data == None:
|
||||||
print("Missing argument node")
|
print("Missing argument node")
|
||||||
exit(3)
|
exit(3)
|
||||||
@ -302,7 +311,6 @@ class connapp:
|
|||||||
if args.data == None:
|
if args.data == None:
|
||||||
print("Missing argument node")
|
print("Missing argument node")
|
||||||
exit(3)
|
exit(3)
|
||||||
# matches = list(filter(lambda k: k == args.data, self.nodes))
|
|
||||||
matches = self.config._getallnodes(args.data)
|
matches = self.config._getallnodes(args.data)
|
||||||
if len(matches) == 0:
|
if len(matches) == 0:
|
||||||
print("No connection found with filter: {}".format(args.data))
|
print("No connection found with filter: {}".format(args.data))
|
||||||
@ -438,7 +446,30 @@ class connapp:
|
|||||||
return actions.get(args.command)(args)
|
return actions.get(args.command)(args)
|
||||||
|
|
||||||
def _ls(self, args):
|
def _ls(self, args):
|
||||||
print(*getattr(self, args.data), sep="\n")
|
items = getattr(self, args.data)
|
||||||
|
if args.filter:
|
||||||
|
items = [ item for item in items if re.search(args.filter[0], item)]
|
||||||
|
if args.format and args.data == "nodes":
|
||||||
|
newitems = []
|
||||||
|
for i in items:
|
||||||
|
formated = {}
|
||||||
|
info = self.config.getitem(i)
|
||||||
|
if "@" in i:
|
||||||
|
name_part, location_part = i.split("@", 1)
|
||||||
|
formated["location"] = "@" + location_part
|
||||||
|
else:
|
||||||
|
name_part = i
|
||||||
|
formated["location"] = ""
|
||||||
|
formated["name"] = name_part
|
||||||
|
formated["host"] = info["host"]
|
||||||
|
items_copy = list(formated.items())
|
||||||
|
for key, value in items_copy:
|
||||||
|
upper_key = key.upper()
|
||||||
|
upper_value = value.upper()
|
||||||
|
formated[upper_key] = upper_value
|
||||||
|
newitems.append(args.format[0].format(**formated))
|
||||||
|
items = newitems
|
||||||
|
print(*items, sep="\n")
|
||||||
|
|
||||||
def _mvcp(self, args):
|
def _mvcp(self, args):
|
||||||
if not self.case:
|
if not self.case:
|
||||||
@ -757,14 +788,13 @@ class connapp:
|
|||||||
|
|
||||||
def _node_run(self, args):
|
def _node_run(self, args):
|
||||||
command = " ".join(args.data[1:])
|
command = " ".join(args.data[1:])
|
||||||
matches = list(filter(lambda k: k == args.data[0], self.nodes))
|
script = {}
|
||||||
if len(matches) == 0:
|
script["name"] = "Output"
|
||||||
print("{} not found".format(args.data[0]))
|
script["action"] = "run"
|
||||||
exit(2)
|
script["nodes"] = args.data[0]
|
||||||
node = self.config.getitem(matches[0])
|
script["commands"] = [command]
|
||||||
node = self.node(matches[0],**node, config = self.config)
|
script["output"] = "stdout"
|
||||||
node.run(command)
|
self._cli_run(script)
|
||||||
print(node.output)
|
|
||||||
|
|
||||||
def _yaml_generate(self, args):
|
def _yaml_generate(self, args):
|
||||||
if os.path.exists(args.data[0]):
|
if os.path.exists(args.data[0]):
|
||||||
@ -800,7 +830,11 @@ class connapp:
|
|||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
print("'{}' is mandatory".format(e.args[0]))
|
print("'{}' is mandatory".format(e.args[0]))
|
||||||
exit(11)
|
exit(11)
|
||||||
nodes = self.connnodes(self.config.getitems(nodelist), config = self.config)
|
nodes = self.config._getallnodes(nodelist)
|
||||||
|
if len(nodes) == 0:
|
||||||
|
print("{} don't match any node".format(nodelist))
|
||||||
|
exit(2)
|
||||||
|
nodes = self.connnodes(self.config.getitems(nodes), config = self.config)
|
||||||
stdout = False
|
stdout = False
|
||||||
if output is None:
|
if output is None:
|
||||||
pass
|
pass
|
||||||
@ -818,9 +852,12 @@ class connapp:
|
|||||||
args.update(thisoptions)
|
args.update(thisoptions)
|
||||||
except:
|
except:
|
||||||
options = None
|
options = None
|
||||||
|
try:
|
||||||
size = str(os.get_terminal_size())
|
size = str(os.get_terminal_size())
|
||||||
p = re.search(r'.*columns=([0-9]+)', size)
|
p = re.search(r'.*columns=([0-9]+)', size)
|
||||||
columns = int(p.group(1))
|
columns = int(p.group(1))
|
||||||
|
except:
|
||||||
|
columns = 80
|
||||||
if action == "run":
|
if action == "run":
|
||||||
nodes.run(**args)
|
nodes.run(**args)
|
||||||
print(script["name"].upper() + "-" * (columns - len(script["name"])))
|
print(script["name"].upper() + "-" * (columns - len(script["name"])))
|
||||||
@ -1162,12 +1199,12 @@ class connapp:
|
|||||||
|
|
||||||
def _type_node(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$")):
|
def _type_node(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$")):
|
||||||
if not pat.match(arg_value):
|
if not pat.match(arg_value):
|
||||||
raise argparse.ArgumentTypeError
|
raise ValueError(f"Argument error: {arg_value}")
|
||||||
return arg_value
|
return arg_value
|
||||||
|
|
||||||
def _type_profile(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$#-]+$")):
|
def _type_profile(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$#-]+$")):
|
||||||
if not pat.match(arg_value):
|
if not pat.match(arg_value):
|
||||||
raise argparse.ArgumentTypeError
|
raise ValueError
|
||||||
return arg_value
|
return arg_value
|
||||||
|
|
||||||
def _help(self, type):
|
def _help(self, type):
|
||||||
|
@ -436,7 +436,7 @@ class node:
|
|||||||
passwords = self._passtx(self.password)
|
passwords = self._passtx(self.password)
|
||||||
else:
|
else:
|
||||||
passwords = []
|
passwords = []
|
||||||
expects = ['yes/no', 'refused', 'supported', 'cipher', 'ssh-keygen.*\"', 'timeout', 'unavailable', 'closed', '[p|P]assword:|[u|U]sername:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, pexpect.TIMEOUT, "No route to host", "resolve hostname", "no matching"]
|
expects = ['yes/no', 'refused', 'supported', 'cipher', 'ssh-keygen.*\"', 'timeout|timed.out', 'unavailable', 'closed', '[p|P]assword:|[u|U]sername:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, pexpect.TIMEOUT, "No route to host", "resolve hostname", "no matching"]
|
||||||
elif self.protocol == "telnet":
|
elif self.protocol == "telnet":
|
||||||
cmd = "telnet " + self.host
|
cmd = "telnet " + self.host
|
||||||
if self.port != '':
|
if self.port != '':
|
||||||
@ -449,7 +449,7 @@ class node:
|
|||||||
passwords = self._passtx(self.password)
|
passwords = self._passtx(self.password)
|
||||||
else:
|
else:
|
||||||
passwords = []
|
passwords = []
|
||||||
expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'ssh-keygen.*\"', 'timeout', 'unavailable', 'closed', '[p|P]assword:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, pexpect.TIMEOUT, "No route to host", "resolve hostname", "no matching"]
|
expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'ssh-keygen.*\"', 'timeout|timed.out', 'unavailable', 'closed', '[p|P]assword:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, pexpect.TIMEOUT, "No route to host", "resolve hostname", "no matching"]
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid protocol: " + self.protocol)
|
raise ValueError("Invalid protocol: " + self.protocol)
|
||||||
attempts = 1
|
attempts = 1
|
||||||
@ -476,9 +476,6 @@ class node:
|
|||||||
else:
|
else:
|
||||||
self.missingtext = True
|
self.missingtext = True
|
||||||
break
|
break
|
||||||
if results == 4:
|
|
||||||
child.terminate()
|
|
||||||
return "Connection failed code:" + str(results) + "\n" + child.after.decode()
|
|
||||||
if results in [1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15]:
|
if results in [1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15]:
|
||||||
child.terminate()
|
child.terminate()
|
||||||
if results == 12 and attempts != max_attempts:
|
if results == 12 and attempts != max_attempts:
|
||||||
@ -486,7 +483,11 @@ class node:
|
|||||||
endloop = True
|
endloop = True
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
return "Connection failed code:" + str(results)
|
if results == 12:
|
||||||
|
after = "Connection timeout"
|
||||||
|
else:
|
||||||
|
after = child.after.decode()
|
||||||
|
return ("Connection failed code:" + str(results) + "\n" + child.before.decode() + after + child.readline().decode()).rstrip()
|
||||||
if results == 8:
|
if results == 8:
|
||||||
if len(passwords) > 0:
|
if len(passwords) > 0:
|
||||||
child.sendline(passwords[i])
|
child.sendline(passwords[i])
|
||||||
|
@ -639,7 +639,7 @@ __pdoc__ = {
|
|||||||
try:
|
try:
|
||||||
self.model = self.config.config["openai"]["model"]
|
self.model = self.config.config["openai"]["model"]
|
||||||
except:
|
except:
|
||||||
self.model = "gpt-3.5-turbo-0613"
|
self.model = "gpt-3.5-turbo"
|
||||||
self.temp = temp
|
self.temp = temp
|
||||||
self.__prompt = {}
|
self.__prompt = {}
|
||||||
self.__prompt["original_system"] = """
|
self.__prompt["original_system"] = """
|
||||||
@ -698,7 +698,7 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
"""
|
"""
|
||||||
self.__prompt["original_function"]["parameters"]["required"] = ["type", "filter"]
|
self.__prompt["original_function"]["parameters"]["required"] = ["type", "filter"]
|
||||||
self.__prompt["command_system"] = """
|
self.__prompt["command_system"] = """
|
||||||
For each device listed below, provide the command(s) needed to perform the specified action, depending on the device OS (e.g., Cisco IOSXR router, Linux server).
|
For each OS listed below, provide the command(s) needed to perform the specified action, depending on the device OS (e.g., Cisco IOSXR router, Linux server).
|
||||||
The application knows how to connect to devices via SSH, so you only need to provide the command(s) to run after connecting.
|
The application knows how to connect to devices via SSH, so you only need to provide the command(s) to run after connecting.
|
||||||
If the commands needed are not for the specific OS type, just send an empty list (e.g., []).
|
If the commands needed are not for the specific OS type, just send an empty list (e.g., []).
|
||||||
Note: Preserving the integrity of user-provided commands is of utmost importance. If a user has provided a specific command to run, include that command exactly as it was given, even if it's not recognized or understood. Under no circumstances should you modify or alter user-provided commands.
|
Note: Preserving the integrity of user-provided commands is of utmost importance. If a user has provided a specific command to run, include that command exactly as it was given, even if it's not recognized or understood. Under no circumstances should you modify or alter user-provided commands.
|
||||||
@ -706,14 +706,14 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
self.__prompt["command_user"]= """
|
self.__prompt["command_user"]= """
|
||||||
input: show me the full configuration for all this devices:
|
input: show me the full configuration for all this devices:
|
||||||
|
|
||||||
Devices:
|
OS:
|
||||||
router1: cisco ios
|
cisco ios:
|
||||||
"""
|
"""
|
||||||
self.__prompt["command_assistant"] = {"name": "get_commands", "arguments": "{\n \"router1\": \"show running-configuration\"\n}"}
|
self.__prompt["command_assistant"] = {"name": "get_commands", "arguments": "{\n \"cisco ios\": \"show running-configuration\"\n}"}
|
||||||
self.__prompt["command_function"] = {}
|
self.__prompt["command_function"] = {}
|
||||||
self.__prompt["command_function"]["name"] = "get_commands"
|
self.__prompt["command_function"]["name"] = "get_commands"
|
||||||
self.__prompt["command_function"]["descriptions"] = """
|
self.__prompt["command_function"]["descriptions"] = """
|
||||||
For each device listed below, provide the command(s) needed to perform the specified action, depending on the device OS (e.g., Cisco IOSXR router, Linux server).
|
For each OS listed below, provide the command(s) needed to perform the specified action, depending on the device OS (e.g., Cisco IOSXR router, Linux server).
|
||||||
The application knows how to connect to devices via SSH, so you only need to provide the command(s) to run after connecting.
|
The application knows how to connect to devices via SSH, so you only need to provide the command(s) to run after connecting.
|
||||||
If the commands needed are not for the specific OS type, just send an empty list (e.g., []).
|
If the commands needed are not for the specific OS type, just send an empty list (e.g., []).
|
||||||
"""
|
"""
|
||||||
@ -774,16 +774,16 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
myfunction = False
|
myfunction = False
|
||||||
return myfunction
|
return myfunction
|
||||||
|
|
||||||
def _clean_command_response(self, raw_response):
|
def _clean_command_response(self, raw_response, node_list):
|
||||||
#Parse response for command request to openAI GPT.
|
#Parse response for command request to openAI GPT.
|
||||||
info_dict = {}
|
info_dict = {}
|
||||||
info_dict["commands"] = []
|
info_dict["commands"] = []
|
||||||
info_dict["variables"] = {}
|
info_dict["variables"] = {}
|
||||||
info_dict["variables"]["__global__"] = {}
|
info_dict["variables"]["__global__"] = {}
|
||||||
for key, value in raw_response.items():
|
for key, value in node_list.items():
|
||||||
key = key.strip()
|
|
||||||
newvalue = {}
|
newvalue = {}
|
||||||
for i,e in enumerate(value, start=1):
|
commands = raw_response[value]
|
||||||
|
for i,e in enumerate(commands, start=1):
|
||||||
newvalue[f"command{i}"] = e
|
newvalue[f"command{i}"] = e
|
||||||
if f"{{command{i}}}" not in info_dict["commands"]:
|
if f"{{command{i}}}" not in info_dict["commands"]:
|
||||||
info_dict["commands"].append(f"{{command{i}}}")
|
info_dict["commands"].append(f"{{command{i}}}")
|
||||||
@ -795,20 +795,22 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
#Send the request for commands for each device to openAI GPT.
|
#Send the request for commands for each device to openAI GPT.
|
||||||
output_list = []
|
output_list = []
|
||||||
command_function = deepcopy(self.__prompt["command_function"])
|
command_function = deepcopy(self.__prompt["command_function"])
|
||||||
|
node_list = {}
|
||||||
for key, value in nodes.items():
|
for key, value in nodes.items():
|
||||||
tags = value.get('tags', {})
|
tags = value.get('tags', {})
|
||||||
try:
|
try:
|
||||||
if os_value := tags.get('os'):
|
if os_value := tags.get('os'):
|
||||||
output_list.append(f"{key}: {os_value}")
|
node_list[key] = os_value
|
||||||
command_function["parameters"]["properties"][key] = {}
|
output_list.append(f"{os_value}")
|
||||||
command_function["parameters"]["properties"][key]["type"] = "array"
|
command_function["parameters"]["properties"][os_value] = {}
|
||||||
command_function["parameters"]["properties"][key]["description"] = f"OS: {os_value}"
|
command_function["parameters"]["properties"][os_value]["type"] = "array"
|
||||||
command_function["parameters"]["properties"][key]["items"] = {}
|
command_function["parameters"]["properties"][os_value]["description"] = f"OS: {os_value}"
|
||||||
command_function["parameters"]["properties"][key]["items"]["type"] = "string"
|
command_function["parameters"]["properties"][os_value]["items"] = {}
|
||||||
|
command_function["parameters"]["properties"][os_value]["items"]["type"] = "string"
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
output_str = "\n".join(output_list)
|
output_str = "\n".join(list(set(output_list)))
|
||||||
command_input = f"input: {user_input}\n\nDevices:\n{output_str}"
|
command_input = f"input: {user_input}\n\nOS:\n{output_str}"
|
||||||
message = []
|
message = []
|
||||||
message.append({"role": "system", "content": dedent(self.__prompt["command_system"]).strip()})
|
message.append({"role": "system", "content": dedent(self.__prompt["command_system"]).strip()})
|
||||||
message.append({"role": "user", "content": dedent(self.__prompt["command_user"]).strip()})
|
message.append({"role": "user", "content": dedent(self.__prompt["command_user"]).strip()})
|
||||||
@ -825,7 +827,7 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
output = {}
|
output = {}
|
||||||
result = response["choices"][0]["message"].to_dict()
|
result = response["choices"][0]["message"].to_dict()
|
||||||
json_result = json.loads(result["function_call"]["arguments"])
|
json_result = json.loads(result["function_call"]["arguments"])
|
||||||
output["response"] = self._clean_command_response(json_result)
|
output["response"] = self._clean_command_response(json_result, node_list)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def _get_filter(self, user_input, chat_history = None):
|
def _get_filter(self, user_input, chat_history = None):
|
||||||
@ -1861,7 +1863,7 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
#NODEPARSER
|
#NODEPARSER
|
||||||
nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), formatter_class=argparse.RawTextHelpFormatter)
|
nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), 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, help=self._help("node"))
|
||||||
nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect")
|
nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect")
|
||||||
nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect")
|
nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect")
|
||||||
nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect")
|
nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect")
|
||||||
@ -1889,6 +1891,8 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
#LISTPARSER
|
#LISTPARSER
|
||||||
lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders")
|
lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders")
|
||||||
lsparser.add_argument("ls", action=self._store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False)
|
lsparser.add_argument("ls", action=self._store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False)
|
||||||
|
lsparser.add_argument("--filter", nargs=1, help="Filter results")
|
||||||
|
lsparser.add_argument("--format", nargs=1, help="Format of the output of nodes using {name}, {NAME}, {location}, {LOCATION}, {host} and {HOST}")
|
||||||
lsparser.set_defaults(func=self._func_others)
|
lsparser.set_defaults(func=self._func_others)
|
||||||
#BULKPARSER
|
#BULKPARSER
|
||||||
bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk")
|
bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk")
|
||||||
@ -1995,24 +1999,31 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
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 = self.config._getallnodes(args.data)
|
||||||
if len(matches) == 0:
|
if len(matches) == 0:
|
||||||
print("{} not found".format(args.data))
|
print("{} not found".format(args.data))
|
||||||
exit(2)
|
exit(2)
|
||||||
question = [inquirer.Confirm("delete", message="Are you sure you want to delete {}?".format(matches[0]))]
|
print("Removing: {}".format(matches))
|
||||||
|
question = [inquirer.Confirm("delete", message="Are you sure you want to continue?")]
|
||||||
confirm = inquirer.prompt(question)
|
confirm = inquirer.prompt(question)
|
||||||
if confirm == None:
|
if confirm == None:
|
||||||
exit(7)
|
exit(7)
|
||||||
if confirm["delete"]:
|
if confirm["delete"]:
|
||||||
uniques = self.config._explode_unique(matches[0])
|
|
||||||
if args.data.startswith("@"):
|
if args.data.startswith("@"):
|
||||||
|
uniques = self.config._explode_unique(matches[0])
|
||||||
self.config._folder_del(**uniques)
|
self.config._folder_del(**uniques)
|
||||||
else:
|
else:
|
||||||
self.config._connections_del(**uniques)
|
for node in matches:
|
||||||
|
nodeuniques = self.config._explode_unique(node)
|
||||||
|
self.config._connections_del(**nodeuniques)
|
||||||
self.config._saveconfig(self.config.file)
|
self.config._saveconfig(self.config.file)
|
||||||
|
if len(matches) == 1:
|
||||||
print("{} deleted succesfully".format(matches[0]))
|
print("{} deleted succesfully".format(matches[0]))
|
||||||
|
else:
|
||||||
|
print(f"{len(matches)} nodes deleted succesfully")
|
||||||
|
|
||||||
def _add(self, args):
|
def _add(self, args):
|
||||||
|
args.data = self._type_node(args.data)
|
||||||
if args.data == None:
|
if args.data == None:
|
||||||
print("Missing argument node")
|
print("Missing argument node")
|
||||||
exit(3)
|
exit(3)
|
||||||
@ -2091,7 +2102,6 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
if args.data == None:
|
if args.data == None:
|
||||||
print("Missing argument node")
|
print("Missing argument node")
|
||||||
exit(3)
|
exit(3)
|
||||||
# matches = list(filter(lambda k: k == args.data, self.nodes))
|
|
||||||
matches = self.config._getallnodes(args.data)
|
matches = self.config._getallnodes(args.data)
|
||||||
if len(matches) == 0:
|
if len(matches) == 0:
|
||||||
print("No connection found with filter: {}".format(args.data))
|
print("No connection found with filter: {}".format(args.data))
|
||||||
@ -2227,7 +2237,30 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
return actions.get(args.command)(args)
|
return actions.get(args.command)(args)
|
||||||
|
|
||||||
def _ls(self, args):
|
def _ls(self, args):
|
||||||
print(*getattr(self, args.data), sep="\n")
|
items = getattr(self, args.data)
|
||||||
|
if args.filter:
|
||||||
|
items = [ item for item in items if re.search(args.filter[0], item)]
|
||||||
|
if args.format and args.data == "nodes":
|
||||||
|
newitems = []
|
||||||
|
for i in items:
|
||||||
|
formated = {}
|
||||||
|
info = self.config.getitem(i)
|
||||||
|
if "@" in i:
|
||||||
|
name_part, location_part = i.split("@", 1)
|
||||||
|
formated["location"] = "@" + location_part
|
||||||
|
else:
|
||||||
|
name_part = i
|
||||||
|
formated["location"] = ""
|
||||||
|
formated["name"] = name_part
|
||||||
|
formated["host"] = info["host"]
|
||||||
|
items_copy = list(formated.items())
|
||||||
|
for key, value in items_copy:
|
||||||
|
upper_key = key.upper()
|
||||||
|
upper_value = value.upper()
|
||||||
|
formated[upper_key] = upper_value
|
||||||
|
newitems.append(args.format[0].format(**formated))
|
||||||
|
items = newitems
|
||||||
|
print(*items, sep="\n")
|
||||||
|
|
||||||
def _mvcp(self, args):
|
def _mvcp(self, args):
|
||||||
if not self.case:
|
if not self.case:
|
||||||
@ -2546,14 +2579,13 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
|
|
||||||
def _node_run(self, args):
|
def _node_run(self, args):
|
||||||
command = " ".join(args.data[1:])
|
command = " ".join(args.data[1:])
|
||||||
matches = list(filter(lambda k: k == args.data[0], self.nodes))
|
script = {}
|
||||||
if len(matches) == 0:
|
script["name"] = "Output"
|
||||||
print("{} not found".format(args.data[0]))
|
script["action"] = "run"
|
||||||
exit(2)
|
script["nodes"] = args.data[0]
|
||||||
node = self.config.getitem(matches[0])
|
script["commands"] = [command]
|
||||||
node = self.node(matches[0],**node, config = self.config)
|
script["output"] = "stdout"
|
||||||
node.run(command)
|
self._cli_run(script)
|
||||||
print(node.output)
|
|
||||||
|
|
||||||
def _yaml_generate(self, args):
|
def _yaml_generate(self, args):
|
||||||
if os.path.exists(args.data[0]):
|
if os.path.exists(args.data[0]):
|
||||||
@ -2589,7 +2621,11 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
print("'{}' is mandatory".format(e.args[0]))
|
print("'{}' is mandatory".format(e.args[0]))
|
||||||
exit(11)
|
exit(11)
|
||||||
nodes = self.connnodes(self.config.getitems(nodelist), config = self.config)
|
nodes = self.config._getallnodes(nodelist)
|
||||||
|
if len(nodes) == 0:
|
||||||
|
print("{} don't match any node".format(nodelist))
|
||||||
|
exit(2)
|
||||||
|
nodes = self.connnodes(self.config.getitems(nodes), config = self.config)
|
||||||
stdout = False
|
stdout = False
|
||||||
if output is None:
|
if output is None:
|
||||||
pass
|
pass
|
||||||
@ -2607,9 +2643,12 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
args.update(thisoptions)
|
args.update(thisoptions)
|
||||||
except:
|
except:
|
||||||
options = None
|
options = None
|
||||||
|
try:
|
||||||
size = str(os.get_terminal_size())
|
size = str(os.get_terminal_size())
|
||||||
p = re.search(r'.*columns=([0-9]+)', size)
|
p = re.search(r'.*columns=([0-9]+)', size)
|
||||||
columns = int(p.group(1))
|
columns = int(p.group(1))
|
||||||
|
except:
|
||||||
|
columns = 80
|
||||||
if action == "run":
|
if action == "run":
|
||||||
nodes.run(**args)
|
nodes.run(**args)
|
||||||
print(script["name"].upper() + "-" * (columns - len(script["name"])))
|
print(script["name"].upper() + "-" * (columns - len(script["name"])))
|
||||||
@ -2951,12 +2990,12 @@ Categorize the user's request based on the operation they want to perform on
|
|||||||
|
|
||||||
def _type_node(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$")):
|
def _type_node(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$")):
|
||||||
if not pat.match(arg_value):
|
if not pat.match(arg_value):
|
||||||
raise argparse.ArgumentTypeError
|
raise ValueError(f"Argument error: {arg_value}")
|
||||||
return arg_value
|
return arg_value
|
||||||
|
|
||||||
def _type_profile(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$#-]+$")):
|
def _type_profile(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$#-]+$")):
|
||||||
if not pat.match(arg_value):
|
if not pat.match(arg_value):
|
||||||
raise argparse.ArgumentTypeError
|
raise ValueError
|
||||||
return arg_value
|
return arg_value
|
||||||
|
|
||||||
def _help(self, type):
|
def _help(self, type):
|
||||||
@ -3190,7 +3229,7 @@ tasks:
|
|||||||
#NODEPARSER
|
#NODEPARSER
|
||||||
nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), formatter_class=argparse.RawTextHelpFormatter)
|
nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), 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, help=self._help("node"))
|
||||||
nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect")
|
nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect")
|
||||||
nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect")
|
nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect")
|
||||||
nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect")
|
nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect")
|
||||||
@ -3218,6 +3257,8 @@ tasks:
|
|||||||
#LISTPARSER
|
#LISTPARSER
|
||||||
lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders")
|
lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders")
|
||||||
lsparser.add_argument("ls", action=self._store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False)
|
lsparser.add_argument("ls", action=self._store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False)
|
||||||
|
lsparser.add_argument("--filter", nargs=1, help="Filter results")
|
||||||
|
lsparser.add_argument("--format", nargs=1, help="Format of the output of nodes using {name}, {NAME}, {location}, {LOCATION}, {host} and {HOST}")
|
||||||
lsparser.set_defaults(func=self._func_others)
|
lsparser.set_defaults(func=self._func_others)
|
||||||
#BULKPARSER
|
#BULKPARSER
|
||||||
bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk")
|
bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk")
|
||||||
@ -3747,7 +3788,7 @@ tasks:
|
|||||||
passwords = self._passtx(self.password)
|
passwords = self._passtx(self.password)
|
||||||
else:
|
else:
|
||||||
passwords = []
|
passwords = []
|
||||||
expects = ['yes/no', 'refused', 'supported', 'cipher', 'ssh-keygen.*\"', 'timeout', 'unavailable', 'closed', '[p|P]assword:|[u|U]sername:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, pexpect.TIMEOUT, "No route to host", "resolve hostname", "no matching"]
|
expects = ['yes/no', 'refused', 'supported', 'cipher', 'ssh-keygen.*\"', 'timeout|timed.out', 'unavailable', 'closed', '[p|P]assword:|[u|U]sername:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, pexpect.TIMEOUT, "No route to host", "resolve hostname", "no matching"]
|
||||||
elif self.protocol == "telnet":
|
elif self.protocol == "telnet":
|
||||||
cmd = "telnet " + self.host
|
cmd = "telnet " + self.host
|
||||||
if self.port != '':
|
if self.port != '':
|
||||||
@ -3760,7 +3801,7 @@ tasks:
|
|||||||
passwords = self._passtx(self.password)
|
passwords = self._passtx(self.password)
|
||||||
else:
|
else:
|
||||||
passwords = []
|
passwords = []
|
||||||
expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'ssh-keygen.*\"', 'timeout', 'unavailable', 'closed', '[p|P]assword:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, pexpect.TIMEOUT, "No route to host", "resolve hostname", "no matching"]
|
expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'ssh-keygen.*\"', 'timeout|timed.out', 'unavailable', 'closed', '[p|P]assword:', r'>$|#$|\$$|>.$|#.$|\$.$', 'suspend', pexpect.EOF, pexpect.TIMEOUT, "No route to host", "resolve hostname", "no matching"]
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid protocol: " + self.protocol)
|
raise ValueError("Invalid protocol: " + self.protocol)
|
||||||
attempts = 1
|
attempts = 1
|
||||||
@ -3787,9 +3828,6 @@ tasks:
|
|||||||
else:
|
else:
|
||||||
self.missingtext = True
|
self.missingtext = True
|
||||||
break
|
break
|
||||||
if results == 4:
|
|
||||||
child.terminate()
|
|
||||||
return "Connection failed code:" + str(results) + "\n" + child.after.decode()
|
|
||||||
if results in [1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15]:
|
if results in [1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15]:
|
||||||
child.terminate()
|
child.terminate()
|
||||||
if results == 12 and attempts != max_attempts:
|
if results == 12 and attempts != max_attempts:
|
||||||
@ -3797,7 +3835,11 @@ tasks:
|
|||||||
endloop = True
|
endloop = True
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
return "Connection failed code:" + str(results)
|
if results == 12:
|
||||||
|
after = "Connection timeout"
|
||||||
|
else:
|
||||||
|
after = child.after.decode()
|
||||||
|
return ("Connection failed code:" + str(results) + "\n" + child.before.decode() + after + child.readline().decode()).rstrip()
|
||||||
if results == 8:
|
if results == 8:
|
||||||
if len(passwords) > 0:
|
if len(passwords) > 0:
|
||||||
child.sendline(passwords[i])
|
child.sendline(passwords[i])
|
||||||
|
Loading…
Reference in New Issue
Block a user