Wait for gnocchi service before configuring ceilometer
Currently the ceilometer services are configured and ceilometer dbsync is run as an action. A new interface gnocchi_service is introduced that updates relation data when gnocchi service is up. Add new relation gnocchi_service in metadata.yaml as a mandatory relation. Update dbsync commands so that they are run as part of configure_charm. Remove the action ceilometer-upgrade that does the dbsync as it is no more required. Change-Id: Iba72250f3d31ef9dafa5b3bd16abefd43a74366a
This commit is contained in:
parent
56f17c6d76
commit
bf2bdc90b7
@ -14,11 +14,12 @@ ceilometer-k8s is deployed using below command:
|
||||
|
||||
juju deploy ceilometer-k8s ceilometer --trust
|
||||
|
||||
Now connect the ceilometer operator to keystone identity and rabbitmq
|
||||
operators:
|
||||
Now connect the ceilometer operator to keystone identity, rabbitmq
|
||||
and gnocchi operators:
|
||||
|
||||
juju relate keystone:identity-service ceilometer:identity-service
|
||||
juju relate rabbitmq:amqp ceilometer:amqp
|
||||
juju relate gnocchi:gnocchi-service ceilometer:gnocchi-db
|
||||
|
||||
### Configuration
|
||||
|
||||
@ -40,12 +41,13 @@ ceilometer-k8s requires the following relations:
|
||||
|
||||
`identity-service`: To register endpoints in Keystone
|
||||
`amqp`: To connect to Rabbitmq
|
||||
`gnocchi-db`: To connect to Gnocchi database
|
||||
|
||||
## OCI Images
|
||||
|
||||
The charm by default uses following images:
|
||||
|
||||
ghcr.io/canonical/ceilometer-consolidated:2023.1
|
||||
ghcr.io/canonical/ceilometer-consolidated:2023.2
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -1,7 +1,2 @@
|
||||
# Copyright 2023 Canonical Ltd.
|
||||
# See LICENSE file for licensing details.
|
||||
|
||||
ceilometer-upgrade:
|
||||
description: |
|
||||
Perform ceilometer-upgrade. This action will update Ceilometer data store
|
||||
configuration.
|
||||
# NOTE: no actions yet!
|
||||
{ }
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "INFO: Fetching libs from charmhub."
|
||||
charmcraft fetch-lib charms.gnocchi_k8s.v0.gnocchi_service
|
||||
# charmcraft fetch-lib charms.data_platform_libs.v0.database_requires
|
||||
# charmcraft fetch-lib charms.keystone_k8s.v1.identity_service
|
||||
# charmcraft fetch-lib charms.rabbitmq_k8s.v0.rabbitmq
|
||||
|
@ -0,0 +1,205 @@
|
||||
"""GnocchiService Provides and Requires module.
|
||||
|
||||
This library contains the Requires and Provides classes for handling
|
||||
the Gnocchi service interface.
|
||||
|
||||
Import `GnocchiServiceRequires` in your charm, with the charm object and the
|
||||
relation name:
|
||||
- self
|
||||
- "gnocchi-db"
|
||||
|
||||
Two events are also available to respond to:
|
||||
- readiness_changed
|
||||
- goneaway
|
||||
|
||||
A basic example showing the usage of this relation follows:
|
||||
|
||||
```
|
||||
from charms.gnocchi_k8s.v0.gnocchi_service import (
|
||||
GnocchiServiceRequires
|
||||
)
|
||||
|
||||
class GnocchiServiceClientCharm(CharmBase):
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
# GnocchiService Requires
|
||||
self.gnocchi_svc = GnocchiServiceRequires(
|
||||
self, "gnocchi-db",
|
||||
)
|
||||
self.framework.observe(
|
||||
self.gnocchi_svc.on.readiness_changed,
|
||||
self._on_gnocchi_service_readiness_changed
|
||||
)
|
||||
self.framework.observe(
|
||||
self.gnocchi_svc.on.goneaway,
|
||||
self._on_gnocchi_service_goneaway
|
||||
)
|
||||
|
||||
def _on_gnocchi_service_readiness_changed(self, event):
|
||||
'''React to the Gnocchi service readiness changed event.
|
||||
|
||||
This event happens when Gnocchi service relation is added to the
|
||||
model and relation data is changed.
|
||||
'''
|
||||
# Do something with the configuration provided by relation.
|
||||
pass
|
||||
|
||||
def _on_gnocchi_service_goneaway(self, event):
|
||||
'''React to the Gnocchi Service goneaway event.
|
||||
|
||||
This event happens when Gnocchi service relation is removed.
|
||||
'''
|
||||
# HeatSharedConfig Relation has goneaway.
|
||||
pass
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import (
|
||||
Optional,
|
||||
)
|
||||
|
||||
from ops.charm import (
|
||||
CharmBase,
|
||||
RelationBrokenEvent,
|
||||
RelationChangedEvent,
|
||||
RelationEvent,
|
||||
)
|
||||
from ops.framework import (
|
||||
EventSource,
|
||||
Object,
|
||||
ObjectEvents,
|
||||
)
|
||||
from ops.model import (
|
||||
Relation,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# The unique Charmhub library identifier, never change it
|
||||
LIBID = "97b7682b415040f3b32d77fff8d93e7e"
|
||||
|
||||
# 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 = 2
|
||||
|
||||
|
||||
class GnocchiServiceReadinessRequestEvent(RelationEvent):
|
||||
"""GnocchiServiceReadinessRequest Event."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class GnocchiServiceProviderEvents(ObjectEvents):
|
||||
"""Events class for `on`."""
|
||||
|
||||
service_readiness = EventSource(GnocchiServiceReadinessRequestEvent)
|
||||
|
||||
|
||||
class GnocchiServiceProvides(Object):
|
||||
"""GnocchiServiceProvides class."""
|
||||
|
||||
on = GnocchiServiceProviderEvents()
|
||||
|
||||
def __init__(self, charm: CharmBase, relation_name: str):
|
||||
super().__init__(charm, relation_name)
|
||||
self.charm = charm
|
||||
self.relation_name = relation_name
|
||||
self.framework.observe(
|
||||
self.charm.on[relation_name].relation_changed,
|
||||
self._on_relation_changed,
|
||||
)
|
||||
|
||||
def _on_relation_changed(self, event: RelationChangedEvent):
|
||||
"""Handle Gnocchi service relation changed."""
|
||||
logging.debug("Gnocchi Service relation changed")
|
||||
self.on.service_readiness.emit(event.relation)
|
||||
|
||||
def set_service_status(self, relation: Relation, is_ready: bool) -> None:
|
||||
"""Set gnocchi service readiness status on the relation."""
|
||||
if not self.charm.unit.is_leader():
|
||||
logging.debug("Not a leader unit, skipping setting ready status")
|
||||
return
|
||||
|
||||
logging.debug(
|
||||
f"Setting ready status on relation {relation.app.name} "
|
||||
f"{relation.name}/{relation.id}"
|
||||
)
|
||||
relation.data[self.charm.app]["ready"] = json.dumps(is_ready)
|
||||
|
||||
|
||||
class GnocchiServiceReadinessChangedEvent(RelationEvent):
|
||||
"""GnocchiServiceReadinessChanged Event."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class GnocchiServiceGoneAwayEvent(RelationEvent):
|
||||
"""GnocchiServiceGoneAway Event."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class GnocchiServiceRequirerEvents(ObjectEvents):
|
||||
"""Events class for `on`."""
|
||||
|
||||
readiness_changed = EventSource(GnocchiServiceReadinessChangedEvent)
|
||||
goneaway = EventSource(GnocchiServiceGoneAwayEvent)
|
||||
|
||||
|
||||
class GnocchiServiceRequires(Object):
|
||||
"""GnocchiServiceRequires class."""
|
||||
|
||||
on = GnocchiServiceRequirerEvents()
|
||||
|
||||
def __init__(self, charm: CharmBase, relation_name: str):
|
||||
super().__init__(charm, relation_name)
|
||||
self.charm = charm
|
||||
self.relation_name = relation_name
|
||||
self.framework.observe(
|
||||
self.charm.on[relation_name].relation_changed,
|
||||
self._on_relation_changed,
|
||||
)
|
||||
self.framework.observe(
|
||||
self.charm.on[relation_name].relation_broken,
|
||||
self._on_relation_broken,
|
||||
)
|
||||
|
||||
def _on_relation_changed(self, event: RelationChangedEvent):
|
||||
"""Handle Gnocchi Service relation changed."""
|
||||
logging.debug("Gnocchi service readiness data changed")
|
||||
self.on.readiness_changed.emit(event.relation)
|
||||
|
||||
def _on_relation_broken(self, event: RelationBrokenEvent):
|
||||
"""Handle Gnocchi Service relation broken."""
|
||||
logging.debug("Gnocchi service on_broken")
|
||||
self.on.goneaway.emit(event.relation)
|
||||
|
||||
@property
|
||||
def _gnocchi_service_rel(self) -> Optional[Relation]:
|
||||
"""The gnocchi service relation."""
|
||||
return self.framework.model.get_relation(self.relation_name)
|
||||
|
||||
def get_remote_app_data(self, key: str) -> Optional[str]:
|
||||
"""Return the value for the given key from remote app data."""
|
||||
if self._gnocchi_service_rel:
|
||||
data = self._gnocchi_service_rel.data[
|
||||
self._gnocchi_service_rel.app
|
||||
]
|
||||
return data.get(key)
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def service_ready(self) -> bool:
|
||||
"""Return if gnocchi service is ready or not."""
|
||||
is_ready = self.get_remote_app_data("ready")
|
||||
if is_ready:
|
||||
return json.loads(is_ready)
|
||||
|
||||
return False
|
@ -47,6 +47,8 @@ requires:
|
||||
identity-credentials:
|
||||
interface: keystone-credentials
|
||||
limit: 1
|
||||
gnocchi-db:
|
||||
interface: gnocchi
|
||||
|
||||
peers:
|
||||
peers:
|
||||
|
@ -25,6 +25,7 @@ from typing import (
|
||||
List,
|
||||
)
|
||||
|
||||
import ops.charm
|
||||
import ops.framework
|
||||
import ops_sunbeam.charm as sunbeam_charm
|
||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||
@ -34,13 +35,19 @@ from charms.ceilometer_k8s.v0.ceilometer_service import (
|
||||
CeilometerConfigRequestEvent,
|
||||
CeilometerServiceProvides,
|
||||
)
|
||||
from charms.gnocchi_k8s.v0.gnocchi_service import (
|
||||
GnocchiServiceRequires,
|
||||
)
|
||||
from ops.charm import (
|
||||
ActionEvent,
|
||||
CharmBase,
|
||||
RelationEvent,
|
||||
)
|
||||
from ops.main import (
|
||||
main,
|
||||
)
|
||||
from ops.model import (
|
||||
BlockedStatus,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -48,6 +55,71 @@ CEILOMETER_CENTRAL_CONTAINER = "ceilometer-central"
|
||||
CEILOMETER_NOTIFICATION_CONTAINER = "ceilometer-notification"
|
||||
|
||||
|
||||
class GnocchiServiceRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
||||
"""Handle gnocchi service relation on the requires side."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
charm: CharmBase,
|
||||
relation_name: str,
|
||||
callback_f: Callable,
|
||||
mandatory: bool = False,
|
||||
):
|
||||
"""Create a new gnocchi service handler.
|
||||
|
||||
Create a new GnocchiServiceRequiresHandler that handles initial
|
||||
events from the relation and invokes the provided callbacks based on
|
||||
the event raised.
|
||||
|
||||
:param charm: the Charm class the handler is for
|
||||
:type charm: ops.charm.CharmBase
|
||||
:param relation_name: the relation the handler is bound to
|
||||
:type relation_name: str
|
||||
:param callback_f: the function to call when the nodes are connected
|
||||
:type callback_f: Callable
|
||||
:param mandatory: If the relation is mandatory to proceed with
|
||||
configuring charm
|
||||
:type mandatory: bool
|
||||
"""
|
||||
super().__init__(charm, relation_name, callback_f, mandatory)
|
||||
|
||||
def setup_event_handler(self) -> ops.charm.Object:
|
||||
"""Configure event handlers for Gnocchi service relation."""
|
||||
logger.debug("Setting up Gnocchi service event handler")
|
||||
svc = GnocchiServiceRequires(
|
||||
self.charm,
|
||||
self.relation_name,
|
||||
)
|
||||
self.framework.observe(
|
||||
svc.on.readiness_changed,
|
||||
self._on_gnocchi_service_readiness_changed,
|
||||
)
|
||||
self.framework.observe(
|
||||
svc.on.goneaway,
|
||||
self._on_gnocchi_service_goneaway,
|
||||
)
|
||||
return svc
|
||||
|
||||
def _on_gnocchi_service_readiness_changed(
|
||||
self, event: RelationEvent
|
||||
) -> None:
|
||||
"""Handle config_changed event."""
|
||||
logger.debug("Gnocchi service readiness changed event received")
|
||||
self.callback_f(event)
|
||||
|
||||
def _on_gnocchi_service_goneaway(self, event: RelationEvent) -> None:
|
||||
"""Handle gone_away event."""
|
||||
logger.debug("Gnocchi service gone away event received")
|
||||
self.callback_f(event)
|
||||
if self.mandatory:
|
||||
self.status.set(BlockedStatus("integration missing"))
|
||||
|
||||
@property
|
||||
def ready(self) -> bool:
|
||||
"""Whether handler is ready for use."""
|
||||
return self.interface.service_ready
|
||||
|
||||
|
||||
class CeilometerServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||
"""Handler for ceilometer service relation."""
|
||||
|
||||
@ -177,13 +249,11 @@ class CeilometerOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||
service_name = "ceilometer"
|
||||
shared_metering_secret_key = "shared-metering-secret"
|
||||
|
||||
mandatory_relations = {"amqp", "identity-credentials"}
|
||||
db_sync_cmds = [["ceilometer-upgrade"]]
|
||||
mandatory_relations = {"amqp", "identity-credentials", "gnocchi-db"}
|
||||
|
||||
def __init__(self, framework: ops.framework):
|
||||
super().__init__(framework)
|
||||
self.framework.observe(
|
||||
self.on.ceilometer_upgrade_action, self._ceilometer_upgrade_action
|
||||
)
|
||||
|
||||
def get_shared_meteringsecret(self):
|
||||
"""Return the shared metering secret."""
|
||||
@ -210,6 +280,11 @@ class CeilometerOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||
return
|
||||
super().configure_charm(event)
|
||||
|
||||
@property
|
||||
def db_sync_container_name(self) -> str:
|
||||
"""Name of Containerto run db sync from."""
|
||||
return CEILOMETER_NOTIFICATION_CONTAINER
|
||||
|
||||
@property
|
||||
def service_user(self) -> str:
|
||||
"""Service user file and directory ownership."""
|
||||
@ -266,6 +341,14 @@ class CeilometerOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||
self.set_config_from_event,
|
||||
)
|
||||
handlers.append(self.config_svc)
|
||||
if self.can_add_handler("gnocchi-db", handlers):
|
||||
self.gnocchi_svc = GnocchiServiceRequiresHandler(
|
||||
self,
|
||||
"gnocchi-db",
|
||||
self.configure_charm,
|
||||
"gnocchi-db" in self.mandatory_relations,
|
||||
)
|
||||
handlers.append(self.gnocchi_svc)
|
||||
|
||||
return super().get_relation_handlers(handlers)
|
||||
|
||||
@ -289,30 +372,6 @@ class CeilometerOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||
else:
|
||||
logging.debug("Telemetry secret not yet set, not sending config")
|
||||
|
||||
def _ceilometer_upgrade_action(self, event: ActionEvent) -> None:
|
||||
"""Run ceilometer-upgrade.
|
||||
|
||||
This action will upgrade the data store configuration in gnocchi.
|
||||
"""
|
||||
try:
|
||||
logger.info("Syncing database...")
|
||||
cmd = ["ceilometer-upgrade"]
|
||||
container = self.unit.get_container(
|
||||
CEILOMETER_NOTIFICATION_CONTAINER
|
||||
)
|
||||
process = container.exec(cmd, timeout=5 * 60)
|
||||
out, warnings = process.wait_output()
|
||||
logging.debug("Output from database sync: \n%s", out)
|
||||
if warnings:
|
||||
for line in warnings.splitlines():
|
||||
logger.warning("DB Sync Out: %s", line.strip())
|
||||
event.fail(f"Error in running ceilometer-upgrade: {warnings}")
|
||||
else:
|
||||
event.set_results({"message": "ceilometer-upgrade successful"})
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
event.fail(f"Error in running ceilometer-updgrade: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(CeilometerOperatorCharm)
|
||||
|
@ -93,3 +93,5 @@ relations:
|
||||
- ceilometer:amqp
|
||||
- - keystone:identity-credentials
|
||||
- ceilometer:identity-credentials
|
||||
- - gnocchi:gnocchi-service
|
||||
- ceilometer:gnocchi-db
|
||||
|
@ -33,8 +33,8 @@ target_deploy_status:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^.*$'
|
||||
ceilometer:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^.*$'
|
||||
workload-status: waiting
|
||||
workload-status-message-regex: '^.*Not all relations are ready$'
|
||||
aodh:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^.*$'
|
||||
|
@ -60,6 +60,9 @@ class TestCeilometerOperatorCharm(test_utils.CharmTestCase):
|
||||
test_utils.set_all_pebbles_ready(self.harness)
|
||||
test_utils.add_complete_identity_credentials_relation(self.harness)
|
||||
test_utils.add_complete_amqp_relation(self.harness)
|
||||
self.harness.add_relation(
|
||||
"gnocchi-db", "gnocchi", app_data={"ready": "true"}
|
||||
)
|
||||
|
||||
for c in ["ceilometer-central", "ceilometer-notification"]:
|
||||
self.check_file(c, "/etc/ceilometer/ceilometer.conf")
|
||||
|
Loading…
x
Reference in New Issue
Block a user