Skip to content

Commit

Permalink
Merge pull request #490 from beer-garden/Status_Info_Model
Browse files Browse the repository at this point in the history
Status info model
  • Loading branch information
TheBurchLog authored Jul 25, 2024
2 parents 7b5e0f7 + 65ebb6a commit a27423c
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Brewtils Changelog
------
TBD

- Formalized Status Info model and added helper features to track the history of the status changes.
- Added support models for tracking primary replication
- New Models for User, UserToken, Role, and AliasUserMap
- Must upgrade to a minimum version of Beer Garden 3.27.0 to support new authentication models. If authentication is not enabled, upgrade
Expand Down
55 changes: 52 additions & 3 deletions brewtils/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-

import copy
from datetime import datetime
from enum import Enum

import pytz # noqa # not in requirements file
Expand Down Expand Up @@ -256,7 +258,7 @@ def __init__(
self.description = description
self.id = id
self.status = status.upper() if status else None
self.status_info = status_info or {}
self.status_info = status_info if status_info else StatusInfo()
self.queue_type = queue_type
self.queue_info = queue_info or {}
self.icon_name = icon_name
Expand Down Expand Up @@ -433,6 +435,53 @@ def is_different(self, other):
return False


class StatusHistory(BaseModel):
schema = "StatusHistorySchema"

def __init__(self, status=None, heartbeat=None):
self.status = status
self.heartbeat = heartbeat

def __str__(self):
return "%s:%s" % (
self.status,
self.heartbeat,
)

def __repr__(self):
return "<StatusHistory: status=%s, heartbeat=%s>" % (
self.status,
self.heartbeat,
)


class StatusInfo(BaseModel):
schema = "StatusInfoSchema"

def __init__(self, heartbeat=None, history=None):
self.heartbeat = heartbeat
self.history = history or []

def set_status_heartbeat(self, status, max_history=None):

self.heartbeat = datetime.utcnow()
self.history.append(
StatusHistory(status=copy.deepcopy(status), heartbeat=self.heartbeat)
)

if max_history and max_history > 0 and len(self.history) > max_history:
self.history = self.history[(max_history * -1) :]

def __str__(self):
return self.heartbeat

def __repr__(self):
return "<StatusInfo: heartbeat=%s, history=%s>" % (
self.heartbeat,
self.history,
)


class RequestFile(BaseModel):
schema = "RequestFileSchema"

Expand Down Expand Up @@ -1438,7 +1487,7 @@ def __init__(
self.id = id
self.name = name
self.status = status.upper() if status else None
self.status_info = status_info or {}
self.status_info = status_info if status_info else StatusInfo()
self.namespaces = namespaces or []
self.systems = systems or []

Expand Down Expand Up @@ -1498,7 +1547,7 @@ def __init__(
):
self.api = api
self.status = status
self.status_info = status_info or {}
self.status_info = status_info if status_info else StatusInfo()
self.config = config or {}

def __str__(self):
Expand Down
76 changes: 76 additions & 0 deletions brewtils/schema_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class SchemaParser(object):
"AliasUserMapSchema": brewtils.models.AliasUserMap,
"SubscriberSchema": brewtils.models.Subscriber,
"TopicSchema": brewtils.models.Topic,
"StatusInfoSchema": brewtils.models.StatusInfo,
"StatusHistorySchema": brewtils.models.StatusHistory,
"ReplicationSchema": brewtils.models.Replication,
}

Expand Down Expand Up @@ -469,6 +471,40 @@ def parse_topic(cls, topic, from_string=False, **kwargs):
)

@classmethod
def parse_status_info(cls, status_info, from_string=False, **kwargs):
"""Convert raw JSON string or dictionary to a status info model object
Args:
status_info: The raw input
from_string: True if input is a JSON string, False if a dictionary
**kwargs: Additional parameters to be passed to the Schema (e.g. many=True)
Returns:
A StatusInfo object
"""
return cls.parse(
status_info, brewtils.models.StatusInfo, from_string=from_string, **kwargs
)

@classmethod
def parse_status_history(cls, status_history, from_string=False, **kwargs):
"""Convert raw JSON string or dictionary to a status history model object
Args:
status_history: The raw input
from_string: True if input is a JSON string, False if a dictionary
**kwargs: Additional parameters to be passed to the Schema (e.g. many=True)
Returns:
A StatusHistory object
"""
return cls.parse(
status_history,
brewtils.models.StatusHistory,
from_string=from_string,
**kwargs
)
@classmethod
def parse_replication(cls, replication, from_string=False, **kwargs):
"""Convert raw JSON string or dictionary to a replication model object
Expand Down Expand Up @@ -1024,6 +1060,46 @@ def serialize_topic(cls, topic, to_string=True, **kwargs):
**kwargs
)

@classmethod
def serialize_status_info(cls, status_info, to_string=True, **kwargs):
"""Convert a status info model into serialized form
Args:
status_info: The status info object(s) to be serialized
to_string: True to generate a JSON-formatted string, False to generate a
dictionary
**kwargs: Additional parameters to be passed to the Schema (e.g. many=True)
Returns:
Serialized representation of status_info
"""
return cls.serialize(
status_info,
to_string=to_string,
schema_name=brewtils.models.StatusInfo.schema,
**kwargs
)

@classmethod
def serialize_status_history(cls, status_history, to_string=True, **kwargs):
"""Convert a status history model into serialized form
Args:
status_history: The status history object(s) to be serialized
to_string: True to generate a JSON-formatted string, False to generate a
dictionary
**kwargs: Additional parameters to be passed to the Schema (e.g. many=True)
Returns:
Serialized representation of status_history
"""
return cls.serialize(
status_history,
to_string=to_string,
schema_name=brewtils.models.StatusHistory.schema,
**kwargs
)

@classmethod
def serialize_replication(cls, replication, to_string=True, **kwargs):
"""Convert a replication model into serialized form
Expand Down
10 changes: 10 additions & 0 deletions brewtils/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
"AliasUserMapSchema",
"SubscriberSchema",
"TopicSchema",
"StatusInfoSchema",
"StatusHistorySchema",
"ReplicationSchema",
]

Expand Down Expand Up @@ -330,8 +332,14 @@ class RequestSchema(RequestTemplateSchema):
target_garden = fields.String(allow_none=True)


class StatusHistorySchema(BaseSchema):
heartbeat = DateTime(allow_none=True, format="epoch", example="1500065932000")
status = fields.Str(allow_none=True)


class StatusInfoSchema(BaseSchema):
heartbeat = DateTime(allow_none=True, format="epoch", example="1500065932000")
history = fields.Nested("StatusHistorySchema", many=True, allow_none=True)


class PatchSchema(BaseSchema):
Expand Down Expand Up @@ -656,6 +664,8 @@ class UserSchema(BaseSchema):
"AliasUserMap": AliasUserMapSchema,
"Subscriber": SubscriberSchema,
"Topic": TopicSchema,
"StatusInfo": StatusInfoSchema,
"StatusHistory": StatusHistorySchema,
"Replication": ReplicationSchema,
# Compatibility for the Job trigger types
"interval": IntervalTriggerSchema,
Expand Down
44 changes: 42 additions & 2 deletions brewtils/test/comparable.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
Resolvable,
Role,
Runner,
StatusHistory,
StatusInfo,
Subscriber,
System,
Topic,
Expand Down Expand Up @@ -68,6 +70,8 @@
"assert_runner_equal",
"assert_subscriber_equal",
"assert_topic_equal",
"assert_status_info_equal",
"assert_status_history_equal",
"assert_replication_equal",
]

Expand Down Expand Up @@ -191,7 +195,6 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs):


# These are the 'simple' models - they don't have any nested models as fields
assert_instance_equal = partial(_assert_wrapper, expected_type=Instance)
assert_choices_equal = partial(_assert_wrapper, expected_type=Choices)
assert_patch_equal = partial(_assert_wrapper, expected_type=PatchOperation)
assert_logging_config_equal = partial(_assert_wrapper, expected_type=LoggingConfig)
Expand All @@ -203,12 +206,48 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs):
assert_request_file_equal = partial(_assert_wrapper, expected_type=RequestFile)
assert_runner_equal = partial(_assert_wrapper, expected_type=Runner)
assert_resolvable_equal = partial(_assert_wrapper, expected_type=Resolvable)
assert_connection_equal = partial(_assert_wrapper, expected_type=Connection)
assert_subscriber_equal = partial(_assert_wrapper, expected_type=Subscriber)
assert_status_history_equal = partial(_assert_wrapper, expected_type=StatusHistory)


def assert_status_info_equal(obj1, obj2, do_raise=False):
return _assert_wrapper(
obj1,
obj2,
expected_type=StatusInfo,
deep_fields={
"history": partial(assert_status_history_equal, do_raise=True),
},
do_raise=do_raise,
)


def assert_instance_equal(obj1, obj2, do_raise=False):
return _assert_wrapper(
obj1,
obj2,
expected_type=Instance,
deep_fields={"status_info": partial(assert_status_info_equal, do_raise=True)},
do_raise=do_raise,
)


def assert_connection_equal(obj1, obj2, do_raise=False):
return _assert_wrapper(
obj1,
obj2,
expected_type=Connection,
deep_fields={"status_info": partial(assert_status_info_equal, do_raise=True)},
do_raise=do_raise,
)


assert_alias_user_map_equal = partial(_assert_wrapper, expected_type=AliasUserMap)
assert_subscriber_equal = partial(_assert_wrapper, expected_type=Subscriber)
assert_replication_equal = partial(_assert_wrapper, expected_type=Replication)



def assert_command_equal(obj1, obj2, do_raise=False):
return _assert_wrapper(
obj1,
Expand Down Expand Up @@ -424,6 +463,7 @@ def assert_garden_equal(obj1, obj2, do_raise=False):
"systems": partial(assert_system_equal, do_raise=True),
"receiving_connections": partial(assert_connection_equal, do_raise=True),
"publishing_connections": partial(assert_connection_equal, do_raise=True),
"status_info": partial(assert_status_info_equal, do_raise=True),
},
do_raise=do_raise,
)
Expand Down
Loading

0 comments on commit a27423c

Please sign in to comment.