Add N2VCNotFound exception
[osm/N2VC.git] / n2vc / n2vc_juju_conn.py
index 4f0ee37..6b8ac09 100644 (file)
@@ -32,7 +32,7 @@ from n2vc.n2vc_conn import N2VCConnector
 from n2vc.n2vc_conn import obj_to_dict, obj_to_yaml
 from n2vc.exceptions \
     import N2VCBadArgumentsException, N2VCException, N2VCConnectionException, \
-    N2VCExecutionException, N2VCInvalidCertificate
+    N2VCExecutionException, N2VCInvalidCertificate, N2VCNotFound
 from n2vc.juju_observer import JujuModelObserver
 
 from juju.controller import Controller
@@ -168,6 +168,7 @@ class N2VCJujuConnector(N2VCConnector):
         else:
             self.apt_mirror = None
 
+        self.cloud = vca_config.get('cloud')
         self.log.debug('Arguments have been checked')
 
         # juju data
@@ -337,7 +338,8 @@ class N2VCJujuConnector(N2VCConnector):
         artifact_path: str,
         db_dict: dict,
         progress_timeout: float = None,
-        total_timeout: float = None
+        total_timeout: float = None,
+        config: dict = None,
     ):
 
         self.log.info('Installing configuration sw on ee_id: {}, artifact path: {}, db_dict: {}'
@@ -385,7 +387,8 @@ class N2VCJujuConnector(N2VCConnector):
                 machine_id=machine_id,
                 db_dict=db_dict,
                 progress_timeout=progress_timeout,
-                total_timeout=total_timeout
+                total_timeout=total_timeout,
+                config=config
             )
         except Exception as e:
             raise N2VCException(message='Error desploying charm into ee={} : {}'.format(ee_id, e))
@@ -585,15 +588,15 @@ class N2VCJujuConnector(N2VCConnector):
                                 .format(ee_id, application_name, e))
 
         # destroy the machine
-        try:
-            await self._juju_destroy_machine(
-                model_name=model_name,
-                machine_id=machine_id,
-                total_timeout=total_timeout
-            )
-        except Exception as e:
-            raise N2VCException(message='Error deleting execution environment {} (machine {}) : {}'
-                                .format(ee_id, machine_id, e))
+        # try: 
+            await self._juju_destroy_machine(
+                model_name=model_name,
+                machine_id=machine_id,
+                total_timeout=total_timeout
+            )
+        except Exception as e:
+            raise N2VCException(message='Error deleting execution environment {} (machine {}) : {}'
+                                .format(ee_id, machine_id, e))
 
         self.log.info('Execution environment {} deleted'.format(ee_id))
 
@@ -953,7 +956,8 @@ class N2VCJujuConnector(N2VCConnector):
             machine_id: str,
             db_dict: dict,
             progress_timeout: float = None,
-            total_timeout: float = None
+            total_timeout: float = None,
+            config: dict = None
     ) -> (Application, int):
 
         # get juju model and observer
@@ -979,7 +983,8 @@ class N2VCJujuConnector(N2VCConnector):
                 channel='stable',
                 num_units=1,
                 series=series,
-                to=machine_id
+                to=machine_id,
+                config=config
             )
 
             # register application with observer
@@ -1024,7 +1029,10 @@ class N2VCJujuConnector(N2VCConnector):
 
         application = await self._juju_get_application(model_name=model_name, application_name=application_name)
 
-        unit = application.units[0]
+        unit = None
+        for u in application.units:
+            if await u.is_leader_from_status():
+                unit = u
         if unit is not None:
             actions = await application.get_actions()
             if action_name in actions:
@@ -1166,7 +1174,8 @@ class N2VCJujuConnector(N2VCConnector):
 
                 model = await self.controller.add_model(
                     model_name=model_name,
-                    config=config_dict
+                    config=config_dict,
+                    cloud_name=self.cloud,
                 )
                 self.log.info('New model created, name={}'.format(model_name))
             else:
@@ -1223,9 +1232,11 @@ class N2VCJujuConnector(N2VCConnector):
 
         # get juju model and observer
         model = await self._juju_get_model(model_name=model_name)
+        observer = self.juju_observers[model_name]
 
         application = model.applications.get(application_name)
         if application:
+            observer.unregister_application(application_name)
             await application.destroy()
         else:
             self.log.debug('Application not found: {}'.format(application_name))
@@ -1244,20 +1255,28 @@ class N2VCJujuConnector(N2VCConnector):
 
         # get juju model and observer
         model = await self._juju_get_model(model_name=model_name)
+        observer = self.juju_observers[model_name]
 
         machines = await model.get_machines()
         if machine_id in machines:
             machine = model.machines[machine_id]
-            await machine.destroy(force=True)
-            # max timeout
-            end = time.time() + total_timeout
-            # wait for machine removal
-            machines = await model.get_machines()
-            while machine_id in machines and time.time() < end:
-                self.log.debug('Waiting for machine {} is destroyed'.format(machine_id))
-                await asyncio.sleep(0.5)
+            observer.unregister_machine(machine_id)
+            # TODO: change this by machine.is_manual when this is upstreamed: https://github.com/juju/python-libjuju/pull/396
+            if "instance-id" in machine.safe_data and machine.safe_data[
+                "instance-id"
+            ].startswith("manual:"):
+                self.log.debug("machine.destroy(force=True) started.")
+                await machine.destroy(force=True)
+                self.log.debug("machine.destroy(force=True) passed.")
+                # max timeout
+                end = time.time() + total_timeout
+                # wait for machine removal
                 machines = await model.get_machines()
-            self.log.debug('Machine destroyed: {}'.format(machine_id))
+                while machine_id in machines and time.time() < end:
+                    self.log.debug("Waiting for machine {} is destroyed".format(machine_id))
+                    await asyncio.sleep(0.5)
+                    machines = await model.get_machines()
+                self.log.debug("Machine destroyed: {}".format(machine_id))
         else:
             self.log.debug('Machine not found: {}'.format(machine_id))
 
@@ -1273,8 +1292,27 @@ class N2VCJujuConnector(N2VCConnector):
             total_timeout = 3600
 
         model = await self._juju_get_model(model_name=model_name)
+
+        if not model:
+            raise N2VCNotFound(
+                message="Model {} does not exist".format(model_name)
+            )
+
         uuid = model.info.uuid
 
+        # destroy applications
+        for application_name in model.applications:
+            try:
+                await self._juju_destroy_application(model_name=model_name, application_name=application_name)
+            except Exception as e:
+                self.log.error(
+                    "Error destroying application {} in model {}: {}".format(
+                        application_name,
+                        model_name,
+                        e
+                    )
+                )
+
         # destroy machines
         machines = await model.get_machines()
         for machine_id in machines:
@@ -1285,8 +1323,6 @@ class N2VCJujuConnector(N2VCConnector):
                 pass
 
         await self._juju_disconnect_model(model_name=model_name)
-        self.juju_models[model_name] = None
-        self.juju_observers[model_name] = None
 
         self.log.debug('destroying model {}...'.format(model_name))
         await self.controller.destroy_model(uuid)