我有一个脚本,可以在用户选择的SAN交换机上执行长时间运行的命令,并且可能会破坏命令。一旦运行,我希望防止在 any 交换机上再次运行同一脚本至少半个小时。
我一直在寻找解决方案,但我认为存在争用情况(棘手,configparser等),或者仅涉及并发锁定(tendo.singleton,fcntl等)
sqlite3看起来是一个不错的选择,但我偶尔会遇到锁定错误-而不是只是等待-sqlite3.OperationalError:数据库已锁定
实际上,我对sqlite和SQL的了解还不够广,不知道编写健壮的锁/查询/更新类型的东西的最佳方法。
我认为我想要的处理是:
这是我尝试过的sqlite3代码。不确定它是否真的能达到我想要的效果(例如,我不知道如何正确地进行“进行中”)
此外,我是否过于复杂?很高兴有一个简单的解决方案,但确实存在危险,有人可能要运行多个副本,这可能会导致问题。
等待1/2小时的默认操作也可以。
感谢您的任何建议
#!/usr/bin/env python3
""" sample header module to start with
version v3.27.0 $Format:%h$
"""
# Standard library imports
from datetime import datetime
import os
import sqlite3
import sys
class PersistConfig:
""" Simple sqlite3 implementation to handle persistant config
that is also able to be updated concurretly from other
copies of this script
"""
# TODO really want a list of time things, with a lockable
# TODO timestamp, info, in-progress
# TODO set in-prog before
def __init__(self, database, switch):
self.database = database
self.switch = switch
self.ago = None
self.last_switch = None
self.now = int(now.timestamp())
self.mins_delay = 30 # Minutes between runs
if not os.path.exists(database):
with sqlite3.connect(database) as conn:
with conn:
conn.execute('BEGIN')
conn.execute("""
create table switchrun (
switch text primary key not null,
rundate datetime not null,
anyswitch text
);
""")
conn.execute("insert into switchrun ( switch, rundate ) "
"values ('any',0);")
def can_run(self):
""" Make sure the last run wasn't too recent.
If not, update the last run to now to stop anyone else
Also return details of last run
"""
with sqlite3.connect(self.database, timeout=30) as conn:
conn.execute("BEGIN") # Mark transaction start. Take shared lock
# Find last run
for row in conn.execute("select rundate, anyswitch from switchrun "
"where switch = 'any'"):
rundate, switch = row
mins_ago = (self.now - rundate) / 60
self.ago = mins_ago
self.last_switch = switch
if mins_ago > self.mins_delay:
conn.execute(
'insert or replace into switchrun ( switch, rundate ) '
'values (?,?);', (self.switch, self.now))
conn.execute(
'insert or replace into switchrun'
' ( switch, rundate, anyswitch ) '
'values (?,?,?);', ('any', self.now, self.switch))
return True
else:
return False
raise ValueError("Can't read last details")
def main(args):
switch = args[0]
db = 'config.db'
config = PersistConfig(db, switch)
if config.can_run():
print(f"OK!! Last run {config.ago:.0f} mins ago "
f"on {config.last_switch}")
else:
print(f"Denied. Last run {config.ago:.0f} mins ago "
f"on {config.last_switch}")
if __name__ == '__main__':
now = datetime.now()
main(sys.argv[1:])