From 41aac6942e89fa24b938ec188a0933e52acff623 Mon Sep 17 00:00:00 2001 From: An-Cheng Huang Date: Mon, 17 Dec 2007 14:46:33 -0800 Subject: migrate clustering from fairfield to glendale --- .gitignore | 20 ++ AUTHORS | 1 + COPYING | 26 ++ Makefile.am | 17 ++ NEWS | 1 + README | 1 + configure.ac | 30 ++ debian/README | 6 + debian/autogen.sh | 37 +++ debian/changelog | 5 + debian/compat | 1 + debian/control | 14 + debian/copyright | 34 +++ debian/docs | 2 + debian/linda | 1 + debian/lintian | 2 + debian/rules | 102 +++++++ debian/vyatta-cluster.postinst.in | 4 + scripts/VyattaClusterConfig.pm | 312 +++++++++++++++++++++ scripts/vyatta-show-cluster.pl | 267 ++++++++++++++++++ scripts/vyatta-update-cluster.pl | 78 ++++++ templates-cfg/cluster/dead-interval/node.def | 3 + templates-cfg/cluster/group/node.def | 3 + .../cluster/group/node.tag/auto-failback/node.def | 3 + .../cluster/group/node.tag/monitor/node.def | 3 + .../cluster/group/node.tag/primary/node.def | 2 + .../cluster/group/node.tag/secondary/node.def | 3 + .../cluster/group/node.tag/service/node.def | 3 + templates-cfg/cluster/interface/node.def | 3 + templates-cfg/cluster/keepalive-interval/node.def | 3 + templates-cfg/cluster/node.def | 2 + templates-cfg/cluster/pre-shared-secret/node.def | 2 + templates-op/show/cluster/node.def | 1 + templates-op/show/cluster/status/node.def | 2 + 34 files changed, 994 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 configure.ac create mode 100644 debian/README create mode 100755 debian/autogen.sh create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/linda create mode 100644 debian/lintian create mode 100755 debian/rules create mode 100644 debian/vyatta-cluster.postinst.in create mode 100644 scripts/VyattaClusterConfig.pm create mode 100755 scripts/vyatta-show-cluster.pl create mode 100755 scripts/vyatta-update-cluster.pl create mode 100644 templates-cfg/cluster/dead-interval/node.def create mode 100644 templates-cfg/cluster/group/node.def create mode 100644 templates-cfg/cluster/group/node.tag/auto-failback/node.def create mode 100644 templates-cfg/cluster/group/node.tag/monitor/node.def create mode 100644 templates-cfg/cluster/group/node.tag/primary/node.def create mode 100644 templates-cfg/cluster/group/node.tag/secondary/node.def create mode 100644 templates-cfg/cluster/group/node.tag/service/node.def create mode 100644 templates-cfg/cluster/interface/node.def create mode 100644 templates-cfg/cluster/keepalive-interval/node.def create mode 100644 templates-cfg/cluster/node.def create mode 100644 templates-cfg/cluster/pre-shared-secret/node.def create mode 100644 templates-op/show/cluster/node.def create mode 100644 templates-op/show/cluster/status/node.def diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4572ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +*~ +.*.swp +/aclocal.m4 +/autom4te.cache +/build-stamp +/ChangeLog +/config +/config.log +/config.guess +/config.status +/config.sub +/configure +/debian/files +/debian/vyatta-cluster +/debian/vyatta-cluster.postinst +/debian/vyatta-cluster.postrm +/INSTALL +/Makefile.in +/Makefile + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..ee635b2 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +eng@vyatta.com diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..c1e79ac --- /dev/null +++ b/COPYING @@ -0,0 +1,26 @@ +/* + * Package: vyatt-cluster + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) "YEAR" Vyatta, Inc. + * All Rights Reserved. + * + * Author: An-Cheng Huang + * Date: 2007 + * Description: Vyatta configuration/operational commands for clustering + * + * **** End License **** + */ diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..21df18b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,17 @@ +cfgdir = $(datadir)/vyatta-cfg/templates +opdir = $(datadir)/vyatta-op/templates +share_perl5dir = $(datadir)/perl5 + +sbin_SCRIPTS = scripts/vyatta-update-cluster.pl +sbin_SCRIPTS += scripts/vyatta-show-cluster.pl + +share_perl5_DATA = scripts/VyattaClusterConfig.pm + +cpiop = find . ! -regex '\(.*~\|.*\.bak\|.*\.swp\|.*\#.*\#\)' -print0 | \ + cpio -0pd + +install-exec-hook: + mkdir -p $(DESTDIR)$(cfgdir) + cd templates-cfg; $(cpiop) $(DESTDIR)$(cfgdir) + mkdir -p $(DESTDIR)$(opdir) + cd templates-op; $(cpiop) $(DESTDIR)$(opdir) diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..78fdaa6 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +see http://www.vyatta.com/news/ diff --git a/README b/README new file mode 100644 index 0000000..41ab9d8 --- /dev/null +++ b/README @@ -0,0 +1 @@ +This package has the Vyatta configuration and operational templates and scripts for clustering. diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..38b3cd2 --- /dev/null +++ b/configure.ac @@ -0,0 +1,30 @@ +# Process this file with autoconf to produce a configure script. +AC_PREREQ(2.59) + +m4_define([VERSION_ID], [m4_esyscmd([ + if test -f .version ; then + head -n 1 .version | tr -d \\n + else + echo -n 2.4 + fi])]) +AC_INIT([vyatta-cluster], VERSION_ID, [vyatta-support@vyatta.com]) + +test -n "$VYATTA_VERSION" || VYATTA_VERSION=$PACKAGE_VERSION + +AC_CONFIG_AUX_DIR([config]) +AM_INIT_AUTOMAKE([gnu no-dist-gzip dist-bzip2 subdir-objects]) +AC_PREFIX_DEFAULT([/opt/vyatta]) + +AC_ARG_ENABLE([nostrip], + AC_HELP_STRING([--enable-nostrip], + [include -nostrip option during packaging]), + [NOSTRIP=-nostrip], [NOSTRIP=]) + +AC_CONFIG_FILES( + [Makefile] + [debian/vyatta-cluster.postinst]) + +AC_SUBST(NOSTRIP) + +AC_OUTPUT + diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..ae0cf33 --- /dev/null +++ b/debian/README @@ -0,0 +1,6 @@ +The Debian Package vyatta-cluster +---------------------------- + +This package has the Vyatta configuration and operational templates and scripts for clustering. + + -- An-Cheng Huang Fri, 14 Dec 2007 17:02:02 -0700 diff --git a/debian/autogen.sh b/debian/autogen.sh new file mode 100755 index 0000000..ff125d1 --- /dev/null +++ b/debian/autogen.sh @@ -0,0 +1,37 @@ +#!/bin/sh + + +if [ -d .git ] ; then +# generate GNU/Debian format ChangeLog from git log + + rm -f ChangeLog + + if which git2cl >/dev/null ; then + git-log --pretty --numstat --summary | git2cl >> ChangeLog + else + git-log --pretty=short >> ChangeLog + fi + +# append repository reference + + url=` git repo-config --get remote.origin.url` + test "x$url" = "x" && url=`pwd` + + branch=`git-branch --no-color | sed '/^\* /!d; s/^\* //'` + test "x$branch" = "x" && branch=master + + sha=`git log --pretty=oneline --no-color -n 1 | cut -c-8` + test "x$sha" = "x" && sha=00000000 + + echo "$url#$branch-$sha" >> ChangeLog + +fi + +rm -rf config +rm -f aclocal.m4 config.guess config.statusconfig.sub configure INSTALL + +autoreconf --force --install + +rm -f config.sub config.guess +ln -s /usr/share/misc/config.sub . +ln -s /usr/share/misc/config.guess . diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..6f5cb8e --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +vyatta-cluster (0.1) unstable; urgency=low + + * Initial Release. + + -- An-Cheng Huang Fri, 14 Dec 2007 17:02:02 -0700 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..4ee0ef2 --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: vyatta-cluster +Section: contrib/net +Priority: extra +Maintainer: An-Cheng Huang +Build-Depends: debhelper (>= 5), autotools-dev +Standards-Version: 3.7.2 + +Package: vyatta-cluster +Architecture: all +Depends: vyatta-cfg, + vyatta-op, + heartbeat(>=2.1.2-1) +Description: Vyatta configuration/operational commands for clustering + Vyatta configuration and operational templates and scripts for clustering diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..ad2bbbf --- /dev/null +++ b/debian/copyright @@ -0,0 +1,34 @@ +This package was debianized by An-Cheng Huang on +Fri, 14 Dec 2007 17:02:02 -0700. + +It's original content from the GIT repository + +Upstream Author: + + + +Copyright: + + Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + +License: + + The contents of this package are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. + +The Debian packaging is (C) 2007, Tom Grennan and +is licensed under the GPL, see above. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..50bd824 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +NEWS +README diff --git a/debian/linda b/debian/linda new file mode 100644 index 0000000..0381d9d --- /dev/null +++ b/debian/linda @@ -0,0 +1 @@ +Tag: file-in-opt diff --git a/debian/lintian b/debian/lintian new file mode 100644 index 0000000..d024a1d --- /dev/null +++ b/debian/lintian @@ -0,0 +1,2 @@ +vyatta-cluster: file-in-unusual-dir +vyatta-cluster: dir-or-file-in-opt diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..406cf53 --- /dev/null +++ b/debian/rules @@ -0,0 +1,102 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) +PACKAGE=vyatta-cluster +PKGDIR=$(CURDIR)/debian/$(PACKAGE) + +CFLAGS = -Wall -g + +configure = ./configure +configure += --host=$(DEB_HOST_GNU_TYPE) +configure += --build=$(DEB_BUILD_GNU_TYPE) +configure += --prefix=/opt/vyatta +configure += --mandir=\$${prefix}/share/man +configure += --infodir=\$${prefix}/share/info +configure += CFLAGS="$(CFLAGS)" +configure += LDFLAGS="-Wl,-z,defs" + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +configure: configure.ac Makefile.am + chmod +x debian/autogen.sh + debian/autogen.sh + +config.status: configure + dh_testdir + rm -f config.cache + $(configure) + +build: build-stamp + +build-stamp: config.status + dh_testdir + $(MAKE) + touch $@ + +clean: clean-patched + +# Clean everything up, including everything auto-generated +# at build time that needs not to be kept around in the Debian diff +clean-patched: + dh_testdir + dh_testroot + if test -f Makefile ; then $(MAKE) clean distclean ; fi + rm -f build-stamp + rm -f config.status config.sub config.guess config.log + rm -f aclocal.m4 configure Makefile.in Makefile INSTALL + rm -f etc/default/vyatta + rm -rf config + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + $(MAKE) DESTDIR=$(PKGDIR) install + + install -D --mode=0644 debian/lintian $(PKGDIR)/usr/share/lintian/overrides/$(PACKAGE) + install -D --mode=0644 debian/linda $(PKGDIR)/usr/share/linda/overrides/$(PACKAGE) + +# Build architecture-independent files here. +binary-indep: build install + dh_testdir + dh_testroot + dh_installchangelogs ChangeLog + dh_installdocs + dh_install + dh_installdebconf + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture-dependent files here. +binary-arch: build install +# This is an architecture independent package +# so; we have nothing to do by default. + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/debian/vyatta-cluster.postinst.in b/debian/vyatta-cluster.postinst.in new file mode 100644 index 0000000..9d7d866 --- /dev/null +++ b/debian/vyatta-cluster.postinst.in @@ -0,0 +1,4 @@ +#!/bin/bash + +update-rc.d -f heartbeat remove >/dev/null + diff --git a/scripts/VyattaClusterConfig.pm b/scripts/VyattaClusterConfig.pm new file mode 100644 index 0000000..affb3e8 --- /dev/null +++ b/scripts/VyattaClusterConfig.pm @@ -0,0 +1,312 @@ +package VyattaClusterConfig; + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaConfig; + +my $DEFAULT_INITDEAD = 120; +my $DEFAULT_LOG_FACILITY = 'daemon'; +my $SERVICE_DIR = "/etc/init.d"; + +my %fields = ( + _interface => undef, + _pre_shared => undef, + _keepalive_itvl => undef, + _dead_itvl => undef, + _groups => {}, + _is_empty => 1, +); + +sub new { + my $that = shift; + my $class = ref ($that) || $that; + my $self = { + %fields, + }; + + bless $self, $class; + return $self; +} + +sub setup { + my ( $self, $level ) = @_; + my $config = new VyattaConfig; + + $config->setLevel("$level"); + my @nodes = $config->listNodes(); + if (scalar(@nodes) <= 0) { + $self->{_is_empty} = 1; + return 0; + } else { + $self->{_is_empty} = 0; + } + + my @tmp = $config->returnValues("interface"); + $self->{_interface} = [ @tmp ]; + $self->{_pre_shared} = $config->returnValue("pre-shared-secret"); + $self->{_keepalive_itvl} = $config->returnValue("keepalive-interval"); + $self->{_dead_itvl} = $config->returnValue("dead-interval"); + + $config->setLevel("$level group"); + my @groups = $config->listNodes(); + my $group; + for $group (@groups) { + my $hashref = {}; + $config->setLevel("$level group $group"); + $hashref->{_primary} = $config->returnValue("primary"); + @tmp = $config->returnValues("secondary"); + $hashref->{_secondary} = [ @tmp ]; + $hashref->{_auto_failback} = $config->returnValue("auto-failback"); + @tmp = $config->returnValues("monitor"); + $hashref->{_monitor} = [ @tmp ]; + @tmp = $config->returnValues("service"); + $hashref->{_service} = [ @tmp ]; + $self->{_groups}->{$group} = $hashref; + } + + return 0; +} + +sub setupOrig { + my ( $self, $level ) = @_; + my $config = new VyattaConfig; + + $config->setLevel("$level"); + my @nodes = $config->listOrigNodes(); + if (scalar(@nodes) <= 0) { + $self->{_is_empty} = 1; + return 0; + } else { + $self->{_is_empty} = 0; + } + + my @tmp = $config->returnOrigValues("interface"); + $self->{_interface} = [ @tmp ]; + $self->{_pre_shared} = $config->returnOrigValue("pre-shared-secret"); + $self->{_keepalive_itvl} = $config->returnOrigValue("keepalive-interval"); + $self->{_dead_itvl} = $config->returnOrigValue("dead-interval"); + + $config->setLevel("$level group"); + my @groups = $config->listOrigNodes(); + my $group; + for $group (@groups) { + my $hashref = {}; + $config->setLevel("$level group $group"); + $hashref->{_primary} = $config->returnOrigValue("primary"); + @tmp = $config->returnOrigValues("secondary"); + $hashref->{_secondary} = [ @tmp ]; + $hashref->{_auto_failback} = $config->returnOrigValue("auto-failback"); + @tmp = $config->returnOrigValues("monitor"); + $hashref->{_monitor} = [ @tmp ]; + @tmp = $config->returnOrigValues("service"); + $hashref->{_service} = [ @tmp ]; + $self->{_groups}->{$group} = $hashref; + } + + return 0; +} + +sub primaryNode { + my ($self) = @_; + my @groups = keys %{$self->{_groups}}; + my $hashref = $self->{_groups}->{$groups[0]}; + return $hashref->{_primary}; +} + +sub secondaryNode { + my ($self) = @_; + my @groups = keys %{$self->{_groups}}; + my $hashref = $self->{_groups}->{$groups[0]}; + return ${$hashref->{_secondary}}[0]; +} + +sub monitorNodes { + my ($self) = @_; + my @groups = keys %{$self->{_groups}}; + my $hashref = $self->{_groups}->{$groups[0]}; + return @{$hashref->{_monitor}}; +} + +sub serviceStr { + my ($self) = @_; + my @groups = keys %{$self->{_groups}}; + my $hashref = $self->{_groups}->{$groups[0]}; + return (join " ", @{$hashref->{_service}}); +} + +sub isEmpty { + my ($self) = @_; + return $self->{_is_empty}; +} + +sub authkeys { + my ($self) = @_; + my $key = $self->{_pre_shared}; + return (undef, "pre-shared secret not defined") if (!defined($key)); + my $str =<& /dev/null"); + if ($? >> 8) { + return "interface $_ does not exist"; + } + } + return undef; +} + +sub ha_cf { + my ($self) = @_; + my @groups = keys %{$self->{_groups}}; + return (undef, "no resource group defined") if ($#groups < 0); + return (undef, "using multiple resource groups is not supported yet") + if ($#groups > 0); + + my $ierr = check_interfaces(@{$self->{_interface}}); + if (defined($ierr)) { + return (undef, $ierr); + } + my $interfaces = join " ", @{$self->{_interface}}; + my $kitvl = $self->{_keepalive_itvl}; + my $ditvl = $self->{_dead_itvl}; + + my $hashref = $self->{_groups}->{$groups[0]}; + my $primary = $hashref->{_primary}; + my @secondaries = @{$hashref->{_secondary}}; + my $pings = join " ", @{$hashref->{_monitor}}; + my $auto_failback = ($hashref->{_auto_failback} eq "true") ? + "on" : "off"; + my $my_name = `uname -n`; + chomp $my_name; + + return (undef, "heartbeat interface(s) not defined") if ($interfaces eq ""); + return (undef, "keepalive interval not defined") if (!defined($kitvl)); + return (undef, "dead interval not defined") if (!defined($ditvl)); + return (undef, "cluster primary system not defined") + if (!defined($primary)); + return (undef, "cluster secondary node(s) not defined") + if ($#secondaries < 0); + return (undef, "using multiple secondary nodes is not supported yet") + if ($#secondaries > 0); + return (undef, + "dead interval must be more than twice the keepalive interval") + if ($ditvl <= (2 * $kitvl)); + return (undef, + "dead interval must be smaller than $DEFAULT_INITDEAD seconds") + if ($ditvl >= $DEFAULT_INITDEAD); + return (undef, + "the current node '$my_name' is not defined in the configuration") + if (($my_name ne $primary) && ($my_name ne $secondaries[0])); + + my $monitor_str = ""; + if ($pings ne "") { + $monitor_str = "\nping $pings\n" + . "respawn hacluster /usr/lib/heartbeat/ipfail"; + } + + my $wtime = int($kitvl * 2); + if ($wtime > $ditvl) { + $wtime = $ditvl; + } + + my $str =<= 0 && $a1 < 256) && ($a2 >= 0 && $a2 < 256) + && ($a3 >= 0 && $a3 < 256) && ($a4 >= 0 && $a4 < 256)); + return 0; +} + +my @service_list = (); +sub isValidService { + my $service = shift; + if (scalar(@service_list) == 0) { + opendir(SDIR, "$SERVICE_DIR") + or (print STDERR "Error: can't open $SERVICE_DIR" && return 0); + @service_list = grep !/^\./, readdir SDIR; + } + return 1 if (grep /^$service$/, @service_list); + return 0; +} + +sub haresources { + my ($self) = @_; + my @groups = keys %{$self->{_groups}}; + return (undef, "no resource group defined") if ($#groups < 0); + return (undef, "using multiple resource groups is not supported yet") + if ($#groups > 0); + + my $hashref = $self->{_groups}->{$groups[0]}; + my $primary = $hashref->{_primary}; + + my @init_services = (); + my @ip_addresses = (); + foreach (@{$hashref->{_service}}) { + if (!isValidIPv4($_)) { + if (isValidService($_)) { + push @init_services, $_; + } else { + return (undef, "\"$_\" is not a valid IP address or service name"); + } + } else { + push @ip_addresses, $_; + } + } + # this forces all ip addresses to be before all services, which may not + # be the desirable behavior in all cases. + my $ip_str = join " ", @ip_addresses; + my $serv_str = join " ", @init_services; + my $services = join " ", ($ip_str, $serv_str); + return (undef, "cluster primary system not defined") if (!defined($primary)); + return (undef, "cluster service(s) not defined") if ($services eq ""); + + my $str =<{_interface}}); + $str .= "\n pre-shared-secret $self->{_pre_shared}"; + $str .= "\n keepalive-interval $self->{_keepalive_itvl}"; + $str .= "\n dead-interval $self->{_dead_itvl}"; + my $group; + foreach $group (keys %{$self->{_groups}}) { + $str .= "\n group $group"; + my $hashref = $self->{_groups}->{$group}; + $str .= "\n primary $hashref->{_primary}"; + $str .= "\n secondary " . (join ",", @{$hashref->{_secondary}}); + $str .= "\n monitor " . (join ",", @{$hashref->{_monitor}}); + $str .= "\n service " . (join ",", @{$hashref->{_service}}); + } + + return $str; +} + +1; + diff --git a/scripts/vyatta-show-cluster.pl b/scripts/vyatta-show-cluster.pl new file mode 100755 index 0000000..2ad29ce --- /dev/null +++ b/scripts/vyatta-show-cluster.pl @@ -0,0 +1,267 @@ +#!/usr/bin/perl + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaClusterConfig; + +my $CL_STATUS = "/usr/bin/cl_status"; + +my $config = new VyattaClusterConfig; +$config->setupOrig("cluster"); +if ($config->isEmpty()) { + # config is empty. + print "Clustering is not configured\n"; + exit 0; +} + +my $primary = $config->primaryNode(); +my $secondary = $config->secondaryNode(); +my @monitors = $config->monitorNodes(); +my $services = $config->serviceStr(); + +my $pri_st = `$CL_STATUS nodestatus $primary 2>/dev/null`; +chomp $pri_st; +my $sec_st = `$CL_STATUS nodestatus $secondary 2>/dev/null`; +chomp $sec_st; +my %mon_st = (); +my $non_reach = 0; +foreach (@monitors) { + my $st = `$CL_STATUS nodestatus $_ 2>/dev/null`; + chomp $st; + if ($st ne "ping") { + $non_reach = 1; + } + $mon_st{$_} = $st; +} +my $res_st = `$CL_STATUS rscstatus 2>/dev/null`; +chomp $res_st; + +my $my_name = `uname -n`; +chomp $my_name; + +if ($my_name eq $primary) { + printPrimaryStatus(); + exit 0; +} elsif ($my_name eq $secondary) { + printSecondaryStatus(); + exit 0; +} else { + print "Error: this node ($my_name) is neither primary ($primary) " + . "nor secondary ($secondary)\n"; + exit 1; +} + +sub printPrimaryStatus { + my $all_reachable = 1; + foreach (keys %mon_st) { + if ($mon_st{$_} eq "dead") { + $all_reachable = 0; + last; + } + } + + print "=== Status report on primary node $my_name ===\n\n"; + print " Primary $primary (this node): "; + my $other_init = 0; + if ($pri_st eq "up") { + print "Started (waiting for secondary)"; + $other_init = 1; + } elsif (($res_st eq "all") || ($res_st eq "local")) { + print "Active"; + } elsif ($res_st eq "transition") { + print "Node status in transition"; + } elsif ($res_st eq "none") { + if ($pri_st eq "active") { + if ($all_reachable) { + # work around heartbeat state problem + print "Active (standby)"; + } else { + print "Down (at least 1 monitor not reachable)"; + } + } elsif ($pri_st eq "dead") { + # this should be unreachable + print "Down"; + } else { + print "Unknown"; + } + } else { + print "Unknown"; + } + print "\n\n"; + + print " Secondary $secondary: "; + if ($other_init) { + print "Initializing"; + } elsif ($res_st eq "all") { + if ($sec_st eq "active") { + # this could also be "Down (at least 1 monitor node not reachable)" + # we might want to just say "Unknown". + print "Active (standby)"; + } elsif ($sec_st eq "dead") { + print "Down"; + } elsif ($sec_st eq "up") { + print "Initializing"; + } else { + print "Unknown"; + } + } elsif ($res_st eq "local") { + if ($sec_st eq "active") { + print "Active (standby)"; + } else { + print "Unknown"; + } + } elsif ($res_st eq "transition") { + print "Node status in transition"; + } elsif ($res_st eq "none") { + if ($sec_st eq "active") { + print "Active"; + } else { + print "Unknown"; + } + } else { + print "Unknown"; + } + print "\n\n"; + + foreach (keys %mon_st) { + print " Monitor $_: "; + if ($other_init) { + print "Initializing"; + } elsif ($res_st eq "transition") { + print "Node status in transition"; + } elsif ($mon_st{$_} eq "ping") { + print "Reachable"; + } elsif ($mon_st{$_} eq "dead") { + print "Unreachable"; + } else { + print "Unknown"; + } + print "\n"; + } + print "\n"; + + print " Resources [$services]:\n "; + if ($other_init) { + print "Initializing"; + } elsif (($res_st eq "all") || ($res_st eq "local")) { + print "Active on primary $my_name (this node)"; + } elsif ($res_st eq "transition") { + print "Resource status in transition"; + } elsif ($res_st eq "none") { + print "Active on secondary $secondary"; + } else { + print "Unknown"; + } + print "\n\n"; +} + +sub printSecondaryStatus { + print "=== Status report on secondary node $my_name ===\n\n"; + my $other_init = 0; + if ($sec_st eq "up") { + $other_init = 1; + } + + my $all_reachable = 1; + foreach (keys %mon_st) { + if ($mon_st{$_} eq "dead") { + $all_reachable = 0; + last; + } + } + + print " Primary $primary: "; + if ($other_init) { + print "Initializing"; + } elsif ($res_st eq "all") { + if ($pri_st eq "active") { + # this could also be "Down (at least 1 monitor node not reachable)". + # we might want to just say "Unknown". + print "Active (standby)"; + } elsif ($pri_st eq "dead") { + print "Down"; + } elsif ($pri_st eq "up") { + print "Initializing"; + } else { + print "Unknown"; + } + } elsif ($res_st eq "local") { + if ($pri_st eq "active") { + print "Active"; + } else { + print "Unknown"; + } + } elsif ($res_st eq "transition") { + print "Node status in transition"; + } elsif ($res_st eq "none") { + if ($pri_st eq "active") { + print "Active"; + } else { + print "Unknown"; + } + } else { + print "Unknown"; + } + print "\n\n"; + + print " Secondary $my_name (this node): "; + if ($sec_st eq "up") { + print "Started (waiting for primary)"; + } elsif ($res_st eq "all") { + print "Active"; + } elsif ($res_st eq "local") { + print "Active (standby)"; + } elsif ($res_st eq "transition") { + print "Node status in transition"; + } elsif ($res_st eq "none") { + if ($sec_st eq "active") { + if ($all_reachable) { + # work around heartbeat state problem + print "Active (standby)"; + } else { + print "Down (at least 1 monitor not reachable)"; + } + } elsif ($sec_st eq "dead") { + # this should be unreachable + print "Down"; + } else { + print "Unknown"; + } + } else { + print "Unknown"; + } + print "\n\n"; + + foreach (keys %mon_st) { + print " Monitor $_: "; + if ($other_init) { + print "Initializing"; + } elsif ($res_st eq "transition") { + print "Node status in transition"; + } elsif ($mon_st{$_} eq "ping") { + print "Reachable"; + } elsif ($mon_st{$_} eq "dead") { + print "Unreachable"; + } else { + print "Unknown"; + } + print "\n"; + } + print "\n"; + + print " Resources [$services]:\n "; + if ($other_init) { + print "Initializing"; + } elsif ($res_st eq "all") { + print "Active on secondary $my_name (this node)"; + } elsif ($res_st eq "transition") { + print "Resource status in transition"; + } elsif (($res_st eq "none") || ($res_st eq "local")) { + print "Active on primary $primary"; + } else { + print "Unknown"; + } + print "\n\n"; +} + +exit 0; diff --git a/scripts/vyatta-update-cluster.pl b/scripts/vyatta-update-cluster.pl new file mode 100755 index 0000000..c90dcf8 --- /dev/null +++ b/scripts/vyatta-update-cluster.pl @@ -0,0 +1,78 @@ +#!/usr/bin/perl + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaClusterConfig; + +my $HA_DIR = "/etc/ha.d"; +my $HA_INIT = "/etc/init.d/heartbeat"; +my $SERVICE_DIR = "/etc/init.d"; + +my $config = new VyattaClusterConfig; +$config->setup("cluster"); +if ($config->isEmpty()) { + # config is empty => deleted. + # shutdown clustering. + system("$HA_INIT stop"); + exit 0; +} + +open(OUT, ">>/tmp/cl.log") or exit 1; + +my ($authkeys, $haresources, $ha_cf, $err, @init_services); +while (1) { + ($authkeys, $err) = $config->authkeys(); + last if (!defined($authkeys)); + ($haresources, $err, @init_services) = $config->haresources(); + last if (!defined($haresources)); + ($ha_cf, $err) = $config->ha_cf(); + last; +} +if (defined($err)) { + print STDERR "Cluster configuration error: $err\n"; + exit 1; +} + +my $ret = system("mkdir -p $HA_DIR"); +if ($ret >> 8) { + print STDERR "Error: cannot create $HA_DIR\n"; + exit 1; +} + +if (!open(CONF_AUTH, ">$HA_DIR/authkeys")) { + print STDERR "Error: cannot create $HA_DIR/authkeys\n"; + exit 1; +} +if (!open(CONF_RES, ">$HA_DIR/haresources")) { + print STDERR "Error: cannot create $HA_DIR/haresources\n"; + exit 1; +} +if (!open(CONF_CF, ">$HA_DIR/ha.cf")) { + print STDERR "Error: cannot create $HA_DIR/ha.cf\n"; + exit 1; +} +print CONF_AUTH $authkeys; +print CONF_RES $haresources; +print CONF_CF $ha_cf; +close CONF_AUTH; +close CONF_RES; +close CONF_CF; +if (!chmod(0600, "$HA_DIR/authkeys")) { + print STDERR "Error: cannot change $HA_DIR/authkeys permissions\n"; + exit 1; +} + +# stop each service in case it is already started +foreach (@init_services) { + system("$SERVICE_DIR/$_ stop"); +} + +# restart clustering. +# using "stop" + "start" ("restart" will cause a long wait). +# (may need to change to "restart".) +system("$HA_INIT stop"); +system("$HA_INIT start"); + +close OUT; +exit 0; + diff --git a/templates-cfg/cluster/dead-interval/node.def b/templates-cfg/cluster/dead-interval/node.def new file mode 100644 index 0000000..edf946f --- /dev/null +++ b/templates-cfg/cluster/dead-interval/node.def @@ -0,0 +1,3 @@ +type: u32 +help: "How long until a node is considered dead after missing heartbeats (seconds)" +default: 20 diff --git a/templates-cfg/cluster/group/node.def b/templates-cfg/cluster/group/node.def new file mode 100644 index 0000000..641c8a7 --- /dev/null +++ b/templates-cfg/cluster/group/node.def @@ -0,0 +1,3 @@ +tag: +type: txt +help: "Name of resource group for clustering" diff --git a/templates-cfg/cluster/group/node.tag/auto-failback/node.def b/templates-cfg/cluster/group/node.tag/auto-failback/node.def new file mode 100644 index 0000000..7dac862 --- /dev/null +++ b/templates-cfg/cluster/group/node.tag/auto-failback/node.def @@ -0,0 +1,3 @@ +type: bool +default: false +help: "Fail back to primary node if it recovers from failure" diff --git a/templates-cfg/cluster/group/node.tag/monitor/node.def b/templates-cfg/cluster/group/node.tag/monitor/node.def new file mode 100644 index 0000000..fdf9437 --- /dev/null +++ b/templates-cfg/cluster/group/node.tag/monitor/node.def @@ -0,0 +1,3 @@ +multi: +type: ipv4 +help: "IP address(es) for monitoring connectivity" diff --git a/templates-cfg/cluster/group/node.tag/primary/node.def b/templates-cfg/cluster/group/node.tag/primary/node.def new file mode 100644 index 0000000..707bea9 --- /dev/null +++ b/templates-cfg/cluster/group/node.tag/primary/node.def @@ -0,0 +1,2 @@ +type: txt +help: "Host name of the primary node" diff --git a/templates-cfg/cluster/group/node.tag/secondary/node.def b/templates-cfg/cluster/group/node.tag/secondary/node.def new file mode 100644 index 0000000..63793ee --- /dev/null +++ b/templates-cfg/cluster/group/node.tag/secondary/node.def @@ -0,0 +1,3 @@ +multi: +type: txt +help: "Host name(s) of the secondary node(s)" diff --git a/templates-cfg/cluster/group/node.tag/service/node.def b/templates-cfg/cluster/group/node.tag/service/node.def new file mode 100644 index 0000000..839c702 --- /dev/null +++ b/templates-cfg/cluster/group/node.tag/service/node.def @@ -0,0 +1,3 @@ +multi: +type: txt +help: "IP address(es) or service name(s) in this resource group" diff --git a/templates-cfg/cluster/interface/node.def b/templates-cfg/cluster/interface/node.def new file mode 100644 index 0000000..448ed6a --- /dev/null +++ b/templates-cfg/cluster/interface/node.def @@ -0,0 +1,3 @@ +multi: +type: txt +help: "Interface(s) for sending/receiving heartbeat packets" diff --git a/templates-cfg/cluster/keepalive-interval/node.def b/templates-cfg/cluster/keepalive-interval/node.def new file mode 100644 index 0000000..525dbce --- /dev/null +++ b/templates-cfg/cluster/keepalive-interval/node.def @@ -0,0 +1,3 @@ +type: u32 +help: "Time interval between heartbeat packets (seconds)" +default: 5 diff --git a/templates-cfg/cluster/node.def b/templates-cfg/cluster/node.def new file mode 100644 index 0000000..b2cacb1 --- /dev/null +++ b/templates-cfg/cluster/node.def @@ -0,0 +1,2 @@ +help: "Configure clustering" +end: "sudo /opt/vyatta/sbin/vyatta-update-cluster.pl" diff --git a/templates-cfg/cluster/pre-shared-secret/node.def b/templates-cfg/cluster/pre-shared-secret/node.def new file mode 100644 index 0000000..a70da79 --- /dev/null +++ b/templates-cfg/cluster/pre-shared-secret/node.def @@ -0,0 +1,2 @@ +type: txt +help: "Pre-shared secret for authentication between cluster nodes" diff --git a/templates-op/show/cluster/node.def b/templates-op/show/cluster/node.def new file mode 100644 index 0000000..a4386d8 --- /dev/null +++ b/templates-op/show/cluster/node.def @@ -0,0 +1 @@ +help: Show clustering information diff --git a/templates-op/show/cluster/status/node.def b/templates-op/show/cluster/status/node.def new file mode 100644 index 0000000..a9ff771 --- /dev/null +++ b/templates-op/show/cluster/status/node.def @@ -0,0 +1,2 @@ +help: Show the current clustering status +run: sudo /opt/vyatta/sbin/vyatta-show-cluster.pl -- cgit v1.2.3