Raise N2VCNotFound exception in delete_namespace
[osm/N2VC.git] / n2vc / k8s_juju_conn.py
index 3acc10e..3316087 100644 (file)
 #     See the License for the specific language governing permissions and
 #     limitations under the License.
 
 #     See the License for the specific language governing permissions and
 #     limitations under the License.
 
+import asyncio
 import concurrent
 from .exceptions import NotImplemented
 
 import concurrent
 from .exceptions import NotImplemented
 
+import io
 import juju
 # from juju.bundle import BundleHandler
 from juju.controller import Controller
 from juju.model import Model
 from juju.errors import JujuAPIError, JujuError
 
 import juju
 # from juju.bundle import BundleHandler
 from juju.controller import Controller
 from juju.model import Model
 from juju.errors import JujuAPIError, JujuError
 
-import logging
-
 from n2vc.k8s_conn import K8sConnector
 
 import os
 # import re
 # import ssl
 from n2vc.k8s_conn import K8sConnector
 
 import os
 # import re
 # import ssl
-import subprocess
 # from .vnf import N2VC
 
 import uuid
 # from .vnf import N2VC
 
 import uuid
@@ -43,7 +42,7 @@ class K8sJujuConnector(K8sConnector):
             db: object,
             kubectl_command: str = '/usr/bin/kubectl',
             juju_command: str = '/usr/bin/juju',
             db: object,
             kubectl_command: str = '/usr/bin/kubectl',
             juju_command: str = '/usr/bin/juju',
-            log=None,
+            log: object = None,
             on_update_db=None,
     ):
         """
             on_update_db=None,
     ):
         """
@@ -63,15 +62,15 @@ class K8sJujuConnector(K8sConnector):
         )
 
         self.fs = fs
         )
 
         self.fs = fs
-        self.info('Initializing K8S Juju connector')
+        self.log.debug('Initializing K8S Juju connector')
 
         self.authenticated = False
         self.models = {}
 
         self.authenticated = False
         self.models = {}
-        self.log = logging.getLogger(__name__)
 
 
+        self.juju_command = juju_command
         self.juju_secret = ""
 
         self.juju_secret = ""
 
-        self.info('K8S Juju connector initialized')
+        self.log.debug('K8S Juju connector initialized')
 
     """Initialization"""
     async def init_env(
 
     """Initialization"""
     async def init_env(
@@ -80,13 +79,14 @@ class K8sJujuConnector(K8sConnector):
         namespace: str = 'kube-system',
         reuse_cluster_uuid: str = None,
     ) -> (str, bool):
         namespace: str = 'kube-system',
         reuse_cluster_uuid: str = None,
     ) -> (str, bool):
-        """Initialize a Kubernetes environment
-
-        :param k8s_creds dict: A dictionary containing the Kubernetes cluster
-        configuration
-        :param namespace str: The Kubernetes namespace to initialize
+        """
+        It prepares a given K8s cluster environment to run Juju bundles.
 
 
-        :return: UUID of the k8s context or raises an exception
+        :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid '.kube/config'
+        :param namespace: optional namespace to be used for juju. By default, 'kube-system' will be used
+        :param reuse_cluster_uuid: existing cluster uuid for reuse
+        :return: uuid of the K8s cluster and True if connector has installed some software in the cluster
+        (on error, an exception will be raised)
         """
 
         """Bootstrapping
         """
 
         """Bootstrapping
@@ -94,10 +94,6 @@ class K8sJujuConnector(K8sConnector):
         Bootstrapping cannot be done, by design, through the API. We need to
         use the CLI tools.
         """
         Bootstrapping cannot be done, by design, through the API. We need to
         use the CLI tools.
         """
-        # TODO: The path may change
-        jujudir = "/usr/local/bin"
-
-        self.k8scli = "{}/juju".format(jujudir)
 
         """
         WIP: Workflow
 
         """
         WIP: Workflow
@@ -121,43 +117,43 @@ class K8sJujuConnector(K8sConnector):
         # TODO: Pull info from db based on the namespace #
         ##################################################
 
         # TODO: Pull info from db based on the namespace #
         ##################################################
 
+        ###################################################
+        # TODO: Make it idempotent, calling add-k8s and   #
+        # bootstrap whenever reuse_cluster_uuid is passed #
+        # as parameter                                    #
+        # `init_env` is called to initialize the K8s      #
+        # cluster for juju. If this initialization fails, #
+        # it can be called again by LCM with the param    #
+        # reuse_cluster_uuid, e.g. to try to fix it.       #
+        ###################################################
+
         if not reuse_cluster_uuid:
             # This is a new cluster, so bootstrap it
 
             cluster_uuid = str(uuid.uuid4())
 
         if not reuse_cluster_uuid:
             # This is a new cluster, so bootstrap it
 
             cluster_uuid = str(uuid.uuid4())
 
-            # Add k8s cloud to Juju (unless it's microk8s)
+            # Is a local k8s cluster?
+            localk8s = self.is_local_k8s(k8s_creds)
 
 
-            # Convert to a dict
-            # k8s_creds = yaml.safe_load(k8s_creds)
+            # If the k8s is external, the juju controller needs a loadbalancer
+            loadbalancer = False if localk8s else True
 
 
-            # Does the kubeconfig contain microk8s?
-            microk8s = self.is_microk8s_by_credentials(k8s_creds)
+            # Name the new k8s cloud
+            k8s_cloud = "k8s-{}".format(cluster_uuid)
 
 
-            if not microk8s:
-                # Name the new k8s cloud
-                k8s_cloud = "{}-k8s".format(namespace)
+            self.log.debug("Adding k8s cloud {}".format(k8s_cloud))
+            await self.add_k8s(k8s_cloud, k8s_creds)
 
 
-                print("Adding k8s cloud {}".format(k8s_cloud))
-                await self.add_k8s(k8s_cloud, k8s_creds)
-
-                # Bootstrap Juju controller
-                print("Bootstrapping...")
-                await self.bootstrap(k8s_cloud, cluster_uuid)
-                print("Bootstrap done.")
-            else:
-                # k8s_cloud = 'microk8s-test'
-                k8s_cloud = "{}-k8s".format(namespace)
-
-                await self.add_k8s(k8s_cloud, k8s_creds)
-
-                await self.bootstrap(k8s_cloud, cluster_uuid)
+            # Bootstrap Juju controller
+            self.log.debug("Bootstrapping...")
+            await self.bootstrap(k8s_cloud, cluster_uuid, loadbalancer)
+            self.log.debug("Bootstrap done.")
 
             # Get the controller information
 
             # Parse ~/.local/share/juju/controllers.yaml
             # controllers.testing.api-endpoints|ca-cert|uuid
 
             # Get the controller information
 
             # Parse ~/.local/share/juju/controllers.yaml
             # controllers.testing.api-endpoints|ca-cert|uuid
-            print("Getting controller endpoints")
+            self.log.debug("Getting controller endpoints")
             with open(os.path.expanduser(
                 "~/.local/share/juju/controllers.yaml"
             )) as f:
             with open(os.path.expanduser(
                 "~/.local/share/juju/controllers.yaml"
             )) as f:
@@ -169,7 +165,7 @@ class K8sJujuConnector(K8sConnector):
 
             # Parse ~/.local/share/juju/accounts
             # controllers.testing.user|password
 
             # Parse ~/.local/share/juju/accounts
             # controllers.testing.user|password
-            print("Getting accounts")
+            self.log.debug("Getting accounts")
             with open(os.path.expanduser(
                 "~/.local/share/juju/accounts.yaml"
             )) as f:
             with open(os.path.expanduser(
                 "~/.local/share/juju/accounts.yaml"
             )) as f:
@@ -179,11 +175,6 @@ class K8sJujuConnector(K8sConnector):
                 self.juju_user = controller['user']
                 self.juju_secret = controller['password']
 
                 self.juju_user = controller['user']
                 self.juju_secret = controller['password']
 
-            print("user: {}".format(self.juju_user))
-            print("secret: {}".format(self.juju_secret))
-            print("endpoint: {}".format(self.juju_endpoint))
-            print("ca-cert: {}".format(self.juju_ca_cert))
-
             # raise Exception("EOL")
 
             self.juju_public_key = None
             # raise Exception("EOL")
 
             self.juju_public_key = None
@@ -194,12 +185,12 @@ class K8sJujuConnector(K8sConnector):
                 'secret': self.juju_secret,
                 'cacert': self.juju_ca_cert,
                 'namespace': namespace,
                 'secret': self.juju_secret,
                 'cacert': self.juju_ca_cert,
                 'namespace': namespace,
-                'microk8s': microk8s,
+                'loadbalancer': loadbalancer,
             }
 
             # Store the cluster configuration so it
             # can be used for subsequent calls
             }
 
             # Store the cluster configuration so it
             # can be used for subsequent calls
-            print("Setting config")
+            self.log.debug("Setting config")
             await self.set_config(cluster_uuid, config)
 
         else:
             await self.set_config(cluster_uuid, config)
 
         else:
@@ -219,15 +210,15 @@ class K8sJujuConnector(K8sConnector):
             await self.login(cluster_uuid)
 
         # We're creating a new cluster
             await self.login(cluster_uuid)
 
         # We're creating a new cluster
-        print("Getting model {}".format(self.get_namespace(cluster_uuid), cluster_uuid=cluster_uuid))
-        model = await self.get_model(
-            self.get_namespace(cluster_uuid), 
-            cluster_uuid=cluster_uuid
-        )
+        #print("Getting model {}".format(self.get_namespace(cluster_uuid), cluster_uuid=cluster_uuid))
+        #model = await self.get_model(
+        #    self.get_namespace(cluster_uuid),
+        #    cluster_uuid=cluster_uuid
+        #)
 
 
-        # Disconnect from the model
-        if model and model.is_connected():
-            await model.disconnect()
+        ## Disconnect from the model
+        #if model and model.is_connected():
+        #    await model.disconnect()
 
         return cluster_uuid, True
 
 
         return cluster_uuid, True
 
@@ -249,6 +240,16 @@ class K8sJujuConnector(K8sConnector):
     ):
         raise NotImplemented()
 
     ):
         raise NotImplemented()
 
+    async def synchronize_repos(
+        self,
+        cluster_uuid: str,
+        name: str
+    ):
+        """
+        Returns None as currently add_repo is not implemented
+        """
+        return None
+
     """Reset"""
     async def reset(
             self,
     """Reset"""
     async def reset(
             self,
@@ -272,34 +273,28 @@ class K8sJujuConnector(K8sConnector):
                 # Destroy the model
                 namespace = self.get_namespace(cluster_uuid)
                 if await self.has_model(namespace):
                 # Destroy the model
                 namespace = self.get_namespace(cluster_uuid)
                 if await self.has_model(namespace):
-                    print("[reset] Destroying model")
+                    self.log.debug("[reset] Destroying model")
                     await self.controller.destroy_model(
                         namespace,
                         destroy_storage=True
                     )
 
                 # Disconnect from the controller
                     await self.controller.destroy_model(
                         namespace,
                         destroy_storage=True
                     )
 
                 # Disconnect from the controller
-                print("[reset] Disconnecting controller")
-                await self.controller.disconnect()
+                self.log.debug("[reset] Disconnecting controller")
+                await self.logout()
 
                 # Destroy the controller (via CLI)
 
                 # Destroy the controller (via CLI)
-                print("[reset] Destroying controller")
+                self.log.debug("[reset] Destroying controller")
                 await self.destroy_controller(cluster_uuid)
 
                 await self.destroy_controller(cluster_uuid)
 
-                """Remove the k8s cloud
-
-                Only remove the k8s cloud if it's not a microk8s cloud,
-                since microk8s is a built-in cloud type.
-                """
-                # microk8s = self.is_microk8s_by_cluster_uuid(cluster_uuid)
-                # if not microk8s:
-                print("[reset] Removing k8s cloud")
-                namespace = self.get_namespace(cluster_uuid)
-                k8s_cloud = "{}-k8s".format(namespace)
+                self.log.debug("[reset] Removing k8s cloud")
+                k8s_cloud = "k8s-{}".format(cluster_uuid)
                 await self.remove_cloud(k8s_cloud)
 
         except Exception as ex:
                 await self.remove_cloud(k8s_cloud)
 
         except Exception as ex:
-            print("Caught exception during reset: {}".format(ex))
+            self.log.debug("Caught exception during reset: {}".format(ex))
+
+        return True
 
     """Deployment"""
 
 
     """Deployment"""
 
@@ -310,7 +305,8 @@ class K8sJujuConnector(K8sConnector):
         atomic: bool = True,
         timeout: float = 300,
         params: dict = None,
         atomic: bool = True,
         timeout: float = 300,
         params: dict = None,
-        db_dict: dict = None
+        db_dict: dict = None,
+        kdu_name: str = None
     ) -> bool:
         """Install a bundle
 
     ) -> bool:
         """Install a bundle
 
@@ -321,25 +317,28 @@ class K8sJujuConnector(K8sConnector):
         :param timeout int: The time, in seconds, to wait for the install
                             to finish
         :param params dict: Key-value pairs of instantiation parameters
         :param timeout int: The time, in seconds, to wait for the install
                             to finish
         :param params dict: Key-value pairs of instantiation parameters
+        :param kdu_name: Name of the KDU instance to be installed
 
         :return: If successful, returns ?
         """
 
         if not self.authenticated:
 
         :return: If successful, returns ?
         """
 
         if not self.authenticated:
-            print("[install] Logging in to the controller")
+            self.log.debug("[install] Logging in to the controller")
             await self.login(cluster_uuid)
 
         ##
             await self.login(cluster_uuid)
 
         ##
-        # Get or create the model, based on the namespace the cluster was
-        # instantiated with.
-        namespace = self.get_namespace(cluster_uuid)
-        namespace = "gitlab-demo"
-        self.log.debug("Checking for model named {}".format(namespace))
-        model = await self.get_model(namespace, cluster_uuid=cluster_uuid)
-        if not model:
-            # Create the new model
-            self.log.debug("Adding model: {}".format(namespace))
-            model = await self.add_model(namespace, cluster_uuid=cluster_uuid)
+        # Get or create the model, based on the NS
+        # uuid.
+        if kdu_name:
+            kdu_instance = "{}-{}".format(kdu_name, db_dict["filter"]["_id"])
+        else:
+            kdu_instance = db_dict["filter"]["_id"]
+
+        self.log.debug("Checking for model named {}".format(kdu_instance))
+
+        # Create the new model
+        self.log.debug("Adding model: {}".format(kdu_instance))
+        model = await self.add_model(kdu_instance, cluster_uuid=cluster_uuid)
 
         if model:
             # TODO: Instantiation parameters
 
         if model:
             # TODO: Instantiation parameters
@@ -352,6 +351,8 @@ class K8sJujuConnector(K8sConnector):
                 - <URL_where_to_fetch_juju_bundle>
             """
 
                 - <URL_where_to_fetch_juju_bundle>
             """
 
+            previous_workdir = os.getcwd()
+
             bundle = kdu_model
             if kdu_model.startswith("cs:"):
                 bundle = kdu_model
             bundle = kdu_model
             if kdu_model.startswith("cs:"):
                 bundle = kdu_model
@@ -359,31 +360,30 @@ class K8sJujuConnector(K8sConnector):
                 # Download the file
                 pass
             else:
                 # Download the file
                 pass
             else:
-                # Local file
+                new_workdir = kdu_model.strip(kdu_model.split("/")[-1])
 
 
-                # if kdu_model.endswith(".tar.gz") or kdu_model.endswith(".tgz")
-                # Uncompress temporarily
-                # bundle = <uncompressed file>
-                pass
+                os.chdir(new_workdir)
+
+                bundle = "local:{}".format(kdu_model)
 
             if not bundle:
                 # Raise named exception that the bundle could not be found
                 raise Exception()
 
 
             if not bundle:
                 # Raise named exception that the bundle could not be found
                 raise Exception()
 
-            print("[install] deploying {}".format(bundle))
+            self.log.debug("[install] deploying {}".format(bundle))
             await model.deploy(bundle)
 
             # Get the application
             if atomic:
                 # applications = model.applications
             await model.deploy(bundle)
 
             # Get the application
             if atomic:
                 # applications = model.applications
-                print("[install] Applications: {}".format(model.applications))
+                self.log.debug("[install] Applications: {}".format(model.applications))
                 for name in model.applications:
                 for name in model.applications:
-                    print("[install] Waiting for {} to settle".format(name))
+                    self.log.debug("[install] Waiting for {} to settle".format(name))
                     application = model.applications[name]
                     try:
                         # It's not enough to wait for all units to be active;
                         # the application status needs to be active as well.
                     application = model.applications[name]
                     try:
                         # It's not enough to wait for all units to be active;
                         # the application status needs to be active as well.
-                        print("Waiting for all units to be active...")
+                        self.log.debug("Waiting for all units to be active...")
                         await model.block_until(
                             lambda: all(
                                 unit.agent_status == 'idle'
                         await model.block_until(
                             lambda: all(
                                 unit.agent_status == 'idle'
@@ -394,19 +394,22 @@ class K8sJujuConnector(K8sConnector):
                             ),
                             timeout=timeout
                         )
                             ),
                             timeout=timeout
                         )
-                        print("All units active.")
+                        self.log.debug("All units active.")
 
                     except concurrent.futures._base.TimeoutError:
 
                     except concurrent.futures._base.TimeoutError:
-                        print("[install] Timeout exceeded; resetting cluster")
+                        os.chdir(previous_workdir)
+                        self.log.debug("[install] Timeout exceeded; resetting cluster")
                         await self.reset(cluster_uuid)
                         return False
 
             # Wait for the application to be active
             if model.is_connected():
                         await self.reset(cluster_uuid)
                         return False
 
             # Wait for the application to be active
             if model.is_connected():
-                print("[install] Disconnecting model")
+                self.log.debug("[install] Disconnecting model")
                 await model.disconnect()
 
                 await model.disconnect()
 
-            return True
+            os.chdir(previous_workdir)
+
+            return kdu_instance
         raise Exception("Unable to install")
 
     async def instances_list(
         raise Exception("Unable to install")
 
     async def instances_list(
@@ -478,9 +481,9 @@ class K8sJujuConnector(K8sConnector):
             """
             # TODO: This should be returned in an agreed-upon format
             for name in bundle['applications']:
             """
             # TODO: This should be returned in an agreed-upon format
             for name in bundle['applications']:
-                print(model.applications)
+                self.log.debug(model.applications)
                 application = model.applications[name]
                 application = model.applications[name]
-                print(application)
+                self.log.debug(application)
 
                 path = bundle['applications'][name]['charm']
 
 
                 path = bundle['applications'][name]['charm']
 
@@ -519,35 +522,27 @@ class K8sJujuConnector(K8sConnector):
     async def uninstall(
         self,
         cluster_uuid: str,
     async def uninstall(
         self,
         cluster_uuid: str,
-        kdu_instance: str,
+        kdu_instance: str
     ) -> bool:
         """Uninstall a KDU instance
 
     ) -> bool:
         """Uninstall a KDU instance
 
-        :param cluster_uuid str: The UUID of the cluster to uninstall
+        :param cluster_uuid str: The UUID of the cluster
         :param kdu_instance str: The unique name of the KDU instance
 
         :return: Returns True if successful, or raises an exception
         """
         :param kdu_instance str: The unique name of the KDU instance
 
         :return: Returns True if successful, or raises an exception
         """
-        removed = False
-
-        # Remove an application from the model
-        model = await self.get_model(self.get_namespace(cluster_uuid), cluster_uuid=cluster_uuid)
-
-        if model:
-            # Get the application
-            if kdu_instance not in model.applications:
-                # TODO: Raise a named exception
-                raise Exception("Application not found.")
+        if not self.authenticated:
+            self.log.debug("[uninstall] Connecting to controller")
+            await self.login(cluster_uuid)
 
 
-            application = model.applications[kdu_instance]
+        self.log.debug("[uninstall] Destroying model")
 
 
-            # Destroy the application
-            await application.destroy()
+        await self.controller.destroy_models(kdu_instance)
 
 
-            # TODO: Verify removal
+        self.log.debug("[uninstall] Model destroyed and disconnecting")
+        await self.logout()
 
 
-            removed = True
-        return removed
+        return True
 
     """Introspection"""
     async def inspect_kdu(
 
     """Introspection"""
     async def inspect_kdu(
@@ -668,21 +663,31 @@ class K8sJujuConnector(K8sConnector):
 
         :returns: True if successful, otherwise raises an exception.
         """
 
         :returns: True if successful, otherwise raises an exception.
         """
-        cmd = [self.k8scli, "add-k8s", "--local", cloud_name]
 
 
-        p = subprocess.run(
-            cmd,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            # input=yaml.dump(credentials, Dumper=yaml.Dumper).encode("utf-8"),
-            input=credentials.encode("utf-8"),
-            # encoding='ascii'
+        cmd = [self.juju_command, "add-k8s", "--local", cloud_name]
+        self.log.debug(cmd)
+
+        process = await asyncio.create_subprocess_exec(
+            *cmd,
+            stdout=asyncio.subprocess.PIPE,
+            stderr=asyncio.subprocess.PIPE,
+            stdin=asyncio.subprocess.PIPE,
         )
         )
-        retcode = p.returncode
-        print("add-k8s return code: {}".format(retcode))
 
 
-        if retcode > 0:
-            raise Exception(p.stderr)
+        # Feed the process the credentials
+        process.stdin.write(credentials.encode("utf-8"))
+        await process.stdin.drain()
+        process.stdin.close()
+
+        stdout, stderr = await process.communicate()
+
+        return_code = process.returncode
+
+        self.log.debug("add-k8s return code: {}".format(return_code))
+
+        if return_code > 0:
+            raise Exception(stderr)
+
         return True
 
     async def add_model(
         return True
 
     async def add_model(
@@ -702,16 +707,23 @@ class K8sJujuConnector(K8sConnector):
             await self.login(cluster_uuid)
 
         self.log.debug("Adding model '{}' to cluster_uuid '{}'".format(model_name, cluster_uuid))
             await self.login(cluster_uuid)
 
         self.log.debug("Adding model '{}' to cluster_uuid '{}'".format(model_name, cluster_uuid))
-        model = await self.controller.add_model(
-            model_name,
-            config={'authorized-keys': self.juju_public_key}
-        )
+        try:
+            model = await self.controller.add_model(
+                model_name,
+                config={'authorized-keys': self.juju_public_key}
+            )
+        except Exception as ex:
+            self.log.debug(ex)
+            self.log.debug("Caught exception: {}".format(ex))
+            pass
+
         return model
 
     async def bootstrap(
         self,
         cloud_name: str,
         return model
 
     async def bootstrap(
         self,
         cloud_name: str,
-        cluster_uuid: str
+        cluster_uuid: str,
+        loadbalancer: bool
     ) -> bool:
         """Bootstrap a Kubernetes controller
 
     ) -> bool:
         """Bootstrap a Kubernetes controller
 
@@ -719,25 +731,36 @@ class K8sJujuConnector(K8sConnector):
 
         :param cloud_name str: The name of the cloud.
         :param cluster_uuid str: The UUID of the cluster to bootstrap.
 
         :param cloud_name str: The name of the cloud.
         :param cluster_uuid str: The UUID of the cluster to bootstrap.
+        :param loadbalancer bool: If the controller should use loadbalancer or not.
         :returns: True upon success or raises an exception.
         """
         :returns: True upon success or raises an exception.
         """
-        cmd = [self.k8scli, "bootstrap", cloud_name, cluster_uuid]
-        print("Bootstrapping controller {} in cloud {}".format(
+
+        if not loadbalancer:
+            cmd = [self.juju_command, "bootstrap", cloud_name, cluster_uuid]
+        else:
+            """
+            For public clusters, specify that the controller service is using a LoadBalancer.
+            """
+            cmd = [self.juju_command, "bootstrap", cloud_name, cluster_uuid, "--config", "controller-service-type=loadbalancer"]
+
+        self.log.debug("Bootstrapping controller {} in cloud {}".format(
             cluster_uuid, cloud_name
         ))
 
             cluster_uuid, cloud_name
         ))
 
-        p = subprocess.run(
-            cmd,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            # encoding='ascii'
+        process = await asyncio.create_subprocess_exec(
+            *cmd,
+            stdout=asyncio.subprocess.PIPE,
+            stderr=asyncio.subprocess.PIPE,
         )
         )
-        retcode = p.returncode
 
 
-        if retcode > 0:
+        stdout, stderr = await process.communicate()
+
+        return_code = process.returncode
+
+        if return_code > 0:
             #
             #
-            if b'already exists' not in p.stderr:
-                raise Exception(p.stderr)
+            if b'already exists' not in stderr:
+                raise Exception(stderr)
 
         return True
 
 
         return True
 
@@ -753,7 +776,7 @@ class K8sJujuConnector(K8sConnector):
         :returns: True upon success or raises an exception.
         """
         cmd = [
         :returns: True upon success or raises an exception.
         """
         cmd = [
-            self.k8scli,
+            self.juju_command,
             "destroy-controller",
             "--destroy-all-models",
             "--destroy-storage",
             "destroy-controller",
             "--destroy-all-models",
             "--destroy-storage",
@@ -761,18 +784,20 @@ class K8sJujuConnector(K8sConnector):
             cluster_uuid
         ]
 
             cluster_uuid
         ]
 
-        p = subprocess.run(
-            cmd,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            # encoding='ascii'
+        process = await asyncio.create_subprocess_exec(
+            *cmd,
+            stdout=asyncio.subprocess.PIPE,
+            stderr=asyncio.subprocess.PIPE,
         )
         )
-        retcode = p.returncode
 
 
-        if retcode > 0:
+        stdout, stderr = await process.communicate()
+
+        return_code = process.returncode
+
+        if return_code > 0:
             #
             #
-            if 'already exists' not in p.stderr:
-                raise Exception(p.stderr)
+            if 'already exists' not in stderr:
+                raise Exception(stderr)
 
     def get_config(
         self,
 
     def get_config(
         self,
@@ -861,36 +886,26 @@ class K8sJujuConnector(K8sConnector):
             return True
         return False
 
             return True
         return False
 
-    def is_microk8s_by_cluster_uuid(
-        self,
-        cluster_uuid: str,
-    ) -> bool:
-        """Check if a cluster is micro8s
-
-        Checks if a cluster is running microk8s
-
-        :param cluster_uuid str: The UUID of the cluster
-        :returns: A boolean if the cluster is running microk8s
-        """
-        config = self.get_config(cluster_uuid)
-        return config['microk8s']
-
-    def is_microk8s_by_credentials(
+    def is_local_k8s(
         self,
         credentials: str,
     ) -> bool:
         self,
         credentials: str,
     ) -> bool:
-        """Check if a cluster is micro8s
+        """Check if a cluster is local
 
 
-        Checks if a cluster is running microk8s
+        Checks if a cluster is running in the local host
 
         :param credentials dict: A dictionary containing the k8s credentials
 
         :param credentials dict: A dictionary containing the k8s credentials
-        :returns: A boolean if the cluster is running microk8s
+        :returns: A boolean if the cluster is running locally
         """
         creds = yaml.safe_load(credentials)
         """
         creds = yaml.safe_load(credentials)
-        if creds:
-            for context in creds['contexts']:
-                if 'microk8s' in context['name']:
-                    return True
+        if os.getenv("OSMLCM_VCA_APIPROXY"):
+            host_ip = os.getenv("OSMLCM_VCA_APIPROXY")
+
+        if creds and host_ip:
+            for cluster in creds['clusters']:
+                if 'server' in cluster['cluster']:
+                    if host_ip in cluster['cluster']['server']:
+                        return True
 
         return False
 
 
         return False
 
@@ -931,7 +946,7 @@ class K8sJujuConnector(K8sConnector):
                 self.authenticated = True
                 self.log.debug("JujuApi: Logged into controller")
             except Exception as ex:
                 self.authenticated = True
                 self.log.debug("JujuApi: Logged into controller")
             except Exception as ex:
-                print(ex)
+                self.log.debug(ex)
                 self.log.debug("Caught exception: {}".format(ex))
                 pass
         else:
                 self.log.debug("Caught exception: {}".format(ex))
                 pass
         else:
@@ -940,12 +955,12 @@ class K8sJujuConnector(K8sConnector):
 
     async def logout(self):
         """Logout of the Juju controller."""
 
     async def logout(self):
         """Logout of the Juju controller."""
-        print("[logout]")
+        self.log.debug("[logout]")
         if not self.authenticated:
             return False
 
         for model in self.models:
         if not self.authenticated:
             return False
 
         for model in self.models:
-            print("Logging out of model {}".format(model))
+            self.log.debug("Logging out of model {}".format(model))
             await self.models[model].disconnect()
 
         if self.controller:
             await self.models[model].disconnect()
 
         if self.controller:
@@ -971,31 +986,34 @@ class K8sJujuConnector(K8sConnector):
         """
 
         # Remove the bootstrapped controller
         """
 
         # Remove the bootstrapped controller
-        cmd = [self.k8scli, "remove-k8s", "--client", cloud_name]
-        p = subprocess.run(
-            cmd,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            # encoding='ascii'
+        cmd = [self.juju_command, "remove-k8s", "--client", cloud_name]
+        process = await asyncio.create_subprocess_exec(
+            *cmd,
+            stdout=asyncio.subprocess.PIPE,
+            stderr=asyncio.subprocess.PIPE,
         )
         )
-        retcode = p.returncode
 
 
-        if retcode > 0:
-            raise Exception(p.stderr)
+        stdout, stderr = await process.communicate()
+
+        return_code = process.returncode
+
+        if return_code > 0:
+            raise Exception(stderr)
 
         # Remove the cloud from the local config
 
         # Remove the cloud from the local config
-        cmd = [self.k8scli, "remove-cloud", "--client", cloud_name]
-        p = subprocess.run(
-            cmd,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            # encoding='ascii'
+        cmd = [self.juju_command, "remove-cloud", "--client", cloud_name]
+        process = await asyncio.create_subprocess_exec(
+            *cmd,
+            stdout=asyncio.subprocess.PIPE,
+            stderr=asyncio.subprocess.PIPE,
         )
         )
-        retcode = p.returncode
 
 
-        if retcode > 0:
-            raise Exception(p.stderr)
+        stdout, stderr = await process.communicate()
+
+        return_code = process.returncode
 
 
+        if return_code > 0:
+            raise Exception(stderr)
 
         return True
 
 
         return True
 
@@ -1015,7 +1033,7 @@ class K8sJujuConnector(K8sConnector):
 
         cluster_config = "{}/{}.yaml".format(self.fs.path, cluster_uuid)
         if not os.path.exists(cluster_config):
 
         cluster_config = "{}/{}.yaml".format(self.fs.path, cluster_uuid)
         if not os.path.exists(cluster_config):
-            print("Writing config to {}".format(cluster_config))
+            self.log.debug("Writing config to {}".format(cluster_config))
             with open(cluster_config, 'w') as f:
                 f.write(yaml.dump(config, Dumper=yaml.Dumper))
 
             with open(cluster_config, 'w') as f:
                 f.write(yaml.dump(config, Dumper=yaml.Dumper))