Move add_unit logic into Application
[osm/N2VC.git] / juju / application.py
index 978500f..90a78ca 100644 (file)
@@ -1,3 +1,4 @@
+import asyncio
 import logging
 
 from . import model
@@ -7,6 +8,26 @@ log = logging.getLogger(__name__)
 
 
 class Application(model.ModelEntity):
+    @property
+    def _unit_match_pattern(self):
+        return r'^{}.*$'.format(self.entity_id)
+
+    def on_unit_add(self, callable_):
+        """Add a "unit added" observer to this entity, which will be called
+        whenever a unit is added to this application.
+
+        """
+        self.model.add_observer(
+            callable_, 'unit', 'add', self._unit_match_pattern)
+
+    def on_unit_remove(self, callable_):
+        """Add a "unit removed" observer to this entity, which will be called
+        whenever a unit is removed from this application.
+
+        """
+        self.model.add_observer(
+            callable_, 'unit', 'remove', self._unit_match_pattern)
+
     @property
     def units(self):
         return [
@@ -24,7 +45,7 @@ class Application(model.ModelEntity):
         """
         pass
 
-    def add_unit(self, count=1, to=None):
+    async def add_unit(self, count=1, to=None):
         """Add one or more units to this service.
 
         :param int count: Number of units to add
@@ -36,7 +57,24 @@ class Application(model.ModelEntity):
             If None, a new machine is provisioned.
 
         """
-        pass
+        app_facade = client.ApplicationFacade()
+        app_facade.connect(self.connection)
+
+        log.debug(
+            'Adding %s unit%s to %s',
+            count, '' if count == 1 else 's', self.name)
+
+        result = await app_facade.AddUnits(
+            application=self.name,
+            placement=to,
+            num_units=count,
+        )
+
+        return await asyncio.gather(*[
+            asyncio.ensure_future(self.model._wait_for_new('unit', unit_id))
+            for unit_id in result.units
+        ])
+
     add_units = add_unit
 
     def allocate(self, budget, value):
@@ -87,11 +125,17 @@ class Application(model.ModelEntity):
         return await app_facade.Destroy(self.name)
     remove = destroy
 
-    def expose(self):
+    async def expose(self):
         """Make this service publicly available over the network.
 
         """
-        pass
+        app_facade = client.ApplicationFacade()
+        app_facade.connect(self.connection)
+
+        log.debug(
+            'Exposing %s', self.name)
+
+        return await app_facade.Expose(self.name)
 
     def get_config(self):
         """Return the configuration settings for this service.