cluster-devel.redhat.com archive mirror
 help / color / mirror / Atom feed
* [Cluster-devel] [PATCH dlm/tool] python: add bindings and test example
@ 2021-09-17 19:44 Alexander Aring
  0 siblings, 0 replies; only message in thread
From: Alexander Aring @ 2021-09-17 19:44 UTC (permalink / raw)
  To: cluster-devel.redhat.com

This patch introduces a python directory with some python examples. This
is still in some experimental state to provide some dlm testing
framework by using libdlm.
---
 python/README                   |   7 +
 python/bindings/__init__.py     |   0
 python/bindings/dlm.py          | 244 ++++++++++++++++++++++++++++++++
 python/tests/dlm.py             |   1 +
 python/tests/recovery_interrupt |  60 ++++++++
 5 files changed, 312 insertions(+)
 create mode 100644 python/README
 create mode 100644 python/bindings/__init__.py
 create mode 100644 python/bindings/dlm.py
 create mode 120000 python/tests/dlm.py
 create mode 100755 python/tests/recovery_interrupt

diff --git a/python/README b/python/README
new file mode 100644
index 00000000..b598a53d
--- /dev/null
+++ b/python/README
@@ -0,0 +1,7 @@
+This directory contains python bindings to libdlm and short written tests in python to test dlm functionality.
+NOTE: the bindings are still experimental and only used for testing now.
+
+Future work:
+	- look into pytest
+	- add ebpf examples
+	- look into MPI for barrier()
diff --git a/python/bindings/__init__.py b/python/bindings/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/python/bindings/dlm.py b/python/bindings/dlm.py
new file mode 100644
index 00000000..b77d374c
--- /dev/null
+++ b/python/bindings/dlm.py
@@ -0,0 +1,244 @@
+from enum import IntEnum, IntFlag
+import sys, os, errno, platform
+import ctypes, ctypes.util
+
+#bindings
+
+if platform.system() != "Linux":
+	sys.exit("Not supported")
+
+c_path_libdlm = ctypes.util.find_library("dlm_lt")
+if not c_path_libdlm:
+	print("Unable to find the specified library.")
+	sys.exit()
+
+try:
+	c_libdlm = ctypes.CDLL(c_path_libdlm)
+except OSError:
+	print("Unable to load the system C library")
+	sys.exit()
+
+class C_DLM_LSHANDLE(ctypes.c_void_p):
+	pass
+
+class C_DLM_LKSB(ctypes.Structure):
+	_fields_ = [('sb_status', ctypes.c_int),
+		    ('sb_lkid', ctypes.c_uint32),
+		    ('sb_flags', ctypes.c_char),
+		    ('sb_lvbptr', ctypes.c_char_p)]
+
+C_BAST_CB = ctypes.CFUNCTYPE(None, ctypes.py_object)
+
+#dlm_create_lockspace
+c_dlm_create_lockspace = c_libdlm.dlm_create_lockspace
+c_dlm_create_lockspace.argtypes = [ctypes.c_char_p, #name
+				   ctypes.c_uint, #mode
+				  ]
+c_dlm_create_lockspace.restype = C_DLM_LSHANDLE
+
+#dlm_release_lockspace
+c_dlm_release_lockspace = c_libdlm.dlm_release_lockspace
+c_dlm_release_lockspace.argtypes = [ctypes.c_char_p, #name
+				    C_DLM_LSHANDLE, #ls
+				    ctypes.c_int, #force
+				   ]
+c_dlm_release_lockspace.restype = ctypes.c_int
+
+#dlm_ls_lock_wait
+c_dlm_ls_lock_wait = c_libdlm.dlm_ls_lock_wait
+c_dlm_ls_lock_wait.argtypes = [C_DLM_LSHANDLE, #ls
+			       ctypes.c_uint32, #mode
+			       ctypes.POINTER(C_DLM_LKSB), #lksb
+			       ctypes.c_uint32, #flags
+			       ctypes.c_char_p, #name
+			       ctypes.c_uint, #namelen
+			       ctypes.c_uint32, #parent
+			       ctypes.py_object, #bastarg
+			       C_BAST_CB, #bastaddr
+			       ctypes.c_void_p, #range
+			      ]
+c_dlm_ls_lock_wait.restype = ctypes.c_int
+
+#dlm_ls_unlock_wait
+c_dlm_ls_unlock_wait = c_libdlm.dlm_ls_unlock_wait
+c_dlm_ls_unlock_wait.argtypes = [C_DLM_LSHANDLE, #ls
+				 ctypes.c_uint32, #lkid
+				 ctypes.c_uint32, #flags
+				 ctypes.POINTER(C_DLM_LKSB), #lksb
+				]
+c_dlm_ls_unlock_wait.restype = ctypes.c_int
+
+#classes
+
+class LockMode(IntEnum):
+	IV = -1
+	NL = 0
+	CR = 1
+	CW = 2
+	PR = 3
+	PW = 4
+	EX = 5
+
+class LockFlag(IntFlag):
+	NOQUEUE = 0x00000001
+	CANCEL = 0x00000002
+	CONVERT = 0x00000004
+	VALBLK = 0x00000008
+	QUECVT = 0x00000010
+	IVVALBLK = 0x00000020
+	CONVDEADLK = 0x00000040
+	PERSISTENT = 0x00000080
+	NODLCKWT = 0x00000100
+	NODLCKBLK = 0x00000200
+	EXPEDITE = 0x00000400
+	NOQUEUEBAST = 0x00000800
+	HEADQUE = 0x00001000
+	NOORDER = 0x00002000
+	ORPHAN = 0x00004000
+	ALTPR = 0x00008000
+	ALTCW = 0x00010000
+	FORCEUNLOCK = 0x00020000
+	TIMEOUT = 0x00040000
+
+class LockSBFlag(IntFlag):
+	DEMOTED = 0x01
+	VALNOTVALID = 0x02
+	ALTMODE = 0x04
+
+class DLMError(OSError):
+
+	def __init__(self, errno):
+		if not errno < 0:
+			raise ValueError()
+
+		errno = abs(errno)
+		super().__init__(errno, os.strerror(errno))
+
+DLM_LOCK_TO_STR_FORMAT = \
+"""name: {}
+last_mode: {}
+last_flags: {}
+local_locked: {}
+last_sb: status: {}, lkid: {}, flags: {}, lvb: {}"""
+
+class Lockspace:
+
+	def __init__(self, name="default", mode=0o600):
+		self.__lsname = name
+		self.__ls = c_dlm_create_lockspace(self.__lsname.encode(), mode)
+		if not self.__ls:
+			raise DLMError(-errno.ENOMEM)
+
+	def release(self, force=0):
+		if not self.__ls:
+			return
+
+		rc = c_dlm_release_lockspace(self.__lsname.encode(), self.__ls,
+					     force)
+		if rc:
+			raise DLMError(rc)
+
+		self.__ls = None
+
+	def __del__(self):
+		self.release()
+
+	def __str__(self):
+		return "Lockspace: {}".format(self.__lsname)
+
+	def get_name(self):
+		return self.__lsname
+
+	# lockspace lock factory
+	def create_lock(self, name):
+		class Lock:
+
+			#note name should be 8 byte aligned for now
+			def __init__(self, ls, c_ls, name):
+				self.__local_locked = False
+				self.__last_mode = LockMode.IV
+				self.__last_flags = LockFlag(0)
+
+				self.__lk = C_DLM_LKSB()
+				self.__lk.sb_status = 0
+				self.__lk.sb_lkid = 0
+				self.__lk.sb_flags = LockSBFlag(0)
+				self.__lk.sb_lvbptr = None
+
+				self.__ls = ls
+				self.__c_ls = c_ls
+				self.__name = name
+
+			def __del__(self):
+				if self.__local_locked:
+					self.unlock_wait()
+
+			def __str__(self):
+				sb = self.get_sb()
+				return DLM_LOCK_TO_STR_FORMAT.format(
+						self.__name,
+						str(self.__last_mode),
+						str(self.__last_flags),
+						self.__local_locked,
+						str(sb.status),
+						sb.lkid, str(sb.flags),
+						str(sb.lvb))
+
+			def get_name(self):
+				return self.__name
+
+			def get_ls(self):
+				return self.__ls
+
+			# get a copy of current sb state in high-level python
+			def get_sb(self):
+				class LockSB:
+
+					def __init__(self, status, lkid,
+						     flags, lvb):
+						self.status = status
+						self.lkid = lkid
+						self.flags = LockSBFlag(flags[0])
+						self.lvb = lvb
+
+				return LockSB(self.__lk.sb_status,
+					      self.__lk.sb_lkid,
+					      self.__lk.sb_flags,
+					      self.__lk.sb_lvbptr)
+
+			def lock_wait(self, mode=LockMode.EX,
+				      flags=LockFlag(0), bast=None,
+				      bastarg=None):
+				if bast:
+					bast = C_BAST_CB(bast)
+				else:
+					bast = ctypes.cast(None, C_BAST_CB)
+
+				rc = c_dlm_ls_lock_wait(self.__c_ls, mode,
+							ctypes.byref(self.__lk),
+							flags,
+							self.__name.encode(),
+							len(self.__name), 0,
+							bastarg, bast, None)
+				if rc:
+					raise DLMError(rc)
+
+				self.__last_mode = mode
+				self.__last_flags = flags
+				self.__local_locked = True
+
+			def unlock_wait(self, flags=0):
+				rc = c_dlm_ls_unlock_wait(self.__c_ls,
+							  self.__lk.sb_lkid,
+							  flags,
+							  ctypes.byref(self.__lk))
+				if rc:
+					raise DLMError(rc)
+
+				self.__last_flags = flags
+				self.__local_locked = False
+
+		lock = Lock(self, self.__ls, name)
+		return lock
+
+# vim: tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab
diff --git a/python/tests/dlm.py b/python/tests/dlm.py
new file mode 120000
index 00000000..057a99c5
--- /dev/null
+++ b/python/tests/dlm.py
@@ -0,0 +1 @@
+../bindings/dlm.py
\ No newline@end of file
diff --git a/python/tests/recovery_interrupt b/python/tests/recovery_interrupt
new file mode 100755
index 00000000..de9d2600
--- /dev/null
+++ b/python/tests/recovery_interrupt
@@ -0,0 +1,60 @@
+#!/bin/env python3
+
+from signal import signal, SIGINT
+from dlm import Lockspace
+import argparse
+import logging
+import time
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('-l', '--lock',
+		    action='store_true',
+		    help='do lock activity between ls start/stop')
+parser.add_argument('-w', '--wait',
+		    help='wait time for contention',
+		    type=int, default=1)
+parser.add_argument("-d", "--debug", default="info",
+		    help=("logging debug level"),
+		    choices=["debug", "info", "warning"])
+
+args = parser.parse_args()
+
+debug_levels = {"debug": logging.DEBUG, "info": logging.INFO, "warning": logging.WARNING}
+debug_level = debug_levels[args.debug.lower()]
+
+logging.basicConfig(level=debug_level, format="%(asctime)s:%(levelname)s: %(message)s")
+
+def handler(signal, frame):
+	global end
+	end = True
+
+signal(SIGINT, handler)
+end = False
+while not end:
+	ls = Lockspace()
+	lsname = ls.get_name()
+	logging.info("lockspace {} created".format(lsname))
+
+	if args.lock:
+		lock = ls.create_lock("fooobaar")
+		lockname = lock.get_name()
+		logging.info("lock {} created".format(lockname))
+
+		lock.lock_wait()
+		logging.info("lock {} lock()".format(lockname))
+
+		#contention
+		logging.info("lock {} wait for {} secs".format(lockname, args.wait))
+		time.sleep(args.wait)
+
+		lock.unlock_wait()
+		logging.info("lock {} unlock()".format(lockname))
+
+		del lock
+		logging.info("lock {} removed".format(lockname))
+
+	del ls
+	logging.info("lockspace {} removed".format(lsname))
+
+# vim: tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab
-- 
2.27.0



^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2021-09-17 19:44 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-09-17 19:44 [Cluster-devel] [PATCH dlm/tool] python: add bindings and test example Alexander Aring

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).