From 7fbd82239e9d3c25f1adcc14f7ec94154ff07dbd Mon Sep 17 00:00:00 2001 From: Huan Xie Date: Thu, 4 Feb 2016 09:10:06 +0000 Subject: [PATCH] Fuel plugins: Add support for using neutron vlan network 1. Enable Linux bridge in Dom0 so that neutron security group can work 2. Config vif driver as OpenVswitch driver in nova-compute.conf 3. Config integration bridge in nova-compute.conf 4. Config xenapi connection information in neturon rootwrap.conf 5. Config root_helper and bridge_mappings in ml2_conf.ini in compute node 6. Replace novaplugins-kilo.iso, use xenserverplugins-liberty.iso instead. xenserverplugins-liberty.iso contains both nova and neutron Dom0 RPM, which also match with stable/liberty. 7. Add scripts build-xenserver-suppack.sh for building Dom0 plugin ISO, in this ISO, netwrap and xenhost will be replaced with the ones in our patchset folder. Changes in netwrap is used for supporting neutron security group Changes in xenhost is used for supporting OVS interim bridge Change-Id: I7c1bc1a841877bccb019fa72407df92872831dac --- deployment_scripts/compute_post_deployment.py | 190 +++++-- deployment_scripts/fuelplugin-utils/README.md | 33 ++ .../build-xenserver-suppack.sh | 140 +++++ deployment_scripts/patchset/netwrap | 82 +++ deployment_scripts/patchset/xenhost | 482 ++++++++++++++++++ ...-kilo.iso => xenserverplugins-liberty.iso} | Bin 419840 -> 428032 bytes 6 files changed, 872 insertions(+), 55 deletions(-) create mode 100644 deployment_scripts/fuelplugin-utils/README.md create mode 100755 deployment_scripts/fuelplugin-utils/build-xenserver-suppack.sh create mode 100644 deployment_scripts/patchset/netwrap create mode 100755 deployment_scripts/patchset/xenhost rename deployment_scripts/{novaplugins-kilo.iso => xenserverplugins-liberty.iso} (91%) diff --git a/deployment_scripts/compute_post_deployment.py b/deployment_scripts/compute_post_deployment.py index df81efc..c2d4198 100755 --- a/deployment_scripts/compute_post_deployment.py +++ b/deployment_scripts/compute_post_deployment.py @@ -1,15 +1,14 @@ #!/usr/bin/env python +import ConfigParser +import logging +import netifaces import os -from logging import debug, info, warning, DEBUG, basicConfig -from subprocess import Popen, PIPE -import yaml -from shutil import rmtree -from tempfile import mkstemp, mkdtemp +import re from socket import inet_ntoa from struct import pack -import netifaces -import re +import subprocess +import yaml ASTUTE_PATH = '/etc/astute.yaml' @@ -17,22 +16,27 @@ ASTUTE_SECTION = 'fuel-plugin-xenserver' LOG_ROOT = '/var/log/fuel-plugin-xenserver' LOG_FILE = 'compute_post_deployment.log' HIMN_IP = '169.254.0.1' +INT_BRIDGE = 'br-int' +XS_PLUGIN_ISO = 'xenserverplugins-liberty.iso' +DIST_PACKAGES_DIR = '/usr/lib/python2.7/dist-packages/' if not os.path.exists(LOG_ROOT): os.mkdir(LOG_ROOT) -basicConfig(filename=os.path.join(LOG_ROOT, LOG_FILE), level=DEBUG) +logging.basicConfig(filename=os.path.join(LOG_ROOT, LOG_FILE), + level=logging.DEBUG) def reportError(err): - warning(err) + logging.warning(err) raise Exception(err) def execute(*cmd, **kwargs): cmd = map(str, cmd) - info(' '.join(cmd)) - proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) + logging.info(' '.join(cmd)) + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) if 'prompt' in kwargs: prompt = kwargs.get('prompt') @@ -45,7 +49,7 @@ def execute(*cmd, **kwargs): (out, err) = (out.replace('\n', ''), err.replace('\n', '')) if out: - debug(out) + logging.debug(out) if proc.returncode is not None and proc.returncode != 0: reportError(err) @@ -91,13 +95,13 @@ def astute_get(dct, keys, default=None, fail_if_missing=True): def get_options(astute, astute_section): """Return username and password filled in plugin.""" - if not astute_section in astute: + if astute_section not in astute: reportError('%s not found' % astute_section) options = astute[astute_section] - info('username: {username}'.format(**options)) - info('password: {password}'.format(**options)) - info('install_xapi: {install_xapi}'.format(**options)) + logging.info('username: {username}'.format(**options)) + logging.info('password: {password}'.format(**options)) + logging.info('install_xapi: {install_xapi}'.format(**options)) return options['username'], options['password'], \ options['install_xapi'] @@ -112,8 +116,8 @@ def get_endpoints(astute): endpoints[k]['IP'][0] ) for k in endpoints]) - info('storage network: {storage}'.format(**endpoints)) - info('mgmt network: {mgmt}'.format(**endpoints)) + logging.info('storage network: {storage}'.format(**endpoints)) + logging.info('mgmt network: {mgmt}'.format(**endpoints)) return endpoints @@ -128,7 +132,7 @@ def init_eth(): himn_mac = execute( 'xenstore-read', '/local/domain/%s/vm-data/himn_mac' % domid) - info('himn_mac: %s' % himn_mac) + logging.info('himn_mac: %s' % himn_mac) _mac = lambda eth: \ netifaces.ifaddresses(eth).get(netifaces.AF_LINK)[0]['addr'] @@ -137,7 +141,7 @@ def init_eth(): reportError('Cannot find eth matches himn_mac') eth = eths[0] - info('himn_eth: %s' % eth) + logging.info('himn_eth: %s' % eth) ip = netifaces.ifaddresses(eth).get(netifaces.AF_INET) @@ -149,7 +153,7 @@ def init_eth(): 'post-up route del default dev {eth}').format(eth=eth) with open(fname, 'w') as f: f.write(s) - info('%s created' % fname) + logging.info('%s created' % fname) execute('ifdown', eth) execute('ifup', eth) ip = netifaces.ifaddresses(eth).get(netifaces.AF_INET) @@ -158,7 +162,7 @@ def init_eth(): himn_local = ip[0]['addr'] himn_xs = '.'.join(himn_local.split('.')[:-1] + ['1']) if HIMN_IP == himn_xs: - info('himn_local: %s' % himn_local) + logging.info('himn_local: %s' % himn_local) return eth, himn_local reportError('HIMN failed to get IP address from XenServer') @@ -173,23 +177,11 @@ def check_hotfix_exists(himn, username, password, hotfix): def install_xenapi_sdk(): """Install XenAPI Python SDK""" - execute('cp', 'XenAPI.py', '/usr/lib/python2.7/dist-packages/') + execute('cp', 'XenAPI.py', DIST_PACKAGES_DIR) def create_novacompute_conf(himn, username, password, public_ip): """Fill nova-compute.conf with HIMN IP and root password. """ - template = '\n'.join([ - '[DEFAULT]', - 'compute_driver=xenapi.XenAPIDriver', - 'force_config_drive=True', - 'novncproxy_base_url=https://%s:6080/vnc_auto.html', - 'vncserver_proxyclient_address=%s', - '[xenserver]', - 'connection_url=http://%s', - 'connection_username="%s"', - 'connection_password="%s"' - ]) - mgmt_if = netifaces.ifaddresses('br-mgmt') if mgmt_if and mgmt_if.get(netifaces.AF_INET) \ and mgmt_if.get(netifaces.AF_INET)[0]['addr']: @@ -197,19 +189,27 @@ def create_novacompute_conf(himn, username, password, public_ip): else: reportError('Cannot get IP Address on Management Network') - s = template % (public_ip, mgmt_ip, himn, username, password) - fname = '/etc/nova/nova-compute.conf' - with open(fname, 'w') as f: - f.write(s) - info('%s created' % fname) - - -def restart_nova_services(): - """Restart nova services""" - execute('stop', 'nova-compute') - execute('start', 'nova-compute') - execute('stop', 'nova-network') - execute('start', 'nova-network') + filename = '/etc/nova/nova-compute.conf' + cf = ConfigParser.ConfigParser() + try: + cf.read(filename) + cf.set('DEFAULT', 'compute_driver', 'xenapi.XenAPIDriver') + cf.set('DEFAULT', 'force_config_drive', 'True') + cf.set('DEFAULT', 'novncproxy_base_url', + 'https://%s:6080/vnc_auto.html' % public_ip) + cf.set('DEFAULT', 'vncserver_proxyclient_address', mgmt_ip) + if not cf.has_section('xenserver'): + cf.add_section('xenserver') + cf.set('xenserver', 'connection_url', 'http://%s' % himn) + cf.set('xenserver', 'connection_username', username) + cf.set('xenserver', 'connection_password', password) + cf.set('xenserver', 'vif_driver', + 'nova.virt.xenapi.vif.XenAPIOpenVswitchDriver') + cf.set('xenserver', 'ovs_integration_bridge', INT_BRIDGE) + cf.write(open(filename, 'w')) + except Exception: + reportError('Cannot set configurations to %s' % filename) + logging.info('%s created' % filename) def route_to_compute(endpoints, himn_xs, himn_local, username, password): @@ -238,17 +238,17 @@ def route_to_compute(endpoints, himn_xs, himn_local, username, password): % ' '.join(params) ssh(himn_xs, username, password, sh) else: - info('%s network ip is missing' % endpoint_name) + logging.info('%s network ip is missing' % endpoint_name) def install_suppack(himn, username, password): """Install xapi driver supplemental pack. """ - # TODO: check if installed - scp(himn, username, password, '/tmp/', 'novaplugins-kilo.iso') - out = ssh( + # TODO(Johnhua): check if installed + scp(himn, username, password, '/tmp/', XS_PLUGIN_ISO) + ssh( himn, username, password, 'xe-install-supplemental-pack', - '/tmp/novaplugins-kilo.iso', prompt='Y\n') - ssh(himn, username, password, 'rm', '/tmp/novaplugins-kilo.iso') + '/tmp/%s' % XS_PLUGIN_ISO, prompt='Y\n') + ssh(himn, username, password, 'rm', '/tmp/%s' % XS_PLUGIN_ISO) def forward_from_himn(eth): @@ -301,6 +301,78 @@ def install_logrotate_script(himn, username, password): CRONTAB''') +def modify_neutron_rootwrap_conf(himn, username, password): + """Set xenapi configurations""" + filename = '/etc/neutron/rootwrap.conf' + cf = ConfigParser.ConfigParser() + try: + cf.read(filename) + cf.set('xenapi', 'xenapi_connection_url', 'http://%s' % himn) + cf.set('xenapi', 'xenapi_connection_username', username) + cf.set('xenapi', 'xenapi_connection_password', password) + cf.write(open(filename, 'w')) + except Exception: + reportError("Fail to modify file %s", filename) + logging.info('Modify file %s successfully', filename) + + +def modify_neutron_ovs_agent_conf(int_br, br_mappings): + filename = '/etc/neutron/plugins/ml2/ml2_conf.ini' + cf = ConfigParser.ConfigParser() + try: + cf.read(filename) + cf.set('agent', 'root_helper', + 'neutron-rootwrap-xen-dom0 /etc/neutron/rootwrap.conf') + cf.set('agent', 'root_helper_daemon', '') + cf.set('agent', 'minimize_polling', False) + cf.set('ovs', 'integration_bridge', int_br) + cf.set('ovs', 'bridge_mappings', br_mappings) + cf.write(open(filename, 'w')) + except Exception: + reportError("Fail to modify %s", filename) + logging.info('Modify %s successfully', filename) + + +def get_private_network_ethX(): + # find out bridge which is used for private network + values = astute['network_scheme']['transformations'] + for item in values: + if item['action'] == 'add-port' and item['bridge'] == 'br-aux': + return item['name'] + + +def find_bridge_mappings(astute, himn, username, password): + ethX = get_private_network_ethX() + if not ethX: + reportError("Cannot find eth used for private network") + + # find the ethX mac in /sys/class/net/ethX/address + fo = open('/sys/class/net/%s/address' % ethX, 'r') + mac = fo.readline() + fo.close() + network_uuid = ssh(himn, username, password, + 'xe vif-list params=network-uuid minimal=true MAC=%s' % mac) + bridge = ssh(himn, username, password, + 'xe network-param-get param-name=bridge uuid=%s' % network_uuid) + + # find physical network name + phynet_setting = astute['quantum_settings']['L2']['phys_nets'] + physnet = phynet_setting.keys()[0] + return physnet + ':' + bridge + + +def restart_services(service_name): + execute('stop', service_name) + execute('start', service_name) + + +def enable_linux_bridge(himn, username, password): + # When using OVS under XS6.5, it will prevent use of Linux bridge in + # Dom0, but neutron-openvswitch-agent in compute node will use Linux + # bridge, so we remove this restriction here + ssh(himn, username, password, 'rm -f /etc/modprobe.d/blacklist-bridge') + + if __name__ == '__main__': install_xenapi_sdk() astute = get_astute(ASTUTE_PATH) @@ -318,12 +390,20 @@ if __name__ == '__main__': endpoints, HIMN_IP, himn_local, username, password) if install_xapi: install_suppack(HIMN_IP, username, password) + enable_linux_bridge(HIMN_IP, username, password) forward_from_himn(himn_eth) # port forwarding for novnc forward_port('br-mgmt', himn_eth, HIMN_IP, '80') create_novacompute_conf(HIMN_IP, username, password, public_ip) - restart_nova_services() + restart_services('nova-compute') install_logrotate_script(HIMN_IP, username, password) + + # neutron-l2-agent in compute node + modify_neutron_rootwrap_conf(HIMN_IP, username, password) + br_mappings = find_bridge_mappings(astute, HIMN_IP, + username, password) + modify_neutron_ovs_agent_conf(INT_BRIDGE, br_mappings) + restart_services('neutron-plugin-openvswitch-agent') diff --git a/deployment_scripts/fuelplugin-utils/README.md b/deployment_scripts/fuelplugin-utils/README.md new file mode 100644 index 0000000..c62a05b --- /dev/null +++ b/deployment_scripts/fuelplugin-utils/README.md @@ -0,0 +1,33 @@ +# build-xenserver-suppack.sh + +This script is used to build iso for XenServer Dom0 xapi plugin. + +It will build both Nova and Neutron Dom0 plugin RPM packages firstly, +and then make them in one ISO. + + +## usage: + +#####./build-xenserver-suppack.sh $xs-version $xs-build $os-git-branch $os-plugin-version + +* xs-version: XenServer version which can be used for this plugin + +* xs-build: XenServer build number + +* os-git-branch: OpenStack branch that's used for building this plugin + +* os-plugin-version: OpenStack XenServer Dom0 plguin version + + + +*NOTE: If no input parameters given, default values are used* + +*xs-version: 6.5* + +*xs-build: 90233c* + +*os-git-branch: stable/liberty* + +*os-plugin-version: 2015.1* + + diff --git a/deployment_scripts/fuelplugin-utils/build-xenserver-suppack.sh b/deployment_scripts/fuelplugin-utils/build-xenserver-suppack.sh new file mode 100755 index 0000000..623399a --- /dev/null +++ b/deployment_scripts/fuelplugin-utils/build-xenserver-suppack.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +set -eux + +# ============================================= +# Usage of this script: +# ./build-xenserver-suppack.sh xs-version xs-build git-branch plugin-version +# or +# ./build-xenserver-suppack.sh +# +# You can provide explict input parameters or you can use the default ones: +# XenServer version +# XenServer build +# OpenStack release branch +# XenServer OpenStack plugin version + + +THIS_FILE=$(readlink -f $0) +FUELPLUG_UTILS_ROOT=$(dirname $THIS_FILE) +DEPLOYMENT_SCRIPT_ROOT=$(dirname $FUELPLUG_UTILS_ROOT) +cd $FUELPLUG_UTILS_ROOT +rm -rf xenserver-suppack +mkdir -p xenserver-suppack && cd xenserver-suppack + + +# ============================================= +# Configurable items + +# xenserver version info +XS_VERSION=${1:-"6.5"} +XS_BUILD=${2:-"90233c"} + +# branch info +GITBRANCH=${3:-"stable/liberty"} + +# nova and neutron xenserver dom0 plugin version +XS_PLUGIN_VERSION=${4:-"2015.1"} + +# OpenStack release +OS_RELEASE=liberty + +# repository info +NOVA_GITREPO="https://git.openstack.org/openstack/nova" +NEUTRON_GITREPO="https://git.openstack.org/openstack/neutron" +DDK_ROOT_URL="http://copper.eng.hq.xensource.com/builds/ddk-xs6_2.tgz" +RPM_BUILDER_REPO="https://github.com/citrix-openstack/xenserver-nova-suppack-builder" + +# Update system and install dependencies +export DEBIAN_FRONTEND=noninteractive + + +# ============================================= +# Check out rpm packaging repo +rm -rf xenserver-nova-suppack-builder +git clone $RPM_BUILDER_REPO + + +# ============================================= +# Create nova rpm file +rm -rf nova +git clone "$NOVA_GITREPO" nova +cd nova +git fetch origin "$GITBRANCH" +git checkout FETCH_HEAD +# patch xenhost as this file is not merged to liberty +cp $DEPLOYMENT_SCRIPT_ROOT/patchset/xenhost plugins/xenserver/xenapi/etc/xapi.d/plugins/ +cd .. + +cp -r xenserver-nova-suppack-builder/plugins/xenserver/xenapi/* nova/plugins/xenserver/xenapi/ +cd nova/plugins/xenserver/xenapi/contrib +./build-rpm.sh $XS_PLUGIN_VERSION +cd $FUELPLUG_UTILS_ROOT/xenserver-suppack/ +RPMFILE=$(find -name "openstack-xen-plugins-*.noarch.rpm" -print) + + +# ============================================= +# Create neutron rpm file +rm -rf neutron +git clone "$NEUTRON_GITREPO" neutron +cd neutron +git fetch origin "$GITBRANCH" +git checkout FETCH_HEAD +# patch netwrap as this file is not merged to liberty +cp $DEPLOYMENT_SCRIPT_ROOT/patchset/netwrap \ + neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/etc/xapi.d/plugins/ +chmod +x neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/etc/xapi.d/plugins/netwrap +cd .. + +cp -r xenserver-nova-suppack-builder/neutron/* \ + neutron/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/ +cd neutron/neutron/plugins/ml2/drivers/openvswitch/agent/xenapi/contrib +./build-rpm.sh $XS_PLUGIN_VERSION +cd $FUELPLUG_UTILS_ROOT/xenserver-suppack/ +NEUTRON_RPMFILE=$(find -name "openstack-neutron-xen-plugins-*.noarch.rpm" -print) + + +# ============================================= +# Create Supplemental pack +rm -rf suppack +mkdir suppack + +DDKROOT=$(mktemp -d) + +wget -qO - "$DDK_ROOT_URL" | sudo tar -xzf - -C "$DDKROOT" + +sudo mkdir $DDKROOT/mnt/host +sudo mount --bind $(pwd) $DDKROOT/mnt/host + +sudo tee $DDKROOT/buildscript.py << EOF +from xcp.supplementalpack import * +from optparse import OptionParser + +parser = OptionParser() +parser.add_option('--pdn', dest="product_name") +parser.add_option('--pdv', dest="product_version") +parser.add_option('--bld', dest="build") +parser.add_option('--out', dest="outdir") +(options, args) = parser.parse_args() + +xs = Requires(originator='xs', name='main', test='ge', + product='XenServer', version='$XS_VERSION', + build='$XS_BUILD') + +setup(originator='xs', name='xenserverplugins-$OS_RELEASE', product='XenServer', + version=options.product_version, build=options.build, vendor='Citrix Systems, Inc.', + description="OpenStack XenServer Plugins", packages=args, requires=[xs], + outdir=options.outdir, output=['iso']) +EOF + +sudo chroot $DDKROOT python buildscript.py \ +--pdn=xenserverplugins \ +--pdv=$OS_RELEASE \ +--bld=0 \ +--out=/mnt/host/suppack \ +/mnt/host/$RPMFILE \ +/mnt/host/$NEUTRON_RPMFILE + +# Cleanup +sudo umount $DDKROOT/mnt/host +sudo rm -rf "$DDKROOT" diff --git a/deployment_scripts/patchset/netwrap b/deployment_scripts/patchset/netwrap new file mode 100644 index 0000000..41b37b8 --- /dev/null +++ b/deployment_scripts/patchset/netwrap @@ -0,0 +1,82 @@ +#!/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 +# Changes in this file merged post liberty, CommitID: +# b0cef88866db3d325974b1691ac3e1030144ee19 +# + +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', + ] + + +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) + + if proc.returncode != 0: + raise PluginError(err) + return out + + +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) + result = _run_command(cmd, json.loads(args.get('cmd_input', 'null'))) + return json.dumps(result) + + +if __name__ == "__main__": + XenAPIPlugin.dispatch({"run_command": run_command}) diff --git a/deployment_scripts/patchset/xenhost b/deployment_scripts/patchset/xenhost new file mode 100755 index 0000000..1382329 --- /dev/null +++ b/deployment_scripts/patchset/xenhost @@ -0,0 +1,482 @@ +#!/usr/bin/env python + +# Copyright 2011 OpenStack Foundation +# Copyright 2011 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. + +# NOTE: XenServer still only supports Python 2.4 in it's dom0 userspace +# which means the Nova xenapi plugins must use only Python 2.4 features + +# +# XenAPI plugin for host operations +# Changes in this file will be merged post liberty +# OVS interim bridge: https://review.openstack.org/#/c/242846/29 +# Neutron security group: https://review.openstack.org/#/c/251271/10 +# + +try: + import json +except ImportError: + import simplejson as json +import logging +import re +import sys +import time + +import utils + +import pluginlib_nova as pluginlib +import XenAPI +import XenAPIPlugin + +try: + import xmlrpclib +except ImportError: + import six.moves.xmlrpc_client as xmlrpclib + + +pluginlib.configure_logging("xenhost") +_ = pluginlib._ + + +host_data_pattern = re.compile(r"\s*(\S+) \([^\)]+\) *: ?(.*)") +config_file_path = "/usr/etc/xenhost.conf" +DEFAULT_TRIES = 23 +DEFAULT_SLEEP = 10 + + +def jsonify(fnc): + def wrapper(*args, **kwargs): + return json.dumps(fnc(*args, **kwargs)) + return wrapper + + +class TimeoutError(StandardError): + pass + + +def _run_command(cmd, cmd_input=None): + """Wrap utils.run_command to raise PluginError on failure + """ + try: + return utils.run_command(cmd, cmd_input=cmd_input) + except utils.SubprocessException, e: + raise pluginlib.PluginError(e.err) + + +def _resume_compute(session, compute_ref, compute_uuid): + """Resume compute node on slave host after pool join. This has to + happen regardless of the success or failure of the join operation.""" + try: + # session is valid if the join operation has failed + session.xenapi.VM.start(compute_ref, False, True) + except XenAPI.Failure, e: + # if session is invalid, e.g. xapi has restarted, then the pool + # join has been successful, wait for xapi to become alive again + for c in xrange(0, DEFAULT_TRIES): + try: + _run_command(["xe", "vm-start", "uuid=%s" % compute_uuid]) + return + except pluginlib.PluginError, e: + logging.exception('Waited %d seconds for the slave to ' + 'become available.' % (c * DEFAULT_SLEEP)) + time.sleep(DEFAULT_SLEEP) + raise pluginlib.PluginError('Unrecoverable error: the host has ' + 'not come back for more than %d seconds' + % (DEFAULT_SLEEP * (DEFAULT_TRIES + 1))) + + +@jsonify +def set_host_enabled(self, arg_dict): + """Sets this host's ability to accept new instances. + It will otherwise continue to operate normally. + """ + enabled = arg_dict.get("enabled") + if enabled is None: + raise pluginlib.PluginError( + _("Missing 'enabled' argument to set_host_enabled")) + + host_uuid = arg_dict['host_uuid'] + if enabled == "true": + result = _run_command(["xe", "host-enable", "uuid=%s" % host_uuid]) + elif enabled == "false": + result = _run_command(["xe", "host-disable", "uuid=%s" % host_uuid]) + else: + raise pluginlib.PluginError(_("Illegal enabled status: %s") % enabled) + # Should be empty string + if result: + raise pluginlib.PluginError(result) + # Return the current enabled status + cmd = ["xe", "host-param-get", "uuid=%s" % host_uuid, "param-name=enabled"] + host_enabled = _run_command(cmd) + if host_enabled == "true": + status = "enabled" + else: + status = "disabled" + return {"status": status} + + +def _write_config_dict(dct): + conf_file = file(config_file_path, "w") + json.dump(dct, conf_file) + conf_file.close() + + +def _get_config_dict(): + """Returns a dict containing the key/values in the config file. + If the file doesn't exist, it is created, and an empty dict + is returned. + """ + try: + conf_file = file(config_file_path) + config_dct = json.load(conf_file) + conf_file.close() + except IOError: + # File doesn't exist + config_dct = {} + # Create the file + _write_config_dict(config_dct) + return config_dct + + +@jsonify +def get_config(self, arg_dict): + """Return the value stored for the specified key, or None if no match.""" + conf = _get_config_dict() + params = arg_dict["params"] + try: + dct = json.loads(params) + except Exception, e: + dct = params + key = dct["key"] + ret = conf.get(key) + if ret is None: + # Can't jsonify None + return "None" + return ret + + +@jsonify +def set_config(self, arg_dict): + """Write the specified key/value pair, overwriting any existing value.""" + conf = _get_config_dict() + params = arg_dict["params"] + try: + dct = json.loads(params) + except Exception, e: + dct = params + key = dct["key"] + val = dct["value"] + if val is None: + # Delete the key, if present + conf.pop(key, None) + else: + conf.update({key: val}) + _write_config_dict(conf) + + +def iptables_config(session, args): + # command should be either save or restore + logging.debug("iptables_config:enter") + logging.debug("iptables_config: args=%s", args) + cmd_args = pluginlib.exists(args, 'cmd_args') + logging.debug("iptables_config: cmd_args=%s", cmd_args) + process_input = pluginlib.optional(args, 'process_input') + logging.debug("iptables_config: process_input=%s", process_input) + cmd = json.loads(cmd_args) + cmd = map(str, cmd) + + # either execute iptable-save or iptables-restore + # command must be only one of these two + # process_input must be used only with iptables-restore + if len(cmd) > 0 and cmd[0] in ('iptables-save', + 'iptables-restore', + 'ip6tables-save', + 'ip6tables-restore'): + result = _run_command(cmd, process_input) + ret_str = json.dumps(dict(out=result, + err='')) + logging.debug("iptables_config:exit") + return ret_str + else: + # else don't do anything and return an error + raise pluginlib.PluginError(_("Invalid iptables command")) + + +def network_config(session, args): + # function to config OVS bridge and Linux bridge + ALLOWED_CMDS = [ + 'ovs-vsctl', + 'brctl', + 'ip' + ] + cmd = json.loads(args.get('cmd')) + if cmd is None or cmd == []: + msg = _("empty command is supplied") + raise pluginlib.PluginError(msg) + if cmd[0] not in ALLOWED_CMDS: + msg = _("Dom0 execution of '%s' is not permitted") % cmd[0] + raise pluginlib.PluginError(msg) + result = _run_command(cmd, json.loads(args.get('cmd_input', 'null'))) + return json.dumps(result) + + +def _power_action(action, arg_dict): + # Host must be disabled first + host_uuid = arg_dict['host_uuid'] + result = _run_command(["xe", "host-disable", "uuid=%s" % host_uuid]) + if result: + raise pluginlib.PluginError(result) + # All running VMs must be shutdown + result = _run_command(["xe", "vm-shutdown", "--multiple", + "resident-on=%s" % host_uuid]) + if result: + raise pluginlib.PluginError(result) + cmds = {"reboot": "host-reboot", + "startup": "host-power-on", + "shutdown": "host-shutdown",} + result = _run_command(["xe", cmds[action], "uuid=%s" % host_uuid]) + # Should be empty string + if result: + raise pluginlib.PluginError(result) + return {"power_action": action} + + +@jsonify +def host_reboot(self, arg_dict): + """Reboots the host.""" + return _power_action("reboot", arg_dict) + + +@jsonify +def host_shutdown(self, arg_dict): + """Reboots the host.""" + return _power_action("shutdown", arg_dict) + + +@jsonify +def host_start(self, arg_dict): + """Starts the host. Currently not feasible, since the host + runs on the same machine as Xen. + """ + return _power_action("startup", arg_dict) + + +@jsonify +def host_join(self, arg_dict): + """Join a remote host into a pool whose master is the host + where the plugin is called from. The following constraints apply: + + - The host must have no VMs running, except nova-compute, which will be + shut down (and restarted upon pool-join) automatically, + - The host must have no shared storage currently set up, + - The host must have the same license of the master, + - The host must have the same supplemental packs as the master.""" + session = XenAPI.Session(arg_dict.get("url")) + session.login_with_password(arg_dict.get("user"), + arg_dict.get("password")) + compute_ref = session.xenapi.VM.get_by_uuid(arg_dict.get('compute_uuid')) + session.xenapi.VM.clean_shutdown(compute_ref) + try: + if arg_dict.get("force"): + session.xenapi.pool.join(arg_dict.get("master_addr"), + arg_dict.get("master_user"), + arg_dict.get("master_pass")) + else: + session.xenapi.pool.join_force(arg_dict.get("master_addr"), + arg_dict.get("master_user"), + arg_dict.get("master_pass")) + finally: + _resume_compute(session, compute_ref, arg_dict.get("compute_uuid")) + + +@jsonify +def host_data(self, arg_dict): + """Runs the commands on the xenstore host to return the current status + information. + """ + host_uuid = arg_dict['host_uuid'] + resp = _run_command(["xe", "host-param-list", "uuid=%s" % host_uuid]) + parsed_data = parse_response(resp) + # We have the raw dict of values. Extract those that we need, + # and convert the data types as needed. + ret_dict = cleanup(parsed_data) + # Add any config settings + config = _get_config_dict() + ret_dict.update(config) + return ret_dict + + +def parse_response(resp): + data = {} + for ln in resp.splitlines(): + if not ln: + continue + mtch = host_data_pattern.match(ln.strip()) + try: + k, v = mtch.groups() + data[k] = v + except AttributeError: + # Not a valid line; skip it + continue + return data + + +@jsonify +def host_uptime(self, arg_dict): + """Returns the result of the uptime command on the xenhost.""" + return {"uptime": _run_command(['uptime'])} + + +def cleanup(dct): + """Take the raw KV pairs returned and translate them into the + appropriate types, discarding any we don't need. + """ + def safe_int(val): + """Integer values will either be string versions of numbers, + or empty strings. Convert the latter to nulls. + """ + try: + return int(val) + except ValueError: + return None + + def strip_kv(ln): + return [val.strip() for val in ln.split(":", 1)] + + out = {} + +# sbs = dct.get("supported-bootloaders", "") +# out["host_supported-bootloaders"] = sbs.split("; ") +# out["host_suspend-image-sr-uuid"] = dct.get("suspend-image-sr-uuid", "") +# out["host_crash-dump-sr-uuid"] = dct.get("crash-dump-sr-uuid", "") +# out["host_local-cache-sr"] = dct.get("local-cache-sr", "") + out["enabled"] = dct.get("enabled", "true") == "true" + out["host_memory"] = omm = {} + omm["total"] = safe_int(dct.get("memory-total", "")) + omm["overhead"] = safe_int(dct.get("memory-overhead", "")) + omm["free"] = safe_int(dct.get("memory-free", "")) + omm["free-computed"] = safe_int( + dct.get("memory-free-computed", "")) + +# out["host_API-version"] = avv = {} +# avv["vendor"] = dct.get("API-version-vendor", "") +# avv["major"] = safe_int(dct.get("API-version-major", "")) +# avv["minor"] = safe_int(dct.get("API-version-minor", "")) + + out["enabled"] = dct.get("enabled", True) + out["host_uuid"] = dct.get("uuid", None) + out["host_name-label"] = dct.get("name-label", "") + out["host_name-description"] = dct.get("name-description", "") +# out["host_host-metrics-live"] = dct.get( +# "host-metrics-live", "false") == "true" + out["host_hostname"] = dct.get("hostname", "") + out["host_ip_address"] = dct.get("address", "") + oc = dct.get("other-config", "") + out["host_other-config"] = ocd = {} + if oc: + for oc_fld in oc.split("; "): + ock, ocv = strip_kv(oc_fld) + ocd[ock] = ocv + + capabilities = dct.get("capabilities", "") + out["host_capabilities"] = capabilities.replace(";", "").split() +# out["host_allowed-operations"] = dct.get( +# "allowed-operations", "").split("; ") +# lsrv = dct.get("license-server", "") +# out["host_license-server"] = ols = {} +# if lsrv: +# for lspart in lsrv.split("; "): +# lsk, lsv = lspart.split(": ") +# if lsk == "port": +# ols[lsk] = safe_int(lsv) +# else: +# ols[lsk] = lsv +# sv = dct.get("software-version", "") +# out["host_software-version"] = osv = {} +# if sv: +# for svln in sv.split("; "): +# svk, svv = strip_kv(svln) +# osv[svk] = svv + cpuinf = dct.get("cpu_info", "") + out["host_cpu_info"] = ocp = {} + if cpuinf: + for cpln in cpuinf.split("; "): + cpk, cpv = strip_kv(cpln) + if cpk in ("cpu_count", "family", "model", "stepping"): + ocp[cpk] = safe_int(cpv) + else: + ocp[cpk] = cpv +# out["host_edition"] = dct.get("edition", "") +# out["host_external-auth-service-name"] = dct.get( +# "external-auth-service-name", "") + return out + +def query_gc(session, sr_uuid, vdi_uuid): + result = _run_command(["/opt/xensource/sm/cleanup.py", + "-q", "-u", sr_uuid]) + # Example output: "Currently running: True" + return result[19:].strip() == "True" + +def get_pci_device_details(session): + """Returns a string that is a list of pci devices with details. + + This string is obtained by running the command lspci. With -vmm option, + it dumps PCI device data in machine readable form. This verbose format + display a sequence of records separated by a blank line. We will also + use option "-n" to get vendor_id and device_id as numeric values and + the "-k" option to get the kernel driver used if any. + """ + return _run_command(["lspci", "-vmmnk"]) + + +def get_pci_type(session, pci_device): + """Returns the type of the PCI device (type-PCI, type-VF or type-PF). + + pci-device -- The address of the pci device + """ + # We need to add the domain if it is missing + if pci_device.count(':') == 1: + pci_device = "0000:" + pci_device + output = _run_command(["ls", "/sys/bus/pci/devices/" + pci_device + "/"]) + + if "physfn" in output: + return "type-VF" + if "virtfn" in output: + return "type-PF" + return "type-PCI" + + +if __name__ == "__main__": + # Support both serialized and non-serialized plugin approaches + _, methodname = xmlrpclib.loads(sys.argv[1]) + if methodname in ['query_gc', 'get_pci_device_details', 'get_pci_type']: + utils.register_plugin_calls(query_gc, + get_pci_device_details, + get_pci_type) + + XenAPIPlugin.dispatch( + {"host_data": host_data, + "set_host_enabled": set_host_enabled, + "host_shutdown": host_shutdown, + "host_reboot": host_reboot, + "host_start": host_start, + "host_join": host_join, + "get_config": get_config, + "set_config": set_config, + "iptables_config": iptables_config, + "network_config": network_config, + "host_uptime": host_uptime}) diff --git a/deployment_scripts/novaplugins-kilo.iso b/deployment_scripts/xenserverplugins-liberty.iso similarity index 91% rename from deployment_scripts/novaplugins-kilo.iso rename to deployment_scripts/xenserverplugins-liberty.iso index 1fc0c4ca8f91ffd330793dd6c8e0ebf9b1ab62f9..89d8caea3093134d2b425b49c797262bd0941372 100644 GIT binary patch delta 31732 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

pW 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 z zik~0|eO3A6m;E}h+`!)VIU)<($n7HCwxux7=`Dpwqwb8;4bTkQKT#{Uzl^&5dWtXf zx1Dzd(oaJt0PzT1dL#lrcxTyFLNCggdX|^x9@9p*d2C{s*ZTOWc19{Kl*TP@3X}?N z)UsuIKmU(dDrMMEK~jQcRz(TGC4=q&=gm;E8ovJ%{@m#8(uKH6{E(e&J;@Kef}BEJ z)&2)@o(7ENeZV$E6(BzkvG6+j^z`J9+iGQjKGNQ5+t&>t_p^{gk~|Z4Rs3cXDb?-m z0-d5Bf}0|`m!EPDgQVE`f_LK)9=Sn|s2V>Pi^~_kYhhGXOH*c)(~_3VHOl$o`z7B& zSh)uK-4Ks)QO)#DB2HuBdv5;Hb+HGroEzK0T@1Ni7XpBkb0PaCYTs>VEVgC2OqUW- zXR3X7vU17XQLyyakQXDIgsdBr>*q5D59OIX7QS zFpeayeEiT+2I{w+1fd2bBS|}2EqMQy@fnUur$9i!79v3pVR9E)H`n*AI*+_Q39sbJ zi)km#60Mlm9lk%0i|btEqX&EO8-wJHI{u=sIs4d(Lu7OyonT>NTWZPqF$A|uLA2Bu za*CO;k5;r9jW0tvjh%d^Pl)i~PvysB_6F_O`0y2VMy5eCtB@GQ-&zgzed;xbOdyqv zZj1oOg)S>mqbKC{QNc~*xG7!q)bJOA0PD@vM93(E`dV>6*VEt7SU+7tWnPP-2xV6e zexJ!3ePKnMiVQ=WcEhjF7nx=Ti3>)V@c;x)9EhuxD(UUfM4j&Gb=inU9ubZi_n<5Vr>F17-R!qkrlJGJza=0-G=&~o>_-o{wbRV}3o<|M2TL=Pmmi9-{EH zfnt=S(ppfA_Orci3$EQ9`t*3y>XJ?2gIeoTm)GaN*)~J8*XmK5ZE3D%)=R#ncDrJ0 zxIbaU1>d-BEmh+}jxn6NT;fw_iGIJPEol+P9NpP|CX+0uQl^;e2~x4xweLqDce__0H|WPw~H|2p4rp!Ba=>Q@F(| zaHD zKql9oh(jxO>;ob14n>4UhPuYfuTbcxhhEB&#~U4qdRVf7&7&Ab3z6^dJ)eUt)989^ zh+ePi#rrD}Jxj_1Mg|8M^*w%`b&5xL4iDprqyEG)PH>9grx!~h)7m*ly7^l(9FAf+ ze7P^3ca?PjYM3VTxlVp0e^QBIw(b@#8F6wlVNd+Qc8x*8&i)QZ_w(k)4uz1(LVg~X z2N$PT5Z+A)T&lEeV1iJ7MdBA6L;?LGZViDXnqk4M=@uc*W5I8Lp2y$#IYj%QBJ&tS zq?6s}sus8LJIpv6O6n)DvRk5-*`Q_SVY)s16BYi#wkqJFfZSAPaux7#cNUuR@k%5& z-$DAK#F#?EVol9)!T!Jjw{PTc%F%cO{&HYaQS{ZK>=Pvd>5L)>W6^?UcCyD`y=Q40 zsA`CLuOQ7U*a(LJDCl243ESrOZ<|GPc$riMS)NOImZ;?ssPk~GPK!4}$v22(uwgSV z>t2ryua;&6jGCc`uzlaclbzlhx-gRKMQy3-Xx|B+RV@vorx%0xM?v!B zlzfb}8-H8z0Tq7v{kP*1={mH*-AomFd;*3`HeStOWt2%|mV!kJgJCpvFuFJOWz(F7 z+=i;O5=rnb!1UfxPnV3Bl9$yHlbU=ovC<_DeL%0DB+WpCF1*?xlM+#?fXvE$re(a9 zkmcO=uMVBGl6iO8^dk!*G8Q;G;T@CPsTs^01P2%*lMyZK))0&t;gz2#59ILbi*x51 zpX|~tILH&76f2g>cF;DcN7(kQ6_DV9!`&wAksCcazck&A2%JF5N?S5nR&F75bf zP4%4(1#pQ7(*Qa`F`J2UFj;~&KT=BLR00sl^0MbVQF8sWP40X)9T%Is=R+>;Z^&N3 z_Dd0RTv?slY}%_yZ=jj6hrX{grxoWX00xKK<2Qc6f^m$g}h-wURDvyXW(4f;-2@UJC z(j;Dz-s|U;|BXUFr_qryv$Z-Pu#Ou8*>PW+Og4bB-t)4IZC5R9gCK z-vIl6(=S_=H?&mc$AhoY$ek3-;VH*v)(SuALgB#%d{Urhjpstes3ac}4%G4+a3O%UF671o){MTn_`R$fT;%LMSFbaK#W z?@7Fb23!F)3mSs@Gbk}H`@F-EYyNt#tJD&tnO?>%)4}K5l5fuSWBQHv7t^*~=m`K; zI`|*2-*0Pwft1^2mvRp>uYb}nV|4Se8JL?4Oyp8r3_-dhQ~kX&G&qtr!}gcd)nQM` zv1EsH3B>vWcpD_$+_iNjy{Q5%7BNv86W|S0We zcUlA(-|fD*yd44W{g?aq_B->|<{NV8ABQA}@1S*{-H<6fpcpv+o44)V97g(&ISc^z zhm0kF)!4c|TTU*I-ayy0$_%K_;p8+j18R17%uLyMc(~Y1I83>DS%DTHpa92+-PF{K zo5zghT?qpeA_1jHAcDXI{r2|uk1poDG^XkZ=x_RGV+V?3-pfCQ9l?17=eob1*)q4smBDy_x_R}{NXGEmHl9xk9r$m1TOSn&N8554AkSJ;6qvl z+VjCQ!1zAFG%&s|K>UN3K3D=6-zU8K;P;8&`SydQfbo67?}z%mzvf5%%Ll(Z$^gIy zKu6!d8t4KeZ~&k{=wJLHDFb`o^Y;Ptf$_cl`<}e>yQB=bKro<~>mN4xV2%&|aFl`Z zez4g`z2FA}j|AYK0fasZT!8U?0jeJiG=KfW?<;?w&lp(;(+`wM;Y<|u37w#tjvT${$CPCCSW%ZphT?Zznh-kOJTKAf#xy> z;I9)_%OCh$d2a=jT^Sj&8Uy`RCZ;T`K<|_>D^L{1Y0S&PX2``2lvsUiPWJXdN#Xm$ z|EKoR*~ys6$-(x$JMfwR_FsB2{!d%vKa&W(AFg+sBOJ4PB0-vC7HBjVi*1RnFNM;oNI{T+$zNpf(KIi5)=7_%Zk6(kO;FgEpBWLBpBQ3Wc@0HXfim(GzLTZnm{W>LvL2hallg5grZixlz!pwN0ESh z1?TzTud|xM0f$n=Mpzb{8dX!swey@wM%X<`Zi+e-7?y^V@DQETwN50MChe7Qv>4x7 zlP{)ptQ^oDF%*TEv*YOeU;)ZyZqIr6N+@=hZC~A|)HJ#_+&bif-1%qC*69Ok&(~vW zBod#F0(3Vkd=w$IOP*u3GkTgzM;c@qMmL3R{L`?k16>X|yt+B(q?#@Q7U^3sp}-ZV z2*yp%iQ-xw5@C2SN&W-{EIA|asXyT?Ipmj*mf5T9eIx%Gq!Vi>jsup>=1DCebNgL#cqIENeiq@i1Eg41Yf`9dF_;TQHYm#=6!i-AziI z33T1a%-u9lJC-fp?8?qFGVq@Z<5^cf#nnyV)Kj!%|6JZ&7#}-|3KM8W7xum+k1DY6 z@Zx{vX_u-2w59ih2zvc$u3TMzWsbaPg{{-qtm~BsP5OLtoOlCBRgnJSt^Mo5<{-lC z0fB0MkY*!XIcfEH3X@Zg&N3kl6EVoYK-VrVOuS+>{|hZPwx<{j=y~mW|4r|Z;HQyP zY+SXB)!~QW#}T+MYB)!~WrxWX1Q+G0<2J0CHsI<#ctZ2~7lpsAVb+;hq4`6?Rtgj!>SjwRu(R?>fxDDQ~Y*d5x!oq z70=&7l}bvQoG@#pb^oJ&4Php;hyM+czEDQoR0DTtZSUI#0Alw?a%J4ujpj7$;uVeV zY%jM)#zwVJOjmyaPA57fJ|(#_B2M`l4nn2~RE{?3hB!TaCzP;+u8_?TWwJl3;MLz_e1f&B-xIqVP_*CD4Gq~$*|>SQSva}4*?2hFje#acBOYTi5@$WL&KUpmgz<;(Rtic56dgvFf{s9?)PfS=07anA|7f}Y?J>5oG%|H^`Ibsu s3(5@rZ_olZh5e7gJM~*Fs2MmH6KCi_EhsR70TVbdL9`syLc+BCA8RRV8UO$Q delta 25827 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 z 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