Fix generate_asn mask and add unit tests
Change-Id: I18776f026ba288a150e783e4102006e690572d6f
This commit is contained in:
parent
1ff8ee74a1
commit
3eca0062fd
3
.stestr.conf
Normal file
3
.stestr.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
test_path=./unit_tests
|
||||||
|
top_dir=./
|
@ -2,3 +2,8 @@ name: bgp
|
|||||||
summary: BGP interface
|
summary: BGP interface
|
||||||
version: 1
|
version: 1
|
||||||
repo: https://github.com/openstack-charmers/charm-interface-bgp.git
|
repo: https://github.com/openstack-charmers/charm-interface-bgp.git
|
||||||
|
ignore:
|
||||||
|
- 'unit_tests'
|
||||||
|
- '.stestr.conf'
|
||||||
|
- 'test-requirements.txt'
|
||||||
|
- 'tox.ini'
|
||||||
|
@ -45,16 +45,13 @@ class BGPEndpoint(reactive.Endpoint):
|
|||||||
4 200 000 000 - 4 211 081 214
|
4 200 000 000 - 4 211 081 214
|
||||||
"""
|
"""
|
||||||
asn_base = 4211081215
|
asn_base = 4211081215
|
||||||
mask = netaddr.IPAddress('5.255.255.255')
|
mask = netaddr.IPAddress('4.255.255.255')
|
||||||
unit_ip = netaddr.IPAddress(
|
unit_ip = netaddr.IPAddress(
|
||||||
ch_core.hookenv.unit_get('private-address'))
|
ch_core.hookenv.unit_get('private-address'))
|
||||||
masked_ip = unit_ip & mask
|
masked_ip = unit_ip & mask
|
||||||
|
|
||||||
asn = asn_base + int(masked_ip)
|
asn = asn_base + int(masked_ip)
|
||||||
|
|
||||||
# XXX: This assert should be removed from code and put in a unit test
|
|
||||||
assert asn <= 4294967294
|
|
||||||
|
|
||||||
return asn
|
return asn
|
||||||
|
|
||||||
def publish_info(self, asn=None, passive=False, bindings=None):
|
def publish_info(self, asn=None, passive=False, bindings=None):
|
||||||
|
6
test-requirements.txt
Normal file
6
test-requirements.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Lint and unit test requirements
|
||||||
|
flake8
|
||||||
|
os-testr>=0.4.1
|
||||||
|
charms.reactive
|
||||||
|
mock>=1.2
|
||||||
|
coverage>=3.6
|
23
tox.ini
Normal file
23
tox.ini
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[tox]
|
||||||
|
skipsdist = True
|
||||||
|
envlist = pep8,py35
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
setenv = VIRTUAL_ENV={envdir}
|
||||||
|
PYTHONHASHSEED=0
|
||||||
|
TERM=linux
|
||||||
|
install_command =
|
||||||
|
pip install {opts} {packages}
|
||||||
|
|
||||||
|
[testenv:py35]
|
||||||
|
basepython = python3
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = ostestr {posargs}
|
||||||
|
|
||||||
|
[testenv:pep8]
|
||||||
|
basepython = python3
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = flake8 {posargs} . unit_tests
|
||||||
|
|
||||||
|
[testenv:venv]
|
||||||
|
commands = {posargs}
|
87
unit_tests/__init__.py
Normal file
87
unit_tests/__init__.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# Copyright 2016 Canonical Ltd
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import mock
|
||||||
|
|
||||||
|
# mock out some charmhelpers libraries as they have apt install side effects
|
||||||
|
apt_pkg = mock.MagicMock()
|
||||||
|
charmhelpers = mock.MagicMock()
|
||||||
|
sys.modules['apt_pkg'] = apt_pkg
|
||||||
|
sys.modules['charmhelpers'] = charmhelpers
|
||||||
|
sys.modules['charmhelpers.core'] = charmhelpers.core
|
||||||
|
sys.modules['charmhelpers.core.decorators'] = charmhelpers.core.decorators
|
||||||
|
sys.modules['charmhelpers.core.hookenv'] = charmhelpers.core.hookenv
|
||||||
|
sys.modules['charmhelpers.core.host'] = charmhelpers.core.host
|
||||||
|
sys.modules['charmhelpers.core.templating'] = charmhelpers.core.templating
|
||||||
|
sys.modules['charmhelpers.core.unitdata'] = charmhelpers.core.unitdata
|
||||||
|
sys.modules['charmhelpers.contrib'] = charmhelpers.contrib
|
||||||
|
sys.modules['charmhelpers.contrib.openstack'] = charmhelpers.contrib.openstack
|
||||||
|
sys.modules['charmhelpers.contrib.openstack.ha'] = (
|
||||||
|
charmhelpers.contrib.openstack.ha)
|
||||||
|
sys.modules['charmhelpers.contrib.openstack.utils'] = (
|
||||||
|
charmhelpers.contrib.openstack.utils)
|
||||||
|
sys.modules['charmhelpers.contrib.openstack.templating'] = (
|
||||||
|
charmhelpers.contrib.openstack.templating)
|
||||||
|
sys.modules['charmhelpers.contrib.openstack.context'] = (
|
||||||
|
charmhelpers.contrib.openstack.context)
|
||||||
|
sys.modules['charmhelpers.contrib.network'] = charmhelpers.contrib.network
|
||||||
|
sys.modules['charmhelpers.contrib.network.ip'] = (
|
||||||
|
charmhelpers.contrib.network.ip)
|
||||||
|
sys.modules['charmhelpers.fetch'] = charmhelpers.fetch
|
||||||
|
sys.modules['charmhelpers.cli'] = charmhelpers.cli
|
||||||
|
sys.modules['charmhelpers.contrib.hahelpers'] = charmhelpers.contrib.hahelpers
|
||||||
|
sys.modules['charmhelpers.contrib.hahelpers.cluster'] = (
|
||||||
|
charmhelpers.contrib.hahelpers.cluster)
|
||||||
|
|
||||||
|
# mock in the openstack releases so that the tests can run
|
||||||
|
# Note that these don't need to be maintained UNLESS new functionality is for
|
||||||
|
# later OpenStack releases.
|
||||||
|
charmhelpers.contrib.openstack.utils.OPENSTACK_RELEASES = (
|
||||||
|
'diablo',
|
||||||
|
'essex',
|
||||||
|
'folsom',
|
||||||
|
'grizzly',
|
||||||
|
'havana',
|
||||||
|
'icehouse',
|
||||||
|
'juno',
|
||||||
|
'kilo',
|
||||||
|
'liberty',
|
||||||
|
'mitaka',
|
||||||
|
'newton',
|
||||||
|
'ocata',
|
||||||
|
'pike',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _fake_retry(num_retries, base_delay=0, exc_type=Exception):
|
||||||
|
def _retry_on_exception_inner_1(f):
|
||||||
|
def _retry_on_exception_inner_2(*args, **kwargs):
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return _retry_on_exception_inner_2
|
||||||
|
return _retry_on_exception_inner_1
|
||||||
|
|
||||||
|
|
||||||
|
mock.patch(
|
||||||
|
'charmhelpers.core.decorators.retry_on_exception',
|
||||||
|
_fake_retry).start()
|
||||||
|
|
||||||
|
|
||||||
|
def _fake_cached(f):
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
mock.patch(
|
||||||
|
'charmhelpers.core.hookenv.cached',
|
||||||
|
_fake_cached).start()
|
32
unit_tests/test_provides.py
Normal file
32
unit_tests/test_provides.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Copyright 2018 Canonical Ltd.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import unit_tests.utils as ut_utils
|
||||||
|
import provides
|
||||||
|
|
||||||
|
|
||||||
|
class TestBGPProvides(ut_utils.BaseTestCase):
|
||||||
|
def test_generate_asn_min(self):
|
||||||
|
self.patch_object(provides, 'ch_core')
|
||||||
|
self.ch_core.hookenv.unit_get.return_value = '0.0.0.0'
|
||||||
|
endpoint = provides.BGPEndpoint('bgpserver')
|
||||||
|
asn = endpoint.generate_asn()
|
||||||
|
self.assertEqual(asn, 4211081215)
|
||||||
|
|
||||||
|
def test_generate_asn_max(self):
|
||||||
|
self.patch_object(provides, 'ch_core')
|
||||||
|
self.ch_core.hookenv.unit_get.return_value = '255.255.255.255'
|
||||||
|
endpoint = provides.BGPEndpoint('bgpserver')
|
||||||
|
asn = endpoint.generate_asn()
|
||||||
|
self.assertEqual(asn, 4294967294)
|
82
unit_tests/utils.py
Normal file
82
unit_tests/utils.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
# Note that the unit_tests/__init__.py also mocks out two charmhelpers imports
|
||||||
|
# that have side effects that try to apt install modules:
|
||||||
|
# sys.modules['charmhelpers.contrib.openstack.utils'] = mock.MagicMock()
|
||||||
|
# sys.modules['charmhelpers.contrib.network.ip'] = mock.MagicMock()
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import io
|
||||||
|
import mock
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def patch_open():
|
||||||
|
'''Patch open() to allow mocking both open() itself and the file that is
|
||||||
|
yielded.
|
||||||
|
|
||||||
|
Yields the mock for "open" and "file", respectively.'''
|
||||||
|
mock_open = mock.MagicMock(spec=open)
|
||||||
|
mock_file = mock.MagicMock(spec=io.FileIO)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def stub_open(*args, **kwargs):
|
||||||
|
mock_open(*args, **kwargs)
|
||||||
|
yield mock_file
|
||||||
|
|
||||||
|
with mock.patch('builtins.open', stub_open):
|
||||||
|
yield mock_open, mock_file
|
||||||
|
|
||||||
|
|
||||||
|
class BaseTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self._patches = {}
|
||||||
|
self._patches_start = {}
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for k, v in self._patches.items():
|
||||||
|
v.stop()
|
||||||
|
setattr(self, k, None)
|
||||||
|
self._patches = None
|
||||||
|
self._patches_start = None
|
||||||
|
|
||||||
|
def patch_object(self, obj, attr, return_value=None, name=None, new=None,
|
||||||
|
**kwargs):
|
||||||
|
if name is None:
|
||||||
|
name = attr
|
||||||
|
if new is not None:
|
||||||
|
mocked = mock.patch.object(obj, attr, new=new, **kwargs)
|
||||||
|
else:
|
||||||
|
mocked = mock.patch.object(obj, attr, **kwargs)
|
||||||
|
self._patches[name] = mocked
|
||||||
|
started = mocked.start()
|
||||||
|
if new is None:
|
||||||
|
started.return_value = return_value
|
||||||
|
self._patches_start[name] = started
|
||||||
|
setattr(self, name, started)
|
||||||
|
|
||||||
|
def patch(self, item, return_value=None, name=None, new=None, **kwargs):
|
||||||
|
if name is None:
|
||||||
|
raise RuntimeError("Must pass 'name' to .patch()")
|
||||||
|
if new is not None:
|
||||||
|
mocked = mock.patch(item, new=new, **kwargs)
|
||||||
|
else:
|
||||||
|
mocked = mock.patch(item, **kwargs)
|
||||||
|
self._patches[name] = mocked
|
||||||
|
started = mocked.start()
|
||||||
|
if new is None:
|
||||||
|
started.return_value = return_value
|
||||||
|
self._patches_start[name] = started
|
||||||
|
setattr(self, name, started)
|
Loading…
Reference in New Issue
Block a user