def _configure(self) -> None: # We can't use broad 0.0.0.0 IP address to make it possible to run # multiple hax instances at the same machine (i.e. in failover # situation). # Instead, every hax will use a private IP only. node_address = self._get_my_hostname() # Note that bq-delivered mechanism must use a unique node name rather # than broad '0.0.0.0' that doesn't identify the node from outside. inbox_filter = InboxFilter( OffsetStorage(node_address, key_prefix='bq-delivered', kv=self.consul_util.kv)) conf_obj = ConfObjUtil(self.consul_util) planner = self.planner herald = self.herald consul_util = self.consul_util app = self._create_server() app.add_routes([ web.get('/', hello_reply), web.post('/', process_ha_states(planner, consul_util)), web.post( '/watcher/bq', process_bq_update(inbox_filter, BQProcessor(planner, herald, conf_obj))), web.post('/api/v1/sns/{operation}', process_sns_operation(planner)), web.get('/api/v1/sns/repair-status', get_sns_status(planner, SnsRepairStatus)), web.get('/api/v1/sns/rebalance-status', get_sns_status(planner, SnsRebalanceStatus)), ]) self.app = app
def run_server( queue: Queue, herald: DeliveryHerald, consul_util: ConsulUtil, threads_to_wait: List[StoppableThread] = [], port=8008, ): node_address = consul_util.get_hax_ip_address() # We can't use broad 0.0.0.0 IP address to make it possible to run # multiple hax instances at the same machine (i.e. in failover situation). # Instead, every hax will use a private IP only. web_address = node_address # Note that bq-delivered mechanism must use a unique node name rather than # broad '0.0.0.0' that doesn't identify the node from outside. inbox_filter = InboxFilter( OffsetStorage(node_address, key_prefix='bq-delivered')) conf_obj = ConfObjUtil(consul_util) app = web.Application(middlewares=[encode_exception]) app.add_routes([ web.get('/', hello_reply), web.post('/', process_ha_states(queue, consul_util)), web.post( '/watcher/bq', process_bq_update(inbox_filter, BQProcessor(queue, herald, conf_obj))), web.post('/api/v1/sns/{operation}', process_sns_operation(queue)), web.get('/api/v1/sns/repair-status', get_sns_status(queue, SnsRepairStatus)), web.get('/api/v1/sns/rebalance-status', get_sns_status(queue, SnsRebalanceStatus)), ]) LOG.info(f'Starting HTTP server at {web_address}:{port} ...') try: web.run_app(app, host=web_address, port=port) LOG.debug('Server stopped normally') finally: LOG.debug('Stopping the threads') for thread in threads_to_wait: thread.stop() for thread in threads_to_wait: thread.join() LOG.info('The http server has stopped')
def __init__(self, queue: Queue, delivery_herald: DeliveryHerald): self.queue = queue self.confobjutil = ConfObjUtil() self.herald = delivery_herald
class BQProcessor: """ Broadcast Queue Processor. This is the place where a real processing logic should be located. """ def __init__(self, queue: Queue, delivery_herald: DeliveryHerald): self.queue = queue self.confobjutil = ConfObjUtil() self.herald = delivery_herald def process(self, message: Tuple[int, Any]) -> None: (i, msg) = message LOG.debug('Message #%d received: %s (type: %s)', i, msg, type(msg).__name__) try: self.payload_process(msg) except Exception: LOG.exception( 'Failed to process a message #%d.' ' The message is skipped.', i) LOG.debug('Message #%d processed', i) def payload_process(self, msg: str) -> None: data = None try: data = json.loads(msg) except json.JSONDecodeError: LOG.error('Cannot parse payload, invalid json') return payload = data['payload'] msg_type = data['message_type'] handlers: Dict[str, Callable[[Dict[str, Any]], None]] = { 'M0_HA_MSG_NVEC': self.handle_device_state_set, 'STOB_IOQ_ERROR': self.handle_ioq_stob_error, } if msg_type not in handlers: LOG.warn('Unsupported message type given: %s. Message skipped.', msg_type) return handlers[msg_type](payload) def handle_device_state_set(self, payload: Dict[str, Any]) -> None: # To add check for multiple object entries in a payload. # for objinfo in payload: hastate: Optional[HAState] = self.to_ha_state(payload) if not hastate: LOG.debug('No ha states to broadcast.') return q: Queue = Queue(1) LOG.debug('HA broadcast, node: %s device: %s state: %s', payload['node'], payload['device'], payload['state']) self.queue.put(BroadcastHAStates(states=[hastate], reply_to=q)) ids: List[MessageId] = q.get() self.herald.wait_for_any(HaLinkMessagePromise(ids)) def handle_ioq_stob_error(self, payload: Dict[str, Any]) -> None: fid = Fid.parse(payload['conf_sdev']) if fid.is_null(): LOG.debug('Fid is 0:0. Skipping the message.') return q: Queue = Queue(1) self.queue.put( BroadcastHAStates( states=[HAState(fid, status=ServiceHealth.FAILED)], reply_to=q)) ids: List[MessageId] = q.get() self.herald.wait_for_any(HaLinkMessagePromise(ids)) def to_ha_state(self, objinfo: Dict[str, str]) -> Optional[HAState]: try: sdev_fid = self.confobjutil.drive_to_sdev_fid( objinfo['node'], objinfo['device']) state = ServiceHealth.OK if objinfo[ 'state'] == 'online' else ServiceHealth.FAILED except KeyError as error: LOG.error('Invalid json payload, no key (%s) present', error) return None return HAState(sdev_fid, status=state)