def test_json_simple_property(): """ Test simple dict """ dataIn = {'a': 1, 'b': 2, 'c': 3} dataOut1 = {'a': 1, 'b': 2, 'c': 3} dataOut2 = {'c': 3, 'b': 2, 'a': 1} dataOut3 = {'a': 1, 'b': 2} dataOut4 = {'b': 2} dataOut5 = {} patch_strIn = '[{"path": "/a", "op": "remove"}]' patch_strOutPropExists = '[{"path": "/a", "op": "remove"}]' patch_strOutPropNotExists = '[]' patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut1, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut2, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut3, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropNotExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut4, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropNotExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut5, patch) assert patch == expected_patch
def merge(self, desired_data): u"""Merge in properties from controller instead of replacing""" # 1. stop processing if no merging is needed prev_updates = self._retrieve_whitelist_updates() if desired_data == {} and prev_updates is None: # nothing needs to be done (cccl has not and will not make changes # to this resource) return False prev_data = copy.deepcopy(self._data) # 2. remove old CCCL updates pospatch.convert_to_positional_patch(self._data, prev_updates) try: # This actually backs out the previous updates # to get back to the original F5 resource state. if prev_updates: self._data = prev_updates.apply(self._data) except Exception as e: # pylint: disable=broad-except LOGGER.warning("Failed removing updates to resource %s: %s", self.name, e) # 3. perform new merge with latest CCCL specific config original_resource = copy.deepcopy(self) self._data = merge(self._data, desired_data) self.post_merge_adjustments() # 4. compute the new updates so we can back out next go-around cur_updates = jsonpatch.make_patch(self._data, original_resource.data) # 5. remove move / adjust indexes per resource specific pospatch.convert_from_positional_patch(self._data, cur_updates) changed = self._data != prev_data # 6. update metadata with new CCCL updates self._save_whitelist_updates(cur_updates) # 7. determine if there was a needed change return changed
def merge(self, desired_data): u"""Merge in properties from controller instead of replacing""" # 1. stop processing if no merging is needed prev_updates = self._retrieve_whitelist_updates() if desired_data == {} and prev_updates is None: # nothing needs to be done (cccl has not and will not make changes # to this resource) return False prev_data = copy.deepcopy(self._data) # 2. remove old CCCL updates pospatch.convert_to_positional_patch(self._data, prev_updates) try: # This actually backs out the previous updates # to get back to the original F5 resource state. if prev_updates: self._data = prev_updates.apply(self._data) except Exception as e: # pylint: disable=broad-except LOGGER.warning("Failed removing updates to resource %s: %s", self.name, e) # 3. perform new merge with latest CCCL specific config original_resource = copy.deepcopy(self) self._data = merge(self._data, desired_data) self.post_merge_adjustments() # 4. compute the new updates so we can back out next go-around cur_updates = jsonpatch.make_patch(self._data, original_resource.data) # 5. remove move / adjust indexes per resource specific pospatch.convert_from_positional_patch(self._data, cur_updates) changed = self._data != prev_data # 6. update metadata with new CCCL updates self._save_whitelist_updates(cur_updates) # 7. determine if there was a needed change return changed
def test_json_simple_array(): """ Test simple arrays """ dataIn = { 'rules': [ 'A', 'B', ], } dataOut1 = { 'rules': [ 'A', 'B', ], } dataOut2 = { 'rules': [ 'B', 'A', ], } dataOut3 = { 'rules': [ 'A', ], } dataOut4 = { 'rules': [ 'B', ], } patch_strIn = '[{"path": "/rules/1", "op": "remove"}]' patch_strOut1 = '[{"path": "/rules/1", "op": "remove"}]' patch_strOut2 = '[{"path": "/rules/0", "op": "remove"}]' patch_strOut3 = '[]' patch_strOut4 = '[{"path": "/rules/0", "op": "remove"}]' patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut1) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut1, patch) assert patch == expected_patch # Test that if the order is switched (user rearranges the # order on the Big-IP), the patching still works correctly patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut2) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut2, patch) assert patch == expected_patch # Test if the patched entry is deleted (e.g. user deleted a # rule/policy on the Big-IP) patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut3) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut3, patch) assert patch == expected_patch # Test if another entry is deleted (e.g. user deleted a # rule/policy on the Big-IP) patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut4) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut4, patch) assert patch == expected_patch
def test_json_simple_property(): """ Test simple dict """ dataIn = { 'a': 1, 'b': 2, 'c': 3 } dataOut1 = { 'a': 1, 'b': 2, 'c': 3 } dataOut2 = { 'c': 3, 'b': 2, 'a': 1 } dataOut3 = { 'a': 1, 'b': 2 } dataOut4 = { 'b': 2 } dataOut5 = { } patch_strIn = '[{"path": "/a", "op": "remove"}]' patch_strOutPropExists = '[{"path": "/a", "op": "remove"}]' patch_strOutPropNotExists = '[]' patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut1, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut2, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut3, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropNotExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut4, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOutPropNotExists) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut5, patch) assert patch == expected_patch
def test_json_compound_array(): """ Test arrays of dictionaries """ dataIn = { 'profiles': [ { 'partition': 'Common', 'name': 'http', 'context': 'all' }, { 'partition': 'Common', 'name': 'tcp', 'context': 'all' } ], } dataOut1 = { 'profiles': [ { 'partition': 'Common', 'name': 'http', 'context': 'all' }, { 'partition': 'Common', 'name': 'tcp', 'context': 'all' } ] } dataOut2 = { 'profiles': [ { 'partition': 'Common', 'name': 'tcp', 'context': 'all' } ] } dataOut3 = { 'profiles': [ { 'partition': 'Common', 'name': 'http', 'context': 'all' }, { 'partition': 'Common', 'name': 'tcp', } ] } dataOut4 = { 'profiles': [ { 'partition': 'Common', 'name': 'http', 'context': 'all' } ] } patch_strIn = \ '[{"path": "/profiles/1/context", "value": "none", "op": "replace"}]' patch_strOut1 = \ '[{"path": "/profiles/1/context", "value": "none", "op": "replace"}]' patch_strOut2 = \ '[{"path": "/profiles/0/context", "value": "none", "op": "replace"}]' patch_strOut3 = \ '[]' patch_strOut4 = '[]' patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut1) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut1, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut2) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut2, patch) assert patch == expected_patch # if an array entry is modified by user, we must treat it as a new # entry and will not attempt to patch it (fortunately, this does not # seem possible on the Big-IP). patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut3) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut3, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut4) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut4, patch) assert patch == expected_patch
def test_json_simple_array(): """ Test simple arrays """ dataIn = { 'rules': [ 'A', 'B', ], } dataOut1 = { 'rules': [ 'A', 'B', ], } dataOut2 = { 'rules': [ 'B', 'A', ], } dataOut3 = { 'rules': [ 'A', ], } dataOut4 = { 'rules': [ 'B', ], } patch_strIn = '[{"path": "/rules/1", "op": "remove"}]' patch_strOut1 = '[{"path": "/rules/1", "op": "remove"}]' patch_strOut2 = '[{"path": "/rules/0", "op": "remove"}]' patch_strOut3 = '[]' patch_strOut4 = '[{"path": "/rules/0", "op": "remove"}]' patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut1) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut1, patch) assert patch == expected_patch # Test that if the order is switched (user rearranges the # order on the Big-IP), the patching still works correctly patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut2) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut2, patch) assert patch == expected_patch # Test if the patched entry is deleted (e.g. user deleted a # rule/policy on the Big-IP) patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut3) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut3, patch) assert patch == expected_patch # Test if another entry is deleted (e.g. user deleted a # rule/policy on the Big-IP) patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut4) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut4, patch) assert patch == expected_patch
def test_json_compound_array(): """ Test arrays of dictionaries """ dataIn = { 'profiles': [{ 'partition': 'Common', 'name': 'http', 'context': 'all' }, { 'partition': 'Common', 'name': 'tcp', 'context': 'all' }], } dataOut1 = { 'profiles': [{ 'partition': 'Common', 'name': 'http', 'context': 'all' }, { 'partition': 'Common', 'name': 'tcp', 'context': 'all' }] } dataOut2 = { 'profiles': [{ 'partition': 'Common', 'name': 'tcp', 'context': 'all' }] } dataOut3 = { 'profiles': [{ 'partition': 'Common', 'name': 'http', 'context': 'all' }, { 'partition': 'Common', 'name': 'tcp', }] } dataOut4 = { 'profiles': [{ 'partition': 'Common', 'name': 'http', 'context': 'all' }] } patch_strIn = \ '[{"path": "/profiles/1/context", "value": "none", "op": "replace"}]' patch_strOut1 = \ '[{"path": "/profiles/1/context", "value": "none", "op": "replace"}]' patch_strOut2 = \ '[{"path": "/profiles/0/context", "value": "none", "op": "replace"}]' patch_strOut3 = \ '[]' patch_strOut4 = '[]' patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut1) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut1, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut2) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut2, patch) assert patch == expected_patch # if an array entry is modified by user, we must treat it as a new # entry and will not attempt to patch it (fortunately, this does not # seem possible on the Big-IP). patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut3) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut3, patch) assert patch == expected_patch patch = jsonpatch.JsonPatch.from_string(patch_strIn) expected_patch = jsonpatch.JsonPatch.from_string(patch_strOut4) pospatch.convert_from_positional_patch(dataIn, patch) pospatch.convert_to_positional_patch(dataOut4, patch) assert patch == expected_patch