summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorTomi Juvonen <tomi.juvonen@nokia.com>2016-05-19 03:50:24 +0000
committerGerrit Code Review <gerrit@172.30.200.206>2016-05-19 03:50:24 +0000
commit725aae8ef5e0a3b7bc4f84d78c7cb9f84dcec57d (patch)
tree6f249068b8dd34c98f8e37d721daf8fca8c86fb4 /docs
parent8e583dc3a3fc0392a5197803a598ab5734310d66 (diff)
parentd133d967e8499c098cfb94e09c7669ce537919f1 (diff)
Merge "Manual for get-valid-server-state"
Diffstat (limited to 'docs')
-rw-r--r--docs/manuals/get-valid-server-state.rst125
-rw-r--r--docs/manuals/index.rst1
-rw-r--r--docs/userguide/featureusage.rst20
3 files changed, 140 insertions, 6 deletions
diff --git a/docs/manuals/get-valid-server-state.rst b/docs/manuals/get-valid-server-state.rst
new file mode 100644
index 00000000..5d74e9ed
--- /dev/null
+++ b/docs/manuals/get-valid-server-state.rst
@@ -0,0 +1,125 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+
+======================
+Get valid server state
+======================
+
+Related Blueprints:
+===================
+
+https://blueprints.launchpad.net/nova/+spec/get-valid-server-state
+
+Problem description
+===================
+
+Previously when the owner of a VM has queried his VMs, he has not received
+enough state information, states have not changed fast enough in the VIM and
+they have not been accurate in some scenarios. With this change this gap is now
+closed.
+
+A typical case is that, in case of a fault of a host, the user of a high
+availability service running on top of that host, needs to make an immediate
+switch over from the faulty host to an active standby host. Now, if the compute
+host is forced down [1] as a result of that fault, the user has to be notified
+about this state change such that the user can react accordingly. Similarly,
+a change of the host state to "maintenance" should also be notified to the
+users.
+
+What is changed
+===============
+
+A new ``host_status`` parameter is added to the ``/servers/{server_id}`` and
+``/servers/detail`` endpoints in microversion 2.16. By this new parameter
+user can get additional state information about the host.
+
+``host_status`` possible values where next value in list can override the
+previous:
+
+- ``UP`` if nova-compute is up.
+- ``UNKNOWN`` if nova-compute status was not reported by servicegroup driver
+ within configured time period. Default is within 60 seconds,
+ but can be changed with ``service_down_time`` in nova.conf.
+- ``DOWN`` if nova-compute was forced down.
+- ``MAINTENANCE`` if nova-compute was disabled. MAINTENANCE in API directly
+ means nova-compute service is disabled. Different wording is used to avoid
+ the impression that the whole host is down, as only scheduling of new VMs
+ is disabled.
+- Empty string indicates there is no host for server.
+
+``host_status`` is returned in the response in case the policy permits. By
+default the policy is for admin only in Nova policy.json::
+
+ "os_compute_api:servers:show:host_status": "rule:admin_api"
+
+For an NFV use case this has to also be enabled for the owner of the VM::
+
+ "os_compute_api:servers:show:host_status": "rule:admin_or_owner"
+
+REST API examples:
+==================
+
+Case where nova-compute is enabled and reporting normally::
+
+ GET /v2.1/{tenant_id}/servers/{server_id}
+
+ 200 OK
+ {
+ "server": {
+ "host_status": "UP",
+ ...
+ }
+ }
+
+Case where nova-compute is enabled, but not reporting normally::
+
+ GET /v2.1/{tenant_id}/servers/{server_id}
+
+ 200 OK
+ {
+ "server": {
+ "host_status": "UNKNOWN",
+ ...
+ }
+ }
+
+Case where nova-compute is enabled, but forced_down::
+
+ GET /v2.1/{tenant_id}/servers/{server_id}
+
+ 200 OK
+ {
+ "server": {
+ "host_status": "DOWN",
+ ...
+ }
+ }
+
+Case where nova-compute is disabled::
+
+ GET /v2.1/{tenant_id}/servers/{server_id}
+
+ 200 OK
+ {
+ "server": {
+ "host_status": "MAINTENANCE",
+ ...
+ }
+ }
+
+Host Status is also visible in python-novaclient::
+
+ +-------+------+--------+------------+-------------+----------+-------------+
+ | ID | Name | Status | Task State | Power State | Networks | Host Status |
+ +-------+------+--------+------------+-------------+----------+-------------+
+ | 9a... | vm1 | ACTIVE | - | RUNNING | xnet=... | UP |
+ +-------+------+--------+------------+-------------+----------+-------------+
+
+Links:
+======
+
+[1] Manual for OpenStack NOVA API for marking host down
+http://artifacts.opnfv.org/doctor/brahmaputra/docs/manuals/mark-host-down_manual.html
+
+[2] OpenStack compute manual page
+http://developer.openstack.org/api-ref-compute-v2.1.html#compute-v2.1
diff --git a/docs/manuals/index.rst b/docs/manuals/index.rst
index 52d36096..05831b2b 100644
--- a/docs/manuals/index.rst
+++ b/docs/manuals/index.rst
@@ -10,3 +10,4 @@ Manuals
:maxdepth: 2
.. include:: mark-host-down_manual.rst
+.. include:: get-valid-server-state.rst
diff --git a/docs/userguide/featureusage.rst b/docs/userguide/featureusage.rst
index 59090a77..4df2d41a 100644
--- a/docs/userguide/featureusage.rst
+++ b/docs/userguide/featureusage.rst
@@ -16,8 +16,8 @@ OpenStack Alarming (Aodh) API with relevant internal components support.
See, upstream spec document:
http://specs.openstack.org/openstack/ceilometer-specs/specs/liberty/event-alarm-evaluator.html
-You can find an example of consumer of this notification in doctor repository.
-It can be executed as follows:
+An example of a consumer of this notification can be found in the Doctor
+repository. It can be executed as follows:
.. code-block:: bash
@@ -26,12 +26,20 @@ It can be executed as follows:
CONSUMER_PORT=12346
python consumer.py "$CONSUMER_PORT" > consumer.log 2>&1 &
-Consistent resource state awareness (Compute/host-down)
--------------------------------------------------------
+Consistent resource state awareness
+-----------------------------------
-Resource state of compute host can be fixed according to an input from a monitor
-sitting out side of OpenStack Compute (Nova) by using force-down API.
+Resource state of compute host can be changed/updated according to a trigger
+from a monitor running outside of OpenStack Compute (Nova) by using
+force-down API.
See
http://artifacts.opnfv.org/doctor/brahmaputra/docs/manuals/mark-host-down_manual.html
for more detail.
+
+The resource state of a compute host can be retrieved by a user with the
+OpenStack Compute (Nova) servers API.
+
+See
+http://artifacts.opnfv.org/doctor/colorado/docs/manuals/get-valid-server-state.html
+for more detail.
ompanying materials # are made available under the terms of the Apache License, Version 2.0 # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 """Define the classes required to fully cover robot.""" import errno import logging import os import unittest import mock from robot.errors import DataError, RobotError from robot.result import model from robot.utils.robottime import timestamp_to_secs from xtesting.core import robotframework __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>" class ResultVisitorTesting(unittest.TestCase): """The class testing ResultVisitor.""" # pylint: disable=missing-docstring def setUp(self): self.visitor = robotframework.ResultVisitor() def test_empty(self): self.assertFalse(self.visitor.get_data()) def test_ok(self): data = {'name': 'foo', 'parent': 'bar', 'status': 'PASS', 'starttime': "20161216 16:00:00.000", 'endtime': "20161216 16:00:01.000", 'elapsedtime': 1000, 'text': 'Hello, World!', 'critical': True} test = model.TestCase( name=data['name'], status=data['status'], message=data['text'], starttime=data['starttime'], endtime=data['endtime']) test.parent = mock.Mock() config = {'name': data['parent'], 'criticality.test_is_critical.return_value': data[ 'critical']} test.parent.configure_mock(**config) self.visitor.visit_test(test) self.assertEqual(self.visitor.get_data(), [data]) class ParseResultTesting(unittest.TestCase): """The class testing RobotFramework.parse_results().""" # pylint: disable=missing-docstring _config = {'name': 'dummy', 'starttime': '20161216 16:00:00.000', 'endtime': '20161216 16:00:01.000'} def setUp(self): self.test = robotframework.RobotFramework( case_name='robot', project_name='xtesting') @mock.patch('robot.api.ExecutionResult', side_effect=DataError) def test_raises_exc(self, mock_method): with self.assertRaises(DataError): self.test.parse_results() mock_method.assert_called_once_with( os.path.join(self.test.res_dir, 'output.xml')) def _test_result(self, config, result): suite = mock.Mock() suite.configure_mock(**config) with mock.patch('robot.api.ExecutionResult', return_value=mock.Mock(suite=suite)): self.test.parse_results() self.assertEqual(self.test.result, result) self.assertEqual(self.test.start_time, timestamp_to_secs(config['starttime'])) self.assertEqual(self.test.stop_time, timestamp_to_secs(config['endtime'])) self.assertEqual(self.test.details, {'description': config['name'], 'tests': []}) def test_null_passed(self): self._config.update({'statistics.critical.passed': 0, 'statistics.critical.total': 20}) self._test_result(self._config, 0) def test_no_test(self): self._config.update({'statistics.critical.passed': 20, 'statistics.critical.total': 0}) self._test_result(self._config, 0) def test_half_success(self): self._config.update({'statistics.critical.passed': 10, 'statistics.critical.total': 20}) self._test_result(self._config, 50) def test_success(self): self._config.update({'statistics.critical.passed': 20, 'statistics.critical.total': 20}) self._test_result(self._config, 100) class RunTesting(unittest.TestCase): """The class testing RobotFramework.run().""" # pylint: disable=missing-docstring suites = ["foo"] variable = [] variablefile = [] include = [] def setUp(self): self.test = robotframework.RobotFramework( case_name='robot', project_name='xtesting') def test_exc_key_error(self): self.assertEqual(self.test.run(), self.test.EX_RUN_ERROR) @mock.patch('robot.run') def _test_makedirs_exc(self, *args): with mock.patch.object(self.test, 'parse_results') as mock_method: self.assertEqual( self.test.run( suites=self.suites, variable=self.variable, variablefile=self.variablefile, include=self.include), self.test.EX_RUN_ERROR) args[0].assert_not_called() mock_method.asser_not_called() @mock.patch('os.makedirs', side_effect=Exception) def test_makedirs_exc(self, *args): self._test_makedirs_exc() args[0].assert_called_once_with(self.test.res_dir) @mock.patch('os.makedirs', side_effect=OSError) def test_makedirs_oserror(self, *args): self._test_makedirs_exc() args[0].assert_called_once_with(self.test.res_dir) @mock.patch('robot.run') def _test_makedirs(self, *args): with mock.patch.object(self.test, 'parse_results') as mock_method: self.assertEqual( self.test.run(suites=self.suites, variable=self.variable), self.test.EX_OK) args[0].assert_called_once_with( *self.suites, log='NONE', output=self.test.xml_file, report='NONE', stdout=mock.ANY, variable=self.variable, variablefile=self.variablefile, include=self.include) mock_method.assert_called_once_with() @mock.patch('os.makedirs', side_effect=OSError(errno.EEXIST, '')) def test_makedirs_oserror17(self, *args): self._test_makedirs() args[0].assert_called_once_with(self.test.res_dir) @mock.patch('os.makedirs') def test_makedirs(self, *args): self._test_makedirs() args[0].assert_called_once_with(self.test.res_dir) @mock.patch('os.makedirs') @mock.patch('robot.run') def _test_parse_results(self, status, *args): self.assertEqual( self.test.run( suites=self.suites, variable=self.variable, variablefile=self.variablefile, include=self.include), status) args[0].assert_called_once_with( *self.suites, log='NONE', output=self.test.xml_file, report='NONE', stdout=mock.ANY, variable=self.variable, variablefile=self.variablefile, include=self.include) args[1].assert_called_once_with(self.test.res_dir) def test_parse_results_exc(self): with mock.patch.object(self.test, 'parse_results', side_effect=Exception) as mock_method: self._test_parse_results(self.test.EX_RUN_ERROR) mock_method.assert_called_once_with() def test_parse_results_robot_error(self): with mock.patch.object(self.test, 'parse_results', side_effect=RobotError('foo')) as mock_method: self._test_parse_results(self.test.EX_RUN_ERROR) mock_method.assert_called_once_with() if __name__ == "__main__": logging.disable(logging.CRITICAL) unittest.main(verbosity=2)