Create basic set of unit tests for sysinv ihosts API

Creating a basic set of unit tests for the sysinv ihosts API.
This increases the coverage of the sys/api/controllers/v1/host.py
file from 19% to 47%. It also allows new testcases for the ihosts
API to be easily added.

One minor code change was required to the actual API itself as it
was not functional under python 3.

Change-Id: Ic40f3bc122fa8659fa6813c604e3433aa44ffa58
Story: 2005939
Task: 34292
Signed-off-by: Bart Wensley <barton.wensley@windriver.com>
This commit is contained in:
Bart Wensley 2019-07-19 14:26:12 -05:00
parent afbf4b7920
commit 6e5e93ed81
6 changed files with 1508 additions and 602 deletions

View File

@ -1725,7 +1725,7 @@ class HostController(rest.RestController):
ihost_dict = host.as_dict()
# bm_password is not a part of ihost, so retrieve it from the body
body = json.loads(pecan.request.body)
body = json.loads(pecan.request.body.decode('utf-8'))
if 'bm_password' in body:
ihost_dict['bm_password'] = body['bm_password']
else:

View File

@ -1,62 +1,9 @@
This file discusses the current status of sysinv tests and areas where issues
still exist and what to do in order to test them.
At present, in it's current state, a py27 tox test will result in 18 tests being
skipped. If testing in a VM e.g. Ubuntu, it can be reduced to 16 skipped tests,
where one of those tests only exists for legacy reasons: MYSQL used to be used,
however now we only use SQLite and PostgreSQL, so _test_mysql_opportunistically
in db/sqlalchemy/test_migrations.py results in a skipped test on account that it
is no longer supported but is being kept in the codebase in the event that MYSQL
is ever used again.
One of those skips is also not actually a test, but is test-requirements.txt
which gets skipped because the filename is prefaced with 'test' so tox assumes
it's a test file, but because it doesn't contain any tests there are no tests to
pass or fail.
Two of the skips are in sysinv/tests/test_sysinv_deploy_helper.py where they're
hard-coded to skip because the tests are incompatible with the current Sysinv
db.
Thus the number of tests being skipped that need to be investigated/fixed is 12.
--------------------------------------------------------------------------------
RUNNING TESTS:
To fully test Sysinv in a local Ubuntu VM or similar, go to test_migrations.py
and in the function test_postgresql_opportunistically, comment out the
self.skipTest line to enable the test to be run.
Also go to the function test_postgresql_connect_fail and comment out the
self.skipTest line so that test can be run as well.
Lastly, in the function _reset_databases, go to the bottom and uncomment
self._reset_pg(conn_pieces) so the postgres DB can be reset between runs.
If this last line is not uncommented, your first run of the py27 tests will
work, but after that you will get
migrate.exceptions.DatabaseAlreadyControlledError
Do not push these lines uncommented upstream to the repo as Jenkins does not
have postgres set up and will throw errors which will send e-mails out to the
team.
If you've never run sysinv tests on your system before see
http://wiki.wrs.com/PBUeng/ToxUnitTesting#Sysinv
The above link contains information on setting up the postgres database used by
tests under TestMigrations.
The following has been pasted from the above link just to keep this file
self-contained:
Prior to running tests you will need certain packages installed:
sudo apt-get install sqlite3 libsqlite3-dev libvirt-dev libsasl2-dev libldap2-dev
To set up the postgres db for the first time enter the following in console:
sudo apt-get install postgresql postgresql-contrib
pip install psycopg2
sudo -u postgres psql
CREATE USER openstack_citest WITH CREATEDB LOGIN PASSWORD 'openstack_citest';
CREATE DATABASE openstack_citest WITH OWNER openstack_citest;
\q
To actually run the tests, in console navigate to
$MY_REPO/stx/stx-config/sysinv/sysinv/sysinv
@ -75,6 +22,35 @@ To run either individually enter:
tox -e py27
tox -e flake8
--------------------------------------------------------------------------------
RUNNING TESTS WITH POSTGRESQL:
The default behaviour is to run the sysinv tests with the mySQL database. This
should be fine in most cases.
If you really want to test with postgreSQL, in a local Ubuntu VM or similar:
- go to test_migrations.py and in the function
test_postgresql_opportunistically, comment out the self.skipTest line to
enable the test to be run.
- Also go to the function test_postgresql_connect_fail and comment out the
self.skipTest line so that test can be run as well.
- Lastly, in the function _reset_databases, go to the bottom and uncomment
self._reset_pg(conn_pieces) so the postgres DB can be reset between runs.
If this last line is not uncommented, your first run of the py27 tests will
work, but after that you will get
migrate.exceptions.DatabaseAlreadyControlledError
Do not push these lines uncommented upstream to the repo.
To set up the postgres db for the first time enter the following in console:
sudo apt-get install postgresql postgresql-contrib
pip install psycopg2
sudo -u postgres psql
CREATE USER openstack_citest WITH CREATEDB LOGIN PASSWORD 'openstack_citest';
CREATE DATABASE openstack_citest WITH OWNER openstack_citest;
\q
--------------------------------------------------------------------------------
OUTSTANDING ISSUES:
@ -93,34 +69,6 @@ tests/api/test_acl.py
Fails due to invalid user token resulting in
raise mismatch_error testtools.matchers._impl.MismatchError: 401 != 403
tests/api/test_invservers.py
test_create_ihost
Issues may be related to keyring.
Fails with
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\\"}"}'
test_create_ihost_valid_extra
Fails for the same reason as the above test.
test_post_ports_subresource
Fails for the same reason as the above test.
test_delete_iHost
Fails for the same reason as the above test.
test_delete_ports_subresource
Fails for the same reason as the above test.
test_one
Fails due to mismatch error: matches Contains('serialid')
Looks like /v1/ihosts populates from tests/db/utils.py so serialid
is included. In this test there's an
assertNotIn('serialid', data['ihosts'][0]), not sure if this is what
we're intending to check for or not.
tests/conductor/test_manager.py
test_configure_ihost_new
IOError: [Errno 13] Permission denied: '/tmp/dnsmasq.hosts'
@ -137,10 +85,6 @@ tests/conductor/test_manager.py
IOError: [Errno 13] Permission denied: '/tmp/dnsmasq.hosts'
This dnsmasq file doesn't exist. Same issue as in the first test.
As far as tests go, the tests in sysinv/tests/api above have the highest
priority of the remaining tests to be fixed.
There also exists the issue of using postgres for db migrations in
tests/db/sqlalchemy/test_migrations.py. The issue with this is that these
migrations can only be run on local VMs such as Ubuntu, and not on the build
@ -164,105 +108,4 @@ combines code formatting with syntax and import checking, additionally, flake8
provides the option to test code complexity and return warnings if the
complexity exceeds whatever limit you've set.
The following flake8 Errors and Failures are ignored in tox.ini in sysinv/sysinv
because they were found to be insignificant and too tedious to correct, or were
found to be non-issues.
The list and explanations follow:
F403: 'from <module> import *' used; unable to detect undefined names
Replacing the above with 'import <module>' requires one to go to all
instances where the module was used, and to prefix the use of that module's
function or variable with the name of the module.
F401: '<module> imported but unused'
Some instances where the issue is reported have the indicated module used
by other files making calls to the file where this is reported. Attempts to
reduce the number of occurences of this issue were made, but errors popped
up eratically due to missing imports and 69 instances were too many to test
one-by-one.
F821: 'undefined name <var>'
There were 124 instances, almost all of which complained about '_' not being
defined, but '_' is something that is actually used and is from
sysinv.openstack.common.gettextutils import _
These are usually defined in the file containing the function call
containing the "undefined name" as a parameter.
It may however be worth looking through this list occasionally to make sure
no orphaned variables are making their way into the code.
F841: 'local variable <var> is assigned to but never used'
Some instances had the variable used by external file calls and there were
69 instances to manually sort through.
E501: 'line too long (<length> > 79 characters)'
There are 580 instances, and besides this being a non-issue, attempting to
fix this may make the code horribly unreadable, or result in indentation
errors being caused which can themselves be impossible to fix (the reason
will be discussed below).
E127: 'continuation line over-indented for visual indent'
There are 231 instances, and this issue can be impossible to fix: attempting
to fix indentation can result in you either getting an over-indented or
under-indented error no matter what you do.
E128: 'continuation line under-indented for visual indent'
There are 455 instances, see above for reason why they remain. These
visual indent issues also do not affect the code and are therefore
non-issues.
E231: 'missing whitepace after ',''
Does not affect code operation, and fixing this issue reduces code
readability and will cause 'line too long' error.
E266: 'too many leading '#' for block comment'
Double # are usually used to indicate TODO. Reducing this to a single #
will make these messages look like comments and may confuse or mislead
readers.
E402: 'module level import not at top of file'
Every instance of this module is intentionally imported after patching.
E711: 'comparison to None should be 'if cond is not None:''
'if != None' and 'if not None' are not precisely equivalent in python.
This error has been ignored under the assumption that the designer was
aware of this and wrote it this way intentionally.
E116: 'unexpected indentation (comment)'
Changing the indentation to be at the outermost level reduces readability
and thus this error is ignored.
E203: 'whitespace before ':''
The current spacing was used to allign dictionary values for readability.
Changing spacing to clear this error will reduce readability.
E731: 'do not assign a lambda expression, use a def'
PEP8 doesn't like lambdas in assignmments because it isn't as useful
for tracebacks and duplicates the functionality of using def. However,
this isn't an actual issue and has been used to one-line very simple
functionality.
E712: 'comparison to True should be 'if cond is True:' or 'if cond:''
'if <var> == True' and 'if <var>' or 'if <var> is True' are not precisely
equivalent in python. This error has been ignored under the assumption that
the designer was aware of this and wrote it this way intentionally.
E713: 'test for membership should be 'not in''
'not <x> in' and '<x> not in' are translated by the compiler to be the same
thing. Should probably be changed to make it more pythonic.
E702: 'multiple statements on one line (semicolon)'
Short statements were put on one line to save space and for readability.
E714: 'test for object identity should be 'is not'
Translates in the compiler to be the same thing. Should be changed.
E126: 'continuation line over-indented for hanging indent'
Doesn't affect functionality, and following this rule can reduce
readability. Also is not enforced by PEP8 or unanimously accepted.
E121: 'continuation line under-indented for hanging indent'
Doesn't affect functionality, and following this rule can reduce
readability. Also is not enforced by PEP8 or unanimously accepted.
--------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -1,411 +0,0 @@
# 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 range(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 range(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 range(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()
'''

View File

@ -101,6 +101,16 @@ def create_test_node(**kw):
return dbapi.inode_create(node)
def post_get_test_ihost(**kw):
inv = get_test_ihost(**kw)
del inv['bm_mac']
del inv['peer_id']
del inv['action_state']
del inv['recordtype']
del inv['uuid']
return inv
def get_test_ihost(**kw):
inv = {
'id': kw.get('id', 123),
@ -129,7 +139,6 @@ def get_test_ihost(**kw):
'subfunctions': kw.get('subfunctions', "ihostsubfunctions"),
'subfunction_oper': kw.get('subfunction_oper', "disabled"),
'subfunction_avail': kw.get('subfunction_avail', "not-installed"),
# 'reservation': None,
'reserved': kw.get('reserved', None),
'ihost_action': kw.get('ihost_action', None),
'action_state': kw.get('action_state', constants.HAS_REINSTALLED),
@ -145,7 +154,6 @@ def get_test_ihost(**kw):
'install_output': kw.get('install_output', 'text'),
'console': kw.get('console', 'ttyS0,115200'),
'tboot': kw.get('tboot', ''),
'vsc_controllers': kw.get('vsc_controllers', "vsccontrollers"),
'ttys_dcd': kw.get('ttys_dcd', None),
'updated_at': None,
'created_at': None,

View File

@ -15,10 +15,10 @@ from sysinv.tests.db import base
from sysinv.tests.db import utils
class TestihostObject(base.DbTestCase):
class TestHostObject(base.DbTestCase):
def setUp(self):
super(TestihostObject, self).setUp()
super(TestHostObject, self).setUp()
self.fake_node = utils.get_test_ihost()
self.obj_node = objects.host.from_db_object(
self._get_db_node(self.fake_node))