python: Implement "vlog/reopen" unixctl command in Python vlog.
authorBen Pfaff <blp@nicira.com>
Tue, 1 May 2012 21:13:00 +0000 (14:13 -0700)
committerBen Pfaff <blp@nicira.com>
Tue, 22 May 2012 17:17:05 +0000 (10:17 -0700)
Signed-off-by: Ben Pfaff <blp@nicira.com>
python/ovs/vlog.py
tests/test-unixctl.py
tests/unixctl-py.at
tests/vlog.at

index b585591b61212274d6eb73486432823ef65f1285..b3a8b813f0e607483a3682774ba313859d4ae776 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (c) 2011 Nicira, Inc.
+# Copyright (c) 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.
@@ -20,6 +20,7 @@ import socket
 import sys
 
 import ovs.dirs
+import ovs.unixctl
 import ovs.util
 
 FACILITIES = {"console": "info", "file": "info", "syslog": "info"}
@@ -41,6 +42,8 @@ class Vlog:
     __inited = False
     __msg_num = 0
     __mfl = {}  # Module -> facility -> level
+    __log_file = None
+    __file_handler = None
 
     def __init__(self, name):
         """Creates a new Vlog object representing a module called 'name'.  The
@@ -99,6 +102,7 @@ class Vlog:
 
         Vlog.__inited = True
         logging.raiseExceptions = False
+        Vlog.__log_file = log_file
         for f in FACILITIES:
             logger = logging.getLogger(f)
             logger.setLevel(logging.DEBUG)
@@ -110,11 +114,15 @@ class Vlog:
                     logger.addHandler(logging.handlers.SysLogHandler(
                         address="/dev/log",
                         facility=logging.handlers.SysLogHandler.LOG_DAEMON))
-                elif f == "file" and log_file:
-                    logger.addHandler(logging.FileHandler(log_file))
+                elif f == "file" and Vlog.__log_file:
+                    Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
+                    logger.addHandler(Vlog.__file_handler)
             except (IOError, socket.error):
                 logger.setLevel(logging.CRITICAL)
 
+        ovs.unixctl.command_register("vlog/reopen", "", 0, 0,
+                                     Vlog._unixctl_vlog_reopen, None)
+
     @staticmethod
     def set_level(module, facility, level):
         """ Sets the log level of the 'module'-'facility' tuple to 'level'.
@@ -149,6 +157,25 @@ class Vlog:
             for f in facilities:
                 Vlog.__mfl[m][f] = level
 
+    @staticmethod
+    def reopen_log_file():
+        """Closes and then attempts to re-open the current log file.  (This is
+        useful just after log rotation, to ensure that the new log file starts
+        being used.)"""
+
+        if Vlog.__log_file:
+            logger = logging.getLogger("file")
+            logger.removeHandler(Vlog.__file_handler)
+            Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
+            logger.addHandler(Vlog.__file_handler)
+
+    @staticmethod
+    def _unixctl_vlog_reopen(conn, unused_argv, unused_aux):
+        if Vlog.__log_file:
+            Vlog.reopen_log_file()
+            conn.reply(None)
+        else:
+            conn.reply("Logging to file not configured")
 
 def add_args(parser):
     """Adds vlog related options to 'parser', an ArgumentParser object.  The
index 3d86db17daa763f1bf4d68afb8be2e5217689895..ab03479bea2412d3a2df47418753c7d895df95e7 100644 (file)
@@ -40,6 +40,11 @@ def unixctl_echo_error(conn, argv, aux):
     conn.reply_error(str(argv))
 
 
+def unixctl_log(conn, argv, unused_aux):
+    vlog.info(str(argv[0]))
+    conn.reply(None)
+
+
 def unixctl_block(conn, unused_argv, unused_aux):
     pass
 
@@ -64,6 +69,7 @@ def main():
     ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, "aux_exit")
     ovs.unixctl.command_register("echo", "[arg ...]", 1, 2, unixctl_echo,
                                  "aux_echo")
+    ovs.unixctl.command_register("log", "[arg ...]", 1, 2, unixctl_log, None)
     ovs.unixctl.command_register("echo_error", "[arg ...]", 1, 2,
                                  unixctl_echo_error, "aux_echo_error")
     ovs.unixctl.command_register("block", "", 0, 0, unixctl_block, None)
index f9caa60f7b6d43cf9b7046621dbbbb1628752aa7..37070c9187dc305a011903ab1175a4f49fec64d7 100644 (file)
@@ -104,7 +104,9 @@ The available commands are:
   echo_error              [[arg ...]]
   exit
   help
+  log                     [[arg ...]]
   version
+  vlog/reopen
 ])
 mv stdout expout
 AT_CHECK([PYAPPCTL -t test-unixctl.py help], [0], [expout])
index 597c27acd46316a6676cbe239ee7f7a9ba710480..a1afb10f00cfea5576ee3455c9bab9121f8024e2 100644 (file)
@@ -103,3 +103,80 @@ AssertionError
 ])
 
 AT_CLEANUP
+
+AT_SETUP([vlog - vlog/reopen - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+trap 'kill `cat test-unixctl.py.pid`' 0
+
+AT_CAPTURE_FILE([log])
+AT_CAPTURE_FILE([log.old])
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl.py log message])
+mv log log.old
+AT_CHECK([APPCTL -t test-unixctl.py log message2])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message3])
+AT_CHECK([APPCTL -t test-unixctl.py exit])
+trap '' 0
+
+AT_CHECK([sed 's/.*|//' log.old], [0], [dnl
+Entering run loop.
+message
+message2
+])
+AT_CHECK([sed 's/.*|//' log], [0], [dnl
+message3
+])
+AT_CLEANUP
+
+AT_SETUP([vlog - vlog/reopen without log file - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+trap 'kill `cat test-unixctl.py.pid`' 0
+
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen], [0],
+  [Logging to file not configured
+])
+AT_CLEANUP
+
+dnl This checks that if vlog/reopen can't reopen the log file,
+dnl nothing particularly bad (e.g. Python throws an exception and
+dnl aborts the program) happens.
+AT_SETUP([vlog - vlog/reopen can't reopen log file - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+
+# Verify that /dev/full is a character device that fails writes.
+AT_SKIP_IF([test ! -c /dev/full])
+AT_SKIP_IF([echo > /dev/full])
+
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+trap 'kill `cat test-unixctl.py.pid`' 0
+
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach])
+AT_CHECK([APPCTL -t test-unixctl.py log message])
+mv log log.old
+ln -s /dev/full log
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message2])
+rm log
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message3])
+AT_CHECK([APPCTL -t test-unixctl.py exit])
+AT_CHECK([sed 's/.*|//' log.old], [0], [dnl
+Entering run loop.
+message
+])
+AT_CHECK([sed 's/.*|//' log], [0], [dnl
+message3
+])
+AT_CLEANUP