From 4562c3535ba3df3e78d00f4edc6dd7a072fcb399 Mon Sep 17 00:00:00 2001 From: David Trattnig <david.trattnig@o94.at> Date: Fri, 26 Jun 2020 17:24:46 +0200 Subject: [PATCH] Active source logic. --- .../sample/sample-development.engine-api.ini | 1 + src/models.py | 31 ++++-- src/service.py | 104 +++++++++++------- 3 files changed, 86 insertions(+), 50 deletions(-) diff --git a/config/sample/sample-development.engine-api.ini b/config/sample/sample-development.engine-api.ini index 225e84f..7c706a5 100644 --- a/config/sample/sample-development.engine-api.ini +++ b/config/sample/sample-development.engine-api.ini @@ -46,6 +46,7 @@ sync_host="http://localhost:8010" ; host_id=0 ; main_host_1="http://localhost:8008" ; main_host_2="http://localhost:8009" +; default_source=1 sync_api_store_playlog="/api/v1/playlog/store" \ No newline at end of file diff --git a/src/models.py b/src/models.py index a55fbcc..9034b0c 100644 --- a/src/models.py +++ b/src/models.py @@ -46,8 +46,8 @@ class PlayLog(db.Model): track_type = Column(Integer) timeslot_id = Column(Integer) show_name = Column(String(256)) - log_source = Column(Integer) - is_synced = Column(Boolean) + log_source = Column(Integer) # The play-out source this log is coming from (e.g. engine1, engine2) + is_synced = Column(Boolean) # Only relevant for main nodes, in a multi-node setup @@ -63,8 +63,8 @@ class PlayLog(db.Model): self.track_type = data.track_type self.timeslot_id = data.timeslot_id self.show_name = data.show_name - self.log_source = data.log_source # The play-out source this log is coming from (e.g. engine1, engine2) - self.is_synced = False # Only relevant for main nodes, in a multi-node setup + self.log_source = data.log_source + self.is_synced = False def save(self): @@ -178,7 +178,8 @@ class TrackSchema(ma.SQLAlchemySchema): class ActivityLog(db.Model): """ - Table holding a log of play-out source active and sync states. + Table holding a log of play-out source activity and their sync states. + Only used in "SYNC" deployment mode. """ __tablename__ = 'activity_log' @@ -193,20 +194,30 @@ class ActivityLog(db.Model): def __init__(self, source_number): """ - Initializes a activity entry + Initializes an activity entry """ self.log_time = datetime.datetime.now() self.source_number = source_number self.is_synced = False + def get_active_source(): + """ + Retrieves the currently active source. + """ + db.session.commit() + source = db.session.query(ActivityLog).\ + order_by(ActivityLog.log_time.desc()).first() + return source + + def save(self): db.session.add(self) db.session.commit() -class HealthHistory(db.Model): +class HealthHistoryEntry(db.Model): """ Table holding an history of health information for sources. """ @@ -218,15 +229,19 @@ class HealthHistory(db.Model): # Columns source_number = Column(Integer) is_healthy = Column(Boolean) + is_synced = Column(Boolean) # Only relevant for main nodes, in a multi-node setup health_info = Column(String(2048)) - def __init__(self, source_number): + def __init__(self, source_number, is_healthy, health_info): """ Initializes an health entry. """ self.log_time = datetime.datetime.now() self.source_number = source_number + self.is_healthy = is_healthy + self.is_synced = False + self.health_info = health_info def save(self): diff --git a/src/service.py b/src/service.py index d2073fd..818eb8a 100644 --- a/src/service.py +++ b/src/service.py @@ -35,7 +35,7 @@ class NodeType(Enum): class ApiService(): """ - Service handling API actions. + Service handling for API actions. """ config = None @@ -43,7 +43,7 @@ class ApiService(): node_type = None sync_host = None main_hosts = None - + active_source = None def __init__(self, config, logger): """ @@ -64,7 +64,7 @@ class ApiService(): self.main_hosts = [ config.get("main_host_1"), config.get("main_host_2") ] if not self.main_hosts[0] and not self.main_hosts[1]: self.logger.warn("Not a single main host defined. Be aware what you are doing.") - msg = "No sync possible as no host nodes are configured." + msg = "No sync possible as no host nodes are configured" else: msg = "Syncing data of hosts '%s'" % (self.main_hosts) @@ -73,11 +73,21 @@ class ApiService(): self.node_type = NodeType.MAIN self.sync_host = config.get("sync_host") if not self.sync_host: - msg = "No child node for synchronization defined." + msg = "No child node for synchronization defined" else: msg = "Pushing data to '%s'" % (self.sync_host) - self.logger.info("Running in '%s' mode. %s" % (self.node_type, msg)) + # Set active source + self.active_source = ActivityLog.get_active_source() + if not self.active_source: + if self.node_type == NodeType.MAIN: + source_number = self.config.get("host_id") + else: + source_number = self.config.get("default_source") + self.set_active_source(source_number) + self.logger("Active source: %s" % self.active_source) + + self.logger.info("Running in '%s' mode. %s." % (self.node_type, msg)) @@ -137,75 +147,85 @@ class ApiService(): if not data.log_source: data.log_source = self.host_id - playlog = PlayLog(data) - playlog.save() - self.logger.debug("Stored playlog for '%s'" % playlog.track_start) - - # Main Node: Push to Sync Node, if enabled - if self.node_type = NodeType.MAIN and self.sync_host: - playlog_schema = PlayLogSchema() - json_playlog = playlog_schema.dump(playlog) - - try: - api_url = self.sync_host + self.config.get("sync_api_store_playlog") - r = requests.post(api_url, json=json_playlog) - if r.status_code == 200: - self.logger.info("Successfully pushed playlog for '%s' to '%s'" % (playlog.track_start, self.sync_host)) - playlog.is_synced = True - playlog.save() - else: - self.logger.error("Error while pushing playlog to sync-node: " + r.json()) - except Exception as e: - self.logger.error("Error while posting to sync-node API '%s'!" % (api_url), e) + # Main Node: Alway log entry, independed of the source + # Sync Node: Only log entry when it's coming from an active source + if self.node_type == NodeType.MAIN or \ + (self.node_type == NodeType.SYNC and data.log_source == self.active_source): + + playlog = PlayLog(data) + playlog.save() + self.logger.debug("Stored playlog for '%s'" % playlog.track_start) + + # Main Node: Push to Sync Node, if enabled + if self.node_type == NodeType.MAIN and self.sync_host: + playlog_schema = PlayLogSchema() + json_playlog = playlog_schema.dump(playlog) + + try: + api_url = self.sync_host + self.config.get("sync_api_store_playlog") + r = requests.post(api_url, json=json_playlog) + if r.status_code == 200: + self.logger.info("Successfully pushed playlog for '%s' to '%s'" % (playlog.track_start, self.sync_host)) + playlog.is_synced = True + playlog.save() + else: + self.logger.error("Error while pushing playlog to sync-node: " + r.json()) + except Exception as e: + self.logger.error("Error while posting to sync-node API '%s'!" % (api_url), e) + else: + self.logger.info("Ditching playlog sent from an inactive source") def clock_info(self): """ - Retrieves the currently playing playlist. + Retrieves the dataset required to render the studio clock. """ - return "current playlist" + return "studio clock data" - def activate_engine(self, engine_number): + def set_active_source(self, source_number): """ - Activates one engine and deactivates the other + Sets the active source (engine1, engine2, other) identified by its source number. Args: - engine_number (Integer): Number of the engine + source_number (Integer): Number of the engine """ - return 'do some magic!' + if self.active_source > 0: + self.active_source = source_number + activity_log = ActivityLog(active_source) + activity_log.save() - def get_active_engine(self): + def get_active_source(self): """ - Retrieves the status entry of the currently active engine + Retrieves number of the currently active source (engine1, engine2, other) Returns: - StatusEntry + (Integer) """ - return 'do some magic!' + return self.active_source - def get_engine_health(self, engine_number): + def get_source_health(self, source_number): """ - Retrieves the most recent health info of the requested engine + Retrieves the most recent health info of the requested source Args: - engine_number (Integer): Number of the engine + source_number (Integer): Number of the play-out source Returns: - (HealthInfo) + (HealthHistoryEntry) """ return 'do some magic!' - def log_engine_health(self, engine_number): + def log_source_health(self, source_number): """ - Logs another health entry for the given engine + Logs another health entry for the given source Args: - engine_number (Integer): Number of the engine + source_number (Integer): Number of the play-out source """ return 'do some magic!' -- GitLab