# Copyright 2015 Red Hat, Inc. # # 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 datetime from distutils import spawn import os import re import signal import fixtures from neutronclient.common import exceptions as nc_exc from neutronclient.v2_0 import client from oslo_log import log as logging from oslo_utils import fileutils from neutron.agent.linux import async_process from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.common import utils as common_utils from neutron.tests import base from neutron.tests.common import net_helpers from neutron.tests.fullstack import base as fullstack_base LOG = logging.getLogger(__name__) class ProcessFixture(fixtures.Fixture): def __init__(self, test_name, process_name, exec_name, config_filenames, namespace=None, kill_signal=signal.SIGKILL): super(ProcessFixture, self).__init__() self.test_name = test_name self.process_name = process_name self.exec_name = exec_name self.config_filenames = config_filenames self.process = None self.kill_signal = kill_signal self.namespace = namespace def _setUp(self): self.start() self.addCleanup(self.stop) def start(self): test_name = base.sanitize_log_path(self.test_name) log_dir = os.path.join(fullstack_base.DEFAULT_LOG_DIR, test_name) fileutils.ensure_tree(log_dir, mode=0o755) timestamp = datetime.datetime.now().strftime("%Y-%m-%d--%H-%M-%S-%f") log_file = "%s--%s.log" % (self.process_name, timestamp) run_as_root = bool(self.namespace) exec_name = (self.exec_name if run_as_root else spawn.find_executable(self.exec_name)) cmd = [exec_name, '--log-dir', log_dir, '--log-file', log_file] for filename in self.config_filenames: cmd += ['--config-file', filename] self.process = async_process.AsyncProcess( cmd, run_as_root=run_as_root, namespace=self.namespace ) self.process.start(block=True) LOG.debug("Process started: %s", self.process_name) def stop(self, kill_signal=None): kill_signal = kill_signal or self.kill_signal try: self.process.stop(block=True, kill_signal=kill_signal) except async_process.AsyncProcessException as e: if "Process is not running" not in str(e): raise LOG.debug("Process stopped: %s", self.process_name) def restart(self, executor=None): def _restart(): self.stop() self.start() LOG.debug("Restarting process: %s", self.process_name) if executor is None: _restart() else: return executor.submit(_restart) class RabbitmqEnvironmentFixture(fixtures.Fixture): def __init__(self, host="127.0.0.1"): super(RabbitmqEnvironmentFixture, self).__init__() self.host = host def _setUp(self): self.user = common_utils.get_rand_name(prefix='user') self.password = common_utils.get_rand_name(prefix='pass') self.vhost = common_utils.get_rand_name(prefix='vhost') self._execute('add_user', self.user, self.password) self.addCleanup(self._execute, 'delete_user', self.user) self._execute('add_vhost', self.vhost) self.addCleanup(self._execute, 'delete_vhost', self.vhost) self._execute('set_permissions', '-p', self.vhost, self.user, '.*', '.*', '.*') def _execute(self, *args): cmd = ['rabbitmqctl'] cmd.extend(args) utils.execute(cmd, run_as_root=True) class ServiceFixture(fixtures.Fixture): def restart(self, executor=None): return self.process_fixture.restart(executor=executor) def start(self): return self.process_fixture.start() def stop(self, kill_signal=None): return self.process_fixture.stop(kill_signal=kill_signal) class NeutronServerFixture(ServiceFixture): NEUTRON_SERVER = "neutron-server" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, plugin_cfg_fixture, service_cfg_fixtures=None): super(NeutronServerFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.plugin_cfg_fixture = plugin_cfg_fixture self.service_cfg_fixtures = service_cfg_fixtures def _setUp(self): config_filenames = [self.neutron_cfg_fixture.filename, self.plugin_cfg_fixture.filename] if self.service_cfg_fixtures: config_filenames.extend( [scf.filename for scf in self.service_cfg_fixtures]) self.process_fixture = self.useFixture(ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_SERVER, exec_name=self.NEUTRON_SERVER, config_filenames=config_filenames, kill_signal=signal.SIGTERM)) common_utils.wait_until_true(self.server_is_live) def server_is_live(self): try: self.client.list_networks() return True except nc_exc.NeutronClientException: return False @property def client(self): url = ("http://127.0.0.1:%s" % self.neutron_cfg_fixture.config.DEFAULT.bind_port) return client.Client(auth_strategy="noauth", endpoint_url=url) class OVSAgentFixture(ServiceFixture): NEUTRON_OVS_AGENT = "neutron-openvswitch-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture): super(OVSAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.neutron_config = self.neutron_cfg_fixture.config self.agent_cfg_fixture = agent_cfg_fixture self.agent_config = agent_cfg_fixture.config def _setUp(self): self.br_int = self.useFixture( net_helpers.OVSBridgeFixture( self.agent_cfg_fixture.get_br_int_name())).bridge config_filenames = [self.neutron_cfg_fixture.filename, self.agent_cfg_fixture.filename] self.process_fixture = self.useFixture(ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_OVS_AGENT, exec_name=spawn.find_executable( 'ovs_agent.py', path=os.path.join(base.ROOTDIR, 'common', 'agents')), config_filenames=config_filenames, kill_signal=signal.SIGTERM)) class LinuxBridgeAgentFixture(ServiceFixture): NEUTRON_LINUXBRIDGE_AGENT = "neutron-linuxbridge-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture, namespace=None): super(LinuxBridgeAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.neutron_config = self.neutron_cfg_fixture.config self.agent_cfg_fixture = agent_cfg_fixture self.agent_config = agent_cfg_fixture.config self.namespace = namespace def _setUp(self): config_filenames = [self.neutron_cfg_fixture.filename, self.agent_cfg_fixture.filename] self.process_fixture = self.useFixture( ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_LINUXBRIDGE_AGENT, exec_name=self.NEUTRON_LINUXBRIDGE_AGENT, config_filenames=config_filenames, namespace=self.namespace ) ) class L3AgentFixture(ServiceFixture): NEUTRON_L3_AGENT = "neutron-l3-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, l3_agent_cfg_fixture, namespace=None): super(L3AgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.l3_agent_cfg_fixture = l3_agent_cfg_fixture self.namespace = namespace def _setUp(self): self.plugin_config = self.l3_agent_cfg_fixture.config config_filenames = [self.neutron_cfg_fixture.filename, self.l3_agent_cfg_fixture.filename] # if we execute in namespace as root, then allow rootwrap to find the # executable, otherwise construct full path ourselves if self.namespace: exec_name = 'l3_agent.py' else: exec_name = spawn.find_executable( 'l3_agent.py', path=os.path.join(fullstack_base.ROOTDIR, 'cmd')) self.process_fixture = self.useFixture( ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_L3_AGENT, exec_name=exec_name, config_filenames=config_filenames, namespace=self.namespace ) ) def get_namespace_suffix(self): return self.plugin_config.DEFAULT.test_namespace_suffix class DhcpAgentFixture(fixtures.Fixture): NEUTRON_DHCP_AGENT = "neutron-dhcp-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture, namespace=None): super(DhcpAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.agent_cfg_fixture = agent_cfg_fixture self.namespace = namespace def _setUp(self): self.plugin_config = self.agent_cfg_fixture.config config_filenames = [self.neutron_cfg_fixture.filename, self.agent_cfg_fixture.filename] # if we execute in namespace as root, then allow rootwrap to find the # executable, otherwise construct full path ourselves if self.namespace: exec_name = 'dhcp_agent.py' else: exec_name = spawn.find_executable( 'dhcp_agent.py', path=os.path.join(fullstack_base.ROOTDIR, 'cmd')) self.process_fixture = self.useFixture( ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_DHCP_AGENT, exec_name=exec_name, config_filenames=config_filenames, namespace=self.namespace ) ) self.dhcp_namespace_pattern = re.compile( r"qdhcp-[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}%s" % self.get_namespace_suffix()) self.addCleanup(self.clean_dhcp_namespaces) def get_agent_hostname(self): return self.neutron_cfg_fixture.config['DEFAULT']['host'] def get_namespace_suffix(self): return self.plugin_config.DEFAULT.test_namespace_suffix def kill(self): self.process_fixture.stop() self.clean_dhcp_namespaces() def clean_dhcp_namespaces(self): """Delete all DHCP namespaces created by DHCP agent. In some tests for DHCP agent HA agents are killed when handling DHCP service for network(s). In such case DHCP namespace is not deleted by DHCP agent and such namespaces are found and deleted using agent's namespace suffix. """ for namespace in ip_lib.list_network_namespaces(): if self.dhcp_namespace_pattern.match(namespace): try: ip_lib.delete_network_namespace(namespace) except RuntimeError: # Continue cleaning even if namespace deletions fails pass