fuel-web/nailgun/nailgun/test/integration/test_node_collection_handlers.py
Jenkins d91d7bcfbf Networks configuration API is now available for stopped and ready nodes
So it is possible to update network configuration for node in
'stopped' and 'ready' state using NodeCollectionHandler,
NodeNICsHandler and NodeCollectionNICsHandler

Network config update via NodeAgentHandler is still restricted but
it is still possible to update metadata from this handler.

'is agent' request flag is now added automatically if not persist
when update is launched via NodeAgentHandler.

Change-Id: I5f41c74553b4227cd968900fb121d26ff2029cda
Partial-Bug: #1581527
2016-05-27 19:40:09 +03:00

640 lines
21 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
from oslo_serialization import jsonutils
from nailgun.db.sqlalchemy.models import Node
from nailgun.db.sqlalchemy.models import Notification
from nailgun.extensions.network_manager.models.network import NodeNICInterface
from nailgun.test.base import BaseIntegrationTest
from nailgun.utils import reverse
class TestHandlers(BaseIntegrationTest):
def test_node_list_empty(self):
resp = self.app.get(
reverse('NodeCollectionHandler'),
headers=self.default_headers
)
self.assertEqual(200, resp.status_code)
self.assertEqual([], resp.json_body)
def test_notification_node_id(self):
node = self.env.create_node(
api=True,
meta=self.env.default_metadata()
)
notif = self.db.query(Notification).first()
self.assertEqual(node['id'], notif.node_id)
resp = self.app.get(
reverse('NotificationCollectionHandler'),
headers=self.default_headers
)
notif_api = resp.json_body[0]
self.assertEqual(node['id'], notif_api['node_id'])
def test_node_get_with_cluster(self):
cluster = self.env.create(
cluster_kwargs={"api": True},
nodes_kwargs=[
{"cluster_id": None},
{},
]
)
resp = self.app.get(
reverse('NodeCollectionHandler'),
params={'cluster_id': cluster.id},
headers=self.default_headers
)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(resp.json_body))
self.assertEqual(
self.env.nodes[1].id,
resp.json_body[0]['id']
)
def test_node_get_with_cluster_None(self):
self.env.create(
cluster_kwargs={"api": True},
nodes_kwargs=[
{"cluster_id": None},
{},
]
)
resp = self.app.get(
reverse('NodeCollectionHandler'),
params={'cluster_id': ''},
headers=self.default_headers
)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(resp.json_body))
self.assertEqual(self.env.nodes[0].id, resp.json_body[0]['id'])
def test_node_get_without_cluster_specification(self):
self.env.create(
cluster_kwargs={"api": True},
nodes_kwargs=[
{"cluster_id": None},
{},
]
)
resp = self.app.get(
reverse('NodeCollectionHandler'),
headers=self.default_headers
)
self.assertEqual(200, resp.status_code)
self.assertEqual(2, len(resp.json_body))
def test_node_get_with_cluster_and_assigned_ip_addrs(self):
self.env.create(
cluster_kwargs={},
nodes_kwargs=[
{"pending_addition": True, "api": True},
{"pending_addition": True, "api": True}
]
)
self.env.network_manager.assign_ips(
self.env.clusters[-1],
self.env.nodes,
"management"
)
resp = self.app.get(
reverse('NodeCollectionHandler'),
headers=self.default_headers
)
self.assertEqual(200, resp.status_code)
self.assertEqual(2, len(resp.json_body))
def test_node_creation(self):
resp = self.app.post(
reverse('NodeCollectionHandler'),
jsonutils.dumps({'mac': self.env.generate_random_mac(),
'meta': self.env.default_metadata(),
'status': 'discover'}),
headers=self.default_headers)
self.assertEqual(resp.status_code, 201)
self.assertEqual('discover', resp.json_body['status'])
def test_node_update(self):
node = self.env.create_node(api=False)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'mac': node.mac, 'manufacturer': 'new'}]),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
resp = self.app.get(
reverse('NodeCollectionHandler'),
headers=self.default_headers
)
node = self.db.query(Node).get(node.id)
self.assertEqual('new', node.manufacturer)
def test_node_update_empty_mac_or_id(self):
node = self.env.create_node(api=False)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'manufacturer': 'man0'}]),
headers=self.default_headers,
expect_errors=True)
self.assertEqual(resp.status_code, 400)
self.assertEqual(
resp.json_body["message"],
"Neither MAC nor ID is specified"
)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'id': node.id,
'mac': None,
'manufacturer': 'man4'}]),
headers=self.default_headers,
expect_errors=True)
self.assertEqual(resp.status_code, 400)
self.assertIn(
"schema['properties']['mac']",
resp.json_body["message"]
)
self.assertIn(
"None is not of type 'string'",
resp.json_body["message"]
)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'mac': node.mac,
'manufacturer': 'man5'}]),
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'id': node.id,
'manufacturer': 'man6'}]),
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'mac': node.mac,
'manufacturer': 'man7'}]),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'id': node.id,
'mac': node.mac,
'manufacturer': 'man8'}]),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
def node_update_with_invalid_id(self):
node = self.env.create_node(api=False)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'id': 'new_id',
'mac': node.mac}]),
headers=self.default_headers,
expect_errors=True)
self.assertEqual(resp.status_code, 400)
self.assertEqual(
resp.json_body["message"],
"Invalid ID specified"
)
def test_node_update_agent_discover(self):
self.env.create_node(
api=False,
status='provisioning',
meta=self.env.default_metadata()
)
node_db = self.env.nodes[0]
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps(
{'mac': node_db.mac,
'status': 'discover', 'manufacturer': 'new'}
),
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)
resp = self.app.get(
reverse('NodeCollectionHandler'),
headers=self.default_headers
)
node_db = self.db.query(Node).get(node_db.id)
self.assertEqual('new', node_db.manufacturer)
self.assertEqual('provisioning', node_db.status)
def test_stopped_node_network_update_restricted_for_agent(self):
node = self.env.create_node(
api=False,
status='stopped',
meta=self.env.default_metadata()
)
node_db = self.env.nodes[0]
interfaces = node.meta['interfaces']
new_interfaces = copy.deepcopy(interfaces)
new_interfaces[1]['mac'] = '2a:00:0d:0d:00:2a'
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps(
{
'mac': node_db.mac,
'meta': {
'interfaces': new_interfaces
}
}
),
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)
node_db = self.db.query(Node).get(node_db.id)
interface_db = self.db.query(NodeNICInterface).filter_by(
node_id=node_db.id,
name=new_interfaces[1]['name']
).first()
self.assertNotEqual(
interface_db.mac,
'2a:00:0d:0d:00:2a')
def test_stopped_node_network_update_allowed_for_ui(self):
node = self.env.create_node(
api=False,
status='stopped',
meta=self.env.default_metadata()
)
node_db = self.env.nodes[0]
interfaces = node.meta['interfaces']
new_interfaces = copy.deepcopy(interfaces)
new_interfaces[1]['mac'] = '2a:00:0d:0d:00:2a'
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([
{
'mac': node_db.mac,
'meta': {
'interfaces': new_interfaces
}
}
]),
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)
interface_db = self.db.query(NodeNICInterface).filter_by(
node_id=node_db.id,
name=new_interfaces[1]['name']
).first()
self.assertEqual(
interface_db.mac,
'2a:00:0d:0d:00:2a')
def test_node_timestamp_updated_only_by_agent(self):
node = self.env.create_node(api=False)
timestamp = node.timestamp
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([
{'mac': node.mac, 'status': 'discover',
'manufacturer': 'old'}
]),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
node = self.db.query(Node).get(node.id)
self.assertEqual(node.timestamp, timestamp)
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps(
{'mac': node.mac, 'status': 'discover',
'manufacturer': 'new'}
),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
node = self.db.query(Node).get(node.id)
self.assertNotEqual(node.timestamp, timestamp)
self.assertEqual('new', node.manufacturer)
def test_agent_caching(self):
node = self.env.create_node(api=False)
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps({
'mac': node.mac,
'manufacturer': 'new',
'agent_checksum': 'test'
}),
headers=self.default_headers)
response = resp.json_body
self.assertEqual(resp.status_code, 200)
self.assertFalse('cached' in response and response['cached'])
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps({
'mac': node.mac,
'manufacturer': 'new',
'agent_checksum': 'test'
}),
headers=self.default_headers)
response = resp.json_body
self.assertEqual(resp.status_code, 200)
self.assertTrue('cached' in response and response['cached'])
def test_agent_updates_node_by_interfaces(self):
node = self.env.create_node(api=False)
interface = node.meta['interfaces'][0]
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps({
'mac': '00:00:00:00:00:00',
'meta': {
'interfaces': [interface]},
}),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
def test_node_create_ip_not_in_admin_range(self):
node = self.env.create_node(api=False)
# Set IP outside of admin network range on eth1
meta = copy.deepcopy(node.meta)
meta['interfaces'][1]['ip'] = '10.21.0.3'
self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps({
'mac': node.mac,
'meta': meta,
}),
headers=self.default_headers)
self.env.network_manager.update_interfaces_info(node)
# node.mac == eth0 mac so eth0 should now be admin interface
admin_iface = self.env.network_manager.get_admin_interface(node)
self.assertEqual(admin_iface.name, 'eth0')
def test_node_create_ext_mac(self):
node1 = self.env.create_node(
api=False
)
node2_json = {
"mac": self.env.generate_random_mac(),
"meta": self.env.default_metadata(),
"status": "discover"
}
node2_json["meta"]["interfaces"][0]["mac"] = node1.mac
resp = self.app.post(
reverse('NodeCollectionHandler'),
jsonutils.dumps(node2_json),
headers=self.default_headers,
expect_errors=True)
self.assertEqual(resp.status_code, 409)
def test_node_create_without_mac(self):
node = self.env.create_node(
api=True,
exclude=["mac"],
expect_http=400,
expected_error="No mac address specified"
)
self.assertEqual(node, None)
def test_node_create_with_invalid_disk_model(self):
meta = self.env.default_metadata()
meta['disks'][0]['model'] = None
node = self.env.create_node(
api=True,
expect_http=201,
meta=meta
)
self.assertIsNotNone(node)
def test_node_create_mac_validation(self):
# entry format: (mac_address, http_response_code)
maccaddresses = (
# invalid macaddresses
('60a44c3528ff', 400),
('60:a4:4c:35:28', 400),
('60:a4:4c:35:28:fg', 400),
('76:DC:7C:CA:G4:75', 400),
('76-DC-7C-CA-G4-75', 400),
# valid macaddresses
('60:a4:4c:35:28:ff', 201),
('48-2C-6A-1E-59-3D', 201),
)
for mac, http_code in maccaddresses:
response = self.app.post(
reverse('NodeCollectionHandler'),
jsonutils.dumps({
'mac': mac,
'status': 'discover',
}),
headers=self.default_headers,
expect_errors=(http_code != 201)
)
self.assertEqual(response.status_code, http_code)
def test_node_update_ext_mac(self):
meta = self.env.default_metadata()
node1 = self.env.create_node(
api=False,
mac=meta["interfaces"][0]["mac"],
meta={}
)
node1_json = {
"mac": self.env.generate_random_mac(),
"meta": meta
}
# We want to be sure that new mac is not equal to old one
self.assertNotEqual(node1.mac, node1_json["mac"])
# Here we are trying to update node
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([node1_json]),
headers=self.default_headers,
expect_errors=True
)
self.assertEqual(resp.status_code, 200)
# Here we are checking if node mac is successfully updated
self.assertEqual(node1_json["mac"], resp.json_body[0]["mac"])
self.assertEqual(meta, resp.json_body[0]["meta"])
def test_duplicated_node_create_fails(self):
node = self.env.create_node(api=False)
resp = self.app.post(
reverse('NodeCollectionHandler'),
jsonutils.dumps({'mac': node.mac, 'status': 'discover'}),
headers=self.default_headers,
expect_errors=True)
self.assertEqual(409, resp.status_code)
def test_node_creation_fail(self):
resp = self.app.post(
reverse('NodeCollectionHandler'),
jsonutils.dumps({'mac': self.env.generate_random_mac(),
'meta': self.env.default_metadata(),
'status': 'error'}),
headers=self.default_headers,
expect_errors=True)
self.assertEqual(resp.status_code, 403)
def test_reset_cluster_name_when_unassign_node(self):
node_name = 'new_node_name'
self.env.create(
nodes_kwargs=[
{'pending_roles': ['controller'],
'pending_addition': True,
'name': node_name}])
node = self.env.nodes[0]
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([{'id': node.id,
'cluster_id': None,
'pending_roles': []}]),
headers=self.default_headers)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(resp.json_body))
self.assertEqual(node.id, resp.json_body[0]['id'])
self.assertEqual(node.name, node_name)
self.assertEqual(node.cluster, None)
self.assertEqual(node.pending_roles, [])
def test_discovered_node_unified_name(self):
node_mac = self.env.generate_random_mac()
def node_name_test(mac):
self.env.create_node(
api=True,
**{'mac': mac}
)
node = self.app.get(reverse('NodeCollectionHandler')).json_body[0]
self.assertEqual(node['name'],
'Untitled ({0})'.format(node_mac[-5:]))
node_name_test(node_mac.upper())
node_id = self.app.get(
reverse('NodeCollectionHandler')
).json_body[0]['id']
self.app.delete(
reverse('NodeHandler', {'obj_id': node_id})
)
node_name_test(node_mac.lower())
def check_pending_roles(self, to_check, msg):
node = self.env.create_node(api=False)
data = {'id': node.id,
'cluster_id': 1}
data.update(to_check)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([data]),
headers=self.default_headers,
expect_errors=True)
self.assertEqual(400, resp.status_code)
self.assertIn(msg, resp.json_body["message"])
def test_pending_role_non_existing(self):
cluster = self.env.create()
self.check_pending_roles({'pending_roles': ['qwe'],
'cluster_id': cluster.id},
'are not valid for node')
def test_pending_role_duplicates(self):
self.check_pending_roles({'pending_roles': ['cinder', 'cinder']},
'contains duplicates')
def test_pending_role_not_list(self):
self.check_pending_roles({'pending_roles': 'cinder'},
"Failed validating 'type'")
def test_pending_role_not_strings(self):
self.check_pending_roles({'pending_roles': ['cinder', 1]},
"Failed validating 'type'")
def test_role_non_existing(self):
cluster = self.env.create()
self.check_pending_roles({'roles': ['qwe'],
'cluster_id': cluster.id},
'are not valid for node')
def test_role_duplicates(self):
self.check_pending_roles({'roles': ['cinder', 'cinder']},
'contains duplicates')
def test_roles_not_list(self):
self.check_pending_roles({'roles': 'cinder'},
'Failed validating')
def test_roles_not_strings(self):
self.check_pending_roles({'roles': ['cinder', 1]},
'Failed validating')
def check_update_role_no_cluster_id(self, data_to_check):
self.env.create()
node = self.env.create_node(api=False)
data = {'id': node.id}
data.update(data_to_check)
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([data]),
headers=self.default_headers,
expect_errors=True)
self.assertEqual(400, resp.status_code)
self.assertIn("doesn't belong to any cluster",
resp.json_body["message"])
def test_update_role_no_cluster_id(self):
self.check_update_role_no_cluster_id({'pending_roles': ['compute']})
def test_update_pending_role_no_cluster_id(self):
self.check_update_role_no_cluster_id({'roles': ['compute']})