Enable notifications

Currently the notifications are not sent by the service.
Add configuration parameter enable-telemetry-notifications
to toggle notifications. Set notification driver to messagingv2
when the parameter is set to True.
Update metadata.yaml to include amqp as an optional interface.

Change-Id: Ia77fa388e238ca13a7194a3d38de33a42f57538b
This commit is contained in:
Hemanth Nakkina 2023-08-25 16:41:58 +05:30 committed by Hemanth N
parent cdfa393ec3
commit 8787171492
8 changed files with 94 additions and 75 deletions

View File

@ -64,3 +64,7 @@ options:
description: | description: |
Keystone identity backend, valid options are sql and pam Keystone identity backend, valid options are sql and pam
enable-telemetry-notifications:
type: boolean
default: False
description: Enable notifications to send to telemetry.

View File

@ -5,5 +5,5 @@ charmcraft fetch-lib charms.nginx_ingress_integrator.v0.ingress
charmcraft fetch-lib charms.data_platform_libs.v0.database_requires charmcraft fetch-lib charms.data_platform_libs.v0.database_requires
#charmcraft fetch-lib charms.sunbeam_keystone_operator.v1.identity_service #charmcraft fetch-lib charms.sunbeam_keystone_operator.v1.identity_service
#charmcraft fetch-lib charms.sunbeam_keystone_operator.v0.identity_credentials #charmcraft fetch-lib charms.sunbeam_keystone_operator.v0.identity_credentials
charmcraft fetch-lib charms.sunbeam_rabbitmq_operator.v0.amqp charmcraft fetch-lib charms.rabbitmq_k8s.v0.rabbitmq
charmcraft fetch-lib charms.traefik_k8s.v1.ingress charmcraft fetch-lib charms.traefik_k8s.v1.ingress

View File

@ -1,10 +1,9 @@
"""AMQPProvides and Requires module. """RabbitMQProvides and Requires module.
This library contains the Requires and Provides classes for handling This library contains the Requires and Provides classes for handling
the amqp interface. the rabbitmq interface.
Import `AMQPRequires` in your charm, with the charm object and the Import `RabbitMQRequires` in your charm, with the charm object and the
relation name: relation name:
- self - self
- "amqp" - "amqp"
@ -21,13 +20,13 @@ Two events are also available to respond to:
A basic example showing the usage of this relation follows: A basic example showing the usage of this relation follows:
``` ```
from charms.sunbeam_rabbitmq_operator.v0.amqp import AMQPRequires from charms.rabbitmq_k8s.v0.rabbitmq import RabbitMQRequires
class AMQPClientCharm(CharmBase): class RabbitMQClientCharm(CharmBase):
def __init__(self, *args): def __init__(self, *args):
super().__init__(*args) super().__init__(*args)
# AMQP Requires # RabbitMQ Requires
self.amqp = AMQPRequires( self.amqp = RabbitMQRequires(
self, "amqp", self, "amqp",
username="myusername", username="myusername",
vhost="vhostname" vhost="vhostname"
@ -40,42 +39,42 @@ class AMQPClientCharm(CharmBase):
self.amqp.on.goneaway, self._on_amqp_goneaway) self.amqp.on.goneaway, self._on_amqp_goneaway)
def _on_amqp_connected(self, event): def _on_amqp_connected(self, event):
'''React to the AMQP connected event. '''React to the RabbitMQ connected event.
This event happens when n AMQP relation is added to the This event happens when n RabbitMQ relation is added to the
model before credentials etc have been provided. model before credentials etc have been provided.
''' '''
# Do something before the relation is complete # Do something before the relation is complete
pass pass
def _on_amqp_ready(self, event): def _on_amqp_ready(self, event):
'''React to the AMQP ready event. '''React to the RabbitMQ ready event.
The AMQP interface will use the provided username and vhost for the The RabbitMQ interface will use the provided username and vhost for the
request to the rabbitmq server. request to the rabbitmq server.
''' '''
# AMQP Relation is ready. Do something with the completed relation. # RabbitMQ Relation is ready. Do something with the completed relation.
pass pass
def _on_amqp_goneaway(self, event): def _on_amqp_goneaway(self, event):
'''React to the AMQP goneaway event. '''React to the RabbitMQ goneaway event.
This event happens when an AMQP relation is removed. This event happens when an RabbitMQ relation is removed.
''' '''
# AMQP Relation has goneaway. shutdown services or suchlike # RabbitMQ Relation has goneaway. shutdown services or suchlike
pass pass
``` ```
""" """
# The unique Charmhub library identifier, never change it # The unique Charmhub library identifier, never change it
LIBID = "ab1414b6baf044f099caf9c117f1a101" LIBID = "45622352791142fd9cf87232e3bd6f2a"
# Increment this major API version when introducing breaking changes # Increment this major API version when introducing breaking changes
LIBAPI = 0 LIBAPI = 0
# Increment this PATCH version before using `charmcraft publish-lib` or reset # Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version # to 0 if you are raising the major API version
LIBPATCH = 4 LIBPATCH = 1
import logging import logging
@ -94,39 +93,38 @@ from typing import List
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class AMQPConnectedEvent(EventBase): class RabbitMQConnectedEvent(EventBase):
"""AMQP connected Event.""" """RabbitMQ connected Event."""
pass pass
class AMQPReadyEvent(EventBase): class RabbitMQReadyEvent(EventBase):
"""AMQP ready for use Event.""" """RabbitMQ ready for use Event."""
pass pass
class AMQPGoneAwayEvent(EventBase): class RabbitMQGoneAwayEvent(EventBase):
"""AMQP relation has gone-away Event""" """RabbitMQ relation has gone-away Event"""
pass pass
class AMQPServerEvents(ObjectEvents): class RabbitMQServerEvents(ObjectEvents):
"""Events class for `on`""" """Events class for `on`"""
connected = EventSource(AMQPConnectedEvent) connected = EventSource(RabbitMQConnectedEvent)
ready = EventSource(AMQPReadyEvent) ready = EventSource(RabbitMQReadyEvent)
goneaway = EventSource(AMQPGoneAwayEvent) goneaway = EventSource(RabbitMQGoneAwayEvent)
class AMQPRequires(Object): class RabbitMQRequires(Object):
""" """
AMQPRequires class RabbitMQRequires class
""" """
on = AMQPServerEvents() on = RabbitMQServerEvents()
_stored = StoredState()
def __init__(self, charm, relation_name: str, username: str, vhost: str): def __init__(self, charm, relation_name: str, username: str, vhost: str):
super().__init__(charm, relation_name) super().__init__(charm, relation_name)
@ -152,89 +150,88 @@ class AMQPRequires(Object):
) )
def _on_amqp_relation_joined(self, event): def _on_amqp_relation_joined(self, event):
"""AMQP relation joined.""" """RabbitMQ relation joined."""
logging.debug("RabbitMQAMQPRequires on_joined") logging.debug("RabbitMQRabbitMQRequires on_joined")
self.on.connected.emit() self.on.connected.emit()
self.request_access(self.username, self.vhost) self.request_access(self.username, self.vhost)
def _on_amqp_relation_changed(self, event): def _on_amqp_relation_changed(self, event):
"""AMQP relation changed.""" """RabbitMQ relation changed."""
logging.debug("RabbitMQAMQPRequires on_changed/departed") logging.debug("RabbitMQRabbitMQRequires on_changed/departed")
if self.password: if self.password:
self.on.ready.emit() self.on.ready.emit()
def _on_amqp_relation_broken(self, event): def _on_amqp_relation_broken(self, event):
"""AMQP relation broken.""" """RabbitMQ relation broken."""
logging.debug("RabbitMQAMQPRequires on_broken") logging.debug("RabbitMQRabbitMQRequires on_broken")
self.on.goneaway.emit() self.on.goneaway.emit()
@property @property
def _amqp_rel(self) -> Relation: def _amqp_rel(self) -> Relation:
"""The AMQP relation.""" """The RabbitMQ relation."""
return self.framework.model.get_relation(self.relation_name) return self.framework.model.get_relation(self.relation_name)
@property @property
def password(self) -> str: def password(self) -> str:
"""Return the AMQP password from the server side of the relation.""" """Return the RabbitMQ password from the server side of the relation."""
return self._amqp_rel.data[self._amqp_rel.app].get("password") return self._amqp_rel.data[self._amqp_rel.app].get("password")
@property @property
def hostname(self) -> str: def hostname(self) -> str:
"""Return the hostname from the AMQP relation""" """Return the hostname from the RabbitMQ relation"""
return self._amqp_rel.data[self._amqp_rel.app].get("hostname") return self._amqp_rel.data[self._amqp_rel.app].get("hostname")
@property @property
def ssl_port(self) -> str: def ssl_port(self) -> str:
"""Return the SSL port from the AMQP relation""" """Return the SSL port from the RabbitMQ relation"""
return self._amqp_rel.data[self._amqp_rel.app].get("ssl_port") return self._amqp_rel.data[self._amqp_rel.app].get("ssl_port")
@property @property
def ssl_ca(self) -> str: def ssl_ca(self) -> str:
"""Return the SSL port from the AMQP relation""" """Return the SSL port from the RabbitMQ relation"""
return self._amqp_rel.data[self._amqp_rel.app].get("ssl_ca") return self._amqp_rel.data[self._amqp_rel.app].get("ssl_ca")
@property @property
def hostnames(self) -> List[str]: def hostnames(self) -> List[str]:
"""Return a list of remote RMQ hosts from the AMQP relation""" """Return a list of remote RMQ hosts from the RabbitMQ relation"""
_hosts = [] _hosts = []
for unit in self._amqp_rel.units: for unit in self._amqp_rel.units:
_hosts.append(self._amqp_rel.data[unit].get("ingress-address")) _hosts.append(self._amqp_rel.data[unit].get("ingress-address"))
return _hosts return _hosts
def request_access(self, username: str, vhost: str) -> None: def request_access(self, username: str, vhost: str) -> None:
"""Request access to the AMQP server.""" """Request access to the RabbitMQ server."""
if self.model.unit.is_leader(): if self.model.unit.is_leader():
logging.debug("Requesting AMQP user and vhost") logging.debug("Requesting RabbitMQ user and vhost")
self._amqp_rel.data[self.charm.app]["username"] = username self._amqp_rel.data[self.charm.app]["username"] = username
self._amqp_rel.data[self.charm.app]["vhost"] = vhost self._amqp_rel.data[self.charm.app]["vhost"] = vhost
class HasAMQPClientsEvent(EventBase): class HasRabbitMQClientsEvent(EventBase):
"""Has AMQPClients Event.""" """Has RabbitMQClients Event."""
pass pass
class ReadyAMQPClientsEvent(EventBase): class ReadyRabbitMQClientsEvent(EventBase):
"""AMQPClients Ready Event.""" """RabbitMQClients Ready Event."""
pass pass
class AMQPClientEvents(ObjectEvents): class RabbitMQClientEvents(ObjectEvents):
"""Events class for `on`""" """Events class for `on`"""
has_amqp_clients = EventSource(HasAMQPClientsEvent) has_amqp_clients = EventSource(HasRabbitMQClientsEvent)
ready_amqp_clients = EventSource(ReadyAMQPClientsEvent) ready_amqp_clients = EventSource(ReadyRabbitMQClientsEvent)
class AMQPProvides(Object): class RabbitMQProvides(Object):
""" """
AMQPProvides class RabbitMQProvides class
""" """
on = AMQPClientEvents() on = RabbitMQClientEvents()
_stored = StoredState()
def __init__(self, charm, relation_name, callback): def __init__(self, charm, relation_name, callback):
super().__init__(charm, relation_name) super().__init__(charm, relation_name)
@ -255,35 +252,35 @@ class AMQPProvides(Object):
) )
def _on_amqp_relation_joined(self, event): def _on_amqp_relation_joined(self, event):
"""Handle AMQP joined.""" """Handle RabbitMQ joined."""
logging.debug("RabbitMQAMQPProvides on_joined data={}" logging.debug("RabbitMQRabbitMQProvides on_joined data={}"
.format(event.relation.data)) .format(event.relation.data[event.relation.app]))
self.on.has_amqp_clients.emit() self.on.has_amqp_clients.emit()
def _on_amqp_relation_changed(self, event): def _on_amqp_relation_changed(self, event):
"""Handle AMQP changed.""" """Handle RabbitMQ changed."""
logging.debug("RabbitMQAMQPProvides on_changed data={}" logging.debug("RabbitMQRabbitMQProvides on_changed data={}"
.format(event.relation.data)) .format(event.relation.data[event.relation.app]))
# Validate data on the relation # Validate data on the relation
if self.username(event) and self.vhost(event): if self.username(event) and self.vhost(event):
self.on.ready_amqp_clients.emit() self.on.ready_amqp_clients.emit()
if self.charm.unit.is_leader(): if self.charm.unit.is_leader():
self.callback(event, self.username(event), self.vhost(event)) self.callback(event, self.username(event), self.vhost(event))
else: else:
logging.warning("Received AMQP changed event without the " logging.warning("Received RabbitMQ changed event without the "
"expected keys ('username', 'vhost') in the " "expected keys ('username', 'vhost') in the "
"application data bag. Incompatible charm in " "application data bag. Incompatible charm in "
"other end of relation?") "other end of relation?")
def _on_amqp_relation_broken(self, event): def _on_amqp_relation_broken(self, event):
"""Handle AMQP broken.""" """Handle RabbitMQ broken."""
logging.debug("RabbitMQAMQPProvides on_departed") logging.debug("RabbitMQRabbitMQProvides on_departed")
# TODO clear data on the relation # TODO clear data on the relation
def username(self, event): def username(self, event):
"""Return the AMQP username from the client side of the relation.""" """Return the RabbitMQ username from the client side of the relation."""
return event.relation.data[event.relation.app].get("username") return event.relation.data[event.relation.app].get("username")
def vhost(self, event): def vhost(self, event):
"""Return the AMQP vhost from the client side of the relation.""" """Return the RabbitMQ vhost from the client side of the relation."""
return event.relation.data[event.relation.app].get("vhost") return event.relation.data[event.relation.app].get("vhost")

View File

@ -41,6 +41,9 @@ requires:
ingress-public: ingress-public:
interface: ingress interface: ingress
limit: 1 limit: 1
amqp:
interface: rabbitmq
optional: true
peers: peers:
peers: peers:

View File

@ -729,12 +729,14 @@ export OS_AUTH_VERSION=3
@property @property
def config_contexts(self) -> List[sunbeam_contexts.ConfigContext]: def config_contexts(self) -> List[sunbeam_contexts.ConfigContext]:
"""Configuration adapters for the operator.""" """Configuration adapters for the operator."""
return [ contexts = super().config_contexts
contexts.extend(
[
KeystoneConfigAdapter(self, "ks_config"), KeystoneConfigAdapter(self, "ks_config"),
KeystoneLoggingAdapter(self, "ks_logging"), KeystoneLoggingAdapter(self, "ks_logging"),
sunbeam_contexts.WSGIWorkerConfigContext(self, "wsgi_config"),
sunbeam_contexts.CharmConfigContext(self, "options"),
] ]
)
return contexts
@property @property
def container_configs(self): def container_configs(self):

View File

@ -7,6 +7,10 @@
log_config_append = /etc/keystone/logging.conf log_config_append = /etc/keystone/logging.conf
debug = {{ options.debug }} debug = {{ options.debug }}
{% if amqp -%}
transport_url = {{ amqp.transport_url }}
{%- endif %}
[identity] [identity]
driver = {{ ks_config.identity_backend }} driver = {{ ks_config.identity_backend }}
{% if ks_config.default_domain_id -%} {% if ks_config.default_domain_id -%}
@ -106,3 +110,5 @@ admin_project_name = admin
# This goes in the section above, selectively # This goes in the section above, selectively
# Bug #1819134 # Bug #1819134
max_request_body_size = 114688 max_request_body_size = 114688
{% include "parts/section-oslo-notifications" %}

View File

@ -0,0 +1,4 @@
{% if options.enable_telemetry_notifications -%}
[oslo_messaging_notifications]
driver = messagingv2
{%- endif %}

View File

@ -54,6 +54,9 @@ relations:
- - traefik:ingress - - traefik:ingress
- glance:ingress-public - glance:ingress-public
- - rabbitmq:amqp
- keystone:amqp
- - mysql:database - - mysql:database
- keystone:database - keystone:database