Allow a specific cache directory or no cache

If the env var XDG_CACHE_HOME is set, the cache directory is
"$XDG_CACHE_HOME/ianardap", else if HOME is set, it is
"$HOME/.ianardapcaches", else no cache is used but a loud
warning is displayed at the end of the result to encourage
users to set a cache directory.

Adapted pytest tests, no change needed on test_exe_matrix. They are
obviously slower with no cache. NB: two tests now fail independently
of this change.

Issue: #4
This commit is contained in:
Seb35 2022-12-18 19:09:49 +01:00
parent b3709c64e9
commit 0b10e8d563
5 changed files with 48 additions and 25 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__/

View File

@ -19,6 +19,8 @@ You need Python 3 and [Requests](http://python-requests.org/). You can install R
Then, copy the script `check_expire` and the file `ianardap.py`to the directory of local plugins.
The use of a cache directory is not mandatory, but highly recommended. It can be either `$XDG_CACHE_HOME/ianardap` or `$HOME/.ianardapcaches` depending on the environment variables set.
## Icinga configuration
If you use Icinga, here is a possible definition of the command:
@ -35,6 +37,7 @@ object CheckCommand "expiration" {
"-v" = { set_if = "$expiration_verbose$" }
}
env.XDG_CACHE_HOME = "/var/cache/nagios"
}
apply Service "expiration" {
@ -60,6 +63,13 @@ object Host "bortzmeyer-org" {
}
```
If needed, create the cache directory accordingly:
```
mkdir /var/cache/nagios
chown nagios: /var/cache/nagios
```
## Zabbix configuration
For monitoring systems that do not rely on exit codes but on calculation mechanism based on the metric they receive you can use the `-u` option that will only return the expiration date in unixtime format.

View File

@ -53,6 +53,8 @@ def usage(msg=None):
print("")
def details():
if database and not database.cachefile:
print(" (WARNING: no cache directory used, please set environement variable XDG_CACHE_HOME or HOME.)", end="")
if verbose:
print(" RDAP database \"%s\", version %s published on %s, retrieved on %s, RDAP server is %s" % \
(database.description, database.version, database.publication, database.retrieved, server))

View File

@ -21,7 +21,8 @@ IANABASES = {"domains": "https://data.iana.org/rdap/dns.json",
"v6prefixes": "https://data.iana.org/rdap/ipv6.json",
"as": "https://data.iana.org/rdap/asn.json",
"objects": "https://data.iana.org/rdap/object-tags.json"}
CACHE = os.environ["HOME"] + "/.ianardapcaches"
CACHE = os.environ["XDG_CACHE_HOME"] + "/ianardap" if "XDG_CACHE_HOME" in os.environ else \
(os.environ["HOME"] + "/.ianardapcaches" if "HOME" in os.environ else None)
MAXAGE = 24 # Hours. Used only if the server no longer gives the information.
IANATIMEOUT = 10 # Seconds
MAXTESTS = 3 # Maximum attempts to get the database
@ -60,28 +61,33 @@ file (see the documentation of the module).
"""
cache_valid = False
if not os.path.exists(cachedir):
os.mkdir(cachedir)
self.category = category
cachefile = os.path.join(cachedir, category)
if pickleformat:
self.cachefile = cachefile + ".pickle"
else:
self.cachefile = cachefile + ".json"
self.lockname = self.cachefile + ".lock"
self.expirationfile = self.cachefile + ".expires"
self.cachefile = None
self.lockname = None
self.expirationfile = None
if cachedir:
if not os.path.exists(cachedir):
os.mkdir(cachedir)
cachefile = os.path.join(cachedir, category)
if pickleformat:
self.cachefile = cachefile + ".pickle"
else:
self.cachefile = cachefile + ".json"
self.lockname = self.cachefile + ".lock"
self.expirationfile = self.cachefile + ".expires"
if maxage is not None:
with open(self.expirationfile, 'w'):
self.expirationtime = time.mktime((datetime.datetime.now() + \
datetime.timedelta(hours=maxage)).timetuple())
os.utime(self.expirationfile,
times = (self.expirationtime, self.expirationtime))
self.expirationtime = time.mktime((datetime.datetime.now() + \
datetime.timedelta(hours=maxage)).timetuple())
if self.expirationfile:
with open(self.expirationfile, 'w'):
os.utime(self.expirationfile,
times = (self.expirationtime, self.expirationtime))
loaded = False
tests = 0
errmsg = "No error"
while not loaded and tests < MAXTESTS:
self.lock()
if os.path.exists(self.cachefile) and \
if self.cachefile and os.path.exists(self.cachefile) and \
(pathlib.Path(self.expirationfile).exists() and \
datetime.datetime.fromtimestamp(os.path.getmtime(self.expirationfile)) > \
datetime.datetime.now()):
@ -139,9 +145,10 @@ file (see the documentation of the module).
try:
content = response.content
database = json.loads(content)
with open(self.expirationfile, 'w'):
os.utime(self.expirationfile,
times = (self.expirationtime, self.expirationtime))
if self.expirationfile:
with open(self.expirationfile, 'w'):
os.utime(self.expirationfile,
times = (self.expirationtime, self.expirationtime))
except json.decoder.JSONDecodeError:
tests += 1
errmsg = "Invalid JSON retrieved from %s" % IANABASE
@ -173,7 +180,7 @@ file (see the documentation of the module).
else: # IP addresses will be complicated, because of the
# longest prefix rule.
raise Exception("Unsupported category %s" % self.category)
if not cache_valid:
if self.cachefile and not cache_valid:
self.lock()
cache = open(self.cachefile, "wb")
if pickleformat:
@ -184,12 +191,14 @@ file (see the documentation of the module).
self.unlock()
def lock(self):
self.lockhandle = open(self.lockname, 'w')
fcntl.lockf(self.lockhandle, fcntl.LOCK_EX)
if self.lockname:
self.lockhandle = open(self.lockname, 'w')
fcntl.lockf(self.lockhandle, fcntl.LOCK_EX)
def unlock(self):
fcntl.lockf(self.lockhandle, fcntl.LOCK_UN)
self.lockhandle.close()
if self.lockname:
fcntl.lockf(self.lockhandle, fcntl.LOCK_UN)
self.lockhandle.close()
def find(self, id):
"""Get the RDAP server(s), as an array, for a given identifier. None

View File

@ -29,8 +29,9 @@ def test_refresh():
# Force a resfresh
database = ianardap.IanaRDAPDatabase(maxage=0)
assert (database.retrieved > (datetime.datetime.now() - datetime.timedelta(minutes=1))) and \
((not database.cachefile) or \
(datetime.datetime.fromtimestamp(os.path.getmtime(database.cachefile)) > \
(datetime.datetime.now() - datetime.timedelta(minutes=1)))
(datetime.datetime.now() - datetime.timedelta(minutes=1))))
def test_find_exists():
database = ianardap.IanaRDAPDatabase()