Ondřej Svoboda has uploaded a new change for review. Change subject: netinfo, tool: Add a vdsm-tool to determine bonding defaults offline ......................................................................
netinfo, tool: Add a vdsm-tool to determine bonding defaults offline dump-bonding-defaults reads bonding option defaults (per mode) and saves them to /var/lib/vdsm/bonding-defaults.json. VDSM loads the file once at runtime and refers to this static information of defaults when reporting bonding options of bonds in the system. As the bonding defaults are bound to the used kernel it is the responsibility of the packager (or the sysadmin in customized setups) to regenerate the file by running (during the build, or installation, respectively): vdsm-tool dump-bonding-defaults It is currently not advised to run the tool on startup because NetworkManager may ifup the reference bond, making the tool unable to determine all options. Change-Id: I89b907ba80f23f417d5e481db9350247445ab772 Bug-Url: https://bugzilla.redhat.com/show_bug.cgi?id=1142701 Signed-off-by: Ondřej Svoboda <osvob...@redhat.com> --- M debian/vdsm-python.install M debian/vdsm.install M init/vdsmd_init_common.sh.in M lib/vdsm/netinfo.py M lib/vdsm/tool/Makefile.am A lib/vdsm/tool/dump_bonding_defaults.py M tests/netinfoTests.py M vdsm.spec.in M vdsm/Makefile.am A vdsm/bonding-defaults.json M vdsm/sudoers.vdsm.in 11 files changed, 543 insertions(+), 48 deletions(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/28/33528/1 diff --git a/debian/vdsm-python.install b/debian/vdsm-python.install index 0294be8..42f08c2 100644 --- a/debian/vdsm-python.install +++ b/debian/vdsm-python.install @@ -17,6 +17,7 @@ ./usr/lib/python2.7/dist-packages/vdsm/sslutils.py ./usr/lib/python2.7/dist-packages/vdsm/tool/__init__.py ./usr/lib/python2.7/dist-packages/vdsm/tool/dummybr.py +./usr/lib/python2.7/dist-packages/vdsm/tool/dump_bonding_defaults.py ./usr/lib/python2.7/dist-packages/vdsm/tool/configurator.py ./usr/lib/python2.7/dist-packages/vdsm/tool/load_needed_modules.py ./usr/lib/python2.7/dist-packages/vdsm/tool/nwfilter.py diff --git a/debian/vdsm.install b/debian/vdsm.install index c97b8dd..4a0b976 100644 --- a/debian/vdsm.install +++ b/debian/vdsm.install @@ -23,6 +23,7 @@ ./lib/udev/rules.d/61-vdsm-lvm.rules ./usr/lib/python2.7/dist-packages/sos/plugins/vdsm.py ./usr/lib/python2.7/dist-packages/vdsmapi.py +./var/lib/vdsm/bonding-defaults.json ./usr/libexec/vdsm/curl-img-wrap ./usr/libexec/vdsm/ovirt_functions.sh ./usr/libexec/vdsm/persist-vdsm-hooks diff --git a/init/vdsmd_init_common.sh.in b/init/vdsmd_init_common.sh.in index 298b658..49e9f19 100644 --- a/init/vdsmd_init_common.sh.in +++ b/init/vdsmd_init_common.sh.in @@ -100,6 +100,11 @@ } +task_dump_bonding_defaults(){ + "${VDSM_TOOL}" dump-bonding-defaults +} + + task_tune_system(){ "@SYSCTL_PATH@" -q -p "/etc/sysctl.d/vdsm.conf" } @@ -228,6 +233,10 @@ case "$1" in --pre-start) + # If dump_bonding_defaults is desired (for the uneliminable possibility + # of added bonding options or tweaked kernel defaults) it has to be run + # after load_needed_modules (modprobe bonding). + # NetworkManager must not crash on nmcli conn delete. run_tasks " \ mkdirs \ configure_coredump \ diff --git a/lib/vdsm/netinfo.py b/lib/vdsm/netinfo.py index 5dc2cd6..f98dcea 100644 --- a/lib/vdsm/netinfo.py +++ b/lib/vdsm/netinfo.py @@ -24,12 +24,11 @@ from datetime import datetime from functools import partial from itertools import chain +import json import logging import os -import random import shlex import socket -import string import struct from xml.dom import minidom @@ -47,7 +46,7 @@ from . import libvirtconnection from .netconfpersistence import RunningConfig from .netlink import iter_addrs, iter_links -from .utils import execCmd, memoized, CommandPath +from .utils import memoized NET_CONF_DIR = '/etc/sysconfig/network-scripts/' @@ -69,6 +68,7 @@ BONDING_MASTERS = '/sys/class/net/bonding_masters' BONDING_SLAVES = '/sys/class/net/%s/bonding/slaves' BONDING_OPT = '/sys/class/net/%s/bonding/%s' +BONDING_DEFAULTS = constants.P_VDSM_LIB + 'bonding-defaults.json' BRIDGING_OPT = '/sys/class/net/%s/bridge/%s' _BONDING_FAILOVER_MODES = frozenset(('1', '3')) _BONDING_LOADBALANCE_MODES = frozenset(('0', '2', '4', '5', '6')) @@ -78,7 +78,6 @@ )) _IFCFG_ZERO_SUFFIXED = frozenset( ('IPADDR0', 'GATEWAY0', 'PREFIX0', 'NETMASK0')) -_TEE_BINARY = CommandPath('tee', constants.EXT_TEE) LIBVIRT_NET_PREFIX = 'vdsm-' DEFAULT_MTU = '1500' @@ -187,7 +186,7 @@ return opts -def _realBondOpts(bond): +def realBondOpts(bond): """ Return a dictionary in the same format as bondOpts(). Exclude entries that are not bonding options, e.g. 'ad_num_ports' or 'slaves'. @@ -522,47 +521,14 @@ return paddr -def _randomIfaceName(): - MAX_LENGTH = 15 - CHARS = string.ascii_lowercase + string.ascii_uppercase + string.digits - - return ''.join(random.choice(CHARS) for _ in range(MAX_LENGTH)) - - @memoized def _getAllDefaultBondingOptions(): """ Return default options per mode, in a dictionary of dictionaries. All keys - are strings. + are numeric modes stored as strings for coherence with 'mode' option value. """ - teeCmd = _TEE_BINARY.cmd - MAX_MODE = 6 - - bondName = _randomIfaceName() - rc, _, err = execCmd([teeCmd, BONDING_MASTERS], - data='+' + bondName, sudo=True) - if rc: - raise RuntimeError('Creating a reference bond failed', '\n'.join(err)) - - opts = {} - try: - defaultMode = bondOpts(bondName, keys=['mode'])['mode'] - - # read default values for all modes - for mode in range(0, MAX_MODE + 1): - mode = str(mode) - rc, _, err = execCmd([teeCmd, BONDING_OPT % (bondName, 'mode')], - data=mode, sudo=True) - - # only read non-empty options - opts[mode] = dict(((opt, val) for (opt, val) in - _realBondOpts(bondName).iteritems() if val)) - opts[mode]['mode'] = defaultMode - - finally: - execCmd([teeCmd, BONDING_MASTERS], data='-' + bondName, sudo=True) - - return opts + with open(BONDING_DEFAULTS) as defaults: + return json.loads(defaults.read()) @memoized @@ -584,7 +550,7 @@ Return non-empty options differing from defaults, excluding not actual or not applicable options, e.g. 'ad_num_ports' or 'slaves'. """ - opts = _realBondOpts(bond) + opts = realBondOpts(bond) mode = opts['mode'][-1] if 'mode' in opts else None defaults = _getDefaultBondingOptions(mode) diff --git a/lib/vdsm/tool/Makefile.am b/lib/vdsm/tool/Makefile.am index 7be78fc..a181617 100644 --- a/lib/vdsm/tool/Makefile.am +++ b/lib/vdsm/tool/Makefile.am @@ -40,6 +40,7 @@ dist_vdsmtool_PYTHON = \ __init__.py \ dummybr.py \ + dump_bonding_defaults.py \ nwfilter.py \ configurator.py \ passwd.py \ diff --git a/lib/vdsm/tool/dump_bonding_defaults.py b/lib/vdsm/tool/dump_bonding_defaults.py new file mode 100644 index 0000000..09bb82d --- /dev/null +++ b/lib/vdsm/tool/dump_bonding_defaults.py @@ -0,0 +1,83 @@ +# Copyright 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Refer to the README and COPYING files for full details of the license +# + + +import json +import random +import string + +from ..netinfo import (BONDING_MASTERS, BONDING_OPT, BONDING_DEFAULTS, + bondOpts, realBondOpts) +from . import expose, ExtraArgsError + + +def _random_iface_name(): + MAX_LENGTH = 15 + CHARS = string.ascii_lowercase + string.ascii_uppercase + string.digits + + return ''.join(random.choice(CHARS) for _ in range(MAX_LENGTH)) + + +def _get_default_bonding_options(): + """ + Return default options per mode, in a dictionary of dictionaries. All keys + are strings. + """ + MAX_MODE = 6 + bond_name = _random_iface_name() + opts = {} + + with open(BONDING_MASTERS, 'w') as bonds: + bonds.write('+' + bond_name) + + try: + default_mode = bondOpts(bond_name, keys=['mode'])['mode'] + + # read default values for all modes + for mode in range(0, MAX_MODE + 1): + mode = str(mode) + with open(BONDING_OPT % (bond_name, 'mode'), 'w') as opt: + opt.write(mode) + + # only read non-empty options + opts[mode] = dict(((opt, val) for (opt, val) in + realBondOpts(bond_name).iteritems() if val)) + opts[mode]['mode'] = default_mode + + finally: + with open(BONDING_MASTERS, 'w') as bonds: + bonds.write('-' + bond_name) + + return opts + + +@expose('dump-bonding-defaults') +def main(*args): + """dump-bonding-defaults + + Read bonding option defaults (per mode) and dump them to BONDING_DEFAULTS + in JSON format. + """ + + if len(args) > 1: + raise ExtraArgsError() + + with open(BONDING_DEFAULTS, 'w') as defaults: + json.dump(_get_default_bonding_options(), defaults, sort_keys=True, + indent=4, separators=(',', ': ')) diff --git a/tests/netinfoTests.py b/tests/netinfoTests.py index a3a1e64..0b20b86 100644 --- a/tests/netinfoTests.py +++ b/tests/netinfoTests.py @@ -30,7 +30,8 @@ from vdsm import netconfpersistence from vdsm import netinfo from vdsm.netinfo import (getBootProtocol, getDhclientIfaces, BONDING_MASTERS, - BONDING_OPT, _randomIfaceName, _getBondingOptions) + BONDING_OPT, _getBondingOptions) +from vdsm.tool.dump_bonding_defaults import _random_iface_name from functional import dummy, veth from ipwrapperTests import _fakeTypeDetection @@ -354,7 +355,7 @@ @ValidateRunningAsRoot def testGetBondingOptions(self): INTERVAL = '12345' - bondName = _randomIfaceName() + bondName = _random_iface_name() with open(BONDING_MASTERS, 'w') as bonds: bonds.write('+' + bondName) diff --git a/vdsm.spec.in b/vdsm.spec.in index ca2e86f..21ac926 100644 --- a/vdsm.spec.in +++ b/vdsm.spec.in @@ -1159,6 +1159,7 @@ %dir %{_sysconfdir}/pki/%{vdsm_name}/certs %dir %{_sysconfdir}/pki/%{vdsm_name}/libvirt-spice %config(noreplace) %{_sysconfdir}/pki/%{vdsm_name}/keys/libvirt_password +%{_localstatedir}/lib/%{vdsm_name}/bonding-defaults.json %dir %{_localstatedir}/lib/%{vdsm_name} %dir %{_localstatedir}/lib/%{vdsm_name}/netconfback %dir %{_localstatedir}/lib/%{vdsm_name}/persistence @@ -1200,6 +1201,7 @@ %{python_sitearch}/%{vdsm_name}/tool/load_needed_modules.py* %endif %{python_sitearch}/%{vdsm_name}/tool/dummybr.py* +%{python_sitearch}/%{vdsm_name}/tool/dump_bonding_defaults.py* %{python_sitearch}/%{vdsm_name}/tool/nwfilter.py* %{python_sitearch}/%{vdsm_name}/tool/configurator.py* %{python_sitearch}/%{vdsm_name}/tool/configurators/__init__* diff --git a/vdsm/Makefile.am b/vdsm/Makefile.am index 96deef2..55edbf5 100644 --- a/vdsm/Makefile.am +++ b/vdsm/Makefile.am @@ -94,6 +94,7 @@ $(nodist_man8_MANS) EXTRA_DIST = \ + bonding-defaults.json \ dsaversion.py.in \ dumpStorageTable.py.in \ libvirt_password \ @@ -133,6 +134,7 @@ chmod 775 $(DESTDIR)$(localstatedir)/lib/libvirt/qemu/channels install-data-local: \ + install-data-bonding-defaults \ install-data-dhclient-hooks \ install-data-libvirtpass \ install-data-logger \ @@ -159,6 +161,7 @@ $(MKDIR_P) $(DESTDIR)$(localstatedir)/lib/libvirt/qemu/channels uninstall-local: \ + uninstall-data-bonding-defaults \ uninstall-data-dhclient-hooks \ uninstall-data-libvirtpass \ uninstall-data-logger \ @@ -186,6 +189,15 @@ uninstall-data-libvirtpass: $(RM) $(DESTDIR)$(vdsmtsdir)/keys/libvirt_password +# lib/vdsm does not exist yet +install-data-bonding-defaults: + $(MKDIR_P) $(DESTDIR)$(vdsmlibdir) + $(INSTALL_DATA) -m 600 $(srcdir)/bonding-defaults.json \ + $(DESTDIR)$(vdsmlibdir)/bonding-defaults.json + +uninstall-data-bonding-defaults: + $(RM) $(DESTDIR)$(vdsmlibdir)/bonding-defaults.json + install-data-rwtab: $(MKDIR_P) $(DESTDIR)$(sysconfdir)/rwtab.d $(INSTALL_DATA) vdsm.rwtab \ diff --git a/vdsm/bonding-defaults.json b/vdsm/bonding-defaults.json new file mode 100644 index 0000000..52f2d6c --- /dev/null +++ b/vdsm/bonding-defaults.json @@ -0,0 +1,422 @@ +{ + "0": { + "ad_select": [ + "stable", + "0" + ], + "all_slaves_active": [ + "0" + ], + "arp_interval": [ + "0" + ], + "arp_validate": [ + "none", + "0" + ], + "downdelay": [ + "0" + ], + "fail_over_mac": [ + "none", + "0" + ], + "lacp_rate": [ + "slow", + "0" + ], + "miimon": [ + "0" + ], + "min_links": [ + "0" + ], + "mode": [ + "balance-rr", + "0" + ], + "num_grat_arp": [ + "1" + ], + "num_unsol_na": [ + "1" + ], + "primary_reselect": [ + "always", + "0" + ], + "resend_igmp": [ + "1" + ], + "updelay": [ + "0" + ], + "use_carrier": [ + "1" + ], + "xmit_hash_policy": [ + "layer2", + "0" + ] + }, + "1": { + "ad_select": [ + "stable", + "0" + ], + "all_slaves_active": [ + "0" + ], + "arp_interval": [ + "0" + ], + "arp_validate": [ + "none", + "0" + ], + "downdelay": [ + "0" + ], + "fail_over_mac": [ + "none", + "0" + ], + "lacp_rate": [ + "slow", + "0" + ], + "miimon": [ + "0" + ], + "min_links": [ + "0" + ], + "mode": [ + "balance-rr", + "0" + ], + "num_grat_arp": [ + "1" + ], + "num_unsol_na": [ + "1" + ], + "primary_reselect": [ + "always", + "0" + ], + "resend_igmp": [ + "1" + ], + "updelay": [ + "0" + ], + "use_carrier": [ + "1" + ], + "xmit_hash_policy": [ + "layer2", + "0" + ] + }, + "2": { + "ad_select": [ + "stable", + "0" + ], + "all_slaves_active": [ + "0" + ], + "arp_interval": [ + "0" + ], + "arp_validate": [ + "none", + "0" + ], + "downdelay": [ + "0" + ], + "fail_over_mac": [ + "none", + "0" + ], + "lacp_rate": [ + "slow", + "0" + ], + "miimon": [ + "0" + ], + "min_links": [ + "0" + ], + "mode": [ + "balance-rr", + "0" + ], + "num_grat_arp": [ + "1" + ], + "num_unsol_na": [ + "1" + ], + "primary_reselect": [ + "always", + "0" + ], + "resend_igmp": [ + "1" + ], + "updelay": [ + "0" + ], + "use_carrier": [ + "1" + ], + "xmit_hash_policy": [ + "layer2", + "0" + ] + }, + "3": { + "ad_select": [ + "stable", + "0" + ], + "all_slaves_active": [ + "0" + ], + "arp_interval": [ + "0" + ], + "arp_validate": [ + "none", + "0" + ], + "downdelay": [ + "0" + ], + "fail_over_mac": [ + "none", + "0" + ], + "lacp_rate": [ + "slow", + "0" + ], + "miimon": [ + "0" + ], + "min_links": [ + "0" + ], + "mode": [ + "balance-rr", + "0" + ], + "num_grat_arp": [ + "1" + ], + "num_unsol_na": [ + "1" + ], + "primary_reselect": [ + "always", + "0" + ], + "resend_igmp": [ + "1" + ], + "updelay": [ + "0" + ], + "use_carrier": [ + "1" + ], + "xmit_hash_policy": [ + "layer2", + "0" + ] + }, + "4": { + "ad_select": [ + "stable", + "0" + ], + "all_slaves_active": [ + "0" + ], + "arp_interval": [ + "0" + ], + "arp_validate": [ + "none", + "0" + ], + "downdelay": [ + "0" + ], + "fail_over_mac": [ + "none", + "0" + ], + "lacp_rate": [ + "slow", + "0" + ], + "miimon": [ + "0" + ], + "min_links": [ + "0" + ], + "mode": [ + "balance-rr", + "0" + ], + "num_grat_arp": [ + "1" + ], + "num_unsol_na": [ + "1" + ], + "primary_reselect": [ + "always", + "0" + ], + "resend_igmp": [ + "1" + ], + "updelay": [ + "0" + ], + "use_carrier": [ + "1" + ], + "xmit_hash_policy": [ + "layer2", + "0" + ] + }, + "5": { + "ad_select": [ + "stable", + "0" + ], + "all_slaves_active": [ + "0" + ], + "arp_interval": [ + "0" + ], + "arp_validate": [ + "none", + "0" + ], + "downdelay": [ + "0" + ], + "fail_over_mac": [ + "none", + "0" + ], + "lacp_rate": [ + "slow", + "0" + ], + "miimon": [ + "0" + ], + "min_links": [ + "0" + ], + "mode": [ + "balance-rr", + "0" + ], + "num_grat_arp": [ + "1" + ], + "num_unsol_na": [ + "1" + ], + "primary_reselect": [ + "always", + "0" + ], + "resend_igmp": [ + "1" + ], + "updelay": [ + "0" + ], + "use_carrier": [ + "1" + ], + "xmit_hash_policy": [ + "layer2", + "0" + ] + }, + "6": { + "ad_select": [ + "stable", + "0" + ], + "all_slaves_active": [ + "0" + ], + "arp_interval": [ + "0" + ], + "arp_validate": [ + "none", + "0" + ], + "downdelay": [ + "0" + ], + "fail_over_mac": [ + "none", + "0" + ], + "lacp_rate": [ + "slow", + "0" + ], + "miimon": [ + "0" + ], + "min_links": [ + "0" + ], + "mode": [ + "balance-rr", + "0" + ], + "num_grat_arp": [ + "1" + ], + "num_unsol_na": [ + "1" + ], + "primary_reselect": [ + "always", + "0" + ], + "resend_igmp": [ + "1" + ], + "updelay": [ + "0" + ], + "use_carrier": [ + "1" + ], + "xmit_hash_policy": [ + "layer2", + "0" + ] + } +} \ No newline at end of file diff --git a/vdsm/sudoers.vdsm.in b/vdsm/sudoers.vdsm.in index dbc035c..584807d 100644 --- a/vdsm/sudoers.vdsm.in +++ b/vdsm/sudoers.vdsm.in @@ -32,10 +32,7 @@ @SETSID_PATH@ @IONICE_PATH@ -c ? -n ? @SU_PATH@ vdsm -s /bin/sh -c /usr/libexec/vdsm/spmprotect.sh*, \ @SERVICE_PATH@ vdsmd *, \ @REBOOT_PATH@ -f -Cmnd_Alias VDSM_NETWORK = \ - @TEE_PATH@ /sys/class/net/bonding_masters, \ - @TEE_PATH@ /sys/class/net/*/bonding/mode -vdsm ALL=(ALL) NOPASSWD: VDSM_LIFECYCLE, VDSM_STORAGE, VDSM_NETWORK +vdsm ALL=(ALL) NOPASSWD: VDSM_LIFECYCLE, VDSM_STORAGE Defaults:vdsm !requiretty Defaults:vdsm !syslog -- To view, visit http://gerrit.ovirt.org/33528 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I89b907ba80f23f417d5e481db9350247445ab772 Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: ovirt-3.5 Gerrit-Owner: Ondřej Svoboda <osvob...@redhat.com> _______________________________________________ vdsm-patches mailing list vdsm-patches@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches