Test nova to neutron port notification
Nova should update neutron in case port is attached to the instance that has been booted recently. We should see the log with server id and port id on one of the controller nodes. Change-Id: Id0b0a10cf83ee8d2173dd25b7417f6f8f96af3ac
This commit is contained in:
parent
0cafacd668
commit
705164604a
|
@ -28,6 +28,8 @@ OVN_CONTROLLER = _agent.OVN_CONTROLLER
|
|||
OVN_METADATA_AGENT = _agent.OVN_METADATA_AGENT
|
||||
AgentNotFoundOnHost = _agent.AgentNotFoundOnHost
|
||||
skip_if_missing_networking_agents = _agent.skip_if_missing_networking_agents
|
||||
skip_unless_is_ovn = _agent.skip_unless_is_ovn
|
||||
skip_unless_is_ovs = _agent.skip_unless_is_ovs
|
||||
list_networking_agents = _agent.list_networking_agents
|
||||
|
||||
|
||||
|
|
|
@ -69,3 +69,13 @@ def skip_if_missing_networking_agents(binary: typing.Optional[str] = None,
|
|||
', '.join("{!s}={!r}".format(k, v) for k, v in params.items()))
|
||||
return tobiko.skip_if(message, missing_networking_agents, count=count,
|
||||
**params)
|
||||
|
||||
|
||||
def skip_unless_is_ovn():
|
||||
'''Skip the test if ovn_controller agent does not exist'''
|
||||
return skip_if_missing_networking_agents(OVN_CONTROLLER)
|
||||
|
||||
|
||||
def skip_unless_is_ovs():
|
||||
'''Skip the test if openvswitch agent does not exist'''
|
||||
return skip_if_missing_networking_agents(OPENVSWITCH_AGENT)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
from __future__ import absolute_import
|
||||
|
||||
from tobiko.shell.files import _exception
|
||||
from tobiko.shell.files import _logs
|
||||
|
||||
|
||||
LogParserError = _exception.LogParserError
|
||||
LogFileNotFound = _exception.LogFileNotFound
|
||||
|
||||
LogFile = _logs.LogFile
|
||||
ClusterLogFile = _logs.ClusterLogFile
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import tobiko
|
||||
|
||||
|
||||
class LogParserError(tobiko.TobikoException):
|
||||
pass
|
||||
|
||||
|
||||
class LogFileNotFound(LogParserError):
|
||||
message = 'File {filename} was not found on {host}'
|
|
@ -0,0 +1,110 @@
|
|||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
from tobiko.shell import files
|
||||
from tobiko.shell import sh
|
||||
from tobiko.openstack import topology
|
||||
|
||||
|
||||
class LogFile(object):
|
||||
|
||||
def __init__(self, hostname, filename):
|
||||
self.filename = filename
|
||||
self.host = topology.get_openstack_node(hostname=hostname)
|
||||
self._list_logfiles()
|
||||
self.cmd = ''
|
||||
self.found = []
|
||||
|
||||
def find(self, regex):
|
||||
self._list_logfiles()
|
||||
self.cmd = f"zgrep -Eh {regex}"
|
||||
self.found = sh.execute(f'{self.cmd} {" ".join(self.logfiles)}',
|
||||
ssh_client=self.host.ssh_client,
|
||||
expect_exit_status=None,
|
||||
sudo=True).stdout.split('\n')
|
||||
try:
|
||||
self.found.remove('')
|
||||
except ValueError:
|
||||
pass
|
||||
return self.found
|
||||
|
||||
def find_new(self):
|
||||
self._list_logfiles()
|
||||
if not self.cmd:
|
||||
err_msg = 'find_new() method can be only executed after find()'
|
||||
raise files.LogParserError(message=err_msg)
|
||||
tmp = sh.execute(f'{self.cmd} {" ".join(self.logfiles)}',
|
||||
ssh_client=self.host.ssh_client,
|
||||
expect_exit_status=None,
|
||||
sudo=True).stdout.split('\n')
|
||||
found = []
|
||||
for log_string in tmp:
|
||||
if log_string not in self.found and log_string != '':
|
||||
found.append(log_string)
|
||||
self.found.append(log_string)
|
||||
return found
|
||||
|
||||
def _list_logfiles(self):
|
||||
file_path, file_name = os.path.split(self.filename)
|
||||
result = sh.execute(f'find {file_path} -name {file_name}*',
|
||||
ssh_client=self.host.ssh_client,
|
||||
expect_exit_status=None,
|
||||
sudo=True)
|
||||
self.logfiles = set(result.stdout.split('\n'))
|
||||
if '' in self.logfiles:
|
||||
self.logfiles.remove('')
|
||||
if self.logfiles == []:
|
||||
raise files.LogFileNotFound(filename=str(self.filename),
|
||||
host=str(self.host.name))
|
||||
|
||||
|
||||
class ClusterLogFile(object):
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.hostnames = []
|
||||
self.logfiles = []
|
||||
|
||||
def add_host(self, hostname):
|
||||
if hostname in self.hostnames:
|
||||
return
|
||||
self.hostnames.append(hostname)
|
||||
self.logfiles.append(LogFile(hostname, self.filename))
|
||||
|
||||
def add_group(self, group):
|
||||
for host in topology.list_openstack_nodes(group=group):
|
||||
self.add_host(host.name)
|
||||
|
||||
def find(self, regex):
|
||||
for logfile in self.logfiles:
|
||||
logfile.find(regex)
|
||||
return self.found
|
||||
|
||||
def find_new(self):
|
||||
new_lines = []
|
||||
for logfile in self.logfiles:
|
||||
new_lines += logfile.find_new()
|
||||
return new_lines
|
||||
|
||||
@property
|
||||
def found(self):
|
||||
found = []
|
||||
for logfile in self.logfiles:
|
||||
found += logfile.found
|
||||
return found
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
from __future__ import absolute_import
|
|
@ -18,9 +18,11 @@ import netaddr
|
|||
import testtools
|
||||
|
||||
import tobiko
|
||||
from tobiko.shell import files
|
||||
from tobiko.shell import ping
|
||||
from tobiko.shell import ip
|
||||
from tobiko.openstack import neutron
|
||||
from tobiko.openstack import nova
|
||||
from tobiko.openstack import stacks
|
||||
|
||||
|
||||
|
@ -98,3 +100,32 @@ class CentosServerL3HAPortTestWith(PortTest):
|
|||
class UbuntuServerL3HAPortTestWith(PortTest):
|
||||
#: Resources stack with floating IP and Nova server
|
||||
stack = tobiko.required_setup_fixture(stacks.L3haUbuntuServerStackFixture)
|
||||
|
||||
|
||||
class PortLogsStack(stacks.CirrosServerStackFixture):
|
||||
pass
|
||||
|
||||
|
||||
@neutron.skip_unless_is_ovs()
|
||||
class PortLogs(testtools.TestCase):
|
||||
|
||||
stack = tobiko.required_setup_fixture(PortLogsStack)
|
||||
|
||||
def test_nova_port_notification(self):
|
||||
expected_logfile = '/var/log/containers/neutron/server.log'
|
||||
logfile = files.ClusterLogFile(expected_logfile)
|
||||
try:
|
||||
logfile.add_group('controller')
|
||||
except files.LogFileNotFound as ex:
|
||||
tobiko.skip(str(ex))
|
||||
logfile.find(f'Nova.+event.+response.*{self.stack.server_id}')
|
||||
nova.shutoff_server(self.stack.server_id)
|
||||
nova.activate_server(self.stack.server_id)
|
||||
new_events = logfile.find_new()
|
||||
self.assertEqual(len(new_events), 2)
|
||||
self.assertTrue(
|
||||
any('network-vif-unplugged' in event for event in new_events))
|
||||
self.assertTrue(
|
||||
any('network-vif-plugged' in event for event in new_events))
|
||||
self.assertTrue(
|
||||
all(self.stack.port_id in event for event in new_events))
|
||||
|
|
Loading…
Reference in New Issue