diff options
Diffstat (limited to 'extras')
35 files changed, 1813 insertions, 86 deletions
diff --git a/extras/LinuxRPM/Makefile.am b/extras/LinuxRPM/Makefile.am new file mode 100644 index 000000000..1dafa982b --- /dev/null +++ b/extras/LinuxRPM/Makefile.am @@ -0,0 +1,57 @@ + +GFS_TAR = ../../glusterfs-$(VERSION).tar.gz + +.PHONY: all + +all: + @echo "To build RPMS run 'make glusterrpms'" + +.PHONY: glusterrpms prep srcrpm testsrpm clean + +glusterrpms: prep srcrpm rpms + -rm -rf rpmbuild + +prep: + if [ ! -e $(GFS_TAR) ]; then \ + $(MAKE) -C ../.. dist; \ + fi + -mkdir -p rpmbuild/SPECS + -mkdir -p rpmbuild/RPMS + -mkdir -p rpmbuild/SRPMS + -rm -rf rpmbuild/SOURCES + @if [ -d /d/cache/glusterfs -a -e /d/cache/glusterfs/sources ]; then \ + echo "copying glusterfs rpm files from local cache..." ; \ + mkdir -p ./rpmbuild/SOURCES; \ + cp /d/cache/glusterfs/* ./rpmbuild/SOURCES/ ; \ + elif [ -x /usr/bin/git ]; then \ + echo "fetching glusterfs rpm files from fedora git repo..."; \ + cd ./rpmbuild && git clone git://pkgs.fedoraproject.org/glusterfs.git > /dev/null && mv glusterfs SOURCES; \ + else \ + echo "glusterfs rpm files not fetched, you don't have git installed!" ; \ + exit 1 ; \ + fi + cp ../../*.tar.gz ./rpmbuild/SOURCES + cp ../../glusterfs.spec ./rpmbuild/SPECS + +srcrpm: + rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bs rpmbuild/SPECS/glusterfs.spec + mv rpmbuild/SRPMS/* . + +rpms: + rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bb rpmbuild/SPECS/glusterfs.spec + mv rpmbuild/RPMS/*/* . + +# EPEL-5 does not like new versions of rpmbuild and requires some +# _source_* defines + +testsrpm: prep + rpmbuild --define '_topdir $(shell pwd)/rpmbuild' \ + --define '_source_payload w9.gzdio' \ + --define '_source_filedigest_algorithm 1' \ + -bs rpmbuild/SPECS/glusterfs.spec + mv rpmbuild/SRPMS/* ../.. + -rm -rf rpmbuild + +clean: + -rm -rf rpmbuild + -rm -f *.rpm diff --git a/extras/Makefile.am b/extras/Makefile.am index 47ace72e5..cf619329b 100644 --- a/extras/Makefile.am +++ b/extras/Makefile.am @@ -1,15 +1,20 @@ -docdir = $(datadir)/doc/glusterfs/ -EditorModedir = $(docdir)/ +EditorModedir = $(docdir) EditorMode_DATA = glusterfs-mode.el glusterfs.vim -SUBDIRS = init.d benchmarking hook-scripts $(OCF_SUBDIR) +SUBDIRS = init.d systemd benchmarking hook-scripts $(OCF_SUBDIR) LinuxRPM geo-rep confdir = $(sysconfdir)/glusterfs -conf_DATA = glusterfs-logrotate +conf_DATA = glusterfs-logrotate gluster-rsyslog-7.2.conf gluster-rsyslog-5.8.conf \ + logger.conf.example glusterfs-georep-logrotate + +voldir = $(sysconfdir)/glusterfs +vol_DATA = glusterd.vol EXTRA_DIST = specgen.scm MacOSX/Portfile glusterfs-mode.el glusterfs.vim \ migrate-unify-to-distribute.sh backend-xattr-sanitize.sh \ backend-cleanup.sh disk_usage_sync.sh quota-remove-xattr.sh \ - quota-metadata-cleanup.sh glusterfs-logrotate clear_xattrs.sh \ - group-virt.example glusterd-sysconfig + quota-metadata-cleanup.sh glusterfs-logrotate clear_xattrs.sh \ + group-virt.example glusterd-sysconfig gluster-rsyslog-7.2.conf \ + gluster-rsyslog-5.8.conf logger.conf.example glusterd.vol \ + glusterfs-georep-logrotate diff --git a/extras/benchmarking/Makefile.am b/extras/benchmarking/Makefile.am index 04cc06182..bfcc59277 100644 --- a/extras/benchmarking/Makefile.am +++ b/extras/benchmarking/Makefile.am @@ -1,7 +1,5 @@ -docdir = $(datadir)/doc/$(PACKAGE_NAME)/benchmarking - -benchmarkingdir = $(docdir) +benchmarkingdir = $(docdir)/benchmarking benchmarking_DATA = rdd.c glfs-bm.c README launch-script.sh local-script.sh diff --git a/extras/geo-rep/Makefile.am b/extras/geo-rep/Makefile.am new file mode 100644 index 000000000..fc5f56d54 --- /dev/null +++ b/extras/geo-rep/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = gsync-sync-gfid.c gsync-upgrade.sh generate-gfid-file.sh \ + get-gfid.sh slave-upgrade.sh diff --git a/extras/geo-rep/generate-gfid-file.sh b/extras/geo-rep/generate-gfid-file.sh new file mode 100644 index 000000000..c6739fbf1 --- /dev/null +++ b/extras/geo-rep/generate-gfid-file.sh @@ -0,0 +1,53 @@ +#!/bin/bash +#Usage: generate-gfid-file.sh <master-volfile-server:master-volume> <path-to-get-gfid.sh> <output-file> + +function get_gfids() +{ + GET_GFID_CMD=$1 + OUTPUT_FILE=$2 + find . -exec $GET_GFID_CMD {} \; > $OUTPUT_FILE +} + +function mount_client() +{ + local T; # temporary mount + local i; # inode number + + VOLFILE_SERVER=$1; + VOLUME=$2; + GFID_CMD=$3; + OUTPUT=$4; + + T=$(mktemp -d); + + glusterfs -s $VOLFILE_SERVER --volfile-id $VOLUME $T; + + i=$(stat -c '%i' $T); + + [ "x$i" = "x1" ] || fatal "could not mount volume $MASTER on $T"; + + cd $T; + + get_gfids $GFID_CMD $OUTPUT + + cd -; + + umount $T || fatal "could not umount $MASTER from $T"; + + rmdir $T || warn "rmdir of $T failed"; +} + + +function main() +{ + SLAVE=$1 + GET_GFID_CMD=$2 + OUTPUT=$3 + + VOLFILE_SERVER=`echo $SLAVE | sed -e 's/\(.*\):.*/\1/'` + VOLUME_NAME=`echo $SLAVE | sed -e 's/.*:\(.*\)/\1/'` + + mount_client $VOLFILE_SERVER $VOLUME_NAME $GET_GFID_CMD $OUTPUT +} + +main "$@"; diff --git a/extras/geo-rep/get-gfid.sh b/extras/geo-rep/get-gfid.sh new file mode 100755 index 000000000..a4d609b0b --- /dev/null +++ b/extras/geo-rep/get-gfid.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +ATTR_STR=`getfattr -h $1 -n glusterfs.gfid.string` +GLFS_PATH=`echo $ATTR_STR | sed -e 's/# file: \(.*\) glusterfs.gfid.string*/\1/g'` +GFID=`echo $ATTR_STR | sed -e 's/.*glusterfs.gfid.string="\(.*\)"/\1/g'` + +echo "$GFID $GLFS_PATH" diff --git a/extras/geo-rep/gsync-sync-gfid.c b/extras/geo-rep/gsync-sync-gfid.c new file mode 100644 index 000000000..601f4720e --- /dev/null +++ b/extras/geo-rep/gsync-sync-gfid.c @@ -0,0 +1,106 @@ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <attr/xattr.h> +#include <libgen.h> +#include <ctype.h> +#include <stdlib.h> + +#ifndef UUID_CANONICAL_FORM_LEN +#define UUID_CANONICAL_FORM_LEN 36 +#endif + +#ifndef GF_FUSE_AUX_GFID_HEAL +#define GF_FUSE_AUX_GFID_HEAL "glusterfs.gfid.heal" +#endif + +#define GLFS_LINE_MAX (PATH_MAX + (2 * UUID_CANONICAL_FORM_LEN)) + +int +main (int argc, char *argv[]) +{ + char *file = NULL; + char *tmp = NULL; + char *tmp1 = NULL; + char *parent_dir = NULL; + char *gfid = NULL; + char *bname = NULL; + int ret = -1; + int len = 0; + FILE *fp = NULL; + char line[GLFS_LINE_MAX] = {0,}; + char *path = NULL; + void *blob = NULL; + void *tmp_blob = NULL; + + if (argc != 2) { + /* each line in the file has the following format + * uuid-in-canonical-form path-relative-to-gluster-mount. + * Both uuid and relative path are from master mount. + */ + fprintf (stderr, "usage: %s <file-of-paths-to-be-synced>\n", + argv[0]); + goto out; + } + + file = argv[1]; + + fp = fopen (file, "r"); + if (fp == NULL) { + fprintf (stderr, "cannot open %s for reading (%s)\n", + file, strerror (errno)); + goto out; + } + + while (fgets (line, GLFS_LINE_MAX, fp) != NULL) { + tmp = line; + path = gfid = line; + + path += UUID_CANONICAL_FORM_LEN + 1; + + while(isspace (*path)) + path++; + + if ((strlen (line) < GLFS_LINE_MAX) && + (line[strlen (line) - 1] == '\n')) + line[strlen (line) - 1] = '\0'; + + line[UUID_CANONICAL_FORM_LEN] = '\0'; + + tmp = strdup (path); + tmp1 = strdup (path); + parent_dir = dirname (tmp); + bname = basename (tmp1); + + /* gfid + '\0' + bname + '\0' */ + len = UUID_CANONICAL_FORM_LEN + 1 + strlen (bname) + 1; + + blob = calloc (1, len); + + memcpy (blob, gfid, UUID_CANONICAL_FORM_LEN); + + tmp_blob = blob + UUID_CANONICAL_FORM_LEN + 1; + + memcpy (tmp_blob, bname, strlen (bname)); + + ret = setxattr (parent_dir, GF_FUSE_AUX_GFID_HEAL, blob, len, + 0); + if (ret < 0) { + fprintf (stderr, "setxattr on %s/%s failed (%s)\n", + parent_dir, bname, strerror (errno)); + } + memset (line, 0, GLFS_LINE_MAX); + + free (blob); + free (tmp); free (tmp1); + blob = NULL; + } + + ret = 0; +out: + return ret; +} + diff --git a/extras/geo-rep/gsync-upgrade.sh b/extras/geo-rep/gsync-upgrade.sh new file mode 100644 index 000000000..b17948736 --- /dev/null +++ b/extras/geo-rep/gsync-upgrade.sh @@ -0,0 +1,123 @@ +#!/bin/bash +#usage: gsync-upgrade.sh <slave-volfile-server:slave-volume> <gfid-file> +# <path-to-gsync-sync-gfid> <ssh-identity-file> +#<slave-volfile-server>: a machine on which gluster cli can fetch slave volume info. +# slave-volfile-server defaults to localhost. +# +#<gfid-file>: a file containing paths and their associated gfids +# on master. The paths are relative to master mount point +# (not absolute). An example extract of <gfid-file> can be, +# +# <extract> +# 22114455-57c5-46e9-a783-c40f83a72b09 /dir +# 25772386-3eb8-4550-a802-c3fdc938ca80 /dir/file +# </extract> +# +#<ssh-identity-file>: file from which the identity (private key) for public key authentication is read. + +SLAVE_MOUNT='/tmp/glfs_slave' + +function SSH() +{ + HOST=$1 + SSHKEY=$2 + + shift 2 + + ssh -qi $SSHKEY \ + -oPasswordAuthentication=no \ + -oStrictHostKeyChecking=no \ + "$HOST" "$@"; +} + +function get_bricks() +{ + SSHKEY=$3 + + SSH $1 $SSHKEY "gluster volume info $2" | grep -E 'Brick[0-9]+' | sed -e 's/[^:]*:\(.*\)/\1/g' +} + +function cleanup_brick() +{ + HOST=$1 + BRICK=$2 + SSHKEY=$3 + + # TODO: write a C program to receive a list of files and does cleanup on + # them instead of spawning a new setfattr process for each file if + # performance is bad. + SSH -i $SSHKEY $HOST "rm -rf $BRICK/.glusterfs/* && find $BRICK -exec setfattr -x trusted.gfid {} \;" +} + +function cleanup_slave() +{ + SSHKEY=$2 + + VOLFILE_SERVER=`echo $1 | sed -e 's/\(.*\):.*/\1/'` + VOLUME_NAME=`echo $1 | sed -e 's/.*:\(.*\)/\1/'` + + BRICKS=`get_bricks $VOLFILE_SERVER $VOLUME_NAME $SSHKEY` + + for i in $BRICKS; do + HOST=`echo $i | sed -e 's/\(.*\):.*/\1/'` + BRICK=`echo $i | sed -e 's/.*:\(.*\)/\1/'` + cleanup_brick $HOST $BRICK $SSHKEY + done + + SSH -i $SSHKEY $VOLFILE_SERVER "gluster --mode=script volume stop $VOLUME_NAME; gluster volume start $VOLUME_NAME"; + +} + +function mount_client() +{ + local T; # temporary mount + local i; # inode number + GFID_FILE=$3 + SYNC_CMD=$4 + + T=$(mktemp -d); + + glusterfs --aux-gfid-mount -s $1 --volfile-id $2 $T; + + i=$(stat -c '%i' $T); + + [ "x$i" = "x1" ] || fatal "could not mount volume $MASTER on $T"; + + cd $T; + + $SYNC_CMD $GFID_FILE + + cd -; + + umount -l $T || fatal "could not umount $MASTER from $T"; + + rmdir $T || warn "rmdir of $T failed"; +} + +function sync_gfids() +{ + SLAVE=$1 + GFID_FILE=$2 + + SLAVE_VOLFILE_SERVER=`echo $SLAVE | sed -e 's/\(.*\):.*/\1/'` + SLAVE_VOLUME_NAME=`echo $SLAVE | sed -e 's/.*:\(.*\)/\1/'` + + if [ "x$SLAVE_VOLFILE_SERVER" = "x" ]; then + SLAVE_VOLFILE_SERVER="localhost" + fi + + mount_client $SLAVE_VOLFILE_SERVER $SLAVE_VOLUME_NAME $GFID_FILE $3 +} + +function upgrade() +{ + SLAVE=$1 + GFID_FILE=$2 + SYNC_CMD=$3 + SSHKEY=$4 + + cleanup_slave $SLAVE $SSHKEY + sync_gfids $SLAVE $GFID_FILE $SYNC_CMD +} + +upgrade "$@" diff --git a/extras/geo-rep/slave-upgrade.sh b/extras/geo-rep/slave-upgrade.sh new file mode 100644 index 000000000..6198f408a --- /dev/null +++ b/extras/geo-rep/slave-upgrade.sh @@ -0,0 +1,102 @@ +#!/bin/bash +#usage: slave-upgrade.sh <volfile-server:volname> <gfid-file> +# <path-to-gsync-sync-gfid> +#<slave-volfile-server>: a machine on which gluster cli can fetch slave volume info. +# slave-volfile-server defaults to localhost. +# +#<gfid-file>: a file containing paths and their associated gfids +# on master. The paths are relative to master mount point +# (not absolute). An example extract of <gfid-file> can be, +# +# <extract> +# 22114455-57c5-46e9-a783-c40f83a72b09 /dir +# 25772386-3eb8-4550-a802-c3fdc938ca80 /dir/file +# </extract> + +function get_bricks() +{ + gluster volume info $1 | grep -E 'Brick[0-9]+' | sed -e 's/[^:]*:\(.*\)/\1/g' +} + +function cleanup_brick() +{ + HOST=$1 + BRICK=$2 + + # TODO: write a C program to receive a list of files and does cleanup on + # them instead of spawning a new setfattr process for each file if + # performance is bad. + ssh $HOST "rm -rf $BRICK/.glusterfs/* && find $BRICK -exec setfattr -x trusted.gfid {} \; 2>/dev/null" +} + +function cleanup_slave() +{ + VOLUME_NAME=`echo $1 | sed -e 's/.*:\(.*\)/\1/'` + + BRICKS=`get_bricks $VOLUME_NAME` + + for i in $BRICKS; do + HOST=`echo $i | sed -e 's/\(.*\):.*/\1/'` + BRICK=`echo $i | sed -e 's/.*:\(.*\)/\1/'` + cleanup_brick $HOST $BRICK + done + + # Now restart the volume + gluster --mode=script volume stop $VOLUME_NAME; + gluster volume start $VOLUME_NAME; +} + +function mount_client() +{ + local T; # temporary mount + local i; # inode number + + VOLUME_NAME=$2; + GFID_FILE=$3 + SYNC_CMD=$4 + + T=$(mktemp -d); + + glusterfs --aux-gfid-mount -s $1 --volfile-id $VOLUME_NAME $T; + + i=$(stat -c '%i' $T); + + cd $T; + + $SYNC_CMD $GFID_FILE + + cd -; + + umount $T || fatal "could not umount $MASTER from $T"; + + rmdir $T || warn "rmdir of $T failed"; +} + +function sync_gfids() +{ + SLAVE=$1 + GFID_FILE=$2 + SYNC_CMD=$3 + + SLAVE_VOLFILE_SERVER=`echo $SLAVE | sed -e 's/\(.*\):.*/\1/'` + SLAVE_VOLUME_NAME=`echo $SLAVE | sed -e 's/.*:\(.*\)/\1/'` + + if [ "x$SLAVE_VOLFILE_SERVER" = "x" ]; then + SLAVE_VOLFILE_SERVER="localhost" + fi + + mount_client $SLAVE_VOLFILE_SERVER $SLAVE_VOLUME_NAME $GFID_FILE $SYNC_CMD +} + +function upgrade() +{ + SLAVE=$1 + GFID_FILE=$2 + SYNC_CMD=$3 + + cleanup_slave $SLAVE + + sync_gfids $SLAVE $GFID_FILE $SYNC_CMD +} + +upgrade "$@" diff --git a/extras/gluster-rsyslog-5.8.conf b/extras/gluster-rsyslog-5.8.conf new file mode 100644 index 000000000..2519999bc --- /dev/null +++ b/extras/gluster-rsyslog-5.8.conf @@ -0,0 +1,51 @@ +##### gluster.conf ##### + +# +## If you want to log every message to the log file instead of +## intelligently suppressing repeated messages, set off to +## RepeatedMsgReduction. This change requires rsyslog restart +## (eg. run 'service rsyslog restart') +# +#$RepeatedMsgReduction off +$RepeatedMsgReduction on + +# +## The mmcount module provides the capability to count log messages by +## severity or json property of given app-name. The count value is added +## into the log message as json property named '$msgid' +# +$ModLoad mmcount +$mmcountKey gf_code # start counting value of gf_code + +$template Glusterfsd_dynLogFile,"/var/log/glusterfs/bricks/%app-name%.log" +$template Gluster_dynLogFile,"/var/log/glusterfs/%app-name%.log" + +$template GLFS_Template,"%msgid%/%syslogfacility-text:::uppercase%/%syslogseverity-text:::uppercase% [%TIMESTAMP:::date-rfc3339%] %msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n" + +# +## Pass logs to mmcount if app-name is 'gluster' +# +if $app-name contains 'gluster' then :mmcount: + +if $app-name contains 'glusterfsd' then ?Glusterfsd_dynLogFile;GLFS_Template +if $app-name contains 'gluster' and not ( $app-name contains 'glusterfsd' ) then ?Gluster_dynLogFile;GLFS_Template + +# +## Sample configuration to send a email alert for every 50th mmcount +# +#$ModLoad ommail +#$ActionMailSMTPServer smtp.example.com +#$ActionMailFrom rsyslog@example.com +#$ActionMailTo glusteradmin@example.com +#$template mailSubject,"50th message of gf_code=9999 on %hostname%" +#$template mailBody,"RSYSLOG Alert\r\nmsg='%msg%'" +#$ActionMailSubject mailSubject +#$ActionExecOnlyOnceEveryInterval 30 +#if $app-name == 'glusterfsd' and $msgid != 0 and $msgid % 50 == 0 \ +#then :ommail:;RSYSLOG_SyslogProtocol23Format +# + +# +## discard logs where app-name is 'gluster' as we processed already +# +if $app-name contains 'gluster' then ~ diff --git a/extras/gluster-rsyslog-7.2.conf b/extras/gluster-rsyslog-7.2.conf new file mode 100644 index 000000000..8b2841543 --- /dev/null +++ b/extras/gluster-rsyslog-7.2.conf @@ -0,0 +1,76 @@ +##### gluster.conf ##### +# +## If you want to log every message to the log file instead of +## intelligently suppressing repeated messages, set off to +## RepeatedMsgReduction. This change requires rsyslog restart +## (eg. run 'service rsyslog restart') +# +#$RepeatedMsgReduction off +$RepeatedMsgReduction on + +$ModLoad mmjsonparse +*.* :mmjsonparse: + +# +## The mmcount module provides the capability to count log messages by +## severity or json property of given app-name. The count value is added +## into the log message as json property named 'mmcount' +## +## More info at http://www.rsyslog.com/doc/mmcount.html +# +#module(load="mmcount") +#action(type="mmcount" appname="glusterd" key="!gf_code") # count each value of gf_code of appname glusterd +#action(type="mmcount" appname="glusterfsd" key="!gf_code") # count each value of gf_code of appname glusterfsd +#action(type="mmcount" appname="glusterfs" key="!gf_code") # count each value of gf_code of appname glusterfs + +template (name="Glusterfsd_dynLogFile" type="string" string="/var/log/glusterfs/bricks/%app-name%.log") +template (name="Gluster_dynLogFile" type="string" string="/var/log/glusterfs/%app-name%.log") + +template(name="GLFS_template" type="list") { + property(name="$!mmcount") + constant(value="/") + property(name="syslogfacility-text" caseConversion="upper") + constant(value="/") + property(name="syslogseverity-text" caseConversion="upper") + constant(value=" ") + constant(value="[") + property(name="timereported" dateFormat="rfc3339") + constant(value="] ") + constant(value="[") + property(name="$!gf_code") + constant(value="] ") + constant(value="[") + property(name="$!gf_message") + constant(value="] ") + property(name="$!msg") + constant(value="\n") +} + +if $app-name contains 'glusterfsd' then { + action(type="omfile" + DynaFile="Glusterfsd_dynLogFile" + Template="GLFS_template") + stop +} + +if $app-name contains 'gluster' then { + action(type="omfile" + DynaFile="Gluster_dynLogFile" + Template="GLFS_template") + stop +} + +# +## send email for every 50th mmcount +#$ModLoad ommail +#if $app-name == 'glusterfsd' and $!mmcount <> 0 and $!mmcount % 50 == 0 then { +# $ActionMailSMTPServer smtp.example.com +# $ActionMailFrom rsyslog@example.com +# $ActionMailTo glusteradmin@example.com +# $template mailSubject,"50th message of gf_code=9999 on %hostname%" +# $template mailBody,"RSYSLOG Alert\r\nmsg='%msg%'" +# $ActionMailSubject mailSubject +# $ActionExecOnlyOnceEveryInterval 30 +# :ommail:;RSYSLOG_SyslogProtocol23Format +#} +# diff --git a/extras/glusterd.vol b/extras/glusterd.vol new file mode 100644 index 000000000..9bac52ab7 --- /dev/null +++ b/extras/glusterd.vol @@ -0,0 +1,9 @@ +volume management + type mgmt/glusterd + option working-directory /var/lib/glusterd + option transport-type socket,rdma + option transport.socket.keepalive-time 10 + option transport.socket.keepalive-interval 2 + option transport.socket.read-fail-log off +# option base-port 49152 +end-volume diff --git a/extras/glusterfs-georep-logrotate b/extras/glusterfs-georep-logrotate new file mode 100644 index 000000000..6a69ab1e3 --- /dev/null +++ b/extras/glusterfs-georep-logrotate @@ -0,0 +1,18 @@ + +rotate 52 +missingok + +compress +delaycompress +notifempty + +/var/log/glusterfs/geo-replication/*/*.log { +} + + +/var/log/glusterfs/geo-replication-slaves/*.log { +} + + +/var/log/glusterfs/geo-replication-slaves/*/*.log { +} diff --git a/extras/group-virt.example b/extras/group-virt.example index 75a5ef314..7dc777f2d 100644 --- a/extras/group-virt.example +++ b/extras/group-virt.example @@ -2,5 +2,5 @@ quick-read=off read-ahead=off io-cache=off stat-prefetch=off -linux-aio=enable eager-lock=enable +remote-dio=enable diff --git a/extras/hook-scripts/Makefile.am b/extras/hook-scripts/Makefile.am index 5c6249de7..f6bded20c 100644 --- a/extras/hook-scripts/Makefile.am +++ b/extras/hook-scripts/Makefile.am @@ -1 +1 @@ -EXTRA_DIST = S29CTDBsetup.sh S30samba-start.sh S30samba-stop.sh +EXTRA_DIST = S29CTDBsetup.sh S30samba-start.sh S30samba-stop.sh S30samba-set.sh S56glusterd-geo-rep-create-post.sh diff --git a/extras/hook-scripts/S30samba-set.sh b/extras/hook-scripts/S30samba-set.sh new file mode 100755 index 000000000..6b11f5a4f --- /dev/null +++ b/extras/hook-scripts/S30samba-set.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +#Need to be copied to hooks/<HOOKS_VER>/set/post/ + +#TODO: All gluster and samba paths are assumed for fedora like systems. +#Some efforts are required to make it work on other distros. + +#The preferred way of creating a smb share of a gluster volume has changed. +#The old method was to create a fuse mount of the volume and share the mount +#point through samba. +# +#New method eliminates the requirement of fuse mount and changes in fstab. +#glusterfs_vfs plugin for samba makes call to libgfapi to access the volume. +# +#This hook script enables user to enable or disable smb share by volume set +#option. Keys "user.cifs" and "user.smb" both are valid, but user.smb is +#preferred. + + +PROGNAME="Ssamba-set" +OPTSPEC="volname:" +VOL= + +enable_smb="" + +function parse_args () { + ARGS=$(getopt -l $OPTSPEC -o "o" -name $PROGNAME $@) + eval set -- "$ARGS" + + while true; do + case $1 in + --volname) + shift + VOL=$1 + ;; + *) + shift + for pair in $@; do + read key value < <(echo "$pair" | tr "=" " ") + case "$key" in + "user.cifs") + enable_smb=$value + ;; + "user.smb") + enable_smb=$value + ;; + *) + ;; + esac + done + + shift + break + ;; + esac + shift + done +} + +function add_samba_share () { + volname=$1 + STRING="\n[gluster-$volname]\n" + STRING+="comment = For samba share of volume $volname\n" + STRING+="vfs objects = glusterfs\n" + STRING+="glusterfs:volume = $volname\n" + STRING+="glusterfs:logfile = /var/log/samba/glusterfs-$volname.%%M.log\n" + STRING+="glusterfs:loglevel = 7\n" + STRING+="path = /\n" + STRING+="read only = no\n" + STRING+="guest ok = yes\n" + printf "$STRING" >> /etc/samba/smb.conf +} + +function sighup_samba () { + pid=`cat /var/run/smbd.pid` + if [ "x$pid" != "x" ] + then + kill -HUP "$pid"; + else + /etc/init.d/smb start + fi +} + +function del_samba_share () { + volname=$1 + sed -i "/\[gluster-$volname\]/,/^$/d" /etc/samba/smb.conf +} + +function is_volume_started () { + volname=$1 + echo "$(grep status /var/lib/glusterd/vols/"$volname"/info |\ + cut -d"=" -f2)" +} + +parse_args $@ +if [ "0" = $(is_volume_started "$VOL") ]; then + exit 0 +fi + +if [ "$enable_smb" = "enable" ]; then + if ! grep --quiet "\[gluster-$VOL\]" /etc/samba/smb.conf ; then + add_samba_share $VOL + sighup_samba + fi + +elif [ "$enable_smb" = "disable" ]; then + del_samba_share $VOL + sighup_samba +fi diff --git a/extras/hook-scripts/S30samba-start.sh b/extras/hook-scripts/S30samba-start.sh index 75336e6b4..34fde0ef8 100755 --- a/extras/hook-scripts/S30samba-start.sh +++ b/extras/hook-scripts/S30samba-start.sh @@ -1,12 +1,31 @@ #!/bin/bash + #Need to be copied to hooks/<HOOKS_VER>/start/post +#TODO: All gluster and samba paths are assumed for fedora like systems. +#Some efforts are required to make it work on other distros. + +#The preferred way of creating a smb share of a gluster volume has changed. +#The old method was to create a fuse mount of the volume and share the mount +#point through samba. +# +#New method eliminates the requirement of fuse mount and changes in fstab. +#glusterfs_vfs plugin for samba makes call to libgfapi to access the volume. +# +#This hook script automagically creates shares for volume on every volume start +#event by adding the entries in smb.conf file and sending SIGHUP to samba. +# +#In smb.conf: +#glusterfs vfs plugin has to be specified as required vfs object. +#Path value is relative to the root of gluster volume;"/" signifies complete +#volume. + PROGNAME="Ssamba-start" -OPTSPEC="volname:,mnt:" +OPTSPEC="volname:" VOL= -#FIXME: glusterd hook interface will eventually provide mntpt prefix as -# command line arg -MNT_PRE="/mnt/samba" +CONFIGFILE= +LOGFILEBASE= +PIDDIR= function parse_args () { ARGS=$(getopt -l $OPTSPEC -name $PROGNAME $@) @@ -18,10 +37,6 @@ function parse_args () { shift VOL=$1 ;; - --mnt) - shift - MNT_PRE=$1 - ;; *) shift break @@ -31,32 +46,65 @@ function parse_args () { done } -function add_samba_export () { - volname=$1 - mnt_pre=$2 - mkdir -p $mnt_pre/$volname && \ - printf "\n[gluster-$volname]\ncomment=For samba export of volume $volname\npath=$mnt_pre/$volname\nread only=no\nguest ok=yes\n" >> /etc/samba/smb.conf +function find_config_info () { + cmdout=`smbd -b | grep smb.conf` + if [ $? -ne 0 ];then + echo "Samba is not installed" + exit 1 + fi + CONFIGFILE=`echo $cmdout | awk {'print $2'}` + PIDDIR=`smbd -b | grep PIDDIR | awk {'print $2'}` + LOGFILEBASE=`smbd -b | grep 'LOGFILEBASE' | awk '{print $2}'` } -function mount_volume () { +function add_samba_share () { volname=$1 - mnt_pre=$2 - #Mount shouldn't block on glusterd to fetch volfile, hence the 'bg' - mount -t glusterfs `hostname`:$volname $mnt_pre/$volname & + STRING="\n[gluster-$volname]\n" + STRING+="comment = For samba share of volume $volname\n" + STRING+="vfs objects = glusterfs\n" + STRING+="glusterfs:volume = $volname\n" + STRING+="glusterfs:logfile = $LOGFILEBASE/glusterfs-$volname.%%M.log\n" + STRING+="glusterfs:loglevel = 7\n" + STRING+="path = /\n" + STRING+="read only = no\n" + STRING+="guest ok = yes\n" + printf "$STRING" >> ${CONFIGFILE} } function sighup_samba () { - pid=`cat /var/run/smbd.pid` - if [ $pid != "" ] + pid=`cat ${PIDDIR}/smbd.pid` + if [ "x$pid" != "x" ] then - kill -HUP $pid; + kill -HUP "$pid"; else /etc/init.d/smb condrestart fi } +function get_smb () { + volname=$1 + uservalue= + + usercifsvalue=$(grep user.cifs /var/lib/glusterd/vols/"$volname"/info |\ + cut -d"=" -f2) + usersmbvalue=$(grep user.smb /var/lib/glusterd/vols/"$volname"/info |\ + cut -d"=" -f2) + + if [[ $usercifsvalue = "disable" || $usersmbvalue = "disable" ]]; then + uservalue="disable" + fi + echo "$uservalue" +} parse_args $@ -add_samba_export $VOL $MNT_PRE -mount_volume $VOL $MNT_PRE -sighup_samba +if [ $(get_smb "$VOL") = "disable" ]; then + exit 0 +fi + +#Find smb.conf, smbd pid directory and smbd logfile path +find_config_info + +if ! grep --quiet "\[gluster-$VOL\]" ${CONFIGFILE} ; then + add_samba_share $VOL + sighup_samba +fi diff --git a/extras/hook-scripts/S30samba-stop.sh b/extras/hook-scripts/S30samba-stop.sh index 7e05c2111..8950eea43 100755 --- a/extras/hook-scripts/S30samba-stop.sh +++ b/extras/hook-scripts/S30samba-stop.sh @@ -1,11 +1,25 @@ #! /bin/bash -#Need to be copied to hooks/<HOOKS_VER>/stop/post + +#Need to be copied to hooks/<HOOKS_VER>/stop/pre + +#TODO: All gluster and samba paths are assumed for fedora like systems. +#Some efforts are required to make it work on other distros. + +#The preferred way of creating a smb share of a gluster volume has changed. +#The old method was to create a fuse mount of the volume and share the mount +#point through samba. +# +#New method eliminates the requirement of fuse mount and changes in fstab. +#glusterfs_vfs plugin for samba makes call to libgfapi to access the volume. +# +#This hook script automagically removes shares for volume on every volume stop +#event by removing the volume related entries(if any) in smb.conf file. PROGNAME="Ssamba-stop" -OPTSPEC="volname:,mnt:" +OPTSPEC="volname:" VOL= -#FIXME: gluster will eventually pass mnt prefix as command line argument -MNT_PRE="/mnt/samba" +CONFIGFILE= +PIDDIR= function parse_args () { ARGS=$(getopt -l $OPTSPEC -name $PROGNAME $@) @@ -17,11 +31,6 @@ function parse_args () { shift VOL=$1 ;; - --mnt) - shift - MNT_PRE=$1 - echo $1 - ;; *) shift break @@ -31,22 +40,24 @@ function parse_args () { done } -function del_samba_export () { - volname=$1 - cp /etc/samba/smb.conf /tmp/smb.conf - sed -i "/gluster-$volname/,/^$/d" /tmp/smb.conf &&\ - cp /tmp/smb.conf /etc/samba/smb.conf +function find_config_info () { + cmdout=`smbd -b | grep smb.conf` + if [ $? -ne 0 ];then + echo "Samba is not installed" + exit 1 + fi + CONFIGFILE=`echo $cmdout | awk {'print $2'}` + PIDDIR=`smbd -b | grep PIDDIR | awk {'print $2'}` } -function umount_volume () { +function del_samba_share () { volname=$1 - mnt_pre=$2 - umount -l $mnt_pre/$volname + sed -i "/\[gluster-$volname\]/,/^$/d" ${CONFIGFILE} } function sighup_samba () { - pid=`cat /var/run/smbd.pid` - if [ $pid != "" ] + pid=`cat ${PIDDIR}/smbd.pid` + if [ "x$pid" != "x" ] then kill -HUP $pid; else @@ -55,6 +66,6 @@ function sighup_samba () { } parse_args $@ -del_samba_export $VOL -umount_volume $VOL $MNT_PRE +find_config_info +del_samba_share $VOL sighup_samba diff --git a/extras/hook-scripts/S40ufo-stop.py b/extras/hook-scripts/S40ufo-stop.py new file mode 100755 index 000000000..107f19683 --- /dev/null +++ b/extras/hook-scripts/S40ufo-stop.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +import os +from optparse import OptionParser + +if __name__ == '__main__': + # check if swift is installed + try: + from gluster.swift.common.Glusterfs import get_mnt_point, unmount + except ImportError: + import sys + sys.exit("Openstack Swift does not appear to be installed properly"); + + op = OptionParser(usage="%prog [options...]") + op.add_option('--volname', dest='vol', type=str) + op.add_option('--last', dest='last', type=str) + (opts, args) = op.parse_args() + + + mnt_point = get_mnt_point(opts.vol) + if mnt_point: + unmount(mnt_point) + else: + sys.exit("get_mnt_point returned none for mount point") diff --git a/extras/hook-scripts/S56glusterd-geo-rep-create-post.sh b/extras/hook-scripts/S56glusterd-geo-rep-create-post.sh new file mode 100755 index 000000000..1369c22fc --- /dev/null +++ b/extras/hook-scripts/S56glusterd-geo-rep-create-post.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +key_val_pair1=`echo $2 | cut -d ' ' -f 1` +key_val_pair2=`echo $2 | cut -d ' ' -f 2` +key_val_pair3=`echo $2 | cut -d ' ' -f 3` + +key=`echo $key_val_pair1 | cut -d '=' -f 1` +val=`echo $key_val_pair1 | cut -d '=' -f 2` +if [ "$key" != "is_push_pem" ]; then + exit; +fi +if [ "$val" != '1' ]; then + exit; +fi + +key=`echo $key_val_pair2 | cut -d '=' -f 1` +val=`echo $key_val_pair2 | cut -d '=' -f 2` +if [ "$key" != "pub_file" ]; then + exit; +fi +if [ "$val" == "" ]; then + exit; +fi +pub_file=`echo $val` +pub_file_tmp=`echo $val`_tmp + +key=`echo $key_val_pair3 | cut -d '=' -f 1` +val=`echo $key_val_pair3 | cut -d '=' -f 2` +if [ "$key" != "slave_ip" ]; then + exit; +fi +if [ "$val" == "" ]; then + exit; +fi +slave_ip=`echo $val` + +if [ -f $pub_file ]; then + scp $pub_file $slave_ip:$pub_file_tmp + ssh $slave_ip "mv $pub_file_tmp $pub_file" + ssh $slave_ip "gluster system:: copy file /geo-replication/common_secret.pem.pub > /dev/null" + ssh $slave_ip "gluster system:: execute add_secret_pub > /dev/null" +fi diff --git a/extras/init.d/Makefile.am b/extras/init.d/Makefile.am index 66715f431..38898fddd 100644 --- a/extras/init.d/Makefile.am +++ b/extras/init.d/Makefile.am @@ -1,19 +1,22 @@ -EXTRA_DIST = glusterd-Debian glusterd-Redhat glusterd-SuSE glusterd.plist +EXTRA_DIST = glusterd-Debian glusterd-Redhat glusterd-SuSE glusterd.plist rhel5-load-fuse.modules CLEANFILES = -initdir = @initdir@ -launchddir = @launchddir@ +INIT_DIR = @initdir@ +SYSTEMD_DIR = @systemddir@ +LAUNCHD_DIR = @launchddir@ $(GF_DISTRIBUTION): - $(mkdir_p) $(DESTDIR)$(initdir) - $(INSTALL_PROGRAM) glusterd-$(GF_DISTRIBUTION) $(DESTDIR)$(initdir)/glusterd + @if [ ! -d $(SYSTEMD_DIR) ]; then \ + $(mkdir_p) $(DESTDIR)$(INIT_DIR); \ + $(INSTALL_PROGRAM) glusterd-$(GF_DISTRIBUTION) $(DESTDIR)$(INIT_DIR)/glusterd; \ + fi install-exec-local: $(GF_DISTRIBUTION) install-data-local: if GF_DARWIN_HOST_OS - $(mkdir_p) $(DESTDIR)$(launchddir) - $(INSTALL_PROGRAM) glusterd.plist $(DESTDIR)$(launchddir)/com.gluster.glusterd.plist + $(mkdir_p) $(DESTDIR)$(LAUNCHD_DIR) + $(INSTALL_PROGRAM) glusterd.plist $(DESTDIR)$(LAUNCHD_DIR)/com.gluster.glusterd.plist endif diff --git a/extras/init.d/glusterd-Redhat.in b/extras/init.d/glusterd-Redhat.in index 858f82245..e320708ae 100755 --- a/extras/init.d/glusterd-Redhat.in +++ b/extras/init.d/glusterd-Redhat.in @@ -1,20 +1,39 @@ #!/bin/bash # -# chkconfig: 35 20 80 -# description: Gluster File System service for volume management +# glusterd Startup script for the glusterfs server # +# chkconfig: - 20 80 +# description: Clustered file-system server -# Get function from functions library +### BEGIN INIT INFO +# Provides: glusterd +# Required-Start: $local_fs $network +# Required-Stop: $local_fs $network +# Should-Start: +# Should-Stop: +# Default-Start: +# Default-Stop: 0 1 2 3 4 5 6 +# Short-Description: glusterfs server +# Description: Clustered file-system server +### END INIT INFO +# + +# Source function library. . /etc/rc.d/init.d/functions BASE=glusterd -PIDFILE=/var/run/$BASE.pid + +# Fedora File System Layout dictates /run +[ -e /run ] && RUNDIR="/run" +PIDFILE="${RUNDIR:-/var/run}/${BASE}.pid" + PID=`test -f $PIDFILE && cat $PIDFILE` # Overwriteable from sysconfig LOG_LEVEL='' LOG_FILE='' GLUSTERD_OPTIONS='' +GLUSTERD_NOFILE='65536' [ -f /etc/sysconfig/${BASE} ] && . /etc/sysconfig/${BASE} @@ -28,60 +47,96 @@ GLUSTERD_OPTS="--pid-file=$PIDFILE ${GLUSTERD_OPTIONS}" GLUSTERD="$GLUSTERD_BIN $GLUSTERD_OPTS" RETVAL=0 +LOCKFILE=/var/lock/subsys/${BASE} + # Start the service $BASE start() { - pidofproc -p $PIDFILE $GLUSTERD_BIN &> /dev/null - status=$? - if [ $status -eq 0 ]; then + if pidofproc -p $PIDFILE $GLUSTERD_BIN &> /dev/null; then echo "glusterd service is already running with pid $PID" - exit 0 + return 0 else + ulimit -n $GLUSTERD_NOFILE echo -n $"Starting $BASE:" daemon $GLUSTERD RETVAL=$? echo - [ $RETVAL -ne 0 ] && exit $RETVAL + [ $RETVAL -eq 0 ] && touch $LOCKFILE + return $RETVAL fi - } # Stop the service $BASE stop() { echo -n $"Stopping $BASE:" - pidofproc -p $PIDFILE $GLUSTERD_BIN &> /dev/null - status=$? - if [ $status -eq 0 ]; then + if pidofproc -p $PIDFILE $GLUSTERD_BIN &> /dev/null; then killproc -p $PIDFILE $BASE - [ -w $PIDFILE ] && rm -f $PIDFILE else killproc $BASE fi + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f $LOCKFILE + return $RETVAL +} +restart() +{ + stop + start +} + +reload() +{ + restart +} + +force_reload() +{ + restart +} + +rh_status() +{ + status $BASE +} + +rh_status_q() +{ + rh_status &>/dev/null } ### service arguments ### case $1 in start) - start + rh_status_q && exit 0 + $1 ;; stop) - stop - RETVAL=$? + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + reload) + rh_status_q || exit 7 + $1 + ;; + force-reload) + force_reload ;; status) - status $BASE - RETVAL=$? + rh_status ;; - restart) - $0 stop - $0 start + condrestart|try-restart) + rh_status_q || exit 0 + restart ;; *) - echo $"Usage: $0 {start|stop|status|restart}." + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" exit 1 esac -exit $RETVAL +exit $? diff --git a/extras/init.d/rhel5-load-fuse.modules b/extras/init.d/rhel5-load-fuse.modules new file mode 100755 index 000000000..ee194db99 --- /dev/null +++ b/extras/init.d/rhel5-load-fuse.modules @@ -0,0 +1,7 @@ +#!/bin/sh +# +# fusermount-glusterfs requires the /dev/fuse character device. The fuse module +# provides this and is loaded on demand in newer Linux distributions. +# + +[ -c /dev/fuse ] || /sbin/modprobe fuse diff --git a/extras/logger.conf.example b/extras/logger.conf.example new file mode 100644 index 000000000..248be5bda --- /dev/null +++ b/extras/logger.conf.example @@ -0,0 +1,13 @@ +# +# Sample logger.conf file to configure enhanced Logging in GlusterFS +# +# To enable enhanced logging capabilities, +# +# 1. rename this file to /etc/glusterfs/logger.conf +# +# 2. rename /etc/rsyslog.d/gluster.conf.example to +# /etc/rsyslog.d/gluster.conf +# +# This change requires restart of all gluster services/volumes and +# rsyslog. +# diff --git a/extras/prot_filter.py b/extras/prot_filter.py new file mode 100755 index 000000000..7dccacf15 --- /dev/null +++ b/extras/prot_filter.py @@ -0,0 +1,144 @@ +#!/usr/bin/python + +""" + Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +""" + +""" + INSTRUCTIONS + Put this in /usr/lib64/glusterfs/$version/filter to have it run automatically, + or else you'll have to run it by hand every time you change the volume + configuration. Give it a list of volume names on which to enable the + protection functionality; it will deliberately ignore client volfiles for + other volumes, and all server volfiles. It *will* include internal client + volfiles such as those used for NFS or rebalance/self-heal; this is a + deliberate choice so that it will catch deletions from those sources as well. +""" + +volume_list = [ "jdtest" ] + +import copy +import string +import sys +import types + +class Translator: + def __init__ (self, name): + self.name = name + self.xl_type = "" + self.opts = {} + self.subvols = [] + self.dumped = False + def __repr__ (self): + return "<Translator %s>" % self.name + +def load (path): + # If it's a string, open it; otherwise, assume it's already a + # file-like object (most notably from urllib*). + if type(path) in types.StringTypes: + fp = file(path,"r") + else: + fp = path + all_xlators = {} + xlator = None + last_xlator = None + while True: + text = fp.readline() + if text == "": + break + text = text.split() + if not len(text): + continue + if text[0] == "volume": + if xlator: + raise RuntimeError, "nested volume definition" + xlator = Translator(text[1]) + continue + if not xlator: + raise RuntimeError, "text outside volume definition" + if text[0] == "type": + xlator.xl_type = text[1] + continue + if text[0] == "option": + xlator.opts[text[1]] = string.join(text[2:]) + continue + if text[0] == "subvolumes": + for sv in text[1:]: + xlator.subvols.append(all_xlators[sv]) + continue + if text[0] == "end-volume": + all_xlators[xlator.name] = xlator + last_xlator = xlator + xlator = None + continue + raise RuntimeError, "unrecognized keyword %s" % text[0] + if xlator: + raise RuntimeError, "unclosed volume definition" + return all_xlators, last_xlator + +def generate (graph, last, stream=sys.stdout): + for sv in last.subvols: + if not sv.dumped: + generate(graph,sv,stream) + print >> stream, "" + sv.dumped = True + print >> stream, "volume %s" % last.name + print >> stream, " type %s" % last.xl_type + for k, v in last.opts.iteritems(): + print >> stream, " option %s %s" % (k, v) + if last.subvols: + print >> stream, " subvolumes %s" % string.join( + [ sv.name for sv in last.subvols ]) + print >> stream, "end-volume" + +def push_filter (graph, old_xl, filt_type, opts={}): + new_type = "-" + filt_type.split("/")[1] + old_type = "-" + old_xl.xl_type.split("/")[1] + pos = old_xl.name.find(old_type) + if pos >= 0: + new_name = old_xl.name + old_name = new_name[:pos] + new_type + new_name[len(old_type)+pos:] + else: + new_name = old_xl.name + old_type + old_name = old_xl.name + new_type + new_xl = Translator(new_name) + new_xl.xl_type = old_xl.xl_type + new_xl.opts = old_xl.opts + new_xl.subvols = old_xl.subvols + graph[new_xl.name] = new_xl + old_xl.name = old_name + old_xl.xl_type = filt_type + old_xl.opts = opts + old_xl.subvols = [new_xl] + graph[old_xl.name] = old_xl + +if __name__ == "__main__": + path = sys.argv[1] + # Alow an override for debugging. + for extra in sys.argv[2:]: + volume_list.append(extra) + graph, last = load(path) + for v in volume_list: + if graph.has_key(v): + break + else: + print "No configured volumes found - aborting." + sys.exit(0) + for v in graph.values(): + if v.xl_type == "cluster/distribute": + push_filter(graph,v,"features/prot_dht") + elif v.xl_type == "protocol/client": + push_filter(graph,v,"features/prot_client") + # We push debug/trace so that every fop gets a real frame, because DHT + # gets confused if STACK_WIND_TAIL causes certain fops to be invoked + # from anything other than a direct child. + for v in graph.values(): + if v.xl_type == "features/prot_client": + push_filter(graph,v,"debug/trace") + generate(graph,last,stream=open(path,"w")) diff --git a/extras/rebalance.py b/extras/rebalance.py new file mode 100755 index 000000000..80c614c5d --- /dev/null +++ b/extras/rebalance.py @@ -0,0 +1,299 @@ +#!/usr/bin/python + +import atexit +import copy +import optparse +import os +import pipes +import shutil +import string +import subprocess +import sys +import tempfile +import volfilter + +# It's just more convenient to have named fields. +class Brick: + def __init__ (self, path, name): + self.path = path + self.sv_name = name + self.size = 0 + self.curr_size = 0 + self.good_size = 0 + def set_size (self, size): + self.size = size + def set_range (self, rs, re): + self.r_start = rs + self.r_end = re + self.curr_size = self.r_end - self.r_start + 1 + def __repr__ (self): + value = self.path[:] + value += "(%d," % self.size + if self.curr_size: + value += "0x%x,0x%x)" % (self.r_start, self.r_end) + else: + value += "-)" + return value + +def get_bricks (host, vol): + t = pipes.Template() + t.prepend("gluster --remote-host=%s system getspec %s"%(host,vol),".-") + return t.open(None,"r") + +def generate_stanza (vf, all_xlators, cur_subvol): + sv_list = [] + for sv in cur_subvol.subvols: + generate_stanza(vf,all_xlators,sv) + sv_list.append(sv.name) + vf.write("volume %s\n"%cur_subvol.name) + vf.write(" type %s\n"%cur_subvol.type) + for kvpair in cur_subvol.opts.iteritems(): + vf.write(" option %s %s\n"%kvpair) + if sv_list: + vf.write(" subvolumes %s\n"%string.join(sv_list)) + vf.write("end-volume\n\n") + + +def mount_brick (localpath, all_xlators, dht_subvol): + + # Generate a volfile. + vf_name = localpath + ".vol" + vf = open(vf_name,"w") + generate_stanza(vf,all_xlators,dht_subvol) + vf.flush() + vf.close() + + # Create a brick directory and mount the brick there. + os.mkdir(localpath) + subprocess.call(["glusterfs","-f",vf_name,localpath]) + +# We use the command-line tools because there's no getxattr support in the +# Python standard library (which is ridiculous IMO). Adding the xattr package +# from PyPI would create a new and difficult dependency because the bits to +# satisfy it don't seem to exist in Fedora. We already expect the command-line +# tools to be there, so it's safer just to rely on them. +# +# We might have to revisit this if we get as far as actually issuing millions +# of setxattr requests. Even then, it might be better to do that part with a C +# program which has only a build-time dependency. +def get_range (brick): + t = pipes.Template() + cmd = "getfattr -e hex -n trusted.glusterfs.dht %s 2> /dev/null" + t.prepend(cmd%brick,".-") + t.append("grep ^trusted.glusterfs.dht=","--") + f = t.open(None,"r") + try: + value = f.readline().rstrip().split('=')[1][2:] + except: + print "could not get layout for %s (might be OK)" % brick + return None + v_start = int("0x"+value[16:24],16) + v_end = int("0x"+value[24:32],16) + return (v_start, v_end) + +def calc_sizes (bricks, total): + leftover = 1 << 32 + for b in bricks: + if b.size: + b.good_size = (b.size << 32) / total + leftover -= b.good_size + else: + b.good_size = 0 + if leftover: + # Add the leftover to an old brick if we can. + for b in bricks: + if b.good_size: + b.good_size += leftover + break + else: + # Fine, just add it wherever. + bricks[0].good_size += leftover + +# Normalization means sorting the bricks by r_start and (b) ensuring that there +# are no gaps. +def normalize (in_bricks): + out_bricks = [] + curr_hash = 0 + used = 0 + while curr_hash < (1<<32): + curr_best = None + for b in in_bricks: + if b.r_start == curr_hash: + used += 1 + out_bricks.append(b) + in_bricks.remove(b) + curr_hash = b.r_end + 1 + break + else: + print "gap found at 0x%08x" % curr_hash + sys.exit(1) + return out_bricks + in_bricks, used + +def get_score (bricks): + score = 0 + curr_hash = 0 + for b in bricks: + if not b.curr_size: + curr_hash += b.good_size + continue + new_start = curr_hash + curr_hash += b.good_size + new_end = curr_hash - 1 + if new_start > b.r_start: + max_start = new_start + else: + max_start = b.r_start + if new_end < b.r_end: + min_end = new_end + else: + min_end = b.r_end + if max_start <= min_end: + score += (min_end - max_start + 1) + return score + +if __name__ == "__main__": + + my_usage = "%prog [options] server volume [directory]" + parser = optparse.OptionParser(usage=my_usage) + parser.add_option("-f", "--free-space", dest="free_space", + default=False, action="store_true", + help="use free space instead of total space") + parser.add_option("-l", "--leave-mounted", dest="leave_mounted", + default=False, action="store_true", + help="leave subvolumes mounted") + parser.add_option("-v", "--verbose", dest="verbose", + default=False, action="store_true", + help="verbose output") + options, args = parser.parse_args() + + if len(args) == 3: + fix_dir = args[2] + else: + if len(args) != 2: + parser.print_help() + sys.exit(1) + fix_dir = None + hostname, volname = args[:2] + + # Make sure stuff gets cleaned up, even if there are exceptions. + orig_dir = os.getcwd() + work_dir = tempfile.mkdtemp() + bricks = [] + def cleanup_workdir (): + os.chdir(orig_dir) + if options.verbose: + print "Cleaning up %s" % work_dir + for b in bricks: + subprocess.call(["umount",b.path]) + shutil.rmtree(work_dir) + if not options.leave_mounted: + atexit.register(cleanup_workdir) + os.chdir(work_dir) + + # Mount each brick individually, so we can issue brick-specific calls. + if options.verbose: + print "Mounting subvolumes..." + index = 0 + volfile_pipe = get_bricks(hostname,volname) + all_xlators, last_xlator = volfilter.load(volfile_pipe) + for dht_vol in all_xlators.itervalues(): + if dht_vol.type == "cluster/distribute": + break + else: + print "no DHT volume found" + sys.exit(1) + for sv in dht_vol.subvols: + #print "found subvol %s" % sv.name + lpath = "%s/brick%s" % (work_dir, index) + index += 1 + mount_brick(lpath,all_xlators,sv) + bricks.append(Brick(lpath,sv.name)) + if index == 0: + print "no bricks" + sys.exit(1) + + # Collect all of the sizes. + if options.verbose: + print "Collecting information..." + total = 0 + for b in bricks: + info = os.statvfs(b.path) + # We want a standard unit even if different bricks use + # different block sizes. The size is chosen to avoid overflows + # for very large bricks with very small block sizes, but also + # accommodate filesystems which use very large block sizes to + # cheat on benchmarks. + blocksper100mb = 104857600 / info[0] + if options.free_space: + size = info[3] / blocksper100mb + else: + size = info[2] / blocksper100mb + if size <= 0: + print "brick %s has invalid size %d" % (b.path, size) + sys.exit(1) + b.set_size(size) + total += size + + # Collect all of the layout information. + for b in bricks: + hash_range = get_range(b.path) + if hash_range is not None: + rs, re = hash_range + if rs > re: + print "%s has backwards hash range" % b.path + sys.exit(1) + b.set_range(hash_range[0],hash_range[1]) + + if options.verbose: + print "Calculating new layouts..." + calc_sizes(bricks,total) + bricks, used = normalize(bricks) + + # We can't afford O(n!) here, but O(n^2) should be OK and the result + # should be almost as good. + while used < len(bricks): + best_place = used + best_score = get_score(bricks) + for i in xrange(used): + new_bricks = bricks[:] + del new_bricks[used] + new_bricks.insert(i,bricks[used]) + new_score = get_score(new_bricks) + if new_score > best_score: + best_place = i + best_score = new_score + if best_place != used: + nb = bricks[used] + del bricks[used] + bricks.insert(best_place,nb) + used += 1 + + # Finalize whatever we decided on. + curr_hash = 0 + for b in bricks: + b.r_start = curr_hash + curr_hash += b.good_size + b.r_end = curr_hash - 1 + + print "Here are the xattr values for your size-weighted layout:" + for b in bricks: + print " %s: 0x0000000200000000%08x%08x" % ( + b.sv_name, b.r_start, b.r_end) + + if fix_dir: + if options.verbose: + print "Fixing layout for %s" % fix_dir + for b in bricks: + value = "0x0000000200000000%08x%08x" % ( + b.r_start, b.r_end) + path = "%s/%s" % (b.path, fix_dir) + cmd = "setfattr -n trusted.glusterfs.dht -v %s %s" % ( + value, path) + print cmd + + if options.leave_mounted: + print "The following subvolumes are still mounted:" + for b in bricks: + print "%s on %s" % (b.sv_name, b.path) + print "Don't forget to clean up when you're done." + diff --git a/extras/systemd/Makefile.am b/extras/systemd/Makefile.am new file mode 100644 index 000000000..3fc656b82 --- /dev/null +++ b/extras/systemd/Makefile.am @@ -0,0 +1,11 @@ + +CLEANFILES = + +SYSTEMD_DIR = @systemddir@ + +install-exec-local: + @if [ -d $(SYSTEMD_DIR) ]; then \ + $(mkdir_p) $(DESTDIR)$(SYSTEMD_DIR); \ + $(INSTALL_PROGRAM) glusterd.service $(DESTDIR)$(SYSTEMD_DIR)/; \ + fi + diff --git a/extras/systemd/glusterd.service.in b/extras/systemd/glusterd.service.in new file mode 100644 index 000000000..fc8d8c9a2 --- /dev/null +++ b/extras/systemd/glusterd.service.in @@ -0,0 +1,14 @@ +[Unit] +Description=GlusterFS, a clustered file-system server +After=network.target rpcbind.service +Before=network-online.target + +[Service] +Type=forking +PIDFile=/run/glusterd.pid +LimitNOFILE=65536 +ExecStart=@prefix@/sbin/glusterd -p /run/glusterd.pid +KillMode=process + +[Install] +WantedBy=multi-user.target diff --git a/extras/test/bug-920583.t b/extras/test/bug-920583.t new file mode 100755 index 000000000..eedbb800a --- /dev/null +++ b/extras/test/bug-920583.t @@ -0,0 +1,50 @@ +#!/bin/bash + +##Copy this file to tests/bugs before running run.sh (cp extras/test/bug-920583.t tests/bugs/) + +. $(dirname $0)/../include.rc +. $(dirname $0)/../volume.rc + +cleanup; +logdir=`gluster --print-logdir` + +## Start and create a volume +TEST glusterd; +TEST pidof glusterd; + +TEST $CLI volume create $V0 replica 2 stripe 2 $H0:$B0/${V0}{1,2,3,4,5,6,7,8}; + +## Verify volume is is created +EXPECT "$V0" volinfo_field $V0 'Volume Name'; +EXPECT 'Created' volinfo_field $V0 'Status'; + +## Start volume and verify +TEST $CLI volume start $V0; +EXPECT 'Started' volinfo_field $V0 'Status'; + +function log-file-name() +{ + logfilename=$M0".log" + echo ${logfilename:1} | tr / - +} + +log_file=$logdir"/"`log-file-name` + +lookup_unhashed_count=`grep "adding option 'lookup-unhashed'" $log_file | wc -l` +no_child_down_count=`grep "adding option 'assert-no-child-down'" $log_file | wc -l` +mount -t glusterfs $H0:/$V0 $M0 -o "xlator-option=*dht.assert-no-child-down=yes,xlator-option=*dht.lookup-unhashed=yes" +touch $M0/file1; + +new_lookup_unhashed_count=`grep "adding option 'lookup-unhashed'" $log_file | wc -l` +new_no_child_down_count=`grep "adding option 'assert-no-child-down'" $log_file | wc -l` +EXPECT "1" expr $new_lookup_unhashed_count - $lookup_unhashed_count +EXPECT "1" expr $new_no_child_down_count - $no_child_down_count + +## Finish up +TEST $CLI volume stop $V0; +EXPECT 'Stopped' volinfo_field $V0 'Status'; + +TEST $CLI volume delete $V0; +TEST ! $CLI volume info $V0; + +cleanup; diff --git a/extras/test/test-ffop.c b/extras/test/test-ffop.c index 6ce7097d5..2d174d452 100644 --- a/extras/test/test-ffop.c +++ b/extras/test/test-ffop.c @@ -777,6 +777,7 @@ test_open_modes (char *filename) goto out; } +#if 0 /* undefined behaviour, unable to reliably test */ ret = creat (filename, 0644); close (ret); ret = generic_open_read_write (filename, O_TRUNC|O_RDONLY); @@ -784,6 +785,7 @@ test_open_modes (char *filename) fprintf (stderr, "flag O_TRUNC|O_RDONLY failed\n"); goto out; } +#endif ret = generic_open_read_write (filename, O_CREAT|O_RDWR|O_SYNC); if (0 != ret) { diff --git a/extras/volfilter.py b/extras/volfilter.py new file mode 100644 index 000000000..0ca456a78 --- /dev/null +++ b/extras/volfilter.py @@ -0,0 +1,167 @@ +# Copyright (c) 2010-2011 Red Hat, Inc. +# +# This file is part of HekaFS. +# +# HekaFS is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License, version 3, as published by the Free +# Software Foundation. +# +# HekaFS 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 HekaFS. If not, see <http://www.gnu.org/licenses/>. + +import copy +import string +import sys +import types + +good_xlators = [ + "cluster/afr", + "cluster/dht", + "cluster/distribute", + "cluster/replicate", + "cluster/stripe", + "debug/io-stats", + "features/access-control", + "features/locks", + "features/marker", + "features/uidmap", + "performance/io-threads", + "protocol/client", + "protocol/server", + "storage/posix", +] + +def copy_stack (old_xl,suffix,recursive=False): + if recursive: + new_name = old_xl.name + "-" + suffix + else: + new_name = suffix + new_xl = Translator(new_name) + new_xl.type = old_xl.type + # The results with normal assignment here are . . . amusing. + new_xl.opts = copy.deepcopy(old_xl.opts) + for sv in old_xl.subvols: + new_xl.subvols.append(copy_stack(sv,suffix,True)) + # Patch up the path at the bottom. + if new_xl.type == "storage/posix": + new_xl.opts["directory"] += ("/" + suffix) + return new_xl + +def cleanup (parent, graph): + if parent.type in good_xlators: + # Temporary fix so that HekaFS volumes can use the + # SSL-enabled multi-threaded socket transport. + if parent.type == "protocol/server": + parent.type = "protocol/server2" + parent.opts["transport-type"] = "ssl" + elif parent.type == "protocol/client": + parent.type = "protocol/client2" + parent.opts["transport-type"] = "ssl" + sv = [] + for child in parent.subvols: + sv.append(cleanup(child,graph)) + parent.subvols = sv + else: + parent = cleanup(parent.subvols[0],graph) + return parent + +class Translator: + def __init__ (self, name): + self.name = name + self.type = "" + self.opts = {} + self.subvols = [] + self.dumped = False + def __repr__ (self): + return "<Translator %s>" % self.name + +def load (path): + # If it's a string, open it; otherwise, assume it's already a + # file-like object (most notably from urllib*). + if type(path) in types.StringTypes: + fp = file(path,"r") + else: + fp = path + all_xlators = {} + xlator = None + last_xlator = None + while True: + text = fp.readline() + if text == "": + break + text = text.split() + if not len(text): + continue + if text[0] == "volume": + if xlator: + raise RuntimeError, "nested volume definition" + xlator = Translator(text[1]) + continue + if not xlator: + raise RuntimeError, "text outside volume definition" + if text[0] == "type": + xlator.type = text[1] + continue + if text[0] == "option": + xlator.opts[text[1]] = string.join(text[2:]) + continue + if text[0] == "subvolumes": + for sv in text[1:]: + xlator.subvols.append(all_xlators[sv]) + continue + if text[0] == "end-volume": + all_xlators[xlator.name] = xlator + last_xlator = xlator + xlator = None + continue + raise RuntimeError, "unrecognized keyword %s" % text[0] + if xlator: + raise RuntimeError, "unclosed volume definition" + return all_xlators, last_xlator + +def generate (graph, last, stream=sys.stdout): + for sv in last.subvols: + if not sv.dumped: + generate(graph,sv,stream) + print >> stream, "" + sv.dumped = True + print >> stream, "volume %s" % last.name + print >> stream, " type %s" % last.type + for k, v in last.opts.iteritems(): + print >> stream, " option %s %s" % (k, v) + if last.subvols: + print >> stream, " subvolumes %s" % string.join( + [ sv.name for sv in last.subvols ]) + print >> stream, "end-volume" + +def push_filter (graph, old_xl, filt_type, opts={}): + suffix = "-" + old_xl.type.split("/")[1] + if len(old_xl.name) > len(suffix): + if old_xl.name[-len(suffix):] == suffix: + old_xl.name = old_xl.name[:-len(suffix)] + new_xl = Translator(old_xl.name+suffix) + new_xl.type = old_xl.type + new_xl.opts = old_xl.opts + new_xl.subvols = old_xl.subvols + graph[new_xl.name] = new_xl + old_xl.name += ("-" + filt_type.split("/")[1]) + old_xl.type = filt_type + old_xl.opts = opts + old_xl.subvols = [new_xl] + graph[old_xl.name] = old_xl + +def delete (graph, victim): + if len(victim.subvols) != 1: + raise RuntimeError, "attempt to delete non-unary translator" + for xl in graph.itervalues(): + while xl.subvols.count(victim): + i = xl.subvols.index(victim) + xl.subvols[i] = victim.subvols[0] + +if __name__ == "__main__": + graph, last = load(sys.argv[1]) + generate(graph,last) diff --git a/extras/who-wrote-glusterfs/gitdm.aliases b/extras/who-wrote-glusterfs/gitdm.aliases new file mode 100644 index 000000000..784a3e3bc --- /dev/null +++ b/extras/who-wrote-glusterfs/gitdm.aliases @@ -0,0 +1,48 @@ +# +# This is the email aliases file, mapping secondary addresses onto a single, +# canonical address. This file should probably match the contents of .mailmap +# in the root of the git repository. +# +# Format: <alias> <real> + +amar@gluster.com amarts@redhat.com +amar@del.gluster.com amarts@redhat.com +avati@amp.gluster.com avati@redhat.com +avati@blackhole.gluster.com avati@redhat.com +avati@dev.gluster.com avati@redhat.com +avati@gluster.com avati@redhat.com +wheelear@gmail.com awheeler@redhat.com +anush@gluster.com ashetty@redhat.com +csaba@gluster.com csaba@redhat.com +csaba@lowlife.hu csaba@redhat.com +csaba@zresearch.com csaba@redhat.com +harsha@gluster.com fharshav@redhat.com +harsha@zresearch.com fharshav@redhat.com +harsha@dev.gluster.com fharshav@redhat.com +harsha@harshavardhana.net fharshav@redhat.com +kkeithle@f16node1.kkeithle.usersys.redhat.com kkeithle@redhat.com +kaushal@gluster.com kaushal@redhat.com +kaushikbv@gluster.com kbudiger@redhat.com +krishna@gluster.com ksriniva@redhat.com +krishna@zresearch.com ksriniva@redhat.com +krishna@guest-laptop ksriniva@redhat.com +kp@gluster.com kparthas@redhat.com +me@louiszuckerman.com louiszuckerman@gmail.com +msvbhat@gmail.com vbhat@redhat.com +vishwanath@gluster.com vbhat@redhat.com +pavan@dev.gluster.com pavan@gluster.com +zaitcev@yahoo.com zaitcev@kotori.zaitcev.us +pranithk@gluster.com pkarampu@redhat.com +raghavendrabhat@gluster.com raghavendra@redhat.com +raghavendra@gluster.com rgowdapp@redhat.com +raghavendra@zresearch.com rgowdapp@redhat.com +rahulcssjce@gmail.com rahulcs@redhat.com +rajesh@gluster.com rajesh@redhat.com +rajesh.amaravathi@gmail.com rajesh@redhat.com +shehjart@zresearch.com shehjart@gluster.com +venky@gluster.com vshankar@redhat.com +vijay@gluster.com vbellur@redhat.com +vijay@dev.gluster.com vbellur@redhat.com +vijaykumar.koppad@gmail.com vkoppad@redhat.com +vikas@zresearch.com vikas@gluster.com +shishirng@gluster.com sgowda@redhat.com diff --git a/extras/who-wrote-glusterfs/gitdm.config b/extras/who-wrote-glusterfs/gitdm.config new file mode 100644 index 000000000..e1ff2bd5b --- /dev/null +++ b/extras/who-wrote-glusterfs/gitdm.config @@ -0,0 +1,8 @@ +# +# This is the gitdm configuration file for GlusterFS. +# See the gitdm.config in the gitdm repositofy for additional options and +# comments. +# + +EmailAliases gitdm.aliases +EmailMap gitdm.domain-map diff --git a/extras/who-wrote-glusterfs/gitdm.domain-map b/extras/who-wrote-glusterfs/gitdm.domain-map new file mode 100644 index 000000000..f1c305898 --- /dev/null +++ b/extras/who-wrote-glusterfs/gitdm.domain-map @@ -0,0 +1,15 @@ +# +# Here is a set of mappings of domain names onto employer names. +# +active.by ActiveCloud +cern.ch CERN +gluster.com Red Hat +gooddata.com GoodData +hastexo.com hastexo +ibm.com IBM +linbit.com LINBIT +netbsd.org NetBSD +netdirect.ca Net Direct +redhat.com Red Hat +stepping-stone.ch stepping stone GmbH +zresearch.com Red Hat diff --git a/extras/who-wrote-glusterfs/who-wrote-glusterfs.sh b/extras/who-wrote-glusterfs/who-wrote-glusterfs.sh new file mode 100755 index 000000000..487f5874b --- /dev/null +++ b/extras/who-wrote-glusterfs/who-wrote-glusterfs.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# +# Gather statistics on "Who wrote GlusterFS". The idea comes from the excellent +# articles on http://lwn.net/ named "Who wrote <linux-version>?". +# +# gitdm comes from git://git.lwn.net/gitdm.git by Jonathan Corbet. +# +# Confguration files used: +# - gitdm.config: main configuration file, pointing to the others +# - gitdm.aliases: merge users with different emailaddresses into one +# - gitdm.domain-map: map domain names from emailaddresses to companies +# + +DIRNAME=$(dirname $0) + +GITDM_REPO=git://git.lwn.net/gitdm.git +GITDM_DIR=${DIRNAME}/gitdm +GITDM_CMD="python ${GITDM_DIR}/gitdm" + +error() +{ + local ret=${?} + printf "${@}\n" > /dev/stderr + return ${ret} +} + +check_gitdm() +{ + if [ ! -e "${GITDM_DIR}/gitdm" ] + then + git clone --quiet git://git.lwn.net/gitdm.git ${DIRNAME}/gitdm + fi +} + +# The first argument is the revision-range (see 'git rev-list --help'). +# REV can be empty, and the statistics will be calculated over the whole +# current branch. +REV=${1} +shift +# all remaining options are passed to gitdm, see the gitdm script for an +# explanation of the accepted options. +GITDM_OPTS=${@} + +if ! check_gitdm +then + error "Could not find 'gitdm', exiting..." + exit 1 +fi + +git log --numstat -M ${REV} | ${GITDM_CMD} -b ${DIRNAME} -n ${GITDM_OPTS} |
