def matchGroup(header_groups_indexes,line_group_indexes,last_element): ulgmodel.debug('matchGroup header_group_indexes='+str(header_groups_indexes)+' line_group_indexes='+str(line_group_indexes)) if(len(header_groups_indexes) == 1): return 0 # beginning of the second group is right of the beginning of the tested if((len(header_groups_indexes) > 1) and (header_groups_indexes[1][0] > line_group_indexes[0])): return 0 # beginning of the last (and the only possible) group is left # the beginning of the tested if(header_groups_indexes[-1][0] <= line_group_indexes[0]): return (len(header_groups_indexes)-1) # linear algorithm !!! # rewrite to tree(?) for hgipos,hgi in enumerate(header_groups_indexes): if((hgipos >= 1) and (hgipos < (len(header_groups_indexes)-1)) and (header_groups_indexes[hgipos-1][1] <= line_group_indexes[0]) and (header_groups_indexes[hgipos+1][0] >= line_group_indexes[1])): return hgipos if((last_element) and (hgipos >= 1) and (hgi[0] <= line_group_indexes[0]) and (len(header_groups_indexes)-1 > hgipos) and (header_groups_indexes[hgipos+1][0] > line_group_indexes[0])): return hgipos return None
def juniper_parse_sh_route(text, prependas): def split_ases(ases): return str.split(ases) DEFAULT_PARAMS = {"recuse": False, "reconly": False, "aggr": None} res = [] bgp_start = False params = dict(DEFAULT_PARAMS) for l in str.splitlines(text): ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: l=" + str(l)) if juniper_bgp_path_active_regex.match(l): ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: ACTIVE PATH") bgp_start = True params["recuse"] = True continue if juniper_bgp_path_regex.match(l): ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: NON-ACTIVE PATH") bgp_start = True continue m = juniper_bgp_path_content.match(l) if m and bgp_start: ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: path=" + str(m.group(1))) ases = [ulgmodel.annotateAS("AS" + str(asn)) for asn in [prependas] + split_ases(m.group(1))] res.append((ases, params)) params = dict(DEFAULT_PARAMS) bgp_start = False continue ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: res=" + str(res)) return res
def matchBIRDBGPRTLine(line): m = bird_rt_line_regexp.match(line) if(m): return m.groups() else: ulgmodel.debug("BirdShowRouteProtocolCommand: Can not parse line: "+line) return None
def commandThreadBody(session,decreaseUsageMethod): ulgmodel.debug("Running command: "+session.getCommand().getName()) try: session.getRouter().runAsyncCommand(session.getCommand(),session.getParameters(),FakeSessionFile(session)) session.getCommand().finishHook(session) except Exception as e: ulgmodel.log("ERROR: Exception occured while running a command:" + traceback.format_exc()) session.setResult("ERROR in commandThreadBody:\n"+traceback.format_exc()) finally: ulgmodel.debug("Command finished: "+session.getCommand().getName()) session.setFinished() decreaseUsageMethod()
def runCommand(self, session, user=None): class FakeSessionFile(object): def __init__(self, session): self.session = session def write(self, string): self.session.appendResult(string) # define trivial thread function def commandThreadBody(session, decreaseUsageMethod): ulgmodel.debug("Running command: " + session.getCommand().getName()) try: session.getRouter().runAsyncCommand( session.getCommand(), session.getParameters(), FakeSessionFile(session) ) session.getCommand().finishHook(session) except Exception as e: ulgmodel.log("ERROR: Exception occured while running a command:" + traceback.format_exc()) session.setResult("ERROR in commandThreadBody:\n" + traceback.format_exc()) finally: ulgmodel.debug("Command finished: " + session.getCommand().getName()) session.setFinished() decreaseUsageMethod() # check router ACL if user: if not session.getRouter().checkACL(user): self.stopSessionAccessDenied(session) return # try to increase usage counter if self.increaseUsage(): # start new thread if needed if defaults.always_start_thread or session.getRouter().getForkNeeded(): # fork a daemon process (fork two times to decouple with parent) sys.stdout.flush() child_pid = os.fork() if child_pid == 0: # detach process devnull = open(os.devnull, "w") os.dup2(devnull.fileno(), sys.stdout.fileno()) os.dup2(devnull.fileno(), sys.stderr.fileno()) # run the command ulgmodel.debug("Running in a forked process...") commandThreadBody(session, self.decreaseUsage) ulgmodel.debug("Forked process finished...") # exit the child sys.exit(0) else: ulgmodel.debug("Forked a new process PID: " + str(child_pid)) else: # directly run the selected action, DEPRECATED commandThreadBody(session, self.decreaseUsage) else: # stop and report no-op self.stopSessionOverlimit(session) return
def runCommand(self, session): class FakeSessionFile(object): def __init__(self, session): self.session = session def write(self, string): self.session.appendResult(string) # define trivial thread function def commandThreadBody(session, decreaseUsageMethod): ulgmodel.debug("Running command: " + session.getCommand().getName()) try: session.getRouter().runAsyncCommand(session.getCommand(), session.getParameters(), FakeSessionFile(session)) session.getCommand().finishHook(session) except Exception as e: ulgmodel.log( "ERROR: Exception occured while running a command:" + traceback.format_exc()) session.setResult("ERROR in commandThreadBody:\n" + traceback.format_exc()) finally: ulgmodel.debug("Command finished: " + session.getCommand().getName()) session.setFinished() decreaseUsageMethod() # try to increase usage counter if (self.increaseUsage()): # start new thread if needed if (defaults.always_start_thread or session.getRouter().getForkNeeded()): # fork a daemon process (fork two times to decouple with parent) sys.stdout.flush() child_pid = os.fork() if (child_pid == 0): # detach process devnull = open(os.devnull, 'w') os.dup2(devnull.fileno(), sys.stdout.fileno()) os.dup2(devnull.fileno(), sys.stderr.fileno()) # run the command ulgmodel.debug("Running in a forked process...") commandThreadBody(session, self.decreaseUsage) ulgmodel.debug("Forked process finished...") # exit the child sys.exit(0) else: ulgmodel.debug("Forked a new process PID: " + str(child_pid)) else: # directly run the selected action, DEPRECATED commandThreadBody(session, self.decreaseUsage) else: # stop and report no-op self.stopSessionOverlimit(session)
def matchGroup(header_groups_indexes, line_group_indexes, last_element): ulgmodel.debug('matchGroup header_group_indexes=' + str(header_groups_indexes) + ' line_group_indexes=' + str(line_group_indexes)) if (len(header_groups_indexes) == 1): return 0 # beginning of the second group is right of the beginning of the tested if ((len(header_groups_indexes) > 1) and (header_groups_indexes[1][0] > line_group_indexes[0])): return 0 # beginning of the last (and the only possible) group is left # the beginning of the tested if (header_groups_indexes[-1][0] <= line_group_indexes[0]): return (len(header_groups_indexes) - 1) # linear algorithm !!! # rewrite to tree(?) for hgipos, hgi in enumerate(header_groups_indexes): if ((hgipos >= 1) and (hgipos < (len(header_groups_indexes) - 1)) and (header_groups_indexes[hgipos - 1][1] <= line_group_indexes[0]) and (header_groups_indexes[hgipos + 1][0] >= line_group_indexes[1])): return hgipos if ((last_element) and (hgipos >= 1) and (hgi[0] <= line_group_indexes[0]) and (len(header_groups_indexes) - 1 > hgipos) and (header_groups_indexes[hgipos + 1][0] > line_group_indexes[0])): return hgipos return None
def juniper_parse_sh_route(text, prependas): def split_ases(ases): return str.split(ases) DEFAULT_PARAMS = {'recuse': False, 'reconly': False, 'aggr': None} res = [] bgp_start = False params = dict(DEFAULT_PARAMS) for l in str.splitlines(text): ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: l=" + str(l)) if (juniper_bgp_path_active_regex.match(l)): ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: ACTIVE PATH") bgp_start = True params['recuse'] = True continue if (juniper_bgp_path_regex.match(l)): ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: NON-ACTIVE PATH") bgp_start = True continue m = juniper_bgp_path_content.match(l) if (m and bgp_start): ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: path=" + str(m.group(1))) ases = [ ulgmodel.annotateAS("AS" + str(asn)) for asn in [prependas] + split_ases(m.group(1)) ] res.append((ases, params)) params = dict(DEFAULT_PARAMS) bgp_start = False continue ulgmodel.debug("JUNIPER PARSE SHOW ROUTE: res=" + str(res)) return res
def runRawCommand(self, command, outfile): def parseBirdSockLine(line): hm = bird_sock_header_regexp.match(line) if (hm): # first line of the reply return (int(hm.group(1)), hm.group(2)) em = bird_sock_reply_end_regexp.match(line) if (em): # most likely the last line of the reply return (int(em.group(1)), None) if (line[0] == '+'): # ignore async reply return (None, None) if (line[0] == ' '): # return reply line as it is (remove padding) return (None, line[1:]) raise Exception("Can not parse BIRD output line: " + line) def isBirdSockReplyEnd(code): if (code == None): return False if (code == 0): # end of reply return True elif (code == 13): # show status last line return True elif (code == 8001): # network not in table end return True elif (code >= 9000): # probably error return True else: return False # try: # open socket to BIRD s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.settimeout(defaults.default_bird_sock_timeout) s.connect(self.sock) # cretate FD for the socket sf = s.makefile() # wait for initial header l = sf.readline() # send the command string sf.write(command + "\n") sf.flush() # read and capture lines until the output delimiter string is hit while (True): l = sf.readline() ulgmodel.debug("Raw line read: " + l) # process line according to rules take out from the C code lp = parseBirdSockLine(l) if (isBirdSockReplyEnd(lp[0])): # End of reply (0000 or similar code) ulgmodel.debug("End of reply. Code=" + str(lp[0])) if (lp[1]): ulgmodel.debug("Last read line after normalize: " + lp[1]) outfile.write(lp[1].rstrip() + "\n") break else: if (lp[1]): ulgmodel.debug("Read line after normalize: " + lp[1]) outfile.write(lp[1].rstrip() + "\n") else: ulgmodel.debug("Read line was empty after normalize.") # close the socket and return captured result s.close()
def matchCiscoBGPLines(header,lines): # Match cisco lines formatted to be aligned to columns. Like: # Network Next Hop Metric LocPrf Weight Path # * 79.170.248.0/21 91.210.16.6 300 0 20723 i # *>i91.199.207.0/24 217.31.48.123 0 1024 0 44672 i # 0 1 2 3 4 5 6 # # Network Next Hop Metric LocPrf Weight Path # *>i2001:67C:278::/48 # 2001:1AB0:B0F4:FFFF::2 # 0 1024 0 51278 i # *> 2001:1AB0::/32 :: 0 32768 i # 0 1 2 3 4 5 6 # # First find boundaries, second section text (if it overflow next boundary # line wrap is expected) and then remove white spaces and return sections. # # ?1 hat happens when last element is line-wrapped? It looks like it does # not happen in my settings. def divideGroups(line,max_index_start=sys.maxint,table_line=False,first_group_length=3): # divide groups starting before max_index_start result = [] # when parsing table_line (not the header and not the continuation line) # cut off first N charactes and use them as the first group (flags) when there is # some non-blank characters in the first two groups (therefore the line is the leading) if(table_line): if(re.match('[^\s]+',line[0:(first_group_length+2)])): result.append([0,first_group_length]) line = ' '*first_group_length + line[first_group_length:] else: # parsing header, add virtual Status group to the header line = 'S'+line[1:] last_group = False for r in re.compile('[^\s]+').finditer(line): if(not last_group): result.append([r.start(),r.end()]) if(r.start() >= max_index_start): last_group = True # shortcut for empty lines / no results if(len(result)==0): return None # add tail to last groups result[-1][1] = len(line) # in header force the first group to spann till the end of the next group if(not table_line): result[0][1] = result[1][0]-1 return result def matchGroup(header_groups_indexes,line_group_indexes,last_element): ulgmodel.debug('matchGroup header_group_indexes='+str(header_groups_indexes)+' line_group_indexes='+str(line_group_indexes)) if(len(header_groups_indexes) == 1): return 0 # beginning of the second group is right of the beginning of the tested if((len(header_groups_indexes) > 1) and (header_groups_indexes[1][0] > line_group_indexes[0])): return 0 # beginning of the last (and the only possible) group is left # the beginning of the tested if(header_groups_indexes[-1][0] <= line_group_indexes[0]): return (len(header_groups_indexes)-1) # linear algorithm !!! # rewrite to tree(?) for hgipos,hgi in enumerate(header_groups_indexes): if((hgipos >= 1) and (hgipos < (len(header_groups_indexes)-1)) and (header_groups_indexes[hgipos-1][1] <= line_group_indexes[0]) and (header_groups_indexes[hgipos+1][0] >= line_group_indexes[1])): return hgipos if((last_element) and (hgipos >= 1) and (hgi[0] <= line_group_indexes[0]) and (len(header_groups_indexes)-1 > hgipos) and (header_groups_indexes[hgipos+1][0] > line_group_indexes[0])): return hgipos return None def normalize(instr): return instr.strip() hgidxs = divideGroups(header) result = [[]] for l in lines: # divide groups (leave the last group in one part) # define boundary of the first group by the first letter of the second group beginning lgps = divideGroups(l,hgidxs[-1][0],True,hgidxs[1][0]-1) if(lgps==None): continue for lgpidx,lgp in enumerate(lgps): gidx = matchGroup(hgidxs,lgp,(lgpidx == (len(lgps)-1))) if(gidx == None): raise Exception("No group matched for line indexes. line="+l+" header_group_indexes="+ str(hgidxs)+" line_group_index="+str(lgp)) if(gidx < len(result[-1])): result.append([]) while(gidx > len(result[-1])): result[-1].append('') result[-1].append(normalize(l[lgp[0]:lgp[1]])) ulgmodel.debug('bgpmatchlines:'+str(result)) return result
def matchCiscoBGPLines(header, lines): # Match cisco lines formatted to be aligned to columns. Like: # Network Next Hop Metric LocPrf Weight Path # * 79.170.248.0/21 91.210.16.6 300 0 20723 i # *>i91.199.207.0/24 217.31.48.123 0 1024 0 44672 i # 0 1 2 3 4 5 6 # # Network Next Hop Metric LocPrf Weight Path # *>i2001:67C:278::/48 # 2001:1AB0:B0F4:FFFF::2 # 0 1024 0 51278 i # *> 2001:1AB0::/32 :: 0 32768 i # 0 1 2 3 4 5 6 # # First find boundaries, second section text (if it overflow next boundary # line wrap is expected) and then remove white spaces and return sections. # # ?1 hat happens when last element is line-wrapped? It looks like it does # not happen in my settings. def divideGroups(line, max_index_start=sys.maxint, table_line=False, first_group_length=3): # divide groups starting before max_index_start result = [] # when parsing table_line (not the header and not the continuation line) # cut off first N charactes and use them as the first group (flags) when there is # some non-blank characters in the first two groups (therefore the line is the leading) if (table_line): if (re.match('[^\s]+', line[0:(first_group_length + 2)])): result.append([0, first_group_length]) line = ' ' * first_group_length + line[first_group_length:] else: # parsing header, add virtual Status group to the header line = 'S' + line[1:] last_group = False for r in re.compile('[^\s]+').finditer(line): if (not last_group): result.append([r.start(), r.end()]) if (r.start() >= max_index_start): last_group = True # shortcut for empty lines / no results if (len(result) == 0): return None # add tail to last groups result[-1][1] = len(line) # in header force the first group to spann till the end of the next group if (not table_line): result[0][1] = result[1][0] - 1 return result def matchGroup(header_groups_indexes, line_group_indexes, last_element): ulgmodel.debug('matchGroup header_group_indexes=' + str(header_groups_indexes) + ' line_group_indexes=' + str(line_group_indexes)) if (len(header_groups_indexes) == 1): return 0 # beginning of the second group is right of the beginning of the tested if ((len(header_groups_indexes) > 1) and (header_groups_indexes[1][0] > line_group_indexes[0])): return 0 # beginning of the last (and the only possible) group is left # the beginning of the tested if (header_groups_indexes[-1][0] <= line_group_indexes[0]): return (len(header_groups_indexes) - 1) # linear algorithm !!! # rewrite to tree(?) for hgipos, hgi in enumerate(header_groups_indexes): if ((hgipos >= 1) and (hgipos < (len(header_groups_indexes) - 1)) and (header_groups_indexes[hgipos - 1][1] <= line_group_indexes[0]) and (header_groups_indexes[hgipos + 1][0] >= line_group_indexes[1])): return hgipos if ((last_element) and (hgipos >= 1) and (hgi[0] <= line_group_indexes[0]) and (len(header_groups_indexes) - 1 > hgipos) and (header_groups_indexes[hgipos + 1][0] > line_group_indexes[0])): return hgipos return None def normalize(instr): return instr.strip() hgidxs = divideGroups(header) result = [[]] for l in lines: # divide groups (leave the last group in one part) # define boundary of the first group by the first letter of the second group beginning lgps = divideGroups(l, hgidxs[-1][0], True, hgidxs[1][0] - 1) if (lgps == None): continue for lgpidx, lgp in enumerate(lgps): gidx = matchGroup(hgidxs, lgp, (lgpidx == (len(lgps) - 1))) if (gidx == None): raise Exception("No group matched for line indexes. line=" + l + " header_group_indexes=" + str(hgidxs) + " line_group_index=" + str(lgp)) if (gidx < len(result[-1])): result.append([]) while (gidx > len(result[-1])): result[-1].append('') result[-1].append(normalize(l[lgp[0]:lgp[1]])) ulgmodel.debug('bgpmatchlines:' + str(result)) return result
def runRawCommand(self,command,outfile): def parseBirdSockLine(line): hm = bird_sock_header_regexp.match(line) if(hm): # first line of the reply return (int(hm.group(1)),hm.group(2)) em = bird_sock_reply_end_regexp.match(line) if(em): # most likely the last line of the reply return (int(em.group(1)),None) if(line[0] == '+'): # ignore async reply return (None,None) if(line[0] == ' '): # return reply line as it is (remove padding) return (None,line[1:]) raise Exception("Can not parse BIRD output line: "+line) def isBirdSockReplyEnd(code): if(code==None): return False if(code == 0): # end of reply return True elif(code == 13): # show status last line return True elif(code >= 9000): # probably error return True else: return False # try: # open socket to BIRD s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.settimeout(defaults.default_bird_sock_timeout) s.connect(self.sock) # cretate FD for the socket sf=s.makefile() # wait for initial header l = sf.readline() # send the command string sf.write(command+"\n") sf.flush() # read and capture lines until the output delimiter string is hit while(True): l = sf.readline() ulgmodel.debug("Raw line read: " + l) # process line according to rules take out from the C code lp = parseBirdSockLine(l) if(isBirdSockReplyEnd(lp[0])): # End of reply (0000 or similar code) ulgmodel.debug("End of reply. Code="+str(lp[0])) if(lp[1]): ulgmodel.debug("Last read line after normalize: " + lp[1]) outfile.write(lp[1].rstrip()+"\n") break else: if(lp[1]): ulgmodel.debug("Read line after normalize: " + lp[1]) outfile.write(lp[1].rstrip()+"\n") else: ulgmodel.debug("Read line was empty after normalize.") # close the socket and return captured result s.close()