Blame SOURCES/bz2111998-fence_ibm_vpc-add-token-cache-support.patch

e4b78c
From bccac64a5135815ada30d385ab573409f1176905 Mon Sep 17 00:00:00 2001
e4b78c
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
e4b78c
Date: Thu, 7 Jul 2022 14:18:21 +0200
e4b78c
Subject: [PATCH 1/3] build: make xml-check: ignore detected paths in *_file
e4b78c
 parameters not matching saved metadata
e4b78c
e4b78c
---
e4b78c
 make/agentpycheck.mk                          | 2 +-
e4b78c
 83 files changed, 1 insertion(+), 108 deletions(-)
e4b78c
e4b78c
diff --git a/make/agentpycheck.mk b/make/agentpycheck.mk
e4b78c
index f686c4c89..4044dbad3 100644
e4b78c
--- a/make/agentpycheck.mk
e4b78c
+++ b/make/agentpycheck.mk
e4b78c
@@ -1,5 +1,5 @@
e4b78c
 DATADIR:=$(abs_top_srcdir)/tests/data/metadata
e4b78c
-AWK_VAL='BEGIN {store=-1} /name=".*_path"/ {store=2} {if (store!=0) {print}; store--}'
e4b78c
+AWK_VAL='BEGIN {store=-1} /name=".*_path"/ || /name=".*_file"/ {store=2} {if (store!=0) {print}; store--}'
e4b78c
 
e4b78c
 TEST_TARGET=$(filter-out $(TEST_TARGET_SKIP),$(TARGET))
e4b78c
e4b78c
From 1b7f3cc431ca53962506e6d96e7a4938c4388416 Mon Sep 17 00:00:00 2001
e4b78c
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
e4b78c
Date: Fri, 1 Jul 2022 13:29:16 +0200
e4b78c
Subject: [PATCH 2/3] build: add FENCETMPDIR for state files
e4b78c
e4b78c
---
e4b78c
 Makefile.am                  |  3 ++-
e4b78c
 configure.ac                 | 30 ++++++++++++++++++++++++++++++
e4b78c
 m4/PKG_CHECK_VAR.m4          | 24 ++++++++++++++++++++++++
e4b78c
 make/fencebuild.mk           |  1 +
e4b78c
 systemd/Makefile.am          | 24 ++++++++++++++++++++++++
e4b78c
 systemd/fence-agents.conf.in |  1 +
e4b78c
 7 files changed, 97 insertions(+), 2 deletions(-)
e4b78c
 create mode 100644 m4/PKG_CHECK_VAR.m4
e4b78c
 create mode 100644 systemd/Makefile.am
e4b78c
 create mode 100644 systemd/fence-agents.conf.in
e4b78c
e4b78c
diff --git a/Makefile.am b/Makefile.am
e4b78c
index c1091b93a..1d115e5aa 100644
e4b78c
--- a/Makefile.am
e4b78c
+++ b/Makefile.am
e4b78c
@@ -23,7 +23,7 @@ TARFILES		= $(PACKAGE_NAME)-$(VERSION).tar.bz2 \
e4b78c
 
e4b78c
 ACLOCAL_AMFLAGS		= -I m4
e4b78c
 
e4b78c
-SUBDIRS			= lib agents doc
e4b78c
+SUBDIRS			= lib agents doc systemd
e4b78c
 
e4b78c
 .PHONY: $(SUBDIRS)
e4b78c
 
e4b78c
@@ -34,6 +34,7 @@ doc: agents
e4b78c
 install-exec-local:
e4b78c
 			$(INSTALL) -d $(DESTDIR)/$(LOGDIR)
e4b78c
 			$(INSTALL) -d $(DESTDIR)/$(CLUSTERVARRUN)
e4b78c
+			$(INSTALL) -d -m 1755 $(DESTDIR)$(FENCETMPDIR)
e4b78c
 
e4b78c
 uninstall-local:
e4b78c
 			rmdir $(DESTDIR)/$(LOGDIR) || :;
e4b78c
diff --git a/configure.ac b/configure.ac
e4b78c
index 1bad8e3b0..d7afb8dbe 100644
e4b78c
--- a/configure.ac
e4b78c
+++ b/configure.ac
e4b78c
@@ -135,10 +135,38 @@ AC_ARG_WITH([agents],
e4b78c
 	[ AGENTS_LIST="$withval" ],
e4b78c
 	[ AGENTS_LIST="all" ])
e4b78c
 
e4b78c
+FENCETMPDIR=${localstatedir}/run/fence-agents
e4b78c
+AC_ARG_WITH(fencetmpdir,
e4b78c
+    [  --with-fencetmpdir=DIR      directory for fence agents state files [${FENCETMPDIR}]],
e4b78c
+    [ FENCETMPDIR="$withval" ])
e4b78c
+
e4b78c
+# Expand $prefix
e4b78c
+eval FENCETMPDIR="`eval echo ${FENCETMPDIR}`"
e4b78c
+AC_DEFINE_UNQUOTED(FENCETMPDIR,"$FENCETMPDIR", Where Fence agents keep state files)
e4b78c
+AC_SUBST(FENCETMPDIR)
e4b78c
+
e4b78c
+
e4b78c
 if test "x$AGENTS_LIST" = x; then
e4b78c
 	AC_ERROR([No agents selected])
e4b78c
 fi
e4b78c
 
e4b78c
+# PKG_CHECK_MODULES will fail if systemd is not found by default, so make sure
e4b78c
+# we set the proper vars and deal with it
e4b78c
+PKG_CHECK_MODULES([systemd], [systemd], [HAS_SYSTEMD=yes], [HAS_SYSTEMD=no])
e4b78c
+if test "x$HAS_SYSTEMD" == "xyes"; then
e4b78c
+	PKG_CHECK_VAR([SYSTEMD_TMPFILES_DIR], [systemd], [tmpfilesdir])
e4b78c
+	if test "x$SYSTEMD_TMPFILES_DIR" == "x"; then
e4b78c
+		AC_MSG_ERROR([Unable to detect systemd tmpfiles directory automatically])
e4b78c
+	fi
e4b78c
+
e4b78c
+	# sanitize systed vars when using non standard prefix
e4b78c
+	if test "$prefix" != "/usr"; then
e4b78c
+		SYSTEMD_TMPFILES_DIR="$prefix/$SYSTEMD_TMPFILES_DIR"
e4b78c
+		AC_SUBST([SYSTEMD_TMPFILES_DIR])
e4b78c
+	fi
e4b78c
+fi
e4b78c
+AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$HAS_SYSTEMD" == xyes ])
e4b78c
+
e4b78c
 FENCE_KDUMP=0
e4b78c
 if echo "$AGENTS_LIST" | grep -q -E "all|kdump"; then
e4b78c
 	case "$host_os" in
e4b78c
@@ -552,6 +580,8 @@ AC_CONFIG_FILES([Makefile
e4b78c
 		 agents/Makefile
e4b78c
 		 lib/Makefile
e4b78c
 		 doc/Makefile
e4b78c
+		 systemd/Makefile
e4b78c
+		 systemd/fence-agents.conf
e4b78c
 		 agents/virt/Makefile
e4b78c
 		 agents/virt/config/Makefile
e4b78c
 		 agents/virt/common/Makefile
e4b78c
diff --git a/m4/PKG_CHECK_VAR.m4 b/m4/PKG_CHECK_VAR.m4
e4b78c
new file mode 100644
e4b78c
index 000000000..2221a69eb
e4b78c
--- /dev/null
e4b78c
+++ b/m4/PKG_CHECK_VAR.m4
e4b78c
@@ -0,0 +1,24 @@
e4b78c
+dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
e4b78c
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
e4b78c
+dnl -------------------------------------------
e4b78c
+dnl Since: 0.28
e4b78c
+dnl
e4b78c
+dnl Retrieves the value of the pkg-config variable for the given module.
e4b78c
+dnl
e4b78c
+dnl Origin (declared license: GPLv2+ with less restrictive exception):
e4b78c
+dnl https://cgit.freedesktop.org/pkg-config/tree/pkg.m4.in?h=pkg-config-0.29.1#n261
e4b78c
+dnl (AS_VAR_COPY replaced with backward-compatible equivalent and guard
e4b78c
+dnl to prefer system-wide variant by Jan Pokorny <jpokorny@redhat.com>)
e4b78c
+
e4b78c
+m4_ifndef([PKG_CHECK_VAR],[
e4b78c
+AC_DEFUN([PKG_CHECK_VAR],
e4b78c
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
e4b78c
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
e4b78c
+
e4b78c
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
e4b78c
+dnl AS_VAR_COPY([$1], [pkg_cv_][$1])
e4b78c
+$1=AS_VAR_GET([pkg_cv_][$1])
e4b78c
+
e4b78c
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
e4b78c
+])dnl PKG_CHECK_VAR
e4b78c
+])dnl m4_ifndef
e4b78c
diff --git a/make/fencebuild.mk b/make/fencebuild.mk
e4b78c
index 762db62c4..9a3c6d6dd 100644
e4b78c
--- a/make/fencebuild.mk
e4b78c
+++ b/make/fencebuild.mk
e4b78c
@@ -8,6 +8,7 @@ define gen_agent_from_py
e4b78c
 		-e 's#@''LOGDIR@#${LOGDIR}#g' \
e4b78c
 		-e 's#@''SBINDIR@#${sbindir}#g' \
e4b78c
 		-e 's#@''LIBEXECDIR@#${libexecdir}#g' \
e4b78c
+		-e 's#@''FENCETMPDIR@#${FENCETMPDIR}#g' \
e4b78c
 		-e 's#@''IPMITOOL_PATH@#${IPMITOOL_PATH}#g' \
e4b78c
 		-e 's#@''OPENSTACK_PATH@#${OPENSTACK_PATH}#g' \
e4b78c
 		-e 's#@''AMTTOOL_PATH@#${AMTTOOL_PATH}#g' \
e4b78c
diff --git a/systemd/Makefile.am b/systemd/Makefile.am
e4b78c
new file mode 100644
e4b78c
index 000000000..aa3a01679
e4b78c
--- /dev/null
e4b78c
+++ b/systemd/Makefile.am
e4b78c
@@ -0,0 +1,24 @@
e4b78c
+#
e4b78c
+# Copyright (C) 2017 Oyvind Albrigtsen
e4b78c
+#
e4b78c
+# This program is free software; you can redistribute it and/or
e4b78c
+# modify it under the terms of the GNU General Public License
e4b78c
+# as published by the Free Software Foundation; either version 2
e4b78c
+# of the License, or (at your option) any later version.
e4b78c
+# 
e4b78c
+# This program is distributed in the hope that it will be useful,
e4b78c
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
e4b78c
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e4b78c
+# GNU General Public License for more details.
e4b78c
+# 
e4b78c
+# You should have received a copy of the GNU General Public License
e4b78c
+# along with this program; if not, write to the Free Software
e4b78c
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
e4b78c
+#
e4b78c
+
e4b78c
+MAINTAINERCLEANFILES    = Makefile.in
e4b78c
+
e4b78c
+if HAVE_SYSTEMD
e4b78c
+tmpfilesdir		= $(SYSTEMD_TMPFILES_DIR)
e4b78c
+tmpfiles_DATA		= fence-agents.conf
e4b78c
+endif
e4b78c
diff --git a/systemd/fence-agents.conf.in b/systemd/fence-agents.conf.in
e4b78c
new file mode 100644
e4b78c
index 000000000..4181287da
e4b78c
--- /dev/null
e4b78c
+++ b/systemd/fence-agents.conf.in
e4b78c
@@ -0,0 +1 @@
e4b78c
+d @FENCETMPDIR@ 1755 root root
e4b78c
e4b78c
From d5a12d9c30b66eb8720e037c4dce5fe0f3ad7dbb Mon Sep 17 00:00:00 2001
e4b78c
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
e4b78c
Date: Thu, 30 Jun 2022 13:20:37 +0200
e4b78c
Subject: [PATCH 3/3] fence_ibm_vpc: add token cache support
e4b78c
e4b78c
---
e4b78c
 agents/ibm_vpc/fence_ibm_vpc.py         | 126 ++++++++++++++++++++----
e4b78c
 tests/data/metadata/fence_ibm_vpc.xml   |   4 +
e4b78c
 3 files changed, 110 insertions(+), 22 deletions(-)
e4b78c
e4b78c
 def auth_connect(opt):
e4b78c
diff --git a/agents/ibm_vpc/fence_ibm_vpc.py b/agents/ibm_vpc/fence_ibm_vpc.py
e4b78c
index 3da3ce056..847010584 100755
e4b78c
--- a/agents/ibm_vpc/fence_ibm_vpc.py
e4b78c
+++ b/agents/ibm_vpc/fence_ibm_vpc.py
e4b78c
@@ -4,9 +4,10 @@
e4b78c
 import pycurl, io, json
e4b78c
 import logging
e4b78c
 import atexit
e4b78c
+import hashlib
e4b78c
 sys.path.append("@FENCEAGENTSLIBDIR@")
e4b78c
 from fencing import *
e4b78c
-from fencing import fail, run_delay, EC_LOGIN_DENIED, EC_STATUS
e4b78c
+from fencing import fail, run_delay, EC_LOGIN_DENIED, EC_STATUS, EC_GENERIC_ERROR
e4b78c
 
e4b78c
 state = {
e4b78c
 	 "running": "on",
e4b78c
@@ -22,7 +23,7 @@ def get_list(conn, options):
e4b78c
 
e4b78c
 	try:
e4b78c
 		command = "instances?version=2021-05-25&generation=2&limit={}".format(options["--limit"])
e4b78c
-		res = send_command(conn, command)
e4b78c
+		res = send_command(conn, options, command)
e4b78c
 	except Exception as e:
e4b78c
 		logging.debug("Failed: Unable to get list: {}".format(e))
e4b78c
 		return outlets
e4b78c
@@ -38,7 +39,7 @@ def get_list(conn, options):
e4b78c
 def get_power_status(conn, options):
e4b78c
 	try:
e4b78c
 		command = "instances/{}?version=2021-05-25&generation=2".format(options["--plug"])
e4b78c
-		res = send_command(conn, command)
e4b78c
+		res = send_command(conn, options, command)
e4b78c
 		result = state[res["status"]]
e4b78c
 		if options["--verbose-level"] > 1:
e4b78c
 			logging.debug("Result:\n{}".format(json.dumps(res, indent=2)))
e4b78c
@@ -57,27 +58,71 @@ def set_power_status(conn, options):
e4b78c
 
e4b78c
 	try:
e4b78c
 		command = "instances/{}/actions?version=2021-05-25&generation=2".format(options["--plug"])
e4b78c
-		send_command(conn, command, "POST", action, 201)
e4b78c
+		send_command(conn, options, command, "POST", action, 201)
e4b78c
 	except Exception as e:
e4b78c
 		logging.debug("Failed: Unable to set power to {} for {}".format(options["--action"], e))
e4b78c
 		fail(EC_STATUS)
e4b78c
 
e4b78c
 def get_bearer_token(conn, options):
e4b78c
+	import os, errno
e4b78c
+
e4b78c
+	try:
e4b78c
+		# FIPS requires usedforsecurity=False and might not be
e4b78c
+		# available on all distros: https://bugs.python.org/issue9216
e4b78c
+		hash = hashlib.sha256(options["--apikey"].encode("utf-8"), usedforsecurity=False).hexdigest()
e4b78c
+	except (AttributeError, TypeError):
e4b78c
+		hash = hashlib.sha256(options["--apikey"].encode("utf-8")).hexdigest()
e4b78c
+	file_path = options["--token-file"].replace("[hash]", hash)
e4b78c
 	token = None
e4b78c
+
e4b78c
+	if not os.path.isdir(os.path.dirname(file_path)):
e4b78c
+		os.makedirs(os.path.dirname(file_path))
e4b78c
+
e4b78c
+	# For security, remove file with potentially elevated mode
e4b78c
 	try:
e4b78c
-		conn.setopt(pycurl.HTTPHEADER, [
e4b78c
-			"Content-Type: application/x-www-form-urlencoded",
e4b78c
-			"User-Agent: curl",
e4b78c
-		])
e4b78c
-		token = send_command(conn, "https://iam.cloud.ibm.com/identity/token", "POST", "grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={}".format(options["--apikey"]))["access_token"]
e4b78c
-	except Exception:
e4b78c
-		logging.error("Failed: Unable to authenticate")
e4b78c
-		fail(EC_LOGIN_DENIED)
e4b78c
+		os.remove(file_path)
e4b78c
+	except OSError:
e4b78c
+		pass
e4b78c
+
e4b78c
+	try:
e4b78c
+		oldumask = os.umask(0)
e4b78c
+		file_handle = os.open(file_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
e4b78c
+	except OSError as e:
e4b78c
+		if e.errno == errno.EEXIST:  # Failed as the file already exists.
e4b78c
+			logging.error("Failed: File already exists: {}".format(e))
e4b78c
+			sys.exit(EC_GENERIC_ERROR)
e4b78c
+		else:  # Something unexpected went wrong
e4b78c
+			logging.error("Failed: Unable to open file: {}".format(e))
e4b78c
+			sys.exit(EC_GENERIC_ERROR)
e4b78c
+	else:  # No exception, so the file must have been created successfully.
e4b78c
+		with os.fdopen(file_handle, 'w') as file_obj:
e4b78c
+			try:
e4b78c
+				conn.setopt(pycurl.HTTPHEADER, [
e4b78c
+					"Content-Type: application/x-www-form-urlencoded",
e4b78c
+					"User-Agent: curl",
e4b78c
+				])
e4b78c
+				token = send_command(conn, options, "https://iam.cloud.ibm.com/identity/token", "POST", "grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={}".format(options["--apikey"]))["access_token"]
e4b78c
+			except Exception as e:
e4b78c
+				logging.error("Failed: Unable to authenticate: {}".format(e))
e4b78c
+				fail(EC_LOGIN_DENIED)
e4b78c
+			file_obj.write(token)
e4b78c
+	finally:
e4b78c
+		os.umask(oldumask)
e4b78c
 
e4b78c
 	return token
e4b78c
 
e4b78c
+def set_bearer_token(conn, bearer_token):
e4b78c
+	conn.setopt(pycurl.HTTPHEADER, [
e4b78c
+		"Content-Type: application/json",
e4b78c
+		"Authorization: Bearer {}".format(bearer_token),
e4b78c
+		"User-Agent: curl",
e4b78c
+	])
e4b78c
+
e4b78c
+	return conn
e4b78c
+
e4b78c
 def connect(opt):
e4b78c
 	conn = pycurl.Curl()
e4b78c
+	bearer_token = ""
e4b78c
 
e4b78c
 	## setup correct URL
e4b78c
 	conn.base_url = "https://" + opt["--region"] + ".iaas.cloud.ibm.com/v1/"
e4b78c
@@ -91,21 +136,28 @@ def connect(opt):
e4b78c
 	conn.setopt(pycurl.PROXY, "{}".format(opt["--proxy"]))
e4b78c
 
e4b78c
 	# get bearer token
e4b78c
-	bearer_token = get_bearer_token(conn, opt)
e4b78c
+	try:
e4b78c
+		try:
e4b78c
+			# FIPS requires usedforsecurity=False and might not be
e4b78c
+			# available on all distros: https://bugs.python.org/issue9216
e4b78c
+			hash = hashlib.sha256(opt["--apikey"].encode("utf-8"), usedforsecurity=False).hexdigest()
e4b78c
+		except (AttributeError, TypeError):
e4b78c
+			hash = hashlib.sha256(opt["--apikey"].encode("utf-8")).hexdigest()
e4b78c
+		f = open(opt["--token-file"].replace("[hash]", hash))
e4b78c
+		bearer_token = f.read()
e4b78c
+		f.close()
e4b78c
+	except IOError:
e4b78c
+		bearer_token = get_bearer_token(conn, opt)
e4b78c
 
e4b78c
 	# set auth token for later requests
e4b78c
-	conn.setopt(pycurl.HTTPHEADER, [
e4b78c
-		"Content-Type: application/json",
e4b78c
-		"Authorization: Bearer {}".format(bearer_token),
e4b78c
-		"User-Agent: curl",
e4b78c
-	])
e4b78c
+	conn = set_bearer_token(conn, bearer_token)
e4b78c
 
e4b78c
 	return conn
e4b78c
 
e4b78c
 def disconnect(conn):
e4b78c
 	conn.close()
e4b78c
 
e4b78c
-def send_command(conn, command, method="GET", action=None, expected_rc=200):
e4b78c
+def send_command(conn, options, command, method="GET", action=None, expected_rc=200):
e4b78c
 	if not command.startswith("https"):
e4b78c
 		url = conn.base_url + command
e4b78c
 	else:
e4b78c
@@ -130,6 +182,26 @@ def send_command(conn, command, method="GET", action=None, expected_rc=200):
e4b78c
 		raise(e)
e4b78c
 
e4b78c
 	rc = conn.getinfo(pycurl.HTTP_CODE)
e4b78c
+
e4b78c
+	# auth if token has expired
e4b78c
+	if rc in [400, 401, 415]:
e4b78c
+		tokenconn = pycurl.Curl()
e4b78c
+		token = get_bearer_token(tokenconn, options)
e4b78c
+		tokenconn.close()
e4b78c
+		conn = set_bearer_token(conn, token)
e4b78c
+
e4b78c
+		# flush web_buffer
e4b78c
+		web_buffer.close()
e4b78c
+		web_buffer = io.BytesIO()
e4b78c
+		conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write)
e4b78c
+
e4b78c
+		try:
e4b78c
+			conn.perform()
e4b78c
+		except Exception as e:
e4b78c
+			raise(e)
e4b78c
+
e4b78c
+		rc = conn.getinfo(pycurl.HTTP_CODE)
e4b78c
+
e4b78c
 	result = web_buffer.getvalue().decode("UTF-8")
e4b78c
 
e4b78c
 	web_buffer.close()
e4b78c
@@ -173,7 +245,7 @@ def define_new_opts():
e4b78c
 	all_opt["proxy"] = {
e4b78c
                 "getopt" : ":",
e4b78c
                 "longopt" : "proxy",
e4b78c
-                "help" : "--proxy=[http://<URL>:<PORT>]          Proxy: 'http://<URL>:<PORT>'",
e4b78c
+                "help" : "--proxy=[http://<URL>:<PORT>]  Proxy: 'http://<URL>:<PORT>'",
e4b78c
                 "required" : "0",
e4b78c
 		"default": "",
e4b78c
                 "shortdesc" : "Network proxy",
e4b78c
@@ -188,14 +260,26 @@ def define_new_opts():
e4b78c
 		"shortdesc" : "Number of nodes returned by API",
e4b78c
 		"order" : 0
e4b78c
 	}
e4b78c
+	all_opt["token_file"] = {
e4b78c
+		"getopt" : ":",
e4b78c
+		"longopt" : "token-file",
e4b78c
+		"help" : "--token-file=[path]            Path to the token cache file\n"
e4b78c
+			"\t\t\t\t  (Default: @FENCETMPDIR@/fence_ibm_vpc/[hash].token)\n"
e4b78c
+			"\t\t\t\t  [hash] will be replaced by a hashed value",
e4b78c
+		"required" : "0",
e4b78c
+		"default": "@FENCETMPDIR@/fence_ibm_vpc/[hash].token",
e4b78c
+		"shortdesc" : "Path to the token cache file",
e4b78c
+		"order" : 0
e4b78c
+	}
e4b78c
 
e4b78c
 
e4b78c
 def main():
e4b78c
 	device_opt = [
e4b78c
 		"apikey",
e4b78c
 		"region",
e4b78c
-		"limit",
e4b78c
 		"proxy",
e4b78c
+		"limit",
e4b78c
+		"token_file",
e4b78c
 		"port",
e4b78c
 		"no_password",
e4b78c
 	]
e4b78c
diff --git a/tests/data/metadata/fence_ibm_vpc.xml b/tests/data/metadata/fence_ibm_vpc.xml
e4b78c
index acf4925fc..c35bc4619 100644
e4b78c
--- a/tests/data/metadata/fence_ibm_vpc.xml
e4b78c
+++ b/tests/data/metadata/fence_ibm_vpc.xml
e4b78c
@@ -23,6 +23,10 @@
e4b78c
 		<content type="string"  />
e4b78c
 		<shortdesc lang="en">Region</shortdesc>
e4b78c
 	</parameter>
e4b78c
+	<parameter name="token_file" unique="0" required="0">
e4b78c
+		<getopt mixed="--token-file=[path]" />
e4b78c
+		<shortdesc lang="en">Path to the token cache file</shortdesc>
e4b78c
+	</parameter>
e4b78c
 	<parameter name="action" unique="0" required="1">
e4b78c
 		<getopt mixed="-o, --action=[action]" />
e4b78c
 		<content type="string" default="reboot"  />