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:
parent
f44bd98312
commit
a5d2738bf2
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue