Improve the way we monitor changes within the Juju model, by maintaining
a single observer with an Application filter. This reduces the number of
duplicate callbacks being fired, and decreases the chatter between the
controller and client.
Signed-off-by: Adam Israel <adam.israel@canonical.com>
class VCAMonitor(ModelObserver):
"""Monitor state changes within the Juju Model."""
class VCAMonitor(ModelObserver):
"""Monitor state changes within the Juju Model."""
- callback = None
- callback_args = None
log = None
ns_name = None
log = None
ns_name = None
- application_name = None
- def __init__(self, ns_name, application_name, callback, *args):
+ def __init__(self, ns_name):
self.log = logging.getLogger(__name__)
self.ns_name = ns_name
self.log = logging.getLogger(__name__)
self.ns_name = ns_name
- self.application_name = application_name
- self.callback = callback
- self.callback_args = args
+
+ def AddApplication(self, application_name, callback, *callback_args):
+ if application_name not in self.applications:
+ self.applications[application_name] = {
+ 'callback': callback,
+ 'callback_args': callback_args
+ }
+
+ def RemoveApplication(self, application_name):
+ if application_name in self.applications:
+ del self.applications[application_name]
async def on_change(self, delta, old, new, model):
"""React to changes in the Juju model."""
if delta.entity == "unit":
async def on_change(self, delta, old, new, model):
"""React to changes in the Juju model."""
if delta.entity == "unit":
+ # Ignore change events from other applications
+ if delta.data['application'] not in self.applications.keys():
+ return
+
+
+ application_name = delta.data['application']
+
+ callback = self.applications[application_name]['callback']
+ callback_args = self.applications[application_name]['callback_args']
+
if old and new:
old_status = old.workload_status
new_status = new.workload_status
if old_status == new_status:
if old and new:
old_status = old.workload_status
new_status = new.workload_status
if old_status == new_status:
"""The workload status may fluctuate around certain events,
so wait until the status has stabilized before triggering
the callback."""
"""The workload status may fluctuate around certain events,
so wait until the status has stabilized before triggering
the callback."""
- if self.callback:
- self.callback(
+ if callback:
+ callback(
+ delta.data['application'],
+ *callback_args)
+
+ if old and not new:
+ # This is a charm being removed
+ if callback:
+ callback(
+ self.ns_name,
+ delta.data['application'],
+ "removed",
+ *callback_args)
except Exception as e:
self.log.debug("[1] notify_callback exception {}".format(e))
elif delta.entity == "action":
except Exception as e:
self.log.debug("[1] notify_callback exception {}".format(e))
elif delta.entity == "action":
if app:
raise JujuApplicationExists("Can't deploy application \"{}\" to model \"{}\" because it already exists.".format(application_name, model))
if app:
raise JujuApplicationExists("Can't deploy application \"{}\" to model \"{}\" because it already exists.".format(application_name, model))
- ############################################################
- # Create a monitor to watch for application status changes #
- ############################################################
+ ################################################################
+ # Register this application with the model-level event monitor #
+ ################################################################
- self.log.debug("Setting monitor<->callback")
- self.monitors[application_name] = VCAMonitor(model_name, application_name, callback, *callback_args)
- model.add_observer(self.monitors[application_name])
+ self.monitors[model_name].AddApplication(
+ application_name,
+ callback,
+ *callback_args
+ )
########################################################
# Check for specific machine placement (native charms) #
########################################################
# Check for specific machine placement (native charms) #
# This is applied when the Application is deployed
pass
else:
# This is applied when the Application is deployed
pass
else:
- # TODO: We need to sort by seq, and queue the actions in order.
-
seq = primitive['seq']
primitives[seq] = {
seq = primitive['seq']
primitives[seq] = {
model = await self.get_model(model_name)
app = await self.get_application(model, application_name)
if app:
model = await self.get_model(model_name)
app = await self.get_application(model, application_name)
if app:
- self.notify_callback(model_name, application_name, "removing", callback, *callback_args)
+ # Remove this application from event monitoring
+ self.monitors[model_name].RemoveApplication(application_name)
+
+ # self.notify_callback(model_name, application_name, "removing", callback, *callback_args)
+ self.log.debug("Removing the application {}".format(application_name))
+
+ # Notify the callback that this charm has been removed.
self.notify_callback(model_name, application_name, "removed", callback, *callback_args)
self.notify_callback(model_name, application_name, "removed", callback, *callback_args)
except Exception as e:
print("Caught exception: {}".format(e))
self.log.debug(e)
except Exception as e:
print("Caught exception: {}".format(e))
self.log.debug(e)
print("connecting to model {}".format(model_name))
self.models[model_name] = await self.controller.get_model(model_name)
print("connecting to model {}".format(model_name))
self.models[model_name] = await self.controller.get_model(model_name)
+ # Create an observer for this model
+ self.monitors[model_name] = VCAMonitor(model_name)
+ self.models[model_name].add_observer(self.monitors[model_name])
+
return self.models[model_name]
async def login(self):
return self.models[model_name]
async def login(self):