1062 lines
40 KiB
Python
1062 lines
40 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.
|
|
|
|
from mock import patch
|
|
|
|
from copy import deepcopy
|
|
from oslo_serialization import jsonutils
|
|
import six
|
|
|
|
from nailgun import consts
|
|
from nailgun import objects
|
|
|
|
from nailgun.test.base import BaseIntegrationTest
|
|
from nailgun.utils import reverse
|
|
|
|
|
|
class TestHandlers(BaseIntegrationTest):
|
|
|
|
def test_get_handler_with_wrong_nodeid(self):
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': 1}),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 404)
|
|
|
|
def test_get_handler_with_invalid_data(self):
|
|
meta = self.env.default_metadata()
|
|
meta["interfaces"] = []
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
meta_list = [
|
|
{'interfaces': None},
|
|
{'interfaces': {}}
|
|
]
|
|
for nic_meta in meta_list:
|
|
meta = self.env.default_metadata()
|
|
meta.update(nic_meta)
|
|
node_data = {'mac': node['mac'], 'meta': meta}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
expect_errors=True,
|
|
headers=self.default_headers
|
|
)
|
|
self.assertEqual(resp.status_code, 400)
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers
|
|
)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(resp.json_body, [])
|
|
|
|
def test_get_handler_with_incompleted_iface_data(self):
|
|
meta = self.env.default_metadata()
|
|
meta["interfaces"] = []
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
meta_clean_list = [
|
|
{'interfaces': [{'name': '', 'mac': '00:00:00:00:00:00'}]},
|
|
{'interfaces': [{'mac': '00:00:00:00:00:00'}]},
|
|
{'interfaces': [{'name': 'eth0'}]}
|
|
]
|
|
|
|
for nic_meta in meta_clean_list:
|
|
meta = self.env.default_metadata()
|
|
meta.update(nic_meta)
|
|
node_data = {'mac': node['mac'], 'meta': meta}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
expect_errors=True,
|
|
headers=self.default_headers
|
|
)
|
|
self.assertEqual(resp.status_code, 200)
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers
|
|
)
|
|
self.assertEqual(resp.json_body, [])
|
|
|
|
def test_get_handler_with_invalid_speed_data(self):
|
|
meta = self.env.default_metadata()
|
|
meta["interfaces"] = []
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
|
|
meta_clean_list = [
|
|
{'interfaces': [{'name': 'eth0', 'mac': '00:00:00:00:00:00',
|
|
'max_speed': -100}]},
|
|
{'interfaces': [{'name': 'eth0', 'mac': '00:00:00:00:00:00',
|
|
'current_speed': -100}]},
|
|
{'interfaces': [{'name': 'eth0', 'mac': '00:00:00:00:00:00',
|
|
'current_speed': '100'}]},
|
|
{'interfaces': [{'name': 'eth0', 'mac': '00:00:00:00:00:00',
|
|
'max_speed': 10.0}]},
|
|
{'interfaces': [{'name': 'eth0', 'mac': '00:00:00:00:00:00',
|
|
'max_speed': '100'}]},
|
|
{'interfaces': [{'name': 'eth0', 'mac': '00:00:00:00:00:00',
|
|
'current_speed': 10.0}]}
|
|
]
|
|
for nic_meta in meta_clean_list:
|
|
meta = self.env.default_metadata()
|
|
meta.update(nic_meta)
|
|
node_data = {'mac': node['mac'], 'meta': meta}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
expect_errors=True,
|
|
headers=self.default_headers
|
|
)
|
|
self.assertEqual(resp.status_code, 200)
|
|
resp = self.app.get(
|
|
reverse('NodeHandler', kwargs={'obj_id': node['id']}),
|
|
headers=self.default_headers
|
|
)
|
|
ifaces = resp.json_body['meta']['interfaces']
|
|
self.assertEqual(
|
|
ifaces,
|
|
[
|
|
{'name': 'eth0', 'mac': '00:00:00:00:00:00',
|
|
'max_speed': None, 'current_speed': None}
|
|
]
|
|
)
|
|
|
|
def test_get_handler_without_NICs(self):
|
|
meta = self.env.default_metadata()
|
|
meta["interfaces"] = []
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(resp.json_body, [])
|
|
|
|
@patch.dict('nailgun.api.v1.handlers.version.settings.VERSION', {
|
|
'release': '5.0'})
|
|
def test_get_handler_with_NICs_before_61(self):
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [
|
|
{'name': 'eth0', 'mac': self.env.generate_random_mac(),
|
|
'current_speed': 1, 'max_speed': 1},
|
|
{'name': 'eth1', 'mac': self.env.generate_random_mac(),
|
|
'current_speed': 1, 'max_speed': 1}])
|
|
|
|
self.env.create_node(api=True, meta=meta)
|
|
node_db = self.env.nodes[0]
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node_db.id}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertItemsEqual(
|
|
map(lambda i: i['id'], resp.json_body),
|
|
map(lambda i: i.id, node_db.interfaces)
|
|
)
|
|
for nic in meta['interfaces']:
|
|
filtered_nics = filter(
|
|
lambda i: i['mac'] == nic['mac'],
|
|
resp.json_body
|
|
)
|
|
resp_nic = filtered_nics[0]
|
|
self.assertEqual(resp_nic['mac'], nic['mac'])
|
|
self.assertEqual(resp_nic['current_speed'], nic['current_speed'])
|
|
self.assertEqual(resp_nic['max_speed'], nic['max_speed'])
|
|
for conn in ('assigned_networks', ):
|
|
self.assertEqual(resp_nic[conn], [])
|
|
self.assertNotIn('interface_properties', resp_nic)
|
|
|
|
def test_nic_mac_swap(self):
|
|
mac_eth0 = '00:11:22:dd:ee:ff'
|
|
mac_eth1 = 'aa:bb:cc:33:44:55'
|
|
|
|
eth0 = {
|
|
'name': 'eth0',
|
|
'mac': mac_eth0,
|
|
'current_speed': 1,
|
|
'state': 'up',
|
|
'pxe': True
|
|
}
|
|
|
|
eth1 = {
|
|
'name': 'eth1',
|
|
'mac': mac_eth1,
|
|
'current_speed': 1,
|
|
'state': 'up',
|
|
'pxe': False
|
|
}
|
|
|
|
# prepare metadata with our interfaces
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [eth0, eth1])
|
|
|
|
# NOTE(prmtl) hack to have all mac set as we want
|
|
# crete_node() will generate random mac for 1st iface
|
|
# if we will not set it like that
|
|
node_mac = meta['interfaces'][0]['mac']
|
|
node = self.env.create_node(api=True, meta=meta, mac=node_mac)
|
|
self.env.create_cluster(api=True, nodes=[node['id']])
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
original_nic_info = resp.json
|
|
|
|
# swap macs, make them uppercase to check that we handle that correctly
|
|
eth0['mac'], eth1['mac'] = eth1['mac'].upper(), eth0['mac'].upper()
|
|
|
|
# update nodes with swapped macs
|
|
new_meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(new_meta, [eth0, eth1])
|
|
node_data = {'mac': node['mac'], 'meta': new_meta}
|
|
self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
headers=self.default_headers)
|
|
|
|
# check that networks are assigned to the same interfaces
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
updated_nic_info = resp.json
|
|
|
|
for orig_iface in original_nic_info:
|
|
updated_iface = next(
|
|
iface for iface in updated_nic_info
|
|
if iface['mac'] == orig_iface['mac'])
|
|
|
|
self.assertEqual(
|
|
orig_iface['assigned_networks'],
|
|
orig_iface['assigned_networks'])
|
|
# nic names were swapped
|
|
self.assertNotEqual(orig_iface['name'], updated_iface['name'])
|
|
|
|
def test_NIC_updates_by_agent(self):
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [
|
|
{'name': 'eth0', 'mac': '00:00:00:00:00:00', 'current_speed': 1,
|
|
'state': 'up'}])
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
new_meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(new_meta, [
|
|
{'name': 'new_nic',
|
|
'mac': '00:00:00:00:00:00',
|
|
'current_speed': 10,
|
|
'max_speed': 10,
|
|
'state': 'down',
|
|
'interface_properties': {
|
|
'sriov': {
|
|
'sriov_totalvfs': 8,
|
|
'available': True,
|
|
'pci_id': '1234:5678'
|
|
},
|
|
'pci_id': '8765:4321',
|
|
'numa_node': 1
|
|
}}]
|
|
)
|
|
node_data = {'mac': node['mac'], 'meta': new_meta, 'is_agent': True}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(len(resp.json_body), 1)
|
|
resp_nic = resp.json_body[0]
|
|
nic = new_meta['interfaces'][0]
|
|
self.assertEqual(resp_nic['mac'], nic['mac'])
|
|
self.assertEqual(resp_nic['current_speed'], nic['current_speed'])
|
|
self.assertEqual(resp_nic['max_speed'], nic['max_speed'])
|
|
self.assertEqual(resp_nic['state'], nic['state'])
|
|
self.assertEqual(
|
|
resp_nic['interface_properties']['sriov'],
|
|
{
|
|
'enabled': False,
|
|
'sriov_numvfs': None,
|
|
'sriov_totalvfs': 8,
|
|
'available': True,
|
|
'pci_id': '1234:5678',
|
|
'physnet': 'physnet2'
|
|
})
|
|
self.assertEqual(
|
|
resp_nic['interface_properties']['dpdk'],
|
|
{
|
|
'enabled': False,
|
|
'available': False
|
|
})
|
|
for conn in ('assigned_networks', ):
|
|
self.assertEqual(resp_nic[conn], [])
|
|
|
|
def create_cluster_and_node_with_dpdk_support(self, segment_type,
|
|
drivers_mock):
|
|
drivers_mock.return_value = {
|
|
'driver_1': ['8765:4321']
|
|
}
|
|
cluster = self.env.create_cluster(net_segment_type=segment_type)
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [
|
|
{'name': 'eth0', 'mac': '00:00:00:00:00:00', 'current_speed': 1,
|
|
'state': 'up'}])
|
|
node = self.env.create_node(api=True, meta=meta,
|
|
cluster_id=cluster['id'])
|
|
new_meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(new_meta, [
|
|
{'name': 'eth0',
|
|
'mac': '00:00:00:00:00:00',
|
|
'current_speed': 10,
|
|
'max_speed': 10,
|
|
'state': 'up',
|
|
'interface_properties': {
|
|
'pci_id': '8765:4321',
|
|
'numa_node': 1
|
|
}}]
|
|
)
|
|
node_data = {'mac': node['mac'], 'meta': new_meta, 'is_agent': True}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
return node
|
|
|
|
def check_update_dpdk_availability(self, node, dpdk_available):
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(len(resp.json_body), 1)
|
|
resp_nic = resp.json_body[0]
|
|
self.assertEqual(
|
|
resp_nic['interface_properties']['dpdk'],
|
|
{
|
|
'enabled': False,
|
|
'available': dpdk_available
|
|
})
|
|
return resp.json_body
|
|
|
|
def check_put_request_passes_without_dpdk_section(self, node, nics):
|
|
# remove 'dpdk' section from all interfaces
|
|
for nic in nics:
|
|
nic['interface_properties'].pop('dpdk', None)
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler", kwargs={"node_id": node['id']}),
|
|
jsonutils.dumps(nics),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
@patch('nailgun.objects.Release.get_supported_dpdk_drivers')
|
|
def test_update_dpdk_unavailable_tun(self, drivers_mock):
|
|
node = self.create_cluster_and_node_with_dpdk_support(
|
|
consts.NEUTRON_SEGMENT_TYPES.tun, drivers_mock)
|
|
nics = self.check_update_dpdk_availability(node, False)
|
|
self.check_put_request_passes_without_dpdk_section(node, nics)
|
|
|
|
@patch('nailgun.objects.Release.get_supported_dpdk_drivers')
|
|
def test_update_dpdk_available_vlan(self, drivers_mock):
|
|
node = self.create_cluster_and_node_with_dpdk_support(
|
|
consts.NEUTRON_SEGMENT_TYPES.vlan, drivers_mock)
|
|
nics = self.check_update_dpdk_availability(node, True)
|
|
self.check_put_request_passes_without_dpdk_section(node, nics)
|
|
|
|
def test_NIC_offloading_modes(self):
|
|
meta = self.env.default_metadata()
|
|
meta["interfaces"] = []
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
new_meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(new_meta, [
|
|
{'name': 'new_nic',
|
|
'mac': '00:00:00:00:00:00',
|
|
'offloading_modes': [
|
|
{
|
|
'name': 'mode_1',
|
|
'state': True,
|
|
"sub": []
|
|
},
|
|
{
|
|
'name': 'mode_2',
|
|
'state': False,
|
|
"sub": []
|
|
},
|
|
{
|
|
'name': 'mode_3',
|
|
'state': None,
|
|
"sub": []
|
|
}
|
|
]}])
|
|
node_data = {'mac': node['mac'], 'meta': new_meta}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(len(resp.json_body), 1)
|
|
resp_nic = resp.json_body[0]
|
|
nic = new_meta['interfaces'][0]
|
|
self.assertEqual(resp_nic['offloading_modes'], nic['offloading_modes'])
|
|
|
|
def test_NIC_change_offloading_modes(self):
|
|
meta = self.env.default_metadata()
|
|
meta["interfaces"] = []
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
new_meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(new_meta, [
|
|
{'name': 'new_nic',
|
|
'mac': '00:00:00:00:00:00',
|
|
'offloading_modes': [
|
|
{
|
|
'name': 'mode_1',
|
|
'state': None,
|
|
"sub": []
|
|
},
|
|
{
|
|
'name': 'mode_2',
|
|
'state': None,
|
|
"sub": []
|
|
},
|
|
{
|
|
'name': 'mode_3',
|
|
'state': None,
|
|
"sub": []
|
|
}
|
|
]}])
|
|
node_data = {'mac': node['mac'], 'meta': new_meta}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(len(resp.json_body), 1)
|
|
resp_nic = resp.json_body[0]
|
|
nic = new_meta['interfaces'][0]
|
|
self.assertEqual(resp_nic['offloading_modes'], nic['offloading_modes'])
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeCollectionHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(len(resp.json_body), 1)
|
|
|
|
resp_node = resp.json_body[0]
|
|
new_nic = {
|
|
'name': 'new_nic',
|
|
'mac': '00:00:00:00:00:00',
|
|
'offloading_modes': [
|
|
{
|
|
'name': 'mode_1',
|
|
'state': True,
|
|
"sub": []
|
|
},
|
|
{
|
|
'name': 'mode_2',
|
|
'state': False,
|
|
"sub": []
|
|
},
|
|
{
|
|
'name': 'mode_3',
|
|
'state': None,
|
|
"sub": []
|
|
}
|
|
]
|
|
}
|
|
self.env.set_interfaces_in_meta(resp_node["meta"], [
|
|
new_nic])
|
|
|
|
resp_node.pop('group_id')
|
|
|
|
resp = self.app.put(
|
|
reverse('NodeCollectionHandler'),
|
|
jsonutils.dumps([resp_node]),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(len(resp.json_body), 1)
|
|
resp_nic = resp.json_body[0]
|
|
self.assertEqual(
|
|
resp_nic['offloading_modes'],
|
|
new_nic['offloading_modes'])
|
|
|
|
def test_NIC_locking_on_update_by_agent(self):
|
|
lock_vs_status = {
|
|
consts.NODE_STATUSES.discover: False,
|
|
consts.NODE_STATUSES.error: False,
|
|
consts.NODE_STATUSES.provisioning: True,
|
|
consts.NODE_STATUSES.provisioned: True,
|
|
consts.NODE_STATUSES.deploying: True,
|
|
consts.NODE_STATUSES.ready: True,
|
|
consts.NODE_STATUSES.removing: True}
|
|
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [
|
|
{'name': 'eth0', 'mac': '00:00:00:00:00:00', 'current_speed': 1,
|
|
'state': 'up', 'pxe': True}])
|
|
self.env.create_node(api=True, meta=meta)
|
|
new_meta = deepcopy(meta)
|
|
node = self.env.nodes[0]
|
|
|
|
for status, lock in six.iteritems(lock_vs_status):
|
|
node.status = status
|
|
self.db.flush()
|
|
|
|
new_meta['interfaces'][0]['current_speed'] += 1
|
|
node_data = {'mac': node['mac'], 'meta': new_meta}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
resp_nic = resp.json_body[0]
|
|
new_speed = new_meta['interfaces'][0]['current_speed']
|
|
old_speed = meta['interfaces'][0]['current_speed']
|
|
self.assertEqual(resp_nic['current_speed'],
|
|
old_speed if lock else new_speed)
|
|
meta['interfaces'][0]['current_speed'] = resp_nic['current_speed']
|
|
|
|
@patch.dict('nailgun.api.v1.handlers.version.settings.VERSION', {
|
|
'release': '6.1'})
|
|
def test_interface_properties_after_update_by_agent(self):
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [
|
|
{'name': 'eth0', 'mac': '00:00:00:00:00:00', 'current_speed': 1,
|
|
'pxe': True, 'state': 'up'}])
|
|
self.env.create(
|
|
nodes_kwargs=[
|
|
{"api": True, 'meta': meta}
|
|
]
|
|
)
|
|
node = self.env.nodes[0]
|
|
node_data = {'mac': node['mac'], 'meta': meta}
|
|
# check default interface_properties values
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
nic = resp.json_body[0]
|
|
self.assertEqual(
|
|
nic['interface_properties'],
|
|
self.env.network_manager.get_default_interface_properties()
|
|
)
|
|
# change mtu
|
|
nic['interface_properties']['mtu'] = 1500
|
|
nodes_list = [{'id': node['id'], 'interfaces': [nic]}]
|
|
resp_put = self.app.put(
|
|
reverse('NodeCollectionNICsHandler'),
|
|
jsonutils.dumps(nodes_list),
|
|
headers=self.default_headers
|
|
)
|
|
self.assertEqual(resp_put.status_code, 200)
|
|
# update NICs by agent (no interface_properties values provided)
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
# check interface_properties values were not reset to default
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
resp_nic = resp.json_body[0]
|
|
self.assertEqual(resp_nic['interface_properties']['mtu'], 1500)
|
|
|
|
def test_nic_adds_by_agent(self):
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [
|
|
{'name': 'eth0', 'mac': '00:00:00:00:00:00', 'current_speed': 1,
|
|
'pxe': True, 'state': 'up'}])
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
|
|
meta['interfaces'].append({
|
|
'name': 'new_nic', 'mac': '00:00:00:00:00:01'})
|
|
node_data = {'mac': node['mac'], 'meta': meta}
|
|
resp = self.app.put(
|
|
reverse('NodeAgentHandler'),
|
|
jsonutils.dumps(node_data),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertEqual(len(resp.json_body), len(meta['interfaces']))
|
|
for nic in meta['interfaces']:
|
|
filtered_nics = filter(
|
|
lambda i: i['mac'] == nic['mac'],
|
|
resp.json_body
|
|
)
|
|
resp_nic = filtered_nics[0]
|
|
self.assertEqual(resp_nic['mac'], nic['mac'])
|
|
self.assertEqual(resp_nic['current_speed'],
|
|
nic.get('current_speed'))
|
|
self.assertEqual(resp_nic['max_speed'], nic.get('max_speed'))
|
|
self.assertEqual(resp_nic['state'], nic.get('state'))
|
|
for conn in ('assigned_networks', ):
|
|
self.assertEqual(resp_nic[conn], [])
|
|
|
|
def test_ignore_NIC_id_in_meta(self):
|
|
fake_id = 'some_data'
|
|
meta = self.env.default_metadata()
|
|
self.env.set_interfaces_in_meta(meta, [
|
|
{'id': fake_id, 'name': 'eth0', 'mac': '12345'}])
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertNotEquals(resp.json_body[0]['id'], fake_id)
|
|
|
|
def test_mac_address_should_be_in_lower_case(self):
|
|
meta = self.env.default_metadata()
|
|
new_mac = 'AA:BB:CC:DD:11:22'
|
|
self.env.set_interfaces_in_meta(meta, [
|
|
{'name': 'eth0', 'mac': new_mac}])
|
|
node = self.env.create_node(api=True, meta=meta)
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertNotEquals(resp.json_body[0]['mac'], new_mac.lower())
|
|
|
|
def test_remove_assigned_interface(self):
|
|
def get_nodes():
|
|
resp = self.app.get(
|
|
reverse('NodeCollectionHandler',
|
|
kwargs={'cluster_id': cluster.id}),
|
|
headers=self.default_headers,
|
|
)
|
|
return resp.json_body
|
|
|
|
cluster = self.env.create(nodes_kwargs=[{'api': True}])
|
|
|
|
# check all possible handlers
|
|
for handler in ('NodeAgentHandler',
|
|
'NodeHandler',
|
|
'NodeCollectionHandler'):
|
|
|
|
# create node and check it availability
|
|
nodes_data = get_nodes()
|
|
self.assertEqual(len(nodes_data), 1)
|
|
|
|
node_db = objects.Node.get_by_uid(nodes_data[0]['id'])
|
|
|
|
# remove all interfaces except admin one
|
|
adm_eth = self.env.network_manager._get_interface_by_network_name(
|
|
node_db, 'fuelweb_admin')
|
|
ifaces = list(nodes_data[0]['meta']['interfaces'])
|
|
nodes_data[0]['meta']['interfaces'] = \
|
|
[i for i in ifaces if i['name'] == adm_eth.name]
|
|
|
|
# prepare put request
|
|
data = {
|
|
'id': nodes_data[0]['id'],
|
|
'meta': nodes_data[0]['meta'],
|
|
}
|
|
if handler in ('NodeCollectionHandler', ):
|
|
data = [data]
|
|
|
|
if handler in ('NodeHandler', ):
|
|
endpoint = reverse(handler, kwargs={'obj_id': data['id']})
|
|
else:
|
|
endpoint = reverse(handler)
|
|
|
|
self.app.put(
|
|
endpoint,
|
|
jsonutils.dumps(data),
|
|
headers=self.default_headers,
|
|
)
|
|
|
|
# check the node is visible for api
|
|
nodes_data = get_nodes()
|
|
self.assertEqual(len(nodes_data), 1)
|
|
self.assertEqual(len(nodes_data[0]['meta']['interfaces']), 1)
|
|
|
|
# restore removed interfaces
|
|
nodes_data[0]['meta']['interfaces'] = ifaces
|
|
self.app.put(
|
|
reverse(
|
|
'NodeAgentHandler',
|
|
),
|
|
jsonutils.dumps({
|
|
'id': nodes_data[0]['id'],
|
|
'meta': nodes_data[0]['meta'],
|
|
}),
|
|
headers=self.default_headers,
|
|
)
|
|
|
|
# check node availability
|
|
nodes_data = get_nodes()
|
|
self.assertEqual(len(nodes_data), 1)
|
|
self.assertItemsEqual(nodes_data[0]['meta']['interfaces'], ifaces)
|
|
|
|
def test_change_mac_of_assigned_nics(self):
|
|
def get_nodes():
|
|
resp = self.app.get(
|
|
reverse('NodeCollectionHandler',
|
|
kwargs={'cluster_id': cluster.id}),
|
|
headers=self.default_headers,
|
|
)
|
|
return resp.json_body
|
|
|
|
meta = self.env.default_metadata()
|
|
meta["interfaces"] = [
|
|
{'name': 'eth0', 'mac': self.env.generate_random_mac(),
|
|
'pxe': True},
|
|
{'name': 'eth1', 'mac': self.env.generate_random_mac()},
|
|
{'name': 'eth2', 'mac': self.env.generate_random_mac()},
|
|
{'name': 'eth3', 'mac': self.env.generate_random_mac()},
|
|
{'name': 'eth4', 'mac': self.env.generate_random_mac()},
|
|
]
|
|
cluster = self.env.create(nodes_kwargs=[{'api': True, 'meta': meta}])
|
|
|
|
# check all possible handlers
|
|
for handler in ('NodeAgentHandler',
|
|
'NodeHandler',
|
|
'NodeCollectionHandler'):
|
|
|
|
# create node and check it availability
|
|
nodes_data = get_nodes()
|
|
self.assertEqual(len(nodes_data), 1)
|
|
|
|
node_db = objects.Node.get_by_uid(nodes_data[0]['id'])
|
|
|
|
# change mac address of interfaces except admin one
|
|
adm_eth = self.env.network_manager._get_interface_by_network_name(
|
|
node_db, 'fuelweb_admin')
|
|
for iface in nodes_data[0]['meta']['interfaces']:
|
|
if iface['name'] != adm_eth.name:
|
|
iface['mac'] = self.env.generate_random_mac()
|
|
|
|
# prepare put request
|
|
data = {
|
|
'id': nodes_data[0]['id'],
|
|
'meta': nodes_data[0]['meta'],
|
|
}
|
|
if handler in ('NodeCollectionHandler', ):
|
|
data = [data]
|
|
|
|
if handler in ('NodeHandler', ):
|
|
endpoint = reverse(handler, kwargs={'obj_id': data['id']})
|
|
else:
|
|
endpoint = reverse(handler)
|
|
|
|
self.app.put(
|
|
endpoint,
|
|
jsonutils.dumps(data),
|
|
headers=self.default_headers,
|
|
)
|
|
|
|
# check the node is visible for api
|
|
nodes_data = get_nodes()
|
|
self.assertEqual(len(nodes_data), 1)
|
|
|
|
def test_pxe_for_admin_nws_restriction(self):
|
|
meta = self.env.default_metadata()
|
|
# We are using reverse ordered by iface name list
|
|
# for reproducing bug #1474330
|
|
meta['interfaces'] = [
|
|
{'name': 'eth1', 'mac': self.env.generate_random_mac(),
|
|
'pxe': False},
|
|
{'name': 'eth0', 'mac': self.env.generate_random_mac(),
|
|
'pxe': False},
|
|
]
|
|
cluster = self.env.create(nodes_kwargs=[{'api': False, 'meta': meta}])
|
|
|
|
node = cluster.nodes[0]
|
|
|
|
# Processing data through NodeHandler
|
|
resp = self.app.get(
|
|
reverse('NodeHandler', kwargs={'obj_id': node.id}),
|
|
headers=self.default_headers,
|
|
)
|
|
data = resp.json_body
|
|
|
|
resp = self.app.put(
|
|
reverse('NodeHandler', kwargs={'obj_id': data['id']}),
|
|
jsonutils.dumps(data),
|
|
headers=self.default_headers,
|
|
)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
# Processing data through NICsHander
|
|
resp = self.app.get(
|
|
reverse("NodeNICsHandler", kwargs={"node_id": node.id}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = resp.json_body
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler", kwargs={"node_id": node.id}),
|
|
jsonutils.dumps(data),
|
|
headers=self.default_headers,
|
|
)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
|
|
class TestSriovHandlers(BaseIntegrationTest):
|
|
|
|
def setUp(self):
|
|
super(TestSriovHandlers, self).setUp()
|
|
cluster = self.env.create_cluster(
|
|
editable_attributes={
|
|
'common': {
|
|
'libvirt_type': {
|
|
'value': consts.HYPERVISORS.kvm
|
|
}
|
|
}
|
|
}
|
|
)
|
|
self.env.create_nodes_w_interfaces_count(
|
|
1, 3, cluster_id=cluster.id, api=True)
|
|
self.nics = self.get_node_interfaces()
|
|
|
|
def get_node_interfaces(self):
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler',
|
|
kwargs={'node_id': self.env.nodes[0].id}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
return resp.json_body
|
|
|
|
def test_get_update_sriov_properties(self):
|
|
sriov_default = \
|
|
self.env.network_manager.get_default_interface_properties()[
|
|
'sriov']
|
|
# Use NIC #2 as SR-IOV can be enabled only on NICs that have no
|
|
# assigned networks. First two NICs have assigned networks.
|
|
self.assertEqual(self.nics[2]['interface_properties']['sriov'],
|
|
sriov_default)
|
|
|
|
# change NIC properties in DB as SR-IOV parameters can be set up only
|
|
# for NICs that have hardware SR-IOV support
|
|
nic = self.env.nodes[0].nic_interfaces[2]
|
|
nic.interface_properties['sriov']['available'] = True
|
|
nic.interface_properties['sriov']['sriov_totalvfs'] = 8
|
|
nic.interface_properties.changed()
|
|
|
|
nics = self.get_node_interfaces()
|
|
nics[2]['interface_properties']['sriov'].update({
|
|
'enabled': True,
|
|
'sriov_numvfs': 8,
|
|
'physnet': 'new_physnet'
|
|
})
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(nics),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
sriov = resp.json_body[2]['interface_properties']['sriov']
|
|
self.assertEqual(sriov['enabled'], True)
|
|
self.assertEqual(sriov['sriov_numvfs'], 8)
|
|
self.assertEqual(sriov['physnet'], 'new_physnet')
|
|
|
|
def test_update_readonly_sriov_properties_failed(self):
|
|
self.nics[0]['interface_properties']['sriov']['available'] = True
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(self.nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertEqual(
|
|
resp.json_body['message'],
|
|
"Node '{0}' interface '{1}': SR-IOV parameter 'available' cannot "
|
|
"be changed through API".format(
|
|
self.env.nodes[0].id, self.nics[0]['name']))
|
|
|
|
def test_enable_sriov_failed_when_not_available(self):
|
|
self.nics[0]['interface_properties']['sriov']['enabled'] = True
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(self.nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertEqual(
|
|
resp.json_body['message'],
|
|
"Node '{0}' interface '{1}': SR-IOV cannot be enabled as it is"
|
|
" not available".format(
|
|
self.env.nodes[0].id, self.nics[0]['name']))
|
|
|
|
def test_set_sriov_numvfs_failed(self):
|
|
self.nics[0]['interface_properties']['sriov']['sriov_numvfs'] = 8
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(self.nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertEqual(
|
|
resp.json_body['message'],
|
|
"Node '{0}' interface '{1}': '8' virtual functions was "
|
|
"requested but just '0' are available".format(
|
|
self.env.nodes[0].id, self.nics[0]['name']))
|
|
|
|
def test_set_sriov_numvfs_failed_negative_value(self):
|
|
self.nics[0]['interface_properties']['sriov']['sriov_numvfs'] = -40
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(self.nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertIn(
|
|
"-40 is not valid under any of the given schemas",
|
|
resp.json_body['message']
|
|
)
|
|
|
|
def test_set_sriov_numvfs_failed_float_value(self):
|
|
self.nics[0]['interface_properties']['sriov']['sriov_numvfs'] = 2.5
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(self.nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertIn(
|
|
"2.5 is not valid under any of the given schemas",
|
|
resp.json_body['message']
|
|
)
|
|
|
|
def test_set_sriov_numvfs_zero_value(self):
|
|
self.nics[0]['interface_properties']['sriov']['sriov_numvfs'] = 0
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(self.nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertIn(
|
|
"0 is not valid under any of the given schemas",
|
|
resp.json_body['message']
|
|
)
|
|
|
|
def test_enable_sriov_without_number_of_functions(self):
|
|
# change NIC properties in DB as SR-IOV parameters can be set up only
|
|
# for NICs that have hardware SR-IOV support
|
|
nic = objects.NIC.get_by_uid(self.env.nodes[0].nic_interfaces[0].id)
|
|
nic.interface_properties['sriov']['available'] = True
|
|
nic.interface_properties['sriov']['sriov_totalvfs'] = 8
|
|
nic.interface_properties.changed()
|
|
|
|
nics = self.get_node_interfaces()
|
|
nics[0]['interface_properties']['sriov']['enabled'] = True
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertIn(
|
|
"Node '{0}' interface '{1}': virtual functions can not be"
|
|
" enabled for interface when 'sriov_numfs' option is not"
|
|
" specified!".format(self.env.nodes[0].id,
|
|
self.env.nodes[0].nic_interfaces[0].name),
|
|
resp.json_body['message']
|
|
)
|
|
|
|
def test_enable_sriov_failed_with_non_kvm_hypervisor(self):
|
|
node = self.env.create_node(api=True, roles=['compute'])
|
|
self.env.create_cluster(
|
|
api=True,
|
|
nodes=[node['id']],
|
|
editable_attributes={
|
|
'common': {
|
|
'libvirt_type': {
|
|
'value': consts.HYPERVISORS.qemu
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
resp = self.app.get(
|
|
reverse('NodeNICsHandler',
|
|
kwargs={'node_id': node['id']}),
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 200)
|
|
nics = resp.json_body
|
|
nics[0]['interface_properties']['sriov']['enabled'] = True
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": node['id']}),
|
|
jsonutils.dumps(nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertEqual(
|
|
"Only KVM hypervisor works with SR-IOV.",
|
|
resp.json_body['message']
|
|
)
|
|
|
|
def test_enable_sriov_failed_when_nic_has_networks_assigned(self):
|
|
nic = self.env.nodes[0].nic_interfaces[0]
|
|
nic.interface_properties['sriov']['available'] = True
|
|
nic.interface_properties['sriov']['sriov_totalvfs'] = 8
|
|
nic.interface_properties.changed()
|
|
|
|
nics = self.get_node_interfaces()
|
|
nics[0]['interface_properties']['sriov']['enabled'] = True
|
|
nics[0]['interface_properties']['sriov']['sriov_numvfs'] = 8
|
|
|
|
resp = self.app.put(
|
|
reverse("NodeNICsHandler",
|
|
kwargs={"node_id": self.env.nodes[0].id}),
|
|
jsonutils.dumps(nics),
|
|
expect_errors=True,
|
|
headers=self.default_headers)
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertEqual(
|
|
resp.json_body['message'],
|
|
"Node '{0}' interface '{1}': SR-IOV cannot be enabled when "
|
|
"networks are assigned to the interface".format(
|
|
self.env.nodes[0].id, nics[0]['name']))
|