diff --git a/Makefile b/Makefile index d559b4d..1e4de43 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ ${BUILDROOT}/${PLUGIN_NAME}: ${BRANDING} iso -e s/@PLUGIN_REVISION@/${PLUGIN_REVISION}/g {} \ -e s/@VERSION_HOTFIXES@/${VERSION_HOTFIXES}/g {} cp suppack/xenapi-plugins-*.iso ${BUILDROOT}/${PLUGIN_NAME}/deployment_scripts/ + cp suppack/conntrack-tools.iso ${BUILDROOT}/${PLUGIN_NAME}/deployment_scripts/ ${BUILDROOT}/doc/source ${BUILDROOT}/doc/Makefile: ${BRANDING} mkdir -p ${BUILDROOT}/doc diff --git a/plugin_source/deployment_scripts/compute_post_deployment.py b/plugin_source/deployment_scripts/compute_post_deployment.py index 2e37c5e..98469fe 100755 --- a/plugin_source/deployment_scripts/compute_post_deployment.py +++ b/plugin_source/deployment_scripts/compute_post_deployment.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import ConfigParser +from distutils.version import LooseVersion import logging import netifaces import os @@ -16,6 +17,9 @@ LOG_FILE = os.path.join(utils.LOG_ROOT, 'compute_post_deployment.log') INT_BRIDGE = 'br-int' XS_PLUGIN_ISO = 'xenapi-plugins-mitaka.iso' DIST_PACKAGES_DIR = '/usr/lib/python2.7/dist-packages/' +CONNTRACK_ISO = 'conntrack-tools.iso' +CONNTRACK_CONF_SAMPLE =\ + '/usr/share/doc/conntrack-tools-1.4.2/doc/stats/conntrackd.conf' if not os.path.exists(utils.LOG_ROOT): os.mkdir(utils.LOG_ROOT) @@ -127,12 +131,12 @@ def route_to_compute(endpoints, himn_xs, himn_local, username): '> /etc/udev/rules.d/90-reroute.rules')) -def install_suppack(himn, username): +def install_suppack(himn, username, package): """Install xapi driver supplemental pack. """ tmp = utils.ssh(himn, username, 'mktemp', '-d') - utils.scp(himn, username, tmp, XS_PLUGIN_ISO) + utils.scp(himn, username, tmp, package) utils.ssh(himn, username, 'xe-install-supplemental-pack', - tmp + '/' + XS_PLUGIN_ISO, prompt='Y\n') + tmp + '/' + package, prompt='Y\n') utils.ssh(himn, username, 'rm', tmp, '-rf') @@ -306,9 +310,13 @@ def patch_compute_xenapi(): def patch_neutron_ovs_agent(): - # MO8's patch is not needed, keep the func here to add - # conntrack patch later - pass + """Apply neutron patch + + Add conntrack-tools patch to support conntrack in Dom0 + """ + patchset_dir = sys.path[0] + patch_file = '%s/patchset/fix-xenapi-returncode.patch' % patchset_dir + utils.execute('patch', '-d', '/usr/bin', '-p2', '-i', patch_file) def reconfig_multipath(): @@ -346,6 +354,34 @@ def check_and_setup_ceilometer(himn, username, password): restart_services('ceilometer-polling') +def enable_conntrack_service(himn, username): + xcp_ver = utils.ssh(himn, username, + ('xe host-param-get uuid=$(xe host-list --minimal) ' + 'param-name=software-version ' + 'param-key=platform_version')) + if LooseVersion(xcp_ver) < LooseVersion('2.1.0'): + # Only support conntrack-tools since XS7.0(XCP2.1.0) and above + logging.info('No need to enable conntrack-tools with XCP %s' % xcp_ver) + return + + conn_installed = utils.ssh(himn, username, + 'find', '/usr/sbin', '-name', 'conntrackd') + if not conn_installed: + install_suppack(himn, username, CONNTRACK_ISO) + # use conntrack statistic mode, so change conntrackd.conf + utils.ssh(himn, username, + 'mv', + '/etc/conntrackd/conntrackd.conf', + '/etc/conntrackd/conntrackd.conf.back') + utils.ssh(himn, username, + 'cp', + CONNTRACK_CONF_SAMPLE, + '/etc/conntrackd/conntrackd.conf') + + # Restart conntrackd service + utils.ssh(himn, username, 'service', 'conntrackd', 'restart') + + if __name__ == '__main__': install_xenapi_sdk() astute = utils.get_astute() @@ -363,7 +399,7 @@ if __name__ == '__main__': if username and password and endpoints and himn_local: route_to_compute(endpoints, HIMN_IP, himn_local, username) if install_xapi: - install_suppack(HIMN_IP, username) + install_suppack(HIMN_IP, username, XS_PLUGIN_ISO) enable_linux_bridge(HIMN_IP, username) forward_from_himn(himn_eth) @@ -377,6 +413,9 @@ if __name__ == '__main__': install_logrotate_script(HIMN_IP, username) + # enable conntrackd service in Dom0 + enable_conntrack_service(HIMN_IP, username) + # neutron-l2-agent in compute node modify_neutron_rootwrap_conf(HIMN_IP, username, password) br_mappings = find_bridge_mappings(astute, HIMN_IP, username) diff --git a/plugin_source/deployment_scripts/patchset/fix-xenapi-returncode.patch b/plugin_source/deployment_scripts/patchset/fix-xenapi-returncode.patch new file mode 100644 index 0000000..b43e297 --- /dev/null +++ b/plugin_source/deployment_scripts/patchset/fix-xenapi-returncode.patch @@ -0,0 +1,84 @@ +diff --git a/bin/neutron-rootwrap-xen-dom0 b/bin/neutron-rootwrap-xen-dom0 +index 829b9c1..9210e85 100755 +--- a/bin/neutron-rootwrap-xen-dom0 ++++ b/bin/neutron-rootwrap-xen-dom0 +@@ -29,7 +29,6 @@ from oslo_serialization import jsonutils as json + import os + import select + import sys +-import traceback + + import XenAPI + +@@ -45,7 +44,7 @@ def parse_args(): + exec_name = sys.argv.pop(0) + # argv[0] required; path to conf file + if len(sys.argv) < 2: +- print("%s: No command specified" % exec_name) ++ sys.stderr.write("%s: No command specified" % exec_name) + sys.exit(RC_NOCOMMAND) + + config_file = sys.argv.pop(0) +@@ -59,7 +58,7 @@ def _xenapi_section_name(config): + if len(sections) == 1: + return sections[0] + +- print("Multiple [xenapi] sections or no [xenapi] section found!") ++ sys.stderr.write("Multiple [xenapi] sections or no [xenapi] section found!") + sys.exit(RC_BADCONFIG) + + +@@ -74,13 +73,14 @@ def load_configuration(exec_name, config_file): + username = config.get(section, "xenapi_connection_username") + password = config.get(section, "xenapi_connection_password") + except ConfigParser.Error: +- print("%s: Incorrect configuration file: %s" % (exec_name, config_file)) ++ sys.stderr.write("%s: Incorrect configuration file: %s" % ++ (exec_name, config_file)) + sys.exit(RC_BADCONFIG) + if not url or not password: + msg = ("%s: Must specify xenapi_connection_url, " + "xenapi_connection_username (optionally), and " + "xenapi_connection_password in %s") % (exec_name, config_file) +- print(msg) ++ sys.stderr.write(msg) + sys.exit(RC_BADCONFIG) + return dict( + filters_path=filters_path, +@@ -105,7 +105,7 @@ def filter_command(exec_name, filters_path, user_args, exec_dirs): + filter_match = wrapper.match_filter( + filters, user_args, exec_dirs=exec_dirs) + if not filter_match: +- print("Unauthorized command: %s" % ' '.join(user_args)) ++ sys.stderr.write("Unauthorized command: %s" % ' '.join(user_args)) + sys.exit(RC_UNAUTHORIZED) + + +@@ -118,11 +118,17 @@ def run_command(url, username, password, user_args, cmd_input): + result = session.xenapi.host.call_plugin( + host, 'netwrap', 'run_command', + {'cmd': json.dumps(user_args), 'cmd_input': json.dumps(cmd_input)}) +- return json.loads(result) ++ _out = json.loads(result) ++ returncode = _out.get('returncode') ++ _stdout = _out.get('out') ++ _stderr = _out.get('err') ++ sys.stdout.write(_stdout) ++ sys.stderr.write(_stderr) ++ sys.exit(returncode) + finally: + session.xenapi.session.logout() + except Exception as e: +- traceback.print_exc() ++ sys.stderr.write("Failed to execute command in Dom0, %s" % e) + sys.exit(RC_XENAPI_ERROR) + + +@@ -142,4 +148,4 @@ def main(): + + + if __name__ == '__main__': +- print(main()) ++ main() + + diff --git a/plugin_source/deployment_scripts/patchset/netwrap b/plugin_source/deployment_scripts/patchset/netwrap new file mode 100644 index 0000000..33204cf --- /dev/null +++ b/plugin_source/deployment_scripts/patchset/netwrap @@ -0,0 +1,87 @@ +#!/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 gettext +gettext.install('neutron', unicode=1) +try: + import json +except ImportError: + import simplejson as json +import subprocess + +import XenAPIPlugin + + +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. + """ + pipe = subprocess.PIPE + proc = subprocess.Popen(cmd, shell=False, stdin=pipe, stdout=pipe, + stderr=pipe, close_fds=True) + (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: + msg = _("Dom0 execution of '%s' is not permitted") % cmd[0] + raise PluginError(msg) + 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/suppack/build-xenserver-suppack.sh b/suppack/build-xenserver-suppack.sh index d3376dc..c2b0a40 100755 --- a/suppack/build-xenserver-suppack.sh +++ b/suppack/build-xenserver-suppack.sh @@ -108,6 +108,7 @@ rm -rf neutron git clone "$NEUTRON_GITREPO" neutron pushd neutron git checkout -b mos_neutron "$GITBRANCH" +cp $FUELPLUG_UTILS_ROOT/../plugin_source/deployment_scripts/patchset/netwrap neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/etc/xapi.d/plugins/ popd cp -r xenserver-nova-suppack-builder/neutron/* \ @@ -118,6 +119,14 @@ popd NEUTRON_RPMFILE=$(find $FUELPLUG_UTILS_ROOT -name "openstack-neutron-xen-plugins-*.noarch.rpm" -print) +# ============================================= +# Find conntrack-tools related RPMs +EXTRA_RPMS="" +EXTRA_RPMS="$EXTRA_RPMS $(find $FUELPLUG_UTILS_ROOT -name "conntrack-tools-*.rpm" -print)" +EXTRA_RPMS="$EXTRA_RPMS $(find $FUELPLUG_UTILS_ROOT -name "libnetfilter_cthelper-*.rpm" -print)" +EXTRA_RPMS="$EXTRA_RPMS $(find $FUELPLUG_UTILS_ROOT -name "libnetfilter_cttimeout-*.rpm" -print)" +EXTRA_RPMS="$EXTRA_RPMS $(find $FUELPLUG_UTILS_ROOT -name "libnetfilter_queue-*.rpm" -print)" + # ============================================= # Create Supplemental pack @@ -133,24 +142,35 @@ from optparse import OptionParser parser = OptionParser() parser.add_option('--pdn', dest="product_name") parser.add_option('--pdv', dest="product_version") +parser.add_option('--desc', dest="description") parser.add_option('--bld', dest="build") parser.add_option('--out', dest="outdir") (options, args) = parser.parse_args() xcp = Requires(originator='xcp', name='main', test='ge', - product='XenServer', version='$PLATFORM_VERSION', - build='$XS_BUILD') + product='XenServer', version=options.product_version, + build=options.build) -setup(originator='xcp', name='xenapi-plugins-$OS_RELEASE', product='XenServer', + +setup(originator='xcp', name=options.product_name, product='XenServer', version=options.product_version, build=options.build, vendor='Citrix Systems, Inc.', - description="OpenStack XenServer Plugins", packages=args, requires=[xcp], + description=options.description, packages=args, requires=[xcp], outdir=options.outdir, output=['iso']) EOF python buildscript.py \ ---pdn=xenserverplugins \ ---pdv=$OS_RELEASE \ +--pdn=xenapi-plugins-$OS_RELEASE \ +--pdv=$PLATFORM_VERSION \ +--desc="OpenStack XenServer Plugins" \ --bld=0 \ --out=$FUELPLUG_UTILS_ROOT \ $RPMFILE \ $NEUTRON_RPMFILE + +python buildscript.py \ +--pdn=conntrack-tools \ +--pdv=$PLATFORM_VERSION \ +--desc="XenServer Dom0 conntrack-tools" \ +--bld=0 \ +--out=$FUELPLUG_UTILS_ROOT \ +$EXTRA_RPMS diff --git a/suppack/conntrack-rpms/conntrack-tools-1.4.2-3.el7.x86_64.rpm b/suppack/conntrack-rpms/conntrack-tools-1.4.2-3.el7.x86_64.rpm new file mode 100644 index 0000000..c9fb827 Binary files /dev/null and b/suppack/conntrack-rpms/conntrack-tools-1.4.2-3.el7.x86_64.rpm differ diff --git a/suppack/conntrack-rpms/libnetfilter_cthelper-1.0.0-8.el7.x86_64.rpm b/suppack/conntrack-rpms/libnetfilter_cthelper-1.0.0-8.el7.x86_64.rpm new file mode 100644 index 0000000..3d6220d Binary files /dev/null and b/suppack/conntrack-rpms/libnetfilter_cthelper-1.0.0-8.el7.x86_64.rpm differ diff --git a/suppack/conntrack-rpms/libnetfilter_cttimeout-1.0.0-6.el7.x86_64.rpm b/suppack/conntrack-rpms/libnetfilter_cttimeout-1.0.0-6.el7.x86_64.rpm new file mode 100644 index 0000000..3a588bd Binary files /dev/null and b/suppack/conntrack-rpms/libnetfilter_cttimeout-1.0.0-6.el7.x86_64.rpm differ diff --git a/suppack/conntrack-rpms/libnetfilter_queue-1.0.2-1.el7.x86_64.rpm b/suppack/conntrack-rpms/libnetfilter_queue-1.0.2-1.el7.x86_64.rpm new file mode 100644 index 0000000..aae73a9 Binary files /dev/null and b/suppack/conntrack-rpms/libnetfilter_queue-1.0.2-1.el7.x86_64.rpm differ