summaryrefslogtreecommitdiff
path: root/ovn/utilities/ovn-detrace.in
blob: c842adc3212cf0404deb9350b4c9a5683eda98ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#! @PYTHON@
#
# Copyright (c) 2017 eBay Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import getopt
import os
import re
import sys
import time

try:
    from ovs.db import idl
    from ovs import jsonrpc
    from ovs.poller import Poller
    from ovs.stream import Stream
except Exception:
    print("ERROR: Please install the correct Open vSwitch python support")
    print("       libraries (@VERSION@).")
    print("       Alternatively, check that your PYTHONPATH is pointing to")
    print("       the correct location.")
    sys.exit(1)


argv0 = sys.argv[0]


def usage():
    print """\
%(argv0)s:
usage: %(argv0)s < FILE
where FILE is output from ovs-appctl ofproto/trace.

The following options are also available:
  -h, --help                  display this help message
  -V, --version               display version information
  --ovnsb=DATABASE            use DATABASE as southbound DB
  --ovnnb=DATABASE            use DATABASE as northbound DB\
""" % {'argv0': argv0}
    sys.exit(0)


class OVSDB(object):
    @staticmethod
    def wait_for_db_change(idl):
        seq = idl.change_seqno
        stop = time.time() + 10
        while idl.change_seqno == seq and not idl.run():
            poller = Poller()
            idl.wait(poller)
            poller.block()
            if time.time() >= stop:
                raise Exception('Retry Timeout')

    def __init__(self, db_sock, schema_name):
        self._db_sock = db_sock
        self._txn = None
        schema = self._get_schema(schema_name)
        schema.register_all()
        self._idl_conn = idl.Idl(db_sock, schema)
        OVSDB.wait_for_db_change(self._idl_conn)  # Initial Sync with DB

    def _get_schema(self, schema_name):
        error, strm = Stream.open_block(Stream.open(self._db_sock))
        if error:
            raise Exception("Unable to connect to %s" % self._db_sock)
        rpc = jsonrpc.Connection(strm)
        req = jsonrpc.Message.create_request('get_schema', [schema_name])
        error, resp = rpc.transact_block(req)
        rpc.close()

        if error or resp.error:
            raise Exception('Unable to retrieve schema.')
        return idl.SchemaHelper(None, resp.result)

    def get_table(self, table_name):
        return self._idl_conn.tables[table_name]

    def _find_row(self, table_name, find):
        return next(
            (row for row in self.get_table(table_name).rows.values()
             if find(row)), None)

    def _find_row_by_name(self, table_name, value):
        return self._find_row(table_name, lambda row: row.name == value)

    def find_row_by_partial_uuid(self, table_name, value):
        return self._find_row(table_name, lambda row: value in str(row.uuid))


def get_lflow_from_cookie(ovnsb_db, cookie):
    return ovnsb_db.find_row_by_partial_uuid('Logical_Flow', cookie)


def print_lflow(lflow, prefix):
    ldp_uuid = lflow.logical_datapath.uuid
    ldp_name = str(lflow.logical_datapath.external_ids.get('name'))

    print '%sLogical datapath: "%s" (%s) [%s]' % (prefix,
                                                  ldp_name,
                                                  ldp_uuid,
                                                  lflow.pipeline)
    print "%sLogical flow: table=%s (%s), priority=%s, " \
          "match=(%s), actions=(%s)" % (prefix,
                                        lflow.table_id,
                                        lflow.external_ids.get('stage-name'),
                                        lflow.priority,
                                        str(lflow.match).strip('"'),
                                        str(lflow.actions).strip('"'))


def print_lflow_nb_hint(lflow, prefix, ovnnb_db):
    external_ids = lflow.external_ids
    if external_ids.get('stage-name') in ['ls_in_acl',
                                          'ls_out_acl']:
        acl_hint = external_ids.get('stage-hint')
        if not acl_hint:
            return
        acl = ovnnb_db.find_row_by_partial_uuid('ACL', acl_hint)
        if not acl:
            return
        output = "%sACL: %s, priority=%s, " \
                 "match=(%s), %s" % (prefix,
                                     acl.direction,
                                     acl.priority,
                                     acl.match.strip('"'),
                                     acl.action)
        if acl.log:
            output += ' (log)'
        print output


def main():
    try:
        options, args = getopt.gnu_getopt(sys.argv[1:], 'hV',
                                          ['help', 'version', 'ovnsb=', 'ovnnb='])
    except getopt.GetoptError, geo:
        sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
        sys.exit(1)

    ovnsb_db = None
    ovnnb_db = None

    for key, value in options:
        if key in ['-h', '--help']:
            usage()
        elif key in ['-V', '--version']:
            print "%s (Open vSwitch) @VERSION@" % argv0
        elif key in ['--ovnsb']:
            ovnsb_db = value
        elif key in ['--ovnnb']:
            ovnnb_db = value
        else:
            sys.exit(0)

    if len(args) != 0:
        sys.stderr.write("%s: non-option argument not supported "
                         "(use --help for help)\n" % argv0)
        sys.exit(1)

    ovs_rundir = os.getenv('OVS_RUNDIR', '@RUNDIR@')
    if not ovnsb_db:
        ovnsb_db = os.getenv('OVN_SB_DB')
        if not ovnsb_db:
            ovnsb_db = 'unix:%s/ovnsb_db.sock' % ovs_rundir

    if not ovnnb_db:
        ovnnb_db = os.getenv('OVN_NB_DB')
        if not ovnnb_db:
            ovnnb_db = 'unix:%s/ovnnb_db.sock' % ovs_rundir

    ovsdb_ovnsb = OVSDB(ovnsb_db, 'OVN_Southbound')
    ovsdb_ovnnb = OVSDB(ovnnb_db, 'OVN_Northbound')

    regex_cookie = re.compile(r'^.*cookie 0x([0-9a-fA-F]+)')
    regex_table_id = re.compile(r'^[0-9]+\.')
    cookie = None
    while True:
        line = sys.stdin.readline()
        if cookie:
            # print lflow info when the current flow block ends
            if regex_table_id.match(line) or line.strip() == '':
                lflow = get_lflow_from_cookie(ovsdb_ovnsb, cookie)
                print_lflow(lflow, "  * ")
                print_lflow_nb_hint(lflow, "    * ", ovsdb_ovnnb)
                cookie = None

        print line.strip()
        if line == "":
            break

        m = regex_cookie.match(line)
        if not m:
            continue
        cookie = m.group(1)


if __name__ == "__main__":
    main()


# Local variables:
# mode: python
# End: