Пример #1
0
def test_resource_merge_simple_dict():
    """ Test simple dictionary merging """

    test_data = [
        # desired, existing, merged
        ({}, {
            'a': 1
        }, {
            'a': 1
        }),
        ({
            'a': 1
        }, {}, {
            'a': 1
        }),
        ({
            'a': 1
        }, {
            'a': 2
        }, {
            'a': 1
        }),
        ({
            'a': 1
        }, {
            'b': 2
        }, {
            'a': 1,
            'b': 2
        }),
        ({
            'a': 1,
            'b': 3
        }, {
            'b': 2
        }, {
            'a': 1,
            'b': 3
        }),
        ({
            'a': 1,
            'b': 2
        }, {
            'c': 3,
            'd': 4
        }, {
            'a': 1,
            'b': 2,
            'c': 3,
            'd': 4
        })
    ]
    for test in test_data:
        desired = test[0]
        existing = test[1]
        expected = test[2]
        assert merge(existing, desired) == expected
Пример #2
0
def test_resource_merge_list_of_named_dict():
    """ Test merge lists of named dictionary objects

        This is unique for Big-IP resources that are
        lists of named objects (the resources must have
        a unique property 'name').
    """

    test_data = [
        # desired, existing, merged
        (
            [],
            [],
            []
        ),
        (
            [],
            [{'name': 'resource-a', 'value': 1}],
            [{'name': 'resource-a', 'value': 1}]
        ),
        (
            [{'name': 'resource-a', 'value': 1}],
            [],
            [{'name': 'resource-a', 'value': 1}]),
        (
            [{'name': 'resource-a', 'value': 1}],
            [{'name': 'resource-a', 'value': 3}],
            [{'name': 'resource-a', 'value': 1}]
        ),
        (
            [{'name': 'resource-a', 'value1': 1, 'value2': 2}],
            [{'name': 'resource-a', 'value2': 0, 'value3': 3}],
            [{'name': 'resource-a', 'value1': 1, 'value2': 2}]
        ),
        (
            [
                {'name': 'resource-a', 'value1': 1, 'value2': 2},
                {'name': 'resource-b', 'valueB': 'b'}
            ],
            [
                {'name': 'resource-a', 'value2': 0, 'value3': 3},
                {'name': 'resource-c', 'valueC': 'c'},
                {'name': 'resource-b', 'valueB': 'b'}
            ],
            [
                {'name': 'resource-a', 'value1': 1, 'value2': 2},
                {'name': 'resource-b', 'valueB': 'b'},
                {'name': 'resource-c', 'valueC': 'c'}
            ]
        )
    ]
    for test in test_data:
        desired = test[0]
        existing = test[1]
        expected = test[2]
        assert merge(existing, desired) == expected
Пример #3
0
def test_resource_merge_scalars():
    """ Test simple scalar merging (replacing) """

    test_data = [
        # desired, existing, merged
        (4, 3, 4),
        ('a', 'b', 'a'),
        (True, False, True)
    ]
    for test in test_data:
        desired = test[0]
        existing = test[1]
        expected = test[2]
        assert merge(existing, desired) == expected
Пример #4
0
def test_resource_merge_scalars():
    """ Test simple scalar merging (replacing) """

    test_data = [
        # desired, existing, merged
        (4, 3, 4),
        ('a', 'b', 'a'),
        (True, False, True)
    ]
    for test in test_data:
        desired = test[0]
        existing = test[1]
        expected = test[2]
        assert merge(existing, desired) == expected
Пример #5
0
def test_resource_merge_simple_dict():
    """ Test simple dictionary merging """

    test_data = [
        # desired, existing, merged
        ({}, {'a': 1}, {'a': 1}),
        ({'a': 1}, {}, {'a': 1}),
        ({'a': 1}, {'a': 2}, {'a': 1}),
        ({'a': 1}, {'b': 2}, {'a': 1, 'b': 2}),
        ({'a': 1, 'b': 3}, {'b': 2}, {'a': 1, 'b': 3}),
        ({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'a': 1, 'b': 2, 'c': 3, 'd': 4})
    ]
    for test in test_data:
        desired = test[0]
        existing = test[1]
        expected = test[2]
        assert merge(existing, desired) == expected
Пример #6
0
def test_resource_merge_simple_arrays():
    """ Test simple list merging (replacing) """

    test_data = [
        # desired, existing, merged
        ([], [], []),
        ([], [1], [1]),
        ([1], [], [1]),
        ([1], [2], [1, 2]),
        ([2], [1], [2, 1]),
        ([1, 2], [1], [1, 2]),
        ([1], [1, 2], [1, 2]),
        ([1, 3], [1, 2], [1, 3, 2]),
        (['apple', 'orange'], ['apple', 'pear'], ['apple', 'orange', 'pear'])
    ]
    for test in test_data:
        desired = test[0]
        existing = test[1]
        expected = test[2]
        assert merge(existing, desired) == expected
Пример #7
0
def test_resource_merge_simple_arrays():
    """ Test simple list merging (replacing) """

    test_data = [
        # desired, existing, merged
        ([], [], []),
        ([], [1], [1]),
        ([1], [], [1]),
        ([1], [2], [1, 2]),
        ([2], [1], [2, 1]),
        ([1, 2], [1], [1, 2]),
        ([1], [1, 2], [1, 2]),
        ([1, 3], [1, 2], [1, 3, 2]),
        (['apple', 'orange'], ['apple', 'pear'], ['apple', 'orange', 'pear'])
    ]
    for test in test_data:
        desired = test[0]
        existing = test[1]
        expected = test[2]
        assert merge(existing, desired) == expected
Пример #8
0
    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
Пример #9
0
    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
Пример #10
0
def test_resource_merge_sample_resource():
    """ Test actual Big-IP resource """

    desired = {
        'destination':
        '/test/172.16.3.59%0:80',
        'name':
        'ingress_172-16-3-59_80',
        'rules': [],
        'vlansDisabled':
        True,
        'enabled':
        True,
        'sourceAddressTranslation': {
            'type': 'automap'
        },
        'partition':
        'test',
        'source':
        '0.0.0.0%0/0',
        'profiles': [{
            'partition': 'Common',
            'name': 'http',
            'context': 'all'
        }, {
            'partition': 'Common',
            'name': 'tcp',
            'context': 'all'
        }],
        'connectionLimit':
        0,
        'ipProtocol':
        'tcp',
        'vlans': [],
        'policies': [{
            'partition': 'test',
            'name': 'ingress_172-16-3-59_80'
        }]
    }

    existing = {
        'destination':
        '/test/172.16.3.59%0:80',
        'name':
        'ingress_172-16-3-59_80',
        'rules': [],
        'vlansDisabled':
        False,  # should change
        'enabled':
        True,
        'sourceAddressTranslation': {
            'type': 'snat'
        },  # should change
        'partition':
        'test',
        'source':
        '0.0.0.0%0/0',
        'profiles': [
            # html profile should be kept, but added after CCCL entries
            {
                'partition': 'Common',
                'name': 'html',
                'context': 'all'
            },
            {
                'partition': 'Common',
                'name': 'http',
                'context': 'all'
            },
            {
                'partition': 'Common',
                'name': 'tcp',
                'context': 'all'
            }
        ],
        'connectionLimit':
        1,  # should change
        'ipProtocol':
        'tcp',
        'vlans': [{
            'name': 'vlan',
            'a': '1',
            'b': '2'
        }  # should be kept
                  ],
        'policies': []  # will be added to
    }

    expected = {
        'destination':
        '/test/172.16.3.59%0:80',
        'name':
        'ingress_172-16-3-59_80',
        'rules': [],
        'vlansDisabled':
        True,
        'enabled':
        True,
        'sourceAddressTranslation': {
            'type': 'automap'
        },
        'partition':
        'test',
        'source':
        '0.0.0.0%0/0',
        'profiles': [{
            'partition': 'Common',
            'name': 'http',
            'context': 'all'
        }, {
            'partition': 'Common',
            'name': 'tcp',
            'context': 'all'
        }, {
            'partition': 'Common',
            'name': 'html',
            'context': 'all'
        }],
        'connectionLimit':
        0,
        'ipProtocol':
        'tcp',
        'vlans': [{
            'name': 'vlan',
            'a': '1',
            'b': '2'
        }],
        'policies': [{
            'partition': 'test',
            'name': 'ingress_172-16-3-59_80'
        }]
    }

    assert merge(existing, desired) == expected
Пример #11
0
def test_resource_merge_list_of_named_dict():
    """ Test merge lists of named dictionary objects

        This is unique for Big-IP resources that are
        lists of named objects (the resources must have
        a unique property 'name').
    """

    test_data = [
        # desired, existing, merged
        ([], [], []),
        ([], [{
            'name': 'resource-a',
            'value': 1
        }], [{
            'name': 'resource-a',
            'value': 1
        }]),
        ([{
            'name': 'resource-a',
            'value': 1
        }], [], [{
            'name': 'resource-a',
            'value': 1
        }]),
        ([{
            'name': 'resource-a',
            'value': 1
        }], [{
            'name': 'resource-a',
            'value': 3
        }], [{
            'name': 'resource-a',
            'value': 1
        }]),
        ([{
            'name': 'resource-a',
            'value1': 1,
            'value2': 2
        }], [{
            'name': 'resource-a',
            'value2': 0,
            'value3': 3
        }], [{
            'name': 'resource-a',
            'value1': 1,
            'value2': 2
        }]),
        ([{
            'name': 'resource-a',
            'value1': 1,
            'value2': 2
        }, {
            'name': 'resource-b',
            'valueB': 'b'
        }], [{
            'name': 'resource-a',
            'value2': 0,
            'value3': 3
        }, {
            'name': 'resource-c',
            'valueC': 'c'
        }, {
            'name': 'resource-b',
            'valueB': 'b'
        }], [{
            'name': 'resource-a',
            'value1': 1,
            'value2': 2
        }, {
            'name': 'resource-b',
            'valueB': 'b'
        }, {
            'name': 'resource-c',
            'valueC': 'c'
        }])
    ]
    for test in test_data:
        desired = test[0]
        existing = test[1]
        expected = test[2]
        assert merge(existing, desired) == expected
Пример #12
0
def test_resource_merge_sample_resource():
    """ Test actual Big-IP resource """

    desired = {
        'destination': '/test/172.16.3.59%0:80',
        'name': 'ingress_172-16-3-59_80',
        'rules': [],
        'vlansDisabled': True,
        'enabled': True,
        'sourceAddressTranslation': {'type': 'automap'},
        'partition': 'test',
        'source': '0.0.0.0%0/0',
        'profiles': [
            {'partition': 'Common', 'name': 'http', 'context': 'all'},
            {'partition': 'Common', 'name': 'tcp', 'context': 'all'}
        ],
        'connectionLimit': 0,
        'ipProtocol': 'tcp',
        'vlans': [],
        'policies': [
            {'partition': 'test', 'name': 'ingress_172-16-3-59_80'}
        ]
    }

    existing = {
        'destination': '/test/172.16.3.59%0:80',
        'name': 'ingress_172-16-3-59_80',
        'rules': [],
        'vlansDisabled': False,  # should change
        'enabled': True,
        'sourceAddressTranslation': {'type': 'snat'},  # should change
        'partition': 'test',
        'source': '0.0.0.0%0/0',
        'profiles': [
            # html profile should be kept, but added after CCCL entries
            {'partition': 'Common', 'name': 'html', 'context': 'all'},
            {'partition': 'Common', 'name': 'http', 'context': 'all'},
            {'partition': 'Common', 'name': 'tcp', 'context': 'all'}
        ],
        'connectionLimit': 1,  # should change
        'ipProtocol': 'tcp',
        'vlans': [
            {'name': 'vlan', 'a': '1', 'b': '2'}  # should be kept
        ],
        'policies': []  # will be added to
    }

    expected = {
        'destination': '/test/172.16.3.59%0:80',
        'name': 'ingress_172-16-3-59_80',
        'rules': [],
        'vlansDisabled': True,
        'enabled': True,
        'sourceAddressTranslation': {'type': 'automap'},
        'partition': 'test',
        'source': '0.0.0.0%0/0',
        'profiles': [
            {'partition': 'Common', 'name': 'http', 'context': 'all'},
            {'partition': 'Common', 'name': 'tcp', 'context': 'all'},
            {'partition': 'Common', 'name': 'html', 'context': 'all'}
        ],
        'connectionLimit': 0,
        'ipProtocol': 'tcp',
        'vlans': [
            {'name': 'vlan', 'a': '1', 'b': '2'}
        ],
        'policies': [
            {'partition': 'test', 'name': 'ingress_172-16-3-59_80'}
        ]
    }

    assert merge(existing, desired) == expected