tests: Deal with Python output differences.
[cascardo/ovs.git] / tests / test-ovsdb.py
index 1774dd9..4e87dbb 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import codecs
+from __future__ import print_function
+
 import getopt
 import re
 import os
 import signal
 import sys
+import uuid
 
 from ovs.db import error
 import ovs.db.idl
 import ovs.db.schema
 from ovs.db import data
-from ovs.db import types
+import ovs.db.types
 import ovs.ovsuuid
 import ovs.poller
 import ovs.util
+import six
+
 
 def unbox_json(json):
     if type(json) == list and len(json) == 1:
@@ -34,39 +38,42 @@ def unbox_json(json):
     else:
         return json
 
+
 def do_default_atoms():
-    for type in types.ATOMIC_TYPES:
-        if type == types.VoidType:
+    for type_ in ovs.db.types.ATOMIC_TYPES:
+        if type_ == ovs.db.types.VoidType:
             continue
 
-        sys.stdout.write("%s: " % type.to_string())
+        sys.stdout.write("%s: " % type_.to_string())
 
-        atom = data.Atom.default(type)
-        if atom != data.Atom.default(type):
+        atom = data.Atom.default(type_)
+        if atom != data.Atom.default(type_):
             sys.stdout.write("wrong\n")
             sys.exit(1)
 
         sys.stdout.write("OK\n")
 
+
 def do_default_data():
     any_errors = False
     for n_min in 0, 1:
-        for key in types.ATOMIC_TYPES:
-            if key == types.VoidType:
+        for key in ovs.db.types.ATOMIC_TYPES:
+            if key == ovs.db.types.VoidType:
                 continue
-            for value in types.ATOMIC_TYPES:
-                if value == types.VoidType:
+            for value in ovs.db.types.ATOMIC_TYPES:
+                if value == ovs.db.types.VoidType:
                     valueBase = None
                 else:
-                    valueBase = types.BaseType(value)
-                type = types.Type(types.BaseType(key), valueBase, n_min, 1)
-                assert type.is_valid()
+                    valueBase = ovs.db.types.BaseType(value)
+                type_ = ovs.db.types.Type(ovs.db.types.BaseType(key),
+                                          valueBase, n_min, 1)
+                assert type_.is_valid()
 
                 sys.stdout.write("key %s, value %s, n_min %d: "
                                  % (key.to_string(), value.to_string(), n_min))
 
-                datum = data.Datum.default(type)
-                if datum != data.Datum.default(type):
+                datum = data.Datum.default(type_)
+                if datum != data.Datum.default(type_):
                     sys.stdout.write("wrong\n")
                     any_errors = True
                 else:
@@ -74,82 +81,130 @@ def do_default_data():
     if any_errors:
         sys.exit(1)
 
+
 def do_parse_atomic_type(type_string):
     type_json = unbox_json(ovs.json.from_string(type_string))
-    atomic_type = types.AtomicType.from_json(type_json)
-    print ovs.json.to_string(atomic_type.to_json(), sort_keys=True)
+    atomic_type = ovs.db.types.AtomicType.from_json(type_json)
+    print(ovs.json.to_string(atomic_type.to_json(), sort_keys=True))
+
 
 def do_parse_base_type(type_string):
     type_json = unbox_json(ovs.json.from_string(type_string))
-    base_type = types.BaseType.from_json(type_json)
-    print ovs.json.to_string(base_type.to_json(), sort_keys=True)
+    base_type = ovs.db.types.BaseType.from_json(type_json)
+    print(ovs.json.to_string(base_type.to_json(), sort_keys=True))
+
 
 def do_parse_type(type_string):
     type_json = unbox_json(ovs.json.from_string(type_string))
-    type = types.Type.from_json(type_json)
-    print ovs.json.to_string(type.to_json(), sort_keys=True)
+    type_ = ovs.db.types.Type.from_json(type_json)
+    print(ovs.json.to_string(type_.to_json(), sort_keys=True))
+
 
 def do_parse_atoms(type_string, *atom_strings):
     type_json = unbox_json(ovs.json.from_string(type_string))
-    base = types.BaseType.from_json(type_json)
+    base = ovs.db.types.BaseType.from_json(type_json)
     for atom_string in atom_strings:
         atom_json = unbox_json(ovs.json.from_string(atom_string))
         try:
             atom = data.Atom.from_json(base, atom_json)
-            print ovs.json.to_string(atom.to_json())
-        except error.Error, e:
-            print unicode(e)
+            print(ovs.json.to_string(atom.to_json()))
+        except error.Error as e:
+            print(e.args[0])
+
 
 def do_parse_data(type_string, *data_strings):
     type_json = unbox_json(ovs.json.from_string(type_string))
-    type = types.Type.from_json(type_json)
+    type_ = ovs.db.types.Type.from_json(type_json)
     for datum_string in data_strings:
         datum_json = unbox_json(ovs.json.from_string(datum_string))
-        datum = data.Datum.from_json(type, datum_json)
-        print ovs.json.to_string(datum.to_json())
+        datum = data.Datum.from_json(type_, datum_json)
+        print(ovs.json.to_string(datum.to_json()))
+
 
 def do_sort_atoms(type_string, atom_strings):
     type_json = unbox_json(ovs.json.from_string(type_string))
-    base = types.BaseType.from_json(type_json)
+    base = ovs.db.types.BaseType.from_json(type_json)
     atoms = [data.Atom.from_json(base, atom_json)
              for atom_json in unbox_json(ovs.json.from_string(atom_strings))]
-    print ovs.json.to_string([data.Atom.to_json(atom)
-                              for atom in sorted(atoms)])
+    print(ovs.json.to_string([data.Atom.to_json(atom)
+                              for atom in sorted(atoms)]))
+
 
 def do_parse_column(name, column_string):
     column_json = unbox_json(ovs.json.from_string(column_string))
     column = ovs.db.schema.ColumnSchema.from_json(column_json, name)
-    print ovs.json.to_string(column.to_json(), sort_keys=True)
+    print(ovs.json.to_string(column.to_json(), sort_keys=True))
+
 
 def do_parse_table(name, table_string, default_is_root_string='false'):
     default_is_root = default_is_root_string == 'true'
     table_json = unbox_json(ovs.json.from_string(table_string))
     table = ovs.db.schema.TableSchema.from_json(table_json, name)
-    print ovs.json.to_string(table.to_json(default_is_root), sort_keys=True)
+    print(ovs.json.to_string(table.to_json(default_is_root), sort_keys=True))
 
-def do_parse_rows(table_string, *rows):
-    table_json = unbox_json(ovs.json.from_string(table_string))
-    table = ovs.db.schema.TableSchema.from_json(table_json, name)
 
 def do_parse_schema(schema_string):
     schema_json = unbox_json(ovs.json.from_string(schema_string))
     schema = ovs.db.schema.DbSchema.from_json(schema_json)
-    print ovs.json.to_string(schema.to_json(), sort_keys=True)
+    print(ovs.json.to_string(schema.to_json(), sort_keys=True))
+
 
 def print_idl(idl, step):
     n = 0
-    for uuid, row in idl.data["simple"].iteritems():
-        s = ("%03d: i=%s r=%s b=%s s=%s u=%s "
-             "ia=%s ra=%s ba=%s sa=%s ua=%s uuid=%s"
-             % (step, row.i, row.r, row.b, row.s, row.u,
-                row.ia, row.ra, row.ba, row.sa, row.ua, uuid))
-        print(re.sub('""|,', "", s))
-        n += 1
+    if "simple" in idl.tables:
+        simple_columns = ["i", "r", "b", "s", "u", "ia",
+                          "ra", "ba", "sa", "ua", "uuid"]
+        simple = idl.tables["simple"].rows
+        for row in six.itervalues(simple):
+            s = "%03d:" % step
+            for column in simple_columns:
+                if hasattr(row, column) and not (type(getattr(row, column))
+                                                 is ovs.db.data.Atom):
+                    s += " %s=%s" % (column, getattr(row, column))
+            s = re.sub('""|,|u?\'', "", s)
+            s = re.sub('UUID\(([^)]+)\)', r'\1', s)
+            s = re.sub('False', 'false', s)
+            s = re.sub('True', 'true', s)
+            s = re.sub(r'(ba)=([^[][^ ]*) ', r'\1=[\2] ', s)
+            print(s)
+            n += 1
+
+    if "link1" in idl.tables:
+        l1 = idl.tables["link1"].rows
+        for row in six.itervalues(l1):
+            s = ["%03d: i=%s k=" % (step, row.i)]
+            if hasattr(row, "k") and row.k:
+                s.append(str(row.k.i))
+            if hasattr(row, "ka"):
+                s.append(" ka=[")
+                s.append(' '.join(sorted(str(ka.i) for ka in row.ka)))
+                s.append("] l2=")
+            if hasattr(row, "l2") and row.l2:
+                s.append(str(row.l2[0].i))
+            if hasattr(row, "uuid"):
+                s.append(" uuid=%s" % row.uuid)
+            print(''.join(s))
+            n += 1
+
+    if "link2" in idl.tables:
+        l2 = idl.tables["link2"].rows
+        for row in six.itervalues(l2):
+            s = ["%03d:" % step]
+            s.append(" i=%s l1=" % row.i)
+            if hasattr(row, "l1") and row.l1:
+                s.append(str(row.l1[0].i))
+            if hasattr(row, "uuid"):
+                s.append(" uuid=%s" % row.uuid)
+            print(''.join(s))
+            n += 1
+
     if not n:
         print("%03d: empty" % step)
+    sys.stdout.flush()
+
 
 def substitute_uuids(json, symtab):
-    if type(json) in [str, unicode]:
+    if isinstance(json, six.string_types):
         symbol = symtab.get(json)
         if symbol:
             return str(symbol)
@@ -157,13 +212,15 @@ def substitute_uuids(json, symtab):
         return [substitute_uuids(element, symtab) for element in json]
     elif type(json) == dict:
         d = {}
-        for key, value in json.iteritems():
+        for key, value in six.iteritems(json):
             d[key] = substitute_uuids(value, symtab)
         return d
     return json
 
+
 def parse_uuids(json, symtab):
-    if type(json) in [str, unicode] and ovs.ovsuuid.is_valid_string(json):
+    if (isinstance(json, six.string_types)
+            and ovs.ovsuuid.is_valid_string(json)):
         name = "#%d#" % len(symtab)
         sys.stderr.write("%s = %s\n" % (name, json))
         symtab[name] = json
@@ -171,11 +228,186 @@ def parse_uuids(json, symtab):
         for element in json:
             parse_uuids(element, symtab)
     elif type(json) == dict:
-        for value in json.itervalues():
+        for value in six.itervalues(json):
             parse_uuids(value, symtab)
 
-def do_idl(remote, *commands):
-    idl = ovs.db.idl.Idl(remote, "idltest")
+
+def idltest_find_simple(idl, i):
+    for row in six.itervalues(idl.tables["simple"].rows):
+        if row.i == i:
+            return row
+    return None
+
+
+def idl_set(idl, commands, step):
+    txn = ovs.db.idl.Transaction(idl)
+    increment = False
+    fetch_cmds = []
+    events = []
+    for command in commands.split(','):
+        words = command.split()
+        name = words[0]
+        args = words[1:]
+
+        if name == "notifytest":
+            name = args[0]
+            args = args[1:]
+            old_notify = idl.notify
+
+            def notify(event, row, updates=None):
+                if updates:
+                    upcol = list(updates._data.keys())[0]
+                else:
+                    upcol = None
+                events.append("%s|%s|%s" % (event, row.i, upcol))
+                idl.notify = old_notify
+
+            idl.notify = notify
+
+        if name == "set":
+            if len(args) != 3:
+                sys.stderr.write('"set" command requires 3 arguments\n')
+                sys.exit(1)
+
+            s = idltest_find_simple(idl, int(args[0]))
+            if not s:
+                sys.stderr.write('"set" command asks for nonexistent i=%d\n'
+                                 % int(args[0]))
+                sys.exit(1)
+
+            if args[1] == "b":
+                s.b = args[2] == "1"
+            elif args[1] == "s":
+                s.s = args[2]
+            elif args[1] == "u":
+                s.u = uuid.UUID(args[2])
+            elif args[1] == "r":
+                s.r = float(args[2])
+            else:
+                sys.stderr.write('"set" comamnd asks for unknown column %s\n'
+                                 % args[2])
+                sys.stderr.exit(1)
+        elif name == "insert":
+            if len(args) != 1:
+                sys.stderr.write('"set" command requires 1 argument\n')
+                sys.exit(1)
+
+            s = txn.insert(idl.tables["simple"])
+            s.i = int(args[0])
+        elif name == "delete":
+            if len(args) != 1:
+                sys.stderr.write('"delete" command requires 1 argument\n')
+                sys.exit(1)
+
+            s = idltest_find_simple(idl, int(args[0]))
+            if not s:
+                sys.stderr.write('"delete" command asks for nonexistent i=%d\n'
+                                 % int(args[0]))
+                sys.exit(1)
+            s.delete()
+        elif name == "verify":
+            if len(args) != 2:
+                sys.stderr.write('"verify" command requires 2 arguments\n')
+                sys.exit(1)
+
+            s = idltest_find_simple(idl, int(args[0]))
+            if not s:
+                sys.stderr.write('"verify" command asks for nonexistent i=%d\n'
+                                 % int(args[0]))
+                sys.exit(1)
+
+            if args[1] in ("i", "b", "s", "u", "r"):
+                s.verify(args[1])
+            else:
+                sys.stderr.write('"verify" command asks for unknown column '
+                                 '"%s"\n' % args[1])
+                sys.exit(1)
+        elif name == "fetch":
+            if len(args) != 2:
+                sys.stderr.write('"fetch" command requires 2 argument\n')
+                sys.exit(1)
+
+            row = idltest_find_simple(idl, int(args[0]))
+            if not row:
+                sys.stderr.write('"fetch" command asks for nonexistent i=%d\n'
+                                 % int(args[0]))
+                sys.exit(1)
+
+            column = args[1]
+            row.fetch(column)
+            fetch_cmds.append([row, column])
+        elif name == "increment":
+            if len(args) != 1:
+                sys.stderr.write('"increment" command requires 1 argument\n')
+                sys.exit(1)
+
+            s = idltest_find_simple(idl, int(args[0]))
+            if not s:
+                sys.stderr.write('"set" command asks for nonexistent i=%d\n'
+                                 % int(args[0]))
+                sys.exit(1)
+
+            s.increment("i")
+            increment = True
+        elif name == "abort":
+            txn.abort()
+            break
+        elif name == "destroy":
+            print("%03d: destroy" % step)
+            sys.stdout.flush()
+            txn.abort()
+            return
+        elif name == "linktest":
+            l1_0 = txn.insert(idl.tables["link1"])
+            l1_0.i = 1
+            l1_0.k = [l1_0]
+            l1_0.ka = [l1_0]
+            l1_1 = txn.insert(idl.tables["link1"])
+            l1_1.i = 2
+            l1_1.k = [l1_0]
+            l1_1.ka = [l1_0, l1_1]
+        elif name == 'getattrtest':
+            l1 = txn.insert(idl.tables["link1"])
+            i = getattr(l1, 'i', 1)
+            assert i == 1
+            l1.i = 2
+            i = getattr(l1, 'i', 1)
+            assert i == 2
+            l1.k = [l1]
+        else:
+            sys.stderr.write("unknown command %s\n" % name)
+            sys.exit(1)
+
+    status = txn.commit_block()
+    sys.stdout.write("%03d: commit, status=%s"
+                     % (step, ovs.db.idl.Transaction.status_to_string(status)))
+    if increment and status == ovs.db.idl.Transaction.SUCCESS:
+        sys.stdout.write(", increment=%d" % txn.get_increment_new_value())
+    if events:
+        # Event notifications from operations in a single transaction are
+        # not in a gauranteed order due to update messages being dicts
+        sys.stdout.write(", events=" + ", ".join(sorted(events)))
+    sys.stdout.write("\n")
+    sys.stdout.flush()
+
+
+def do_idl(schema_file, remote, *commands):
+    schema_helper = ovs.db.idl.SchemaHelper(schema_file)
+    if commands and commands[0].startswith("?"):
+        readonly = {}
+        for x in commands[0][1:].split("?"):
+            readonly = []
+            table, columns = x.split(":")
+            columns = columns.split(",")
+            for index, column in enumerate(columns):
+                if column[-1] == '!':
+                    columns[index] = columns[index][:-1]
+                    readonly.append(columns[index])
+            schema_helper.register_columns(table, columns, readonly)
+        commands = commands[1:]
+    else:
+        schema_helper.register_all()
+    idl = ovs.db.idl.Idl(remote, schema_helper)
 
     if commands:
         error, stream = ovs.stream.Stream.open_block(
@@ -196,21 +428,22 @@ def do_idl(remote, *commands):
             command = command[1:]
         else:
             # Wait for update.
-            while idl.get_seqno() == seqno and not idl.run():
+            while idl.change_seqno == seqno and not idl.run():
                 rpc.run()
 
                 poller = ovs.poller.Poller()
                 idl.wait(poller)
                 rpc.wait(poller)
                 poller.block()
-                
+
             print_idl(idl, step)
             step += 1
 
-        seqno = idl.get_seqno()
+        seqno = idl.change_seqno
 
         if command == "reconnect":
             print("%03d: reconnect" % step)
+            sys.stdout.flush()
             step += 1
             idl.force_reconnect()
         elif not command.startswith("["):
@@ -218,7 +451,7 @@ def do_idl(remote, *commands):
             step += 1
         else:
             json = ovs.json.from_string(command)
-            if type(json) in [str, unicode]:
+            if isinstance(json, six.string_types):
                 sys.stderr.write("\"%s\": %s\n" % (command, json))
                 sys.exit(1)
             json = substitute_uuids(json, symtab)
@@ -228,6 +461,11 @@ def do_idl(remote, *commands):
                 sys.stderr.write("jsonrpc transaction failed: %s"
                                  % os.strerror(error))
                 sys.exit(1)
+            elif reply.error is not None:
+                sys.stderr.write("jsonrpc transaction failed: %s"
+                                 % reply.error)
+                sys.exit(1)
+
             sys.stdout.write("%03d: " % step)
             sys.stdout.flush()
             step += 1
@@ -235,10 +473,11 @@ def do_idl(remote, *commands):
                 parse_uuids(reply.result, symtab)
             reply.id = None
             sys.stdout.write("%s\n" % ovs.json.to_string(reply.to_json()))
+            sys.stdout.flush()
 
     if rpc:
         rpc.close()
-    while idl.get_seqno() == seqno and not idl.run():
+    while idl.change_seqno == seqno and not idl.run():
         poller = ovs.poller.Poller()
         idl.wait(poller)
         poller.block()
@@ -247,8 +486,9 @@ def do_idl(remote, *commands):
     idl.close()
     print("%03d: done" % step)
 
+
 def usage():
-    print """\
+    print("""\
 %(program_name)s: test utility for Open vSwitch database Python bindings
 usage: %(program_name)s [OPTIONS] COMMAND ARG...
 
@@ -277,24 +517,42 @@ parse-table NAME OBJECT [DEFAULT-IS-ROOT]
   parse table NAME with info OBJECT
 parse-schema JSON
   parse JSON as an OVSDB schema, and re-serialize
-idl SERVER [TRANSACTION...]
-  connect to SERVER and dump the contents of the database
-  as seen initially by the IDL implementation and after
-  executing each TRANSACTION.  (Each TRANSACTION must modify
+idl SCHEMA SERVER [?T1:C1,C2...[?T2:C1,C2,...]...] [TRANSACTION...]
+  connect to SERVER (which has the specified SCHEMA) and dump the
+  contents of the database as seen initially by the IDL implementation
+  and after executing each TRANSACTION.  (Each TRANSACTION must modify
   the database or this command will hang.)
+  By default, all columns of all tables are monitored. The "?" option
+  can be used to monitor specific Table:Column(s). The table and their
+  columns are listed as a string of the form starting with "?":
+      ?<table-name>:<column-name>,<column-name>,...
+  e.g.:
+      ?simple:b - Monitor column "b" in table "simple"
+  Entries for multiple tables are seperated by "?":
+      ?<table-name>:<column-name>,...?<table-name>:<column-name>,...
+  e.g.:
+      ?simple:b?link1:i,k - Monitor column "b" in table "simple",
+                            and column "i", "k" in table "link1"
+  Readonly columns: Suffixing a "!" after a column indicates that the
+  column is to be registered "readonly".
+  e.g.:
+      ?simple:i,b!  - Register interest in column "i" (monitoring) and
+                      column "b" (readonly).
+
 
 The following options are also available:
   -t, --timeout=SECS          give up after SECS seconds
   -h, --help                  display this help message\
-""" % {'program_name': ovs.util.PROGRAM_NAME}
+""" % {'program_name': ovs.util.PROGRAM_NAME})
     sys.exit(0)
 
+
 def main(argv):
     try:
         options, args = getopt.gnu_getopt(argv[1:], 't:h',
                                           ['timeout',
                                            'help'])
-    except getopt.GetoptError, geo:
+    except getopt.GetoptError as geo:
         sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
         sys.exit(1)
 
@@ -313,8 +571,6 @@ def main(argv):
         else:
             sys.exit(0)
 
-    optKeys = [key for key, value in options]
-
     if not args:
         sys.stderr.write("%s: missing command argument "
                          "(use --help for help)\n" % ovs.util.PROGRAM_NAME)
@@ -331,11 +587,11 @@ def main(argv):
                 "parse-column": (do_parse_column, 2),
                 "parse-table": (do_parse_table, (2, 3)),
                 "parse-schema": (do_parse_schema, 1),
-                "idl": (do_idl, (1,))}
+                "idl": (do_idl, (2,))}
 
     command_name = args[0]
     args = args[1:]
-    if not command_name in commands:
+    if command_name not in commands:
         sys.stderr.write("%s: unknown command \"%s\" "
                          "(use --help for help)\n" % (ovs.util.PROGRAM_NAME,
                                                       command_name))
@@ -361,9 +617,10 @@ def main(argv):
 
     func(*args)
 
+
 if __name__ == '__main__':
     try:
         main(sys.argv)
-    except error.Error, e:
+    except error.Error as e:
         sys.stderr.write("%s\n" % e)
         sys.exit(1)