Change in vdsm[ovirt-3.6]: hooks:checkips: add checkips hook

alukiano at redhat.com alukiano at redhat.com
Sun Apr 17 12:37:10 UTC 2016


Artyom Lukianov has uploaded a new change for review.

Change subject: hooks:checkips: add checkips hook
......................................................................

hooks:checkips: add checkips hook

Give possibilty to check connectivity between host and given addresses.
Update network stats with state of addresses.

Change-Id: I53cec37310f0f1844d6fe244419fd8c10e9b7ebb
Bug-Url: https://bugzilla.redhat.com/994170
Signed-off-by: Lukianov Artyom <alukiano at redhat.com>
---
M .gitignore
M configure.ac
M vdsm.spec.in
M vdsm_hooks/Makefile.am
A vdsm_hooks/checkips/Makefile.am
A vdsm_hooks/checkips/README
A vdsm_hooks/checkips/after_get_stats.py
A vdsm_hooks/checkips/checkips_utils.py
A vdsm_hooks/checkips/checkipsd
A vdsm_hooks/checkips/vdsm-checkips.service.in
10 files changed, 445 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/51/56251/1

diff --git a/.gitignore b/.gitignore
index 78af6fc..2e6c58d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -71,3 +71,4 @@
 vdsm_hooks/openstacknet/sudoers
 vdsm_hooks/ovs/sudoers
 vdsm_hooks/persist-vdsm-hooks
+vdsm_hooks/checkips/vdsm-checkips.service
diff --git a/configure.ac b/configure.ac
index 7c19fd9..ef3700f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -165,6 +165,9 @@
 AC_SUBST([SNLKGROUP], [sanlock])
 AC_SUBST([CDROMGROUP], [cdrom])
 
+# Systemd units default path
+AC_SUBST([SYSTEMD_UNIT_DIR], ['/usr/lib/systemd/system'])
+
 # VDSM default paths
 AC_SUBST([vdsmdir], ['${datarootdir}/vdsm'])
 AC_SUBST([vdsmconfdir], ['${sysconfdir}/vdsm'])
@@ -364,6 +367,7 @@
 	vdsm_hooks/Makefile
 	vdsm_hooks/allocate_net/Makefile
 	vdsm_hooks/checkimages/Makefile
+	vdsm_hooks/checkips/Makefile
 	vdsm_hooks/diskunmap/Makefile
 	vdsm_hooks/ethtool_options/Makefile
 	vdsm_hooks/extnet/Makefile
diff --git a/vdsm.spec.in b/vdsm.spec.in
index 19edaac..a2c77e9 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -379,6 +379,19 @@
 VDSM hook used to perform consistency check on a qcow2 format disk image
 using the QEMU disk image utility.
 
+%package hook-checkips
+Summary:        Check connectivity from the host to designated IPs
+Requires:       %{name} = %{version}-%{release}
+
+%description hook-checkips
+VDSM hook used to check connectivity from the host network to designated IPs
+
+%post hook-checkips
+%systemd_post vdsm-checkips.service
+
+%preun hook-checkips
+%systemd_preun vdsm-checkips.service
+
 %package hook-diskunmap
 Summary:        Activate UNMAP for disk/lun devices
 BuildArch:      noarch
@@ -1299,6 +1312,13 @@
 %defattr(-, root, root, -)
 %{_libexecdir}/%{vdsm_name}/hooks/before_vm_start/60_checkimages
 
+%files hook-checkips
+%defattr(-, root, root, -)
+%{_libexecdir}/%{vdsm_name}/hooks/after_get_stats/10_checkips
+%{_libexecdir}/%{vdsm_name}/hooks/checkipsd
+%{_libexecdir}/%{vdsm_name}/hooks/after_get_stats/checkips_utils.py*
+%{_unitdir}/vdsm-checkips.service
+
 %files hook-diskunmap
 %defattr(-, root, root, -)
 %{_libexecdir}/%{vdsm_name}/hooks/before_vm_start/50_diskunmap
diff --git a/vdsm_hooks/Makefile.am b/vdsm_hooks/Makefile.am
index 44856f1..6fca9bb 100644
--- a/vdsm_hooks/Makefile.am
+++ b/vdsm_hooks/Makefile.am
@@ -36,6 +36,7 @@
 SUBDIRS += \
 	allocate_net \
 	checkimages \
+	checkips \
 	diskunmap \
 	extnet \
 	fileinject \
diff --git a/vdsm_hooks/checkips/Makefile.am b/vdsm_hooks/checkips/Makefile.am
new file mode 100644
index 0000000..4b25acd
--- /dev/null
+++ b/vdsm_hooks/checkips/Makefile.am
@@ -0,0 +1,72 @@
+#
+# Copyright 2016 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
+#
+#
+
+utilsfile=checkips_utils.py
+servicefile=checkipsd
+unitfile=vdsm-checkips.service
+
+include $(top_srcdir)/build-aux/Makefile.subs
+
+nodist_noinst_DATA = \
+	vdsm-checkips.service \
+	config.log
+
+CLEANFILES = \
+	$(nodist_noinst_DATA)
+
+EXTRA_DIST = \
+	after_get_stats.py \
+	vdsm-checkips.service.in \
+	$(servicefile) \
+	$(utilsfile)
+
+
+install-data-local: install-data-utils install-data-service install-unit-service
+	$(MKDIR_P) $(DESTDIR)$(vdsmhooksdir)/after_get_stats
+	$(INSTALL_SCRIPT) $(srcdir)/after_get_stats.py \
+		$(DESTDIR)$(vdsmhooksdir)/after_get_stats/10_checkips
+
+install-data-utils:
+	$(MKDIR_P) $(DESTDIR)$(vdsmhooksdir)/after_get_stats
+	$(INSTALL_DATA) $(srcdir)/$(utilsfile) \
+		$(DESTDIR)$(vdsmhooksdir)/after_get_stats/$(utilsfile)
+
+install-data-service:
+	$(MKDIR_P) $(DESTDIR)$(vdsmhooksdir)
+	$(INSTALL_SCRIPT) $(srcdir)/$(servicefile) \
+		$(DESTDIR)$(vdsmhooksdir)/$(servicefile)
+
+install-unit-service:
+	$(MKDIR_P) $(DESTDIR)$(SYSTEMD_UNIT_DIR)
+	$(INSTALL_SCRIPT) -Dm 0644 $(srcdir)/$(unitfile) \
+		$(DESTDIR)$(SYSTEMD_UNIT_DIR)/$(unitfile)
+
+uninstall-local: uninstall-data-utils uninstall-data-service uninstall-unit-service
+	$(RM) $(DESTDIR)$(vdsmhooksdir)/after_get_stats/10_checkips
+
+uninstall-data-utils:
+	$(RM) $(DESTDIR)$(vdsmhooksdir)/after_get_stats/$(utilsfile)
+
+uninstall-data-service:
+	$(RM) $(DESTDIR)$(vdsmhooksdir)/$(servicefile)
+
+uninstall-unit-service:
+	$(RM) $(DESTDIR)$(SYSTEMD_UNIT_DIR)/$(unitfile)
diff --git a/vdsm_hooks/checkips/README b/vdsm_hooks/checkips/README
new file mode 100644
index 0000000..f9e31cd
--- /dev/null
+++ b/vdsm_hooks/checkips/README
@@ -0,0 +1,41 @@
+checkips vdsm hook
+=====================
+This hook check connectivity from host to given addresses
+
+This hook is useful in cases where you need to check connectivity from host to
+specific VLAN, you just need to add custom property checkipv4 or checkipv6,
+depend on what type of IP do you want to check, to network.
+
+Syntax:
+    checkipv4=IPV4 or FQDN
+    checkipv6=IPV6 or FQDN
+
+Where:
+    IPV4 is ip address in IPV4 format
+    IPV6 is ip address in IPV6 format
+
+Example:
+    checkipv4=127.0.0.1
+    checkipv6=::1
+
+Installation:
+    - Use the engine-config to append the proper custom property:
+        $ sudo engine-config -s UserDefinedNetworkCustomProperties='checkipv4=^[0-9\.a-fA-F]+$;checkipv6=^[0-9\.a-fA-F\:]+$'
+    - Verify that the checkipv4 and checkipv6 custom properties was properly added:
+        $ sudo engine-config -g UserDefinedNetworkCustomProperties
+    - Restart ovirt-engine service to apply changes
+        $ sudo service ovirt-engine restart
+    - The hook should be installed on all hosts,
+      and its accompanying service should be enabled and started
+        systemctl vdsm-checkips enable
+        systemctl vdsm-checkips start
+
+Usage:
+    In the host setup network configuration window, choose edit assigned
+    logical network, select custom property checkipv4 or checkipv6 and
+    enter VLAN IP or FQDN that you want to check for
+    connectivity(examples 8.8.8.8,2001:4860:4860::8888). So if you defined
+    checkipv4 and checkipv6 custom properties and if the host fails to ping
+    both IP's, it will drop the network's state to down.
+    If the network is defined as "required",
+    the host would become non-operational.
diff --git a/vdsm_hooks/checkips/after_get_stats.py b/vdsm_hooks/checkips/after_get_stats.py
new file mode 100644
index 0000000..8d829d6
--- /dev/null
+++ b/vdsm_hooks/checkips/after_get_stats.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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 os
+import six
+import sys
+import tempfile
+import time
+import traceback
+
+import checkips_utils
+import hooking
+from vdsm import constants
+import vdsm.netconfpersistence as persist_net
+
+HELP_ARG = '-h'
+TEST_ARG = '-t'
+HELP_TEXT = """usage: %(prog)s [%(help)s] [%(test)s]
+
+Check connectivity from host to specific addresses via ping
+
+optional arguments:
+  %(help)s  show this help message and exit
+  %(test)s  run a dry test for the hook
+""" % {
+    'help': HELP_ARG,
+    'test': TEST_ARG,
+    'prog': sys.argv[0]
+}
+
+CONNECTIVITY_TIMEOUT = 60
+
+
+def _is_network_accessible(net, stats_dir):
+    file_path = os.path.join(stats_dir, net)
+    if os.path.exists(file_path):
+        return (
+            time.time() - os.stat(file_path).st_mtime <=
+            CONNECTIVITY_TIMEOUT
+        )
+    return False
+
+
+def update_networks_state(stats_json, networks, stats_dir):
+    for net, net_attrs in six.iteritems(networks):
+        ping_addresses = checkips_utils.get_ping_addresses(net_attrs)
+        if ping_addresses and not _is_network_accessible(net, stats_dir):
+            net_if = None
+            if 'nic' in net_attrs:
+                net_if = net_attrs['nic']
+            elif 'bonding' in net_attrs:
+                net_if = net_attrs['bonding']
+            if net_if:
+                stats_json['network'][net_if]['state'] = 'down'
+
+
+def test():
+    temp_dir = tempfile.mkdtemp()
+    checkips_utils.touch('check_ipv4', temp_dir)
+    checkips_utils.touch('check_ipv6', temp_dir)
+    try:
+        networks = {
+            'check_ipv4': {
+                'nic': 'eth0_ipv4',
+                'custom': {
+                    'checkipv4': '127.0.0.1'
+                }
+            },
+            'check_ipv6': {
+                'bonding': 'bond_ipv6',
+                'custom': {
+                    'checkipv6': '::1'
+                }
+            },
+            'check_fqdn': {
+                'nic': 'eth_fqdn',
+                'custom': {
+                    'checkipv4': 'test.test'
+                }
+            }
+        }
+        network_stats = {
+            'network': {
+                'eth0_ipv4': {
+                    'state': 'up'
+                },
+                'bond_ipv6': {
+                    'state': 'up'
+                },
+                'eth_fqdn': {
+                    'state': 'up'
+                }
+            }
+        }
+        update_networks_state(network_stats, networks, temp_dir)
+        expected_states = {
+            'eth0_ipv4': 'up',
+            'bond_ipv6': 'up',
+            'eth_fqdn': 'down'
+        }
+        for interface, state in six.iteritems(expected_states):
+            test_msg = 'pass'
+            if network_stats['network'][interface]['state'] != state:
+                test_msg = 'fail'
+            print(
+                'test %s: interface %s has state %s' %
+                (test_msg, interface, state)
+            )
+    finally:
+        os.unlink(os.path.join(temp_dir, 'check_ipv4'))
+        os.unlink(os.path.join(temp_dir, 'check_ipv6'))
+        os.rmdir(temp_dir)
+
+
+def main():
+    stats_json = hooking.read_json()
+    networks = persist_net.PersistentConfig().networks
+    update_networks_state(stats_json, networks, constants.P_VDSM_RUN)
+    hooking.write_json(stats_json)
+
+
+if __name__ == '__main__':
+    if HELP_ARG in sys.argv:
+        hooking.exit_hook(HELP_TEXT)
+
+    try:
+        if TEST_ARG in sys.argv:
+            test()
+        else:
+            main()
+    except:
+        hooking.exit_hook(
+            'checkips hook: [unexpected error]: %s\n' %
+            traceback.format_exc()
+        )
diff --git a/vdsm_hooks/checkips/checkips_utils.py b/vdsm_hooks/checkips/checkips_utils.py
new file mode 100644
index 0000000..44ee5af
--- /dev/null
+++ b/vdsm_hooks/checkips/checkips_utils.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+#
+# Copyright 2008-2016 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 os
+
+CHECKIPV4 = 'checkipv4'
+CHECKIPV6 = 'checkipv6'
+
+
+def touch(net, file_dir):
+    file_path = os.path.join(file_dir, net)
+    with open(file_path, 'a'):
+        os.utime(file_path, None)
+
+
+def get_ping_addresses(net_attrs):
+    ping_addresses = []
+    if 'custom' in net_attrs:
+        for address_type in (CHECKIPV4, CHECKIPV6):
+            if address_type in net_attrs['custom']:
+                ping_addresses.append(
+                    (
+                        address_type, net_attrs['custom'][address_type]
+                    )
+                )
+    return ping_addresses
diff --git a/vdsm_hooks/checkips/checkipsd b/vdsm_hooks/checkips/checkipsd
new file mode 100644
index 0000000..8e081b1
--- /dev/null
+++ b/vdsm_hooks/checkips/checkipsd
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+#
+# Copyright 2008-2016 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 os
+import six
+import subprocess
+import sys
+import syslog
+import time
+import threading
+
+from vdsm import constants
+import vdsm.netconfpersistence as persist_net
+
+sys.path.append(os.path.join(constants.P_VDSM_HOOKS, 'after_get_stats'))
+import checkips_utils
+
+PING_TIMEOUT = 15
+PING_SAMPLE_TIME = 20
+CHECKIPV4 = 'checkipv4'
+CHECKIPV6 = 'checkipv6'
+VDSM_CHECKIPS = 'vdsm-checkips'
+
+
+def _ping_address(address, address_type, network):
+    ping_cmd = 'ping' if address_type == CHECKIPV4 else 'ping6'
+    command = [
+        ping_cmd,
+        '-c', '1',
+        '-w', str(PING_TIMEOUT),
+        address
+    ]
+    p = subprocess.Popen(
+        command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    )
+    out, _ = p.communicate()
+    if not p.returncode:
+        checkips_utils.touch(network, constants.P_VDSM_RUN)
+    # Not sure about this, flood the journalctl log
+    elif out:
+        syslog.syslog(
+            "%s: failed to ping address %s: %s" %
+            (VDSM_CHECKIPS, address, out)
+        )
+
+
+def _ping_addresses():
+    threads = []
+    networks = persist_net.PersistentConfig().networks
+    for net, net_attrs in six.iteritems(networks):
+        ping_addresses = checkips_utils.get_ping_addresses(net_attrs)
+        if ping_addresses:
+            for address_type, address in ping_addresses:
+                ping_thread = threading.Thread(
+                    target=_ping_address,
+                    args=(address, address_type, net)
+                )
+                ping_thread.start()
+                threads.append(ping_thread)
+
+    for ping_thread in threads:
+        ping_thread.join()
+
+
+def main():
+    try:
+        while True:
+            time_before_ping = time.time()
+            _ping_addresses()
+            time.sleep(
+                PING_SAMPLE_TIME - (time.time() - time_before_ping)
+            )
+    except Exception as e:
+        syslog.syslog("%s: service failed to start: %s" % (VDSM_CHECKIPS, e))
+        return 1
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/vdsm_hooks/checkips/vdsm-checkips.service.in b/vdsm_hooks/checkips/vdsm-checkips.service.in
new file mode 100644
index 0000000..568e8a3
--- /dev/null
+++ b/vdsm_hooks/checkips/vdsm-checkips.service.in
@@ -0,0 +1,15 @@
+[Unit]
+Description=Service that ping given ips each 60 seconds
+
+[Service]
+Type=simple
+LimitCORE=infinity
+ExecStart=@HOOKSDIR@/checkipsd
+Restart=on-failure
+RestartSec=10
+User=@VDSMUSER@
+Group=@VDSMGROUP@
+TimeoutStopSec=@SERVICE_STOP_TIMEOUT@
+
+[Install]
+WantedBy=multi-user.target


-- 
To view, visit https://gerrit.ovirt.org/56251
To unsubscribe, visit https://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I53cec37310f0f1844d6fe244419fd8c10e9b7ebb
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: ovirt-3.6
Gerrit-Owner: Artyom Lukianov <alukiano at redhat.com>


More information about the vdsm-patches mailing list