/usr/lib/python2.7/dist-packages/nagiosplugin/cookie.py is in python-nagiosplugin 1.2.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | # Copyright (c) gocept gmbh & co. kg
# See also LICENSE.txt
"""Persistent dict to remember state between invocations.
Cookies are used to remember file positions, counters and the like
between plugin invocations. It is not intended for substantial amounts
of data. Cookies are serialized into JSON and saved to a state file. We
prefer a plain text format to allow administrators to inspect and edit
its content. See :class:`~nagiosplugin.logtail.LogTail` for an
application of cookies to get only new lines of a continuously growing
file.
Cookies are locked exclusively so that at most one process at a time has
access to it. Changes to the dict are not reflected in the file until
:meth:`Cookie.commit` is called. It is recommended to use Cookie as
context manager to get it opened and committed automatically.
"""
from .compat import UserDict, TemporaryFile
from .platform import flock_exclusive
import codecs
import json
import os
import tempfile
class Cookie(UserDict, object):
def __init__(self, statefile=None):
"""Creates a persistent dict to keep state.
After creation, a cookie behaves like a normal dict.
:param statefile: file name to save the dict's contents
.. note:: If `statefile` is empty or None, the Cookie will be
oblivous, i.e., it will forget its contents on garbage
collection. This makes it possible to explicitely throw away
state between plugin runs (for example by a command line
argument).
"""
super(Cookie, self).__init__()
self.path = statefile
self.fobj = None
def __enter__(self):
"""Allows Cookie to be used as context manager.
Opens the file and passes a dict-like object into the
subordinate context. See :meth:`open` for details about opening
semantics. When the context is left in the regular way (no
exception raised), the cookie is :meth:`commit`\ ted to disk.
:yields: open cookie
"""
self.open()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if not exc_type:
self.commit()
self.close()
def open(self):
"""Reads/creates the state file and initializes the dict.
If the state file does not exist, it is touched into existence.
An exclusive lock is acquired to ensure serialized access. If
:meth:`open` fails to parse file contents, it truncates
the file before raising an exception. This guarantees that
plugins will not fail repeatedly when their state files get
damaged.
:returns: Cookie object (self)
:raises ValueError: if the state file is corrupted or does not
deserialize into a dict
"""
self.fobj = self._create_fobj()
flock_exclusive(self.fobj)
if os.fstat(self.fobj.fileno()).st_size:
try:
self.data = self._load()
except ValueError:
self.fobj.truncate(0)
raise
return self
def _create_fobj(self):
if not self.path:
return TemporaryFile('w+', encoding='ascii',
prefix='oblivious_cookie_')
# mode='a+' has problems with mixed R/W operation on Mac OS X
try:
return codecs.open(self.path, 'r+', encoding='ascii')
except IOError:
return codecs.open(self.path, 'w+', encoding='ascii')
def _load(self):
self.fobj.seek(0)
data = json.load(self.fobj)
if not isinstance(data, dict):
raise ValueError('format error: cookie does not contain dict',
self.path, data)
return data
def close(self):
"""Closes a cookie and its underlying state file.
This method has no effect if the cookie is already closed.
Once the cookie is closed, any operation (like :meth:`commit`)
will raise an exception.
"""
if not self.fobj:
return
self.fobj.close()
self.fobj = None
def commit(self):
"""Persists the cookie's dict items in the state file.
The cookies content is serialized as JSON string and saved to
the state file. The buffers are flushed to ensure that the new
content is saved in a durable way.
"""
if not self.fobj:
raise IOError('cannot commit closed cookie', self.path)
self.fobj.seek(0)
self.fobj.truncate()
json.dump(self.data, self.fobj)
self.fobj.write('\n')
self.fobj.flush()
os.fsync(self.fobj)
|