From d80febb3842c3bad11cd6a01bae2fb593f0f7812 Mon Sep 17 00:00:00 2001 From: Feilong Wang Date: Tue, 15 May 2018 23:34:09 +1200 Subject: [PATCH] Add health_status and health_status_reason to cluster APIImpact Related blueprint cluster-healing Change-Id: I78d4d14fb064996de77bdd6a381d2df53a9488b8 --- magnum/api/controllers/v1/cluster.py | 8 ++++ ...bc65a86986_add_health_status_to_cluster.py | 38 +++++++++++++++++++ magnum/db/sqlalchemy/models.py | 2 + magnum/objects/cluster.py | 5 ++- magnum/objects/fields.py | 17 +++++++++ magnum/tests/unit/objects/test_cluster.py | 2 + magnum/tests/unit/objects/test_fields.py | 19 ++++++++++ magnum/tests/unit/objects/test_objects.py | 2 +- 8 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 magnum/db/sqlalchemy/alembic/versions/cbbc65a86986_add_health_status_to_cluster.py diff --git a/magnum/api/controllers/v1/cluster.py b/magnum/api/controllers/v1/cluster.py index 1cb245cf9a..23c924aa90 100755 --- a/magnum/api/controllers/v1/cluster.py +++ b/magnum/api/controllers/v1/cluster.py @@ -134,6 +134,12 @@ class Cluster(base.APIBase): status_reason = wtypes.text """Status reason of the cluster from the heat stack""" + health_status = wtypes.Enum(str, *fields.ClusterStatus.ALL) + """Health status of the cluster from the native COE API""" + + health_status_reason = wtypes.DictType(str, str) + """Health status reason of the cluster from the native COE API""" + discovery_url = wtypes.text """Url used for cluster node discovery""" @@ -211,6 +217,8 @@ class Cluster(base.APIBase): stack_id='49dc23f5-ffc9-40c3-9d34-7be7f9e34d63', status=fields.ClusterStatus.CREATE_COMPLETE, status_reason="CREATE completed successfully", + health_status=fields.ClusterHealthStatus.HEALTHY, + health_status_reason='{"api_server": "OK"}', api_address='172.24.4.3', node_addresses=['172.24.4.4', '172.24.4.5'], created_at=timeutils.utcnow(), diff --git a/magnum/db/sqlalchemy/alembic/versions/cbbc65a86986_add_health_status_to_cluster.py b/magnum/db/sqlalchemy/alembic/versions/cbbc65a86986_add_health_status_to_cluster.py new file mode 100644 index 0000000000..e350aac446 --- /dev/null +++ b/magnum/db/sqlalchemy/alembic/versions/cbbc65a86986_add_health_status_to_cluster.py @@ -0,0 +1,38 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Add health_status and health_status_reason to cluster + +Revision ID: cbbc65a86986 +Revises: 9a1539f1cd2c +Create Date: 2018-05-15 22:24:49.527558 + +""" + +# revision identifiers, used by Alembic. +revision = 'cbbc65a86986' +down_revision = '9a1539f1cd2c' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('cluster', sa.Column('health_status', + sa.String(20), + nullable=True)) + op.add_column('cluster', sa.Column('health_status_reason', + sa.Text, + nullable=True)) + # ### end Alembic commands ### diff --git a/magnum/db/sqlalchemy/models.py b/magnum/db/sqlalchemy/models.py index 5e56a7a45e..378f4b42d1 100644 --- a/magnum/db/sqlalchemy/models.py +++ b/magnum/db/sqlalchemy/models.py @@ -128,6 +128,8 @@ class Cluster(Base): master_count = Column(Integer()) status = Column(String(20)) status_reason = Column(Text) + health_status = Column(String(20)) + health_status_reason = Column(JSONEncodedDict) create_timeout = Column(Integer()) discovery_url = Column(String(255, mysql_ndb_type=TINYTEXT)) master_addresses = Column(JSONEncodedList) diff --git a/magnum/objects/cluster.py b/magnum/objects/cluster.py index 01f704d34f..5110d8adc0 100644 --- a/magnum/objects/cluster.py +++ b/magnum/objects/cluster.py @@ -46,8 +46,9 @@ class Cluster(base.MagnumPersistentObject, base.MagnumObject, # Version 1.15: Added 'labels' field # Version 1.16: Added 'master_flavor_id' field # Version 1.17: Added 'flavor_id' field + # Version 1.18: Added 'health_status' and 'health_status_reason' field - VERSION = '1.17' + VERSION = '1.18' dbapi = dbapi.get_instance() @@ -66,6 +67,8 @@ class Cluster(base.MagnumPersistentObject, base.MagnumObject, 'stack_id': fields.StringField(nullable=True), 'status': m_fields.ClusterStatusField(nullable=True), 'status_reason': fields.StringField(nullable=True), + 'health_status': m_fields.ClusterHealthStatusField(nullable=True), + 'health_status_reason': fields.DictOfStringsField(nullable=True), 'create_timeout': fields.IntegerField(nullable=True), 'api_address': fields.StringField(nullable=True), 'node_addresses': fields.ListOfStringsField(nullable=True), diff --git a/magnum/objects/fields.py b/magnum/objects/fields.py index 07b3a48632..6bb88b994d 100644 --- a/magnum/objects/fields.py +++ b/magnum/objects/fields.py @@ -49,6 +49,19 @@ class ClusterStatus(fields.Enum): super(ClusterStatus, self).__init__(valid_values=ClusterStatus.ALL) +class ClusterHealthStatus(fields.Enum): + HEALTHY = 'HEALTHY' + UNHEALTHY = 'UNHEALTHY' + + ALL = (HEALTHY, UNHEALTHY) + + STATUS_FAILED = (UNHEALTHY) + + def __init__(self): + super(ClusterHealthStatus, self).__init__( + valid_values=ClusterHealthStatus.ALL) + + class FederationStatus(fields.Enum): CREATE_IN_PROGRESS = 'CREATE_IN_PROGRESS' CREATE_FAILED = 'CREATE_FAILED' @@ -150,6 +163,10 @@ class ClusterStatusField(fields.BaseEnumField): AUTO_TYPE = ClusterStatus() +class ClusterHealthStatusField(fields.BaseEnumField): + AUTO_TYPE = ClusterHealthStatus() + + class MagnumServiceField(fields.BaseEnumField): AUTO_TYPE = MagnumServiceState() diff --git a/magnum/tests/unit/objects/test_cluster.py b/magnum/tests/unit/objects/test_cluster.py index 698c13892c..c8deed7960 100644 --- a/magnum/tests/unit/objects/test_cluster.py +++ b/magnum/tests/unit/objects/test_cluster.py @@ -40,6 +40,8 @@ class TestClusterObject(base.DbTestCase): self.fake_cluster['keypair'] = 'keypair1' self.fake_cluster['docker_volume_size'] = 3 self.fake_cluster['labels'] = {} + self.fake_cluster['health_status'] = 'HEALTHY' + self.fake_cluster['health_status_reason'] = {} @mock.patch('magnum.objects.ClusterTemplate.get_by_uuid') def test_get_by_id(self, mock_cluster_template_get): diff --git a/magnum/tests/unit/objects/test_fields.py b/magnum/tests/unit/objects/test_fields.py index 89b7f1ec13..836c95906a 100644 --- a/magnum/tests/unit/objects/test_fields.py +++ b/magnum/tests/unit/objects/test_fields.py @@ -50,6 +50,25 @@ class TestClusterStatus(test_fields.TestField): self.assertRaises(ValueError, self.field.stringify, 'DELETE_STOPPED') +class TestClusterHealthStatus(test_fields.TestField): + def setUp(self): + super(TestClusterHealthStatus, self).setUp() + self.field = fields.ClusterHealthStatusField() + self.coerce_good_values = [('HEALTHY', 'HEALTHY'), + ('UNHEALTHY', 'UNHEALTHY')] + self.coerce_bad_values = ['FAKE'] + + self.to_primitive_values = self.coerce_good_values[0:1] + self.from_primitive_values = self.coerce_good_values[0:1] + + def test_stringify(self): + self.assertEqual("'UNHEALTHY'", + self.field.stringify('UNHEALTHY')) + + def test_stringify_invalid(self): + self.assertRaises(ValueError, self.field.stringify, 'FAKE') + + class TestContainerStatus(test_fields.TestField): def setUp(self): super(TestContainerStatus, self).setUp() diff --git a/magnum/tests/unit/objects/test_objects.py b/magnum/tests/unit/objects/test_objects.py index 03444a6674..e6e091430e 100644 --- a/magnum/tests/unit/objects/test_objects.py +++ b/magnum/tests/unit/objects/test_objects.py @@ -355,7 +355,7 @@ class TestObject(test_base.TestCase, _TestObject): # For more information on object version testing, read # https://docs.openstack.org/magnum/latest/contributor/objects.html object_data = { - 'Cluster': '1.17-c32c07425ab0042c7370bef2902b4d21', + 'Cluster': '1.18-9f0dfcc3e898eef2b9a09647b612adb6', 'ClusterTemplate': '1.18-7fa94f4fdd027acfb4f022f202afdfb5', 'Certificate': '1.1-1924dc077daa844f0f9076332ef96815', 'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',