Merge "Fullstack test for placement sync"

This commit is contained in:
Zuul 2019-05-20 15:26:22 +00:00 committed by Gerrit Code Review
commit 15dc507f9e
6 changed files with 354 additions and 75 deletions

View File

@ -92,7 +92,7 @@ class NeutronConfigFixture(ConfigFixture):
'lock_path': '$state_path/lock',
},
'agent': {
'report_interval': str(env_desc.agent_down_time / 2.0)
'report_interval': str(env_desc.agent_down_time // 2)
},
})
policy_file = self._generate_policy_json()
@ -109,6 +109,18 @@ class NeutronConfigFixture(ConfigFixture):
if env_desc.router_scheduler:
self.config['DEFAULT']['router_scheduler_driver'] = (
env_desc.router_scheduler)
if env_desc.has_placement:
service_plugins = self.config['DEFAULT']['service_plugins']
self.config['DEFAULT']['service_plugins'] = (
'%s,%s' % (service_plugins, 'placement')
)
self.config.update({
'placement': {
'auth_type': 'noauth',
'auth_section': 'http://127.0.0.1:%s/placement' %
env_desc.placement_port
}
})
net_helpers.set_local_port_range(CLIENT_CONN_PORT_START,
CLIENT_CONN_PORT_END)
@ -289,6 +301,22 @@ class SRIOVConfigFixture(ConfigFixture):
super(SRIOVConfigFixture, self)._setUp()
class PlacementConfigFixture(ConfigFixture):
def __init__(self, env_desc, host_desc, temp_dir):
super(PlacementConfigFixture, self).__init__(
env_desc, host_desc, temp_dir, base_filename='placement.ini')
self.config.update({
'DEFAULT': {
'debug': 'True',
'placement_port': self.env_desc.placement_port
}
})
def _setUp(self):
super(PlacementConfigFixture, self)._setUp()
class LinuxBridgeConfigFixture(ConfigFixture):
def __init__(self, env_desc, host_desc, temp_dir, local_ip,

View File

@ -38,7 +38,8 @@ class EnvironmentDescription(object):
service_plugins='router', arp_responder=False,
agent_down_time=75, router_scheduler=None,
global_mtu=constants.DEFAULT_NETWORK_MTU,
debug_iptables=False, log=False, report_bandwidths=False):
debug_iptables=False, log=False, report_bandwidths=False,
has_placement=False, placement_port=None):
self.network_type = network_type
self.l2_pop = l2_pop
self.qos = qos
@ -52,6 +53,8 @@ class EnvironmentDescription(object):
self.service_plugins = service_plugins
self.debug_iptables = debug_iptables
self.report_bandwidths = report_bandwidths
self.has_placement = has_placement
self.placement_port = placement_port
if self.qos:
self.service_plugins += ',qos'
if self.log:
@ -407,6 +410,17 @@ class Environment(fixtures.Fixture):
self.env_desc, None,
self.test_name, neutron_cfg_fixture, plugin_cfg_fixture))
if self.env_desc.has_placement:
placement_cfg_fixture = self.useFixture(
config.PlacementConfigFixture(self.env_desc, self.hosts_desc,
self.temp_dir)
)
self.placement = self.useFixture(
process.PlacementFixture(
self.env_desc, self.hosts_desc, self.test_name,
placement_cfg_fixture)
)
self.hosts = [self._create_host(desc) for desc in self.hosts_desc]
self.wait_until_env_is_up()

View File

@ -213,6 +213,28 @@ class OVSAgentFixture(ServiceFixture):
kill_signal=signal.SIGTERM))
class PlacementFixture(fixtures.Fixture):
def __init__(self, env_desc, host_desc, test_name, placement_cfg_fixture):
super(PlacementFixture, self).__init__()
self.env_desc = env_desc
self.host_desc = host_desc
self.test_name = test_name
self.placement_cfg_fixture = placement_cfg_fixture
self.placement_config = self.placement_cfg_fixture.config
def _setUp(self):
self.process_fixture = self.useFixture(ProcessFixture(
test_name=self.test_name,
process_name='placement',
exec_name=spawn.find_executable(
'placement.py', path=os.path.join(fullstack_base.ROOTDIR,
'servers')
),
config_filenames=[self.placement_cfg_fixture.filename],
kill_signal=signal.SIGTERM))
class SRIOVAgentFixture(ServiceFixture):
NEUTRON_SRIOV_AGENT = "neutron-sriov-nic-agent"

View File

@ -0,0 +1,146 @@
#!/usr/bin/env python
# Copyright (c) 2019 Ericsson
#
# 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 copy
import sys
import uuid
from wsgiref import simple_server as wsgi_simple_server
from oslo_config import cfg
from oslo_config import types
from oslo_log import log as logging
from oslo_serialization import jsonutils
from six.moves import urllib
from neutron.common import config as common_config
LOG = logging.getLogger(__name__)
PortType = types.Integer(1, 65535)
placement_opts = [
cfg.Opt('placement_port', type=PortType)
]
cfg.CONF.register_opts(placement_opts)
class FakePlacement(object):
rp_template = {
"uuid": None,
"generation": 0,
"parent_provider_uuid": None,
"name": None,
}
def __init__(self):
self.host_rp_uuid = str(uuid.uuid4())
host_rp = copy.deepcopy(self.rp_template)
host_rp['uuid'] = self.host_rp_uuid
self.resource_providers = {
self.host_rp_uuid: host_rp
}
def get_resource_providers(self, **kwargs):
id = kwargs.get('id', None)
if not id:
return jsonutils.dumps(
{
'resource_providers':
[self.resource_providers[self.host_rp_uuid]]
})
else:
return jsonutils.dumps(self.resource_providers[id])
def put_traits(self, **kwargs):
# Return empty sting otherwise wsgiref goes mad
return ''
def put_resource_providers(self, **kwargs):
id = kwargs.get('id', None)
req_body = kwargs.get('body', None)
if id:
rp_dict = copy.deepcopy(self.rp_template)
rp_dict['uuid'] = id
rp_dict['parent_provider_uuid'] = req_body['parent_provider_uuid']
rp_dict['name'] = req_body['name']
self.resource_providers[rp_dict['uuid']] = rp_dict
return jsonutils.dumps(rp_dict)
def put_resource_providers_traits(self, **kwargs):
resp = kwargs['body']
resp['resource_provider_generation'] += 1
return jsonutils.dumps(resp)
def put_resource_providers_inventories(self, **kwargs):
resp = kwargs['body']
resp['resource_provider_generation'] += 1
return jsonutils.dumps(resp)
def build_method_name(self, action, path_info):
path_info = urllib.parse.urlparse(path_info).path
path_info_list = path_info.strip('/').split('/')
method_name = action.lower()
for path_chunk in path_info_list:
if any(s in path_chunk for s in ['placement', 'CUSTOM']):
continue
# If there is uuid in the path, that should be thrown out
try:
uuid.UUID(path_chunk)
except ValueError:
method_name += '_' + path_chunk
return method_name
def wsgi_app(self, env, start_response):
response_headers = [('Content-Type', 'application/json')]
http_status = '200 OK'
# Poor men's routing
meth_name = self.build_method_name(env['REQUEST_METHOD'],
env['PATH_INFO'])
params = {}
# Fetch params from url
try:
params['id'] = env['PATH_INFO'].split('/')[3]
except IndexError:
pass
# Fetch body
try:
request_body_size = int(env.get('CONTENT_LENGTH', 0))
if request_body_size > 0:
req_body = env['wsgi.input'].read(request_body_size)
params['body'] = jsonutils.loads(req_body.decode('utf-8'))
except ValueError:
pass
LOG.debug('Request on %s (%s) with body: %s',
env['PATH_INFO'], env['REQUEST_METHOD'], params)
response = getattr(self, meth_name)(**params)
LOG.debug('Response from %s: %s', meth_name, response)
response = response.encode('utf-8')
start_response(http_status, response_headers)
return [response]
if __name__ == "__main__":
common_config.init(sys.argv[1:])
common_config.setup_logging()
placement_port = cfg.CONF.placement_port
LOG.info("Placement fixture started on port: %s", placement_port)
mock_placement = FakePlacement()
wsgi_simple_server.make_server(
'', placement_port, mock_placement.wsgi_app).serve_forever()

View File

@ -25,6 +25,64 @@ from neutron.tests.unit import testlib_api
load_tests = testlib_api.module_load_tests
BR_MAPPINGS = 'bridge_mappings'
DEV_MAPPINGS = 'device_mappings'
def _get_physnet_names_from_mapping(mapping):
physnets = []
for pair in mapping.split(','):
physnets.append(pair.split(':')[0])
return physnets
def _add_new_device_to_agent_config(l2_agent_config, mapping_key_name,
new_dev):
old_bw = l2_agent_config[constants.RP_BANDWIDTHS]
old_mappings = l2_agent_config[mapping_key_name]
if new_dev in old_bw or new_dev in old_mappings:
return
new_mappings = 'physnetnew:%s' % new_dev
new_bw = '%s:%s:%s' % (new_dev,
f_const.MINIMUM_BANDWIDTH_EGRESS_KBPS,
f_const.MINIMUM_BANDWIDTH_INGRESS_KBPS)
l2_agent_config[mapping_key_name] = '%s,%s' % (
old_mappings, new_mappings)
l2_agent_config[constants.RP_BANDWIDTHS] = '%s,%s' % (
old_bw, new_bw)
def _change_agent_conf(l2_agent_config, l2_agent,
mapping_key_name, new_dev):
_add_new_device_to_agent_config(l2_agent_config, mapping_key_name, new_dev)
l2_agent.agent_cfg_fixture.write_config_to_configfile()
def _add_new_bridge_and_restart_agent(host):
l2_agent = host.l2_agent
l2_agent_config = l2_agent.agent_cfg_fixture.config
if 'ovs' in host.agents:
new_dev = utils.get_rand_device_name(prefix='br-new')
_change_agent_conf(
l2_agent_config['ovs'], l2_agent, BR_MAPPINGS, new_dev)
physnets = _get_physnet_names_from_mapping(
l2_agent_config['ovs'][BR_MAPPINGS])
br_phys_new = host.useFixture(
net_helpers.OVSBridgeFixture(new_dev)).bridge
host.connect_to_central_network_via_vlans(br_phys_new)
elif 'sriov' in host.agents:
new_dev = utils.get_rand_device_name(prefix='ens7')
_change_agent_conf(
l2_agent_config['sriov_nic'], l2_agent,
'physical_device_mappings', new_dev)
physnets = _get_physnet_names_from_mapping(
l2_agent_config['sriov_nic']['physical_device_mappings'])
l2_agent.restart()
return physnets
class TestAgentBandwidthReport(base.BaseFullStackTestCase):
@ -35,19 +93,17 @@ class TestAgentBandwidthReport(base.BaseFullStackTestCase):
{'l2_agent_type': constants.AGENT_TYPE_NIC_SWITCH})
]
BR_MAPPINGS = 'bridge_mappings'
DEV_MAPPINGS = 'device_mappings'
def setUp(self):
host_desc = [environment.HostDescription(
l3_agent=False,
l2_agent_type=self.l2_agent_type)]
env_desc = environment.EnvironmentDescription(
network_type='vlan',
l2_pop=False,
report_bandwidths=True,
)
env = environment.Environment(env_desc, host_desc)
def setUp(self, env=None):
if not env:
host_desc = [environment.HostDescription(
l3_agent=False,
l2_agent_type=self.l2_agent_type)]
env_desc = environment.EnvironmentDescription(
network_type='vlan',
l2_pop=False,
report_bandwidths=True,
)
env = environment.Environment(env_desc, host_desc)
super(TestAgentBandwidthReport, self).setUp(env)
@ -55,9 +111,9 @@ class TestAgentBandwidthReport(base.BaseFullStackTestCase):
agent = self.client.show_agent(agent_id)['agent']
agent_configurations = agent['configurations']
if 'Open vSwitch' in agent['agent_type']:
mapping_key = self.BR_MAPPINGS
mapping_key = BR_MAPPINGS
elif 'NIC Switch' in agent['agent_type']:
mapping_key = self.DEV_MAPPINGS
mapping_key = DEV_MAPPINGS
else:
return False
@ -71,7 +127,7 @@ class TestAgentBandwidthReport(base.BaseFullStackTestCase):
agent_configurations):
return False
if mapping_key == self.BR_MAPPINGS:
if mapping_key == BR_MAPPINGS:
if (bridge_or_devices not in
agent_configurations[constants.RP_BANDWIDTHS]):
return False
@ -90,58 +146,6 @@ class TestAgentBandwidthReport(base.BaseFullStackTestCase):
return False
return True
def _get_physnet_names_from_mapping(self, mapping):
physnets = []
for pair in mapping.split(','):
physnets.append(pair.split(':')[0])
return physnets
def _add_new_device_to_agent_config(self, l2_agent_config,
mapping_key_name, new_dev):
old_bw = l2_agent_config[constants.RP_BANDWIDTHS]
old_mappings = l2_agent_config[mapping_key_name]
if new_dev in old_bw or new_dev in old_mappings:
return
new_mappings = 'physnetnew:%s' % new_dev
new_bw = '%s:%s:%s' % (new_dev,
f_const.MINIMUM_BANDWIDTH_EGRESS_KBPS,
f_const.MINIMUM_BANDWIDTH_INGRESS_KBPS)
l2_agent_config[mapping_key_name] = '%s,%s' % (
old_mappings, new_mappings)
l2_agent_config[constants.RP_BANDWIDTHS] = '%s,%s' % (
old_bw, new_bw)
def _change_agent_conf_and_restart_agent(self, l2_agent_config, l2_agent,
mapping_key_name, new_dev):
self._add_new_device_to_agent_config(
l2_agent_config, mapping_key_name, new_dev)
l2_agent.agent_cfg_fixture.write_config_to_configfile()
def _add_new_bridge_and_restart_agent(self, host):
l2_agent = host.l2_agent
l2_agent_config = l2_agent.agent_cfg_fixture.config
if 'ovs' in host.agents:
new_dev = utils.get_rand_device_name(prefix='br-new')
self._change_agent_conf_and_restart_agent(
l2_agent_config['ovs'], l2_agent, self.BR_MAPPINGS, new_dev)
physnets = self._get_physnet_names_from_mapping(
l2_agent_config['ovs'][self.BR_MAPPINGS])
br_phys_new = host.useFixture(
net_helpers.OVSBridgeFixture(new_dev)).bridge
host.connect_to_central_network_via_vlans(br_phys_new)
elif 'sriov' in host.agents:
new_dev = utils.get_rand_device_name(prefix='ens7')
self._change_agent_conf_and_restart_agent(
l2_agent_config['sriov_nic'], l2_agent,
'physical_device_mappings', new_dev)
physnets = self._get_physnet_names_from_mapping(
l2_agent_config['sriov_nic']['physical_device_mappings'])
l2_agent.restart()
return physnets
def test_agent_configurations(self):
agents = self.client.list_agents()
@ -150,10 +154,10 @@ class TestAgentBandwidthReport(base.BaseFullStackTestCase):
agent_config = self.environment.hosts[0].l2_agent.agent_config
if 'ovs' in self.environment.hosts[0].agents:
physnets = self._get_physnet_names_from_mapping(
agent_config['ovs'][self.BR_MAPPINGS])
physnets = _get_physnet_names_from_mapping(
agent_config['ovs'][BR_MAPPINGS])
elif 'sriov' in self.environment.hosts[0].agents:
physnets = self._get_physnet_names_from_mapping(
physnets = _get_physnet_names_from_mapping(
agent_config['sriov_nic']['physical_device_mappings'])
self.assertTrue(
@ -163,8 +167,7 @@ class TestAgentBandwidthReport(base.BaseFullStackTestCase):
# Add new physnet with bandwidth value to agent config and check
# if after agent restart and report_interval wait it is visible in
# the configurations field.
physnets = self._add_new_bridge_and_restart_agent(
self.environment.hosts[0])
physnets = _add_new_bridge_and_restart_agent(self.environment.hosts[0])
agents = self.client.list_agents()
l2_agent = agents['agents'][0]
@ -178,3 +181,69 @@ class TestAgentBandwidthReport(base.BaseFullStackTestCase):
predicate=check_agent_alive,
timeout=float(report_interval) + 10,
sleep=5)
class TestPlacementBandwidthReport(base.BaseFullStackTestCase):
scenarios = [
(constants.AGENT_TYPE_OVS,
{'l2_agent_type': constants.AGENT_TYPE_OVS,
'mech_drivers': 'openvswitch,linuxbridge',
'placement_port': '8080'}),
(constants.AGENT_TYPE_NIC_SWITCH,
{'l2_agent_type': constants.AGENT_TYPE_NIC_SWITCH,
'mech_drivers': 'sriovnicswitch',
'placement_port': '8081'})
]
def setUp(self):
host_desc = [environment.HostDescription(
l3_agent=False,
l2_agent_type=self.l2_agent_type)]
env_desc = environment.EnvironmentDescription(
network_type='vlan',
l2_pop=False,
mech_drivers=self.mech_drivers,
report_bandwidths=True,
has_placement=True,
placement_port=self.placement_port
)
env = environment.Environment(env_desc, host_desc)
super(TestPlacementBandwidthReport, self).setUp(env)
def _check_agent_not_synced(self):
return not self._check_agent_synced()
def _check_agent_synced(self):
agents = self.client.list_agents()
if (len(agents['agents']) == 1 and
agents['agents'][0]['resources_synced']):
return True
return False
def test_configurations_are_synced_towards_placement(self):
neutron_config = self.environment.hosts[0].l2_agent.neutron_config
report_interval = int(neutron_config['agent']['report_interval'])
check_agent_synced = functools.partial(self._check_agent_synced)
utils.wait_until_true(
predicate=check_agent_synced,
timeout=report_interval + 10,
sleep=1)
self.environment.placement.process_fixture.stop()
_add_new_bridge_and_restart_agent(self.environment.hosts[0])
check_agent_not_synced = functools.partial(
self._check_agent_not_synced)
utils.wait_until_true(
predicate=check_agent_not_synced,
timeout=report_interval + 10,
sleep=1)
self.environment.placement.process_fixture.start()
check_agent_synced = functools.partial(self._check_agent_synced)
utils.wait_until_true(
predicate=check_agent_synced,
timeout=report_interval + 10,
sleep=1)