Expose Znode Stats and Capabilities

Expose Znode statistics like creation time, update time,
number of children and capabilities(data). This is useful
when the consumer say Nova Servicegroup wants to make
decision based of the content of the Znode information.

Change-Id: I3da8fc0e2c76d83dbd2133bb8a41cc1ffeb6d807
Closes-Bug: #1493990
This commit is contained in:
Vilobh Meshram 2015-09-09 13:04:55 -07:00
parent f44bd98312
commit a5d2738bf2
4 changed files with 91 additions and 0 deletions

View File

@ -292,6 +292,19 @@ class CoordinationDriver(object):
"""
raise tooz.NotImplemented
@staticmethod
def get_member_info(group_id, member_id):
"""Return the statistics and capabilities of a member asynchronously.
:param group_id: the id of the group of the member
:type group_id: str
:param member_id: the id of the member
:type member_id: str
:returns: capabilities and statistics of a member
:rtype: CoordAsyncResult
"""
raise tooz.NotImplemented
@staticmethod
def update_capabilities(group_id, capabilities):
"""Update capabilities of the caller in the specified group

View File

@ -276,6 +276,39 @@ class BaseZooKeeperDriver(coordination.CoordinationDriver):
timeout_exception=self._timeout_exception,
group_id=group_id, member_id=self._member_id)
@classmethod
def _get_member_info_handler(cls, async_result, timeout,
timeout_exception, group_id,
member_id):
try:
capabilities, znode_stats = async_result.get(block=True,
timeout=timeout)
except timeout_exception as e:
coordination.raise_with_cause(coordination.OperationTimedOut,
utils.exception_message(e),
cause=e)
except exceptions.NoNodeError:
raise coordination.MemberNotJoined(group_id, member_id)
except exceptions.ZookeeperError as e:
coordination.raise_with_cause(coordination.ToozError,
utils.exception_message(e),
cause=e)
else:
member_info = {
'capabilities': cls._loads(capabilities),
'created_at': utils.millis_to_datetime(znode_stats.ctime),
'updated_at': utils.millis_to_datetime(znode_stats.mtime)
}
return member_info
def get_member_info(self, group_id, member_id):
member_path = self._path_member(group_id, member_id)
async_result = self._coord.get_async(member_path)
return ZooAsyncResult(async_result,
self._get_member_info_handler,
timeout_exception=self._timeout_exception,
group_id=group_id, member_id=self._member_id)
@staticmethod
def _get_groups_handler(async_result, timeout, timeout_exception):
try:

View File

@ -226,6 +226,7 @@ class TestAPI(testscenarios.TestWithScenarios,
capa = self._coord.get_member_capabilities(self.group_id,
self.member_id).get()
self.assertEqual(capa, caps)
self.assertEqual(capa['type'], caps['type'])
def test_get_member_capabilities_nonexistent_group(self):
capa = self._coord.get_member_capabilities(self.group_id,
@ -242,6 +243,44 @@ class TestAPI(testscenarios.TestWithScenarios,
self.assertRaises(tooz.coordination.MemberNotJoined,
capa.get)
def test_get_member_info(self):
self._coord.create_group(self.group_id).get()
self._coord.join_group(self.group_id, b"test_capabilities")
member_info = self._coord.get_member_info(self.group_id,
self.member_id).get()
self.assertEqual(member_info['capabilities'], b"test_capabilities")
def test_get_member_info_complex(self):
self._coord.create_group(self.group_id).get()
caps = {
'type': 'warrior',
'abilities': ['fight', 'flight', 'double-hit-damage'],
}
member_info = {'capabilities': 'caps',
'created_at': '0',
'updated_at': '0'}
self._coord.join_group(self.group_id, caps)
member_info = self._coord.get_member_info(self.group_id,
self.member_id).get()
self.assertEqual(member_info['capabilities'], caps)
def test_get_member_info_nonexistent_group(self):
member_info = self._coord.get_member_info(self.group_id,
self.member_id)
# Drivers raise one of those depending on their capability
self.assertRaisesAny([tooz.coordination.MemberNotJoined,
tooz.coordination.GroupNotCreated],
member_info.get)
def test_get_member_info_nonjoined_member(self):
self._coord.create_group(self.group_id).get()
member_id = self._get_random_uuid()
member_info = self._coord.get_member_info(self.group_id,
member_id)
self.assertRaises(tooz.coordination.MemberNotJoined,
member_info.get)
def test_update_capabilities(self):
self._coord.create_group(self.group_id).get()
self._coord.join_group(self.group_id, b"test_capabilities1").get()

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import errno
import os
@ -175,3 +176,8 @@ def loads(blob, excp_cls=coordination.SerializationError):
except (msgpack.UnpackException, ValueError) as e:
coordination.raise_with_cause(excp_cls, exception_message(e),
cause=e)
def millis_to_datetime(milliseconds):
"""Converts number of milliseconds (from epoch) into a datetime object."""
return datetime.datetime.fromtimestamp(float(milliseconds) / 1000)