2 from pprint import pformat
9 logger = logging.getLogger("ovs.flows")
17 # curl -s -u admin:admin -X GET 127.0.0.1:8080/restconf/operational/odl-l3vpn:learnt-vpn-vip-to-port-data
18 # - check if external ip is resolved, devstack uses port 8087
23 N_PACKETS = "n_packets"
27 IDLE_TIMEOUT = "idle_timeout"
28 SEND_FLOW_REMOVED = "send_flow_rem"
33 def __init__(self, data):
37 self.data = data.splitlines()
38 elif type(data) is list:
41 logger.error("init: data is not a supported type")
44 logger.info("init: Copied %d lines", len(self.data))
47 logger.info("init: data has been processed and formatted")
49 def pretty_print(self, data):
50 return "{}".format(pformat(data))
52 def process_data(self):
54 Process the dump-flows data into a map.
56 The processing will tokenize the parts in each line of the flow dump.
58 :return: A list of dictionaries of parsed tokens per line
60 # cookie=0x805138a, duration=193.107s, table=50, n_packets=119, n_bytes=11504, idle_timeout=300,
61 # send_flow_rem priority=20,metadata=0x2138a000000/0xfffffffff000000,dl_src=fa:16:3e:15:a8:66
62 # actions=goto_table:51
65 if len(self.data) == 0:
66 logger.warn("There is no data to process")
69 # skip the header if present
70 if "OFPST_FLOW" in self.data[0]:
72 logger.debug("process_data: will skip first line: OFPST_FLOW line")
75 if "jenkins" in self.data[-1]:
76 end = len(self.data) - 1
77 logger.debug("process_data: will skip last line: jenkins line")
81 # Parse each line of the data. Each line is a single flow.
82 # Create a dictionary of all tokens in that flow.
83 # Append this flow dictionary to a list of flows.
84 for line in self.data[self.start:end]:
86 pline[Flows.IDLE_TIMEOUT] = "---"
87 pline[Flows.SEND_FLOW_REMOVED] = "-"
88 tokens = line.split(" ")
90 # most lines are key=value so look for that pattern
91 splits = token.split("=", 1)
93 if Flows.PRIORITY in splits[0]:
94 splitp = splits[1].split(",", 1)
96 pline[Flows.PRIORITY] = splitp[0]
97 pline[Flows.MATCHES] = splitp[1]
99 pline[Flows.PRIORITY] = splitp[0]
100 pline[Flows.MATCHES] = ""
102 pline[splits[0]] = splits[1].rstrip(",")
103 elif token == Flows.SEND_FLOW_REMOVED:
104 # send_flow_rem is a single token without a value
106 self.pdata.append(pline)
107 logger.debug("process_data: Processed line %d into: \n%s",
108 self.start + len(self.pdata), pformat(pline))
109 logger.info("process_data: Processed %d lines, skipped %d", len(self.pdata),
110 self.start + len(self.data) - end)
114 def re_table(self, match):
116 regex function to add the table name to table lines
118 :param match: The regex match
119 :return: The new line with table name
122 if match.group(Flows.GOTO) is not None:
123 table_id = int(match.group(Flows.GOTO))
124 elif match.group(Flows.RESUBMIT) is not None:
125 table_id = int(match.group(Flows.RESUBMIT))
129 rep = "{}({})".format(match.group(), tables.get_table_name(table_id))
132 def format_data(self):
133 if len(self.pdata) == 0:
134 logger.warn("There is no data to process")
136 header = "{:3} {:9} {:8} {:13} {:6} {:12} {:1} {:3} {:5}\n" \
139 .format("nnn", Flows.COOKIE, Flows.DURATION, Flows.TABLE, "n_pack", Flows.N_BYTES,
143 header_under = "--- --------- -------- ------------- ------ ------------ - --- -----\n"
145 # Match goto_table: nnn or resubmit(,nnn) and return as goto or resubmit match group
146 re_gt = re.compile(r"goto_table:(?P<goto>\d{1,3})|"
147 r"resubmit\(,(?P<resubmit>\d{1,3})\)")
149 # Add the header as the first two lines of formatted data
150 self.fdata = [header, header_under]
152 # Format each line of parsed data
153 for i, line in enumerate(self.pdata):
154 logger.debug("format_data: processing line %d: %s", self.start + i + 1, line)
156 #if Flows.SEND_FLOW_REMOVED in line:
157 # send_flow_rem = " {} ".format(line[Flows.SEND_FLOW_REMOVED])
161 #if Flows.IDLE_TIMEOUT in line:
162 # idle_timeo = " {}={}".format(Flows.IDLE_TIMEOUT, line[Flows.IDLE_TIMEOUT])
166 if Flows.ACTIONS in line:
167 nactions = re_gt.sub(self.re_table, line[Flows.ACTIONS])
169 logger.warn("Missing actions in %s", line)
172 fline = "{:3} {:9} {:8} {:3} {:13} {:6} {:12} {:1} {:3} {:5}\n" \
175 .format(i + 1, line[Flows.COOKIE], line[Flows.DURATION],
176 line[Flows.TABLE], tables.get_table_name(int(line[Flows.TABLE])),
177 line[Flows.N_PACKETS], line[Flows.N_BYTES],
178 line[Flows.SEND_FLOW_REMOVED][0], line[Flows.IDLE_TIMEOUT],
179 line[Flows.PRIORITY],
182 self.fdata.append(fline)
183 logger.debug("format_data: formatted line %d: %s", self.start + i + 1, fline)
186 def write_fdata(self, filename):
187 request.write_file(filename, self.fdata)