Merge "Bug 1000: Fix authentication when deleting service"
[osm/N2VC.git] / n2vc / n2vc_juju_conn.py
index ad001f2..e0f1824 100644 (file)
@@ -41,6 +41,7 @@ from juju.application import Application
 from juju.action import Action
 from juju.machine import Machine
 from juju.client import client
 from juju.action import Action
 from juju.machine import Machine
 from juju.client import client
+from juju.errors import JujuAPIError
 
 from n2vc.provisioner import SSHProvisioner
 
 
 from n2vc.provisioner import SSHProvisioner
 
@@ -157,6 +158,16 @@ class N2VCJujuConnector(N2VCConnector):
         else:
             self.warning('api_proxy is not configured. Support for native charms is disabled')
 
         else:
             self.warning('api_proxy is not configured. Support for native charms is disabled')
 
+        if 'enable_os_upgrade' in vca_config:
+            self.enable_os_upgrade = vca_config['enable_os_upgrade']
+        else:
+            self.enable_os_upgrade = True
+
+        if 'apt_mirror' in vca_config:
+            self.apt_mirror = vca_config['apt_mirror']
+        else:
+            self.apt_mirror = None
+
         self.debug('Arguments have been checked')
 
         # juju data
         self.debug('Arguments have been checked')
 
         # juju data
@@ -457,10 +468,28 @@ class N2VCJujuConnector(N2VCConnector):
         self.debug('adding new relation between {} and {}, endpoints: {}, {}'
                    .format(ee_id_1, ee_id_2, endpoint_1, endpoint_2))
 
         self.debug('adding new relation between {} and {}, endpoints: {}, {}'
                    .format(ee_id_1, ee_id_2, endpoint_1, endpoint_2))
 
+        # check arguments
+        if not ee_id_1:
+            message = 'EE 1 is mandatory'
+            self.error(message)
+            raise N2VCBadArgumentsException(message=message, bad_args=['ee_id_1'])
+        if not ee_id_2:
+            message = 'EE 2 is mandatory'
+            self.error(message)
+            raise N2VCBadArgumentsException(message=message, bad_args=['ee_id_2'])
+        if not endpoint_1:
+            message = 'endpoint 1 is mandatory'
+            self.error(message)
+            raise N2VCBadArgumentsException(message=message, bad_args=['endpoint_1'])
+        if not endpoint_2:
+            message = 'endpoint 2 is mandatory'
+            self.error(message)
+            raise N2VCBadArgumentsException(message=message, bad_args=['endpoint_2'])
+
         if not self._authenticated:
             await self._juju_login()
 
         if not self._authenticated:
             await self._juju_login()
 
-        # get model, application and machines
+        # get the model, the applications and the machines from the ee_id's
         model_1, app_1, machine_1 = self._get_ee_id_components(ee_id_1)
         model_2, app_2, machine_2 = self._get_ee_id_components(ee_id_2)
 
         model_1, app_1, machine_1 = self._get_ee_id_components(ee_id_1)
         model_2, app_2, machine_2 = self._get_ee_id_components(ee_id_2)
 
@@ -472,7 +501,13 @@ class N2VCJujuConnector(N2VCConnector):
 
         # add juju relations between two applications
         try:
 
         # add juju relations between two applications
         try:
-            self._juju_add_relation()
+            await self._juju_add_relation(
+                model_name=model_1,
+                application_name_1=app_1,
+                application_name_2=app_2,
+                relation_1=endpoint_1,
+                relation_2=endpoint_2
+            )
         except Exception as e:
             message = 'Error adding relation between {} and {}'.format(ee_id_1, ee_id_2)
             self.error(message)
         except Exception as e:
             message = 'Error adding relation between {} and {}'.format(ee_id_1, ee_id_2)
             self.error(message)
@@ -1093,6 +1128,7 @@ class N2VCJujuConnector(N2VCConnector):
 
     async def _juju_get_model(self, model_name: str) -> Model:
         """ Get a model object from juju controller
 
     async def _juju_get_model(self, model_name: str) -> Model:
         """ Get a model object from juju controller
+        If the model does not exits, it creates it.
 
         :param str model_name: name of the model
         :returns Model: model obtained from juju controller or Exception
 
         :param str model_name: name of the model
         :returns Model: model obtained from juju controller or Exception
@@ -1121,9 +1157,16 @@ class N2VCJujuConnector(N2VCConnector):
 
             if model_name not in model_list:
                 self.info('Model {} does not exist. Creating new model...'.format(model_name))
 
             if model_name not in model_list:
                 self.info('Model {} does not exist. Creating new model...'.format(model_name))
+                config_dict = {'authorized-keys': self.public_key}
+                if self.apt_mirror:
+                    config_dict['apt-mirror'] = self.apt_mirror
+                if not self.enable_os_upgrade:
+                    config_dict['enable-os-refresh-update'] = False
+                    config_dict['enable-os-upgrade'] = False
+
                 model = await self.controller.add_model(
                     model_name=model_name,
                 model = await self.controller.add_model(
                     model_name=model_name,
-                    config={'authorized-keys': self.public_key}
+                    config=config_dict
                 )
                 self.info('New model created, name={}'.format(model_name))
             else:
                 )
                 self.info('New model created, name={}'.format(model_name))
             else:
@@ -1151,14 +1194,24 @@ class N2VCJujuConnector(N2VCConnector):
             relation_2: str
     ):
 
             relation_2: str
     ):
 
-        self.debug('adding relation')
-
         # get juju model and observer
         model = await self._juju_get_model(model_name=model_name)
 
         r1 = '{}:{}'.format(application_name_1, relation_1)
         r2 = '{}:{}'.format(application_name_2, relation_2)
         # get juju model and observer
         model = await self._juju_get_model(model_name=model_name)
 
         r1 = '{}:{}'.format(application_name_1, relation_1)
         r2 = '{}:{}'.format(application_name_2, relation_2)
-        await model.add_relation(relation1=r1, relation2=r2)
+
+        self.debug('adding relation: {} -> {}'.format(r1, r2))
+        try:
+            await model.add_relation(relation1=r1, relation2=r2)
+        except JujuAPIError as e:
+            # If one of the applications in the relationship doesn't exist, or the relation has already been added,
+            # let the operation fail silently.
+            if 'not found' in e.message:
+                return
+            if 'already exists' in e.message:
+                return
+            # another execption, raise it
+            raise e
 
     async def _juju_destroy_application(
         self,
 
     async def _juju_destroy_application(
         self,
@@ -1222,6 +1275,15 @@ class N2VCJujuConnector(N2VCConnector):
         model = await self._juju_get_model(model_name=model_name)
         uuid = model.info.uuid
 
         model = await self._juju_get_model(model_name=model_name)
         uuid = model.info.uuid
 
+        # destroy machines
+        machines = await model.get_machines()
+        for machine_id in machines:
+            try:
+                await self._juju_destroy_machine(model_name=model_name, machine_id=machine_id)
+            except Exception as e:
+                # ignore exceptions destroying machine
+                pass
+
         await self._juju_disconnect_model(model_name=model_name)
         self.juju_models[model_name] = None
         self.juju_observers[model_name] = None
         await self._juju_disconnect_model(model_name=model_name)
         self.juju_models[model_name] = None
         self.juju_observers[model_name] = None