[openstack-quantum] Upstream patch: add root_helper to quantum agents
Robert A. Kukura
rkukura at fedoraproject.org
Wed Mar 14 14:37:39 UTC 2012
commit fb5b86eb89d8563920a9e67a6765811634099209
Author: Bob Kukura <rkukura at redhat.com>
Date: Wed Mar 14 10:36:41 2012 -0400
Upstream patch: add root_helper to quantum agents
- Add sudoers file enabling quantum-rootwrap for quantum user
- Configure plugin agents to use quantum-rootwrap
- Run plugin agents as quantum user
openstack-quantum.spec | 52 +-
quantum-linuxbridge-agent.service | 3 +-
quantum-openvswitch-agent.service | 3 +-
quantum-ryu-agent.service | 3 +-
quantum-sudoers | 3 +
...-3d3b1c3f78997e08c1f1a876b098d2afb759507b.patch | 931 ++++++++++++++++++++
6 files changed, 979 insertions(+), 16 deletions(-)
---
diff --git a/openstack-quantum.spec b/openstack-quantum.spec
index a7c55f3..b99560d 100644
--- a/openstack-quantum.spec
+++ b/openstack-quantum.spec
@@ -7,25 +7,32 @@
Name: openstack-quantum
Version: 2012.1
-Release: 0.3.%{release_letter}%{milestone}%{?dist}
+Release: 0.4.%{release_letter}%{milestone}%{?dist}
Summary: Virtual network service for OpenStack (quantum)
Group: Applications/System
License: ASL 2.0
URL: http://launchpad.net/quantum/
+
Source0: http://launchpad.net/quantum/%{release_name}/%{release_name}-%{milestone}/+download/quantum-%{version}~%{release_letter}%{milestone}.tar.gz
Source1: quantum.logrotate
-Source2: quantum-server.service
-Source3: quantum-linuxbridge-agent.service
-Source4: quantum-openvswitch-agent.service
-Source5: quantum-ryu-agent.service
+Source2: quantum-sudoers
+
+Source10: quantum-server.service
+Source11: quantum-linuxbridge-agent.service
+Source12: quantum-openvswitch-agent.service
+Source13: quantum-ryu-agent.service
+
# Merged upstream patch https://review.openstack.org/#change,4235
Patch1: quantum.git-82138245694b452341cc41638058adb3972a9926.patch
-# Proposed upstream patch for https://bugs.launchpad.net/quantum/+bug/949261
+# Merged upstream patch for https://bugs.launchpad.net/quantum/+bug/949261
Patch2: quantum.git-f614a7ddf57a2a4c30d9410671622caca6f4e434.patch
+# Proposed upstream patch for https://bugs.launchpad.net/quantum/+bug/948467
+Patch3: quantum.git-3d3b1c3f78997e08c1f1a876b098d2afb759507b.patch
+
BuildArch: noarch
BuildRequires: python2-devel
@@ -99,6 +106,7 @@ Group: Applications/System
Requires: openstack-quantum = %{version}-%{release}
Requires: bridge-utils
+Requires: sudo
%description -n openstack-quantum-linuxbridge
@@ -130,6 +138,7 @@ Group: Applications/System
Requires: openstack-quantum = %{version}-%{release}
Requires: openvswitch
+Requires: sudo
%description -n openstack-quantum-openvswitch
@@ -145,6 +154,7 @@ Summary: Quantum ryu plugin
Group: Applications/System
Requires: openstack-quantum = %{version}-%{release}
+Requires: sudo
%description -n openstack-quantum-ryu
@@ -160,6 +170,7 @@ networks using the Ryu Network Operating System.
%patch1 -p1
%patch2 -p1
+%patch3 -p1
find quantum -name \*.py -exec sed -i '/\/usr\/bin\/env python/d' {} \;
@@ -185,19 +196,28 @@ rm %{buildroot}/usr/etc/init.d/quantum-server
# Install execs
install -p -D -m 755 bin/quantum-server %{buildroot}%{_bindir}/quantum-server
+install -p -D -m 755 bin/quantum-rootwrap %{buildroot}%{_bindir}/quantum-rootwrap
# Move config files to proper location
install -d -m 755 %{buildroot}%{_sysconfdir}/quantum
mv %{buildroot}/usr/etc/quantum/* %{buildroot}%{_sysconfdir}/quantum
+# Configure plugin agents to use quantum-rootwrap
+for f in %{buildroot}%{_sysconfdir}/quantum/plugins/*/*.ini; do
+ sed -i 's/root_helper = sudo/root_helper = sudo quantum-rootwrap/g' $f
+done
+
# Install logrotate
install -p -D -m 644 %{SOURCE1} %{buildroot}%{_sysconfdir}/logrotate.d/openstack-quantum
+# Install sudoers
+install -p -D -m 440 %{SOURCE2} %{buildroot}%{_sysconfdir}/sudoers.d/quantum
+
# Install systemd units
-install -p -D -m 644 %{SOURCE2} %{buildroot}%{_unitdir}/quantum-server.service
-install -p -D -m 644 %{SOURCE3} %{buildroot}%{_unitdir}/quantum-linuxbridge-agent.service
-install -p -D -m 644 %{SOURCE4} %{buildroot}%{_unitdir}/quantum-openvswitch-agent.service
-install -p -D -m 644 %{SOURCE5} %{buildroot}%{_unitdir}/quantum-ryu-agent.service
+install -p -D -m 644 %{SOURCE10} %{buildroot}%{_unitdir}/quantum-server.service
+install -p -D -m 644 %{SOURCE11} %{buildroot}%{_unitdir}/quantum-linuxbridge-agent.service
+install -p -D -m 644 %{SOURCE12} %{buildroot}%{_unitdir}/quantum-openvswitch-agent.service
+install -p -D -m 644 %{SOURCE13} %{buildroot}%{_unitdir}/quantum-ryu-agent.service
# Setup directories
install -d -m 755 %{buildroot}%{_sharedstatedir}/quantum
@@ -287,12 +307,14 @@ fi
%doc LICENSE
%doc README
%{_bindir}/quantum-server
+%{_bindir}/quantum-rootwrap
%{_unitdir}/quantum-server.service
%dir %{_sysconfdir}/quantum
%config(noreplace) %{_sysconfdir}/quantum/quantum.conf
%config(noreplace) %{_sysconfdir}/quantum/plugins.ini
%dir %{_sysconfdir}/quantum/plugins
%config(noreplace) %{_sysconfdir}/logrotate.d/*
+%config(noreplace) %{_sysconfdir}/sudoers.d/quantum
%dir %attr(0755, quantum, quantum) %{_sharedstatedir}/quantum
%dir %attr(0755, quantum, quantum) %{_localstatedir}/log/quantum
@@ -317,6 +339,7 @@ fi
%exclude %{python_sitelib}/quantum/plugins/nicira
%exclude %{python_sitelib}/quantum/plugins/openvswitch
%exclude %{python_sitelib}/quantum/plugins/ryu
+%exclude %{python_sitelib}/quantum/rootwrap/*-agent.py*
%{python_sitelib}/quantum-%%{version}-*.egg-info
@@ -343,6 +366,7 @@ fi
%{_bindir}/quantum-linuxbridge-agent
%{_unitdir}/quantum-linuxbridge-agent.service
%{python_sitelib}/quantum/plugins/linuxbridge
+%{python_sitelib}/quantum/rootwrap/linuxbridge-agent.py*
%dir %{_sysconfdir}/quantum/plugins/linuxbridge
%config(noreplace) %{_sysconfdir}/quantum/plugins/linuxbridge/*.ini
@@ -361,6 +385,7 @@ fi
%{_bindir}/quantum-openvswitch-agent
%{_unitdir}/quantum-openvswitch-agent.service
%{python_sitelib}/quantum/plugins/openvswitch
+%{python_sitelib}/quantum/rootwrap/openvswitch-agent.py*
%dir %{_sysconfdir}/quantum/plugins/openvswitch
%config(noreplace) %{_sysconfdir}/quantum/plugins/openvswitch/*.ini
@@ -371,11 +396,18 @@ fi
%{_bindir}/quantum-ryu-agent
%{_unitdir}/quantum-ryu-agent.service
%{python_sitelib}/quantum/plugins/ryu
+%{python_sitelib}/quantum/rootwrap/ryu-agent.py*
%dir %{_sysconfdir}/quantum/plugins/ryu
%config(noreplace) %{_sysconfdir}/quantum/plugins/ryu/*.ini
%changelog
+* Wed Mar 14 2012 Robert Kukura <rkukura at redhat.com> - 2012.1-0.4.e4
+- Upstream patch: add root_helper to quantum agents
+- Add sudoers file enabling quantum-rootwrap for quantum user
+- Configure plugin agents to use quantum-rootwrap
+- Run plugin agents as quantum user
+
* Fri Mar 9 2012 Robert Kukura <rkukura at redhat.com> - 2012.1-0.3.e4
- Add upstream patch: remove pep8 and strict lxml version from setup.py
- Remove old fix for pep8 dependency
diff --git a/quantum-linuxbridge-agent.service b/quantum-linuxbridge-agent.service
index 0dcab18..9987526 100644
--- a/quantum-linuxbridge-agent.service
+++ b/quantum-linuxbridge-agent.service
@@ -4,8 +4,7 @@ After=syslog.target network.target
[Service]
Type=simple
-#User=quantum
-User=root
+User=quantum
ExecStart=/usr/bin/quantum-linuxbridge-agent /etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini
[Install]
diff --git a/quantum-openvswitch-agent.service b/quantum-openvswitch-agent.service
index d53438c..b7a3e19 100644
--- a/quantum-openvswitch-agent.service
+++ b/quantum-openvswitch-agent.service
@@ -4,8 +4,7 @@ After=syslog.target network.target
[Service]
Type=simple
-#User=quantum
-User=root
+User=quantum
ExecStart=/usr/bin/quantum-openvswitch-agent /etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
[Install]
diff --git a/quantum-ryu-agent.service b/quantum-ryu-agent.service
index b912cd2..eab3b87 100644
--- a/quantum-ryu-agent.service
+++ b/quantum-ryu-agent.service
@@ -4,8 +4,7 @@ After=syslog.target network.target
[Service]
Type=simple
-#User=quantum
-User=root
+User=quantum
ExecStart=/usr/bin/quantum-ryu-agent /etc/quantum/plugins/ryu/ryu.ini
[Install]
diff --git a/quantum-sudoers b/quantum-sudoers
new file mode 100644
index 0000000..3695e03
--- /dev/null
+++ b/quantum-sudoers
@@ -0,0 +1,3 @@
+Defaults:quantum !requiretty
+
+quantum ALL = (root) NOPASSWD: SETENV: /usr/bin/quantum-rootwrap
diff --git a/quantum.git-3d3b1c3f78997e08c1f1a876b098d2afb759507b.patch b/quantum.git-3d3b1c3f78997e08c1f1a876b098d2afb759507b.patch
new file mode 100644
index 0000000..5d73e53
--- /dev/null
+++ b/quantum.git-3d3b1c3f78997e08c1f1a876b098d2afb759507b.patch
@@ -0,0 +1,931 @@
+From 3d3b1c3f78997e08c1f1a876b098d2afb759507b Mon Sep 17 00:00:00 2001
+From: Bob Kukura <rkukura at redhat.com>
+Date: Tue, 13 Mar 2012 17:23:06 -0400
+Subject: [PATCH] Add root_helper to quantum agents.
+
+When running commands that require root privileges, the linuxbridge,
+openvswitch, and ryu agent now prepend the commands with the value of
+the root_helper config variable. This is set to "sudo" in the plugins'
+.ini files, allowing the agent to run as a non-root user with
+appropriate sudo privilidges.
+
+If root_helper is changed to "sudo quantum-rootwrap",
+then the command being run will be filtered against lists of each
+agent's valid commands in quantum/rootwrap. See
+http://wiki.openstack.org/Packager/Rootwrap for details.
+
+Fixes bug 948467.
+
+Change-Id: I549515068a4ce8ae480905ec5eaab6257445d0c3
+Signed-off-by: Bob Kukura <rkukura at redhat.com>
+---
+ bin/quantum-rootwrap | 73 ++++++++++
+ .../plugins/linuxbridge/linuxbridge_conf.ini | 3 +
+ .../plugins/openvswitch/ovs_quantum_plugin.ini | 9 ++
+ etc/quantum/plugins/ryu/ryu.ini | 5 +
+ .../linuxbridge/agent/linuxbridge_quantum_agent.py | 21 ++-
+ .../tests/unit/_test_linuxbridgeAgent.py | 13 ++-
+ .../plugins/openvswitch/agent/ovs_quantum_agent.py | 30 +++--
+ .../plugins/openvswitch/tests/unit/test_tunnel.py | 27 +++--
+ quantum/plugins/ryu/agent/ryu_quantum_agent.py | 17 ++-
+ quantum/rootwrap/__init__.py | 16 +++
+ quantum/rootwrap/filters.py | 143 ++++++++++++++++++++
+ quantum/rootwrap/linuxbridge-agent.py | 46 +++++++
+ quantum/rootwrap/openvswitch-agent.py | 36 +++++
+ quantum/rootwrap/ryu-agent.py | 31 +++++
+ quantum/rootwrap/wrapper.py | 63 +++++++++
+ 15 files changed, 496 insertions(+), 37 deletions(-)
+ create mode 100755 bin/quantum-rootwrap
+ create mode 100644 quantum/rootwrap/__init__.py
+ create mode 100644 quantum/rootwrap/filters.py
+ create mode 100644 quantum/rootwrap/linuxbridge-agent.py
+ create mode 100644 quantum/rootwrap/openvswitch-agent.py
+ create mode 100644 quantum/rootwrap/ryu-agent.py
+ create mode 100644 quantum/rootwrap/wrapper.py
+
+diff --git a/bin/quantum-rootwrap b/bin/quantum-rootwrap
+new file mode 100755
+index 0000000..dcdccb9
+--- /dev/null
++++ b/bin/quantum-rootwrap
+@@ -0,0 +1,73 @@
++#!/usr/bin/env python
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright (c) 2012 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.
++
++"""Root wrapper for Quantum
++
++ Uses modules in quantum.rootwrap containing filters for commands
++ that quantum agents are allowed to run as another user.
++
++ To switch to using this, you should:
++ * Set "--root_helper=sudo quantum-rootwrap" in the agents config file.
++ * Allow quantum to run quantum-rootwrap as root in quantum_sudoers:
++ quantum ALL = (root) NOPASSWD: /usr/bin/quantum-rootwrap
++ (all other commands can be removed from this file)
++
++ To make allowed commands node-specific, your packaging should only
++ install quantum/rootwrap/quantum-*-agent.py on compute nodes where
++ agents that need root privileges are run.
++"""
++
++import os
++import subprocess
++import sys
++
++
++RC_UNAUTHORIZED = 99
++RC_NOCOMMAND = 98
++
++if __name__ == '__main__':
++ # Split arguments, require at least a command
++ execname = sys.argv.pop(0)
++ if len(sys.argv) == 0:
++ print "%s: %s" % (execname, "No command specified")
++ sys.exit(RC_NOCOMMAND)
++
++ userargs = sys.argv[:]
++
++ # 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, "quantum", "__init__.py")):
++ sys.path.insert(0, possible_topdir)
++
++ from quantum.rootwrap import wrapper
++
++ # Execute command if it matches any of the loaded filters
++ filters = wrapper.load_filters()
++ filtermatch = wrapper.match_filter(filters, userargs)
++ if filtermatch:
++ obj = subprocess.Popen(filtermatch.get_command(userargs),
++ stdin=sys.stdin,
++ stdout=sys.stdout,
++ stderr=sys.stderr,
++ env=filtermatch.get_environment(userargs))
++ obj.wait()
++ sys.exit(obj.returncode)
++
++ print "Unauthorized command: %s" % ' '.join(userargs)
++ sys.exit(RC_UNAUTHORIZED)
+diff --git a/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini b/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini
+index 8bead52..dd25e07 100644
+--- a/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini
++++ b/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini
+@@ -22,3 +22,6 @@ physical_interface = eth1
+ [AGENT]
+ #agent's polling interval in seconds
+ polling_interval = 2
++# Change to "sudo quantum-rootwrap" to limit commands that can be run
++# as root.
++root_helper = sudo
+diff --git a/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini b/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
+index d8d5f21..a7a7f1a 100644
+--- a/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
++++ b/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
+@@ -31,6 +31,11 @@ integration-bridge = br-int
+ # Set local-ip to be the local IP address of this hypervisor.
+ # local-ip = 10.0.0.3
+
++[AGENT]
++# Change to "sudo quantum-rootwrap" to limit commands that can be run
++# as root.
++root_helper = sudo
++
+ #-----------------------------------------------------------------------------
+ # Sample Configurations.
+ #-----------------------------------------------------------------------------
+@@ -41,6 +46,8 @@ integration-bridge = br-int
+ # [OVS]
+ # enable-tunneling = False
+ # integration-bridge = br-int
++# [AGENT]
++# root_helper = sudo
+ #
+ # 2. With tunneling.
+ # [DATABASE]
+@@ -51,3 +58,5 @@ integration-bridge = br-int
+ # tunnel-bridge = br-tun
+ # remote-ip-file = /opt/stack/remote-ips.txt
+ # local-ip = 10.0.0.3
++# [AGENT]
++# root_helper = sudo
+diff --git a/etc/quantum/plugins/ryu/ryu.ini b/etc/quantum/plugins/ryu/ryu.ini
+index 6d732c9..2d5a2c5 100644
+--- a/etc/quantum/plugins/ryu/ryu.ini
++++ b/etc/quantum/plugins/ryu/ryu.ini
+@@ -11,3 +11,8 @@ integration-bridge = br-int
+ # openflow-rest-api = <host IP address of ofp rest api service>:<port: 8080>
+ openflow-controller = 127.0.0.1:6633
+ openflow-rest-api = 127.0.0.1:8080
++
++[AGENT]
++# Change to "sudo quantum-rootwrap" to limit commands that can be run
++# as root.
++root_helper = sudo
+diff --git a/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py b/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py
+index a9fa0b9..45cd2b6 100755
+--- a/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py
++++ b/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py
+@@ -30,6 +30,7 @@ import ConfigParser
+ import logging as LOG
+ import MySQLdb
+ import os
++import shlex
+ import signal
+ import sqlite3
+ import sys
+@@ -53,16 +54,18 @@ DB_CONNECTION = None
+
+
+ class LinuxBridge:
+- def __init__(self, br_name_prefix, physical_interface):
++ def __init__(self, br_name_prefix, physical_interface, root_helper):
+ self.br_name_prefix = br_name_prefix
+ self.physical_interface = physical_interface
++ self.root_helper = root_helper
+
+ def run_cmd(self, args):
+- LOG.debug("Running command: " + " ".join(args))
+- p = Popen(args, stdout=PIPE)
++ cmd = shlex.split(self.root_helper) + args
++ LOG.debug("Running command: " + " ".join(cmd))
++ p = Popen(cmd, stdout=PIPE)
+ retval = p.communicate()[0]
+ if p.returncode == -(signal.SIGALRM):
+- LOG.debug("Timeout running command: " + " ".join(args))
++ LOG.debug("Timeout running command: " + " ".join(cmd))
+ if retval:
+ LOG.debug("Command returned: %s" % retval)
+ return retval
+@@ -287,12 +290,15 @@ class LinuxBridge:
+
+ class LinuxBridgeQuantumAgent:
+
+- def __init__(self, br_name_prefix, physical_interface, polling_interval):
++ def __init__(self, br_name_prefix, physical_interface, polling_interval,
++ root_helper):
+ self.polling_interval = int(polling_interval)
++ self.root_helper = root_helper
+ self.setup_linux_bridge(br_name_prefix, physical_interface)
+
+ def setup_linux_bridge(self, br_name_prefix, physical_interface):
+- self.linux_br = LinuxBridge(br_name_prefix, physical_interface)
++ self.linux_br = LinuxBridge(br_name_prefix, physical_interface,
++ self.root_helper)
+
+ def process_port_binding(self, port_id, network_id, interface_id,
+ vlan_id):
+@@ -439,6 +445,7 @@ def main():
+ br_name_prefix = BRIDGE_NAME_PREFIX
+ physical_interface = config.get("LINUX_BRIDGE", "physical_interface")
+ polling_interval = config.get("AGENT", "polling_interval")
++ root_helper = config.get("AGENT", "root_helper")
+ 'Establish database connection and load models'
+ global DB_CONNECTION
+ DB_CONNECTION = config.get("DATABASE", "connection")
+@@ -462,7 +469,7 @@ def main():
+
+ try:
+ plugin = LinuxBridgeQuantumAgent(br_name_prefix, physical_interface,
+- polling_interval)
++ polling_interval, root_helper)
+ LOG.info("Agent initialized successfully, now running...")
+ plugin.daemon_loop(conn)
+ finally:
+diff --git a/quantum/plugins/linuxbridge/tests/unit/_test_linuxbridgeAgent.py b/quantum/plugins/linuxbridge/tests/unit/_test_linuxbridgeAgent.py
+index 9452189..09d30bf 100644
+--- a/quantum/plugins/linuxbridge/tests/unit/_test_linuxbridgeAgent.py
++++ b/quantum/plugins/linuxbridge/tests/unit/_test_linuxbridgeAgent.py
+@@ -22,6 +22,7 @@ import logging as LOG
+ import unittest
+ import sys
+ import os
++import shlex
+ import signal
+ from subprocess import *
+
+@@ -392,20 +393,24 @@ class LinuxBridgeAgentTest(unittest.TestCase):
+ self.physical_interface = config.get("LINUX_BRIDGE",
+ "physical_interface")
+ self.polling_interval = config.get("AGENT", "polling_interval")
++ self.root_helper = config.get("AGENT", "root_helper")
+ except Exception, e:
+ LOG.error("Unable to parse config file \"%s\": \nException%s"
+ % (self.config_file, str(e)))
+ sys.exit(1)
+ self._linuxbridge = linux_agent.LinuxBridge(self.br_name_prefix,
+- self.physical_interface)
++ self.physical_interface,
++ self.root_helper)
+ self._linuxbridge_quantum_agent = linux_agent.LinuxBridgeQuantumAgent(
+ self.br_name_prefix,
+ self.physical_interface,
+- self.polling_interval)
++ self.polling_interval,
++ self.root_helper)
+
+ def run_cmd(self, args):
+- LOG.debug("Running command: " + " ".join(args))
+- p = Popen(args, stdout=PIPE)
++ cmd = shlex.split(self.root_helper) + args
++ LOG.debug("Running command: " + " ".join(cmd))
++ p = Popen(cmd, stdout=PIPE)
+ retval = p.communicate()[0]
+ if p.returncode == -(signal.SIGALRM):
+ LOG.debug("Timeout running command: " + " ".join(args))
+diff --git a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py
+index ef555a5..9774c2e 100755
+--- a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py
++++ b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py
+@@ -21,6 +21,7 @@
+
+ import ConfigParser
+ import logging as LOG
++import shlex
+ import sys
+ import time
+ import signal
+@@ -57,15 +58,17 @@ class VifPort:
+
+
+ class OVSBridge:
+- def __init__(self, br_name):
++ def __init__(self, br_name, root_helper):
+ self.br_name = br_name
++ self.root_helper = root_helper
+
+ def run_cmd(self, args):
+- # LOG.debug("## running command: " + " ".join(args))
+- p = Popen(args, stdout=PIPE)
++ cmd = shlex.split(self.root_helper) + args
++ LOG.debug("## running command: " + " ".join(cmd))
++ p = Popen(cmd, stdout=PIPE)
+ retval = p.communicate()[0]
+ if p.returncode == -(signal.SIGALRM):
+- LOG.debug("## timeout running command: " + " ".join(args))
++ LOG.debug("## timeout running command: " + " ".join(cmd))
+ return retval
+
+ def run_vsctl(self, args):
+@@ -207,7 +210,8 @@ class LocalVLANMapping:
+
+ class OVSQuantumAgent(object):
+
+- def __init__(self, integ_br):
++ def __init__(self, integ_br, root_helper):
++ self.root_helper = root_helper
+ self.setup_integration_br(integ_br)
+
+ def port_bound(self, port, vlan_id):
+@@ -220,7 +224,7 @@ class OVSQuantumAgent(object):
+ self.int_br.clear_db_attribute("Port", port.port_name, "tag")
+
+ def setup_integration_br(self, integ_br):
+- self.int_br = OVSBridge(integ_br)
++ self.int_br = OVSBridge(integ_br, self.root_helper)
+ self.int_br.remove_all_flows()
+ # switch all traffic using L2 learning
+ self.int_br.add_flow(priority=1, actions="normal")
+@@ -323,13 +327,15 @@ class OVSQuantumTunnelAgent(object):
+ # Upper bound on available vlans.
+ MAX_VLAN_TAG = 4094
+
+- def __init__(self, integ_br, tun_br, remote_ip_file, local_ip):
++ def __init__(self, integ_br, tun_br, remote_ip_file, local_ip,
++ root_helper):
+ '''Constructor.
+
+ :param integ_br: name of the integration bridge.
+ :param tun_br: name of the tunnel bridge.
+ :param remote_ip_file: name of file containing list of hypervisor IPs.
+ :param local_ip: local IP address of this hypervisor.'''
++ self.root_helper = root_helper
+ self.available_local_vlans = set(
+ xrange(OVSQuantumTunnelAgent.MIN_VLAN_TAG,
+ OVSQuantumTunnelAgent.MAX_VLAN_TAG))
+@@ -423,7 +429,7 @@ class OVSQuantumTunnelAgent(object):
+ Create patch ports and remove all existing flows.
+
+ :param integ_br: the name of the integration bridge.'''
+- self.int_br = OVSBridge(integ_br)
++ self.int_br = OVSBridge(integ_br, self.root_helper)
+ self.int_br.delete_port("patch-tun")
+ self.patch_tun_ofport = self.int_br.add_patch_port("patch-tun",
+ "patch-int")
+@@ -442,7 +448,7 @@ class OVSQuantumTunnelAgent(object):
+ :param remote_ip_file: path to file that contains list of destination
+ IP addresses.
+ :param local_ip: the ip address of this node.'''
+- self.tun_br = OVSBridge(tun_br)
++ self.tun_br = OVSBridge(tun_br, self.root_helper)
+ self.tun_br.reset_bridge()
+ self.patch_int_ofport = self.tun_br.add_patch_port("patch-int",
+ "patch-tun")
+@@ -630,6 +636,8 @@ def main():
+ if not len(db_connection_url):
+ raise Exception('Empty db_connection_url in configuration file.')
+
++ root_helper = config.get("AGENT", "root_helper")
++
+ except Exception, e:
+ LOG.error("Error parsing common params in config_file: '%s': %s"
+ % (config_file, str(e)))
+@@ -659,10 +667,10 @@ def main():
+ sys.exit(1)
+
+ plugin = OVSQuantumTunnelAgent(integ_br, tun_br, remote_ip_file,
+- local_ip)
++ local_ip, root_helper)
+ else:
+ # Get parameters for OVSQuantumAgent.
+- plugin = OVSQuantumAgent(integ_br)
++ plugin = OVSQuantumAgent(integ_br, root_helper)
+
+ # Start everything.
+ options = {"sql_connection": db_connection_url}
+diff --git a/quantum/plugins/openvswitch/tests/unit/test_tunnel.py b/quantum/plugins/openvswitch/tests/unit/test_tunnel.py
+index ce6179a..ee408f9 100644
+--- a/quantum/plugins/openvswitch/tests/unit/test_tunnel.py
++++ b/quantum/plugins/openvswitch/tests/unit/test_tunnel.py
+@@ -63,14 +63,16 @@ class TunnelTest(unittest.TestCase):
+ self.TUN_OFPORT = 'PATCH_TUN_OFPORT'
+
+ self.mox.StubOutClassWithMocks(ovs_quantum_agent, 'OVSBridge')
+- self.mock_int_bridge = ovs_quantum_agent.OVSBridge(self.INT_BRIDGE)
++ self.mock_int_bridge = ovs_quantum_agent.OVSBridge(self.INT_BRIDGE,
++ 'sudo')
+ self.mock_int_bridge.delete_port('patch-tun')
+ self.mock_int_bridge.add_patch_port(
+ 'patch-tun', 'patch-int').AndReturn(self.TUN_OFPORT)
+ self.mock_int_bridge.remove_all_flows()
+ self.mock_int_bridge.add_flow(priority=1, actions='normal')
+
+- self.mock_tun_bridge = ovs_quantum_agent.OVSBridge(self.TUN_BRIDGE)
++ self.mock_tun_bridge = ovs_quantum_agent.OVSBridge(self.TUN_BRIDGE,
++ 'sudo')
+ self.mock_tun_bridge.reset_bridge()
+ self.mock_tun_bridge.add_patch_port(
+ 'patch-int', 'patch-tun').AndReturn(self.INT_OFPORT)
+@@ -86,7 +88,8 @@ class TunnelTest(unittest.TestCase):
+ b = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ REMOTE_IP_FILE,
+- '10.0.0.1')
++ '10.0.0.1',
++ 'sudo')
+ self.mox.VerifyAll()
+
+ def testProvisionLocalVlan(self):
+@@ -105,7 +108,8 @@ class TunnelTest(unittest.TestCase):
+ a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ REMOTE_IP_FILE,
+- '10.0.0.1')
++ '10.0.0.1',
++ 'sudo')
+ a.available_local_vlans = set([LV_ID])
+ a.provision_local_vlan(NET_UUID, LS_ID)
+ self.mox.VerifyAll()
+@@ -121,7 +125,8 @@ class TunnelTest(unittest.TestCase):
+ a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ REMOTE_IP_FILE,
+- '10.0.0.1')
++ '10.0.0.1',
++ 'sudo')
+ a.available_local_vlans = set()
+ a.local_vlan_map[NET_UUID] = LVM
+ a.reclaim_local_vlan(NET_UUID, LVM)
+@@ -137,7 +142,8 @@ class TunnelTest(unittest.TestCase):
+ a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ REMOTE_IP_FILE,
+- '10.0.0.1')
++ '10.0.0.1',
++ 'sudo')
+ a.local_vlan_map[NET_UUID] = LVM
+ a.port_bound(VIF_PORT, NET_UUID, LS_ID)
+ self.mox.VerifyAll()
+@@ -147,7 +153,8 @@ class TunnelTest(unittest.TestCase):
+ a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ REMOTE_IP_FILE,
+- '10.0.0.1')
++ '10.0.0.1',
++ 'sudo')
+ a.available_local_vlans = set([LV_ID])
+ a.local_vlan_map[NET_UUID] = LVM
+ a.port_unbound(VIF_PORT, NET_UUID)
+@@ -165,7 +172,8 @@ class TunnelTest(unittest.TestCase):
+ a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ REMOTE_IP_FILE,
+- '10.0.0.1')
++ '10.0.0.1',
++ 'sudo')
+ a.available_local_vlans = set([LV_ID])
+ a.local_vlan_map[NET_UUID] = LVM
+ a.port_dead(VIF_PORT)
+@@ -187,7 +195,8 @@ class TunnelTest(unittest.TestCase):
+ a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ REMOTE_IP_FILE,
+- '10.0.0.1')
++ '10.0.0.1',
++ 'sudo')
+
+ all_bindings = a.get_db_port_bindings(db)
+ lsw_id_bindings = a.get_db_vlan_bindings(db)
+diff --git a/quantum/plugins/ryu/agent/ryu_quantum_agent.py b/quantum/plugins/ryu/agent/ryu_quantum_agent.py
+index 11a2e32..77569fb 100755
+--- a/quantum/plugins/ryu/agent/ryu_quantum_agent.py
++++ b/quantum/plugins/ryu/agent/ryu_quantum_agent.py
+@@ -58,8 +58,9 @@ class VifPort:
+
+
+ class OVSBridge:
+- def __init__(self, br_name):
++ def __init__(self, br_name, root_helper):
+ self.br_name = br_name
++ self.root_helper = root_helper
+ self.datapath_id = None
+
+ def find_datapath_id(self):
+@@ -71,10 +72,11 @@ class OVSBridge:
+ self.datapath_id = dp_id
+
+ def run_cmd(self, args):
+- pipe = Popen(args, stdout=PIPE)
++ cmd = shlex.split(self.root_helper) + args
++ pipe = Popen(cmd, stdout=PIPE)
+ retval = pipe.communicate()[0]
+ if pipe.returncode == -(signal.SIGALRM):
+- LOG.debug("## timeout running command: " + " ".join(args))
++ LOG.debug("## timeout running command: " + " ".join(cmd))
+ return retval
+
+ def run_vsctl(self, args):
+@@ -190,7 +192,8 @@ def check_ofp_mode(db):
+
+
+ class OVSQuantumOFPRyuAgent:
+- def __init__(self, integ_br, db):
++ def __init__(self, integ_br, db, root_helper):
++ self.root_helper = root_helper
+ (ofp_controller_addr, ofp_rest_api_addr) = check_ofp_mode(db)
+
+ self.nw_id_external = rest_nw_id.NW_ID_EXTERNAL
+@@ -198,7 +201,7 @@ class OVSQuantumOFPRyuAgent:
+ self._setup_integration_br(integ_br, ofp_controller_addr)
+
+ def _setup_integration_br(self, integ_br, ofp_controller_addr):
+- self.int_br = OVSBridge(integ_br)
++ self.int_br = OVSBridge(integ_br, self.root_helper)
+ self.int_br.find_datapath_id()
+ self.int_br.set_controller(ofp_controller_addr)
+ for port in self.int_br.get_external_ports():
+@@ -297,12 +300,14 @@ def main():
+
+ integ_br = config.get("OVS", "integration-bridge")
+
++ root_helper = config.get("AGENT", "root_helper")
++
+ options = {"sql_connection": config.get("DATABASE", "sql_connection")}
+ db = SqlSoup(options["sql_connection"])
+
+ LOG.info("Connecting to database \"%s\" on %s",
+ db.engine.url.database, db.engine.url.host)
+- plugin = OVSQuantumOFPRyuAgent(integ_br, db)
++ plugin = OVSQuantumOFPRyuAgent(integ_br, db, root_helper)
+ plugin.daemon_loop(db)
+
+ sys.exit(0)
+diff --git a/quantum/rootwrap/__init__.py b/quantum/rootwrap/__init__.py
+new file mode 100644
+index 0000000..9bd7a21
+--- /dev/null
++++ b/quantum/rootwrap/__init__.py
+@@ -0,0 +1,16 @@
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright (c) 2012 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.
+diff --git a/quantum/rootwrap/filters.py b/quantum/rootwrap/filters.py
+new file mode 100644
+index 0000000..48076f4
+--- /dev/null
++++ b/quantum/rootwrap/filters.py
+@@ -0,0 +1,143 @@
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright (c) 2012 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 os
++import re
++
++
++class CommandFilter(object):
++ """Command filter only checking that the 1st argument matches exec_path"""
++
++ def __init__(self, exec_path, run_as, *args):
++ self.exec_path = exec_path
++ self.run_as = run_as
++ self.args = args
++
++ def match(self, userargs):
++ """Only check that the first argument (command) matches exec_path"""
++ if (os.path.basename(self.exec_path) == userargs[0]):
++ return True
++ return False
++
++ def get_command(self, userargs):
++ """Returns command to execute (with sudo -u if run_as != root)."""
++ 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:]
++
++ def get_environment(self, userargs):
++ """Returns specific environment to set, None if none"""
++ return None
++
++
++class RegExpFilter(CommandFilter):
++ """Command filter doing regexp matching for every argument"""
++
++ def match(self, userargs):
++ # Early skip if command or number of args don't match
++ if (len(self.args) != len(userargs)):
++ # DENY: argument numbers don't match
++ return False
++ # Compare each arg (anchoring pattern explicitly at end of string)
++ for (pattern, arg) in zip(self.args, userargs):
++ try:
++ if not re.match(pattern + '$', arg):
++ break
++ except re.error:
++ # DENY: Badly-formed filter
++ return False
++ else:
++ # ALLOW: All arguments matched
++ return True
++
++ # DENY: Some arguments did not match
++ return False
++
++
++class DnsmasqFilter(CommandFilter):
++ """Specific filter for the dnsmasq call (which includes env)"""
++
++ def match(self, userargs):
++ if (userargs[0].startswith("FLAGFILE=") and
++ userargs[1].startswith("NETWORK_ID=") and
++ userargs[2] == "dnsmasq"):
++ return True
++ return False
++
++ def get_command(self, userargs):
++ return [self.exec_path] + userargs[3:]
++
++ def get_environment(self, userargs):
++ env = os.environ.copy()
++ env['FLAGFILE'] = userargs[0].split('=')[-1]
++ env['NETWORK_ID'] = userargs[1].split('=')[-1]
++ return env
++
++
++class KillFilter(CommandFilter):
++ """Specific filter for the kill calls.
++ 1st argument is a list of accepted signals (emptystring means no signal)
++ 2nd argument is a list of accepted affected executables.
++
++ This filter relies on /proc to accurately determine affected
++ executable, so it will only work on procfs-capable systems (not OSX).
++ """
++
++ def match(self, userargs):
++ if userargs[0] != "kill":
++ return False
++ args = list(userargs)
++ if len(args) == 3:
++ signal = args.pop(1)
++ if signal not in self.args[0]:
++ # Requested signal not in accepted list
++ return False
++ else:
++ if len(args) != 2:
++ # Incorrect number of arguments
++ return False
++ if '' not in self.args[0]:
++ # No signal, but list doesn't include empty string
++ return False
++ try:
++ command = os.readlink("/proc/%d/exe" % int(args[1]))
++ if command not in self.args[1]:
++ # Affected executable not in accepted list
++ return False
++ except (ValueError, OSError):
++ # Incorrect PID
++ return False
++ return True
++
++
++class ReadFileFilter(CommandFilter):
++ """Specific filter for the utils.read_file_as_root call"""
++
++ def __init__(self, file_path, *args):
++ self.file_path = file_path
++ super(ReadFileFilter, self).__init__("/bin/cat", "root", *args)
++
++ def match(self, userargs):
++ if userargs[0] != 'cat':
++ return False
++ if userargs[1] != self.file_path:
++ return False
++ if len(userargs) != 2:
++ return False
++ return True
+diff --git a/quantum/rootwrap/linuxbridge-agent.py b/quantum/rootwrap/linuxbridge-agent.py
+new file mode 100644
+index 0000000..5421ff1
+--- /dev/null
++++ b/quantum/rootwrap/linuxbridge-agent.py
+@@ -0,0 +1,46 @@
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright (c) 2012 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.
++
++
++from quantum.rootwrap import filters
++
++filterlist = [
++ # quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py:
++ # 'brctl', 'addbr', bridge_name
++ # 'brctl', 'addif', bridge_name, interface
++ # 'brctl', 'addif', bridge_name, tap_device_name
++ # 'brctl', 'delbr', bridge_name
++ # 'brctl', 'delif', bridge_name, interface_name
++ # 'brctl', 'delif', current_bridge_name, ...
++ # 'brctl', 'setfd', bridge_name, ...
++ # 'brctl', 'stp', bridge_name, 'off'
++ filters.CommandFilter("/usr/sbin/brctl", "root"),
++ filters.CommandFilter("/sbin/brctl", "root"),
++
++ # quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py:
++ # 'ip', 'link', 'add', 'link', ...
++ # 'ip', 'link', 'delete', interface
++ # 'ip', 'link', 'set', bridge_name, 'down'
++ # 'ip', 'link', 'set', bridge_name, 'up'
++ # 'ip', 'link', 'set', interface, 'down'
++ # 'ip', 'link', 'set', interface, 'up'
++ # 'ip', 'link', 'show', 'dev', device
++ # 'ip', 'tuntap'
++ # 'ip', 'tuntap'
++ filters.CommandFilter("/usr/sbin/ip", "root"),
++ filters.CommandFilter("/sbin/ip", "root"),
++ ]
+diff --git a/quantum/rootwrap/openvswitch-agent.py b/quantum/rootwrap/openvswitch-agent.py
+new file mode 100644
+index 0000000..0c00968
+--- /dev/null
++++ b/quantum/rootwrap/openvswitch-agent.py
+@@ -0,0 +1,36 @@
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright (c) 2012 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.
++
++
++from quantum.rootwrap import filters
++
++filterlist = [
++ # quantum/plugins/openvswitch/agent/ovs_quantum_agent.py:
++ # "ovs-vsctl", "--timeout=2", ...
++ filters.CommandFilter("/usr/bin/ovs-vsctl", "root"),
++ filters.CommandFilter("/bin/ovs-vsctl", "root"),
++
++ # quantum/plugins/openvswitch/agent/ovs_quantum_agent.py:
++ # "ovs-ofctl", cmd, self.br_name, args
++ filters.CommandFilter("/usr/bin/ovs-ofctl", "root"),
++ filters.CommandFilter("/bin/ovs-ofctl", "root"),
++
++ # quantum/plugins/openvswitch/agent/ovs_quantum_agent.py:
++ # "xe", "vif-param-get", ...
++ filters.CommandFilter("/usr/bin/xe", "root"),
++ filters.CommandFilter("/usr/sbin/xe", "root"),
++ ]
+diff --git a/quantum/rootwrap/ryu-agent.py b/quantum/rootwrap/ryu-agent.py
+new file mode 100644
+index 0000000..d413ccf
+--- /dev/null
++++ b/quantum/rootwrap/ryu-agent.py
+@@ -0,0 +1,31 @@
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright (c) 2012 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.
++
++
++from quantum.rootwrap import filters
++
++filterlist = [
++ # quantum/plugins/ryu/agent/ryu_quantum_agent.py:
++ # "ovs-vsctl", "--timeout=2", ...
++ filters.CommandFilter("/usr/bin/ovs-vsctl", "root"),
++ filters.CommandFilter("/bin/ovs-vsctl", "root"),
++
++ # quantum/plugins/ryu/agent/ryu_quantum_agent.py:
++ # "xe", "vif-param-get", ...
++ filters.CommandFilter("/usr/bin/xe", "root"),
++ filters.CommandFilter("/usr/sbin/xe", "root"),
++ ]
+diff --git a/quantum/rootwrap/wrapper.py b/quantum/rootwrap/wrapper.py
+new file mode 100644
+index 0000000..bcb0122
+--- /dev/null
++++ b/quantum/rootwrap/wrapper.py
+@@ -0,0 +1,63 @@
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright (c) 2012 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 os
++import sys
++
++
++FILTERS_MODULES = ['quantum.rootwrap.linuxbridge-agent',
++ 'quantum.rootwrap.openvswitch-agent',
++ 'quantum.rootwrap.ryu-agent',
++ ]
++
++
++def load_filters():
++ """Load filters from modules present in quantum.rootwrap."""
++ filters = []
++ for modulename in FILTERS_MODULES:
++ try:
++ __import__(modulename)
++ module = sys.modules[modulename]
++ filters = filters + module.filterlist
++ except ImportError:
++ # It's OK to have missing filters, since filter modules
++ # may be shipped with specific nodes
++ pass
++ return filters
++
++
++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
+--
+1.7.4.1
+
More information about the scm-commits
mailing list