From patchwork Thu Mar 23 06:15:16 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Han Zhou X-Patchwork-Id: 742489 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vpbsc14L0z9s7v for ; Thu, 23 Mar 2017 17:16:20 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="CbYrPAE4"; dkim-atps=neutral Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id E67ACBAA; Thu, 23 Mar 2017 06:15:57 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 77471B43 for ; Thu, 23 Mar 2017 06:15:56 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pf0-f193.google.com (mail-pf0-f193.google.com [209.85.192.193]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id BFB50FC for ; Thu, 23 Mar 2017 06:15:55 +0000 (UTC) Received: by mail-pf0-f193.google.com with SMTP id o126so30114575pfb.1 for ; Wed, 22 Mar 2017 23:15:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=qm/gv0Yc/8QlZaTawK9y8eALdVjOE5bo2il73MR9HIU=; b=CbYrPAE47XAbzJot1W8Urq79jwAdPkewlE93UjyRSlu0vNvkabl8NbufiPKZeuuAiu MW7j5NIQZ37zH2cpsfVYw/BGtGjOhJpoj9Ootts5lxe1rgA32lWhhCMx1CaQ+K/NuYgn 0lsuJYg3KmIMbZASgjFpopSZZztWPbmqKMY6doVcoVHizjAZ5KWW7jGXQYDY2D6ILJLh EF94nQJj/9uvz0KRU/+BDIDCQdSoOekuvtTiG4zfBYt4bkbEAeS4Ye+fEakn3A0Gv/00 0PXm97smwuxkoi1ir/QaMIvPBv6tEuUVa3KvrvoqiPXPk2nqIVBdWHdw53aMrSbeZPjO uAtw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=qm/gv0Yc/8QlZaTawK9y8eALdVjOE5bo2il73MR9HIU=; b=dHnaZAAuGu1VlD7E4sLysno6JBJinwDTxpLx6X0rBKil4ZRMpHhg+eG72rnL5oRt/u X0RctSlXAp4PqBeBNH8+/Jx3sepr3kWrZi1LViXbdLggldpiqYDZpikctix7rcLhwI6z udYbnipzOsE/jTrhHiAvp0Y/U783KLQS//GsQ/+QTczBikzh7pa0SxnYxNSR9H+XVnpc nf2/kbQTgDVkoA/+A1cuymksQNVp3q9gvhclC7gFXoHDGrbLczgpBHlnFnKBVPUNT4KI PC/T16RFeSNwAmmphvUgLWzdML3cpov9vSCv4oFL3uieqo7wTEfvp3A1SblpmNryvlsU QX5g== X-Gm-Message-State: AFeK/H11NBCn9D6gaAWGvdRChhPXIelzPRr1PQYQER6YUmpOnpC1cZKiMazdMpPtdNptHQ== X-Received: by 10.98.60.20 with SMTP id j20mr1065965pfa.128.1490249755261; Wed, 22 Mar 2017 23:15:55 -0700 (PDT) Received: from localhost.localdomain.localdomain (c-50-150-76-39.hsd1.ca.comcast.net. [50.150.76.39]) by smtp.gmail.com with ESMTPSA id r17sm7495910pgg.19.2017.03.22.23.15.54 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 22 Mar 2017 23:15:54 -0700 (PDT) From: Han Zhou To: dev@openvswitch.org Date: Wed, 22 Mar 2017 23:15:16 -0700 Message-Id: <1490249716-57081-2-git-send-email-zhouhan@gmail.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1490249716-57081-1-git-send-email-zhouhan@gmail.com> References: <1490249716-57081-1-git-send-email-zhouhan@gmail.com> X-Spam-Status: No, score=-1.5 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM autolearn=no version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH 2/2] ovn-detrace: A tool decoding ofproto/trace output for ovn debugging. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org A python script to decode ofproto/trace output to add ovn lflow information inline. It expands lflow further to ACLs when relevant. $ ovs-appctl ofproto/trace ... | ovn-decode Signed-off-by: Han Zhou --- manpages.mk | 8 ++ ovn/utilities/automake.mk | 15 ++- ovn/utilities/ovn-detrace.1.in | 37 +++++++ ovn/utilities/ovn-detrace.in | 215 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 271 insertions(+), 4 deletions(-) create mode 100644 ovn/utilities/ovn-detrace.1.in create mode 100755 ovn/utilities/ovn-detrace.in diff --git a/manpages.mk b/manpages.mk index 742bd66..38b9468 100644 --- a/manpages.mk +++ b/manpages.mk @@ -1,5 +1,13 @@ # Generated automatically -- do not modify! -*- buffer-read-only: t -*- +ovn/utilities/ovn-detrace.1: \ + ovn/utilities/ovn-detrace.1.in \ + lib/common-syn.man \ + lib/common.man +ovn/utilities/ovn-detrace.1.in: +lib/common-syn.man: +lib/common.man: + ovn/utilities/ovn-sbctl.8: \ ovn/utilities/ovn-sbctl.8.in \ lib/common.man \ diff --git a/ovn/utilities/automake.mk b/ovn/utilities/automake.mk index 08e48ea..b96f9bf 100644 --- a/ovn/utilities/automake.mk +++ b/ovn/utilities/automake.mk @@ -6,14 +6,18 @@ man_MANS += \ ovn/utilities/ovn-ctl.8 \ ovn/utilities/ovn-nbctl.8 \ ovn/utilities/ovn-sbctl.8 \ - ovn/utilities/ovn-trace.8 + ovn/utilities/ovn-trace.8 \ + ovn/utilities/ovn-detrace.1 -MAN_ROOTS += ovn/utilities/ovn-sbctl.8.in +MAN_ROOTS += \ + ovn/utilities/ovn-sbctl.8.in \ + ovn/utilities/ovn-detrace.1.in # Docker drivers bin_SCRIPTS += \ ovn/utilities/ovn-docker-overlay-driver \ - ovn/utilities/ovn-docker-underlay-driver + ovn/utilities/ovn-docker-underlay-driver \ + ovn/utilities/ovn-detrace EXTRA_DIST += \ ovn/utilities/ovn-ctl \ @@ -22,13 +26,16 @@ EXTRA_DIST += \ ovn/utilities/ovn-docker-underlay-driver \ ovn/utilities/ovn-nbctl.8.xml \ ovn/utilities/ovn-trace.8.xml \ + ovn/utilities/ovn-detrace.in \ ovn/utilities/ovndb-servers.ocf CLEANFILES += \ ovn/utilities/ovn-ctl.8 \ ovn/utilities/ovn-nbctl.8 \ ovn/utilities/ovn-sbctl.8 \ - ovn/utilities/ovn-trace.8 + ovn/utilities/ovn-trace.8 \ + ovn/utilities/ovn-detrace.1 \ + ovn/utilities/ovn-detrace # ovn-nbctl bin_PROGRAMS += ovn/utilities/ovn-nbctl diff --git a/ovn/utilities/ovn-detrace.1.in b/ovn/utilities/ovn-detrace.1.in new file mode 100644 index 0000000..2e5e514 --- /dev/null +++ b/ovn/utilities/ovn-detrace.1.in @@ -0,0 +1,37 @@ +.TH ovn\-detrace 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" +. +.SH NAME +ovn\-detrace \- convert ``ovs\-appctl ofproto/trace'' output to combine +OVN logical flow information. +. +.SH SYNOPSIS +\fBovn\-detrace < \fIfile\fR +.so lib/common-syn.man +. +.SH DESCRIPTION +The \fBovn\-detrace\fR program reads \fBovs\-appctl ofproto/trace\fR output on +stdin, looking for flow cookies, and expand each cookie with corresponding OVN +logical flows. It expands logical flow further with the north-bound information +e.g. the ACL that generated the logical flow, when relevant. +.PP +. +.SH "OPTIONS" +.so lib/common.man +. +.IP "\fB\-\-ovnsb=\fIserver\fR" +The OVN Southbound DB remote to contact. If the \fBOVN_SB_DB\fR +environment variable is set, its value is used as the default. +Otherwise, the default is \fBunix:@RUNDIR@/ovnsb_db.sock\fR, but this +default is unlikely to be useful outside of single-machine OVN test +environments. +. +.IP "\fB\-\-ovnnb=\fIserver\fR" +The OVN Northbound DB remote to contact. If the \fBOVN_NB_DB\fR +environment variable is set, its value is used as the default. +Otherwise, the default is \fBunix:@RUNDIR@/ovnnb_db.sock\fR, but this +default is unlikely to be useful outside of single-machine OVN test +environments. +. +.SH "SEE ALSO" +. +.BR ovs\-appctl (8), ovn\-sbctl (8), ovn-\-nbctl (8), ovn\-trace (8) diff --git a/ovn/utilities/ovn-detrace.in b/ovn/utilities/ovn-detrace.in new file mode 100755 index 0000000..3d8e433 --- /dev/null +++ b/ovn/utilities/ovn-detrace.in @@ -0,0 +1,215 @@ +#! /usr/bin/env @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', '/usr/local/var/run/openvswitch') + 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, "\t* ") + print_lflow_nb_hint(lflow, "\t\t* ", 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: