class SlurmctldCharm(CharmBase): slurm_instance_manager_cls = SlurmSnapInstanceManager def __init__(self, *args): super().__init__(*args) self.dbd_requires = HostPortRequires(self, "slurmdbd-host-port") self.fw_adapter = FrameworkAdapter(self.framework) self.slurm_snap = self.slurm_instance_manager_cls(self, "slurmdctld") self.munge = MungeProvides(self, "munge") event_handler_bindings = { self.on.install: self._on_install, self.dbd_requires.on.host_port_available: self._on_dbd_host_port_available, } for event, handler in event_handler_bindings.items(): self.fw_adapter.observe(event, handler) def _on_install(self, event): handle_install( event, self.fw_adapter, self.slurm_snap, ) def _on_dbd_host_port_available(self, event): handle_dbd_host_port_available( event, self.fw_adapter, )
class SlurmdbdCharm(CharmBase): _state = StoredState() slurm_instance_manager_cls = SlurmSnapInstanceManager def __init__(self, *args): super().__init__(*args) self._state.set_default(db_info=None) self._state.set_default(munge_key=None) # provides host port to slurmctld self.dbd_provides = HostPortProvides(self, "slurmdbd-host-port", f'{socket.gethostname()}', '6819') self.slurm_snap = self.slurm_instance_manager_cls(self, "slurmdbd") self.fw_adapter = FrameworkAdapter(self.framework) self.db = MySQLClient(self, "db") self.munge = MungeRequires(self, "munge") event_handler_bindings = { self.db.on.database_available: self._on_database_available, self.munge.on.munge_available: self._on_munge_available, self.on.install: self._on_install, self.on.start: self._on_start, } for event, handler in event_handler_bindings.items(): self.fw_adapter.observe(event, handler) def _on_install(self, event): handle_install( event, self.fw_adapter, self.slurm_snap, self.dbd_provides, ) def _on_start(self, event): handle_start( event, self.fw_adapter, self.slurm_snap, self._state, ) def _on_database_available(self, event): handle_database_available( event, self.fw_adapter, self._state, ) def _on_munge_available(self, event): handle_munge_available( event, self.fw_adapter, self.slurm_snap, self._state, )
class Charm(CharmBase): _stored = StoredState() def __init__(self, *args): super().__init__(*args) # Abstract out framework and friends so that this object is not # too tightly coupled with the underlying framework's implementation. # From this point forward, our Charm object will only interact with the # adapter and not directly with the framework. self.fw_adapter = FrameworkAdapter(self.framework) self.prometheus = PrometheusInterface(self, 'http-api') self.alertmanager = AlertManagerInterface(self, 'alertmanager') # Bind event handlers to events event_handler_bindings = { self.on.start: self.on_start, self.on.config_changed: self.on_config_changed, self.on.upgrade_charm: self.on_upgrade, self.on.stop: self.on_stop, self.alertmanager.on.new_relation: self.on_new_alertmanager_relation } for event, handler in event_handler_bindings.items(): self.fw_adapter.observe(event, handler) self._stored.set_default(is_started=False) # DELEGATORS # These delegators exist to decouple the actual handlers from the # underlying framework which has some very specific requirements that # do not always apply to every event. For instance if we were to add # an interface in our initializer, we would be forced to write unit # tests that mock out that object even for handlers that do not need # it. This hard coupling results in verbose tests that contain unused # mocks. These tests tend to be hard to follow. To counter that, the # logic is moved away from this class. def on_config_changed(self, event): on_config_changed_handler(event, self.fw_adapter, self._stored) def on_new_alertmanager_relation(self, event): on_new_alertmanager_relation_handler(event, self.fw_adapter) def on_start(self, event): on_start_handler(event, self.fw_adapter) def on_upgrade(self, event): on_upgrade_handler(event, self.fw_adapter) def on_stop(self, event): on_stop_handler(event, self.fw_adapter)
class PrometheusInterface(Object): on = PrometheusEvents() def __init__(self, charm, relation_name): super().__init__(charm, relation_name) self.fw_adapter = FrameworkAdapter(self.framework) self.relation_name = relation_name self.fw_adapter.observe(charm.on[relation_name].relation_changed, self.on_relation_changed) def on_relation_changed(self, event): logger.debug("Emitting new_prom_rel event") self.on.new_prom_rel.emit() logger.debug("Done emitting new_prom_rel_event")
class AlertManagerInterface(Object): on = AlertManagerEvents() def __init__(self, charm, relation_name): super().__init__(charm, relation_name) self.fw_adapter = FrameworkAdapter(self.framework) self.relation_name = relation_name self.fw_adapter.observe(charm.on[relation_name].relation_changed, self.on_relation_changed) def on_relation_changed(self, event): remote_data = event.relation.data[event.unit] logging.debug("Received remote_data: {}".format(dict(remote_data))) logger.debug("Emitting new_relation event") self.on.new_relation.emit(remote_data)
class PrometheusInterface(Object): on = PrometheusEvents() def __init__(self, charm, relation_name): super().__init__(charm, relation_name) self.fw = FrameworkAdapter(self.framework) self.relation_name = relation_name self.fw.observe(charm.on[relation_name].relation_joined, self.on_relation_joined) def render_relation_data(self): logging.debug('render-relation-data in') for relation in self.model.relations[self.relation_name]: relation.data[self.model.unit]['prometheus-port'] = \ str(PROMETHEUS_ADVERTISED_PORT) logging.debug('render-relation-data out') def on_relation_joined(self, event): logging.debug("on-joined; emit new-client") self.on.new_client.emit(event.relation) self.render_relation_data()