Merge pull request #20 from gnuoy/prep-for-opendev

Prepare for more to opendev
This commit is contained in:
Aurelien Lourot
2020-11-13 13:13:46 +01:00
committed by GitHub
19 changed files with 376 additions and 187 deletions

25
LICENSE
View File

@@ -175,3 +175,28 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,16 +0,0 @@
Format: http://dep.debian.net/deps/dep5/
Files: *
Copyright: Copyright 2015, Canonical Ltd., All Rights Reserved.
License: Apache License 2.0
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.

53
perf.py
View File

@@ -1,53 +0,0 @@
import sys
import subprocess
import threading
class Iperf():
"""
Install and start a server automatically
"""
try:
if not nextline:
mtu = None
except NameError:
mtu = None
# The following is a mess - since I'm installing iperf3 in the function
# Surely there is another easier way to get this into the charm?
def __init__(self):
#try:
# subprocess.check_call(['pgrep', 'iperf'], stderr=subprocess.STDOUT)
# if a:
thread = threading.Thread(target=self.start_server, args=())
thread.start()
#except:
# pass
#hookenv.log(sys.exc_info()[0], 'INFO')
def start_server(self):
process = subprocess.Popen(['iperf', '-s', '-m'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
nextline = process.stdout.readline()
nextline = nextline.decode("utf-8")
if nextline == '' and process.poll() is not None:
break
if "bits" in nextline:
self.speed = nextline.rsplit(' ', 2)[1]
sys.stdout.write(self.speed)
sys.stdout.write("\n")
if "MTU" in nextline:
self.mtu = nextline.rsplit(' ', 4)[1]
sys.stdout.write(self.mtu)
sys.stdout.flush()
#output = process.communicate()[0]
#exitCode = process.returncode
#
#output = exitCode
#if (exitCode == 0):
# pass
#elif exitCode:
# raise Exception(command, exitCode, output)
perf = Iperf()
#print (perf.mtu)

View File

@@ -1,3 +1,11 @@
charm-tools
# This file is managed centrally by release-tools and should not be modified
# within individual charm repos. See the 'global' dir contents for available
# choices of *requirements.txt files for OpenStack Charms:
# https://github.com/openstack-charmers/release-tools
#
setuptools<50.0.0 # https://github.com/pypa/setuptools/commit/04e3df22df840c6bb244e9b27bc56750c44b7c85
# Build requirements
charm-tools>=2.4.4
# importlib-resources 1.1.0 removed Python 3.5 support
importlib-resources<1.1.0
simplejson
flake8>=2.2.4,<=2.4.1

View File

@@ -175,3 +175,28 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,4 +1,16 @@
#!/usr/bin/env python
# Copyright 2020 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 os
import subprocess
@@ -536,7 +548,7 @@ def check_dns(nodes):
if unit_id in nofwd:
nofwd.remove(unit_id)
if ip != forward:
mstr = '(r\"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"'
mstr = r'(r\"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"'
if not re.match(mstr, forward):
forward = "Can not resolve hostname to IP {}"\
.format(repr(forward))

View File

@@ -1,3 +1,17 @@
# Copyright 2020 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.
# pylint: disable=unused-argument
from charms.reactive import when, when_not, set_state, remove_state
from charmhelpers.core import hookenv
@@ -64,9 +78,9 @@ def check_check_state(magpie):
Servers should only update their status after iperf has checked them
'''
if magpie.get_iperf_checked():
for units in magpie.get_iperf_checked():
if units and hookenv.local_unit() in units:
set_state('iperf.checked')
for units in magpie.get_iperf_checked():
if units and hookenv.local_unit() in units:
set_state('iperf.checked')
@when('magpie.joined', 'leadership.is_leader')

View File

@@ -0,0 +1,8 @@
# This file is managed centrally by release-tools and should not be modified
# within individual charm repos. See the 'global' dir contents for available
# choices of *requirements.txt files for OpenStack Charms:
# https://github.com/openstack-charmers/release-tools
#
# Functional Test Requirements (let Zaza's dependencies solve all dependencies here!)
git+https://github.com/openstack-charmers/zaza.git#egg=zaza
git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack

View File

@@ -1,21 +0,0 @@
#!/usr/bin/env python3
import unittest
import amulet
class TestDeploy(unittest.TestCase):
"""
Trivial deployment test for Magpie
"""
def test_deploy(self):
self.d = amulet.Deployment(series='xenial')
self.d.add('magpie', charm='~admcleod/magpie')
self.d.setup(timeout=900)
self.d.sentry.wait_for_messages({'magpie': 'Waiting for peers...'},
timeout=3600)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,83 +0,0 @@
#!/usr/bin/env python3
import re
import unittest
import amulet
class TestDeploy(unittest.TestCase):
"""
Deploy 2 peers and make sure their status messages contain "or" or "failed"
This does not test the substrate - only that the charms deploy and relate.
"""
@classmethod
def setUpClass(cls):
cls.d = amulet.Deployment(series='xenial')
cls.d.add('magpie', charm='~admcleod/magpie', units=2)
cls.d.setup(timeout=900)
cls.magpie_0 = cls.d.sentry['magpie'][0]
cls.magpie_1 = cls.d.sentry['magpie'][1]
def test_deploy(self):
self.d.sentry.wait_for_messages({'magpie': re.compile('ok|failed')},
timeout=60)
# following test is commented out until this bug is resolved;
# https://bugs.launchpad.net/juju/+bug/1623480
# def test_check_local_hostname(self):
# self.d.sentry.wait_for_messages({'magpie': {re.compile('.*hostname
# ok.*'}}, timeout=60)
def test_break_dns_single(self):
print('Test break dns single...')
"""
Break DNS on one unit, make sure DNS check fails, fix DNS, toggle
back
"""
self.d.sentry.wait_for_messages({'magpie': 'icmp ok, dns ok'},
timeout=60)
self.magpie_0.run("sudo mv /etc/resolv.conf /etc/resolv.conf.bak")
self.magpie_0.run("hooks/update-status")
self.d.sentry.wait_for_messages({'magpie':
{re.compile('.*dns failed*')}},
timeout=60)
self.magpie_0.run("sudo mv /etc/resolv.conf.bak /etc/resolv.conf")
self.magpie_0.run("hooks/update-status")
self.d.sentry.wait_for_messages({'magpie': 'icmp ok, dns ok'},
timeout=60)
def test_break_dns_all(self):
print('Test break dns all...')
"""
Set DNS with action to 255.255.255.255 - All units should fail DNS.
"""
self.d.configure('magpie', {'dns_server': '255.255.255.255'})
self.magpie_0.run("hooks/update-status")
self.magpie_1.run("hooks/update-status")
self.d.sentry.wait_for_messages({'magpie':
re.compile('icmp ok,.*dns failed.*')})
self.d.configure('magpie', {'dns_server': ''})
self.magpie_0.run("hooks/update-status")
self.magpie_1.run("hooks/update-status")
self.d.sentry.wait_for_messages({'magpie': 'icmp ok, dns ok'})
def test_break_ping_single(self):
print('Test break ping single')
"""
Take primary interface down and make sure ICMP fails.
"""
stoprestart = "(sudo service networking stop; sleep 60; service \
networking start) & "
self.magpie_1.run(stoprestart)
self.magpie_1.run("hooks/update-status")
self.d.sentry.wait_for_messages({'magpie':
{re.compile('icmp failed.*')}},
timeout=120)
self.magpie_1.run("hooks/update-status")
self.d.sentry.wait_for_messages({'magpie': {re.compile('icmp ok.*')}},
timeout=120)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,5 @@
series: bionic
applications:
magpie:
num_units: 3
charm: magpie

View File

@@ -0,0 +1,5 @@
series: focal
applications:
magpie:
num_units: 3
charm: magpie

View File

@@ -0,0 +1,5 @@
series: groovy
applications:
magpie:
num_units: 3
charm: magpie

View File

@@ -1,3 +1,15 @@
reset: false
packages:
- amulet
charm_name: magpie
gate_bundles:
- bionic
- focal
- groovy
smoke_bundles:
- focal
target_deploy_status:
magpie:
workload-status-message: "icmp ok"
tests:
- zaza.openstack.charm_tests.magpie.tests.MagpieTest
tests_options:
force_deploy:
- groovy

50
src/tox.ini Normal file
View File

@@ -0,0 +1,50 @@
# Source charm (with zaza): ./src/tox.ini
# This file is managed centrally by release-tools and should not be modified
# within individual charm repos. See the 'global' dir contents for available
# choices of tox.ini for OpenStack Charms:
# https://github.com/openstack-charmers/release-tools
[tox]
envlist = pep8
skipsdist = True
# NOTE: Avoid build/test env pollution by not enabling sitepackages.
sitepackages = False
# NOTE: Avoid false positives by not skipping missing interpreters.
skip_missing_interpreters = False
[testenv]
setenv = VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
whitelist_externals = juju
passenv = HOME TERM CS_* OS_* TEST_*
deps = -r{toxinidir}/test-requirements.txt
install_command =
pip install {opts} {packages}
[testenv:pep8]
basepython = python3
deps=charm-tools
commands = charm-proof
[testenv:func-noop]
basepython = python3
commands =
functest-run-suite --help
[testenv:func]
basepython = python3
commands =
functest-run-suite --keep-model
[testenv:func-smoke]
basepython = python3
commands =
functest-run-suite --keep-model --smoke
[testenv:func-target]
basepython = python3
commands =
functest-run-suite --keep-model --bundle {posargs}
[testenv:venv]
commands = {posargs}

View File

@@ -1,7 +1,24 @@
# Unit test requirements
flake8>=2.2.4,<=2.4.1
os-testr>=0.4.1
# This file is managed centrally by release-tools and should not be modified
# within individual charm repos. See the 'global' dir contents for available
# choices of *requirements.txt files for OpenStack Charms:
# https://github.com/openstack-charmers/release-tools
#
setuptools<50.0.0 # https://github.com/pypa/setuptools/commit/04e3df22df840c6bb244e9b27bc56750c44b7c85
# Lint and unit test requirements
flake8>=2.2.4
stestr>=2.2.0
requests>=2.18.4
charms.reactive
mock>=1.2
nose>=1.3.7
coverage>=3.6
git+https://github.com/openstack/charms.openstack#egg=charms.openstack
git+https://github.com/openstack/charms.openstack.git#egg=charms.openstack
#
# Revisit for removal / mock improvement:
netifaces # vault
psycopg2-binary # vault
tenacity # vault
pbr # vault
cryptography # vault, keystone-saml-mellon
lxml # keystone-saml-mellon
hvac # vault, barbican-vault

97
tox.ini Normal file
View File

@@ -0,0 +1,97 @@
# Source charm: ./tox.ini
# This file is managed centrally by release-tools and should not be modified
# within individual charm repos. See the 'global' dir contents for available
# choices of tox.ini for OpenStack Charms:
# https://github.com/openstack-charmers/release-tools
[tox]
skipsdist = True
envlist = pep8,py3
# NOTE: Avoid build/test env pollution by not enabling sitepackages.
sitepackages = False
# NOTE: Avoid false positives by not skipping missing interpreters.
skip_missing_interpreters = False
[testenv]
setenv = VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
TERM=linux
LAYER_PATH={toxinidir}/layers
INTERFACE_PATH={toxinidir}/interfaces
JUJU_REPOSITORY={toxinidir}/build
passenv = http_proxy https_proxy INTERFACE_PATH LAYER_PATH JUJU_REPOSITORY
install_command =
pip install {opts} {packages}
deps =
-r{toxinidir}/requirements.txt
[testenv:build]
basepython = python3
commands =
charm-build --log-level DEBUG -o {toxinidir}/build src {posargs}
[testenv:py3]
basepython = python3
deps = -r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
[testenv:py35]
basepython = python3.5
deps = -r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
[testenv:py36]
basepython = python3.6
deps = -r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
[testenv:py37]
basepython = python3.7
deps = -r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
[testenv:py38]
basepython = python3.8
deps = -r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
[testenv:pep8]
basepython = python3
deps = -r{toxinidir}/test-requirements.txt
commands = flake8 {posargs} src unit_tests
[testenv:cover]
# Technique based heavily upon
# https://github.com/openstack/nova/blob/master/tox.ini
basepython = python3
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
setenv =
{[testenv]setenv}
PYTHON=coverage run
commands =
coverage erase
stestr run --slowest {posargs}
coverage combine
coverage html -d cover
coverage xml -o cover/coverage.xml
coverage report
[coverage:run]
branch = True
concurrency = multiprocessing
parallel = True
source =
.
omit =
.tox/*
*/charmhelpers/*
unit_tests/*
[testenv:venv]
basepython = python3
commands = {posargs}
[flake8]
# E402 ignore necessary for path append before sys module import in actions
ignore = E402,W503,W504

View File

@@ -0,0 +1,24 @@
import lib.charms.layer.magpie_tools as magpie_tools
import unit_tests.test_utils
class TestMagpieTools(unit_tests.test_utils.CharmTestCase):
def setUp(self):
super(TestMagpieTools, self).setUp()
self.obj = self.tools = magpie_tools
self.patches = [
'hookenv']
self.patch_all()
def test_safe_status(self):
self.hookenv.config.return_value = {
'supress_status': False}
self.tools.safe_status('active', 'awesome')
self.hookenv.status_set.assert_called_once_with(
'active', 'awesome')
self.hookenv.status_set.reset_mock()
self.hookenv.config.return_value = {
'supress_status': True}
self.tools.safe_status('active', 'awesome')
self.assertFalse(self.hookenv.status_set.called)

55
unit_tests/test_utils.py Normal file
View File

@@ -0,0 +1,55 @@
import mock
import unittest
import unittest.mock
class CharmTestCase(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(self, method):
_m = unittest.mock.patch.object(self.obj, method)
mock = _m.start()
self.addCleanup(_m.stop)
return mock
def patch_all(self):
for method in self.patches:
setattr(self, method, self._patch(method))
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