Merge "Refactor JujuApi to use libjuju asyncronous API"
authorvelandy <rajesh.velandy@riftio.com>
Thu, 5 Oct 2017 16:23:08 +0000 (18:23 +0200)
committerGerrit Code Review <root@osm.etsi.org>
Thu, 5 Oct 2017 16:23:08 +0000 (18:23 +0200)
common/python/rift/mano/config_agent/operdata.py
common/python/rift/mano/utils/juju_api.py [changed mode: 0644->0755]
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py

index 83912db..e89a823 100644 (file)
@@ -16,7 +16,6 @@
 
 import asyncio
 import concurrent.futures
-import gi
 import time
 import gi
 
@@ -39,6 +38,7 @@ import rift.mano.utils.juju_api as juju
 class ConfigAgentAccountNotFound(Exception):
     pass
 
+
 class JujuClient(object):
     def __init__(self, log, ip, port, user, passwd):
         self._log = log
@@ -51,29 +51,39 @@ class JujuClient(object):
                                  server=ip, port=port,
                                  user=user, secret=passwd)
 
-
     def validate_account_creds(self):
-        status = RwcalYang.YangData_Rwcal_ConnectionStatus()
+        """Validate the account credentials.
+
+        Verifies if the account credentials can connect and login to a Juju
+        controller at the provided IP address.
+        """
+        status = "unknown"
+        details = "Connection status not known."
+
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
         try:
-            env = self._api._get_env()
-        except juju.JujuEnvError as e:
-            msg = "JujuClient: Invalid account credentials: %s", str(e)
-            self._log.error(msg)
-            raise Exception(msg)
-        except ConnectionRefusedError as e:
-            msg = "JujuClient: Wrong IP or Port: %s", str(e)
-            self._log.error(msg)
-            raise Exception(msg)
+            loop.run_until_complete(asyncio.gather(
+                self._api.logout(),
+                self._api.login(),
+                loop=loop,
+            ))
         except Exception as e:
             msg = "JujuClient: Connection Failed: %s", str(e)
             self._log.error(msg)
             raise Exception(msg)
         else:
-            status.status = "success"
-            status.details = "Connection was successful"
+            self._log.error("Success reached.")
+            status = "success"
+            details = "Connection was successful"
             self._log.info("JujuClient: Connection Successful")
+        finally:
+            loop.close()
 
-        return status
+        return RwConfigAgentYang.YangData_RwProject_Project_ConfigAgent_Account_ConnectionStatus(
+            status="unknown",
+            details="Connection status lookup not started"
+        )
 
 
 class ConfigAgentAccount(object):
@@ -136,7 +146,7 @@ class ConfigAgentAccount(object):
             try:
                 status = yield from loop.run_in_executor(
                     None,
-                    self._cfg_agent_client_plugin.validate_account_creds
+                    self._cfg_agent_client_plugin.validate_account_creds,
                     )
                 self._status = RwConfigAgentYang.YangData_RwProject_Project_ConfigAgent_Account_ConnectionStatus.from_dict(status.as_dict())
             except Exception as e:
old mode 100644 (file)
new mode 100755 (executable)
index b452b8a..40ff14c
 
 import argparse
 import asyncio
-from functools import partial
 import logging
 import os
 import ssl
-import sys
-import time
 
-try:
-    from jujuclient.juju1.environment import Environment as Env1
-    from jujuclient.juju2.environment import Environment as Env2
-except ImportError as e:
-    # Try importing older jujuclient
-    from jujuclient import Environment as Env1
+import juju.loop
+from juju.controller import Controller
+from juju.model import Model, ModelObserver
 
 try:
     ssl._create_default_https_context = ssl._create_unverified_context
@@ -102,27 +96,62 @@ class JujuActionExecError(JujuActionError):
     pass
 
 
+class JujuAuthenticationError(Exception):
+    pass
+
+
+class JujuMonitor(ModelObserver):
+    """Monitor state changes within the Juju Model."""
+    # async def on_change(self, delta, old, new, model):
+    #     """React to changes in the Juju model."""
+    #
+    #     # TODO: Setup the hook to update the UI if the status of a unit changes
+    #     # to be used when deploying a charm and waiting for it to be "ready"
+    #     if delta.entity in ['application', 'unit'] and delta.type == "change":
+    #         pass
+    #
+    #     # TODO: Add a hook when an action is complete
+
+    pass
+
+
 class JujuApi(object):
-    '''
-    JujuApi wrapper on jujuclient library
+    """JujuApi wrapper on jujuclient library.
 
     There should be one instance of JujuApi for each VNF manged by Juju.
 
     Assumption:
         Currently we use one unit per service/VNF. So once a service
         is deployed, we store the unit name and reuse it
-'''
+    """
+
     log = None
+    controller = None
+    models = {}
+    model = None
+    model_name = None
+    model_uuid = None
+    authenticated = False
+
+    def __init__(self,
+                 log=None,
+                 loop=None,
+                 server='127.0.0.1',
+                 port=17070,
+                 user='admin',
+                 secret=None,
+                 version=None,
+                 model_name='default',
+                 ):
+        """Initialize with the Juju credentials."""
+
+        if log:
+            self.log = log
+        else:
+            self.log = logging.getLogger(__name__)
+
+        self.log.debug('JujuApi instantiated')
 
-    def __init__ (self,
-                  log=None,
-                  loop=None,
-                  server='127.0.0.1',
-                  port=17070,
-                  user='admin',
-                  secret=None,
-                  version=None):
-        '''Initialize with the Juju credentials'''
         self.server = server
         self.port = port
 
@@ -132,939 +161,502 @@ class JujuApi(object):
         else:
             self.user = 'user-{}'.format(user)
 
-        self.loop = loop
+        self.endpoint = '%s:%d' % (server, int(port))
+
+        self.model_name = model_name
+
+        if loop:
+            self.loop = loop
+
+    def __del__(self):
+        """Close any open connections."""
+        yield self.logout()
+
+    async def apply_config(self, config, application):
+        """Apply a configuration to the application."""
+        return await self.set_config(application=application, config=config)
+
+    async def deploy_application(self, charm, name="", path=""):
+        """Deploy an application."""
+        if not self.authenticated:
+            await self.login()
+
+        app = await self.get_application(name)
+        if app is None:
+            # TODO: Handle the error if the charm isn't found.
+            app = await self.model.deploy(
+                path,
+                application_name=name,
+                series='xenial',
+            )
+        return app
+    deploy_service = deploy_application
+
+    async def get_action_status(self, uuid):
+        """Get the status of an action."""
+        if not self.authenticated:
+            await self.login()
+
+        action = await self.model.wait_for_action(uuid)
+        return action.status
+
+    async def get_application(self, application):
+        """Get the deployed application."""
+        if not self.authenticated:
+            await self.login()
+
+        app = None
+        if application in self.model.applications:
+            app = self.model.applications[application]
+        return app
+
+    async def get_application_status(self, application, status=None):
+        """Get the status of an application."""
+        if not self.authenticated:
+            await self.login()
+
+        status = None
+        app = await self.get_application(application)
+        if app:
+            status = app.status
 
-        if log is not None:
-            self.log = log
-        else:
-            self.log = JujuApi._get_logger()
+        return status
+    get_service_status = get_application_status
 
-        if self.log is None:
-            raise JujuApiError("Logger not defined")
+    async def get_config(self, application):
+        """Get the configuration of an application."""
+        if not self.authenticated:
+            await self.login()
 
-        self.version = None
-        if version:
-            self.version = version
-        else:
-            try:
-                if Env2:
-                    pass
-            except NameError:
-                self.log.warn("Using older version of Juju client, which " \
-                              "supports only Juju 1.x")
-                self.version = 1
-
-        endpoint = 'wss://%s:%d' % (server, int(port))
-        self.endpoint = endpoint
-
-        self.charm = None  # Charm used
-        self.service = None  # Service deployed
-        self.units = []  # Storing as list to support more units in future
-
-        self.destroy_retries = 25 # Number retires to destroy service
-        self.retry_delay = 5 # seconds
-
-    def __str__(self):
-        return ("JujuApi-{}".format(self.endpoint))
-
-    @classmethod
-    def _get_logger(cls):
-        if cls.log is not None:
-            return cls.log
-
-        fmt = logging.Formatter(
-            '%(asctime)-23s %(levelname)-5s  (%(name)s@%(process)d:' \
-            '%(filename)s:%(lineno)d) - %(message)s')
-        stderr_handler = logging.StreamHandler(stream=sys.stderr)
-        stderr_handler.setFormatter(fmt)
-        logging.basicConfig(level=logging.DEBUG)
-        cls.log = logging.getLogger('juju-api')
-        cls.log.addHandler(stderr_handler)
-
-        return cls.log
-
-    @staticmethod
-    def format_charm_name(name):
-        '''Format the name to valid charm name
-
-        Charm service name accepts only a to z and -.
-        '''
-
-        new_name = ''
-        for c in name:
-            if c.isdigit():
-                c = chr(97 + int(c))
-            elif not c.isalpha():
-                c = "-"
-            new_name += c
-        return new_name.lower()
-
-    def _get_version_tag(self, tag):
-        version_tag_map = {
-            'applications': {
-                1: 'Services',
-                2: 'applications',
-            },
-            'units': {
-                1: 'Units',
-                2: 'units',
-            },
-            'status': {
-                1: 'Status',
-                2: 'status',
-            },
-            'workload-status': {
-                1: 'Workload',
-                2: 'workload-status',
-            },
-            'charm-url': {
-                1: 'CharmURL',
-                2: 'charm-url',
-            },
-        }
+        config = None
+        app = await self.get_application(application)
+        if app:
+            config = await app.get_config()
 
-        return version_tag_map[tag][self.version]
-
-    def _get_env1(self):
-        try:
-            env = Env1(self.endpoint)
-            l = env.login(self.secret, user=self.user)
-            return env
-
-        except ConnectionRefusedError as e:
-            msg = "{}: Failed Juju 1.x connect: {}".format(self, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise e
-
-        except Exception as e:
-            msg = "{}: Failed Juju 1.x connect: {}".format(self, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuEnvError(msg)
-
-    def _get_env2(self):
-        try:
-            env = Env2(self.endpoint)
-            l = env.login(self.secret, user=self.user)
-        except KeyError as e:
-            msg = "{}: Failed Juju 2.x connect: {}".format(self, e)
-            self.log.debug(msg)
-            raise JujuVersionError(msg)
-
-        try:
-            models = env.models.list()
-            for m in models['user-models']:
-                if m['model']['name'] == 'default':
-                    mep =  '{}/model/{}/api'.format(self.endpoint,
-                                                    m['model']['uuid'])
-                    model = Env2(mep, env_uuid=m['model']['uuid'])
-                    l = model.login(self.secret, user=self.user)
-                    break
-
-            if model is None:
-                raise
-
-            return model
-
-        except Exception as e:
-            msg = "{}: Failed logging to model: {}".format(self, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            env.close()
-            raise JujuModelError(msg)
-
-    def _get_env(self):
-        self.log.debug("{}: Connect to endpoint {}".
-                      format(self, self.endpoint))
-
-        if self.version is None:
-            # Try version 2 first
-            try:
-                env = self._get_env2()
-                self.version = 2
-
-            except JujuVersionError as e:
-                self.log.info("Unable to login as Juju 2.x, trying 1.x")
-                env = self._get_env1()
-                self.version = 1
-
-            return env
-
-        elif self.version == 2:
-            return self._get_env2()
-
-        elif self.version == 1:
-            return self._get_env1()
+        return config
 
-        else:
-            msg = "{}: Unknown version set: {}".format(self, self.version)
-            self.log.error(msg)
-            raise JujuVersionError(msg)
-
-    @asyncio.coroutine
-    def get_env(self):
-        ''' Connect to the Juju controller'''
-        env = yield from self.loop.run_in_executor(
-            None,
-            self._get_env,
-        )
-        return env
-
-    def _get_status(self, env=None):
-        if env is None:
-            env = self._get_env()
-
-        try:
-            status = env.status()
-            return status
-
-        except Exception as e:
-            msg = "{}: exception in getting status: {}". \
-                  format(self, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuStatusError(msg)
-
-    @asyncio.coroutine
-    def get_status(self, env=None):
-        '''Get Juju controller status'''
-        pf = partial(self._get_status, env=env)
-        status = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return status
+    async def get_model(self, name='default'):
+        """Get a model from the Juju Controller.
 
-    def get_all_units(self, status, service=None):
-        '''Parse the status and get the units'''
-        results = {}
-        services = status.get(self._get_version_tag('applications'), {})
-
-        for svc_name, svc_data in services.items():
-            if service and service != svc_name:
-                continue
-            units = svc_data[self._get_version_tag('units')] or {}
-
-            results[svc_name] = {}
-            for unit in units:
-                results[svc_name][unit] = \
-                        units[unit][self._get_version_tag('workload-status')] \
-                        [self._get_version_tag('status')] or None
-        return results
-
-
-    def _get_service_units(self, service=None, status=None, env=None):
-        if service is None:
-            service = self.service
-
-        # Optimizing calls to Juju, as currently we deploy only 1 unit per
-        # service.
-        # if self.service == service and len(self.units):
-        #     return self.units
-
-        if env is None:
-            env = self._get_env()
-
-        if status is None:
-            status = self._get_status(env=env)
-
-        try:
-            resp = self.get_all_units(status, service=service)
-            self.log.debug("Get all units: {}".format(resp))
-            units = set(resp[service].keys())
-
-            if self.service == service:
-                self.units = units
-
-            return units
-
-        except Exception as e:
-            msg = "{}: exception in get units {}".format(self, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuUnitsError(msg)
-
-    @asyncio.coroutine
-    def get_service_units(self, service=None, status=None, env=None):
-        '''Get the unit names for a service'''
-        pf = partial(self._get_service_units,
-                     service=service,
-                     status=status,
-                     env=env)
-        units = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return units
-
-    def _get_service_status(self, service=None, status=None, env=None):
-        if env is None:
-            env = self._get_env()
-
-        if status is None:
-            status = self._get_status(env=env)
-
-        if service is None:
-            service = self.service
-
-        try:
-            srv_status = status[self._get_version_tag('applications')] \
-                         [service][self._get_version_tag('status')] \
-                         [self._get_version_tag('status')]
-            self.log.debug("{}: Service {} status is {}".
-                           format(self, service, srv_status))
-            return srv_status
-
-        except KeyError as e:
-            self.log.info("self: Did not find service {}, e={}".format(self, service, e))
-            return 'NA'
-
-        except Exception as e:
-            msg = "{}: exception checking service status for {}, e {}". \
-                  format(self, service, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuStatusError(msg)
-
-
-    @asyncio.coroutine
-    def get_service_status(self, service=None, status=None, env=None):
-        ''' Get service status
-
-            maintenance : The unit is not yet providing services, but is actively doing stuff.
-            unknown : Service has finished an event but the charm has not called status-set yet.
-            waiting : Service is unable to progress to an active state because of dependency.
-            blocked : Service needs manual intervention to get back to the Running state.
-            active  : Service correctly offering all the services.
-            NA      : Service is not deployed
-        '''
-        pf = partial(self._get_service_status,
-                     service=service,
-                     status=status,
-                     env=env)
-        srv_status = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return srv_status
-
-    def _is_service_deployed(self, service=None, status=None, env=None):
-        resp = self._get_service_status(service=service,
-                                        status=status,
-                                        env=env)
-
-        if resp not in ['terminated', 'NA']:
-            return True
-
-        return False
-
-    @asyncio.coroutine
-    def is_service_deployed(self, service=None, status=None, env=None):
-        '''Check if the service is deployed'''
-        pf = partial(self._is_service_deployed,
-                     service=service,
-                     status=status,
-                     env=env)
-        rc = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return rc
-
-    def _is_service_error(self, service=None, status=None, env=None):
-        resp = self._get_service_status(service=service,
-                                        status=status,
-                                        env=env)
-
-        if resp in ['error']:
-            return True
-
-        return False
-
-    @asyncio.coroutine
-    def is_service_error(self, service=None, status=None, env=None):
-        '''Check if the service is in error state'''
-        pf = partial(self._is_service_error,
-                     service=service,
-                     status=status,
-                     env=env)
-        rc = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return rc
-
-    def _is_service_maint(self, service=None, status=None, env=None):
-        resp = self._get_service_status(service=service,
-                                        status=status,
-                                        env=env)
-
-        if resp in ['maintenance']:
-            return True
-
-        return False
-
-    @asyncio.coroutine
-    def is_service_maint(self, service=None, status=None, env=None):
-        '''Check if the service is in error state'''
-        pf = partial(self._is_service_maint,
-                     service=service,
-                     status=status,
-                     env=env)
-        rc = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return rc
-
-    def _is_service_active(self, service=None, status=None, env=None):
-        resp = self._get_service_status(service=service,
-                                        status=status,
-                                        env=env)
-
-        if resp in ['active']:
-            return True
-
-        return False
-
-    @asyncio.coroutine
-    def is_service_active(self, service=None, status=None, env=None):
-        '''Check if the service is active'''
-        pf = partial(self._is_service_active,
-                     service=service,
-                     status=status,
-                     env=env)
-        rc = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return rc
-
-    def _is_service_blocked(self, service=None, status=None, env=None):
-        resp = self._get_service_status(service=service,
-                                        status=status,
-                                        env=env)
-
-        if resp in ['blocked']:
-            return True
-
-        return False
-
-    @asyncio.coroutine
-    def is_service_blocked(self, service=None, status=None, env=None):
-        '''Check if the service is blocked'''
-        pf = partial(self._is_service_blocked,
-                     service=service,
-                     status=status,
-                     env=env)
-        rc = yield from self.loop.run_in_executor(
+        Note: Model objects returned must call disconnected() before it goes
+        out of scope."""
+        if not self.authenticated:
+            await self.login()
+
+        model = Model()
+
+        uuid = await self.get_model_uuid(name)
+
+        await model.connect(
+            self.endpoint,
+            uuid,
+            self.user,
+            self.secret,
             None,
-            pf,
         )
-        return rc
 
-    def _is_service_up(self, service=None, status=None, env=None):
-        resp = self._get_service_status(service=service,
-                                        status=status,
-                                        env=env)
+        return model
 
-        if resp in ['active', 'blocked']:
-            return True
+    async def get_model_uuid(self, name='default'):
+        """Get the UUID of a model.
 
-        return False
+        Iterate through all models in a controller and find the matching model.
+        """
+        if not self.authenticated:
+            await self.login()
 
-    @asyncio.coroutine
-    def is_service_up(self, service=None, status=None, env=None):
-        '''Check if the service is installed and up'''
-        pf = partial(self._is_service_up,
-                     service=service,
-                     status=status,
-                     env=env)
+        uuid = None
 
-        rc = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return rc
+        models = await self.controller.get_models()
+        for model in models.user_models:
+            if model.model.name == name:
+                uuid = model.model.uuid
+                break
 
-    def _apply_config(self, config, service=None, env=None):
-        if service is None:
-            service = self.service
+        return uuid
 
-        if config is None or len(config) == 0:
-            self.log.warn("{}: Empty config passed for service {}".
-                          format(self, service))
-            return
+    async def get_status(self):
+        """Get the model status."""
+        if not self.authenticated:
+            await self.login()
 
-        if env is None:
-            env = self._get_env()
-
-        status = self._get_status(env=env)
-
-        if not self._is_service_deployed(service=service,
-                                         status=status,
-                                         env=env):
-            raise JujuSrvNotDeployedError("{}: service {} is not deployed".
-                                          format(self, service))
-
-        self.log.debug("{}: Config for service {} update to: {}".
-                       format(self, service, config))
-        try:
-            # Try to fix error on service, most probably due to config issue
-            if self._is_service_error(service=service, status=status, env=env):
-                self._resolve_error(service=service, env=env)
-
-            if self.version == 2:
-                env.service.set(service, config)
-            else:
-                env.set_config(service, config)
-
-        except Exception as e:
-            self.log.error("{}: exception setting config for {} with {}, e {}".
-                           format(self, service, config, e))
-            self.log.exception(e)
-            raise e
-
-    @asyncio.coroutine
-    def apply_config(self, config, service=None, env=None, wait=True):
-        '''Apply a config on the service'''
-        pf = partial(self._apply_config,
-                     config,
-                     service=service,
-                     env=env)
-        yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
+        if not self.model:
+            self.model = self.get_model(self.model_name)
 
-        if wait:
-            # Wait till config finished applying
-            self.log.debug("{}: Wait for config apply to finish".
-                           format(self))
-            delay = 3  # secs
-            maint = True
-            while maint:
-                # Sleep first to give time for config_changed hook to be invoked
-                yield from asyncio.sleep(delay, loop=self.loop)
-                maint = yield from self.is_service_maint(service=service,
-                                                         env=env)
-
-        err = yield from self.is_service_error(service=service, env=env)
-        if err:
-            self.log.error("{}: Service is in error state".
-                           format(self))
-            return False
-
-        self.log.debug("{}: Finished applying config".format(self))
-        return True
-
-    def _set_parameter(self, parameter, value, service=None):
-        return self._apply_config({parameter : value}, service=service)
-
-    @asyncio.coroutine
-    def set_parameter(self, parameter, value, service=None):
-        '''Set a config parameter for a service'''
-        return self.apply_config({parameter : value}, service=service)
-
-    def _resolve_error(self, service=None, status=None, env=None):
-        if env is None:
-            env = self._get_env()
-
-        if status is None:
-            status = self._get_status(env=env)
-
-        if service is None:
-            service = self.service
-
-        if env is None:
-            env = self._get_env()
-        if self._is_service_deployed(service=service, status=status):
-            units = self.get_all_units(status, service=service)
-
-            for unit, ustatus in units[service].items():
-                if ustatus == 'error':
-                    self.log.info("{}: Found unit {} with status {}".
-                                  format(self, unit, ustatus))
-                    try:
-                        # Takes the unit name as service_name/idx unlike action
-                        env.resolved(unit)
-
-                    except Exception as e:
-                        msg = "{}: Resolve on unit {}: {}". \
-                              format(self, unit, e)
-                        self.log.warn(msg)
-
-    @asyncio.coroutine
-    def resolve_error(self, service=None, status=None, env=None):
-        '''Resolve units in error state'''
-        pf = partial(self._resolve_error,
-                     service=service,
-                     status=status,
-                     env=env)
-        yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
+        class model_state:
+            applications = {}
+            machines = {}
+            relations = {}
+
+        status = model_state()
+        status.applications = self.model.applications
+        status.machines = self.model.machines
 
-    def _deploy_service(self, charm, service,
-                        path=None, config=None, env=None):
-        self.log.debug("{}: Deploy service for charm {}({}) with service {}".
-                       format(self, charm, path, service))
+        return status
 
-        if env is None:
-            env = self._get_env()
+    async def is_application_active(self, application):
+        """Check if the application is in an active state."""
+        if not self.authenticated:
+            await self.login()
+
+        state = False
+        status = await self.get_application_status(application)
+        if status and status in ['active']:
+            state = True
+        return state
+    is_service_active = is_application_active
+
+    async def is_application_blocked(self, application):
+        """Check if the application is in a blocked state."""
+        if not self.authenticated:
+            await self.login()
+
+        state = False
+        status = await self.get_application_status(application)
+        if status and status in ['blocked']:
+            state = True
+        return state
+    is_service_blocked = is_application_blocked
+
+    async def is_application_deployed(self, application):
+        """Check if the application is in a deployed state."""
+        if not self.authenticated:
+            await self.login()
+
+        state = False
+        status = await self.get_application_status(application)
+        if status and status in ['active']:
+            state = True
+        return state
+    is_service_deployed = is_application_deployed
+
+    async def is_application_error(self, application):
+        """Check if the application is in an error state."""
+        if not self.authenticated:
+            await self.login()
+
+        state = False
+        status = await self.get_application_status(application)
+        if status and status in ['error']:
+            state = True
+        return state
+    is_service_error = is_application_error
+
+    async def is_application_maint(self, application):
+        """Check if the application is in a maintenance state."""
+        if not self.authenticated:
+            await self.login()
+
+        state = False
+        status = await self.get_application_status(application)
+        if status and status in ['maintenance']:
+            state = True
+        return state
+    is_service_maint = is_application_maint
+
+    async def is_application_up(self, application=None):
+        """Check if the application is up."""
+        if not self.authenticated:
+            await self.login()
+        state = False
+
+        status = await self.get_application_status(application)
+        if status and status in ['active', 'blocked']:
+            state = True
+        return state
+    is_service_up = is_application_up
+
+    async def login(self):
+        """Login to the Juju controller."""
+        if self.authenticated:
+            return
+        cacert = None
+        self.controller = Controller()
+
+        if self.secret:
+            await self.controller.connect(
+                self.endpoint,
+                self.user,
+                self.secret,
+                cacert,
+            )
+        else:
+            await self.controller.connect_current()
 
-        self.service = service
-        self.charm = charm
+        self.authenticated = True
+        self.model = await self.get_model(self.model_name)
 
-        if self._is_service_deployed(service=service, env=env):
-            self.log.info("{}: Charm service {} already deployed".
-                          format (self, service))
-            if config:
-                self._apply_config(config, service=service, env=env)
+    async def logout(self):
+        """Logout of the Juju controller."""
+        if not self.authenticated:
             return
 
-        series = "trusty"
-
-        deploy_to = None
-        if self.version == 1:
-            deploy_to = "lxc:0"
-
-        if path is None:
-            prefix=os.getenv('RIFT_INSTALL', '/')
-            path = os.path.join(prefix, 'usr/rift/charms', series, charm)
-
-        try:
-            self.log.debug("{}: Local charm settings: dir={}, series={}".
-                           format(self, path, series))
-            result = env.add_local_charm_dir(path, series)
-            url = result[self._get_version_tag('charm-url')]
-
-        except Exception as e:
-            msg = '{}: Error setting local charm directory {} for {}: {}'. \
-                  format(self, path, service, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuAddCharmError(msg)
-
-        try:
-            self.log.debug("{}: Deploying using: service={}, url={}, to={}, config={}".
-                           format(self, service, url, deploy_to, config))
-            env.deploy(service, url, config=config, machine_spec=deploy_to)
-
-        except Exception as e:
-            msg = '{}: Error deploying {}: {}'.format(self, service, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuDeployError(msg)
-
-    @asyncio.coroutine
-    def deploy_service(self, charm, service,
-                       wait=False, timeout=300,
-                       path=None, config=None):
-        '''Deploy a service using the charm name provided'''
-        env = yield from self.get_env()
-
-        pf = partial(self._deploy_service,
-                     charm,
-                     service,
-                     path=path,
-                     config=config,
-                     env=env)
-        yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
+        if self.model:
+            await self.model.disconnect()
+            self.model = None
+        if self.controller:
+            await self.controller.disconnect()
+            self.controller = None
+
+        self.authenticated = False
+
+    async def remove_application(self, name):
+        """Remove the application."""
+        if not self.authenticated:
+            await self.login()
+
+        app = await self.get_application(name)
+        if app:
+            await app.destroy()
+
+    async def resolve_error(self, application=None, status=None):
+        """Resolve units in error state."""
+        if not self.authenticated:
+            await self.login()
+
+        app = await self.get_application(application)
+        if app:
+            for unit in app.units:
+                app.resolved(retry=True)
+
+    async def run_action(self, application, action_name, **params):
+        """Execute an action and return an Action object."""
+        if not self.authenticated:
+            await self.login()
+        result = {
+            'status': '',
+            'action': {
+                'tag': None,
+                'results': None,
+            }
+        }
+        app = await self.get_application(application)
+        if app:
+            # We currently only have one unit per application
+            # so use the first unit available.
+            unit = app.units[0]
 
-        rc = True
-        if wait is True:
-            # Wait for the deployed units to start
-            try:
-                self.log.debug("{}: Waiting for service {} to come up".
-                               format(self, service))
-                rc = yield from self.wait_for_service(timeout=timeout, env=env)
-
-            except Exception as e:
-                msg = '{}: Error starting all units for {}: {}'. \
-                      format(self, service, e)
-                self.log.error(msg)
-                self.log.exception(e)
-                raise JujuWaitUnitsError(msg)
-
-        return rc
-
-    @asyncio.coroutine
-    def wait_for_service(self, service=None, timeout=0, env=None):
-        '''Wait for the service to come up'''
-        if service is None:
-            service = self.service
-
-        if env is None:
-            env = yield from self.get_env()
-
-        status = yield from self.get_status(env=env)
-
-        if self._is_service_up(service=service, status=status, env=env):
-            self.log.debug("{}: Service {} is already up".
-                               format(self, service))
-            return True
-
-        # Check if service is deployed
-        if not self._is_service_deployed(service=service, status=status, env=env):
-            raise JujuSrvNotDeployedError("{}: service {} is not deployed".
-                                          format(self, service))
-
-        if timeout < 0:
-            timeout = 0
-
-        count = 0
-        delay = self.retry_delay # seconds
-        self.log.debug("{}: In wait for service {}".format(self, service))
-
-        start_time = time.time()
-        max_time = time.time() + timeout
-        while timeout != 0 and (time.time() <= max_time):
-            count += 1
-            rc = yield from self.is_service_up(service=service, env=env)
-            if rc:
-                self.log.debug("{}: Service {} is up after {} seconds".
-                               format(self, service, time.time()-start_time))
-                return True
-            yield from asyncio.sleep(delay, loop=self.loop)
-        return False
-
-    def _destroy_service(self, service=None):
-        '''Destroy a service on Juju controller'''
-        self.log.debug("{}: Destroy charm service: {}".format(self,service))
-
-        if service is None:
-            service = self.service
-
-        env = self._get_env()
-
-        status = self._get_status(env=env)
-
-        count = 0
-        while self._is_service_deployed(service=service, status=status, env=env):
-            count += 1
-            self.log.debug("{}: Destroy service {}, count {}".
-                           format(self, service, count))
-
-            if count > self.destroy_retries:
-                msg = "{}: Not able to destroy service {} after {} tries". \
-                      format(self, service, count)
-                self.log.error(msg)
-                raise JujuDestroyError(msg)
-
-
-            if self._is_service_error(service=service, status=status):
-                self._resolve_error(service, status)
-
-            try:
-                env.destroy_service(service)
-
-            except Exception as e:
-                msg = "{}: Exception when running destroy on service {}: {}". \
-                      format(self, service, e)
-                self.log.error(msg)
-                self.log.exception(e)
-                raise JujuDestroyError(msg)
-
-            time.sleep(self.retry_delay)
-            status = self._get_status(env=env)
-
-        self.log.debug("{}: Destroyed service {} ({})".
-                       format(self, service, count))
-
-    @asyncio.coroutine
-    def destroy_service(self, service=None):
-        '''Destroy a service on Juju controller'''
-        pf = partial(self._destroy_service,
-                     service=service)
-        yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
+            action = await unit.run_action(action_name, **params)
 
+            # Wait for the action to complete
+            await action.wait()
 
-    def _get_action_status(self, action_tag, env=None):
-        if env is None:
-            env = self._get_env()
-
-        if not action_tag.startswith('action-'):
-            action_tag = 'action-{}'.format(action_tag)
-
-        try:
-            action = env.actions
-        except Exception as e:
-            msg = "{}: exception in Action API: {}".format(self, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuActionApiError(msg)
-
-        try:
-            status = action.info([{'Tag': action_tag}])
-
-            self.log.debug("{}: Action {} status {}".
-                           format(self, action_tag, status))
-            return status['results'][0]
-
-        except Exception as e:
-            msg = "{}: exception in get action status {}".format(self, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuActionInfoError(msg)
-
-    @asyncio.coroutine
-    def get_action_status(self, action_tag, env=None):
-        '''
-        Get the status of an action queued on the controller
-
-        responds with the action status, which is one of three values:
-
-         - completed
-         - pending
-         - failed
-
-         @param action_tag - the action UUID return from the enqueue method
-         eg: action-3428e20d-fcd7-4911-803b-9b857a2e5ec9
-        '''
-        pf = partial(self._get_action_status,
-                     action_tag,
-                     env=env,)
-        status = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
-        return status
+            result['status'] = action.status
+            result['action']['tag'] = action.data['id']
+            result['action']['results'] = action.results
 
-    def _execute_action(self, action_name, params, service=None, env=None):
-        '''Execute the action on all units of a service'''
-        if service is None:
-            service = self.service
-
-        if env is None:
-            env = self._get_env()
-
-        try:
-            action = env.actions
-        except Exception as e:
-            msg = "{}: exception in Action API: {}".format(self, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuActionApiError(msg)
-
-        units = self._get_service_units(service)
-        self.log.debug("{}: Apply action {} on units {}".
-                       format(self, action_name, units))
-
-        # Rename units from <service>/<n> to unit-<service>-<n>
-        unit_tags = []
-        for unit in units:
-            idx = int(unit[unit.index('/')+1:])
-            unit_name = "unit-%s-%d" % (service, idx)
-            unit_tags.append(unit_name)
-        self.log.debug("{}: Unit tags for action: {}".
-                       format(self, unit_tags))
-
-        try:
-            result = action.enqueue_units(unit_tags, action_name, params)
-            self.log.debug("{}: Response for action: {}".
-                           format(self, result))
-            return result['results'][0]
-
-        except Exception as e:
-            msg = "{}: Exception enqueing action {} on units {} with " \
-                  "params {}: {}".format(self, action, unit_tags, params, e)
-            self.log.error(msg)
-            self.log.exception(e)
-            raise JujuActionExecError(msg)
-
-    @asyncio.coroutine
-    def execute_action(self, action_name, params, service=None, env=None):
-        '''Execute an action for a service on the controller
-
-        Currently, we execute the action on all units of the service
-        '''
-        pf = partial(self._execute_action,
-                     action_name,
-                     params,
-                     service=service,
-                     env=env)
-        result = yield from self.loop.run_in_executor(
-            None,
-            pf,
-        )
         return result
+    execute_action = run_action
 
+    async def set_config(self, application, config):
+        """Apply a configuration to the application."""
+        if not self.authenticated:
+            await self.login()
 
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description='Test Juju')
-    parser.add_argument("-s", "--server", default='10.0.202.49', help="Juju controller")
-    parser.add_argument("-u", "--user", default='admin', help="User, default user-admin")
-    parser.add_argument("-p", "--password", default='nfvjuju', help="Password for the user")
-    parser.add_argument("-P", "--port", default=17070, help="Port number, default 17070")
-    parser.add_argument("-d", "--directory", help="Local directory for the charm")
-    parser.add_argument("--service", help="Charm service name")
-    parser.add_argument("--vnf-ip", help="IP of the VNF to configure")
-    args = parser.parse_args()
+        app = await self.get_application(application)
+        if app:
+            await app.set_config(config)
 
-    api = JujuApi(server=args.server,
-                  port=args.port,
-                  user=args.user,
-                  secret=args.password)
+    async def set_parameter(self, parameter, value, application=None):
+        """Set a config parameter for a service."""
+        if not self.authenticated:
+            await self.login()
 
-    env = api._get_env()
-    if env is None:
-        raise "Not able to login to the Juju controller"
+        return await self.apply_config(
+            {parameter: value},
+            application=application,
+        )
 
-    print("Status: {}".format(api._get_status(env=env)))
+    async def wait_for_application(self, name, timeout=300):
+        """Wait for an application to become active."""
+        if not self.authenticated:
+            await self.login()
 
-    if args.directory and args.service:
-        # Deploy the charm
-        charm = os.path.basename(args.directory)
-        api._deploy_service(charm, args.service,
-                            path=args.directory,
-                            env=env)
+        app = await self.get_application(name)
+        if app:
+            await self.model.block_until(
+                lambda: all(
+                    unit.agent_status == 'idle'
+                    and unit.workload_status
+                    in ['active', 'unknown'] for unit in app.units
+                    ),
+                timeout=timeout,
+                )
 
-        while not api._is_service_up():
-            time.sleep(5)
 
-        print ("Service {} is deployed with status {}".
-               format(args.service, api._get_service_status()))
+def get_argparser():
+    parser = argparse.ArgumentParser(description='Test Juju')
+    parser.add_argument(
+        "-s", "--server",
+        default='10.0.202.49',
+        help="Juju controller"
+    )
+    parser.add_argument(
+        "-u", "--user",
+        default='admin',
+        help="User, default user-admin"
+    )
+    parser.add_argument(
+        "-p", "--password",
+        default='',
+        help="Password for the user"
+    )
+    parser.add_argument(
+        "-P", "--port",
+        default=17070,
+        help="Port number, default 17070"
+    )
+    parser.add_argument(
+        "-d", "--directory",
+        help="Local directory for the charm"
+    )
+    parser.add_argument(
+        "--application",
+        help="Charm name"
+    )
+    parser.add_argument(
+        "--vnf-ip",
+        help="IP of the VNF to configure"
+    )
+    parser.add_argument(
+        "-m", "--model",
+        default='default',
+        help="The model to connect to."
+    )
+    return parser.parse_args()
 
-        if args.vnf_ip and \
-           ('clearwater-aio' in args.directory):
-            # Execute config on charm
-            api._apply_config({'proxied_ip': args.vnf_ip})
 
-            while not api._is_service_active():
-                time.sleep(10)
+if __name__ == "__main__":
+    args = get_argparser()
 
-            print ("Service {} is in status {}".
-                   format(args.service, api._get_service_status()))
+    # Set logging level to debug so we can see verbose output from the
+    # juju library.
+    logging.basicConfig(level=logging.DEBUG)
 
-            res = api._execute_action('create-update-user', {'number': '125252352525',
-                                                             'password': 'asfsaf'})
+    # Quiet logging from the websocket library. If you want to see
+    # everything sent over the wire, set this to DEBUG.
+    ws_logger = logging.getLogger('websockets.protocol')
+    ws_logger.setLevel(logging.INFO)
 
-            print ("Action 'creat-update-user response: {}".format(res))
+    endpoint = '%s:%d' % (args.server, int(args.port))
 
-            status = res['status']
-            while status not in [ 'completed', 'failed' ]:
-                time.sleep(2)
-                status = api._get_action_status(res['action']['tag'])['status']
+    loop = asyncio.get_event_loop()
 
-                print("Action status: {}".format(status))
+    api = JujuApi(server=args.server,
+                  port=args.port,
+                  user=args.user,
+                  secret=args.password,
+                  loop=loop,
+                  log=ws_logger,
+                  model_name=args.model
+                  )
 
-            # This action will fail as the number is non-numeric
-            res = api._execute_action('delete-user', {'number': '125252352525asf'})
+    juju.loop.run(api.login())
 
-            print ("Action 'delete-user response: {}".format(res))
+    status = juju.loop.run(api.get_status())
 
-            status = res['status']
-            while status not in [ 'completed', 'failed' ]:
-                time.sleep(2)
-                status = api._get_action_status(res['action']['tag'])['status']
+    print('Applications:', list(status.applications.keys()))
+    print('Machines:', list(status.machines.keys()))
 
-                print("Action status: {}".format(status))
+    if args.directory and args.application:
+        # Deploy the charm
+        charm = os.path.basename(args.directory)
+        juju.loop.run(
+            api.deploy_application(charm,
+                                   name=args.application,
+                                   path=args.directory,
+                                   )
+        )
+
+        juju.loop.run(api.wait_for_application(charm))
+
+        # Wait for the service to come up
+        up = juju.loop.run(api.is_application_up(charm))
+        print("Application is {}".format("up" if up else "down"))
+
+        print("Service {} is deployed".format(args.application))
+
+        ###########################
+        # Execute config on charm #
+        ###########################
+        config = juju.loop.run(api.get_config(args.application))
+        hostname = config['ssh-username']['value']
+        rhostname = hostname[::-1]
+
+        # Apply the configuration
+        juju.loop.run(api.apply_config(
+            {'ssh-username': rhostname}, application=args.application
+        ))
+
+        # Get the configuration
+        config = juju.loop.run(api.get_config(args.application))
+
+        # Verify the configuration has been updated
+        assert(config['ssh-username']['value'] == rhostname)
+
+        ####################################
+        # Get the status of an application #
+        ####################################
+        status = juju.loop.run(api.get_application_status(charm))
+        print("Application Status: {}".format(status))
+
+        ###########################
+        # Execute a simple action #
+        ###########################
+        result = juju.loop.run(api.run_action(charm, 'get-ssh-public-key'))
+        print("Action {} status is {} and returned {}".format(
+            result['status'],
+            result['action']['tag'],
+            result['action']['results']
+        ))
+
+        #####################################
+        # Execute an action with parameters #
+        #####################################
+        result = juju.loop.run(
+            api.run_action(charm, 'run', command='hostname')
+        )
+        print("Action {} status is {} and returned {}".format(
+            result['status'],
+            result['action']['tag'],
+            result['action']['results']
+        ))
+
+    juju.loop.run(api.logout())
+
+    loop.close()
+
+    #     if args.vnf_ip and \
+    #        ('clearwater-aio' in args.directory):
+    #         # Execute config on charm
+    #         api._apply_config({'proxied_ip': args.vnf_ip})
+    #
+    #         while not api._is_service_active():
+    #             time.sleep(10)
+    #
+    #         print ("Service {} is in status {}".
+    #                format(args.service, api._get_service_status()))
+    #
+    #         res = api._execute_action('create-update-user', {'number': '125252352525',
+    #                                                          'password': 'asfsaf'})
+    #
+    #         print ("Action 'creat-update-user response: {}".format(res))
+    #
+    #         status = res['status']
+    #         while status not in [ 'completed', 'failed' ]:
+    #             time.sleep(2)
+    #             status = api._get_action_status(res['action']['tag'])['status']
+    #
+    #             print("Action status: {}".format(status))
+    #
+    #         # This action will fail as the number is non-numeric
+    #         res = api._execute_action('delete-user', {'number': '125252352525asf'})
+    #
+    #         print ("Action 'delete-user response: {}".format(res))
+    #
+    #         status = res['status']
+    #         while status not in [ 'completed', 'failed' ]:
+    #             time.sleep(2)
+    #             status = api._get_action_status(res['action']['tag'])['status']
+    #
+    #             print("Action status: {}".format(status))
index 1e9397e..5068b31 100644 (file)
@@ -175,7 +175,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                           'tags': {},
                           'active': False,
                           'config': vnf_config,
-                          'vnfr_name' : agent_vnfr.name})
+                          'vnfr_name': agent_vnfr.name})
         self._log.debug("jujuCA: Charm %s for vnf %s to be deployed as %s",
                         charm, agent_vnfr.name, vnf_unique_name)
 
@@ -200,7 +200,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
             self._tasks[vnf_unique_name] = {}
 
         self._tasks[vnf_unique_name]['deploy'] = self.loop.create_task(
-            self.api.deploy_service(charm, vnf_unique_name, path=path))
+            self.api.deploy_application(charm, vnf_unique_name, path=path))
 
         self._log.debug("jujuCA: Deploying service %s",
                         vnf_unique_name)
@@ -242,7 +242,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
             self._log.debug ("jujuCA: Terminating VNFr %s, %s",
                              agent_vnfr.name, service)
             self._tasks[service]['destroy'] = self.loop.create_task(
-                    self.api.destroy_service(service)
+                    self.api.remove_application(service)
                 )
 
             del self._juju_vnfs[agent_vnfr.id]
@@ -255,7 +255,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                     tasks.append(action)
                 del tasks
         except KeyError as e:
-            self._log.debug ("jujuCA: Termiating charm service for VNFr {}, e={}".
+            self._log.debug ("jujuCA: Terminating charm service for VNFr {}, e={}".
                              format(agent_vnfr.name, e))
         except Exception as e:
             self._log.error("jujuCA: Exception terminating charm service for VNFR {}: {}".
@@ -360,10 +360,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                                             "params {} for service {}".
                                             format(params, service))
 
-                            rc = yield from self.api.apply_config(
-                                params,
-                                service=service,
-                                wait=True)
+                            rc = yield from self.api.apply_config(params, application=service)
 
                             if rc:
                                 rc = "completed"
@@ -387,9 +384,10 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                                         format(config.name, service, params))
 
                         resp = yield from self.api.execute_action(
+                            service,
                             config.name,
-                            params,
-                            service=service)
+                            **params,
+                        )
 
                         if resp:
                             if 'error' in resp:
@@ -585,12 +583,11 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                             format(vnfr_msg.name))
             return True
 
-        service = vnfr['vnf_juju_name']
-        rc = yield from self.api.is_service_up(service=service)
+        rc = yield from self.api.is_application_up(application=service)
         if not rc:
             return False
 
-        action_ids = []
+        action_ids = []
         try:
             if vnfr_msg.mgmt_interface.ip_address:
                 vnfr['tags'].update({'rw_mgmt_ip': vnfr_msg.mgmt_interface.ip_address})
@@ -633,48 +630,61 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                                 val = self.xlate(param.value,
                                                  vnfr['tags'])
                                 config.update({param.name: val})
+            except KeyError as e:
+                self._log.exception("jujuCA:(%s) Initial config error(%s): config=%s",
+                                    vnfr['vnf_juju_name'], str(e), config)
+                config = None
+                return False
+
+            if config:
+                self.juju_log('info', vnfr['vnf_juju_name'],
+                              "Applying Initial config:%s",
+                              config)
+
+                rc = yield from self.api.apply_config(
+                    config,
+                    application=service,
+                )
+                if rc is False:
+                    self.log.error("Service {} is in error state".format(service))
+                    return False
 
                         if config:
                             self.juju_log('info', vnfr['vnf_juju_name'],
                                           "Applying Initial config:%s",
                                           config)
 
-                            rc = yield from self.api.apply_config(
-                                config,
-                                service=service)
-                            if rc is False:
-                                self.log.error("Service {} is in error state".
-                                               format(service))
-                                return False
-
-                    # Apply any actions specified as part of initial config
-                    else:
-                        self._log.debug("(%s) Initial config action "
-                                        "primitive %s",
-                                        vnfr['vnf_juju_name'], primitive)
-                        action = primitive.name
-                        params = {}
-                        for param in primitive.parameter:
-                            val = self.xlate(param.value, vnfr['tags'])
-                            params.update({param.name: val})
-
-                            self._log.info("(%s) Action %s with params %s",
-                                           vnfr['vnf_juju_name'], action,
-                                           params)
-
-                        resp = yield from self.api.execute_action(
-                            action,
-                            params,
-                            service=service)
-                        if 'error' in resp:
-                            self._log.error("Applying initial config on {}"
-                                            " failed for {} with {}: {}".
-                                            format(vnfr['vnf_juju_name'],
-                                                   action, params, resp))
-                            return False
-
-                        action_ids.append(resp['action']['tag'])
+            # Apply any actions specified as part of initial config
+            for primitive in vnfr['config'].initial_config_primitive:
+                if primitive.name != 'config':
+                    self._log.debug("jujuCA:(%s) Initial config action primitive %s",
+                                    vnfr['vnf_juju_name'], primitive)
+                    action = primitive.name
+                    params = {}
+                    for param in primitive.parameter:
+                        val = self.xlate(param.value, vnfr['tags'])
+                        params.update({param.name: val})
+
+                    self._log.info("jujuCA:(%s) Action %s with params %s",
+                                   vnfr['vnf_juju_name'], action, params)
+                    self._log.debug("executing action")
+                    resp = yield from self.api.execute_action(
+                        service,
+                        action,
+                        **params,
+                    )
+                    self._log.debug("executed action")
+                    if 'error' in resp:
+                        self._log.error("Applying initial config on {} failed for {} with {}: {}".
+                                        format(vnfr['vnf_juju_name'], action, params, resp))
+                        return False
 
+                    # action_ids.append(resp['action']['tag'])
+                    # action_ids.append(resp)
+        except KeyError as e:
+            self._log.info("Juju config agent(%s): VNFR %s not managed by Juju",
+                           vnfr['vnf_juju_name'], agent_vnfr.id)
+            return False
         except Exception as e:
             self._log.exception("jujuCA:(%s) Exception juju "
                                 "apply_initial_config for VNFR {}: {}".
@@ -682,25 +692,6 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                                        agent_vnfr.id, e))
             return False
 
-        # Check if all actions completed
-        pending = True
-        while pending:
-            pending = False
-            for act in action_ids:
-                resp = yield from self.api.get_action_status(act)
-                if 'error' in resp:
-                    self._log.error("Initial config failed for action {}: {}".
-                                    format(act, resp))
-                    return False
-
-                if resp['status'] == 'failed':
-                    self._log.error("Initial config action failed for "
-                                    "action {}: {}".format(act, resp))
-                    return False
-
-                if resp['status'] == 'pending':
-                    pending = True
-
         return True
 
     def add_vnfr_managed(self, agent_vnfr):
@@ -728,7 +719,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
 
             vnfr = self._juju_vnfs[vnfr_id].vnfr
             service = vnfr['vnf_juju_name']
-            resp = self.api.is_service_active(service=service)
+            resp = self.api.is_application_active(application=service)
             self._juju_vnfs[vnfr_id]['active'] = resp
             self._log.debug("jujuCA: Service state for {} is {}".
                             format(service, resp))
@@ -761,7 +752,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
             return rc
 
         try:
-            resp = yield from self.api.get_service_status(service=service)
+            resp = yield from self.api.get_service_status(application=service)
             self._log.debug("jujuCA: Get service %s status? %s", service, resp)
 
             if resp == 'error':