def from_trace_file(source_filename): parrot_filename = None if source_filename.endswith(".trace"): parrot_filename = NSO_Trace_Parrot_Builder(source_filename).build() if parrot_filename: return parrot_filename Logger.fatal( f"Parrot Builder, unrecognized trace file type: {source_filename}")
def __init__(self, source_filename, target_filename=None): self.source_filename = pathlib.Path(source_filename) if not target_filename: target_filename = source_filename + ".parrot.xml" self.target_filename = pathlib.Path(target_filename) if not self.source_filename.exists(): Logger.fatal( f"Parrot Builder, can't find trace file: {source_filename}") self.catalog = {} self.catalog_meta = {}
def reply(self, incoming_message): try: response_msg = self.template.render( request=incoming_message, message_id=incoming_message.get_message_id(), session_id="4711", #FIXME str(self.session_id), ) return response_msg.strip() except Exception as ex: Logger.fatal( f"Template rendering error, {self.template}, {incoming_message.get_xml_text()}: {ex}" )
def run(self, host, port, parrot_file): Logger.info(f"Parroting {parrot_file}") try: parrot_path = pathlib.Path(parrot_file) env = Environment(loader=FileSystemLoader([parrot_path.parent], followlinks=True), autoescape=select_autoescape(['xml'])) self.server.set_template(env.get_template(str(parrot_path.name))) self.server.set_host_port(host, port) self.server.serve() except Exception as e: traceback.print_exc() Logger.fatal(f"Top level exception: {str(e)}")
def run_command_line(self, sys_argv=sys.argv): def usage(sys_argv): print(f'{sys_argv[0]} --netconf=[host:]port parrot-file.xml') verbosity = 4 host = "localhost" port = 8888 template_dirs = [] trace_files = [] parrot_file = None try: opts, args = getopt.getopt( sys_argv[1:], "hd:vt:m:", ["help", "debug=", "verbose", "netconf=", "template-dir="]) except getopt.GetoptError: usage(sys_argv) sys.exit(2) for opt, arg in opts: if opt in ('-h', '--help'): usage(sys_argv) sys.exit() elif opt in ("--netconf"): self.server = Parrot.NETCONF_Parrot() if ":" in arg: (host, port_str) = arg.split(":") port = int(port_str) else: port = int(arg) elif opt in ("-t", "--template-dir"): template_dirs += [arg] elif opt in ("-d", "--debug"): verbosity = int(arg) elif opt in ("-m", "--make-parrot"): trace_files += [arg] elif opt in ("-v", "--verbose"): verbosity += 1 else: Logger.fatal(f'Unknown option "{opt}".') sys.exit(2) for trace_file_name in trace_files: parrot_file = Parrot_Builder.from_trace_file(trace_file_name) if not parrot_file: if len(args) != 1: usage(sys_argv) Logger.fatal(f"{len(args)} parrot files given.", code=2) parrot_file = args[0] if not self.server: usage(sys_argv) Logger.fatal(f"{len(args)} server specified.", code=2) Logger.set_verbosity(verbosity) self.run(host, port, parrot_file)
def _record_message(self, message, meta): # This method should maybe parse the XML properly, but since # we also want this to work with potentially malformed XML, # it is instead making some assumptions about the XML encoding direction = NSO_Trace_Parrot_Builder._meta_dir(meta) if not direction: Logger.fatal(f"Message meta malformed: {meta}") message_id_attr_str = "message-id=" # Assume first occurrence of message-id is the NETCONF message-id pos = message.find(message_id_attr_str) if pos >= 0: pos += len(message_id_attr_str) delimiter = message[pos] # Assume message-id value is properly delimited if delimiter not in ['"', "'"]: Logger.fatal(f"Attribute message-id= malformed (1): {meta}") pos += 1 endpos = message[pos:].find(delimiter) # Assume message-id value is at most 100 chars if endpos < 0 or endpos > 100: Logger.fatal(f"Attribute message-id= malformed (2): {meta}") message_id = message[pos:pos + endpos] self.catalog[(direction, message_id)] = message self.catalog_meta[(direction, message_id)] = meta print( f"Recorded message {direction} {message_id} = {message[:40]}..." ) # Assume hello message is not namespaced, and has no redundant whitespace elif message.startswith("<hello "): self.catalog[(direction, "hello")] = message self.catalog_meta[(direction, "hello")] = meta elif direction == "meta": self.catalog[(direction, meta)] = message self.catalog_meta[(direction, meta)] = meta pass elif not message: pass else: Logger.fatal(f"Message not hello and lacks message-id: {meta}")
def listen(self): NETCONF_Server.set_instance(self) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #sock.bind(('', self.port)) sock.bind((self.host, self.port)) except Exception as e: Logger.fatal('Bind failed: ' + str(e)) try: sock.listen(100) except Exception as e: Logger.fatal('Listen failed: ' + str(e)) Logger.info(f'Listening for connections on port {self.port}...') host_key = None try: host_key = RSAKey(filename=self.host_key_filename) except: pass if not host_key: Logger.info(f'Generating new host key') host_key = RSAKey.generate(2048) if self.host_key_filename: host_key.write_private_key_file(self.host_key_filename, password=None) Logger.info(f"Wrote host key to file, '{self.host_key_filename}'") while True: try: Logger.info(f'Waiting for client to connect') client, addr = sock.accept() except Exception as e: Logger.fatal('Accept failed: ' + str(e)) self.sock = client (ip, port) = addr Logger.info(f'Client {ip}:{port} connected') self.handle_connection(host_key) Logger.info(f'Client {ip}:{port} disconnected')