Merge pull request #48 from openstack-charmers/dragent-infer-asn-from-deployment
Infer ASn from deployment for BGP speaker test configuration
This commit is contained in:
@@ -72,8 +72,18 @@ class TestModel(ut_utils.BaseTestCase):
|
||||
self.unit1.is_leader_from_status.side_effect = _is_leader(False)
|
||||
self.unit2.is_leader_from_status.side_effect = _is_leader(True)
|
||||
self.units = [self.unit1, self.unit2]
|
||||
self.relation1 = mock.MagicMock()
|
||||
self.relation1.id = 42
|
||||
self.relation1.matches.side_effect = \
|
||||
lambda x: True if x == 'app' else False
|
||||
self.relation2 = mock.MagicMock()
|
||||
self.relation2.id = 51
|
||||
self.relation2.matches.side_effect = \
|
||||
lambda x: True if x == 'app:interface' else False
|
||||
self.relations = [self.relation1, self.relation2]
|
||||
_units = mock.MagicMock()
|
||||
_units.units = self.units
|
||||
_units.relations = self.relations
|
||||
self.mymodel = mock.MagicMock()
|
||||
self.mymodel.applications = {
|
||||
'app': _units
|
||||
@@ -179,6 +189,17 @@ class TestModel(ut_utils.BaseTestCase):
|
||||
expected)
|
||||
self.unit1.run.assert_called_once_with(cmd, timeout=None)
|
||||
|
||||
def test_get_relation_id(self):
|
||||
self.patch_object(model, 'Model')
|
||||
self.Model.return_value = self.Model_mock
|
||||
self.assertEqual(model.get_relation_id('testmodel', 'app', 'app'), 42)
|
||||
|
||||
def test_get_relation_id_interface(self):
|
||||
self.patch_object(model, 'Model')
|
||||
self.Model.return_value = self.Model_mock
|
||||
self.assertEqual(model.get_relation_id('testmodel', 'app', 'app',
|
||||
'interface'), 51)
|
||||
|
||||
def test_run_action(self):
|
||||
self.patch_object(model, 'Model')
|
||||
self.patch_object(model, 'get_unit_from_name')
|
||||
|
||||
@@ -155,3 +155,46 @@ class TestJujuUtils(ut_utils.BaseTestCase):
|
||||
# Fatal failure
|
||||
with self.assertRaises(Exception):
|
||||
juju_utils.remote_run(self.unit, _cmd, fatal=True)
|
||||
|
||||
def test_get_unit_names(self):
|
||||
self.patch('zaza.model.get_first_unit_name', new_callable=mock.Mock(),
|
||||
name='_get_first_unit_name')
|
||||
juju_utils._get_unit_names(['aunit/0', 'otherunit/0'])
|
||||
self.assertFalse(self._get_first_unit_name.called)
|
||||
|
||||
def test_get_unit_names_called_with_application_name(self):
|
||||
self.patch_object(juju_utils, 'model')
|
||||
juju_utils._get_unit_names(['aunit', 'otherunit/0'])
|
||||
self.model.get_first_unit_name.assert_called()
|
||||
|
||||
def test_get_relation_from_unit(self):
|
||||
self.patch_object(juju_utils, 'lifecycle_utils')
|
||||
self.patch_object(juju_utils, '_get_unit_names')
|
||||
self.patch_object(juju_utils, 'yaml')
|
||||
self.patch_object(juju_utils, 'model')
|
||||
self._get_unit_names.return_value = ['aunit/0', 'otherunit/0']
|
||||
data = {'foo': 'bar'}
|
||||
self.model.get_relation_id.return_value = 42
|
||||
self.model.run_on_unit.return_value = {'Code': 0, 'Stdout': str(data)}
|
||||
juju_utils.get_relation_from_unit('aunit/0', 'otherunit/0',
|
||||
'arelation')
|
||||
self.model.run_on_unit.assert_called_with(
|
||||
self.lifecycle_utils.get_juju_model(), 'aunit/0',
|
||||
'relation-get --format=yaml -r "42" - "otherunit/0"')
|
||||
self.yaml.load.assert_called_with(str(data))
|
||||
|
||||
def test_get_relation_from_unit_fails(self):
|
||||
self.patch_object(juju_utils, 'lifecycle_utils')
|
||||
self.patch_object(juju_utils, '_get_unit_names')
|
||||
self.patch_object(juju_utils, 'yaml')
|
||||
self.patch_object(juju_utils, 'model')
|
||||
self._get_unit_names.return_value = ['aunit/0', 'otherunit/0']
|
||||
self.model.get_relation_id.return_value = 42
|
||||
self.model.run_on_unit.return_value = {'Code': 1, 'Stderr': 'ERROR'}
|
||||
with self.assertRaises(Exception):
|
||||
juju_utils.get_relation_from_unit('aunit/0', 'otherunit/0',
|
||||
'arelation')
|
||||
self.model.run_on_unit.assert_called_with(
|
||||
self.lifecycle_utils.get_juju_model(), 'aunit/0',
|
||||
'relation-get --format=yaml -r "42" - "otherunit/0"')
|
||||
self.assertFalse(self.yaml.load.called)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import unittest
|
||||
|
||||
from zaza.utilities import generic as generic_utils
|
||||
from zaza.utilities import cli as cli_utils
|
||||
from zaza.charm_tests.dragent import test
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class DRAgentTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
generic_utils.setup_logging()
|
||||
cli_utils.setup_logging()
|
||||
|
||||
def test_bgp_routes(self):
|
||||
test.test_bgp_routes(peer_application_name=self.BGP_PEER_APPLICATION)
|
||||
|
||||
@@ -6,6 +6,7 @@ import sys
|
||||
from zaza.utilities import (
|
||||
cli as cli_utils,
|
||||
openstack as openstack_utils,
|
||||
juju as juju_utils,
|
||||
)
|
||||
|
||||
|
||||
@@ -24,6 +25,19 @@ def setup_bgp_speaker(peer_application_name, keystone_session=None):
|
||||
:returns: None
|
||||
:rtype: None
|
||||
"""
|
||||
# Get ASNs from deployment
|
||||
dr_relation = juju_utils.get_relation_from_unit(
|
||||
'neutron-dynamic-routing',
|
||||
peer_application_name,
|
||||
'bgpclient')
|
||||
peer_asn = dr_relation.get('asn')
|
||||
logging.debug('peer ASn: "{}"'.format(peer_asn))
|
||||
peer_relation = juju_utils.get_relation_from_unit(
|
||||
peer_application_name,
|
||||
'neutron-dynamic-routing',
|
||||
'bgp-speaker')
|
||||
dr_asn = peer_relation.get('asn')
|
||||
logging.debug('our ASn: "{}"'.format(dr_asn))
|
||||
|
||||
# If a session has not been provided, acquire one
|
||||
if not keystone_session:
|
||||
@@ -36,7 +50,7 @@ def setup_bgp_speaker(peer_application_name, keystone_session=None):
|
||||
# Create BGP speaker
|
||||
logging.info("Setting up BGP speaker")
|
||||
bgp_speaker = openstack_utils.create_bgp_speaker(
|
||||
neutron_client, local_as=12345)
|
||||
neutron_client, local_as=dr_asn)
|
||||
|
||||
# Add networks to bgp speaker
|
||||
logging.info("Advertising BGP routes")
|
||||
@@ -53,7 +67,7 @@ def setup_bgp_speaker(peer_application_name, keystone_session=None):
|
||||
logging.info("Setting up BGP peer")
|
||||
bgp_peer = openstack_utils.create_bgp_peer(neutron_client,
|
||||
peer_application_name,
|
||||
remote_as=10000)
|
||||
remote_as=peer_asn)
|
||||
# Add peer to bgp speaker
|
||||
logging.info("Adding BGP peer to BGP speaker")
|
||||
openstack_utils.add_peer_to_bgp_speaker(
|
||||
|
||||
@@ -654,6 +654,35 @@ block_until_file_has_contents = sync_wrapper(
|
||||
async_block_until_file_has_contents)
|
||||
|
||||
|
||||
async def async_get_relation_id(model_name, application_name,
|
||||
remote_application_name,
|
||||
remote_interface_name=None):
|
||||
"""
|
||||
Get relation id of relation from model
|
||||
|
||||
:param model_name: Name of model to operate on
|
||||
:type model_name: str
|
||||
:param application_name: Name of application on this side of relation
|
||||
:type application_name: str
|
||||
:param remote_application_name: Name of application on other side of
|
||||
relation
|
||||
:type remote_application_name: str
|
||||
:param remote_interface_name: Name of interface on remote end of relation
|
||||
:type remote_interface_name: Optional(str)
|
||||
:returns: Relation id of relation if found or None
|
||||
:rtype: any
|
||||
"""
|
||||
async with run_in_model(model_name) as model:
|
||||
for rel in model.applications[application_name].relations:
|
||||
spec = '{}'.format(remote_application_name)
|
||||
if remote_interface_name is not None:
|
||||
spec += ':{}'.format(remote_interface_name)
|
||||
if rel.matches(spec):
|
||||
return(rel.id)
|
||||
|
||||
get_relation_id = sync_wrapper(async_get_relation_id)
|
||||
|
||||
|
||||
def main():
|
||||
# Run the deploy coroutine in an asyncio event loop, using a helper
|
||||
# that abstracts loop creation and teardown.
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# 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 os
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
|
||||
from zaza import (
|
||||
model,
|
||||
@@ -169,3 +183,61 @@ def remote_run(unit, remote_cmd, timeout=None, fatal=None):
|
||||
raise Exception("Error running remote command: {}"
|
||||
.format(result.get("Stderr")))
|
||||
return result.get("Stderr")
|
||||
|
||||
|
||||
def _get_unit_names(names):
|
||||
"""
|
||||
Helper function that resolves application names to first unit name of
|
||||
said application. Any already resolved unit names are returned as-is.
|
||||
|
||||
:param names: List of units/applications to translate
|
||||
:type names: list(str)
|
||||
:returns: List of units
|
||||
:rtype: list(str)
|
||||
"""
|
||||
result = []
|
||||
for name in names:
|
||||
if '/' in name:
|
||||
result.append(name)
|
||||
else:
|
||||
result.append(
|
||||
model.get_first_unit_name(lifecycle_utils.get_juju_model(),
|
||||
name))
|
||||
return result
|
||||
|
||||
|
||||
def get_relation_from_unit(entity, remote_entity, remote_interface_name):
|
||||
"""
|
||||
Get relation data for relation with `remote_interface_name` between
|
||||
`entity` and `remote_entity` from the perspective of `entity`.
|
||||
|
||||
`entity` and `remote_entity` may refer to either a application or a
|
||||
specific unit. If application name is given first unit is found in model.
|
||||
|
||||
:param entity: Application or unit to get relation data from
|
||||
:type entity: str
|
||||
:param remote_entity: Application or Unit in the other end of the relation
|
||||
we want to query
|
||||
:type remote_entity: str
|
||||
:param remote_interface_name: Name of interface to query on remote end of
|
||||
relation
|
||||
:type remote_interface_name: str
|
||||
:returns: dict with relation data
|
||||
:rtype: dict
|
||||
"""
|
||||
application = entity.split('/')[0]
|
||||
remote_application = remote_entity.split('/')[0]
|
||||
rid = model.get_relation_id(lifecycle_utils.get_juju_model(), application,
|
||||
remote_application,
|
||||
remote_interface_name=remote_interface_name)
|
||||
(unit, remote_unit) = _get_unit_names([entity, remote_entity])
|
||||
result = model.run_on_unit(
|
||||
lifecycle_utils.get_juju_model(), unit,
|
||||
'relation-get --format=yaml -r "{}" - "{}"'
|
||||
.format(rid, remote_unit)
|
||||
)
|
||||
if result and int(result.get('Code')) == 0:
|
||||
return yaml.load(result.get('Stdout'))
|
||||
else:
|
||||
raise Exception('Error running remote command: "{}"'
|
||||
.format(result.get("Stderr")))
|
||||
|
||||
Reference in New Issue
Block a user