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:
parent
afbf4b7920
commit
6e5e93ed81
|
@ -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:
|
||||
|
|
|
@ -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
|
@ -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()
|
||||
'''
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
Loading…
Reference in New Issue