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: |
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.sunbeam_keystone_operator.v1.identity_service
#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

View File

@ -1,10 +1,9 @@
"""AMQPProvides and Requires module.
"""RabbitMQProvides and Requires module.
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:
- self
- "amqp"
@ -21,13 +20,13 @@ Two events are also available to respond to:
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):
super().__init__(*args)
# AMQP Requires
self.amqp = AMQPRequires(
# RabbitMQ Requires
self.amqp = RabbitMQRequires(
self, "amqp",
username="myusername",
vhost="vhostname"
@ -40,42 +39,42 @@ class AMQPClientCharm(CharmBase):
self.amqp.on.goneaway, self._on_amqp_goneaway)
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.
'''
# Do something before the relation is complete
pass
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.
'''
# AMQP Relation is ready. Do something with the completed relation.
# RabbitMQ Relation is ready. Do something with the completed relation.
pass
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
```
"""
# The unique Charmhub library identifier, never change it
LIBID = "ab1414b6baf044f099caf9c117f1a101"
LIBID = "45622352791142fd9cf87232e3bd6f2a"
# Increment this major API version when introducing breaking changes
LIBAPI = 0
# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 4
LIBPATCH = 1
import logging
@ -94,39 +93,38 @@ from typing import List
logger = logging.getLogger(__name__)
class AMQPConnectedEvent(EventBase):
"""AMQP connected Event."""
class RabbitMQConnectedEvent(EventBase):
"""RabbitMQ connected Event."""
pass
class AMQPReadyEvent(EventBase):
"""AMQP ready for use Event."""
class RabbitMQReadyEvent(EventBase):
"""RabbitMQ ready for use Event."""
pass
class AMQPGoneAwayEvent(EventBase):
"""AMQP relation has gone-away Event"""
class RabbitMQGoneAwayEvent(EventBase):
"""RabbitMQ relation has gone-away Event"""
pass
class AMQPServerEvents(ObjectEvents):
class RabbitMQServerEvents(ObjectEvents):
"""Events class for `on`"""
connected = EventSource(AMQPConnectedEvent)
ready = EventSource(AMQPReadyEvent)
goneaway = EventSource(AMQPGoneAwayEvent)
connected = EventSource(RabbitMQConnectedEvent)
ready = EventSource(RabbitMQReadyEvent)
goneaway = EventSource(RabbitMQGoneAwayEvent)
class AMQPRequires(Object):
class RabbitMQRequires(Object):
"""
AMQPRequires class
RabbitMQRequires class
"""
on = AMQPServerEvents()
_stored = StoredState()
on = RabbitMQServerEvents()
def __init__(self, charm, relation_name: str, username: str, vhost: str):
super().__init__(charm, relation_name)
@ -152,89 +150,88 @@ class AMQPRequires(Object):
)
def _on_amqp_relation_joined(self, event):
"""AMQP relation joined."""
logging.debug("RabbitMQAMQPRequires on_joined")
"""RabbitMQ relation joined."""
logging.debug("RabbitMQRabbitMQRequires on_joined")
self.on.connected.emit()
self.request_access(self.username, self.vhost)
def _on_amqp_relation_changed(self, event):
"""AMQP relation changed."""
logging.debug("RabbitMQAMQPRequires on_changed/departed")
"""RabbitMQ relation changed."""
logging.debug("RabbitMQRabbitMQRequires on_changed/departed")
if self.password:
self.on.ready.emit()
def _on_amqp_relation_broken(self, event):
"""AMQP relation broken."""
logging.debug("RabbitMQAMQPRequires on_broken")
"""RabbitMQ relation broken."""
logging.debug("RabbitMQRabbitMQRequires on_broken")
self.on.goneaway.emit()
@property
def _amqp_rel(self) -> Relation:
"""The AMQP relation."""
"""The RabbitMQ relation."""
return self.framework.model.get_relation(self.relation_name)
@property
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")
@property
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")
@property
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")
@property
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")
@property
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 = []
for unit in self._amqp_rel.units:
_hosts.append(self._amqp_rel.data[unit].get("ingress-address"))
return _hosts
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():
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]["vhost"] = vhost
class HasAMQPClientsEvent(EventBase):
"""Has AMQPClients Event."""
class HasRabbitMQClientsEvent(EventBase):
"""Has RabbitMQClients Event."""
pass
class ReadyAMQPClientsEvent(EventBase):
"""AMQPClients Ready Event."""
class ReadyRabbitMQClientsEvent(EventBase):
"""RabbitMQClients Ready Event."""
pass
class AMQPClientEvents(ObjectEvents):
class RabbitMQClientEvents(ObjectEvents):
"""Events class for `on`"""
has_amqp_clients = EventSource(HasAMQPClientsEvent)
ready_amqp_clients = EventSource(ReadyAMQPClientsEvent)
has_amqp_clients = EventSource(HasRabbitMQClientsEvent)
ready_amqp_clients = EventSource(ReadyRabbitMQClientsEvent)
class AMQPProvides(Object):
class RabbitMQProvides(Object):
"""
AMQPProvides class
RabbitMQProvides class
"""
on = AMQPClientEvents()
_stored = StoredState()
on = RabbitMQClientEvents()
def __init__(self, charm, relation_name, callback):
super().__init__(charm, relation_name)
@ -255,35 +252,35 @@ class AMQPProvides(Object):
)
def _on_amqp_relation_joined(self, event):
"""Handle AMQP joined."""
logging.debug("RabbitMQAMQPProvides on_joined data={}"
.format(event.relation.data))
"""Handle RabbitMQ joined."""
logging.debug("RabbitMQRabbitMQProvides on_joined data={}"
.format(event.relation.data[event.relation.app]))
self.on.has_amqp_clients.emit()
def _on_amqp_relation_changed(self, event):
"""Handle AMQP changed."""
logging.debug("RabbitMQAMQPProvides on_changed data={}"
.format(event.relation.data))
"""Handle RabbitMQ changed."""
logging.debug("RabbitMQRabbitMQProvides on_changed data={}"
.format(event.relation.data[event.relation.app]))
# Validate data on the relation
if self.username(event) and self.vhost(event):
self.on.ready_amqp_clients.emit()
if self.charm.unit.is_leader():
self.callback(event, self.username(event), self.vhost(event))
else:
logging.warning("Received AMQP changed event without the "
logging.warning("Received RabbitMQ changed event without the "
"expected keys ('username', 'vhost') in the "
"application data bag. Incompatible charm in "
"other end of relation?")
def _on_amqp_relation_broken(self, event):
"""Handle AMQP broken."""
logging.debug("RabbitMQAMQPProvides on_departed")
"""Handle RabbitMQ broken."""
logging.debug("RabbitMQRabbitMQProvides on_departed")
# TODO clear data on the relation
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")
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")

View File

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

View File

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

View File

@ -7,6 +7,10 @@
log_config_append = /etc/keystone/logging.conf
debug = {{ options.debug }}
{% if amqp -%}
transport_url = {{ amqp.transport_url }}
{%- endif %}
[identity]
driver = {{ ks_config.identity_backend }}
{% if ks_config.default_domain_id -%}
@ -106,3 +110,5 @@ admin_project_name = admin
# This goes in the section above, selectively
# Bug #1819134
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
- glance:ingress-public
- - rabbitmq:amqp
- keystone:amqp
- - mysql:database
- keystone:database