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
This commit is contained in:
Huan Xie 2016-11-24 23:29:06 -08:00 committed by Ihar Hrachyshka
parent 3d5d9e8957
commit bc23e29423
10 changed files with 30 additions and 227 deletions

View File

@ -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'])

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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/*

View File

@ -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})

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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