blob: c229a4205bcd2f163be6dcb6486183bc9c865040 [file] [log] [blame]
#
# Copyright 2016 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from uuid import uuid4
import structlog
from simplejson import dumps, loads
from voltha.core.config.config_node import ConfigNode, MergeConflictException
from voltha.core.config.config_rev import ConfigRevision
from voltha.core.config.config_rev_persisted import PersistedConfigRevision
log = structlog.get_logger()
class ConfigRoot(ConfigNode):
__slots__ = (
'_dirty_nodes', # holds set of modified nodes per transaction branch
'_kv_store',
'_loading',
'_rev_cls'
)
def __init__(self, initial_data, kv_store=None, rev_cls=ConfigRevision):
self._kv_store = kv_store
self._dirty_nodes = {}
self._loading = False
if kv_store is not None and \
not issubclass(rev_cls, PersistedConfigRevision):
rev_cls = PersistedConfigRevision
self._rev_cls = rev_cls
super(ConfigRoot, self).__init__(self, initial_data, False)
@property
def kv_store(self):
if self._loading:
# provide fake store for storing things
# TODO this shall be a fake_dict providing noop for all relevant
# operations
return dict()
else:
return self._kv_store
def mkrev(self, *args, **kw):
return self._rev_cls(*args, **kw)
def mk_txbranch(self):
txid = uuid4().hex[:12]
self._dirty_nodes[txid] = {self}
self._mk_txbranch(txid)
return txid
def del_txbranch(self, txid):
for dirty_node in self._dirty_nodes[txid]:
dirty_node._del_txbranch(txid)
del self._dirty_nodes[txid]
def fold_txbranch(self, txid):
try:
self._merge_txbranch(txid, dry_run=1)
except MergeConflictException:
self.del_txbranch(txid)
raise
self._merge_txbranch(txid)
# ~~~~~~ Overridden, root-level CRUD methods to handle transactions ~~~~~~~
def update(self, path, data, strict=None, txid=None, mk_branch=None):
assert mk_branch is None
if txid is not None:
dirtied = self._dirty_nodes[txid]
def track_dirty(node):
dirtied.add(node)
return node._mk_txbranch(txid)
return super(ConfigRoot, self).update(path, data, strict,
txid, track_dirty)
else:
return super(ConfigRoot, self).update(path, data, strict)
def add(self, path, data, txid=None, mk_branch=None):
assert mk_branch is None
if txid is not None:
dirtied = self._dirty_nodes[txid]
def track_dirty(node):
dirtied.add(node)
return node._mk_txbranch(txid)
return super(ConfigRoot, self).add(path, data, txid, track_dirty)
else:
return super(ConfigRoot, self).add(path, data)
def remove(self, path, txid=None, mk_branch=None):
assert mk_branch is None
if txid is not None:
dirtied = self._dirty_nodes[txid]
def track_dirty(node):
dirtied.add(node)
return node._mk_txbranch(txid)
return super(ConfigRoot, self).remove(path, txid, track_dirty)
else:
return super(ConfigRoot, self).remove(path)
# ~~~~~~~~~~~~~~~~ Persistence related ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@classmethod
def load(cls, root_msg_cls, kv_store):
# need to use fake kv store during initial load for not to override
# our real k vstore
fake_kv_store = dict() # shall use more efficient mock dict
root = cls(root_msg_cls(), kv_store=fake_kv_store,
rev_cls=PersistedConfigRevision)
# we can install the real store now
root._kv_store = kv_store
root.load_from_persistence(root_msg_cls)
return root
def _make_latest(self, branch, *args, **kw):
super(ConfigRoot, self)._make_latest(branch, *args, **kw)
# only persist the committed branch
if self._kv_store is not None and branch._txid is None:
root_data = dict(
latest=branch._latest._hash,
tags=dict((k, v._hash) for k, v in self._tags.iteritems())
)
blob = dumps(root_data)
self._kv_store['root'] = blob
def load_from_persistence(self, root_msg_cls):
self._loading = True
blob = self._kv_store['root']
root_data = loads(blob)
for tag, hash in root_data['tags'].iteritems():
raise NotImplementedError()
self.load_latest(root_data['latest'])
self._loading = False