From 820ed8083d6bdf160d4cce00f85cb7b656731c91 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 3 Aug 2018 16:18:37 +0200 Subject: [PATCH] Remove and add BGP speaker from dragent Workaround for LP: #1784083 --- .../test_zaza_utilities_openstack.py | 50 +++++++++++++++++ zaza/configure/bgp_speaker.py | 35 ++++++++++++ zaza/utilities/exceptions.py | 12 +++++ zaza/utilities/openstack.py | 54 +++++++++++++++++++ 4 files changed, 151 insertions(+) diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index 599a1e2..ea63dc5 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -48,6 +48,19 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): self.networks = { "networks": [self.network["network"]]} + self.agents = { + "agents": [ + { + 'id': '7f3afd5b-ff6d-4df3-be0e-3d9651e71873', + 'binary': 'neutron-bgp-dragent', + }]} + + self.bgp_speakers = { + "bgp_speakers": [ + { + 'id': '07a0798d-c29c-4a92-8fcb-c1ec56934729', + }]} + self.neutronclient = mock.MagicMock() self.neutronclient.list_ports.return_value = self.ports self.neutronclient.create_port.return_value = self.port @@ -63,6 +76,10 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): self.neutronclient.list_networks.return_value = self.networks self.neutronclient.create_network.return_value = self.network + self.neutronclient.list_agents.return_value = self.agents + self.neutronclient.list_bgp_speaker_on_dragent.return_value = \ + self.bgp_speakers + def test_create_port(self): self.patch_object(openstack_utils, "get_net_uuid") self.get_net_uuid.return_value = self.net_uuid @@ -595,3 +612,36 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): password='', pkey='akey', username='bob') + + def test_neutron_agent_appears(self): + self.assertEqual( + openstack_utils.neutron_agent_appears(self.neutronclient, + 'neutron-bgp-dragent'), + self.agents) + + def test_neutron_agent_appears_not(self): + _neutronclient = copy.deepcopy(self.neutronclient) + _neutronclient.list_agents.return_value = {'agents': []} + openstack_utils.neutron_agent_appears.retry.stop = \ + tenacity.stop_after_attempt(1) + with self.assertRaises(exceptions.NeutronAgentMissing): + openstack_utils.neutron_agent_appears(_neutronclient, + 'non-existent') + + def test_neutron_bgp_speaker_appears_on_agent(self): + openstack_utils.neutron_bgp_speaker_appears_on_agent.retry.stop = \ + tenacity.stop_after_attempt(1) + self.assertEqual( + openstack_utils.neutron_bgp_speaker_appears_on_agent( + self.neutronclient, 'FAKE_AGENT_ID'), + self.bgp_speakers) + + def test_neutron_bgp_speaker_appears_not_on_agent(self): + _neutronclient = copy.deepcopy(self.neutronclient) + _neutronclient.list_bgp_speaker_on_dragent.return_value = { + 'bgp_speakers': []} + openstack_utils.neutron_bgp_speaker_appears_on_agent.retry.stop = \ + tenacity.stop_after_attempt(1) + with self.assertRaises(exceptions.NeutronBGPSpeakerMissing): + openstack_utils.neutron_bgp_speaker_appears_on_agent( + _neutronclient, 'FAKE_AGENT_ID') diff --git a/zaza/configure/bgp_speaker.py b/zaza/configure/bgp_speaker.py index f2fcc45..cb38eba 100755 --- a/zaza/configure/bgp_speaker.py +++ b/zaza/configure/bgp_speaker.py @@ -4,6 +4,7 @@ import argparse import logging import sys +import neutronclient from zaza.utilities import ( cli as cli_utils, openstack as openstack_utils, @@ -83,6 +84,40 @@ def setup_bgp_speaker(peer_application_name, keystone_session=None): "Advertised floating IP: {}".format( floating_ip["floating_ip_address"])) + # NOTE(fnordahl): As a workaround for LP: #1784083 remove BGP speaker from + # dragent and add it back. + logging.info( + "Waiting for Neutron agent 'neutron-bgp-dragent' to appear...") + keystone_session = openstack_utils.get_overcloud_keystone_session() + neutron_client = openstack_utils.get_neutron_session_client( + keystone_session) + agents = openstack_utils.neutron_agent_appears(neutron_client, + 'neutron-bgp-dragent') + agent_id = None + for agent in agents.get('agents', []): + agent_id = agent.get('id', None) + if agent_id is not None: + break + logging.info( + 'Waiting for BGP speaker to appear on agent "{}"...'.format(agent_id)) + bgp_speakers = openstack_utils.neutron_bgp_speaker_appears_on_agent( + neutron_client, agent_id) + logging.info( + "Removing and adding back bgp-speakers to agent (LP: #1784083)...") + while True: + try: + for bgp_speaker in bgp_speakers.get('bgp_speakers', []): + bgp_speaker_id = bgp_speaker.get('id', None) + logging.info('removing "{}" from "{}"' + ''.format(bgp_speaker_id, agent_id)) + neutron_client.remove_bgp_speaker_from_dragent( + agent_id, bgp_speaker_id) + except neutronclient.common.exceptions.NotFound as e: + logging.info('Exception: "{}"'.format(e)) + break + neutron_client.add_bgp_speaker_to_dragent( + agent_id, {'bgp_speaker_id': bgp_speaker_id}) + def run_from_cli(): """Run BGP Speaker setup from CLI. diff --git a/zaza/utilities/exceptions.py b/zaza/utilities/exceptions.py index f317552..453f29f 100644 --- a/zaza/utilities/exceptions.py +++ b/zaza/utilities/exceptions.py @@ -17,3 +17,15 @@ class SSHFailed(Exception): """SSH failed.""" pass + + +class NeutronAgentMissing(Exception): + """Agent binary does not appear in the Neutron agent list.""" + + pass + + +class NeutronBGPSpeakerMissing(Exception): + """No BGP speaker appeared on agent.""" + + pass diff --git a/zaza/utilities/openstack.py b/zaza/utilities/openstack.py index c8c767c..0e6f8aa 100644 --- a/zaza/utilities/openstack.py +++ b/zaza/utilities/openstack.py @@ -1658,3 +1658,57 @@ def ssh_test(username, ip, vm_name, password=None, privkey=None): return_string, vm_name)) raise exceptions.SSHFailed() + + +@tenacity.retry(wait=tenacity.wait_exponential(multiplier=0.01), + reraise=True, stop=tenacity.stop_after_delay(60) | + tenacity.stop_after_attempt(100)) +def neutron_agent_appears(neutron_client, binary): + """Wait for Neutron agent to appear and return agent_id. + + :param neutron_client: Neutron client + :type neutron_client: Pointer to Neutron client object + :param binary: Name of agent we want to appear + :type binary: str + :returns: result set from Neutron list_agents call + :rtype: dict + :raises: exceptions.NeutronAgentMissing + """ + result = neutron_client.list_agents(binary=binary) + for agent in result.get('agents', []): + agent_id = agent.get('id', None) + if agent_id: + break + else: + raise exceptions.NeutronAgentMissing( + 'no agents for binary "{}"'.format(binary)) + return result + + +@tenacity.retry(wait=tenacity.wait_exponential(multiplier=0.01), + reraise=True, + stop=tenacity.stop_after_delay(60) | + tenacity.stop_after_attempt(100)) +def neutron_bgp_speaker_appears_on_agent(neutron_client, agent_id): + """Wait for Neutron BGP speaker to appear on agent. + + :param neutron_client: Neutron client + :type neutron_client: Pointer to Neutron client object + :param agent_id: Neutron agent UUID + :type agent_id: str + :param speaker_id: Neutron BGP speaker UUID + :type speaker_id: str + :returns: result set from Neutron list_bgp_speaker_on_dragent call + :rtype: dict + :raises: exceptions.NeutronBGPSpeakerMissing + """ + result = neutron_client.list_bgp_speaker_on_dragent(agent_id) + for bgp_speaker in result.get('bgp_speakers', []): + bgp_speaker_id = bgp_speaker.get('id', None) + if bgp_speaker_id: + break + else: + raise exceptions.NeutronBGPSpeakerMissing( + 'No BGP Speaker appeared on agent "{}"' + ''.format(agent_id)) + return result