2020-03-29 15:27:36 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
2020-04-08 18:17:39 +02:00
|
|
|
"""
|
|
|
|
Consumer locator module, that manages the list of consumers.
|
|
|
|
"""
|
|
|
|
|
2020-03-29 16:38:40 +02:00
|
|
|
import datetime
|
2020-03-30 17:10:48 +02:00
|
|
|
from communicator import Communicator
|
2020-03-29 18:27:19 +02:00
|
|
|
import os
|
2020-04-17 16:47:27 +02:00
|
|
|
from redisconnector import RedisConnector
|
2020-04-22 03:59:34 +02:00
|
|
|
import logging
|
2020-03-29 15:27:36 +02:00
|
|
|
|
2020-03-29 17:21:36 +02:00
|
|
|
__author__ = "@dscharnitzky"
|
2020-03-29 15:27:36 +02:00
|
|
|
__copyright__ = "Copyright 2020, GoldenPogácsa Team"
|
|
|
|
__module_name__ = "consumerlocator"
|
|
|
|
__version__text__ = "1"
|
|
|
|
|
2020-04-17 16:47:27 +02:00
|
|
|
KNOWNCONSUMER = os.getenv("PRODUCER_KNOWNCONSUMER", '10.69.42.1')
|
2020-04-22 03:59:34 +02:00
|
|
|
LOGGER = logging.getLogger(__name__)
|
2020-03-29 15:54:52 +02:00
|
|
|
|
2020-04-17 16:18:54 +02:00
|
|
|
|
2020-03-29 15:26:01 +02:00
|
|
|
class ConsumerLocator:
|
2020-03-29 16:46:28 +02:00
|
|
|
"""
|
2020-04-08 18:17:39 +02:00
|
|
|
Component responsible for managing the list of consumers. Requires an instance of :class:`communicator.Communicator`
|
2020-03-29 16:46:28 +02:00
|
|
|
"""
|
|
|
|
|
2020-05-14 20:09:41 +02:00
|
|
|
def __init__(self, communicator: Communicator, redisconnector: RedisConnector):
|
2020-04-08 19:45:05 +02:00
|
|
|
"""**Constructor:**
|
|
|
|
Initializes the object.
|
2020-04-08 18:17:39 +02:00
|
|
|
|
2020-04-06 20:48:36 +02:00
|
|
|
Gets the known consumer's IP address from the PRODUCER_KNOWNCONSUMER envar.
|
2020-04-08 18:17:39 +02:00
|
|
|
|
2020-04-08 19:45:05 +02:00
|
|
|
:param communicator: the :class:'communicator.Communicator' instance that will be used for the low level communication.
|
2020-03-29 16:46:28 +02:00
|
|
|
"""
|
2020-04-17 16:47:27 +02:00
|
|
|
self.red = redisconnector
|
2020-04-21 19:25:27 +02:00
|
|
|
self.red.consumerlist = [{"Host": KNOWNCONSUMER, "State": True, "LastOk": datetime.datetime.now().timestamp()}]
|
2020-04-17 16:47:27 +02:00
|
|
|
self.red.currentconsumer = self.red.consumerlist[0]
|
2020-03-30 19:33:09 +02:00
|
|
|
self.communicator = communicator
|
2020-03-29 17:39:31 +02:00
|
|
|
|
2020-03-30 17:10:48 +02:00
|
|
|
def learnconsumerlist(self) -> None:
|
2020-04-06 20:48:36 +02:00
|
|
|
""""Learns the list of consumers from the current consumer.
|
2020-04-08 18:17:39 +02:00
|
|
|
|
|
|
|
Calls :func:`communicator.Communicator.didiscoveravailableconsumers`, adds the learned consumers to the list
|
|
|
|
if are not present, and then calls :func:`consumerlocator.ConsumerLocator.updateconsumerlist`
|
|
|
|
|
|
|
|
:returns: None
|
2020-03-29 15:54:52 +02:00
|
|
|
"""
|
2020-03-29 18:00:00 +02:00
|
|
|
recievedconsumerlist = self.communicator.discoveravailableconsumers()
|
2020-04-22 03:43:20 +02:00
|
|
|
if not recievedconsumerlist:
|
2020-03-29 18:00:00 +02:00
|
|
|
return
|
2020-04-22 03:43:20 +02:00
|
|
|
|
2020-04-22 03:55:33 +02:00
|
|
|
consumer_list = self.red.consumerlist
|
|
|
|
|
2020-03-29 19:18:25 +02:00
|
|
|
for recconsumer in recievedconsumerlist:
|
|
|
|
contains = False
|
2020-05-08 20:32:32 +02:00
|
|
|
for consumer in consumer_list:
|
2020-03-29 19:18:25 +02:00
|
|
|
if consumer["Host"] == recconsumer:
|
|
|
|
contains = True
|
|
|
|
|
|
|
|
if not contains:
|
2020-04-22 03:59:34 +02:00
|
|
|
LOGGER.info(f"Learned about new consumer at {recconsumer}")
|
2020-05-14 20:09:41 +02:00
|
|
|
consumer_list.append(
|
|
|
|
{"Host": recconsumer, "State": True, "LastOk": datetime.datetime.now().timestamp()})
|
2020-03-29 19:18:25 +02:00
|
|
|
|
2020-04-22 03:55:33 +02:00
|
|
|
self.red.consumerlist = consumer_list
|
2020-03-29 18:27:19 +02:00
|
|
|
self.updateconsumerlist()
|
2020-03-29 16:38:40 +02:00
|
|
|
|
2020-03-30 17:10:48 +02:00
|
|
|
def updateconsumerlist(self) -> None:
|
2020-04-06 20:48:36 +02:00
|
|
|
""" Updates the consumer list based on their availability.
|
2020-04-08 18:17:39 +02:00
|
|
|
|
2020-04-06 20:48:36 +02:00
|
|
|
Marks for each consumer if they are available or not. If a consumer is not available for some time (1 hour),
|
|
|
|
the it will be deleted from the list.
|
2020-04-08 18:17:39 +02:00
|
|
|
|
2020-04-06 20:48:36 +02:00
|
|
|
:return: None
|
2020-03-29 16:38:40 +02:00
|
|
|
"""
|
2020-03-29 18:27:19 +02:00
|
|
|
removelist = []
|
2020-04-22 03:55:33 +02:00
|
|
|
consumer_list = self.red.consumerlist
|
|
|
|
|
|
|
|
for consumer in consumer_list:
|
2020-03-29 17:19:05 +02:00
|
|
|
if not self.communicator.checkconsumer(consumer["Host"]):
|
2020-03-29 16:38:40 +02:00
|
|
|
consumer["State"] = False
|
2020-05-14 20:09:41 +02:00
|
|
|
if datetime.datetime.now() - datetime.datetime.fromtimestamp(consumer["LastOk"]) > datetime.timedelta(
|
|
|
|
hours=1):
|
2020-03-29 18:27:19 +02:00
|
|
|
removelist.append(consumer)
|
2020-03-29 16:38:40 +02:00
|
|
|
else:
|
2020-04-21 19:25:27 +02:00
|
|
|
consumer["LastOk"] = datetime.datetime.now().timestamp()
|
2020-03-29 19:18:25 +02:00
|
|
|
consumer["State"] = True
|
2020-03-29 18:27:19 +02:00
|
|
|
for rem in removelist:
|
2020-04-22 03:55:33 +02:00
|
|
|
consumer_list.remove(rem)
|
|
|
|
|
|
|
|
self.red.consumerlist = consumer_list
|
2020-03-29 15:54:52 +02:00
|
|
|
|
2020-03-29 16:50:23 +02:00
|
|
|
def updateconsumer(self):
|
2020-04-06 20:48:36 +02:00
|
|
|
"""If the current consumer is not available, checks all the consumers in the list and updates the current one.
|
2020-04-08 18:17:39 +02:00
|
|
|
|
|
|
|
Calls :func:`consumerlocator.ConsumerLocator.checkcurrentconsumer` and if needed
|
|
|
|
:func:`consumerlocator.ConsumerLocator.updateconsumerlist`. Sets the :class:`communicator.Communicator`
|
|
|
|
current instance with :func:`communicator.Communicator.set_currentconsumer`.
|
|
|
|
|
2020-03-29 16:46:28 +02:00
|
|
|
:return: the current consumer or None if there are no available customers at the moment.
|
2020-03-29 15:54:52 +02:00
|
|
|
"""
|
2020-03-29 16:38:40 +02:00
|
|
|
|
2020-03-29 18:27:19 +02:00
|
|
|
if not self.checkcurrentconsumer():
|
2020-03-29 19:18:25 +02:00
|
|
|
self.updateconsumerlist()
|
2020-03-29 18:27:19 +02:00
|
|
|
newcurrentconsumer = None
|
2020-03-29 16:38:40 +02:00
|
|
|
|
2020-04-17 16:47:27 +02:00
|
|
|
for consumer in self.red.consumerlist:
|
2020-03-29 16:38:40 +02:00
|
|
|
if consumer["State"]:
|
2020-03-29 18:27:19 +02:00
|
|
|
newcurrentconsumer = consumer
|
2020-03-29 16:38:40 +02:00
|
|
|
break
|
|
|
|
|
2020-04-17 16:47:27 +02:00
|
|
|
self.red.currentconsumer = newcurrentconsumer
|
|
|
|
if self.red.currentconsumer is not None:
|
2020-04-22 03:59:34 +02:00
|
|
|
LOGGER.warning(f"Falling back to consumer at {newcurrentconsumer['Host']}")
|
2020-03-29 19:18:25 +02:00
|
|
|
self.learnconsumerlist()
|
2020-03-29 16:38:40 +02:00
|
|
|
|
2020-04-17 16:47:27 +02:00
|
|
|
if self.red.currentconsumer is not None:
|
|
|
|
self.communicator.set_currentconsumer(self.red.currentconsumer["Host"])
|
|
|
|
return self.red.currentconsumer["Host"]
|
2020-03-29 16:38:40 +02:00
|
|
|
else:
|
|
|
|
return None
|
2020-03-29 15:54:52 +02:00
|
|
|
|
2020-03-30 17:10:48 +02:00
|
|
|
def getcurrentconsumer(self) -> str:
|
2020-04-08 18:17:39 +02:00
|
|
|
"""Returns the currently selected consumer's IP address.
|
|
|
|
|
2020-03-29 15:54:52 +02:00
|
|
|
:return: the current consumer
|
|
|
|
"""
|
2020-04-17 16:47:27 +02:00
|
|
|
return self.red.currentconsumer["Host"]
|
2020-03-29 15:54:52 +02:00
|
|
|
|
2020-03-30 17:10:48 +02:00
|
|
|
def checkcurrentconsumer(self) -> bool:
|
2020-04-08 18:17:39 +02:00
|
|
|
"""Check the current consumer's health.
|
|
|
|
|
2020-03-29 15:54:52 +02:00
|
|
|
:return: True if OK, False if fail
|
|
|
|
"""
|
2020-04-17 16:47:27 +02:00
|
|
|
if self.red.currentconsumer is None:
|
2020-03-29 19:18:25 +02:00
|
|
|
return False
|
2020-04-17 16:47:27 +02:00
|
|
|
return self.communicator.checkconsumer(self.red.currentconsumer["Host"])
|