412 lines
18 KiB
Python
412 lines
18 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
# -*- encoding: utf-8 -*-
|
|
#
|
|
#
|
|
# Copyright (c) 2013-2016 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
"""
|
|
Tests for the API /nodes/ methods.
|
|
"""
|
|
|
|
# import mox
|
|
import webtest.app
|
|
|
|
# from sysinv.common import exception
|
|
# from sysinv.common import states
|
|
# from sysinv.conductor import rpcapi
|
|
from sysinv.openstack.common import uuidutils
|
|
from sysinv.tests.api import base
|
|
from sysinv.tests.db import utils as dbutils
|
|
|
|
|
|
class TestPost(base.FunctionalTest):
|
|
|
|
def test_create_ihost(self):
|
|
# Test skipped because updating ihost's datamodel in utils.py has
|
|
# caused this test to throw an error saying:
|
|
# webtest.app.AppError: Bad response: 400 Bad Request (not 200 OK or
|
|
# 3xx redirect for http://localhost/v1/ihosts)
|
|
# '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\":
|
|
# \\"Client\\", \\"faultstring\\": \\"Unknown attribute for argument
|
|
# host: recordtype\\"}"}'
|
|
self.skipTest("Skipping to prevent failure notification on Jenkins")
|
|
ndict = dbutils.get_test_ihost()
|
|
self.post_json('/ihosts', ndict)
|
|
result = self.get_json('/ihosts/%s' % ndict['uuid'])
|
|
self.assertEqual(ndict['uuid'], result['uuid'])
|
|
|
|
def test_create_ihost_valid_extra(self):
|
|
# Test skipped because updating ihost's datamodel in utils.py has
|
|
# caused this test to throw an error saying:
|
|
# webtest.app.AppError: Bad response: 400 Bad Request (not 200 OK or
|
|
# 3xx redirect for http://localhost/v1/ihosts)
|
|
# '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\":
|
|
# \\"Client\\", \\"faultstring\\": \\"Unknown attribute for argument
|
|
# host: recordtype\\"}"}'
|
|
self.skipTest("Skipping to prevent failure notification on Jenkins")
|
|
ndict = dbutils.get_test_ihost(location={'Country': 'Canada',
|
|
'City': 'Ottawa'})
|
|
self.post_json('/ihosts', ndict)
|
|
result = self.get_json('/ihosts/%s' % ndict['uuid'])
|
|
self.assertEqual(ndict['location'], result['location'])
|
|
|
|
def test_create_ihost_invalid_extra(self):
|
|
ndict = dbutils.get_test_ihost(location={'foo': 0.123})
|
|
self.assertRaises(webtest.app.AppError, self.post_json, '/ihosts',
|
|
ndict)
|
|
|
|
|
|
class TestDelete(base.FunctionalTest):
|
|
|
|
def test_delete_iHost(self):
|
|
# Test skipped because updating ihost's datamodel in utils.py has
|
|
# caused this test to throw an error saying:
|
|
# webtest.app.AppError: Bad response: 400 Bad Request (not 200 OK or
|
|
# 3xx redirect for http://localhost/v1/ihosts)
|
|
# '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\":
|
|
# \\"Client\\", \\"faultstring\\": \\"Unknown attribute for argument
|
|
# host: recordtype\\"}"}'
|
|
self.skipTest("Skipping to prevent failure notification on Jenkins")
|
|
ndict = dbutils.get_test_ihost()
|
|
self.post_json('/ihosts', ndict)
|
|
self.delete('/ihosts/%s' % ndict['uuid'])
|
|
response = self.get_json('/ihosts/%s' % ndict['uuid'],
|
|
expect_errors=True)
|
|
self.assertEqual(response.status_int, 404)
|
|
self.assertEqual(response.content_type, 'application/json')
|
|
self.assertTrue(response.json['error_message'])
|
|
|
|
def test_delete_ports_subresource(self):
|
|
# Test skipped because updating ihost's datamodel in utils.py has
|
|
# caused this test to throw an error saying:
|
|
# webtest.app.AppError: Bad response: 400 Bad Request (not 200 OK or
|
|
# 3xx redirect for http://localhost/v1/ihosts)
|
|
# '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\":
|
|
# \\"Client\\", \\"faultstring\\": \\"Unknown attribute for argument
|
|
# host: recordtype\\"}"}'
|
|
self.skipTest("Skipping to prevent failure notification on Jenkins")
|
|
# get 404 resource not found instead of 403
|
|
ndict = dbutils.get_test_ihost()
|
|
self.post_json('/ihosts', ndict)
|
|
response = self.delete(
|
|
'/ihosts/%s/ports' % ndict['uuid'],
|
|
expect_errors=True)
|
|
self.assertEqual(response.status_int, 403)
|
|
|
|
|
|
class TestListServers(base.FunctionalTest):
|
|
|
|
def setUp(self):
|
|
super(TestListServers, self).setUp()
|
|
self.system = dbutils.create_test_isystem()
|
|
self.load = dbutils.create_test_load()
|
|
|
|
def test_empty_ihost(self):
|
|
data = self.get_json('/ihosts')
|
|
self.assertEqual([], data['ihosts'])
|
|
|
|
def test_one(self):
|
|
# Test skipped because a MismatchError is thrown which lists all of
|
|
# ihost's attributes prefixed with u' and then ends with "matches
|
|
# Contains('serialid')"
|
|
self.skipTest("Skipping to prevent failure notification on Jenkins")
|
|
ndict = dbutils.get_test_ihost(forisystemid=self.system.id)
|
|
ihost = self.dbapi.ihost_create(ndict)
|
|
data = self.get_json('/ihosts')
|
|
self.assertEqual(ihost['uuid'], data['ihosts'][0]["uuid"])
|
|
self.assertIn('hostname', data['ihosts'][0])
|
|
self.assertIn('administrative', data['ihosts'][0])
|
|
self.assertIn('operational', data['ihosts'][0])
|
|
self.assertIn('availability', data['ihosts'][0])
|
|
|
|
self.assertNotIn('serialid', data['ihosts'][0])
|
|
self.assertNotIn('location', data['ihosts'][0])
|
|
|
|
def test_detail(self):
|
|
ndict = dbutils.get_test_ihost(forisystemid=self.system.id)
|
|
ihost = self.dbapi.ihost_create(ndict)
|
|
data = self.get_json('/ihosts/detail')
|
|
self.assertEqual(ihost['uuid'], data['ihosts'][0]["uuid"])
|
|
self.assertIn('hostname', data['ihosts'][0])
|
|
self.assertIn('administrative', data['ihosts'][0])
|
|
self.assertIn('operational', data['ihosts'][0])
|
|
self.assertIn('availability', data['ihosts'][0])
|
|
self.assertIn('serialid', data['ihosts'][0])
|
|
self.assertIn('location', data['ihosts'][0])
|
|
|
|
def test_detail_against_single(self):
|
|
ndict = dbutils.get_test_ihost(forisystemid=self.system.id)
|
|
node = self.dbapi.ihost_create(ndict)
|
|
response = self.get_json('/ihosts/%s/detail' % node['uuid'],
|
|
expect_errors=True)
|
|
self.assertEqual(response.status_int, 404)
|
|
|
|
def test_many(self):
|
|
ihosts = []
|
|
for id in xrange(1000): # there is a limit of 1000 returned by json
|
|
ndict = dbutils.get_test_ihost(id=id, hostname=id, mgmt_mac=id,
|
|
forisystemid=self.system.id,
|
|
mgmt_ip="%s.%s.%s.%s" % (id, id, id, id),
|
|
uuid=uuidutils.generate_uuid())
|
|
s = self.dbapi.ihost_create(ndict)
|
|
ihosts.append(s['uuid'])
|
|
data = self.get_json('/ihosts')
|
|
self.assertEqual(len(ihosts), len(data['ihosts']))
|
|
|
|
uuids = [n['uuid'] for n in data['ihosts']]
|
|
self.assertEqual(ihosts.sort(), uuids.sort()) # uuids.sort
|
|
|
|
def test_ihost_links(self):
|
|
uuid = uuidutils.generate_uuid()
|
|
ndict = dbutils.get_test_ihost(id=1, uuid=uuid,
|
|
forisystemid=self.system.id)
|
|
self.dbapi.ihost_create(ndict)
|
|
data = self.get_json('/ihosts/1')
|
|
self.assertIn('links', data.keys())
|
|
self.assertEqual(len(data['links']), 2)
|
|
self.assertIn(uuid, data['links'][0]['href'])
|
|
|
|
def test_collection_links(self):
|
|
ihosts = []
|
|
for id in xrange(100):
|
|
ndict = dbutils.get_test_ihost(id=id, hostname=id, mgmt_mac=id,
|
|
forisystemid=self.system.id,
|
|
mgmt_ip="%s.%s.%s.%s" % (id, id, id, id),
|
|
uuid=uuidutils.generate_uuid())
|
|
ihost = self.dbapi.ihost_create(ndict)
|
|
ihosts.append(ihost['uuid'])
|
|
data = self.get_json('/ihosts/?limit=100')
|
|
self.assertEqual(len(data['ihosts']), 100)
|
|
|
|
next_marker = data['ihosts'][-1]['uuid']
|
|
self.assertIn(next_marker, data['next'])
|
|
|
|
def test_ports_subresource_link(self):
|
|
ndict = dbutils.get_test_ihost(forisystemid=self.system.id)
|
|
self.dbapi.ihost_create(ndict)
|
|
|
|
data = self.get_json('/ihosts/%s' % ndict['uuid'])
|
|
self.assertIn('ports', data.keys())
|
|
|
|
def test_ports_subresource(self):
|
|
ndict = dbutils.get_test_ihost(forisystemid=self.system.id)
|
|
self.dbapi.ihost_create(ndict)
|
|
|
|
for id in xrange(2):
|
|
pdict = dbutils.get_test_port(id=id,
|
|
host_id=ndict['id'],
|
|
pciaddr=id,
|
|
uuid=uuidutils.generate_uuid())
|
|
ihost_id = ndict['id']
|
|
self.dbapi.ethernet_port_create(ihost_id, pdict)
|
|
|
|
data = self.get_json('/ihosts/%s/ports' % ndict['uuid'])
|
|
self.assertEqual(len(data['ports']), 2)
|
|
self.assertNotIn('next', data.keys())
|
|
|
|
# Test collection pagination
|
|
data = self.get_json(
|
|
'/ihosts/%s/ports?limit=1' % ndict['uuid'])
|
|
self.assertEqual(len(data['ports']), 1)
|
|
self.assertIn('next', data.keys())
|
|
|
|
# def test_nodes_subresource_noid(self):
|
|
# ndict = dbutils.get_test_node()
|
|
# self.dbapi.create_node(ndict)
|
|
# pdict = dbutils.get_test_port(node_id=ndict['id'])
|
|
# self.dbapi.create_port(pdict)
|
|
# No node id specified
|
|
# response = self.get_json('/nodes/ports', expect_errors=True)
|
|
# self.assertEqual(response.status_int, 400)
|
|
|
|
# def test_provision_state(self):
|
|
# ndict = dbutils.get_test_node()
|
|
# self.dbapi.create_node(ndict)
|
|
# data = self.get_json('/nodes/%s/state/provision' % ndict['uuId'])
|
|
# [self.assertIn(key, data) for key in
|
|
# ['available', 'current', 'target', 'links']]
|
|
# TODO(lucasagomes): Add more tests to check to which states it can
|
|
# transition to from the current one, and check if they are present
|
|
# in the available list.
|
|
|
|
# def test_state(self):
|
|
# ndict = dbutils.get_test_node()
|
|
# self.dbapi.create_node(ndict)
|
|
# data = self.get_json('/nodes/%s/state' % ndict['uuid'])
|
|
# [self.assertIn(key, data) for key in ['power', 'provision']]
|
|
|
|
# Check if it only returns a sub-set of the attributes
|
|
# [self.assertIn(key, ['current', 'links'])
|
|
# for key in data['power'].keys()]
|
|
# [self.assertIn(key, ['current', 'links'])
|
|
# for key in data['provision'].keys()]
|
|
|
|
# def test_power_state(self):
|
|
# ndict = dbutils.get_test_node()
|
|
# self.dbapi.create_node(ndict)
|
|
# data = self.get_json('/nodes/%s/state/power' % ndict['uuid'])
|
|
# [self.assertIn(key, data) for key in
|
|
# ['available', 'current', 'target', 'links']]
|
|
# TODO(lucasagomes): Add more tests to check to which states it can
|
|
# transition to from the current one, and check if they are present
|
|
# in the available list.
|
|
|
|
|
|
'''
|
|
class TestPatch(base.FunctionalTest):
|
|
|
|
def setUp(self):
|
|
super(TestPatch, self).setUp()
|
|
ndict = dbutils.get_test_node()
|
|
self.node = self.dbapi.create_node(ndict)
|
|
self.mox.StubOutWithMock(rpcapi.ConductorAPI, 'update_node')
|
|
self.mox.StubOutWithMock(rpcapi.ConductorAPI,
|
|
'start_power_state_change')
|
|
|
|
def test_update_ok(self):
|
|
rpcapi.ConductorAPI.update_node(mox.IgnoreArg(), mox.IgnoreArg()).\
|
|
AndReturn(self.node)
|
|
self.mox.ReplayAll()
|
|
|
|
response = self.patch_json('/nodes/%s' % self.node['uuid'],
|
|
[{'path': '/instance_uuid',
|
|
'value': 'fake instance uuid',
|
|
'op': 'replace'}])
|
|
self.assertEqual(response.content_type, 'application/json')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.mox.VerifyAll()
|
|
|
|
def test_update_state(self):
|
|
self.assertRaises(webtest.app.AppError, self.patch_json,
|
|
'/nodes/%s' % self.node['uuid'],
|
|
{'power_state': 'new state'})
|
|
|
|
def test_update_fails_bad_driver_info(self):
|
|
fake_err = 'Fake Error Message'
|
|
rpcapi.ConductorAPI.update_node(mox.IgnoreArg(), mox.IgnoreArg()).\
|
|
AndRaise(exception.InvalidParameterValue(fake_err))
|
|
self.mox.ReplayAll()
|
|
|
|
response = self.patch_json('/nodes/%s' % self.node['uuid'],
|
|
[{'path': '/driver_info/this',
|
|
'value': 'foo',
|
|
'op': 'add'},
|
|
{'path': '/driver_info/that',
|
|
'value': 'bar',
|
|
'op': 'add'}],
|
|
expect_errors=True)
|
|
self.assertEqual(response.content_type, 'application/json')
|
|
self.assertEqual(response.status_code, 400)
|
|
self.mox.VerifyAll()
|
|
|
|
def test_update_fails_bad_state(self):
|
|
fake_err = 'Fake Power State'
|
|
rpcapi.ConductorAPI.update_node(mox.IgnoreArg(), mox.IgnoreArg()).\
|
|
AndRaise(exception.NodeInWrongPowerState(
|
|
node=self.node['uuid'], pstate=fake_err))
|
|
self.mox.ReplayAll()
|
|
|
|
response = self.patch_json('/nodes/%s' % self.node['uuid'],
|
|
[{'path': '/instance_uuid',
|
|
'value': 'fake instance uuid',
|
|
'op': 'replace'}],
|
|
expect_errors=True)
|
|
self.assertEqual(response.content_type, 'application/json')
|
|
# TODO(deva): change to 409 when wsme 0.5b3 released
|
|
self.assertEqual(response.status_code, 400)
|
|
self.mox.VerifyAll()
|
|
|
|
def test_add_ok(self):
|
|
rpcapi.ConductorAPI.update_node(mox.IgnoreArg(), mox.IgnoreArg()).\
|
|
AndReturn(self.node)
|
|
self.mox.ReplayAll()
|
|
|
|
response = self.patch_json('/nodes/%s' % self.node['uuid'],
|
|
[{'path': '/extra/foo',
|
|
'value': 'bar',
|
|
'op': 'add'}])
|
|
self.assertEqual(response.content_type, 'application/json')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.mox.VerifyAll()
|
|
|
|
def test_add_fail(self):
|
|
self.assertRaises(webtest.app.AppError, self.patch_json,
|
|
'/nodes/%s' % self.node['uuid'],
|
|
[{'path': '/foo', 'value': 'bar', 'op': 'add'}])
|
|
|
|
def test_remove_ok(self):
|
|
rpcapi.ConductorAPI.update_node(mox.IgnoreArg(), mox.IgnoreArg()).\
|
|
AndReturn(self.node)
|
|
self.mox.ReplayAll()
|
|
|
|
response = self.patch_json('/nodes/%s' % self.node['uuid'],
|
|
[{'path': '/extra',
|
|
'op': 'remove'}])
|
|
self.assertEqual(response.content_type, 'application/json')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.mox.VerifyAll()
|
|
|
|
def test_remove_fail(self):
|
|
self.assertRaises(webtest.app.AppError, self.patch_json,
|
|
'/nodes/%s' % self.node['uuid'],
|
|
[{'path': '/extra/non-existent', 'op': 'remove'}])
|
|
|
|
def test_update_state_in_progress(self):
|
|
ndict = dbutils.get_test_node(id=99, uuid=uuidutils.generate_uuid(),
|
|
target_power_state=states.POWER_OFF)
|
|
node = self.dbapi.create_node(ndict)
|
|
self.assertRaises(webtest.app.AppError, self.patch_json,
|
|
'/nodes/%s' % node['uuid'],
|
|
[{'path': '/extra/foo', 'value': 'bar',
|
|
'op': 'add'}])
|
|
|
|
def test_patch_ports_subresource(self):
|
|
response = self.patch_json('/nodes/%s/ports' % self.node['uuid'],
|
|
[{'path': '/extra/foo', 'value': 'bar',
|
|
'op': 'add'}], expect_errors=True)
|
|
self.assertEqual(response.status_int, 403)
|
|
|
|
|
|
class TestPut(base.FunctionalTest):
|
|
|
|
def setUp(self):
|
|
super(TestPut, self).setUp()
|
|
ndict = dbutils.get_test_node()
|
|
self.node = self.dbapi.create_node(ndict)
|
|
self.mox.StubOutWithMock(rpcapi.ConductorAPI, 'update_node')
|
|
self.mox.StubOutWithMock(rpcapi.ConductorAPI,
|
|
'start_power_state_change')
|
|
|
|
def test_power_state(self):
|
|
rpcapi.ConductorAPI.update_node(mox.IgnoreArg(), mox.IgnoreArg()).\
|
|
AndReturn(self.node)
|
|
rpcapi.ConductorAPI.start_power_state_change(mox.IgnoreArg(),
|
|
mox.IgnoreArg(),
|
|
mox.IgnoreArg())
|
|
self.mox.ReplayAll()
|
|
|
|
response = self.put_json('/nodes/%s/state/power' % self.node['uuid'],
|
|
{'target': states.POWER_ON})
|
|
self.assertEqual(response.content_type, 'application/json')
|
|
# FIXME(lucasagomes): WSME should return 202 not 200
|
|
self.assertEqual(response.status_code, 200)
|
|
self.mox.VerifyAll()
|
|
|
|
def test_power_state_in_progress(self):
|
|
rpcapi.ConductorAPI.update_node(mox.IgnoreArg(), mox.IgnoreArg()).\
|
|
AndReturn(self.node)
|
|
rpcapi.ConductorAPI.start_power_state_change(mox.IgnoreArg(),
|
|
mox.IgnoreArg(),
|
|
mox.IgnoreArg())
|
|
self.mox.ReplayAll()
|
|
self.put_json('/nodes/%s/state/power' % self.node['uuid'],
|
|
{'target': states.POWER_ON})
|
|
self.assertRaises(webtest.app.AppError, self.put_json,
|
|
'/nodes/%s/state/power' % self.node['uuid'],
|
|
{'target': states.POWER_ON})
|
|
self.mox.VerifyAll()
|
|
'''
|