예제 #1
0
    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
예제 #2
0
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')
예제 #3
0
 def __init__(self, queue: Queue, delivery_herald: DeliveryHerald):
     self.queue = queue
     self.confobjutil = ConfObjUtil()
     self.herald = delivery_herald
예제 #4
0
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)