storyboard/storyboard/notifications/connection_service.py

154 lines
4.9 KiB
Python

# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from threading import Timer
import pika
from oslo_config import cfg
from oslo_log import log
from storyboard._i18n import _, _LI
CONF = cfg.CONF
LOG = log.getLogger(__name__)
class ConnectionService(object):
"""A generic amqp connection agent that handles unexpected
interactions with RabbitMQ such as channel and connection closures,
by reconnecting on failure.
"""
def __init__(self, conf):
"""Setup the connection instance based on our configuration.
:param conf A configuration object.
"""
self._connection = None
self._channel = None
self._open = False
self.started = False
self._timer = None
self._closing = False
self._open_hooks = set()
self._exchange_name = conf.rabbit_exchange_name
self._application_id = conf.rabbit_application_name
self._properties = pika.BasicProperties(
app_id='storyboard', content_type='application/json')
self._connection_credentials = pika.PlainCredentials(
conf.rabbit_userid,
conf.rabbit_password)
self._connection_parameters = pika.ConnectionParameters(
conf.rabbit_host,
conf.rabbit_port,
conf.rabbit_virtual_host,
self._connection_credentials)
def _connect(self):
"""This method connects to RabbitMQ, establishes a channel, declares
the storyboard exchange if it doesn't yet exist, and executes any
post-connection hooks that an extending class may have registered.
"""
# If the closing flag is set, just exit.
if self._closing:
return
# If a timer is set, kill it.
if self._timer:
LOG.debug(_('Clearing timer...'))
self._timer.cancel()
self._timer = None
# Create the connection
LOG.info(_LI('Connecting to %s'), self._connection_parameters.host)
self._connection = pika.BlockingConnection(self._connection_parameters)
# Create a channel
LOG.debug(_('Creating a new channel'))
self._channel = self._connection.channel()
self._channel.confirm_delivery()
# Declare the exchange
LOG.debug(_('Declaring exchange %s'), self._exchange_name)
self._channel.exchange_declare(exchange=self._exchange_name,
exchange_type='topic',
durable=True,
auto_delete=False)
# Set the open flag and execute any connection hooks.
self._open = True
self._execute_open_hooks()
def _reconnect(self):
"""Reconnect to rabbit.
"""
# Sanity check - if we're closing, do nothing.
if self._closing:
return
# If a timer is already there, assume it's doing its thing...
if self._timer:
return
LOG.debug(_('Scheduling reconnect in 5 seconds...'))
self._timer = Timer(5, self._connect)
self._timer.start()
def _close(self):
"""This method closes the connection to RabbitMQ."""
LOG.info(_LI('Closing connection'))
self._open = False
if self._channel:
self._channel.close()
self._channel = None
if self._connection:
self._connection.close()
self._connection = None
self._closing = False
LOG.debug(_('Connection Closed'))
def _execute_open_hooks(self):
"""Executes all hooks that have been registered to run on open.
"""
for hook in self._open_hooks:
hook()
def start(self):
"""Start the publisher, opening a connection to RabbitMQ. This method
must be explicitly invoked, otherwise any messages will simply be
cached for later broadcast.
"""
# Create the connection.
self.started = True
self._closing = False
self._connect()
def stop(self):
"""Stop the publisher by closing the channel and the connection.
"""
self.started = False
self._closing = True
self._close()
def add_open_hook(self, hook):
"""Add a method that will be executed whenever a connection is
established.
"""
self._open_hooks.add(hook)