]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Import Oslo's common rootwrap to Cinder
authorThierry Carrez <thierry@openstack.org>
Tue, 29 Jan 2013 13:58:07 +0000 (14:58 +0100)
committerThierry Carrez <thierry@openstack.org>
Mon, 4 Feb 2013 16:28:38 +0000 (17:28 +0100)
Import oslo-incubator's rootwrap, which contains all the new
features and bugfixes that were pushed to nova-rootwrap earlier
in this cycle, including logging support and path search.

Implements bp: cinder-common-rootwrap

Change-Id: I68cbb788a3dda58dc146933be190146607b48801

bin/cinder-rootwrap
cinder/openstack/common/rootwrap/__init__.py [moved from cinder/rootwrap/__init__.py with 100% similarity]
cinder/openstack/common/rootwrap/filters.py [moved from cinder/rootwrap/filters.py with 73% similarity]
cinder/openstack/common/rootwrap/wrapper.py [new file with mode: 0644]
cinder/rootwrap/wrapper.py [deleted file]
cinder/tests/test_cinder_rootwrap.py [deleted file]
etc/cinder/rootwrap.conf
openstack-common.conf

index 2a1f5afea68a7c9cccb0a9d9ae8001ae0f5992f7..959c9ff9095859c5e42feae2f3aedbe3bc1069fd 100755 (executable)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-"""Root wrapper for Cinder
+"""Root wrapper for OpenStack services
 
-   Filters which commands cinder is allowed to run as another user.
+   Filters which commands a service is allowed to run as another user.
 
-   To use this, you should set the following in cinder.conf:
+   To use this with cinder, you should set the following in
+   cinder.conf:
    rootwrap_config=/etc/cinder/rootwrap.conf
 
-   You also need to let the cinder user run cinder-rootwrap as root in sudoers:
+   You also need to let the cinder user run cinder-rootwrap
+   as root in sudoers:
    cinder ALL = (root) NOPASSWD: /usr/bin/cinder-rootwrap
-                                 /etc/cinder/rootwrap.conf *
+                                   /etc/cinder/rootwrap.conf *
 
-   To make allowed commands node-specific, your packaging should only
-   install volume.filters on volume nodes (i.e. cinder-api nodes should not
-   have any of those files installed).
+   Service packaging should deploy .filters files only on nodes where
+   they are needed, to avoid allowing more than is necessary.
 """
 
 import ConfigParser
+import logging
 import os
+import pwd
 import signal
 import subprocess
 import sys
@@ -42,6 +45,7 @@ import sys
 RC_UNAUTHORIZED = 99
 RC_NOCOMMAND = 98
 RC_BADCONFIG = 97
+RC_NOEXECFOUND = 96
 
 
 def _subprocess_setup():
@@ -50,45 +54,75 @@ def _subprocess_setup():
     signal.signal(signal.SIGPIPE, signal.SIG_DFL)
 
 
+def _exit_error(execname, message, errorcode, log=True):
+    print "%s: %s" % (execname, message)
+    if log:
+        logging.error(message)
+    sys.exit(errorcode)
+
+
 if __name__ == '__main__':
     # Split arguments, require at least a command
     execname = sys.argv.pop(0)
     if len(sys.argv) < 2:
-        print "%s: %s" % (execname, "No command specified")
-        sys.exit(RC_NOCOMMAND)
+        _exit_error(execname, "No command specified", RC_NOCOMMAND, log=False)
 
     configfile = sys.argv.pop(0)
     userargs = sys.argv[:]
 
-    # Load configuration
-    config = ConfigParser.RawConfigParser()
-    config.read(configfile)
-    try:
-        filters_path = config.get("DEFAULT", "filters_path").split(",")
-    except ConfigParser.Error:
-        print "%s: Incorrect configuration file: %s" % (execname, configfile)
-        sys.exit(RC_BADCONFIG)
-
     # Add ../ to sys.path to allow running from branch
     possible_topdir = os.path.normpath(os.path.join(os.path.abspath(execname),
                                                     os.pardir, os.pardir))
     if os.path.exists(os.path.join(possible_topdir, "cinder", "__init__.py")):
         sys.path.insert(0, possible_topdir)
 
-    from cinder.rootwrap import wrapper
+    from cinder.openstack.common.rootwrap import wrapper
+
+    # Load configuration
+    try:
+        rawconfig = ConfigParser.RawConfigParser()
+        rawconfig.read(configfile)
+        config = wrapper.RootwrapConfig(rawconfig)
+    except ValueError as exc:
+        msg = "Incorrect value in %s: %s" % (configfile, exc.message)
+        _exit_error(execname, msg, RC_BADCONFIG, log=False)
+    except ConfigParser.Error:
+        _exit_error(execname, "Incorrect configuration file: %s" % configfile,
+                    RC_BADCONFIG, log=False)
+
+    if config.use_syslog:
+        wrapper.setup_syslog(execname,
+                             config.syslog_log_facility,
+                             config.syslog_log_level)
 
     # Execute command if it matches any of the loaded filters
-    filters = wrapper.load_filters(filters_path)
-    filtermatch = wrapper.match_filter(filters, userargs)
-    if filtermatch:
-        obj = subprocess.Popen(filtermatch.get_command(userargs),
-                               stdin=sys.stdin,
-                               stdout=sys.stdout,
-                               stderr=sys.stderr,
-                               preexec_fn=_subprocess_setup,
-                               env=filtermatch.get_environment(userargs))
-        obj.wait()
-        sys.exit(obj.returncode)
-
-    print "Unauthorized command: %s" % ' '.join(userargs)
-    sys.exit(RC_UNAUTHORIZED)
+    filters = wrapper.load_filters(config.filters_path)
+    try:
+        filtermatch = wrapper.match_filter(filters, userargs,
+                                           exec_dirs=config.exec_dirs)
+        if filtermatch:
+            command = filtermatch.get_command(userargs,
+                                              exec_dirs=config.exec_dirs)
+            if config.use_syslog:
+                logging.info("(%s > %s) Executing %s (filter match = %s)" % (
+                    os.getlogin(), pwd.getpwuid(os.getuid())[0],
+                    command, filtermatch.name))
+
+            obj = subprocess.Popen(command,
+                                   stdin=sys.stdin,
+                                   stdout=sys.stdout,
+                                   stderr=sys.stderr,
+                                   preexec_fn=_subprocess_setup,
+                                   env=filtermatch.get_environment(userargs))
+            obj.wait()
+            sys.exit(obj.returncode)
+
+    except wrapper.FilterMatchNotExecutable as exc:
+        msg = ("Executable not found: %s (filter match = %s)"
+               % (exc.match.exec_path, exc.match.name))
+        _exit_error(execname, msg, RC_NOEXECFOUND, log=config.use_syslog)
+
+    except wrapper.NoFilterMatched:
+        msg = ("Unauthorized command: %s (no filter matched)"
+               % ' '.join(userargs))
+        _exit_error(execname, msg, RC_UNAUTHORIZED, log=config.use_syslog)
similarity index 73%
rename from cinder/rootwrap/filters.py
rename to cinder/openstack/common/rootwrap/filters.py
index 3509602f2827822b732461725ca1a8125ca5465f..905bbabea76ac9bc827d511c3ade743fb7dd7315 100644 (file)
@@ -15,7 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-
 import os
 import re
 
@@ -24,9 +23,27 @@ class CommandFilter(object):
     """Command filter only checking that the 1st argument matches exec_path"""
 
     def __init__(self, exec_path, run_as, *args):
+        self.name = ''
         self.exec_path = exec_path
         self.run_as = run_as
         self.args = args
+        self.real_exec = None
+
+    def get_exec(self, exec_dirs=[]):
+        """Returns existing executable, or empty string if none found"""
+        if self.real_exec is not None:
+            return self.real_exec
+        self.real_exec = ""
+        if self.exec_path.startswith('/'):
+            if os.access(self.exec_path, os.X_OK):
+                self.real_exec = self.exec_path
+        else:
+            for binary_path in exec_dirs:
+                expanded_path = os.path.join(binary_path, self.exec_path)
+                if os.access(expanded_path, os.X_OK):
+                    self.real_exec = expanded_path
+                    break
+        return self.real_exec
 
     def match(self, userargs):
         """Only check that the first argument (command) matches exec_path"""
@@ -34,12 +51,13 @@ class CommandFilter(object):
             return True
         return False
 
-    def get_command(self, userargs):
+    def get_command(self, userargs, exec_dirs=[]):
         """Returns command to execute (with sudo -u if run_as != root)."""
+        to_exec = self.get_exec(exec_dirs=exec_dirs) or self.exec_path
         if (self.run_as != 'root'):
             # Used to run commands at lesser privileges
-            return ['sudo', '-u', self.run_as, self.exec_path] + userargs[1:]
-        return [self.exec_path] + userargs[1:]
+            return ['sudo', '-u', self.run_as, to_exec] + userargs[1:]
+        return [to_exec] + userargs[1:]
 
     def get_environment(self, userargs):
         """Returns specific environment to set, None if none"""
@@ -73,23 +91,33 @@ class RegExpFilter(CommandFilter):
 class DnsmasqFilter(CommandFilter):
     """Specific filter for the dnsmasq call (which includes env)"""
 
+    CONFIG_FILE_ARG = 'CONFIG_FILE'
+
     def match(self, userargs):
-        if (userargs[0].startswith("FLAGFILE=") and
-            userargs[1].startswith("NETWORK_ID=") and
-            userargs[2] == "dnsmasq"):
+        if (userargs[0] == 'env' and
+                userargs[1].startswith(self.CONFIG_FILE_ARG) and
+                userargs[2].startswith('NETWORK_ID=') and
+                userargs[3] == 'dnsmasq'):
             return True
         return False
 
-    def get_command(self, userargs):
-        return [self.exec_path] + userargs[3:]
+    def get_command(self, userargs, exec_dirs=[]):
+        to_exec = self.get_exec(exec_dirs=exec_dirs) or self.exec_path
+        dnsmasq_pos = userargs.index('dnsmasq')
+        return [to_exec] + userargs[dnsmasq_pos + 1:]
 
     def get_environment(self, userargs):
         env = os.environ.copy()
-        env['FLAGFILE'] = userargs[0].split('=')[-1]
-        env['NETWORK_ID'] = userargs[1].split('=')[-1]
+        env[self.CONFIG_FILE_ARG] = userargs[1].split('=')[-1]
+        env['NETWORK_ID'] = userargs[2].split('=')[-1]
         return env
 
 
+class DeprecatedDnsmasqFilter(DnsmasqFilter):
+    """Variant of dnsmasq filter to support old-style FLAGFILE"""
+    CONFIG_FILE_ARG = 'FLAGFILE'
+
+
 class KillFilter(CommandFilter):
     """Specific filter for the kill calls.
        1st argument is the user to run /bin/kill under
diff --git a/cinder/openstack/common/rootwrap/wrapper.py b/cinder/openstack/common/rootwrap/wrapper.py
new file mode 100644 (file)
index 0000000..06b121e
--- /dev/null
@@ -0,0 +1,149 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 OpenStack, LLC.
+# All Rights Reserved.
+#
+#    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 ConfigParser
+import logging
+import logging.handlers
+import os
+import string
+
+from cinder.openstack.common.rootwrap import filters
+
+
+class NoFilterMatched(Exception):
+    """This exception is raised when no filter matched."""
+    pass
+
+
+class FilterMatchNotExecutable(Exception):
+    """
+    This exception is raised when a filter matched but no executable was
+    found.
+    """
+    def __init__(self, match=None, **kwargs):
+        self.match = match
+
+
+class RootwrapConfig(object):
+
+    def __init__(self, config):
+        # filters_path
+        self.filters_path = config.get("DEFAULT", "filters_path").split(",")
+
+        # exec_dirs
+        if config.has_option("DEFAULT", "exec_dirs"):
+            self.exec_dirs = config.get("DEFAULT", "exec_dirs").split(",")
+        else:
+            # Use system PATH if exec_dirs is not specified
+            self.exec_dirs = os.environ["PATH"].split(':')
+
+        # syslog_log_facility
+        if config.has_option("DEFAULT", "syslog_log_facility"):
+            v = config.get("DEFAULT", "syslog_log_facility")
+            facility_names = logging.handlers.SysLogHandler.facility_names
+            self.syslog_log_facility = getattr(logging.handlers.SysLogHandler,
+                                               v, None)
+            if self.syslog_log_facility is None and v in facility_names:
+                self.syslog_log_facility = facility_names.get(v)
+            if self.syslog_log_facility is None:
+                raise ValueError('Unexpected syslog_log_facility: %s' % v)
+        else:
+            default_facility = logging.handlers.SysLogHandler.LOG_SYSLOG
+            self.syslog_log_facility = default_facility
+
+        # syslog_log_level
+        if config.has_option("DEFAULT", "syslog_log_level"):
+            v = config.get("DEFAULT", "syslog_log_level")
+            self.syslog_log_level = logging.getLevelName(v.upper())
+            if (self.syslog_log_level == "Level %s" % v.upper()):
+                raise ValueError('Unexepected syslog_log_level: %s' % v)
+        else:
+            self.syslog_log_level = logging.ERROR
+
+        # use_syslog
+        if config.has_option("DEFAULT", "use_syslog"):
+            self.use_syslog = config.getboolean("DEFAULT", "use_syslog")
+        else:
+            self.use_syslog = False
+
+
+def setup_syslog(execname, facility, level):
+    rootwrap_logger = logging.getLogger()
+    rootwrap_logger.setLevel(level)
+    handler = logging.handlers.SysLogHandler(address='/dev/log',
+                                             facility=facility)
+    handler.setFormatter(logging.Formatter(
+                         os.path.basename(execname) + ': %(message)s'))
+    rootwrap_logger.addHandler(handler)
+
+
+def build_filter(class_name, *args):
+    """Returns a filter object of class class_name"""
+    if not hasattr(filters, class_name):
+        logging.warning("Skipping unknown filter class (%s) specified "
+                        "in filter definitions" % class_name)
+        return None
+    filterclass = getattr(filters, class_name)
+    return filterclass(*args)
+
+
+def load_filters(filters_path):
+    """Load filters from a list of directories"""
+    filterlist = []
+    for filterdir in filters_path:
+        if not os.path.isdir(filterdir):
+            continue
+        for filterfile in os.listdir(filterdir):
+            filterconfig = ConfigParser.RawConfigParser()
+            filterconfig.read(os.path.join(filterdir, filterfile))
+            for (name, value) in filterconfig.items("Filters"):
+                filterdefinition = [string.strip(s) for s in value.split(',')]
+                newfilter = build_filter(*filterdefinition)
+                if newfilter is None:
+                    continue
+                newfilter.name = name
+                filterlist.append(newfilter)
+    return filterlist
+
+
+def match_filter(filters, userargs, exec_dirs=[]):
+    """
+    Checks user command and arguments through command filters and
+    returns the first matching filter.
+    Raises NoFilterMatched if no filter matched.
+    Raises FilterMatchNotExecutable if no executable was found for the
+    best filter match.
+    """
+    first_not_executable_filter = None
+
+    for f in filters:
+        if f.match(userargs):
+            # Try other filters if executable is absent
+            if not f.get_exec(exec_dirs=exec_dirs):
+                if not first_not_executable_filter:
+                    first_not_executable_filter = f
+                continue
+            # Otherwise return matching filter for execution
+            return f
+
+    if first_not_executable_filter:
+        # A filter matched, but no executable was found for it
+        raise FilterMatchNotExecutable(match=first_not_executable_filter)
+
+    # No filter matched
+    raise NoFilterMatched()
diff --git a/cinder/rootwrap/wrapper.py b/cinder/rootwrap/wrapper.py
deleted file mode 100644 (file)
index 4211a49..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright (c) 2011 OpenStack, LLC.
-# All Rights Reserved.
-#
-#    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 ConfigParser
-import os
-import string
-
-from cinder.rootwrap import filters
-
-
-def build_filter(class_name, *args):
-    """Returns a filter object of class class_name"""
-    if not hasattr(filters, class_name):
-        # TODO(ttx): Log the error (whenever cinder-rootwrap has a log file)
-        return None
-    filterclass = getattr(filters, class_name)
-    return filterclass(*args)
-
-
-def load_filters(filters_path):
-    """Load filters from a list of directories"""
-    filterlist = []
-    for filterdir in filters_path:
-        if not os.path.isdir(filterdir):
-            continue
-        for filterfile in os.listdir(filterdir):
-            filterconfig = ConfigParser.RawConfigParser()
-            filterconfig.read(os.path.join(filterdir, filterfile))
-            for (name, value) in filterconfig.items("Filters"):
-                filterdefinition = [string.strip(s) for s in value.split(',')]
-                newfilter = build_filter(*filterdefinition)
-                if newfilter is None:
-                    continue
-                filterlist.append(newfilter)
-    return filterlist
-
-
-def match_filter(filters, userargs):
-    """
-    Checks user command and arguments through command filters and
-    returns the first matching filter, or None is none matched.
-    """
-
-    found_filter = None
-
-    for f in filters:
-        if f.match(userargs):
-            # Try other filters if executable is absent
-            if not os.access(f.exec_path, os.X_OK):
-                if not found_filter:
-                    found_filter = f
-                continue
-            # Otherwise return matching filter for execution
-            return f
-
-    # No filter matched or first missing executable
-    return found_filter
diff --git a/cinder/tests/test_cinder_rootwrap.py b/cinder/tests/test_cinder_rootwrap.py
deleted file mode 100644 (file)
index cf14551..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-#    Copyright 2011 OpenStack LLC
-#
-#    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 os
-import subprocess
-
-from cinder.rootwrap import filters
-from cinder.rootwrap import wrapper
-from cinder import test
-
-
-class RootwrapTestCase(test.TestCase):
-
-    def setUp(self):
-        super(RootwrapTestCase, self).setUp()
-        self.filters = [
-            filters.RegExpFilter("/bin/ls", "root", 'ls', '/[a-z]+'),
-            filters.CommandFilter("/usr/bin/foo_bar_not_exist", "root"),
-            filters.RegExpFilter("/bin/cat", "root", 'cat', '/[a-z]+'),
-            filters.CommandFilter("/nonexistent/cat", "root"),
-            filters.CommandFilter("/bin/cat", "root")]  # Keep this one last
-
-    def test_RegExpFilter_match(self):
-        usercmd = ["ls", "/root"]
-        filtermatch = wrapper.match_filter(self.filters, usercmd)
-        self.assertFalse(filtermatch is None)
-        self.assertEqual(filtermatch.get_command(usercmd),
-                         ["/bin/ls", "/root"])
-
-    def test_RegExpFilter_reject(self):
-        usercmd = ["ls", "root"]
-        filtermatch = wrapper.match_filter(self.filters, usercmd)
-        self.assertTrue(filtermatch is None)
-
-    def test_missing_command(self):
-        valid_but_missing = ["foo_bar_not_exist"]
-        invalid = ["foo_bar_not_exist_and_not_matched"]
-        filtermatch = wrapper.match_filter(self.filters, valid_but_missing)
-        self.assertTrue(filtermatch is not None)
-        filtermatch = wrapper.match_filter(self.filters, invalid)
-        self.assertTrue(filtermatch is None)
-
-    def test_DnsmasqFilter(self):
-        usercmd = ['FLAGFILE=A', 'NETWORK_ID=foobar', 'dnsmasq', 'foo']
-        f = filters.DnsmasqFilter("/usr/bin/dnsmasq", "root")
-        self.assertTrue(f.match(usercmd))
-        self.assertEqual(f.get_command(usercmd), ['/usr/bin/dnsmasq', 'foo'])
-        env = f.get_environment(usercmd)
-        self.assertEqual(env.get('FLAGFILE'), 'A')
-        self.assertEqual(env.get('NETWORK_ID'), 'foobar')
-
-    @test.skip_if(not os.path.exists("/proc/%d" % os.getpid()),
-                  "Test requires /proc filesystem (procfs)")
-    def test_KillFilter(self):
-        p = subprocess.Popen(["/bin/sleep", "5"])
-        f = filters.KillFilter("root", "/bin/sleep", "-9", "-HUP")
-        f2 = filters.KillFilter("root", "/usr/bin/sleep", "-9", "-HUP")
-        usercmd = ['kill', '-ALRM', p.pid]
-        # Incorrect signal should fail
-        self.assertFalse(f.match(usercmd) or f2.match(usercmd))
-        usercmd = ['kill', p.pid]
-        # Providing no signal should fail
-        self.assertFalse(f.match(usercmd) or f2.match(usercmd))
-        # Providing matching signal should be allowed
-        usercmd = ['kill', '-9', p.pid]
-        self.assertTrue(f.match(usercmd) or f2.match(usercmd))
-
-        f = filters.KillFilter("root", "/bin/sleep")
-        f2 = filters.KillFilter("root", "/usr/bin/sleep")
-        usercmd = ['kill', os.getpid()]
-        # Our own PID does not match /bin/sleep, so it should fail
-        self.assertFalse(f.match(usercmd) or f2.match(usercmd))
-        usercmd = ['kill', 999999]
-        # Nonexistent PID should fail
-        self.assertFalse(f.match(usercmd) or f2.match(usercmd))
-        usercmd = ['kill', p.pid]
-        # Providing no signal should work
-        self.assertTrue(f.match(usercmd) or f2.match(usercmd))
-
-    def test_KillFilter_no_raise(self):
-        """Makes sure ValueError from bug 926412 is gone."""
-        f = filters.KillFilter("root", "")
-        # Providing anything other than kill should be False
-        usercmd = ['notkill', 999999]
-        self.assertFalse(f.match(usercmd))
-        # Providing something that is not a pid should be False
-        usercmd = ['kill', 'notapid']
-        self.assertFalse(f.match(usercmd))
-
-    def test_KillFilter_deleted_exe(self):
-        """Makes sure deleted exe's are killed correctly."""
-        # See bug #967931.
-        def fake_readlink(blah):
-            return '/bin/commandddddd (deleted)'
-
-        f = filters.KillFilter("root", "/bin/commandddddd")
-        usercmd = ['kill', 1234]
-        # Providing no signal should work
-        self.stubs.Set(os, 'readlink', fake_readlink)
-        self.assertTrue(f.match(usercmd))
-
-    def test_ReadFileFilter(self):
-        goodfn = '/good/file.name'
-        f = filters.ReadFileFilter(goodfn)
-        usercmd = ['cat', '/bad/file']
-        self.assertFalse(f.match(['cat', '/bad/file']))
-        usercmd = ['cat', goodfn]
-        self.assertEqual(f.get_command(usercmd), ['/bin/cat', goodfn])
-        self.assertTrue(f.match(usercmd))
-
-    def test_skips(self):
-        # Check that all filters are skipped and that the last matches
-        usercmd = ["cat", "/"]
-        filtermatch = wrapper.match_filter(self.filters, usercmd)
-        self.assertTrue(filtermatch is self.filters[-1])
index f2bafe891291aa4840d5522ab44ce35942560b01..dfa8a99ccddc760f21ec19624e8dd9b39d46373e 100644 (file)
@@ -5,3 +5,23 @@
 # List of directories to load filter definitions from (separated by ',').
 # These directories MUST all be only writeable by root !
 filters_path=/etc/cinder/rootwrap.d,/usr/share/cinder/rootwrap
+
+# List of directories to search executables in, in case filters do not
+# explicitely specify a full path (separated by ',')
+# If not specified, defaults to system PATH environment variable.
+# These directories MUST all be only writeable by root !
+exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin
+
+# Enable logging to syslog
+# Default value is False
+use_syslog=False
+
+# Which syslog facility to use.
+# Valid values include auth, authpriv, syslog, user0, user1...
+# Default value is 'syslog'
+syslog_log_facility=syslog
+
+# Which messages to log.
+# INFO means log all usage
+# ERROR means only log unsuccessful attempts
+syslog_log_level=ERROR
index d330537e04dcdaaaa11bbc0456c5b3a2309f55bb..f00a3a9798e979840ac400f60d3a6438c47d453f 100644 (file)
@@ -1,7 +1,7 @@
 [DEFAULT]
 
 # The list of modules to copy from openstack-common
-modules=cfg,exception,excutils,gettextutils,importutils,iniparser,jsonutils,local,rpc,timeutils,log,setup,notifier,context,network_utils,policy,uuidutils,lockutils,fileutils,gettextutils,scheduler,scheduler.filters,scheduler.weights,install_venv_common,flakes
+modules=cfg,exception,excutils,gettextutils,importutils,iniparser,jsonutils,local,rootwrap,rpc,timeutils,log,setup,notifier,context,network_utils,policy,uuidutils,lockutils,fileutils,gettextutils,scheduler,scheduler.filters,scheduler.weights,install_venv_common,flakes
 
 # The base module to hold the copy of openstack.common
 base=cinder