From bc23e29423206072c04766b5a81d424ea1c955da Mon Sep 17 00:00:00 2001 From: Huan Xie Date: Thu, 24 Nov 2016 23:29:06 -0800 Subject: [PATCH] Use os-xenapi for neutron when XenServer as hypervisor We have made os-xenapi repository to deal with XenServer Dom0 specific functions, this patch is to change neutron to use os-xenapi when XenServer is hypervisor and move the building RPM scripts into os-xenapi repo Depends-On: I8a31c81d9475387fe4ed7030b70b26098e588771 Change-Id: Ia958c366189386b1b5abbadbb4d74950aaa23bb2 --- bin/neutron-rootwrap-xen-dom0 | 7 +- neutron/agent/linux/xenapi_root_helper.py | 49 +++------- .../drivers/openvswitch/agent/xenapi/README | 16 ---- .../agent/xenapi/contrib/build-rpm.sh | 34 ------- .../SPECS/openstack-quantum-xen-plugins.spec | 30 ------ .../agent/xenapi/etc/xapi.d/plugins/netwrap | 94 ------------------- neutron/tests/unit/agent/linux/test_utils.py | 9 +- .../agent/linux/test_xenapi_root_helper.py | 12 +-- ...on-rootwrap-xen-dom0-124ee3647beecc17.yaml | 5 + requirements.txt | 1 + 10 files changed, 30 insertions(+), 227 deletions(-) delete mode 100644 neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/README delete mode 100755 neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib/build-rpm.sh delete mode 100644 neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib/rpmbuild/SPECS/openstack-quantum-xen-plugins.spec delete mode 100644 neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/etc/xapi.d/plugins/netwrap create mode 100644 releasenotes/notes/deprecate-neutron-rootwrap-xen-dom0-124ee3647beecc17.yaml diff --git a/bin/neutron-rootwrap-xen-dom0 b/bin/neutron-rootwrap-xen-dom0 index a73068a8fd5..e28d68b9cc8 100755 --- a/bin/neutron-rootwrap-xen-dom0 +++ b/bin/neutron-rootwrap-xen-dom0 @@ -30,7 +30,7 @@ import os import select import sys -import XenAPI +from os_xenapi.client import XenAPI RC_UNAUTHORIZED = 99 @@ -116,7 +116,7 @@ def run_command(url, username, password, user_args, cmd_input): try: host = session.xenapi.session.get_this_host(session.handle) result = session.xenapi.host.call_plugin( - host, 'netwrap', 'run_command', + host, 'netwrap.py', 'run_command', {'cmd': json.dumps(user_args), 'cmd_input': json.dumps(cmd_input)}) result_dict = json.loads(result) returncode = result_dict.get('returncode') @@ -133,6 +133,9 @@ def run_command(url, username, password, user_args, cmd_input): def main(): + # Deprecated: This script is deprecated and will be deleted in next release + sys.stderr.write("Deprecated: neutron-rootwrap-xen-dom0 is deprecated, " + "will be deleted in next release.") exec_name, config_file, user_args = parse_args() config = load_configuration(exec_name, config_file) filter_command(exec_name, config['filters_path'], user_args, config['exec_dirs']) diff --git a/neutron/agent/linux/xenapi_root_helper.py b/neutron/agent/linux/xenapi_root_helper.py index 17d9e95e301..f78320876a6 100644 --- a/neutron/agent/linux/xenapi_root_helper.py +++ b/neutron/agent/linux/xenapi_root_helper.py @@ -22,6 +22,8 @@ in dom0 via calling XenAPI plugin. The XenAPI plugin is responsible to determine whether a command is safe to execute. """ +from os_xenapi.client import session +from os_xenapi.client import XenAPI from oslo_config import cfg from oslo_log import log as logging from oslo_rootwrap import cmd as oslo_rootwrap_cmd @@ -44,19 +46,17 @@ xenapi_conf.register_xenapi_opts(cfg.CONF) class XenAPIClient(object): def __init__(self): - self._session = None - self._host = None - self._XenAPI = None + self._session = self._create_session( + cfg.CONF.xenapi.connection_url, + cfg.CONF.xenapi.connection_username, + cfg.CONF.xenapi.connection_password) def _call_plugin(self, plugin, fn, args): - host = self._this_host() - return self.get_session().xenapi.host.call_plugin( - host, plugin, fn, args) + return self._session.call_plugin(plugin, fn, args) def _create_session(self, url, username, password): - session = self._get_XenAPI().Session(url) - session.login_with_password(username, password) - return session + return session.XenAPISession(url, username, password, + originator="neutron") def _get_return_code(self, failure_details): # The details will be as: @@ -71,20 +71,6 @@ class XenAPIClient(object): # otherwise we get unexpected exception. return RC_UNKNOWN_XENAPI_ERROR - def _get_XenAPI(self): - # Delay importing XenAPI as this module may not exist - # for non-XenServer hypervisors. - if self._XenAPI is None: - import XenAPI - self._XenAPI = XenAPI - return self._XenAPI - - def _this_host(self): - if not self._host: - session = self.get_session() - self._host = session.xenapi.session.get_this_host(session.handle) - return self._host - def execute(self, cmd, stdin=None): out = "" err = "" @@ -93,7 +79,7 @@ class XenAPIClient(object): return oslo_rootwrap_cmd.RC_NOCOMMAND, out, err try: result_raw = self._call_plugin( - 'netwrap', 'run_command', + 'netwrap.py', 'run_command', {'cmd': jsonutils.dumps(cmd), 'cmd_input': jsonutils.dumps(stdin)}) result = jsonutils.loads(result_raw) @@ -101,20 +87,7 @@ class XenAPIClient(object): out = result['out'] err = result['err'] return returncode, out, err - except self._get_XenAPI().Failure as failure: + except XenAPI.Failure as failure: LOG.exception(_LE('Failed to execute command: %s'), cmd) returncode = self._get_return_code(failure.details) return returncode, out, err - - def get_session(self): - if self._session is None: - url = cfg.CONF.xenapi.connection_url - username = cfg.CONF.xenapi.connection_username - password = cfg.CONF.xenapi.connection_password - try: - self._session = self._create_session(url, username, password) - except Exception: - # Shouldn't reach here, otherwise it's a fatal error. - LOG.exception(_LE("Failed to initiate XenAPI session")) - raise SystemExit(1) - return self._session diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/README b/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/README deleted file mode 100644 index 0a02c99e1b1..00000000000 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/README +++ /dev/null @@ -1,16 +0,0 @@ -This directory contains files that are required for the XenAPI support. -They should be installed in the XenServer / Xen Cloud Platform dom0. - -If you install them manually, you will need to ensure that the newly -added files are executable. You can do this by running the following -command (from dom0): - - chmod a+x /etc/xapi.d/plugins/* - -Otherwise, you can build an rpm by running the following command: - - ./contrib/build-rpm.sh - -and install the rpm by running the following command (from dom0): - - rpm -i openstack-neutron-xen-plugins.rpm diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib/build-rpm.sh b/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib/build-rpm.sh deleted file mode 100755 index c3455a1393b..00000000000 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib/build-rpm.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -set -eux - -thisdir=$(dirname $(readlink -f "$0")) -export NEUTRON_ROOT="$thisdir/../../../../../../" -export PYTHONPATH=$NEUTRON_ROOT - -cd $NEUTRON_ROOT -VERSION=$(sh -c "(cat $NEUTRON_ROOT/neutron/version.py; \ - echo 'print version_info.release_string()') | \ - python") -cd - - -PACKAGE=openstack-neutron-xen-plugins -RPMBUILD_DIR=$PWD/rpmbuild -if [ ! -d $RPMBUILD_DIR ]; then - echo $RPMBUILD_DIR is missing - exit 1 -fi - -for dir in BUILD BUILDROOT SRPMS RPMS SOURCES; do - rm -rf $RPMBUILD_DIR/$dir - mkdir -p $RPMBUILD_DIR/$dir -done - -rm -rf /tmp/$PACKAGE -mkdir /tmp/$PACKAGE -cp -r ../etc/xapi.d /tmp/$PACKAGE -tar czf $RPMBUILD_DIR/SOURCES/$PACKAGE.tar.gz -C /tmp $PACKAGE - -rpmbuild -ba --nodeps --define "_topdir $RPMBUILD_DIR" \ - --define "version $VERSION" \ - $RPMBUILD_DIR/SPECS/$PACKAGE.spec diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib/rpmbuild/SPECS/openstack-quantum-xen-plugins.spec b/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib/rpmbuild/SPECS/openstack-quantum-xen-plugins.spec deleted file mode 100644 index 8ba03eaf14b..00000000000 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib/rpmbuild/SPECS/openstack-quantum-xen-plugins.spec +++ /dev/null @@ -1,30 +0,0 @@ -Name: openstack-neutron-xen-plugins -Version: %{version} -Release: 1 -Summary: Files for XenAPI support. -License: ASL 2.0 -Group: Applications/Utilities -Source0: openstack-neutron-xen-plugins.tar.gz -BuildArch: noarch -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) - -%define debug_package %{nil} - -%description -This package contains files that are required for XenAPI support for Neutron. - -%prep -%setup -q -n openstack-neutron-xen-plugins - -%install -rm -rf $RPM_BUILD_ROOT -mkdir -p $RPM_BUILD_ROOT/etc -cp -r xapi.d $RPM_BUILD_ROOT/etc -chmod a+x $RPM_BUILD_ROOT/etc/xapi.d/plugins/* - -%clean -rm -rf $RPM_BUILD_ROOT - -%files -%defattr(-,root,root,-) -/etc/xapi.d/plugins/* diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/etc/xapi.d/plugins/netwrap b/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/etc/xapi.d/plugins/netwrap deleted file mode 100644 index 895d40f62ab..00000000000 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/etc/xapi.d/plugins/netwrap +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2012 OpenStack Foundation -# Copyright 2012 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -# -# XenAPI plugin for executing network commands (ovs, iptables, etc) on dom0 -# - -import errno -import gettext -gettext.install('neutron', unicode=1) -try: - import json -except ImportError: - import simplejson as json -import subprocess - -import XenAPIPlugin - - -MSG_UNAUTHORIZED = "Unauthorized command" -MSG_NOT_FOUND = "Executable not found" - -ALLOWED_CMDS = [ - 'ip', - 'ipset', - 'iptables-save', - 'iptables-restore', - 'ip6tables-save', - 'ip6tables-restore', - 'sysctl', - # NOTE(yamamoto): of_interface=native doesn't use ovs-ofctl - 'ovs-ofctl', - 'ovs-vsctl', - 'ovsdb-client', - 'conntrack', - ] - - -class PluginError(Exception): - """Base Exception class for all plugin errors.""" - def __init__(self, *args): - Exception.__init__(self, *args) - -def _run_command(cmd, cmd_input): - """Abstracts out the basics of issuing system commands. If the command - returns anything in stderr, a PluginError is raised with that information. - Otherwise, the output from stdout is returned. - """ - try: - pipe = subprocess.PIPE - proc = subprocess.Popen(cmd, shell=False, stdin=pipe, stdout=pipe, - stderr=pipe, close_fds=True) - except OSError, e: - if e.errno == errno.ENOENT: - raise PluginError(MSG_NOT_FOUND) - (out, err) = proc.communicate(cmd_input) - return proc.returncode, out, err - - -def run_command(session, args): - cmd = json.loads(args.get('cmd')) - if cmd and cmd[0] not in ALLOWED_CMDS: - raise PluginError(MSG_UNAUTHORIZED) - returncode, out, err = _run_command( - cmd, json.loads(args.get('cmd_input', 'null'))) - if not err: - err = "" - if not out: - out = "" - # This runs in Dom0, will return to neutron-ovs-agent in compute node - result = {'returncode': returncode, - 'out': out, - 'err': err} - return json.dumps(result) - - -if __name__ == "__main__": - XenAPIPlugin.dispatch({"run_command": run_command}) diff --git a/neutron/tests/unit/agent/linux/test_utils.py b/neutron/tests/unit/agent/linux/test_utils.py index e453f3d5f0c..4b32b98874e 100644 --- a/neutron/tests/unit/agent/linux/test_utils.py +++ b/neutron/tests/unit/agent/linux/test_utils.py @@ -41,9 +41,12 @@ class AgentUtilsExecuteTest(base.BaseTestCase): def test_xenapi_root_helper(self): token = utils.xenapi_root_helper.ROOT_HELPER_DAEMON_TOKEN self.config(group='AGENT', root_helper_daemon=token) - cmd_client = utils.RootwrapDaemonHelper.get_client() - self.assertIsInstance(cmd_client, - utils.xenapi_root_helper.XenAPIClient) + with mock.patch( + 'neutron.agent.linux.utils.xenapi_root_helper.XenAPIClient')\ + as mock_xenapi_class: + mock_client = mock_xenapi_class.return_value + cmd_client = utils.RootwrapDaemonHelper.get_client() + self.assertEqual(cmd_client, mock_client) def test_without_helper(self): expected = "%s\n" % self.test_file diff --git a/neutron/tests/unit/agent/linux/test_xenapi_root_helper.py b/neutron/tests/unit/agent/linux/test_xenapi_root_helper.py index 9a073af0efc..0f96b1f66e6 100644 --- a/neutron/tests/unit/agent/linux/test_xenapi_root_helper.py +++ b/neutron/tests/unit/agent/linux/test_xenapi_root_helper.py @@ -25,9 +25,7 @@ class TestXenapiRootHelper(base.BaseTestCase): def _get_fake_xenapi_client(self): class FakeXenapiClient(helper.XenAPIClient): def __init__(self): - super(FakeXenapiClient, self).__init__() - # Mock XenAPI which may not exist in the unit test env. - self.XenAPI = mock.MagicMock() + self._session = mock.MagicMock() return FakeXenapiClient() @@ -75,7 +73,7 @@ class TestXenapiRootHelper(base.BaseTestCase): rc, out, err = xenapi_client.execute(cmd) mock_call_plugin.assert_called_once_with( - 'netwrap', 'run_command', expect_cmd_args) + 'netwrap.py', 'run_command', expect_cmd_args) self.assertEqual(0, rc) self.assertEqual("vif158.2", out) self.assertEqual("", err) @@ -85,9 +83,3 @@ class TestXenapiRootHelper(base.BaseTestCase): xenapi_client = self._get_fake_xenapi_client() rc, out, err = xenapi_client.execute(cmd) self.assertEqual(oslo_rootwrap_cmd.RC_NOCOMMAND, rc) - - def test_get_session_except(self): - xenapi_client = self._get_fake_xenapi_client() - with mock.patch.object(helper.XenAPIClient, "_create_session", - side_effect=Exception()): - self.assertRaises(SystemExit, xenapi_client.get_session) diff --git a/releasenotes/notes/deprecate-neutron-rootwrap-xen-dom0-124ee3647beecc17.yaml b/releasenotes/notes/deprecate-neutron-rootwrap-xen-dom0-124ee3647beecc17.yaml new file mode 100644 index 00000000000..4c520e709a4 --- /dev/null +++ b/releasenotes/notes/deprecate-neutron-rootwrap-xen-dom0-124ee3647beecc17.yaml @@ -0,0 +1,5 @@ +--- +deprecations: + - Now that rootwrap daemon mode is supported for XenServer, the + ``neutron-rootwrap-xen-dom0`` script is deprecated and will be removed + in a next release. diff --git a/requirements.txt b/requirements.txt index 49d30fef9fc..2ddbeb4b797 100644 --- a/requirements.txt +++ b/requirements.txt @@ -50,3 +50,4 @@ weakrefmethod>=1.0.2;python_version=='2.7' # PSF python-novaclient>=7.1.0 # Apache-2.0 python-designateclient>=1.5.0 # Apache-2.0 +os-xenapi>=0.1.1 # Apache-2.0