- add create_key function (fails if key already exists)
- add setkey_match_prev function (set value if previous value matches)
- add missing quotes
- add etcd3.plugin
---
src/osaf/consensus/plugins/etcd.plugin | 86 +++++++-
src/osaf/consensus/plugins/etcd3.plugin | 366 +++++++++++++++++++++++++++++++
src/osaf/consensus/plugins/sample.plugin | 67 +++++-
3 files changed, 501 insertions(+), 18 deletions(-)
create mode 100644 src/osaf/consensus/plugins/etcd3.plugin
diff --git a/src/osaf/consensus/plugins/etcd.plugin
b/src/osaf/consensus/plugins/etcd.plugin
index 586059b32..6ed85ac92 100644
--- a/src/osaf/consensus/plugins/etcd.plugin
+++ b/src/osaf/consensus/plugins/etcd.plugin
@@ -29,7 +29,7 @@ readonly etcd_timeout="5s"
# 0 - success, <value> is echoed to stdout
# non-zero - failure
get() {
- readonly key=$1
+ readonly key="$1"
if value=$(etcdctl $etcd_options --timeout $etcd_timeout get
"$directory$key" 2>&1)
then
@@ -49,8 +49,8 @@ get() {
# 0 - success
# non-zero - failure
setkey() {
- readonly key=$1
- readonly value=$2
+ readonly key="$1"
+ readonly value="$2"
if etcdctl $etcd_options --timeout $etcd_timeout set "$directory$key" \
"$value" >/dev/null
@@ -61,6 +61,58 @@ setkey() {
fi
}
+# create
+# create <key> and set to <value> in key-value store. Fails if the key
+# already exists
+# params:
+# $1 - <key>
+# $2 - <value>
+# returns:
+# 0 - success
+# 1 - already exists
+# 2 or above - other failure
+create_key() {
+ readonly key="$1"
+ readonly value="$2"
+
+ if output=$(etcdctl $etcd_options --timeout $etcd_timeout mk
"$directory$key" \
+ "$value" 2>&1)
+ then
+ return 0
+ else
+ if echo $output | grep "already exists"
+ then
+ return 1
+ fi
+ fi
+
+ return 2
+}
+
+# set
+# set <key> to <value> in key-value store, if the existing value matches
+# <prev>
+# params:
+# $1 - <key>
+# $2 - <value>
+# $3 - <prev>
+# returns:
+# 0 - success
+# non-zero - failure
+setkey_match_prev() {
+ readonly key="$1"
+ readonly value="$2"
+ readonly prev="$3"
+
+ if etcdctl $etcd_options --timeout $etcd_timeout set "$directory$key" \
+ "$value" --swap-with-value "$prev" >/dev/null
+ then
+ return 0
+ else
+ return 1
+ fi
+}
+
# erase
# erase <key> in key-value store
# params:
@@ -69,7 +121,7 @@ setkey() {
# 0 - success
# non-zero - failure
erase() {
- readonly key=$1
+ readonly key="$1"
if etcdctl $etcd_options --timeout $etcd_timeout \
rm "$directory$key" >/dev/null 2>&1
@@ -90,8 +142,8 @@ erase() {
# 2 or above - other failure
# NOTE: if lock is already acquired by <owner>, then timeout is extended
lock() {
- readonly owner=$1
- readonly timeout=$2
+ readonly owner="$1"
+ readonly timeout="$2"
if etcdctl $etcd_options --timeout $etcd_timeout \
mk "$directory$keyname" "$owner" \
@@ -145,7 +197,7 @@ lock_owner() {
# 2 or above - other failure
#
unlock() {
- readonly owner=$1
+ readonly owner="$1"
readonly forced=${2:-false}
if [ "$forced" = false ]; then
@@ -185,7 +237,7 @@ unlock() {
# 0 - success, <new_value> is echoed to stdout
# non-zero - failure
watch() {
- readonly key=$1
+ readonly key="$1"
if value=$(etcdctl $etcd_options --timeout $etcd_timeout \
watch "$directory$key" 2>&1)
@@ -216,6 +268,22 @@ case "$1" in
setkey "$2" "$3"
exit $?
;;
+ set_if_prev)
+ if [ "$#" -ne 4 ]; then
+ echo "Usage: $0 set <key> <value> <previous_value>"
+ exit 1
+ fi
+ setkey_match_prev "$2" "$3" "$4"
+ exit $?
+ ;;
+ create)
+ if [ "$#" -ne 3 ]; then
+ echo "Usage: $0 create <key> <value>"
+ exit 1
+ fi
+ create_key "$2" "$3"
+ exit $?
+ ;;
erase)
if [ "$#" -ne 2 ]; then
echo "Usage: $0 erase <key>"
@@ -269,7 +337,7 @@ case "$1" in
exit $?
;;
*)
- echo "Usage: $0 {get|set|erase|lock|unlock|lock_owner|watch|watch_lock}"
+ echo "Usage: $0
{get|set|create|set_if_prev|erase|lock|unlock|lock_owner|watch|watch_lock}"
;;
esac
diff --git a/src/osaf/consensus/plugins/etcd3.plugin
b/src/osaf/consensus/plugins/etcd3.plugin
new file mode 100644
index 000000000..451440567
--- /dev/null
+++ b/src/osaf/consensus/plugins/etcd3.plugin
@@ -0,0 +1,366 @@
+#!/usr/bin/env bash
+# -*- OpenSAF -*-
+#
+# (C) Copyright 2018 Ericsson AB 2018 - All Rights Reserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed
+# under the GNU Lesser General Public License Version 2.1, February 1999.
+# The complete license can be accessed from the following location:
+# http://opensource.org/licenses/lgpl-license.php
+# See the Copying file included with the OpenSAF distribution for full
+# licensing terms.
+#
+# Please note: this API is subject to change and may be modified
+# in a future version of OpenSAF. Future API versions may not be
+# backward compatible. This plugin may need to be adapted.
+
+readonly keyname="opensaf_consensus_lock"
+readonly directory="/opensaf/"
+readonly etcd_options=""
+readonly etcd_timeout="5s"
+
+export ETCDCTL_API=3
+
+# get
+# retrieve <value> of <key> from key-value store
+# params:
+# $1 - <key>
+# returns:
+# 0 - success, <value> is echoed to stdout
+# non-zero - failure
+get() {
+ readonly key="$1"
+
+ if value=$(etcdctl $etcd_options --dial-timeout $etcd_timeout get
"$directory$key" | tail -n1)
+ then
+ echo "$value"
+ return 0
+ else
+ return 1
+ fi
+}
+
+# set
+# set <key> to <value> in key-value store
+# params:
+# $1 - <key>
+# $2 - <value>
+# returns:
+# 0 - success
+# non-zero - failure
+setkey() {
+ readonly key="$1"
+ readonly value="$2"
+
+ if etcdctl $etcd_options --dial-timeout $etcd_timeout put "$directory$key" \
+ "$value" >/dev/null
+ then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# create
+# create <key> and set to <value> in key-value store. Fails if the key
+# already exists
+# params:
+# $1 - <key>
+# $2 - <value>
+# returns:
+# 0 - success
+# 1 - already exists
+# 2 or above - other failure
+create_key() {
+ readonly key="$1"
+ readonly value="$2"
+ # first try to create the key
+ transaction="create(\""$directory$key"\") = \"0\"
+
+ put \""$directory$key"\" \""$value"\"
+
+ "
+ output=$(etcdctl $etcd_options --dial-timeout $etcd_timeout txn <<<
"$transaction")
+ if [[ "$output" == *"OK"* ]]; then
+ return 0
+ fi
+
+ if output=$(etcdctl $etcd_options --dial-timeout $etcd_timeout get
"$directory$key" | tail -n1)
+ then
+ return 1
+ else
+ return 2
+ fi
+}
+
+# set
+# set <key> to <value> in key-value store, if the existing value matches
+# <prev>
+# params:
+# $1 - <key>
+# $2 - <value>
+# $3 - <prev>
+# returns:
+# 0 - success
+# non-zero - failure
+setkey_match_prev() {
+ readonly key="$1"
+ readonly value="$2"
+ readonly prev="$3"
+
+ # key already exists, make sure it's empty
+ transaction="value(\""$directory$key"\") = \"$prev\"
+
+ put \""$directory$key"\" \""$value"\"
+
+ "
+ output=$(etcdctl $etcd_options --dial-timeout $etcd_timeout txn <<<
"$transaction")
+ if [[ "$output" == *"OK"* ]]; then
+ return 0
+ fi
+
+ return 1
+}
+
+# erase
+# erase <key> in key-value store
+# params:
+# $1 - <key>
+# returns:
+# 0 - success
+# non-zero - failure
+erase() {
+ readonly key="$1"
+
+ if etcdctl $etcd_options --dial-timeout $etcd_timeout \
+ del "$directory$key" >/dev/null 2>&1
+ then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# lock
+# params:
+# $1 - <owner>, owner of the lock is set to this
+# $2 - <timeout>, will automatically unlock after <timeout> seconds
+# returns:
+# 0 - success
+# 1 - the lock is owned by someone else
+# 2 or above - other failure
+# NOTE: if lock is already acquired by <owner>, then timeout is extended
+# TODO: timeout not yet implemented
+lock() {
+ readonly owner="$1"
+ readonly timeout="$2"
+ # first try to create the key
+ transaction="create(\""$directory$keyname"\") = \"0\"
+
+ put \""$directory$keyname"\" \""$owner"\"
+
+ "
+ output=$(etcdctl $etcd_options --dial-timeout $etcd_timeout txn <<<
"$transaction")
+ if [[ "$output" == *"OK"* ]]; then
+ return 0
+ fi
+
+ # key already exists, make sure it's empty
+ transaction="value(\""$directory$keyname"\") = \"\"
+
+ put \""$directory$keyname"\" \""$owner"\"
+
+ "
+ output=$(etcdctl $etcd_options --dial-timeout $etcd_timeout txn <<<
"$transaction")
+ if [[ "$output" == *"OK"* ]]; then
+ return 0
+ fi
+
+ current_owner=$(etcdctl $etcd_options --dial-timeout $etcd_timeout get
"$directory$keyname" | tail -n1)
+ # see if we already hold the lock
+ if [ "$current_owner" = "$owner" ]; then
+ return 0
+ fi
+
+ if [ -n "$current_owner" ]; then
+ # owned by someone else
+ echo "$current_owner"
+ return 1
+ fi
+
+ # for troubleshooting
+ echo "$output"
+ return 2
+}
+
+# get
+# retrieve <owner> of lock
+# params:
+# none
+# returns:
+# 0 - success, <owner> is echoed to stdout
+# non-zero - failure or not locked
+lock_owner() {
+ get "$keyname"
+ return $?
+}
+
+# unlock
+# params:
+# $1 - owner
+# $2 - <forced>
+# - (optional parameter)
+# - if set 'true', will unlock even if lock is not held by node
+# - defaults to 'false'
+# returns:
+# 0 - success
+# 1 - the lock is owned by someone else
+# 2 or above - other failure
+#
+unlock() {
+ readonly owner="$1"
+ readonly forced=${2:-false}
+ if [ "$forced" = false ]; then
+ # unlock on succeeds if owner matches
+ transaction="value(\""$directory$keyname"\") = \""$owner"\"
+
+ put \""$directory$keyname"\" \"\"
+
+ "
+ output=$(etcdctl $etcd_options --dial-timeout $etcd_timeout txn <<<
"$transaction")
+ if [[ "$output" == *"OK"* ]]; then
+ return 0
+ fi
+
+ # failed! check we own the lock
+ current_owner=lock_owner
+ if [[ "$owner" != "$current_owner" && -n "$current_owner" ]]; then
+ # for troubleshooting
+ echo "$output"
+ echo "$current_owner"
+ return 1
+ fi
+
+ # for troubleshooting
+ echo "$output"
+ return 2
+ fi
+
+ if etcdctl $etcd_options --dial-timeout $etcd_timeout \
+ del "$directory$keyname" >/dev/null 2>&1
+ then
+ return 0
+ else
+ return 2
+ fi
+}
+
+# watch
+# watch <key> in key-value store
+# params:
+# $1 - <key>
+# returns:
+# 0 - success, <new_value> is echoed to stdout
+# non-zero - failure
+watch() {
+ readonly watch_key="$1"
+ etcdctl $etcd_options --dial-timeout $etcd_timeout \
+ watch "$directory$watch_key" | grep -m0 \"\" 2>&1
+ get "$watch_key"
+ return 0
+}
+
+# argument parsing
+case "$1" in
+ get)
+ if [ "$#" -ne 2 ]; then
+ echo "Usage: $0 get <key>"
+ exit 1
+ fi
+ get "$2"
+ exit $?
+ ;;
+ set)
+ if [ "$#" -ne 3 ]; then
+ echo "Usage: $0 set <key> <value>"
+ exit 1
+ fi
+ setkey "$2" "$3"
+ exit $?
+ ;;
+ set_if_prev)
+ if [ "$#" -ne 4 ]; then
+ echo "Usage: $0 set <key> <value> <previous_value>"
+ exit 1
+ fi
+ setkey_match_prev "$2" "$3" "$4"
+ exit $?
+ ;;
+ create)
+ if [ "$#" -ne 3 ]; then
+ echo "Usage: $0 create <key> <value>"
+ exit 1
+ fi
+ create_key "$2" "$3"
+ exit $?
+ ;;
+ erase)
+ if [ "$#" -ne 2 ]; then
+ echo "Usage: $0 erase <key>"
+ exit 1
+ fi
+ erase "$2"
+ exit $?
+ ;;
+ lock)
+ if [ "$#" -ne 3 ]; then
+ echo "Usage: $0 lock <owner> <timeout>"
+ exit 1
+ fi
+ lock "$2" "$3"
+ exit $?
+ ;;
+ lock_owner)
+ if [ "$#" -ne 1 ]; then
+ echo "Usage: $0 lock_owner"
+ exit 1
+ fi
+ lock_owner
+ exit $?
+ ;;
+ unlock)
+ if [ "$#" -eq 2 ]; then
+ unlock "$2"
+ exit $?
+ elif [ "$#" -eq 3 ] && [ "$3" = "--force" ]; then
+ unlock "$2" 1
+ exit $?
+ else
+ echo "Usage: $0 unlock <owner> [--force]"
+ exit 1
+ fi
+ ;;
+ watch)
+ if [ "$#" -ne 2 ]; then
+ echo "Usage: $0 watch <key>"
+ exit 1
+ fi
+ watch "$2"
+ exit $?
+ ;;
+ watch_lock)
+ if [ "$#" -ne 1 ]; then
+ echo "Usage: $0 watch_lock"
+ exit 1
+ fi
+ watch "$keyname"
+ exit $?
+ ;;
+ *)
+ echo "Usage: $0
{get|set|create|set_if_prev|erase|lock|unlock|lock_owner|watch|watch_lock}"
+ ;;
+esac
+
+exit 1
diff --git a/src/osaf/consensus/plugins/sample.plugin
b/src/osaf/consensus/plugins/sample.plugin
index 433d23d98..445cf8d84 100644
--- a/src/osaf/consensus/plugins/sample.plugin
+++ b/src/osaf/consensus/plugins/sample.plugin
@@ -26,7 +26,7 @@ readonly keyname="opensaf_consensus_lock"
# 0 - success, <value> is echoed to stdout
# non-zero - failure
get() {
- readonly key=$1
+ readonly key="$1"
...
}
@@ -39,8 +39,41 @@ get() {
# 0 - success
# non-zero - failure
setkey() {
- readonly key=$1
- readonly value=$2
+ readonly key="$1"
+ readonly value="$2"
+ ...
+}
+
+# create
+# create <key> and set to <value> in key-value store. Fails if the key
+# already exists
+# params:
+# $1 - <key>
+# $2 - <value>
+# returns:
+# 0 - success
+# 1 - already exists
+# 2 or above - other failure
+create_key() {
+ readonly key="$1"
+ readonly value="$2"
+ ...
+}
+
+# set
+# set <key> to <value> in key-value store, if the existing value matches
+# <prev>
+# params:
+# $1 - <key>
+# $2 - <value>
+# $3 - <prev>
+# returns:
+# 0 - success
+# non-zero - failure
+setkey_match_prev() {
+ readonly key="$1"
+ readonly value="$2"
+ readonly prev="$3"
...
}
@@ -52,7 +85,7 @@ setkey() {
# 0 - success
# non-zero - failure
erase() {
- readonly key=$1
+ readonly key="$1"
...
}
@@ -64,8 +97,8 @@ erase() {
# 0 - success
# non-zero - failure
lock() {
- readonly owner=$1
- readonly timeout=$2
+ readonly owner="$1"
+ readonly timeout="$2"
...
}
@@ -92,7 +125,7 @@ lock_owner() {
# 1 - the lock is owned by someone else
# 2 or above - other failure#
unlock() {
- readonly owner=$1
+ readonly owner="$1"
readonly forced=${2:-false}
...
}
@@ -105,7 +138,7 @@ unlock() {
# 0 - success, <new_value> is echoed to stdout
# non-zero - failure
watch() {
- readonly key=$1
+ readonly key="$1"
..
}
@@ -127,6 +160,22 @@ case "$1" in
setkey "$2" "$3"
exit $?
;;
+ set_if_prev)
+ if [ "$#" -ne 4 ]; then
+ echo "Usage: $0 set <key> <value> <previous_value>"
+ exit 1
+ fi
+ setkey_match_prev "$2" "$3" "$4"
+ exit $?
+ ;;
+ create)
+ if [ "$#" -ne 3 ]; then
+ echo "Usage: $0 create <key> <value>"
+ exit 1
+ fi
+ create_key "$2" "$3"
+ exit $?
+ ;;
erase)
if [ "$#" -ne 2 ]; then
echo "Usage: $0 erase <key>"
@@ -180,7 +229,7 @@ case "$1" in
exit $?
;;
*)
- echo "Usage: $0 {get|set|erase|lock|unlock|lock_owner|watch|watch_lock}"
+ echo "Usage: $0
{get|set|create|set_if_prev|erase|lock|unlock|lock_owner|watch|watch_lock}"
;;
esac
--
2.14.1
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel