From 0df91a7f799060cd2e9b8a0adac1efacb974bcb3 Mon Sep 17 00:00:00 2001 From: Tao Li Date: Fri, 23 Dec 2016 10:35:01 +0800 Subject: [PATCH] Fix the display of updated_at time when using memcache driver. When using mc driver(set servicegroup_driver=mc), the service reports state by memcache. but we use 'nova service-list' get the status of services registered in nova, the 'update_at' time is not the last updated time not as db driver. so we should get the time in memcache to update the time in response. Closes-Bug: #1647269 Change-Id: I1d4763436ad6de2483c39f5fe97fa6e8d283d8a3 --- nova/api/openstack/compute/services.py | 4 ++- nova/servicegroup/api.py | 4 +++ nova/servicegroup/drivers/base.py | 4 +++ nova/servicegroup/drivers/db.py | 4 +++ nova/servicegroup/drivers/mc.py | 16 +++++++++++ nova/tests/unit/servicegroup/test_api.py | 11 ++++++++ .../unit/servicegroup/test_db_servicegroup.py | 11 ++++++++ .../unit/servicegroup/test_mc_servicegroup.py | 28 +++++++++++++++++++ 8 files changed, 81 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/compute/services.py b/nova/api/openstack/compute/services.py index 9052cccf6a08..7d97ff6c7cf9 100644 --- a/nova/api/openstack/compute/services.py +++ b/nova/api/openstack/compute/services.py @@ -71,13 +71,15 @@ class ServiceController(wsgi.Controller): active = 'enabled' if svc['disabled']: active = 'disabled' + updated_time = self.servicegroup_api.get_updated_time(svc) + service_detail = {'binary': svc['binary'], 'host': svc['host'], 'id': svc['id'], 'zone': svc['availability_zone'], 'status': active, 'state': state, - 'updated_at': svc['updated_at'], + 'updated_at': updated_time, 'disabled_reason': svc['disabled_reason']} for field in additional_fields: diff --git a/nova/servicegroup/api.py b/nova/servicegroup/api.py index a7f51ad7149b..d715161f05a1 100644 --- a/nova/servicegroup/api.py +++ b/nova/servicegroup/api.py @@ -78,3 +78,7 @@ class API(object): return False return self._driver.is_up(member) + + def get_updated_time(self, member): + """Get the updated time from drivers except db""" + return self._driver.updated_time(member) diff --git a/nova/servicegroup/drivers/base.py b/nova/servicegroup/drivers/base.py index 4e707893b90f..abe89248f4e1 100644 --- a/nova/servicegroup/drivers/base.py +++ b/nova/servicegroup/drivers/base.py @@ -27,3 +27,7 @@ class Driver(object): def is_up(self, member): """Check whether the given member is up.""" raise NotImplementedError() + + def updated_time(self, service_ref): + """Get the updated time""" + raise NotImplementedError() diff --git a/nova/servicegroup/drivers/db.py b/nova/servicegroup/drivers/db.py index 63d94f461e14..02cc2ee0092d 100644 --- a/nova/servicegroup/drivers/db.py +++ b/nova/servicegroup/drivers/db.py @@ -79,6 +79,10 @@ class DbDriver(base.Driver): 'lhb': str(last_heartbeat), 'el': str(elapsed)}) return is_up + def updated_time(self, service_ref): + """Get the updated time from db""" + return service_ref['updated_at'] + def _report_state(self, service): """Update the state of this service in the datastore.""" diff --git a/nova/servicegroup/drivers/mc.py b/nova/servicegroup/drivers/mc.py index fafe8b27e601..d5d3d0832a39 100644 --- a/nova/servicegroup/drivers/mc.py +++ b/nova/servicegroup/drivers/mc.py @@ -17,6 +17,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import iso8601 from oslo_log import log as logging from oslo_utils import timeutils @@ -67,6 +68,21 @@ class MemcachedDriver(base.Driver): return is_up + def updated_time(self, service_ref): + """Get the updated time from memcache""" + key = "%(topic)s:%(host)s" % service_ref + updated_time_in_mc = self.mc.get(str(key)) + updated_time_in_db = service_ref['updated_at'] + + if updated_time_in_mc: + # Change mc time to offset-aware time + updated_time_in_mc = \ + updated_time_in_mc.replace(tzinfo=iso8601.iso8601.Utc()) + if updated_time_in_db <= updated_time_in_mc: + return updated_time_in_mc + + return updated_time_in_db + def _report_state(self, service): """Update the state of this service in the datastore.""" try: diff --git a/nova/tests/unit/servicegroup/test_api.py b/nova/tests/unit/servicegroup/test_api.py index ce15daec25d3..b451285e4efc 100644 --- a/nova/tests/unit/servicegroup/test_api.py +++ b/nova/tests/unit/servicegroup/test_api.py @@ -60,3 +60,14 @@ class ServiceGroupApiTestCase(test.NoDBTestCase): result = self.servicegroup_api.service_is_up(member) self.assertIs(result, False) driver.is_up.assert_not_called() + + def test_get_updated_time(self): + member = {"host": "fake-host", + "topic": "compute", + "forced_down": False} + retval = "2016-11-02T22:40:31.000000" + + driver = self.servicegroup_api._driver + driver.updated_time = mock.MagicMock(return_value=retval) + result = self.servicegroup_api.get_updated_time(member) + self.assertEqual(retval, result) diff --git a/nova/tests/unit/servicegroup/test_db_servicegroup.py b/nova/tests/unit/servicegroup/test_db_servicegroup.py index 4c2810194502..b54b2cb4b2d4 100644 --- a/nova/tests/unit/servicegroup/test_db_servicegroup.py +++ b/nova/tests/unit/servicegroup/test_db_servicegroup.py @@ -106,3 +106,14 @@ class DBServiceGroupTestCase(test.NoDBTestCase): def test_report_state_unexpected_error(self): self._test_report_state_error(RuntimeError) + + def test_get_updated_time(self): + retval = "2016-11-02T22:40:31.000000" + service_ref = { + 'host': 'fake-host', + 'topic': 'compute', + 'updated_at': retval + } + + result = self.servicegroup_api.get_updated_time(service_ref) + self.assertEqual(retval, result) diff --git a/nova/tests/unit/servicegroup/test_mc_servicegroup.py b/nova/tests/unit/servicegroup/test_mc_servicegroup.py index fd91c949965b..6d432db49a85 100644 --- a/nova/tests/unit/servicegroup/test_mc_servicegroup.py +++ b/nova/tests/unit/servicegroup/test_mc_servicegroup.py @@ -15,10 +15,12 @@ # License for the specific language governing permissions and limitations # under the License. +import iso8601 import mock from nova import servicegroup from nova import test +from oslo_utils import timeutils class MemcachedServiceGroupTestCase(test.NoDBTestCase): @@ -63,3 +65,29 @@ class MemcachedServiceGroupTestCase(test.NoDBTestCase): fn(service) self.mc_client.set.assert_called_once_with('compute:fake-host', mock.ANY) + + def test_get_updated_time(self): + updated_at_time = timeutils.parse_strtime("2016-04-18T02:56:25.198871") + service_ref = { + 'host': 'fake-host', + 'topic': 'compute', + 'updated_at': updated_at_time.replace(tzinfo=iso8601.iso8601.Utc()) + } + + self.mc_client.get.return_value = None + self.assertEqual(service_ref['updated_at'], + self.servicegroup_api.get_updated_time(service_ref)) + self.mc_client.get.assert_called_once_with('compute:fake-host') + self.mc_client.reset_mock() + retval = timeutils.utcnow() + self.mc_client.get.return_value = retval + self.assertEqual(retval.replace(tzinfo=iso8601.iso8601.Utc()), + self.servicegroup_api.get_updated_time(service_ref)) + self.mc_client.get.assert_called_once_with('compute:fake-host') + self.mc_client.reset_mock() + service_ref['updated_at'] = \ + retval.replace(tzinfo=iso8601.iso8601.Utc()) + self.mc_client.get.return_value = updated_at_time + self.assertEqual(service_ref['updated_at'], + self.servicegroup_api.get_updated_time(service_ref)) + self.mc_client.get.assert_called_once_with('compute:fake-host')