From d30bc253f052b80f8e2e237b58aa521b04fc2984 Mon Sep 17 00:00:00 2001 From: Dmitry Shulyak Date: Tue, 22 Sep 2015 11:52:57 +0300 Subject: [PATCH 1/2] Add support for revert of a change --- solar/solar/cli/system_log.py | 28 +++++++++++++++++++++------- solar/solar/system_log/change.py | 20 ++++++++++++++++++-- solar/solar/system_log/data.py | 4 ++-- solar/solar/system_log/operations.py | 7 ++++--- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/solar/solar/cli/system_log.py b/solar/solar/cli/system_log.py index 02bef8ea..cd266506 100644 --- a/solar/solar/cli/system_log.py +++ b/solar/solar/cli/system_log.py @@ -76,13 +76,27 @@ def commit(uid): @changes.command() @click.option('-n', default=5) -def history(n): - commited = list(data.CL().collection(n)) - if not commited: - click.echo('No history.') - return - commited.reverse() - click.echo(commited) +@click.option('-d', default=False, is_flag=True) +@click.option('-s', default=False, is_flag=True) +def history(n, d, s): + log = list(data.CL().collection(n)) + for item in log: + if s: + click.echo(item.uid) + continue + + click.echo(item) + if d: + for line in item.details: + click.echo(' '*4+line) + if not log: + click.echo('No history') + + +@changes.command() +@click.argument('uid') +def revert(uid): + change.revert(uid) @changes.command() diff --git a/solar/solar/system_log/change.py b/solar/solar/system_log/change.py index 580c29d1..e6579517 100644 --- a/solar/solar/system_log/change.py +++ b/solar/solar/system_log/change.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -from dictdiffer import diff +import dictdiffer import networkx as nx from solar.core.log import log @@ -39,7 +39,7 @@ def guess_action(from_, to): def create_diff(staged, commited): - return list(diff(commited, staged)) + return list(dictdiffer.diff(commited, staged)) def _stage_changes(staged_resources, commited_resources, staged_log): @@ -102,3 +102,19 @@ def parameters(res, action, data): 'type': 'solar_resource', # unique identifier for a node should be passed 'target': data.get('ip')} + + +def revert_uids(uids): + commited = data.CD() + history = data.CL() + for uid in uids: + item = history.get(uid) + res_db = resource.load(item.res) + args_to_update = dictdiffer.revert( + item.diff, commited.get(item.res, {})) + res_db.update(args_to_update) + + +def revert(uid): + return revert_uids([uid]) + diff --git a/solar/solar/system_log/data.py b/solar/solar/system_log/data.py index 02f7addb..226a7a41 100644 --- a/solar/solar/system_log/data.py +++ b/solar/solar/system_log/data.py @@ -115,7 +115,7 @@ class Log(object): self.ordered_log = db.get_ordered_hash(path) def append(self, logitem): - self.ordered_log.add([(logitem.log_action, logitem.to_dict())]) + self.ordered_log.add([(logitem.uid, logitem.to_dict())]) def pop(self, uid): item = self.get(uid) @@ -125,7 +125,7 @@ class Log(object): return item def update(self, logitem): - self.ordered_log.update(logitem.log_action, logitem.to_dict()) + self.ordered_log.update(logitem.uid, logitem.to_dict()) def clean(self): self.ordered_log.clean() diff --git a/solar/solar/system_log/operations.py b/solar/solar/system_log/operations.py index 7ce0b3fb..e347d237 100644 --- a/solar/solar/system_log/operations.py +++ b/solar/solar/system_log/operations.py @@ -18,7 +18,7 @@ from dictdiffer import patch def set_error(log_action, *args, **kwargs): sl = data.SL() - item = sl.get(log_action) + item = next((i for i in sl if i.log_action == log_action), None) if item: item.state = data.STATES.error sl.update(item) @@ -26,10 +26,11 @@ def set_error(log_action, *args, **kwargs): def move_to_commited(log_action, *args, **kwargs): sl = data.SL() - item = sl.pop(log_action) + item = next((i for i in sl if i.log_action == log_action), None) + sl.pop(item.uid) if item: commited = data.CD() - staged_data = patch(item.diff, commited.get(item.log_action, {})) + staged_data = patch(item.diff, commited.get(item.res, {})) cl = data.CL() item.state = data.STATES.success cl.append(item) From ee2db4ba4b16ed560c80b75075e5583e05ee787b Mon Sep 17 00:00:00 2001 From: Dmitry Shulyak Date: Wed, 23 Sep 2015 15:44:49 +0300 Subject: [PATCH 2/2] Add test for revert of value update --- solar/solar/cli/system_log.py | 8 ++--- solar/solar/system_log/change.py | 14 +++++--- solar/solar/test/test_system_log_api.py | 46 +++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 solar/solar/test/test_system_log_api.py diff --git a/solar/solar/cli/system_log.py b/solar/solar/cli/system_log.py index cd266506..a21cb65c 100644 --- a/solar/solar/cli/system_log.py +++ b/solar/solar/cli/system_log.py @@ -39,7 +39,7 @@ def validate(): @changes.command() -@click.option('-d', default=False, is_flag=True) +@click.option('-d', default=False, is_flag=True, help='detailed view') def stage(d): log = list(change.stage_changes().reverse()) for item in log: @@ -75,9 +75,9 @@ def commit(uid): @changes.command() -@click.option('-n', default=5) -@click.option('-d', default=False, is_flag=True) -@click.option('-s', default=False, is_flag=True) +@click.option('-n', default=5, help='number of items to show') +@click.option('-d', default=False, is_flag=True, help='detailed view') +@click.option('-s', default=False, is_flag=True, help='short view, only uid') def history(n, d, s): log = list(data.CL().collection(n)) for item in log: diff --git a/solar/solar/system_log/change.py b/solar/solar/system_log/change.py index e6579517..110bb486 100644 --- a/solar/solar/system_log/change.py +++ b/solar/solar/system_log/change.py @@ -42,6 +42,14 @@ def create_diff(staged, commited): return list(dictdiffer.diff(commited, staged)) +def create_logitem(resource, action, diffed): + return data.LogItem( + utils.generate_uuid(), + resource, + '{}.{}'.format(resource, action), + diffed) + + def _stage_changes(staged_resources, commited_resources, staged_log): for res_uid in staged_resources.keys(): @@ -52,11 +60,7 @@ def _stage_changes(staged_resources, commited_resources, staged_log): if df: action = guess_action(commited_data, staged_data) - log_item = data.LogItem( - utils.generate_uuid(), - res_uid, - '{}.{}'.format(res_uid, action), - df) + log_item = create_logitem(res_uid, action, df) staged_log.append(log_item) return staged_log diff --git a/solar/solar/test/test_system_log_api.py b/solar/solar/test/test_system_log_api.py new file mode 100644 index 00000000..be36e5a7 --- /dev/null +++ b/solar/solar/test/test_system_log_api.py @@ -0,0 +1,46 @@ +# Copyright 2015 Mirantis, Inc. +# +# 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 pytest import fixture +from solar.system_log import change +from solar.system_log import data +from solar.system_log import operations +from solar.core.resource import resource +from solar.interfaces import orm + + +def test_revert_update(): + commit = {'a': '10'} + previous = {'a': '9'} + res = orm.DBResource(id='test1', name='test1', base_path='x') + res.save() + res.add_input('a', 'str', '9') + action = 'update' + + resource_obj = resource.load(res.name) + + assert resource_obj.args == previous + + log = data.SL() + logitem =change.create_logitem( + res.name, action, change.create_diff(commit, previous)) + log.append(logitem) + resource_obj.update(commit) + operations.move_to_commited(logitem.log_action) + + assert resource_obj.args == commit + + change.revert(logitem.uid) + assert resource_obj.args == previous