0fac7bdba5
Remove restrictions on set of BOND properties. Now BOND can have any attributes. Remove redundant `bond_properties`. Change-Id: I60d1a0628d84c5ba49bb5b45824d660297dacccc Implements: blueprint nics-and-nodes-attributes-via-plugin
1055 lines
38 KiB
Python
1055 lines
38 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2013 Mirantis, 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 copy
|
|
import operator
|
|
|
|
import mock
|
|
from oslo_serialization import jsonutils
|
|
from six.moves import range
|
|
import unittest2
|
|
|
|
from nailgun import consts
|
|
from nailgun import objects
|
|
from nailgun.test.base import BaseIntegrationTest
|
|
from nailgun.test.base import fake_tasks
|
|
from nailgun.test.base import mock_rpc
|
|
from nailgun.utils import reverse
|
|
|
|
|
|
class TestVerifyNetworkTaskManagers(BaseIntegrationTest):
|
|
|
|
def setUp(self):
|
|
super(TestVerifyNetworkTaskManagers, self).setUp()
|
|
|
|
meta1 = self.env.generate_interfaces_in_meta(2)
|
|
meta2 = self.env.generate_interfaces_in_meta(2)
|
|
|
|
mac1 = meta1['interfaces'][0]['mac']
|
|
mac2 = meta2['interfaces'][0]['mac']
|
|
|
|
self.cluster = self.env.create(
|
|
cluster_kwargs={
|
|
'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron},
|
|
nodes_kwargs=[
|
|
{"api": True, "meta": meta1, "mac": mac1},
|
|
{"api": True, "meta": meta2, "mac": mac2},
|
|
])
|
|
|
|
@mock_rpc()
|
|
def test_network_verify_task_managers_dhcp_on_master(self):
|
|
task = self.env.launch_verify_networks()
|
|
self.assertNotEqual(task.status, consts.TASK_STATUSES.error)
|
|
|
|
@mock_rpc()
|
|
def test_network_verify_compares_received_with_cached(self):
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
|
|
self.assertEqual(200, resp.status_code)
|
|
nets = resp.json_body
|
|
|
|
nets['networks'][-1]["vlan_start"] = 500
|
|
task = self.env.launch_verify_networks(nets)
|
|
self.assertNotEqual(task.status, consts.TASK_STATUSES.error)
|
|
|
|
@mock_rpc(pass_mock=True)
|
|
def test_network_verify_fails_if_admin_intersection(self, mocked_rpc):
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
self.assertEqual(200, resp.status_code)
|
|
nets = resp.json_body
|
|
|
|
admin_ng = objects.NetworkGroup.get_admin_network_group()
|
|
|
|
# find first non-admin network
|
|
network = next((
|
|
net for net in nets['networks']
|
|
if net['name'] != consts.NETWORKS.fuelweb_admin),
|
|
None)
|
|
network['cidr'] = admin_ng.cidr
|
|
|
|
task = self.env.launch_verify_networks(nets)
|
|
self.assertEqual(task.status, consts.TASK_STATUSES.error)
|
|
self.assertIn(
|
|
"Address space intersection between networks:\n",
|
|
task.message)
|
|
self.assertIn("admin (PXE)", task.message)
|
|
self.assertIn(network['name'], task.message)
|
|
self.assertEqual(mocked_rpc.called, False)
|
|
|
|
@mock_rpc(pass_mock=True)
|
|
def test_network_verify_fails_if_untagged_intersection(self, mocked_rpc):
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
self.assertEqual(200, resp.status_code)
|
|
nets = resp.json_body
|
|
|
|
for net in nets['networks']:
|
|
if net['name'] in ('storage',):
|
|
net['vlan_start'] = None
|
|
|
|
task = self.env.launch_verify_networks(nets)
|
|
self.assertEqual(task.status, consts.TASK_STATUSES.error)
|
|
self.assertIn(
|
|
'Some untagged networks are assigned to the same physical '
|
|
'interface. You should assign them to different physical '
|
|
'interfaces. Affected:\n',
|
|
task.message
|
|
)
|
|
for n in self.env.nodes:
|
|
self.assertIn('"storage"', task.message)
|
|
self.assertEqual(mocked_rpc.called, False)
|
|
|
|
def check_verify_networks_less_than_2_online_nodes_error(self):
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
nets = resp.json_body
|
|
|
|
task = self.env.launch_verify_networks(nets)
|
|
self.db.refresh(task)
|
|
self.assertEqual(task.status, consts.TASK_STATUSES.error)
|
|
error_msg = 'At least two online nodes are required to be in ' \
|
|
'the environment for network verification.'
|
|
self.assertEqual(task.message, error_msg)
|
|
|
|
def test_verify_networks_1_node_error(self):
|
|
self.db.delete(self.env.nodes[0])
|
|
self.db.flush()
|
|
self.check_verify_networks_less_than_2_online_nodes_error()
|
|
|
|
def test_verify_networks_1_online_node_error(self):
|
|
self.env.nodes[0].online = False
|
|
self.db.flush()
|
|
self.check_verify_networks_less_than_2_online_nodes_error()
|
|
|
|
@fake_tasks()
|
|
def test_verify_networks_offline_nodes_notice(self):
|
|
self.env.create_node(api=True,
|
|
cluster_id=self.cluster.id,
|
|
online=False)
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
nets = resp.json_body
|
|
|
|
task = self.env.launch_verify_networks(nets)
|
|
self.assertEqual(task.cache['args']['offline'], 1)
|
|
self.assertEqual(task.status, consts.TASK_STATUSES.ready)
|
|
|
|
error_msg = 'Notice: 1 node(s) were offline during connectivity ' \
|
|
'check so they were skipped from the check.'
|
|
self.assertEqual(task.message, error_msg)
|
|
|
|
def test_network_verify_when_env_not_ready(self):
|
|
blocking_statuses = (
|
|
consts.CLUSTER_STATUSES.deployment,
|
|
)
|
|
for status in blocking_statuses:
|
|
self.cluster.status = status
|
|
self.db.flush()
|
|
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
nets = resp.json_body
|
|
|
|
task = self.env.launch_verify_networks(nets)
|
|
self.assertEqual(task.status, consts.TASK_STATUSES.error)
|
|
error_msg = (
|
|
"Environment is not ready to run network verification "
|
|
"because it is in '{0}' state.".format(status)
|
|
)
|
|
self.assertEqual(task.message, error_msg)
|
|
|
|
def test_network_verify_if_old_task_is_running(self):
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
nets = resp.body
|
|
|
|
self.env.create_task(
|
|
name="verify_networks",
|
|
status=consts.TASK_STATUSES.running,
|
|
cluster_id=self.cluster.id)
|
|
|
|
resp = self.app.put(
|
|
reverse(
|
|
'NeutronNetworkConfigurationVerifyHandler',
|
|
kwargs={'cluster_id': self.cluster.id}),
|
|
nets,
|
|
headers=self.default_headers,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(400, resp.status_code)
|
|
|
|
@unittest2.skip('Multicast is always disabled.')
|
|
@fake_tasks(fake_rpc=False)
|
|
def test_multicast_enabled_when_corosync_section_present(self, mocked_rpc):
|
|
self.env.launch_verify_networks()
|
|
self.assertIn('subtasks', mocked_rpc.call_args[0][1])
|
|
subtasks = mocked_rpc.call_args[0][1]['subtasks']
|
|
self.assertEqual(len(subtasks), 2)
|
|
dhcp_subtask, multicast = subtasks[0], subtasks[1]
|
|
self.assertEqual(dhcp_subtask['method'], 'check_dhcp')
|
|
self.assertEqual(multicast['method'], 'multicast_verification')
|
|
|
|
@unittest2.skip('Multicast is always disabled.')
|
|
@fake_tasks(fake_rpc=False)
|
|
def test_multicast_disabled_when_corosync_is_not_present(self, mocked_rpc):
|
|
editable = copy.deepcopy(self.cluster.attributes.editable)
|
|
del editable['corosync']
|
|
self.cluster.attributes.editable = editable
|
|
self.env.launch_verify_networks()
|
|
self.assertIn('subtasks', mocked_rpc.call_args[0][1])
|
|
subtasks = mocked_rpc.call_args[0][1]['subtasks']
|
|
self.assertEqual(len(subtasks), 1)
|
|
dhcp_subtask = subtasks[0]
|
|
self.assertEqual(dhcp_subtask['method'], 'check_dhcp')
|
|
|
|
|
|
class TestVerifyNetworksDisabled(BaseIntegrationTest):
|
|
|
|
def setUp(self):
|
|
super(TestVerifyNetworksDisabled, self).setUp()
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [{
|
|
"mac": "00:00:00:00:00:66",
|
|
"max_speed": 1000,
|
|
"name": "eth0",
|
|
"current_speed": 1000
|
|
}, {
|
|
"mac": "00:00:00:00:00:77",
|
|
"max_speed": 1000,
|
|
"name": "eth1",
|
|
"current_speed": None
|
|
}, {
|
|
"mac": "00:00:00:00:00:88",
|
|
"max_speed": 1000,
|
|
"name": "eth2",
|
|
"current_speed": None}])
|
|
self.cluster = self.env.create(
|
|
cluster_kwargs={'status': consts.CLUSTER_STATUSES.operational,
|
|
'net_provider': 'neutron',
|
|
'net_segment_type': 'vlan'},
|
|
nodes_kwargs=[
|
|
{
|
|
'api': False,
|
|
},
|
|
{
|
|
'api': False,
|
|
},
|
|
]
|
|
)
|
|
|
|
@mock_rpc()
|
|
def test_network_verification_neutron_with_vlan_segmentation(self):
|
|
task = self.env.launch_verify_networks()
|
|
self.assertNotEqual(task.status, consts.TASK_STATUSES.error)
|
|
|
|
|
|
class TestNetworkVerificationWithBonds(BaseIntegrationTest):
|
|
|
|
def setUp(self):
|
|
super(TestNetworkVerificationWithBonds, self).setUp()
|
|
meta1 = self.env.default_metadata()
|
|
meta2 = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta1, [
|
|
{"name": "eth0", "mac": "00:00:00:00:00:66",
|
|
"pxe": True},
|
|
{"name": "eth1", "mac": "00:00:00:00:00:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:00:88"}]
|
|
)
|
|
self.env.set_interfaces_in_meta(meta2, [
|
|
{"name": "eth0", "mac": "00:00:00:00:11:66", "current_speed": 100,
|
|
"pxe": True},
|
|
{"name": "eth1", "mac": "00:00:00:00:22:77", "current_speed": 100},
|
|
{"name": "eth2", "mac": "00:00:00:00:33:88", "current_speed": 100}]
|
|
)
|
|
self.cluster = self.env.create(
|
|
cluster_kwargs={
|
|
'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron,
|
|
'net_segment_type': 'gre'
|
|
},
|
|
nodes_kwargs=[
|
|
{'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta1},
|
|
{'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta2}
|
|
]
|
|
)
|
|
|
|
for node in self.env.nodes:
|
|
data, admin_nic, other_nic, empty_nic = self.verify_nics(node)
|
|
self.env.make_bond_via_api(
|
|
"ovs-bond0", consts.BOND_MODES.balance_slb,
|
|
[other_nic["name"], empty_nic["name"]], node["id"],
|
|
attrs={'type__': {'value': consts.BOND_TYPES.ovs}})
|
|
self.verify_bonds(node)
|
|
|
|
def verify_nics(self, node):
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler',
|
|
kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
admin_nic, other_nic, empty_nic = None, None, None
|
|
for nic in resp.json_body:
|
|
net_names = [n['name'] for n in nic['assigned_networks']]
|
|
if 'fuelweb_admin' in net_names:
|
|
admin_nic = nic
|
|
elif net_names:
|
|
other_nic = nic
|
|
else:
|
|
empty_nic = nic
|
|
self.assertTrue(admin_nic and other_nic and empty_nic)
|
|
return resp.json_body, admin_nic, other_nic, empty_nic
|
|
|
|
def verify_bonds(self, node):
|
|
resp = self.env.node_nics_get(node["id"])
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
bond = filter(lambda nic: nic["type"] ==
|
|
consts.NETWORK_INTERFACE_TYPES.bond,
|
|
resp.json_body)
|
|
self.assertEqual(len(bond), 1)
|
|
self.assertEqual(bond[0]["name"], "ovs-bond0")
|
|
|
|
@property
|
|
def expected_args(self):
|
|
expected_networks = [
|
|
{u'vlans': [0, 101, 102, 103], u'iface': u'eth0'},
|
|
{u'vlans': [0], u'iface': u'eth1'},
|
|
{u'vlans': [0], u'iface': u'eth2'}
|
|
]
|
|
expected_bonds = {
|
|
u'ovs-bond0': [u'eth1', u'eth2']
|
|
}
|
|
|
|
_expected_args = []
|
|
for node in self.env.nodes:
|
|
_expected_args.append({
|
|
u'uid': node['id'],
|
|
u'name': node['name'],
|
|
u'status': node['status'],
|
|
u'networks': expected_networks,
|
|
u'bonds': expected_bonds,
|
|
u'excluded_networks': []
|
|
})
|
|
|
|
return _expected_args
|
|
|
|
@property
|
|
def expected_args_deployed(self):
|
|
expected_networks = [
|
|
{u'vlans': [0, 101, 102, 103], u'iface': u'eth0'},
|
|
{u'vlans': [0], u'iface': u'ovs-bond0'},
|
|
]
|
|
|
|
_expected_args = []
|
|
for node in self.env.nodes:
|
|
_expected_args.append({
|
|
u'uid': node['id'],
|
|
u'name': node['name'],
|
|
u'status': node['status'],
|
|
u'networks': expected_networks,
|
|
u'excluded_networks': []
|
|
})
|
|
|
|
return _expected_args
|
|
|
|
@mock_rpc()
|
|
def test_network_verification_neutron_with_bonds(self):
|
|
task = self.env.launch_verify_networks()
|
|
self.assertNotEqual(task.status, consts.TASK_STATUSES.error)
|
|
self.assertEqual(task.cache['args']['nodes'], self.expected_args)
|
|
|
|
@mock_rpc()
|
|
def test_network_verification_neutron_with_bonds_warn(self):
|
|
resp = self.app.get(
|
|
reverse(
|
|
'NeutronNetworkConfigurationHandler',
|
|
kwargs={'cluster_id': self.cluster.id}
|
|
),
|
|
headers=self.default_headers
|
|
)
|
|
resp = self.app.put(
|
|
reverse(
|
|
'NeutronNetworkConfigurationVerifyHandler',
|
|
kwargs={'cluster_id': self.cluster.id}),
|
|
resp.body,
|
|
headers=self.default_headers,
|
|
expect_errors=True
|
|
)
|
|
# When run tasks synchronously, API returns 200 - task is finished
|
|
# When we run asynchronously, API returns 202 - task created
|
|
self.assertEqual(202, resp.status_code)
|
|
self.assertEqual(
|
|
resp.json_body['result'],
|
|
{u'warning': [u"Node '{0}': interface 'ovs-bond0' slave NICs have "
|
|
u"different or unrecognized speeds".format(
|
|
self.env.nodes[0].name)]})
|
|
|
|
def check_network_verification_on_bootstrap_nodes_with_bonds(self,
|
|
bond_mode):
|
|
expected_task_args = []
|
|
for node in self.env.nodes:
|
|
|
|
for bond in node.bond_interfaces:
|
|
bond.mode = bond_mode
|
|
|
|
expected_task_args.append({
|
|
u'uid': node['id'],
|
|
u'name': node['name'],
|
|
u'status': node['status'],
|
|
u'networks': [
|
|
{u'vlans': [0, 101, 102, 103], u'iface': u'eth0'}
|
|
],
|
|
u'bonds': {u'ovs-bond0': [u'eth1', u'eth2']},
|
|
u'excluded_networks': [
|
|
{u'iface': u'eth1'},
|
|
{u'iface': u'eth2'}
|
|
]
|
|
})
|
|
|
|
task = self.env.launch_verify_networks()
|
|
self.assertNotEqual(task.status, consts.TASK_STATUSES.error)
|
|
self.assertEqual(task.cache['args']['nodes'], expected_task_args)
|
|
|
|
@fake_tasks()
|
|
def check_network_verification_on_deployed_nodes_with_bonds(self,
|
|
bond_mode):
|
|
for node in self.env.nodes:
|
|
for bond in node.bond_interfaces:
|
|
bond.mode = bond_mode
|
|
|
|
deployment_task = self.env.launch_deployment()
|
|
self.assertEqual(deployment_task.status, consts.TASK_STATUSES.ready)
|
|
|
|
verify_network_task = self.env.launch_verify_networks()
|
|
self.assertEqual(
|
|
verify_network_task.cache['args']['nodes'],
|
|
self.expected_args_deployed
|
|
)
|
|
|
|
@mock_rpc()
|
|
def test_verify_on_bootstrap_nodes_w_lacp_bonds(self):
|
|
self.check_network_verification_on_bootstrap_nodes_with_bonds(
|
|
consts.BOND_MODES.l_802_3ad
|
|
)
|
|
|
|
@mock_rpc()
|
|
def test_verify_on_bootstrap_nodes_w_balance_rr_bonds(self):
|
|
self.check_network_verification_on_bootstrap_nodes_with_bonds(
|
|
consts.BOND_MODES.balance_rr
|
|
)
|
|
|
|
def test_verify_on_deployed_nodes_w_lacp_bonds(self):
|
|
self.check_network_verification_on_deployed_nodes_with_bonds(
|
|
consts.BOND_MODES.l_802_3ad
|
|
)
|
|
|
|
def test_verify_on_deployed_nodes_w_balance_rr_bonds(self):
|
|
self.check_network_verification_on_deployed_nodes_with_bonds(
|
|
consts.BOND_MODES.balance_rr
|
|
)
|
|
|
|
|
|
class TestNetworkVerificationWithTemplates(BaseIntegrationTest):
|
|
|
|
def setUp(self):
|
|
super(TestNetworkVerificationWithTemplates, self).setUp()
|
|
|
|
def create_env(self, net_type=consts.NEUTRON_SEGMENT_TYPES.vlan):
|
|
meta1 = self.env.default_metadata()
|
|
meta2 = self.env.default_metadata()
|
|
meta3 = self.env.default_metadata()
|
|
|
|
self.env.set_interfaces_in_meta(meta1, [
|
|
{"name": "eth0", "mac": "00:00:00:00:00:66"},
|
|
{"name": "eth1", "mac": "00:00:00:00:00:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:00:88"},
|
|
{"name": "eth3", "mac": "00:00:00:00:00:99"}]
|
|
)
|
|
self.env.set_interfaces_in_meta(meta2, [
|
|
{"name": "eth0", "mac": "00:00:00:00:11:66"},
|
|
{"name": "eth1", "mac": "00:00:00:00:11:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:11:88"},
|
|
{"name": "eth3", "mac": "00:00:00:00:11:99"}]
|
|
)
|
|
self.env.set_interfaces_in_meta(meta3, [
|
|
{"name": "eth0", "mac": "00:00:00:00:22:66"},
|
|
{"name": "eth1", "mac": "00:00:00:00:22:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:22:88"},
|
|
{"name": "eth3", "mac": "00:00:00:00:22:99"}]
|
|
)
|
|
self.cluster = self.env.create(
|
|
release_kwargs={'version': 'liberty-8.0'},
|
|
cluster_kwargs={
|
|
'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron,
|
|
'net_segment_type': net_type,
|
|
},
|
|
nodes_kwargs=[{
|
|
'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta1,
|
|
'roles': ['controller'],
|
|
}, {
|
|
'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta2,
|
|
'roles': ['compute', 'cinder'],
|
|
}, {
|
|
'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta3,
|
|
'roles': ['compute'],
|
|
}]
|
|
)
|
|
|
|
template = self.env.read_fixtures(['network_template_80'])[0]
|
|
template.pop('pk')
|
|
self.upload_template(self.cluster['id'], template)
|
|
|
|
if net_type == consts.NEUTRON_SEGMENT_TYPES.vlan:
|
|
self.private_vlan_ids = list(range(1000, 1031))
|
|
else:
|
|
self.private_vlan_ids = []
|
|
|
|
@property
|
|
def expected_bonds(self):
|
|
return [
|
|
None,
|
|
None,
|
|
None,
|
|
]
|
|
|
|
@property
|
|
def expected_networks_on_undeployed_node(self):
|
|
compute_networks = [
|
|
{u'vlans': [0], u'iface': u'eth0'},
|
|
{u'vlans': [104], u'iface': u'eth1'},
|
|
{u'vlans': [0], u'iface': u'eth2'},
|
|
{u'vlans': [101] + self.private_vlan_ids, u'iface': u'eth3'},
|
|
]
|
|
|
|
return [
|
|
[
|
|
{u'vlans': [0], u'iface': u'eth0'},
|
|
{u'vlans': [104], u'iface': u'eth1'},
|
|
],
|
|
compute_networks,
|
|
compute_networks,
|
|
]
|
|
|
|
@property
|
|
def expected_networks_on_deployed_node(self):
|
|
compute_networks = [
|
|
{u'vlans': [0], u'iface': u'eth0'},
|
|
{u'vlans': [104], u'iface': u'eth1'},
|
|
{u'vlans': [0], u'iface': u'eth2'},
|
|
{u'vlans': [101] + self.private_vlan_ids,
|
|
u'iface': u'eth3'},
|
|
]
|
|
|
|
return [
|
|
[
|
|
{u'vlans': [0], u'iface': u'eth0'},
|
|
{u'vlans': [104], u'iface': u'eth1'},
|
|
],
|
|
compute_networks,
|
|
compute_networks,
|
|
]
|
|
|
|
def upload_template(self, cluster_id, template):
|
|
resp = self.app.put(
|
|
reverse(
|
|
'TemplateNetworkConfigurationHandler',
|
|
kwargs={'cluster_id': cluster_id},
|
|
),
|
|
jsonutils.dumps(template),
|
|
headers=self.default_headers,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
def verify_networks(self, expected_networks, expected_bonds=None):
|
|
task = self.env.launch_verify_networks()
|
|
|
|
for i, node in enumerate(task.cache['args']['nodes']):
|
|
networks = sorted(node['networks'],
|
|
key=operator.itemgetter('iface'))
|
|
self.assertEqual(
|
|
networks, expected_networks[i])
|
|
|
|
if expected_bonds and expected_bonds[i] is not None:
|
|
self.assertEqual(node['bonds'], expected_bonds[i])
|
|
|
|
if expected_bonds is None:
|
|
self.assertNotIn('bonds', node)
|
|
|
|
@mock_rpc()
|
|
def test_get_ifaces_on_undeployed_node(self):
|
|
self.create_env()
|
|
self.verify_networks(
|
|
self.expected_networks_on_undeployed_node,
|
|
self.expected_bonds)
|
|
|
|
@fake_tasks()
|
|
def test_get_ifaces_on_deployed_node(self):
|
|
self.create_env()
|
|
deployment_task = self.env.launch_deployment()
|
|
self.assertNotEqual(deployment_task.status, consts.TASK_STATUSES.error)
|
|
|
|
self.verify_networks(
|
|
self.expected_networks_on_deployed_node)
|
|
|
|
@mock_rpc()
|
|
def test_get_ifaces_for_gre_network(self):
|
|
self.create_env(consts.NEUTRON_SEGMENT_TYPES.gre)
|
|
self.verify_networks(
|
|
self.expected_networks_on_undeployed_node,
|
|
self.expected_bonds)
|
|
|
|
@mock_rpc()
|
|
def test_get_ifaces_for_tun_network(self):
|
|
self.create_env(consts.NEUTRON_SEGMENT_TYPES.tun)
|
|
self.verify_networks(
|
|
self.expected_networks_on_undeployed_node,
|
|
self.expected_bonds)
|
|
|
|
|
|
class TestNetworkVerificationWithTemplates90(BaseIntegrationTest):
|
|
|
|
def setUp(self):
|
|
super(TestNetworkVerificationWithTemplates90, self).setUp()
|
|
|
|
def create_env(self, net_type=consts.NEUTRON_SEGMENT_TYPES.vlan):
|
|
meta1 = self.env.default_metadata()
|
|
meta2 = self.env.default_metadata()
|
|
meta3 = self.env.default_metadata()
|
|
|
|
self.env.set_interfaces_in_meta(meta1, [
|
|
{"name": "eth0", "mac": "00:00:00:00:00:66"},
|
|
{"name": "eth1", "mac": "00:00:00:00:00:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:00:88"},
|
|
{"name": "eth3", "mac": "00:00:00:00:00:99"}]
|
|
)
|
|
self.env.set_interfaces_in_meta(meta2, [
|
|
{"name": "eth0", "mac": "00:00:00:00:11:66"},
|
|
{"name": "eth1", "mac": "00:00:00:00:11:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:11:88"},
|
|
{"name": "eth3", "mac": "00:00:00:00:11:99"}]
|
|
)
|
|
self.env.set_interfaces_in_meta(meta3, [
|
|
{"name": "eth0", "mac": "00:00:00:00:22:66"},
|
|
{"name": "eth1", "mac": "00:00:00:00:22:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:22:88"},
|
|
{"name": "eth3", "mac": "00:00:00:00:22:99"}]
|
|
)
|
|
self.cluster = self.env.create(
|
|
release_kwargs={'version': 'mitaka-9.0',
|
|
'operating_system': consts.RELEASE_OS.ubuntu},
|
|
cluster_kwargs={
|
|
'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron,
|
|
'net_segment_type': net_type,
|
|
},
|
|
nodes_kwargs=[{
|
|
'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta1,
|
|
'roles': ['controller'],
|
|
}, {
|
|
'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta2,
|
|
'roles': ['compute', 'cinder'],
|
|
}, {
|
|
'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta3,
|
|
'roles': ['compute'],
|
|
}]
|
|
)
|
|
|
|
objects.Cluster.patch_attributes(
|
|
self.cluster,
|
|
{'editable': {
|
|
'common': {
|
|
'libvirt_type': {
|
|
'value': consts.HYPERVISORS.kvm}}}})
|
|
|
|
template = self.env.read_fixtures(['network_template_90'])[0]
|
|
template.pop('pk')
|
|
self.upload_template(self.cluster['id'], template)
|
|
|
|
for node in self.cluster.nodes:
|
|
if 'compute' in node.all_roles:
|
|
objects.Node.update_attributes(
|
|
node,
|
|
{
|
|
'hugepages': {
|
|
'dpdk': {'value': 128},
|
|
'nova': {'value': {'2048': 128}}
|
|
},
|
|
'cpu_pinning': {
|
|
'dpdk': {'value': 2}
|
|
}
|
|
}
|
|
)
|
|
|
|
if net_type == consts.NEUTRON_SEGMENT_TYPES.vlan:
|
|
self.private_vlan_ids = list(range(1000, 1031))
|
|
else:
|
|
self.private_vlan_ids = []
|
|
|
|
@property
|
|
def expected_bonds(self):
|
|
return [
|
|
None,
|
|
None,
|
|
None,
|
|
]
|
|
|
|
@property
|
|
def expected_networks_on_undeployed_node_with_dpdk(self):
|
|
compute_networks = [
|
|
{u'vlans': [0], u'iface': u'eth0'},
|
|
{u'vlans': [104], u'iface': u'eth1'},
|
|
{u'vlans': [0], u'iface': u'eth2'},
|
|
{u'vlans': [101] + self.private_vlan_ids, u'iface': u'eth3'},
|
|
]
|
|
|
|
return [
|
|
[
|
|
{u'vlans': [0], u'iface': u'eth0'},
|
|
{u'vlans': [104], u'iface': u'eth1'},
|
|
],
|
|
compute_networks,
|
|
compute_networks,
|
|
]
|
|
|
|
@property
|
|
def expected_networks_on_deployed_node_with_dpdk(self):
|
|
compute_networks = [
|
|
{u'vlans': [0], u'iface': u'eth0'},
|
|
{u'vlans': [104], u'iface': u'eth1'},
|
|
{u'vlans': [0], u'iface': u'eth2'},
|
|
]
|
|
|
|
return [
|
|
[
|
|
{u'vlans': [0], u'iface': u'eth0'},
|
|
{u'vlans': [104], u'iface': u'eth1'},
|
|
],
|
|
compute_networks,
|
|
compute_networks,
|
|
]
|
|
|
|
def upload_template(self, cluster_id, template):
|
|
resp = self.app.put(
|
|
reverse(
|
|
'TemplateNetworkConfigurationHandler',
|
|
kwargs={'cluster_id': cluster_id},
|
|
),
|
|
jsonutils.dumps(template),
|
|
headers=self.default_headers,
|
|
expect_errors=True
|
|
)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
def verify_networks(self, expected_networks, expected_bonds=None):
|
|
task = self.env.launch_verify_networks()
|
|
|
|
for i, node in enumerate(task.cache['args']['nodes']):
|
|
networks = sorted(node['networks'],
|
|
key=operator.itemgetter('iface'))
|
|
self.assertEqual(
|
|
networks, expected_networks[i])
|
|
|
|
if expected_bonds and expected_bonds[i] is not None:
|
|
self.assertEqual(node['bonds'], expected_bonds[i])
|
|
|
|
if expected_bonds is None:
|
|
self.assertNotIn('bonds', node)
|
|
|
|
@mock_rpc()
|
|
def test_get_ifaces_on_undeployed_node_with_dpdk(self):
|
|
self.create_env()
|
|
self.verify_networks(
|
|
self.expected_networks_on_undeployed_node_with_dpdk,
|
|
self.expected_bonds)
|
|
|
|
@fake_tasks()
|
|
def test_get_ifaces_on_deployed_node_with_dpdk(self):
|
|
self.create_env()
|
|
deploy_task = self.env.launch_deployment()
|
|
self.assertEqual(deploy_task.status, consts.TASK_STATUSES.ready)
|
|
self.verify_networks(
|
|
self.expected_networks_on_deployed_node_with_dpdk)
|
|
|
|
|
|
class TestVerifyNovaFlatDHCP(BaseIntegrationTest):
|
|
|
|
def setUp(self):
|
|
super(TestVerifyNovaFlatDHCP, self).setUp()
|
|
nodes_kwargs = []
|
|
for role in ['controller', 'compute', 'cinder']:
|
|
nodes_kwargs.append(
|
|
{
|
|
'roles': [role],
|
|
'mac': self.env.generate_random_mac(),
|
|
'api': True,
|
|
}
|
|
)
|
|
|
|
self.cluster = self.env.create(
|
|
cluster_kwargs={
|
|
'net_provider': consts.CLUSTER_NET_PROVIDERS.nova_network},
|
|
nodes_kwargs=nodes_kwargs,
|
|
)
|
|
|
|
def test_flat_dhcp_verify(self):
|
|
nets = self.env.nova_networks_get(self.cluster.id).json_body
|
|
public = next(
|
|
(net for net in nets['networks']
|
|
if net['name'] == consts.NETWORKS.public))
|
|
ip_range = ['172.16.0.35', '172.16.0.38']
|
|
public['ip_ranges'] = [ip_range]
|
|
public['meta']['ip_range'] = ip_range
|
|
self.env.nova_networks_put(self.cluster.id, nets)
|
|
|
|
resp = self.env.launch_verify_networks(expect_errors=True)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertEqual(resp.json_body['message'],
|
|
"Not enough free IP addresses in ranges "
|
|
"[172.16.0.35-172.16.0.38] of 'public' network")
|
|
|
|
|
|
class TestVerifyNeutronVlan(BaseIntegrationTest):
|
|
|
|
def setUp(self):
|
|
super(TestVerifyNeutronVlan, self).setUp()
|
|
meta1 = self.env.default_metadata()
|
|
meta2 = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta1, [
|
|
{"name": "eth0", "mac": "00:00:00:00:00:66"},
|
|
{"name": "eth1", "mac": "00:00:00:00:00:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:00:88"}])
|
|
self.env.set_interfaces_in_meta(meta2, [
|
|
{"name": "eth0", "mac": "00:00:00:00:01:66"},
|
|
{"name": "eth1", "mac": "00:00:00:00:01:77"},
|
|
{"name": "eth2", "mac": "00:00:00:00:01:88"}])
|
|
self.cluster = self.env.create(
|
|
cluster_kwargs={
|
|
'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron,
|
|
'net_segment_type': 'vlan'
|
|
},
|
|
nodes_kwargs=[
|
|
{
|
|
'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta1,
|
|
'roles': ['controller'],
|
|
},
|
|
{
|
|
'api': True,
|
|
'pending_addition': True,
|
|
'meta': meta2,
|
|
'roles': ['compute'],
|
|
}]
|
|
)
|
|
|
|
def _assign_dpdk_to_nic(self, node, dpdk_nic, other_nic):
|
|
other_nets = other_nic.assigned_networks_list
|
|
dpdk_nets = dpdk_nic.assigned_networks_list
|
|
|
|
for i, net in enumerate(other_nets):
|
|
if net['name'] == consts.NETWORKS.private:
|
|
dpdk_nets.append(other_nets.pop(i))
|
|
break
|
|
objects.NIC.assign_networks(other_nic, other_nets)
|
|
objects.NIC.assign_networks(dpdk_nic, dpdk_nets)
|
|
|
|
objects.NIC.update(
|
|
dpdk_nic,
|
|
{
|
|
'interface_properties': {
|
|
'dpdk': {
|
|
'enabled': True,
|
|
'available': True,
|
|
},
|
|
'pci_id': 'test_id:1',
|
|
}
|
|
})
|
|
|
|
@fake_tasks()
|
|
def test_verify_networks_after_stop(self):
|
|
deploy_task = self.env.launch_deployment()
|
|
self.assertEqual(deploy_task.status, consts.TASK_STATUSES.ready)
|
|
|
|
# FIXME(aroma): remove when stop action will be reworked for ha
|
|
# cluster. To get more details, please, refer to [1]
|
|
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
|
|
objects.Cluster.set_deployed_before_flag(self.cluster, value=False)
|
|
|
|
stop_task = self.env.stop_deployment()
|
|
self.assertEqual(stop_task.status, consts.TASK_STATUSES.ready)
|
|
self.db.refresh(self.cluster)
|
|
self.assertEqual(self.cluster.status, consts.CLUSTER_STATUSES.stopped)
|
|
self.assertFalse(self.cluster.is_locked)
|
|
# Moving nodes online by hands. Our fake threads do this with
|
|
# random success
|
|
for node in sorted(self.cluster.nodes, key=lambda n: n.id):
|
|
node.online = True
|
|
self.db.commit()
|
|
verify_task = self.env.launch_verify_networks()
|
|
self.assertEqual(verify_task.status, consts.TASK_STATUSES.ready)
|
|
|
|
@mock_rpc()
|
|
def test_network_verification_neutron_with_vlan_segmentation(self):
|
|
# get Neutron L2 VLAN ID range
|
|
vlan_rng_be = self.cluster.network_config.vlan_range
|
|
vlan_rng = set(range(vlan_rng_be[0], vlan_rng_be[1] + 1))
|
|
|
|
# get nodes NICs for private network
|
|
resp = self.app.get(reverse('NodeCollectionHandler'),
|
|
headers=self.default_headers)
|
|
self.assertEqual(200, resp.status_code)
|
|
priv_nics = {}
|
|
for node in resp.json_body:
|
|
for net in node['network_data']:
|
|
if net['name'] == 'private':
|
|
priv_nics[node['id']] = net['dev']
|
|
break
|
|
|
|
# check private VLAN range for nodes in Verify parameters
|
|
task = self.env.launch_verify_networks()
|
|
self.assertEqual(task.status, consts.TASK_STATUSES.running)
|
|
for node in task.cache['args']['nodes']:
|
|
for net in node['networks']:
|
|
if net['iface'] == priv_nics[node['uid']]:
|
|
self.assertTrue(vlan_rng <= set(net['vlans']))
|
|
break
|
|
|
|
@mock_rpc()
|
|
def test_network_verification_parameters_w_one_node_having_public(self):
|
|
# Decrease VLAN range and set public VLAN
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
nets = resp.json_body
|
|
nets['networking_parameters']['vlan_range'] = [1000, 1004]
|
|
for net in nets['networks']:
|
|
if net['name'] == consts.NETWORKS.public:
|
|
net['vlan_start'] = 333
|
|
resp = self.env.neutron_networks_put(self.cluster.id, nets)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
task = self.env.launch_verify_networks()
|
|
self.assertNotEqual(task.status, consts.TASK_STATUSES.error)
|
|
# Public VLANs are not being checked on both nodes
|
|
for n in range(2):
|
|
self.assertEqual(
|
|
task.cache['args']['nodes'][n]['networks'],
|
|
[{'vlans': [0, 101, 102, 1000, 1001, 1002, 1003, 1004],
|
|
'iface': 'eth0'}])
|
|
|
|
@mock_rpc()
|
|
def test_network_verify_parameters_w_two_nodes_having_public(self):
|
|
self.env.create_nodes_w_interfaces_count(
|
|
nodes_count=1,
|
|
if_count=3,
|
|
api=True,
|
|
pending_addition=True,
|
|
roles=['controller'],
|
|
cluster_id=self.cluster.id)
|
|
# Decrease VLAN range and set public VLAN
|
|
resp = self.env.neutron_networks_get(self.cluster.id)
|
|
nets = resp.json_body
|
|
nets['networking_parameters']['vlan_range'] = [1000, 1004]
|
|
for net in nets['networks']:
|
|
if net['name'] == consts.NETWORKS.public:
|
|
net['vlan_start'] = 333
|
|
resp = self.env.neutron_networks_put(self.cluster.id, nets)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
task = self.env.launch_verify_networks()
|
|
self.assertNotEqual(task.status, consts.TASK_STATUSES.error)
|
|
eth0_vlans = {'iface': 'eth0',
|
|
'vlans': [0, 101, 102, 1000, 1001, 1002, 1003, 1004]}
|
|
eth1_vlans = {'iface': 'eth1',
|
|
'vlans': [333]}
|
|
# There is public VLAN on 1st controller node
|
|
self.assertEqual(
|
|
task.cache['args']['nodes'][0]['networks'],
|
|
[eth0_vlans, eth1_vlans])
|
|
# There is no public VLAN on compute node
|
|
self.assertEqual(
|
|
task.cache['args']['nodes'][1]['networks'],
|
|
[eth0_vlans])
|
|
# There is public VLAN on 2nd controller node
|
|
self.assertEqual(
|
|
task.cache['args']['nodes'][2]['networks'],
|
|
[eth0_vlans, eth1_vlans])
|
|
|
|
@mock_rpc()
|
|
def test_repo_availability_tasks_are_created(self):
|
|
task = self.env.launch_verify_networks()
|
|
self.assertNotEqual(task.status, consts.TASK_STATUSES.error)
|
|
|
|
check_repo_tasks = filter(
|
|
lambda t: t.name in (
|
|
consts.TASK_NAMES.check_repo_availability,
|
|
consts.TASK_NAMES.check_repo_availability_with_setup),
|
|
task.subtasks)
|
|
|
|
msg = "envs >= 6.1 must have check repo availability tasks"
|
|
self.assertTrue(bool(check_repo_tasks), msg)
|
|
|
|
@mock_rpc()
|
|
def test_repo_availability_tasks_are_not_created(self):
|
|
self.cluster.release.version = '2014.1-6.0'
|
|
self.db.flush()
|
|
|
|
task = self.env.launch_verify_networks()
|
|
|
|
check_repo_tasks = filter(
|
|
lambda t: t.name in (
|
|
consts.TASK_NAMES.check_repo_availability,
|
|
consts.TASK_NAMES.check_repo_availability_with_setup),
|
|
task.subtasks)
|
|
|
|
msg = "envs < 6.1 must not have check repo availability tasks"
|
|
self.assertFalse(bool(check_repo_tasks), msg)
|
|
|
|
@mock_rpc(pass_mock=True)
|
|
@mock.patch('nailgun.objects.Release.get_supported_dpdk_drivers')
|
|
def test_verify_network_w_non_ready_dpdk_node(self, mrpc, mdrivers):
|
|
mdrivers.return_value = {'driver_1': ['test_id:1']}
|
|
|
|
controller = next(
|
|
n for n in self.cluster.nodes if 'controller' in n.roles)
|
|
controller.status = consts.NODE_STATUSES.ready
|
|
|
|
compute = next(n for n in self.cluster.nodes if 'compute' in n.roles)
|
|
self._assign_dpdk_to_nic(
|
|
compute, compute.interfaces[1], compute.interfaces[0])
|
|
|
|
self.env.launch_verify_networks()
|
|
payload = mrpc.call_args[0][1]['args']['nodes']
|
|
|
|
# now we have 1 node with dpdk and 1 without. since dpdk node is
|
|
# not in ready state, vlan check over private net must be performed
|
|
# on both nodes
|
|
vlans = set(range(*self.cluster.network_config.vlan_range))
|
|
|
|
for node in payload:
|
|
self.assertTrue(
|
|
any(map(lambda net: vlans.issubset(net['vlans']),
|
|
node['networks'])))
|