charmhelpers.core.unitdata¶
Intro¶
A simple way to store state in units. This provides a key value storage with support for versioned, transactional operation, and can calculate deltas from previous values to simplify unit logic when processing changes.
Hook Integration¶
There are several extant frameworks for hook execution, including
- charmhelpers.core.hookenv.Hooks
- charmhelpers.core.services.ServiceManager
The storage classes are framework agnostic, one simple integration is via the HookData contextmanager. It will record the current hook execution environment (including relation data, config data, etc.), setup a transaction and allow easy access to the changes from previously seen values. One consequence of the integration is the reservation of particular keys (‘rels’, ‘unit’, ‘env’, ‘config’, ‘charm_revisions’) for their respective values.
Here’s a fully worked integration example using hookenv.Hooks:
from charmhelper.core import hookenv, unitdata
hook_data = unitdata.HookData()
db = unitdata.kv()
hooks = hookenv.Hooks()
@hooks.hook
def config_changed():
# Print all changes to configuration from previously seen
# values.
for changed, (prev, cur) in hook_data.conf.items():
print('config changed', changed,
'previous value', prev,
'current value', cur)
# Get some unit specific bookeeping
if not db.get('pkg_key'):
key = urllib.urlopen('https://example.com/pkg_key').read()
db.set('pkg_key', key)
# Directly access all charm config as a mapping.
conf = db.getrange('config', True)
# Directly access all relation data as a mapping
rels = db.getrange('rels', True)
if __name__ == '__main__':
with hook_data():
hook.execute()
A more basic integration is via the hook_scope context manager which simply manages transaction scope (and records hook name, and timestamp):
>>> from unitdata import kv
>>> db = kv()
>>> with db.hook_scope('install'):
... # do work, in transactional scope.
... db.set('x', 1)
>>> db.get('x')
1
Usage¶
Values are automatically json de/serialized to preserve basic typing and complex data struct capabilities (dicts, lists, ints, booleans, etc).
Individual values can be manipulated via get/set:
>>> kv.set('y', True)
>>> kv.get('y')
True
# We can set complex values (dicts, lists) as a single key.
>>> kv.set('config', {'a': 1, 'b': True'})
# Also supports returning dictionaries as a record which
# provides attribute access.
>>> config = kv.get('config', record=True)
>>> config.b
True
Groups of keys can be manipulated with update/getrange:
>>> kv.update({'z': 1, 'y': 2}, prefix="gui.")
>>> kv.getrange('gui.', strip=True)
{'z': 1, 'y': 2}
When updating values, its very helpful to understand which values have actually changed and how have they changed. The storage provides a delta method to provide for this:
>>> data = {'debug': True, 'option': 2}
>>> delta = kv.delta(data, 'config.')
>>> delta.debug.previous
None
>>> delta.debug.current
True
>>> delta
{'debug': (None, True), 'option': (None, 2)}
Note the delta method does not persist the actual change, it needs to be explicitly saved via ‘update’ method:
>>> kv.update(data, 'config.')
Values modified in the context of a hook scope retain historical values associated to the hookname.
>>> with db.hook_scope('config-changed'):
... db.set('x', 42)
>>> db.gethistory('x')
[(1, u'x', 1, u'install', u'2015-01-21T16:49:30.038372'),
(2, u'x', 42, u'config-changed', u'2015-01-21T16:49:30.038786')]
API Reference¶
Delta | Delta(previous, current) |
DeltaSet | |
HookData | Simple integration for existing hook exec frameworks. |
Record | |
Storage | Simple key value database for local unit state within charms. |
kv |
- class charmhelpers.core.unitdata.Delta¶
Bases: tuple
Delta(previous, current)
- current¶
Alias for field number 1
- previous¶
Alias for field number 0
- class charmhelpers.core.unitdata.DeltaSet¶
- class charmhelpers.core.unitdata.HookData¶
Bases: object
Simple integration for existing hook exec frameworks.
Records all unit information, and stores deltas for processing by the hook.
Sample:
from charmhelper.core import hookenv, unitdata changes = unitdata.HookData() db = unitdata.kv() hooks = hookenv.Hooks() @hooks.hook def config_changed(): # View all changes to configuration for changed, (prev, cur) in changes.conf.items(): print('config changed', changed, 'previous value', prev, 'current value', cur) # Get some unit specific bookeeping if not db.get('pkg_key'): key = urllib.urlopen('https://example.com/pkg_key').read() db.set('pkg_key', key) if __name__ == '__main__': with changes(): hook.execute()
- class charmhelpers.core.unitdata.Record¶
Bases: dict
- class charmhelpers.core.unitdata.Storage(path=None)¶
Bases: object
Simple key value database for local unit state within charms.
Modifications are automatically committed at hook exit. That’s currently regardless of exit code.
To support dicts, lists, integer, floats, and booleans values are automatically json encoded/decoded.
- close()¶
- debug(fh=<open file '<stderr>', mode 'w' at 0x7f2b853751e0>)¶
- delta(mapping, prefix)¶
return a delta containing values that have changed.
- flush(save=True)¶
- get(key, default=None, record=False)¶
- gethistory(key, deserialize=False)¶
- getrange(key_prefix, strip=False)¶
- hook_scope(*args, **kwds)¶
Scope all future interactions to the current hook execution revision.
- set(key, value)¶
- unset(key)¶
- update(mapping, prefix='')¶
- charmhelpers.core.unitdata.kv()¶